npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@natyapp/langchain

v1.3.1

Published

TypeScript client for Naty LangChain API with async file uploads, real-time progress tracking, and Chat support

Readme

Naty LangChain Client

TypeScript client for the Naty LangChain API - Updated for OpenAPI 3.1.0 specification with full support for RAG, Chat, and File Upload features.

🚀 New Features

V2 API (latest — recommended)

  • V2 Query API (/v2/query): Unified endpoint supporting 5 pipeline modes (rag, direct, chat, multi_hop, summary), streaming SSE, multimodal (images), and per-request temperature override
  • V2 Config API (/v2/config): Read and update per-tenant LLM and pipeline configuration at runtime
  • V2 Upload API (/v2/documents/upload): Single unified upload endpoint supporting all file types (PDF, DOCX, XLSX, CSV, TXT, MD)

V1 API (stable)

  • Chat API with Streaming: Professional chat with hybrid RAG (history + vector search)
  • File Upload APIs: Upload and process Excel, PDF, Word, and CSV files
  • Health Check: Monitor system status and dependencies
  • Enhanced Types: Comprehensive TypeScript interfaces for all endpoints
  • Streaming Support: Real-time responses with Server-Sent Events (SSE)

Installation

npm install @natyapp/langchain
# or
yarn add @natyapp/langchain

Quick Start

Initialize Client

import { NatyLangChainClient } from '@natyapp/langchain';

const client = new NatyLangChainClient({
  baseURL: 'http://localhost:8000',
  apiKey: 'nlck-key_prod_1',           // X-API-Key authentication
  tenantId: 'demo-tenant',             // X-Tenant-ID header (for multi-tenancy)
  requestId: 'custom-request-id',      // Optional: Custom X-Request-ID
});

Basic QA (V1)

const response = await client.qa.ask({
  question: 'Como funciona o RAG?',
  k: 3,
});
console.log(response.answer);

V2 Query — Recommended

// RAG mode (default): retrieves from vector store + generates answer
const response = await client.v2.query({
  question: 'Quais são as políticas de férias?',
  mode: 'rag',
});
console.log(response.answer);
console.log(response.sources);   // [{file_name, chunk_index, ...}]

// Direct mode: sends question straight to LLM (no retrieval)
const direct = await client.v2.query({
  question: 'Escreva um slogan criativo para nosso produto.',
  mode: 'direct',
  temperature: 1.2,           // override per request (0.0–2.0)
});

// Chat mode: multi-turn conversation with session history
const chat = await client.v2.query({
  question: 'O que ficou pendente na reunião anterior?',
  mode: 'chat',
  session_id: 'session-user-001',
});

// Streaming (any mode)
const stream = await client.v2.stream({
  question: 'Explique a arquitetura do sistema.',
  mode: 'rag',
});
for await (const chunk of stream) {
  if (chunk.delta) process.stdout.write(chunk.delta);
  if (chunk.done)  console.log('\nFontes:', chunk.sources);
}

🆕 V2 API

V2 Query API

import { V2QueryRequest, V2QueryResponse } from '@natyapp/langchain';

// All available options
const request: V2QueryRequest = {
  question: 'Quais documentos mencionam LGPD?',
  mode: 'rag',               // 'rag' | 'direct' | 'chat' | 'multi_hop' | 'summary'
  k: 6,                      // number of documents to retrieve (default: 4)
  filters: { category: 'legal' },  // vector store filters (optional)
  session_id: 'sess-001',    // required for mode='chat'
  stream: false,             // set true to receive SSE stream
  temperature: 0.3,          // 0.0–2.0, overrides tenant config (optional)
  images: [                  // multimodal — supported in rag, direct, chat (optional)
    { url: 'https://example.com/diagram.png', mime_type: 'image/png' }
  ],
};

// Non-streaming
const response: V2QueryResponse = await client.v2.query(request);
// response.answer   — generated text
// response.mode     — pipeline mode used
// response.sources  — [{file_name, chunk_index, ...}]

// Streaming (SSE async iterator)
for await (const frame of client.v2.stream(request)) {
  if (frame.delta) process.stdout.write(frame.delta);
  if (frame.done)  console.log('\nDone. Sources:', frame.sources);
}

