cencori 1.0.3 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -134,10 +134,12 @@ console.log(response.embeddings[0]); // [0.1, 0.2, ...]
134
134
 
135
135
  ### Image Generation
136
136
 
137
+ Generate images from text prompts using multiple providers:
138
+
137
139
  ```typescript
138
140
  const response = await cencori.ai.generateImage({
139
141
  prompt: 'A futuristic city at sunset with flying cars',
140
- model: 'dall-e-3',
142
+ model: 'gpt-image-1.5', // Best text rendering, top ELO rating
141
143
  size: '1024x1024',
142
144
  quality: 'hd'
143
145
  });
@@ -145,6 +147,12 @@ const response = await cencori.ai.generateImage({
145
147
  console.log(response.images[0].url); // https://...
146
148
  ```
147
149
 
150
+ **Supported Models:**
151
+ | Provider | Models | Description |
152
+ |----------|--------|-------------|
153
+ | **OpenAI** | `gpt-image-1.5`, `gpt-image-1`, `dall-e-3`, `dall-e-2` | Text rendering, creative |
154
+ | **Google** | `gemini-3-pro-image`, `imagen-3` | High photorealism |
155
+
148
156
  ## Framework Integrations
149
157
 
150
158
  ### Vercel AI SDK
@@ -205,15 +213,29 @@ await cencori.workflow.trigger('data-enrichment', {
205
213
  });
206
214
  ```
207
215
 
208
- ### Storage
216
+ ### Memory (Context Store)
209
217
 
210
218
  ```typescript
