npm package: @natyapp/langchain
Description: TypeScript client for Naty LangChain API with async file uploads, real-time progress tracking, and Chat support
Author: NatyApp
Latest version: 1.2.0

# 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

- **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

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

## Quick Start

### Initialize Client

```typescript
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

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

## 💬 Chat API with Streaming

### Professional Chat with Hybrid RAG

```typescript
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

```typescript
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

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

### Upload PDF Files

```typescript
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

```typescript
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

```typescript
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

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

## ⚡ Custom QA with Streaming

```typescript
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

```typescript
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

The following client usage:

```typescript
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:

```bash
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

```typescript
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

#### QA API

```typescript
// 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

```typescript
// 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

```typescript
// 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`:

```typescript
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):

```typescript
// 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

```typescript
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

1. **Update client initialization:**
   ```typescript
   // 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:**
   ```typescript
   // 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

```bash
# 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