// Override tenant ID per request
const resp = await client.v2.query(request, 'other-tenant-id');

Query Modes

| Mode | Description | Use case | |------|-------------|----------| | rag | Retrieval-Augmented Generation | Most document Q&A | | direct | Direct LLM call (no retrieval) | Creative text, formatting | | chat | Multi-turn conversation with history | Support, iterative analysis | | multi_hop | Decomposes complex questions | Cross-document research | | summary | Summarization of retrieved docs | Executive briefings |

Temperature Priority

request.temperature > tenant_config.temperature > global default (0.2)

V2 Config API

import { V2TenantConfig, V2ConfigUpdateRequest } from '@natyapp/langchain';

// Get current tenant configuration
const config: V2TenantConfig = await client.v2Config.get();
console.log(config.pipeline_default);   // 'rag'
console.log(config.llm_model);          // 'gpt-4o-mini'
console.log(config.temperature);        // 0.2

// Update configuration (patch semantics — omitted fields are preserved)
const updated = await client.v2Config.update({
  llm_model: 'gpt-4o',
  temperature: 0.5,
  reranker_enabled: true,
  system_prompt_default: 'You are a helpful assistant. Answer in Portuguese.',
});

// Override tenant ID per request
const otherConfig = await client.v2Config.get('other-tenant-id');

Available Configuration Fields

interface V2ConfigUpdateRequest {
  pipeline_default?: 'rag' | 'direct' | 'chat' | 'multi_hop' | 'summary';
  llm_provider?: 'openai' | 'ollama';
  llm_model?: string;          // e.g. 'gpt-4o', 'gpt-4o-mini', 'llama3'
  max_tokens?: number;         // max response tokens
  temperature?: number;        // 0.0 – 2.0
  system_prompt_default?: string;
  retriever_mode?: 'vector' | 'hybrid';
  reranker_enabled?: boolean;
  tools_enabled?: string[];
}

V2 Upload API

import { V2UploadOptions, V2UploadResponse } from '@natyapp/langchain';

// Upload any supported file type (single unified endpoint)
const files = [pdfFile, excelFile, docxFile];  // File objects (browser) or Buffers (Node)

const options: V2UploadOptions = {
  source: 'legal-docs',      // tag for filtering later (optional)
  chunk_size: 1000,          // tokens per chunk (optional)
  chunk_overlap: 200,        // overlap between chunks (optional)
  on_conflict: 'replace',    // 'error' | 'replace' | 'version' (optional)
};

const result: V2UploadResponse = await client.v2Upload.upload(files, options);
console.log(result.job_id);        // track processing progress
console.log(result.status_url);    // URL to poll for status

// Override tenant ID
const res = await client.v2Upload.upload(files, options, 'other-tenant-id');

Supported File Types

| Extension | Type | |-----------|------| | .pdf | PDF | | .docx, .doc | Word | | .xlsx | Excel | | .csv | CSV | | .txt | Plain text | | .md | Markdown |

Track Upload Progress

After uploading, poll the job status endpoint:

// Using V1 Jobs API (also available for V2 uploads)
const status = await client.jobs.getStatus(result.job_id);
// status.state: 'pending' | 'processing' | 'completed' | 'failed'
// status.progress: 0–100

💬 Chat API with Streaming

Professional Chat with Hybrid RAG

import { ChatMessage, ChatQARequest } from '@natyapp/langchain';

const chatHistory: ChatMessage[] = [
  { role: 'user', content: 'Olá!' },
  { role: 'assistant', content: 'Oi! Como posso ajudá-lo?' }
];

const request: ChatQARequest = {
  question: 'Como funciona o sistema de RAG?',
  system_prompt: 'Você é um especialista em IA',
  chat_history: chatHistory,
  rag_enabled: true,
  k: 5,
  max_history_messages: 10,
  strict_context: true,
  include_metadata: false
};

// Non-streaming response (NEW)
const response = await client.chat.chat(request);
console.log(response.answer);
console.log(`Used ${response.rag_documents_used} documents`);

// Streaming response
const stream = await client.chat.stream(request);
const reader = stream.getReader();

// Or use async iterator
for await (const chunk of client.chat.streamIterator(request)) {
  if (chunk.type === 'content') {
    console.log(chunk.data.text);
  }
}