211
- // 🚧 Coming Soon
212
- const results = await cencori.storage.vectors.search('query', {
213
- limit: 5
219
+ // Store a memory with auto-embedding
220
+ await cencori.memory.store({
221
+ namespace: 'docs',
222
+ content: 'Refund policy allows returns within 30 days',
223
+ metadata: { category: 'policy' }
214
224
  });
215
225
 
216
- await cencori.storage.knowledge.query('What is our refund policy?');
226
+ // Semantic search
227
+ const results = await cencori.memory.search({
228
+ namespace: 'docs',
229
+ query: 'what is the refund policy?',
230
+ limit: 5
231
+ });
232
+
233
+ // RAG helper
234
+ const response = await cencori.ai.rag({
235
+ model: 'gpt-4o',
236
+ messages: [{ role: 'user', content: 'What is our refund policy?' }],
237
+ namespace: 'docs'
238
+ });
217
239
  ```
218
240
 
219
241
  ## Why Cencori?
@@ -95,6 +95,75 @@ declare class AINamespace {
95
95
  * console.log(response.images[0].url);
96
96
  */
97
97
  generateImage(request: ImageGenerationRequest): Promise<ImageGenerationResponse>;
98
+ /**
99
+ * RAG (Retrieval-Augmented Generation) - Chat with automatic memory context
100
+ *
101
+ * Searches your memory namespace for relevant context and includes it
102
+ * in the prompt automatically. Returns the AI response along with sources.
103
+ *
104
+ * @example
105
+ * const response = await cencori.ai.rag({
106
+ * model: 'gpt-4o',
107
+ * messages: [{ role: 'user', content: 'What are our company policies?' }],
108
+ * namespace: 'company-docs',
109
+ * limit: 5, // number of memories to retrieve
110
+ * });
111
+ * console.log(response.message.content);
112
+ * console.log(response.sources); // retrieved context
113
+ */
114
+ rag(request: RagRequest): Promise<RagResponse>;
115
+ /**
116
+ * Stream RAG responses with automatic memory context
117
+ *
118
+ * @example
119
+ * for await (const chunk of cencori.ai.ragStream({ model: 'gpt-4o', messages, namespace: 'docs' })) {
120
+ * if (chunk.type === 'sources') console.log('Sources:', chunk.sources);
121
+ * if (chunk.type === 'content') process.stdout.write(chunk.delta);
122
+ * }
123
+ */
124
+ ragStream(request: RagRequest): AsyncGenerator<RagStreamChunk, void, unknown>;
125
+ }
126
+ interface RagRequest {
127
+ model: string;
128
+ messages: Array<{
129
+ role: 'system' | 'user' | 'assistant';
130
+ content: string;
131
+ }>;
132
+ namespace: string;
133
+ temperature?: number;
134
+ maxTokens?: number;
135
+ limit?: number;
136
+ threshold?: number;
137
+ includeSources?: boolean;
138
+ }
139
+ interface RagResponse {
140
+ message: {
141
+ role: string;
142
+ content: string;
143
+ };
144
+ model: string;
145
+ provider: string;
146
+ usage: {
147
+ promptTokens: number;
148
+ completionTokens: number;
149
+ totalTokens: number;
150
+ };
151
+ sources?: Array<{
152
+ content: string;
153
+ metadata: Record<string, unknown>;
154
+ similarity: number;
155
+ }>;
156
+ latencyMs: number;
157
+ }
158
+ interface RagStreamChunk {
159
+ type: 'sources' | 'content';
160
+ delta?: string;
161
+ finish_reason?: string;
162
+ sources?: Array<{
163
+ content: string;
164
+ metadata: Record<string, unknown>;
165
+ similarity: number;
166
+ }>;
98
167
  }
99
168
 
100
- export { AINamespace, type StreamChunk };
169
+ export { AINamespace, type RagRequest, type RagResponse, type RagStreamChunk, type StreamChunk };
@@ -95,6 +95,75 @@ declare class AINamespace {
95
95
  * console.log(response.images[0].url);
96
96
  */
97
97
  generateImage(request: ImageGenerationRequest): Promise<ImageGenerationResponse>;
98
+ /**
99
+ * RAG (Retrieval-Augmented Generation) - Chat with automatic memory context
100
+ *
101
+ * Searches your memory namespace for relevant context and includes it
102
+ * in the prompt automatically. Returns the AI response along with sources.
103
+ *
104
+ * @example
105
+ * const response = await cencori.ai.rag({
106
+ * model: 'gpt-4o',
107
+ * messages: [{ role: 'user', content: 'What are our company policies?' }],
108
+ * namespace: 'company-docs',
109
+ * limit: 5, // number of memories to retrieve
110
+ * });
111
+ * console.log(response.message.content);
112
+ * console.log(response.sources); // retrieved context
113
+ */
114
+ rag(request: RagRequest): Promise<RagResponse>;
115
+ /**
116
+ * Stream RAG responses with automatic memory context
117
+ *
118
+ * @example
119
+ * for await (const chunk of cencori.ai.ragStream({ model: 'gpt-4o', messages, namespace: 'docs' })) {
120
+ * if (chunk.type === 'sources') console.log('Sources:', chunk.sources);
121
+ * if (chunk.type === 'content') process.stdout.write(chunk.delta);
122
+ * }
123
+ */
124
+ ragStream(request: RagRequest): AsyncGenerator<RagStreamChunk, void, unknown>;
125
+ }
126
+ interface RagRequest {
127
+ model: string;
128
+ messages: Array<{
129
+ role: 'system' | 'user' | 'assistant';
130
+ content: string;
131
+ }>;
132
+ namespace: string;
133
+ temperature?: number;
134
+ maxTokens?: number;
135
+ limit?: number;
136
+ threshold?: number;
137
+ includeSources?: boolean;
138
+ }
139
+ interface RagResponse {
140
+ message: {
141
+ role: string;
142
+ content: string;
143
+ };
144
+ model: string;
145
+ provider: string;
146
+ usage: {
147
+ promptTokens: number;
148
+ completionTokens: number;
149
+ totalTokens: number;
150
+ };
151
+ sources?: Array<{
152
+ content: string;
153
+ metadata: Record<string, unknown>;
154
+ similarity: number;
155
+ }>;
156
+ latencyMs: number;
157
+ }
158
+ interface RagStreamChunk {
159
+ type: 'sources' | 'content';
160
+ delta?: string;
161
+ finish_reason?: string;
162
+ sources?: Array<{
163
+ content: string;
164
+ metadata: Record<string, unknown>;
165
+ similarity: number;
166
+ }>;
98
167
  }
99
168
 
100
- export { AINamespace, type StreamChunk };
169
+ export { AINamespace, type RagRequest, type RagResponse, type RagStreamChunk, type StreamChunk };
package/dist/ai/index.js CHANGED
@@ -312,6 +312,131 @@ var AINamespace = class {
312
312
  provider: data.provider
313
313
  };
314
314
  }
315
+ /**
316
+ * RAG (Retrieval-Augmented Generation) - Chat with automatic memory context
317
+ *
318
+ * Searches your memory namespace for relevant context and includes it
319
+ * in the prompt automatically. Returns the AI response along with sources.
320
+ *
321
+ * @example
322
+ * const response = await cencori.ai.rag({
323
+ * model: 'gpt-4o',
324
+ * messages: [{ role: 'user', content: 'What are our company policies?' }],
325
+ * namespace: 'company-docs',
326
+ * limit: 5, // number of memories to retrieve
327
+ * });
328
+ * console.log(response.message.content);
329
+ * console.log(response.sources); // retrieved context
330
+ */
331
+ async rag(request) {
332
+ const response = await fetch(`${this.config.baseUrl}/api/ai/rag`, {
333
+ method: "POST",
334
+ headers: {
335
+ "CENCORI_API_KEY": this.config.apiKey,
336
+ "Content-Type": "application/json",
337
+ ...this.config.headers
338
+ },
339
+ body: JSON.stringify({
340
+ model: request.model,
341
+ messages: request.messages,
342
+ namespace: request.namespace,
343
+ temperature: request.temperature,
344
+ maxTokens: request.maxTokens,
345
+ limit: request.limit ?? 5,
346
+ threshold: request.threshold ?? 0.5,
347
+ include_sources: request.includeSources ?? true,
348
+ stream: false
349
+ })
350
+ });
351
+ if (!response.ok) {
352
+ const errorData = await response.json().catch(() => ({ error: "Unknown error" }));
353
+ throw new Error(`Cencori API error: ${errorData.error || response.statusText}`);
354
+ }
355
+ const data = await response.json();
356
+ return {
357
+ message: {
358
+ role: "assistant",
359
+ content: data.message.content
360
+ },
361
+ model: data.model,
362
+ provider: data.provider,
363
+ usage: {
364
+ promptTokens: data.usage.prompt_tokens,
365
+ completionTokens: data.usage.completion_tokens,
366
+ totalTokens: data.usage.total_tokens
367
+ },
368
+ sources: data.sources?.map((s) => ({
369
+ content: s.content,
370
+ metadata: s.metadata,
371
+ similarity: s.similarity
372
+ })),
373
+ latencyMs: data.latency_ms
374
+ };
375
+ }
376
+ /**
377
+ * Stream RAG responses with automatic memory context
378
+ *
379
+ * @example
380
+ * for await (const chunk of cencori.ai.ragStream({ model: 'gpt-4o', messages, namespace: 'docs' })) {
381
+ * if (chunk.type === 'sources') console.log('Sources:', chunk.sources);
382
+ * if (chunk.type === 'content') process.stdout.write(chunk.delta);
383
+ * }
384
+ */
385
+ async *ragStream(request) {
386
+ const response = await fetch(`${this.config.baseUrl}/api/ai/rag`, {
387
+ method: "POST",
388
+ headers: {
389
+ "CENCORI_API_KEY": this.config.apiKey,
390
+ "Content-Type": "application/json",
391
+ ...this.config.headers
392
+ },
393
+ body: JSON.stringify({
394
+ model: request.model,
395
+ messages: request.messages,
396
+ namespace: request.namespace,
397
+ temperature: request.temperature,
398
+ maxTokens: request.maxTokens,
399
+ limit: request.limit ?? 5,
400
+ threshold: request.threshold ?? 0.5,
401
+ include_sources: request.includeSources ?? true,
402
+ stream: true
403
+ })
404
+ });
405
+ if (!response.ok) {
406
+ const errorData = await response.json().catch(() => ({ error: "Unknown error" }));
407
+ throw new Error(`Cencori API error: ${errorData.error || response.statusText}`);
408
+ }
409
+ if (!response.body) {
410
+ throw new Error("Response body is null");
411
+ }
412
+ const reader = response.body.getReader();
413
+ const decoder = new TextDecoder();
414
+ let buffer = "";
415
+ try {
416
+ while (true) {
417
+ const { done, value } = await reader.read();
418
+ if (done) break;
419
+ buffer += decoder.decode(value, { stream: true });
420
+ const lines = buffer.split("\n");
421
+ buffer = lines.pop() || "";
422
+ for (const line of lines) {
423
+ if (line.trim() === "") continue;
424
+ if (!line.startsWith("data: ")) continue;
425
+ const data = line.slice(6);
426
+ if (data === "[DONE]") {
427
+ return;
428
+ }
429
+ try {
430
+ const chunk = JSON.parse(data);
431
+ yield chunk;
432
+ } catch {
433
+ }
434
+ }
435
+ }
436
+ } finally {
437
+ reader.releaseLock();
438
+ }
439
+ }
315
440
  };
316
441
  // Annotate the CommonJS export names for ESM import in node:
317
442
  0 && (module.exports = {
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/ai/index.ts"],"sourcesContent":["/**\n * AI Gateway - Chat, Completions, Embeddings, and Streaming\n * \n * @example\n * const response = await cencori.ai.chat({\n * model: 'gpt-4o',\n * messages: [{ role: 'user', content: 'Hello!' }]\n * });\n */\n\nimport type {\n CencoriConfig,\n ChatRequest,\n ChatResponse,\n CompletionRequest,\n EmbeddingRequest,\n EmbeddingResponse,\n GenerateObjectRequest,\n GenerateObjectResponse,\n ImageGenerationRequest,\n ImageGenerationResponse,\n ToolCall\n} from '../types';\n\n// API Response types\ninterface OpenAIChatResponse {\n id: string;\n model: string;\n choices?: Array<{\n message?: {\n content?: string;\n tool_calls?: Array<{\n id: string;\n type: 'function';\n function: {\n name: string;\n arguments: string;\n };\n }>;\n };\n finish_reason?: string;\n }>;\n usage?: {\n prompt_tokens?: number;\n completion_tokens?: number;\n total_tokens?: number;\n };\n}\n\ninterface OpenAIEmbeddingResponse {\n model: string;\n data?: Array<{\n embedding: number[];\n }>;\n usage?: {\n total_tokens?: number;\n };\n}\n\n/**\n * Stream chunk from chat stream\n */\nexport interface StreamChunk {\n delta: string;\n finish_reason?: 'stop' | 'length' | 'content_filter' | 'tool_calls' | 'error';\n /** Tool calls in progress during streaming */\n toolCalls?: ToolCall[];\n /** Error message if the stream encountered an error */\n error?: string;\n}\n\nexport class AINamespace {\n private config: Required<CencoriConfig>;\n\n constructor(config: Required<CencoriConfig>) {\n this.config = config;\n }\n\n /**\n * Create a chat completion\n * \n * @example\n * const response = await cencori.ai.chat({\n * model: 'gpt-4o',\n * messages: [{ role: 'user', content: 'Hello!' }]\n * });\n */\n async chat(request: ChatRequest): Promise<ChatResponse> {\n const response = await fetch(`${this.config.baseUrl}/api/ai/chat`, {\n method: 'POST',\n headers: {\n 'CENCORI_API_KEY': this.config.apiKey,\n 'Content-Type': 'application/json',\n ...this.config.headers,\n },\n body: JSON.stringify({\n model: request.model,\n messages: request.messages,\n temperature: request.temperature,\n maxTokens: request.maxTokens,\n stream: false,\n tools: request.tools,\n toolChoice: request.toolChoice,\n }),\n });\n\n if (!response.ok) {\n const errorData = await response.json().catch(() => ({ error: 'Unknown error' })) as { error?: string };\n throw new Error(`Cencori API error: ${errorData.error || response.statusText}`);\n }\n\n const data = await response.json() as OpenAIChatResponse;\n\n const choice = data.choices?.[0];\n const toolCalls = choice?.message?.tool_calls?.map(tc => ({\n id: tc.id,\n type: tc.type as 'function',\n function: {\n name: tc.function.name,\n arguments: tc.function.arguments,\n },\n }));\n\n return {\n id: data.id,\n model: data.model,\n content: choice?.message?.content ?? '',\n toolCalls,\n finishReason: choice?.finish_reason as ChatResponse['finishReason'],\n usage: {\n promptTokens: data.usage?.prompt_tokens ?? 0,\n completionTokens: data.usage?.completion_tokens ?? 0,\n totalTokens: data.usage?.total_tokens ?? 0,\n },\n };\n }\n\n /**\n * Stream chat completions\n * Returns an async generator that yields chunks as they arrive\n * \n * @example\n * for await (const chunk of cencori.ai.chatStream({ model: 'gpt-4o', messages })) {\n * process.stdout.write(chunk.delta);\n * }\n */\n async *chatStream(request: ChatRequest): AsyncGenerator<StreamChunk, void, unknown> {\n const response = await fetch(`${this.config.baseUrl}/api/ai/chat`, {\n method: 'POST',\n headers: {\n 'CENCORI_API_KEY': this.config.apiKey,\n 'Content-Type': 'application/json',\n ...this.config.headers,\n },\n body: JSON.stringify({\n model: request.model,\n messages: request.messages,\n temperature: request.temperature,\n maxTokens: request.maxTokens,\n stream: true,\n tools: request.tools,\n toolChoice: request.toolChoice,\n }),\n });\n\n if (!response.ok) {\n const errorData = await response.json().catch(() => ({ error: 'Unknown error' })) as { error?: string };\n throw new Error(`Cencori API error: ${errorData.error || response.statusText}`);\n }\n\n if (!response.body) {\n throw new Error('Response body is null');\n }\n\n const reader = response.body.getReader();\n const decoder = new TextDecoder();\n let buffer = '';\n\n try {\n while (true) {\n const { done, value } = await reader.read();\n\n if (done) break;\n\n buffer += decoder.decode(value, { stream: true });\n const lines = buffer.split('\\n');\n\n // Keep the last incomplete line in the buffer\n buffer = lines.pop() || '';\n\n for (const line of lines) {\n if (line.trim() === '') continue;\n if (!line.startsWith('data: ')) continue;\n\n const data = line.slice(6); // Remove 'data: ' prefix\n\n if (data === '[DONE]') {\n return;\n }\n\n try {\n const chunk = JSON.parse(data) as StreamChunk;\n yield chunk;\n } catch {\n // Skip malformed JSON\n }\n }\n }\n } finally {\n reader.releaseLock();\n }\n }\n\n /**\n * Create a text completion\n * \n * @example\n * const response = await cencori.ai.completions({\n * model: 'gpt-4o',\n * prompt: 'Write a haiku about coding'\n * });\n */\n async completions(request: CompletionRequest): Promise<ChatResponse> {\n // Convert to chat format internally\n return this.chat({\n model: request.model,\n messages: [{ role: 'user', content: request.prompt }],\n temperature: request.temperature,\n maxTokens: request.maxTokens,\n });\n }\n\n /**\n * Create embeddings\n * \n * @example\n * const response = await cencori.ai.embeddings({\n * model: 'text-embedding-3-small',\n * input: 'Hello world'\n * });\n */\n async embeddings(request: EmbeddingRequest): Promise<EmbeddingResponse> {\n const response = await fetch(`${this.config.baseUrl}/api/ai/embeddings`, {\n method: 'POST',\n headers: {\n 'CENCORI_API_KEY': this.config.apiKey,\n 'Content-Type': 'application/json',\n ...this.config.headers,\n },\n body: JSON.stringify({\n model: request.model,\n input: request.input,\n }),\n });\n\n if (!response.ok) {\n const errorData = await response.json().catch(() => ({ error: 'Unknown error' })) as { error?: string };\n throw new Error(`Cencori API error: ${errorData.error || response.statusText}`);\n }\n\n const data = await response.json() as OpenAIEmbeddingResponse;\n\n return {\n model: data.model,\n embeddings: data.data?.map((d) => d.embedding) ?? [],\n usage: {\n totalTokens: data.usage?.total_tokens ?? 0,\n },\n };\n }\n\n /**\n * Generate structured output with JSON schema\n * \n * @example\n * const response = await cencori.ai.generateObject({\n * model: 'gpt-4o',\n * prompt: 'Generate a user profile',\n * schema: {\n * type: 'object',\n * properties: {\n * name: { type: 'string' },\n * age: { type: 'number' }\n * },\n * required: ['name', 'age']\n * }\n * });\n * console.log(response.object); // { name: 'John', age: 30 }\n */\n async generateObject<T = unknown>(request: GenerateObjectRequest): Promise<GenerateObjectResponse<T>> {\n // Build messages from prompt or use provided messages\n const messages = request.messages ?? [\n { role: 'user' as const, content: request.prompt ?? '' }\n ];\n\n // Use function calling to enforce JSON schema\n const response = await fetch(`${this.config.baseUrl}/api/ai/chat`, {\n method: 'POST',\n headers: {\n 'CENCORI_API_KEY': this.config.apiKey,\n 'Content-Type': 'application/json',\n ...this.config.headers,\n },\n body: JSON.stringify({\n model: request.model,\n messages,\n temperature: request.temperature,\n maxTokens: request.maxTokens,\n stream: false,\n tools: [{\n type: 'function',\n function: {\n name: request.schemaName ?? 'generate_object',\n description: request.schemaDescription ?? 'Generate a structured object matching the schema',\n parameters: request.schema,\n },\n }],\n toolChoice: {\n type: 'function',\n function: { name: request.schemaName ?? 'generate_object' },\n },\n }),\n });\n\n if (!response.ok) {\n const errorData = await response.json().catch(() => ({ error: 'Unknown error' })) as { error?: string };\n throw new Error(`Cencori API error: ${errorData.error || response.statusText}`);\n }\n\n const data = await response.json() as OpenAIChatResponse;\n const toolCall = data.choices?.[0]?.message?.tool_calls?.[0];\n\n if (!toolCall) {\n throw new Error('Model did not return structured output');\n }\n\n let parsedObject: T;\n try {\n parsedObject = JSON.parse(toolCall.function.arguments) as T;\n } catch {\n throw new Error('Failed to parse structured output as JSON');\n }\n\n return {\n object: parsedObject,\n usage: {\n promptTokens: data.usage?.prompt_tokens ?? 0,\n completionTokens: data.usage?.completion_tokens ?? 0,\n totalTokens: data.usage?.total_tokens ?? 0,\n },\n };\n }\n\n /**\n * Generate images from a text prompt\n * \n * @example\n * const response = await cencori.ai.generateImage({\n * prompt: 'A futuristic city at sunset',\n * model: 'dall-e-3',\n * size: '1024x1024'\n * });\n * console.log(response.images[0].url);\n */\n async generateImage(request: ImageGenerationRequest): Promise<ImageGenerationResponse> {\n const response = await fetch(`${this.config.baseUrl}/api/ai/images/generate`, {\n method: 'POST',\n headers: {\n 'CENCORI_API_KEY': this.config.apiKey,\n 'Content-Type': 'application/json',\n ...this.config.headers,\n },\n body: JSON.stringify({\n prompt: request.prompt,\n model: request.model ?? 'dall-e-3',\n n: request.n,\n size: request.size,\n quality: request.quality,\n style: request.style,\n responseFormat: request.responseFormat,\n }),\n });\n\n if (!response.ok) {\n const errorData = await response.json().catch(() => ({ error: 'Unknown error' })) as { error?: string; message?: string };\n throw new Error(`Cencori API error: ${errorData.message || errorData.error || response.statusText}`);\n }\n\n const data = await response.json() as {\n images: Array<{ url?: string; b64_json?: string; revisedPrompt?: string }>;\n model: string;\n provider: string;\n };\n\n return {\n images: data.images.map(img => ({\n url: img.url,\n b64Json: img.b64_json,\n revisedPrompt: img.revisedPrompt,\n })),\n model: data.model,\n provider: data.provider,\n };\n }\n}\n\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../src/ai/index.ts"],"sourcesContent":["/**\n * AI Gateway - Chat, Completions, Embeddings, and Streaming\n * \n * @example\n * const response = await cencori.ai.chat({\n * model: 'gpt-4o',\n * messages: [{ role: 'user', content: 'Hello!' }]\n * });\n */\n\nimport type {\n CencoriConfig,\n ChatRequest,\n ChatResponse,\n CompletionRequest,\n EmbeddingRequest,\n EmbeddingResponse,\n GenerateObjectRequest,\n GenerateObjectResponse,\n ImageGenerationRequest,\n ImageGenerationResponse,\n ToolCall\n} from '../types';\n\n// API Response types\ninterface OpenAIChatResponse {\n id: string;\n model: string;\n choices?: Array<{\n message?: {\n content?: string;\n tool_calls?: Array<{\n id: string;\n type: 'function';\n function: {\n name: string;\n arguments: string;\n };\n }>;\n };\n finish_reason?: string;\n }>;\n usage?: {\n prompt_tokens?: number;\n completion_tokens?: number;\n total_tokens?: number;\n };\n}\n\ninterface OpenAIEmbeddingResponse {\n model: string;\n data?: Array<{\n embedding: number[];\n }>;\n usage?: {\n total_tokens?: number;\n };\n}\n\n/**\n * Stream chunk from chat stream\n */\nexport interface StreamChunk {\n delta: string;\n finish_reason?: 'stop' | 'length' | 'content_filter' | 'tool_calls' | 'error';\n /** Tool calls in progress during streaming */\n toolCalls?: ToolCall[];\n /** Error message if the stream encountered an error */\n error?: string;\n}\n\nexport class AINamespace {\n private config: Required<CencoriConfig>;\n\n constructor(config: Required<CencoriConfig>) {\n this.config = config;\n }\n\n /**\n * Create a chat completion\n * \n * @example\n * const response = await cencori.ai.chat({\n * model: 'gpt-4o',\n * messages: [{ role: 'user', content: 'Hello!' }]\n * });\n */\n async chat(request: ChatRequest): Promise<ChatResponse> {\n const response = await fetch(`${this.config.baseUrl}/api/ai/chat`, {\n method: 'POST',\n headers: {\n 'CENCORI_API_KEY': this.config.apiKey,\n 'Content-Type': 'application/json',\n ...this.config.headers,\n },\n body: JSON.stringify({\n model: request.model,\n messages: request.messages,\n temperature: request.temperature,\n maxTokens: request.maxTokens,\n stream: false,\n tools: request.tools,\n toolChoice: request.toolChoice,\n }),\n });\n\n if (!response.ok) {\n const errorData = await response.json().catch(() => ({ error: 'Unknown error' })) as { error?: string };\n throw new Error(`Cencori API error: ${errorData.error || response.statusText}`);\n }\n\n const data = await response.json() as OpenAIChatResponse;\n\n const choice = data.choices?.[0];\n const toolCalls = choice?.message?.tool_calls?.map(tc => ({\n id: tc.id,\n type: tc.type as 'function',\n function: {\n name: tc.function.name,\n arguments: tc.function.arguments,\n },\n }));\n\n return {\n id: data.id,\n model: data.model,\n content: choice?.message?.content ?? '',\n toolCalls,\n finishReason: choice?.finish_reason as ChatResponse['finishReason'],\n usage: {\n promptTokens: data.usage?.prompt_tokens ?? 0,\n completionTokens: data.usage?.completion_tokens ?? 0,\n totalTokens: data.usage?.total_tokens ?? 0,\n },\n };\n }\n\n /**\n * Stream chat completions\n * Returns an async generator that yields chunks as they arrive\n * \n * @example\n * for await (const chunk of cencori.ai.chatStream({ model: 'gpt-4o', messages })) {\n * process.stdout.write(chunk.delta);\n * }\n */\n async *chatStream(request: ChatRequest): AsyncGenerator<StreamChunk, void, unknown> {\n const response = await fetch(`${this.config.baseUrl}/api/ai/chat`, {\n method: 'POST',\n headers: {\n 'CENCORI_API_KEY': this.config.apiKey,\n 'Content-Type': 'application/json',\n ...this.config.headers,\n },\n body: JSON.stringify({\n model: request.model,\n messages: request.messages,\n temperature: request.temperature,\n maxTokens: request.maxTokens,\n stream: true,\n tools: request.tools,\n toolChoice: request.toolChoice,\n }),\n });\n\n if (!response.ok) {\n const errorData = await response.json().catch(() => ({ error: 'Unknown error' })) as { error?: string };\n throw new Error(`Cencori API error: ${errorData.error || response.statusText}`);\n }\n\n if (!response.body) {\n throw new Error('Response body is null');\n }\n\n const reader = response.body.getReader();\n const decoder = new TextDecoder();\n let buffer = '';\n\n try {\n while (true) {\n const { done, value } = await reader.read();\n\n if (done) break;\n\n buffer += decoder.decode(value, { stream: true });\n const lines = buffer.split('\\n');\n\n // Keep the last incomplete line in the buffer\n buffer = lines.pop() || '';\n\n for (const line of lines) {\n if (line.trim() === '') continue;\n if (!line.startsWith('data: ')) continue;\n\n const data = line.slice(6); // Remove 'data: ' prefix\n\n if (data === '[DONE]') {\n return;\n }\n\n try {\n const chunk = JSON.parse(data) as StreamChunk;\n yield chunk;\n } catch {\n // Skip malformed JSON\n }\n }\n }\n } finally {\n reader.releaseLock();\n }\n }\n\n /**\n * Create a text completion\n * \n * @example\n * const response = await cencori.ai.completions({\n * model: 'gpt-4o',\n * prompt: 'Write a haiku about coding'\n * });\n */\n async completions(request: CompletionRequest): Promise<ChatResponse> {\n // Convert to chat format internally\n return this.chat({\n model: request.model,\n messages: [{ role: 'user', content: request.prompt }],\n temperature: request.temperature,\n maxTokens: request.maxTokens,\n });\n }\n\n /**\n * Create embeddings\n * \n * @example\n * const response = await cencori.ai.embeddings({\n * model: 'text-embedding-3-small',\n * input: 'Hello world'\n * });\n */\n async embeddings(request: EmbeddingRequest): Promise<EmbeddingResponse> {\n const response = await fetch(`${this.config.baseUrl}/api/ai/embeddings`, {\n method: 'POST',\n headers: {\n 'CENCORI_API_KEY': this.config.apiKey,\n 'Content-Type': 'application/json',\n ...this.config.headers,\n },\n body: JSON.stringify({\n model: request.model,\n input: request.input,\n }),\n });\n\n if (!response.ok) {\n const errorData = await response.json().catch(() => ({ error: 'Unknown error' })) as { error?: string };\n throw new Error(`Cencori API error: ${errorData.error || response.statusText}`);\n }\n\n const data = await response.json() as OpenAIEmbeddingResponse;\n\n return {\n model: data.model,\n embeddings: data.data?.map((d) => d.embedding) ?? [],\n usage: {\n totalTokens: data.usage?.total_tokens ?? 0,\n },\n };\n }\n\n /**\n * Generate structured output with JSON schema\n * \n * @example\n * const response = await cencori.ai.generateObject({\n * model: 'gpt-4o',\n * prompt: 'Generate a user profile',\n * schema: {\n * type: 'object',\n * properties: {\n * name: { type: 'string' },\n * age: { type: 'number' }\n * },\n * required: ['name', 'age']\n * }\n * });\n * console.log(response.object); // { name: 'John', age: 30 }\n */\n async generateObject<T = unknown>(request: GenerateObjectRequest): Promise<GenerateObjectResponse<T>> {\n // Build messages from prompt or use provided messages\n const messages = request.messages ?? [\n { role: 'user' as const, content: request.prompt ?? '' }\n ];\n\n // Use function calling to enforce JSON schema\n const response = await fetch(`${this.config.baseUrl}/api/ai/chat`, {\n method: 'POST',\n headers: {\n 'CENCORI_API_KEY': this.config.apiKey,\n 'Content-Type': 'application/json',\n ...this.config.headers,\n },\n body: JSON.stringify({\n model: request.model,\n messages,\n temperature: request.temperature,\n maxTokens: request.maxTokens,\n stream: false,\n tools: [{\n type: 'function',\n function: {\n name: request.schemaName ?? 'generate_object',\n description: request.schemaDescription ?? 'Generate a structured object matching the schema',\n parameters: request.schema,\n },\n }],\n toolChoice: {\n type: 'function',\n function: { name: request.schemaName ?? 'generate_object' },\n },\n }),\n });\n\n if (!response.ok) {\n const errorData = await response.json().catch(() => ({ error: 'Unknown error' })) as { error?: string };\n throw new Error(`Cencori API error: ${errorData.error || response.statusText}`);\n }\n\n const data = await response.json() as OpenAIChatResponse;\n const toolCall = data.choices?.[0]?.message?.tool_calls?.[0];\n\n if (!toolCall) {\n throw new Error('Model did not return structured output');\n }\n\n let parsedObject: T;\n try {\n parsedObject = JSON.parse(toolCall.function.arguments) as T;\n } catch {\n throw new Error('Failed to parse structured output as JSON');\n }\n\n return {\n object: parsedObject,\n usage: {\n promptTokens: data.usage?.prompt_tokens ?? 0,\n completionTokens: data.usage?.completion_tokens ?? 0,\n totalTokens: data.usage?.total_tokens ?? 0,\n },\n };\n }\n\n /**\n * Generate images from a text prompt\n * \n * @example\n * const response = await cencori.ai.generateImage({\n * prompt: 'A futuristic city at sunset',\n * model: 'dall-e-3',\n * size: '1024x1024'\n * });\n * console.log(response.images[0].url);\n */\n async generateImage(request: ImageGenerationRequest): Promise<ImageGenerationResponse> {\n const response = await fetch(`${this.config.baseUrl}/api/ai/images/generate`, {\n method: 'POST',\n headers: {\n 'CENCORI_API_KEY': this.config.apiKey,\n 'Content-Type': 'application/json',\n ...this.config.headers,\n },\n body: JSON.stringify({\n prompt: request.prompt,\n model: request.model ?? 'dall-e-3',\n n: request.n,\n size: request.size,\n quality: request.quality,\n style: request.style,\n responseFormat: request.responseFormat,\n }),\n });\n\n if (!response.ok) {\n const errorData = await response.json().catch(() => ({ error: 'Unknown error' })) as { error?: string; message?: string };\n throw new Error(`Cencori API error: ${errorData.message || errorData.error || response.statusText}`);\n }\n\n const data = await response.json() as {\n images: Array<{ url?: string; b64_json?: string; revisedPrompt?: string }>;\n model: string;\n provider: string;\n };\n\n return {\n images: data.images.map(img => ({\n url: img.url,\n b64Json: img.b64_json,\n revisedPrompt: img.revisedPrompt,\n })),\n model: data.model,\n provider: data.provider,\n };\n }\n\n /**\n * RAG (Retrieval-Augmented Generation) - Chat with automatic memory context\n * \n * Searches your memory namespace for relevant context and includes it\n * in the prompt automatically. Returns the AI response along with sources.\n * \n * @example\n * const response = await cencori.ai.rag({\n * model: 'gpt-4o',\n * messages: [{ role: 'user', content: 'What are our company policies?' }],\n * namespace: 'company-docs',\n * limit: 5, // number of memories to retrieve\n * });\n * console.log(response.message.content);\n * console.log(response.sources); // retrieved context\n */\n async rag(request: RagRequest): Promise<RagResponse> {\n const response = await fetch(`${this.config.baseUrl}/api/ai/rag`, {\n method: 'POST',\n headers: {\n 'CENCORI_API_KEY': this.config.apiKey,\n 'Content-Type': 'application/json',\n ...this.config.headers,\n },\n body: JSON.stringify({\n model: request.model,\n messages: request.messages,\n namespace: request.namespace,\n temperature: request.temperature,\n maxTokens: request.maxTokens,\n limit: request.limit ?? 5,\n threshold: request.threshold ?? 0.5,\n include_sources: request.includeSources ?? true,\n stream: false,\n }),\n });\n\n if (!response.ok) {\n const errorData = await response.json().catch(() => ({ error: 'Unknown error' })) as { error?: string };\n throw new Error(`Cencori API error: ${errorData.error || response.statusText}`);\n }\n\n const data = await response.json() as RagApiResponse;\n\n return {\n message: {\n role: 'assistant',\n content: data.message.content,\n },\n model: data.model,\n provider: data.provider,\n usage: {\n promptTokens: data.usage.prompt_tokens,\n completionTokens: data.usage.completion_tokens,\n totalTokens: data.usage.total_tokens,\n },\n sources: data.sources?.map(s => ({\n content: s.content,\n metadata: s.metadata,\n similarity: s.similarity,\n })),\n latencyMs: data.latency_ms,\n };\n }\n\n /**\n * Stream RAG responses with automatic memory context\n * \n * @example\n * for await (const chunk of cencori.ai.ragStream({ model: 'gpt-4o', messages, namespace: 'docs' })) {\n * if (chunk.type === 'sources') console.log('Sources:', chunk.sources);\n * if (chunk.type === 'content') process.stdout.write(chunk.delta);\n * }\n */\n async *ragStream(request: RagRequest): AsyncGenerator<RagStreamChunk, void, unknown> {\n const response = await fetch(`${this.config.baseUrl}/api/ai/rag`, {\n method: 'POST',\n headers: {\n 'CENCORI_API_KEY': this.config.apiKey,\n 'Content-Type': 'application/json',\n ...this.config.headers,\n },\n body: JSON.stringify({\n model: request.model,\n messages: request.messages,\n namespace: request.namespace,\n temperature: request.temperature,\n maxTokens: request.maxTokens,\n limit: request.limit ?? 5,\n threshold: request.threshold ?? 0.5,\n include_sources: request.includeSources ?? true,\n stream: true,\n }),\n });\n\n if (!response.ok) {\n const errorData = await response.json().catch(() => ({ error: 'Unknown error' })) as { error?: string };\n throw new Error(`Cencori API error: ${errorData.error || response.statusText}`);\n }\n\n if (!response.body) {\n throw new Error('Response body is null');\n }\n\n const reader = response.body.getReader();\n const decoder = new TextDecoder();\n let buffer = '';\n\n try {\n while (true) {\n const { done, value } = await reader.read();\n\n if (done) break;\n\n buffer += decoder.decode(value, { stream: true });\n const lines = buffer.split('\\n');\n buffer = lines.pop() || '';\n\n for (const line of lines) {\n if (line.trim() === '') continue;\n if (!line.startsWith('data: ')) continue;\n\n const data = line.slice(6);\n\n if (data === '[DONE]') {\n return;\n }\n\n try {\n const chunk = JSON.parse(data) as RagStreamChunk;\n yield chunk;\n } catch {\n // Skip malformed JSON\n }\n }\n }\n } finally {\n reader.releaseLock();\n }\n }\n}\n\n// RAG Types\nexport interface RagRequest {\n model: string;\n messages: Array<{ role: 'system' | 'user' | 'assistant'; content: string }>;\n namespace: string;\n temperature?: number;\n maxTokens?: number;\n limit?: number;\n threshold?: number;\n includeSources?: boolean;\n}\n\nexport interface RagResponse {\n message: { role: string; content: string };\n model: string;\n provider: string;\n usage: {\n promptTokens: number;\n completionTokens: number;\n totalTokens: number;\n };\n sources?: Array<{\n content: string;\n metadata: Record<string, unknown>;\n similarity: number;\n }>;\n latencyMs: number;\n}\n\nexport interface RagStreamChunk {\n type: 'sources' | 'content';\n delta?: string;\n finish_reason?: string;\n sources?: Array<{\n content: string;\n metadata: Record<string, unknown>;\n similarity: number;\n }>;\n}\n\ninterface RagApiResponse {\n message: { role: string; content: string };\n model: string;\n provider: string;\n usage: {\n prompt_tokens: number;\n completion_tokens: number;\n total_tokens: number;\n };\n sources?: Array<{\n content: string;\n metadata: Record<string, unknown>;\n similarity: number;\n }>;\n latency_ms: number;\n}\n"],"mappings":"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","names":[]}
package/dist/ai/index.mjs CHANGED
@@ -288,6 +288,131 @@ var AINamespace = class {
288
288
  provider: data.provider
289
289
  };
290
290
  }
291
+ /**
292
+ * RAG (Retrieval-Augmented Generation) - Chat with automatic memory context
293
+ *
294
+ * Searches your memory namespace for relevant context and includes it
295
+ * in the prompt automatically. Returns the AI response along with sources.
296
+ *
297
+ * @example
298
+ * const response = await cencori.ai.rag({
299
+ * model: 'gpt-4o',
300
+ * messages: [{ role: 'user', content: 'What are our company policies?' }],
301
+ * namespace: 'company-docs',
302
+ * limit: 5, // number of memories to retrieve
303
+ * });
304
+ * console.log(response.message.content);
305
+ * console.log(response.sources); // retrieved context
306
+ */
307
+ async rag(request) {
308
+ const response = await fetch(`${this.config.baseUrl}/api/ai/rag`, {
309
+ method: "POST",
310
+ headers: {
311
+ "CENCORI_API_KEY": this.config.apiKey,
312
+ "Content-Type": "application/json",
313
+ ...this.config.headers
314
+ },
315
+ body: JSON.stringify({
316
+ model: request.model,
317
+ messages: request.messages,
318
+ namespace: request.namespace,
319
+ temperature: request.temperature,
320
+ maxTokens: request.maxTokens,
321
+ limit: request.limit ?? 5,
322
+ threshold: request.threshold ?? 0.5,
323
+ include_sources: request.includeSources ?? true,
324
+ stream: false
325
+ })
326
+ });
327
+ if (!response.ok) {
328
+ const errorData = await response.json().catch(() => ({ error: "Unknown error" }));
329
+ throw new Error(`Cencori API error: ${errorData.error || response.statusText}`);
330
+ }
331
+ const data = await response.json();
332
+ return {
333
+ message: {
334
+ role: "assistant",
335
+ content: data.message.content
336
+ },
337
+ model: data.model,
338
+ provider: data.provider,
339
+ usage: {
340
+ promptTokens: data.usage.prompt_tokens,
341
+ completionTokens: data.usage.completion_tokens,
342
+ totalTokens: data.usage.total_tokens
343
+ },
344
+ sources: data.sources?.map((s) => ({
345
+ content: s.content,
346
+ metadata: s.metadata,
347
+ similarity: s.similarity
348
+ })),
349
+ latencyMs: data.latency_ms
350
+ };
351
+ }
352
+ /**
353
+ * Stream RAG responses with automatic memory context
354
+ *
355
+ * @example
356
+ * for await (const chunk of cencori.ai.ragStream({ model: 'gpt-4o', messages, namespace: 'docs' })) {
357
+ * if (chunk.type === 'sources') console.log('Sources:', chunk.sources);
358
+ * if (chunk.type === 'content') process.stdout.write(chunk.delta);
359
+ * }
360
+ */
361
+ async *ragStream(request) {
362
+ const response = await fetch(`${this.config.baseUrl}/api/ai/rag`, {
363
+ method: "POST",
364
+ headers: {
365
+ "CENCORI_API_KEY": this.config.apiKey,
366
+ "Content-Type": "application/json",
367
+ ...this.config.headers
368
+ },
369
+ body: JSON.stringify({
370
+ model: request.model,
371
+ messages: request.messages,
372
+ namespace: request.namespace,
373
+ temperature: request.temperature,
374
+ maxTokens: request.maxTokens,
375
+ limit: request.limit ?? 5,
376
+ threshold: request.threshold ?? 0.5,
377
+ include_sources: request.includeSources ?? true,
378
+ stream: true
379
+ })
380
+ });
381
+ if (!response.ok) {
382
+ const errorData = await response.json().catch(() => ({ error: "Unknown error" }));
383
+ throw new Error(`Cencori API error: ${errorData.error || response.statusText}`);
384
+ }
385
+ if (!response.body) {
386
+ throw new Error("Response body is null");
387
+ }
388
+ const reader = response.body.getReader();
389
+ const decoder = new TextDecoder();
390
+ let buffer = "";
391
+ try {
392
+ while (true) {
393
+ const { done, value } = await reader.read();
394
+ if (done) break;
395
+ buffer += decoder.decode(value, { stream: true });
396
+ const lines = buffer.split("\n");
397
+ buffer = lines.pop() || "";
398
+ for (const line of lines) {
399
+ if (line.trim() === "") continue;
400
+ if (!line.startsWith("data: ")) continue;
401
+ const data = line.slice(6);
402
+ if (data === "[DONE]") {
403
+ return;
404
+ }
405
+ try {
406
+ const chunk = JSON.parse(data);
407
+ yield chunk;
408
+ } catch {
409
+ }
410
+ }
411
+ }
412
+ } finally {
413
+ reader.releaseLock();
414
+ }
415
+ }
291
416
  };
292
417
  export {
293
418
  AINamespace