📁 File Upload APIs

Upload Excel Files

import { ExcelUploadOptions } from '@natyapp/langchain';

const files = [excelFile1, excelFile2]; // File objects
const options: ExcelUploadOptions = {
  chunk_size: 1000,
  text_format: 'structured',
  include_sheets: 'Sheet1,Sheet2',
  max_rows: 1000
};

const result = await client.fileUpload.uploadExcel(files, options);
console.log(`Processed ${result.sheets_processed.length} sheets`);

Preview Excel Content

const preview = await client.fileUpload.previewExcel(excelFile, {
  max_rows: 10,
  text_format: 'csv'
});

Upload PDF Files

import { PDFUploadOptions } from '@natyapp/langchain';

const options: PDFUploadOptions = {
  extract_by_page: true,
  chunk_size: 800,
  page_range_start: 1,
  page_range_end: 10
};

const result = await client.fileUpload.uploadPDF(pdfFiles, options);

Upload Word Documents

import { WordUploadOptions } from '@natyapp/langchain';

const options: WordUploadOptions = {
  extract_by_paragraph: false,
  include_tables: true,
  include_headers_footers: true,
  preserve_formatting: false
};

const result = await client.fileUpload.uploadWord(wordFiles, options);

Upload CSV Files

import { CSVUploadOptions } from '@natyapp/langchain';

const options: CSVUploadOptions = {
  delimiter: ',',
  encoding: 'utf-8',
  output_format: 'structured',
  row_chunk_size: 100,
  skip_empty_rows: true
};

const result = await client.fileUpload.uploadCSV(csvFiles, options);

🏥 Health Check

const health = await client.health.check();
console.log(`Status: ${health.status}`);
console.log(`Checks:`, health.checks);

⚡ Custom QA with Streaming

import { CustomQAStreamRequest } from '@natyapp/langchain';

const request: CustomQAStreamRequest = {
  system_prompt: 'Responda como especialista técnico',
  question: 'Explique machine learning',
  contexts_text: 'Machine Learning é...',  // Optional explicit context
  strict_context: true,
  k: 4
};

const stream = await client.customQA.stream(request);
// Process streaming response...

Backward Compatibility

import { NatyClient } from '@natyapp/langchain';

// Legacy client still works (shows deprecation warning)
const legacyClient = new NatyClient({
  baseURL: 'http://localhost:8000',
  token: 'legacy-token',    // Will be used as X-API-Key
  tenantId: 'demo-tenant',
});

cURL Equivalent

V2 Query (recommended)

await client.v2.query({ question: 'O que é RAG?', mode: 'rag' });
curl -X POST http://localhost:8000/v2/query \
  -H "Content-Type: application/json" \
  -H "X-API-Key: nlck-key_prod_1" \
  -H "X-Tenant-ID: demo-tenant" \
  -d '{"question": "O que é RAG?", "mode": "rag"}'

V2 Config

# GET
curl http://localhost:8000/v2/config \
  -H "X-API-Key: nlck-key_prod_1" \
  -H "X-Tenant-ID: demo-tenant"

# PUT
curl -X PUT http://localhost:8000/v2/config \
  -H "Content-Type: application/json" \
  -H "X-API-Key: nlck-key_prod_1" \
  -H "X-Tenant-ID: demo-tenant" \
  -d '{"temperature": 0.5, "llm_model": "gpt-4o"}'

V1 QA (legacy)

The following client usage:

const client = new NatyLangChainClient({
  baseURL: 'http://localhost:8000',
  apiKey: 'nlck-key_prod_1',
  tenantId: 'demo-tenant',
  requestId: 'demo-request-id',
});

await client.qa.ask({ question: 'Olá?', k: 3 });

Is equivalent to this cURL command:

curl -X POST http://localhost:8000/v1/qa \
  -H "Content-Type: application/json" \
  -H "X-Tenant-ID: demo-tenant" \
  -H "X-API-Key: nlck-key_prod_1" \
  -H "X-Request-ID: demo-request-id" \
  -d '{"question": "Olá?", "k": 3}'

API Reference

Configuration

interface NatyLangChainClientConfig {
  baseURL: string;
  apiKey?: string;      // API key for X-API-Key header
  tenantId?: string;    // Tenant ID for X-Tenant-ID header
  requestId?: string;   // Custom request ID (auto-generated if not provided)
}

// Backward compatibility
interface NatyClientConfig extends NatyLangChainClientConfig {
  token?: string;       // Legacy token (deprecated, use apiKey)
}

Authentication & Headers

The client automatically manages the following headers:

  • Content-Type: application/json - Always included
  • X-API-Key - API key authentication (replaces Bearer tokens)
  • X-Tenant-ID - Multi-tenant support (when tenantId provided)
  • X-Request-ID - Request tracking (auto-generated UUID if not provided)

Available APIs

V2QueryAPI (client.v2)

client.v2.query(request: V2QueryRequest, tenantId?: string): Promise<V2QueryResponse>
client.v2.stream(request: V2QueryRequest, tenantId?: string): AsyncIterable<V2StreamFrame>

V2ConfigAPI (client.v2Config)

client.v2Config.get(tenantId?: string): Promise<V2TenantConfig>
client.v2Config.update(config: V2ConfigUpdateRequest, tenantId?: string): Promise<V2TenantConfig>

V2UploadAPI (client.v2Upload)

client.v2Upload.upload(files: File[], options?: V2UploadOptions, tenantId?: string): Promise<V2UploadResponse>

V2 Types

type V2QueryMode = 'rag' | 'direct' | 'chat' | 'multi_hop' | 'summary';

interface V2QueryRequest {
  question: string;
  mode?: V2QueryMode;       // default: 'rag'
  k?: number;               // default: 4
  filters?: Record<string, unknown>;
  session_id?: string;      // required for chat mode
  stream?: boolean;
  temperature?: number;     // 0.0–2.0, overrides tenant config
  images?: ImagePayload[];  // for multimodal queries
}

interface ImagePayload {
  url?: string;             // public HTTPS URL
  base64?: string;          // base64-encoded image data
  mime_type: string;        // 'image/png', 'image/jpeg', etc.
}

interface V2QueryResponse {
  answer: string;
  mode: V2QueryMode;
  sources?: Array<{ file_name: string; chunk_index: number; [key: string]: unknown }>;
  session_id?: string;
}

interface V2TenantConfig {
  tenant_id: string;
  pipeline_default: V2QueryMode;
  llm_provider: 'openai' | 'ollama';
  llm_model: string;
  max_tokens: number;
  temperature: number;      // 0.0–2.0
  system_prompt_default?: string;
  retriever_mode: 'vector' | 'hybrid';
  reranker_enabled: boolean;
  tools_enabled: string[];
}

interface V2UploadOptions {
  source?: string;
  chunk_size?: number;
  chunk_overlap?: number;
  on_conflict?: 'error' | 'replace' | 'version';
}

interface V2UploadResponse {
  job_id: string;
  status_url: string;
  files_accepted: number;
}

QA API (V1)

// Simple QA request
const qaResponse = await client.qa.ask({
  question: 'What is LangChain?',
  k: 4,                    // Optional: number of documents to retrieve
  filters: {               // Optional: vector store filters
    category: { $eq: 'tech' }
  }
});

// Override tenant ID per request
const response = await client.qa.ask(request, 'different-tenant-id');

Custom QA API

// Custom QA with system prompt
const customResponse = await client.customQA.ask({
  system_prompt: 'You are a helpful assistant that responds in Portuguese.',
  question: 'Como posso ajudar você hoje?',
  contexts_text: 'Optional explicit context',  // Optional
  filters: { tenant_id: { $eq: 'demo-tenant' } }, // Optional
  k: 4,                                           // Optional
  strict_context: true,                           // Optional
});

// Streaming custom QA
const stream = await client.customQA.stream({
  system_prompt: 'You are a helpful assistant.',
  question: 'Tell me about AI',
  strict_context: false,
});

// Process stream
const reader = stream.getReader();
while (true) {
  const { done, value } = await reader.read();
  if (done) break;
  console.log(new TextDecoder().decode(value));
}

Documents API

// Create documents
const createResponse = await client.documents.create([
  {
    text: 'Document content here',
    metadata: {
      title: 'Document Title',
      category: 'technology',
      source: 'internal',
    },
  },
]);

// Create documents in batch (async operation)
const batchResponse = await client.documents.createBatch([...documents]);

// List documents
const listResponse = await client.documents.list();

// Search documents
const searchResponse = await client.documents.search({
  query: 'search term',
  filters: { category: { $eq: 'technology' } },
  k: 5,
});

// Delete documents by IDs
const deleteResponse = await client.documents.delete(['doc-id-1', 'doc-id-2']);

Error Handling

The client uses enhanced error handling with NatyLangChainError:

import { NatyLangChainError } from '@natyapp/langchain';

try {
  const response = await client.qa.ask({ question: 'Test' });
} catch (error) {
  if (error instanceof NatyLangChainError) {
    console.error('API Error:', error.message);
    console.error('Status:', error.status);      // HTTP status code
    console.error('Code:', error.code);          // Error code
    console.error('Details:', error.details);    // Additional error details
  } else {
    console.error('Unknown error:', error);
  }
}

Logging

The client automatically logs request headers (with API key masking for security):

// Example log output:
// Request headers: {
//   'X-Tenant-ID': 'demo-tenant',
//   'X-Request-ID': 'a90e-fb3b-4931-8dc4-51cc8adabf4e',
//   'X-API-Key': 'nlck...od_1'  // Masked for security
// }

Utility Functions

import { generateRequestId, maskApiKey } from '@natyapp/langchain';

// Generate UUID-like request ID
const requestId = generateRequestId();
console.log(requestId); // "a90e-fb3b-4931-8dc4-51cc8adabf4e"

// Mask API key for logging
const maskedKey = maskApiKey('nlck-key_prod_1234567890');
console.log(maskedKey); // "nlck...7890"

Migration Guide

From v1.x to v2.x (V2 API)

Migrate from the V1 endpoints to the new unified V2 API:

  1. Use client.v2.query() instead of client.qa.ask():

    // Old (V1)
    const res = await client.qa.ask({ question: 'Q?', k: 4 });
    
    // New (V2)
    const res = await client.v2.query({ question: 'Q?', mode: 'rag', k: 4 });
  2. Use client.v2Upload.upload() for all file uploads:

    // Old (V1 — separate endpoints per type)
    await client.fileUpload.uploadPDF(files, options);
    await client.fileUpload.uploadExcel(files, options);
    
    // New (V2 — single endpoint for all types)
    await client.v2Upload.upload([...pdfFiles, ...excelFiles], options);
  3. Manage LLM config via client.v2Config:

    // New in V2 — not available in V1
    await client.v2Config.update({ temperature: 0.7, llm_model: 'gpt-4o' });

From NatyClient to NatyLangChainClient

  1. Update client initialization:

    // Old
    const client = new NatyClient({
      baseURL: 'http://localhost:8000',
      token: 'bearer-token',
      tenantId: 'tenant-id',
    });
    
    // New
    const client = new NatyLangChainClient({
      baseURL: 'http://localhost:8000',
      apiKey: 'nlck-key_prod_1',  // Changed from token
      tenantId: 'tenant-id',
    });
  2. Update API URLs:

    • Documents: /documents/v1/documents
    • QA: /v1/qa (unchanged)
    • Custom QA: /v1/qa/custom (unchanged)
  3. Remove query parameters:

    • No longer need to pass llm, embeddings, retriever query parameters
    • These are now handled by the API internally
  4. Update error handling:

    // Old
    catch (error) {
      console.error(error.message);
    }
    
    // New
    catch (error) {
      if (error instanceof NatyLangChainError) {
        console.error('Status:', error.status);
        console.error('Code:', error.code);
        console.error('Message:', error.message);
      }
    }

Development

# Install dependencies
npm install

# Build
npm run build

# Run tests
npm test

# Run integration tests only
npm test -- tests/integration.test.ts

# Lint
npm run lint

# Format code
npm run format

Examples

See the examples/ directory for complete usage examples:

  • examples/usage-example.ts - Comprehensive usage demonstration

Contributing

  1. Fork the repository
  2. Create your feature branch (git checkout -b feature/amazing-feature)
  3. Commit your changes (git commit -m 'Add some amazing feature')
  4. Push to the branch (git push origin feature/amazing-feature)
  5. Open a Pull Request

License

MIT License