@krutai/ai-provider 0.2.12 → 0.2.13

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/AI_REFERENCE.md CHANGED
@@ -19,7 +19,6 @@ AI Flow:
19
19
  → POST {serverUrl}/validate (key validation)
20
20
  → POST {serverUrl}/generate (single response)
21
21
  → POST {serverUrl}/stream (SSE streaming)
22
- → POST {serverUrl}/chat (multi-turn)
23
22
  → Your deployed LangChain server
24
23
  ```
25
24
 
@@ -43,8 +42,7 @@ packages/ai-provider/
43
42
  |---|---|---|---|
44
43
  | `/validate` | POST | `{ apiKey }` | `{ valid: true/false, message? }` |
45
44
  | `/generate` | POST | `{ prompt, model, system?, maxTokens?, temperature? }` | `{ text/content/message: string }` |
46
- | `/stream` | POST | `{ prompt, model, system?, maxTokens?, temperature? }` | SSE stream `data: <chunk>` |
47
- | `/chat` | POST | `{ messages, model, maxTokens?, temperature? }` | `{ text/content/message: string }` |
45
+ | `/stream` | POST | `{ messages, model, system?, maxTokens?, temperature? }` | SSE stream `data: <chunk>` |
48
46
 
49
47
  All AI endpoints receive `Authorization: Bearer <apiKey>` and `x-api-key: <apiKey>` headers.
50
48
 
@@ -62,8 +60,38 @@ const ai = krutAI({
62
60
  });
63
61
 
64
62
  await ai.initialize(); // validates key with server
63
+ ```
64
+
65
+ ### 1. `chat(prompt: string)` — Simple String Prompts
66
+ Used to get a single, non-streaming text response from a string prompt.
67
+
68
+ ```typescript
69
+ const text = await ai.chat('Write a poem about TypeScript');
70
+ console.log(text);
71
+ ```
72
+
73
+ ### 2. `streamChatResponse(messages: ChatMessage[])` — Multi-Turn & Streaming
74
+ Used for multi-turn conversations and streaming responses. It takes an array of `ChatMessage` objects instead of a single string. It returns a raw fetch `Response` containing the `text/event-stream` body.
75
+
76
+ Ideal for proxying streams (e.g., Next.js API routes) down to your backend component or manually reading the `ReadableStream`.
77
+
78
+ ```typescript
79
+ // Example: Proxying in a Next.js route
80
+ export async function POST(req: Request) {
81
+ const { messages } = await req.json();
82
+
83
+ // ai.streamChatResponse accepts an array of messages:
84
+ // [{ role: 'user', content: '...' }, ...]
85
+ return await ai.streamChatResponse(messages);
86
+ }
65
87
 
66
- const text = await ai.generate('Hello!');
88
+ // Example: Manual Node environment stream reading
89
+ const response = await ai.streamChatResponse([
90
+ { role: 'system', content: 'You are a helpful assistant.' },
91
+ { role: 'user', content: 'Tell me a story' }
92
+ ]);
93
+ const reader = response.body?.getReader();
94
+ // Use a TextDecoder to parse value chunks...
67
95
  ```
68
96
 
69
97
  ### `KrutAIProvider` class ← FULL CLASS API
@@ -83,12 +111,8 @@ await ai.initialize();
83
111
 
84
112
  **Methods:**
85
113
  - `initialize(): Promise<void>` — validates key against server, marks provider ready
86
- - `generate(prompt, opts?): Promise<string>` — single response (non-streaming)
87
- - `stream(prompt, opts?)` — `AsyncGenerator<string>` — SSE-based streaming
88
- - `streamResponse(prompt, opts?)` — `Promise<Response>` — returns the raw fetch Response for proxying
89
- - `streamChat(messages, opts?)` — `AsyncGenerator<string>` — SSE multi-turn streaming
90
- - `streamChatResponse(messages, opts?)` — `Promise<Response>` — returns the raw fetch Response for proxying
91
- - `chat(messages, opts?): Promise<string>` — multi-turn conversation
114
+ - `chat(prompt, opts?): Promise<string>` — single response (non-streaming)
115
+ - `streamChatResponse(messages, opts?)` — `Promise<Response>` — returns the raw fetch Response for proxying (SSE multi-turn streaming)
92
116
  - `getModel(): string` — active model name
93
117
  - `isInitialized(): boolean`
94
118
 
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # @krutai/ai-provider
2
2
 
3
- AI provider package for KrutAI — fetch-based client for your deployed LangChain server.
3
+ AI provider package for KrutAI — fetch-based client form our deployed server.
4
4
 
5
5
  ## Features
6
6
 
@@ -30,13 +30,13 @@ const ai = krutAI({
30
30
  await ai.initialize(); // validates key with your server
31
31
 
32
32
  // Single response
33
- const text = await ai.generate('Write a poem about TypeScript');
33
+ const text = await ai.chat('Write a poem about TypeScript');
34
34
  console.log(text);
35
35
  ```
36
36
 
37
37
  ## Usage
38
38
 
39
- ### Generate (single response)
39
+ ### Chat (single response)
40
40
 
41
41
  ```typescript
42
42
  const ai = krutAI({
@@ -47,7 +47,7 @@ const ai = krutAI({
47
47
 
48
48
  await ai.initialize();
49
49
 
50
- const text = await ai.generate('Explain async/await in JavaScript', {
50
+ const text = await ai.chat('Explain async/await in JavaScript', {
51
51
  system: 'You are a helpful coding tutor.',
52
52
  maxTokens: 500,
53
53
  temperature: 0.7,
@@ -56,21 +56,6 @@ const text = await ai.generate('Explain async/await in JavaScript', {
56
56
  console.log(text);
57
57
  ```
58
58
 
59
- ### Streaming
60
-
61
- ```typescript
62
- const ai = krutAI({
63
- apiKey: process.env.KRUTAI_API_KEY!,
64
- // uses http://localhost:8000 by default
65
- });
66
-
67
- await ai.initialize();
68
-
69
- // stream() is an async generator
70
- for await (const chunk of ai.stream('Tell me a short story')) {
71
- process.stdout.write(chunk);
72
- }
73
- ```
74
59
 
75
60
  ### Multi-turn Chat
76
61
 
@@ -116,29 +101,11 @@ const response = await ai.chat([
116
101
  });
117
102
  ```
118
103
 
119
- ### Streaming Multi-turn Chat
120
-
121
- ```typescript
122
- const ai = krutAI({
123
- apiKey: process.env.KRUTAI_API_KEY!,
124
- });
125
-
126
- await ai.initialize();
127
-
128
- const stream = ai.streamChat([
129
- { role: 'user', content: 'What is the capital of France?' },
130
- { role: 'assistant', content: 'Paris.' },
131
- { role: 'user', content: 'What is it famous for?' },
132
- ]);
104
+ ### Streaming (Proxying SSE Streams)
133
105
 
134
- for await (const chunk of stream) {
135
- process.stdout.write(chunk);
136
- }
137
- ```
106
+ If you are building an API route (e.g., in Next.js) and want to pipe the true Server-Sent Events (SSE) stream down to your backend component, use `streamChatResponse`.
138
107
 
139
- ### Proxying Streams to the Frontend (Next.js / API Routes)
140
-
141
- If you are building an API route (e.g., in Next.js) and want to pipe the true Server-Sent Events (SSE) stream down to your frontend component, use the `Response` variants:
108
+ `streamChatResponse` returns the raw fetch `Response` object containing the `text/event-stream` body from deployed LangChain server.
142
109
 
143
110
  ```typescript
144
111
  // app/api/chat/route.ts
@@ -148,11 +115,30 @@ export async function POST(req: Request) {
148
115
  // Returns the native fetch Response (with text/event-stream headers and body)
149
116
  const response = await ai.streamChatResponse(messages);
150
117
 
151
- // Proxy it directly to the frontend!
118
+ // Proxy it directly to the backend!
152
119
  return response;
153
120
  }
154
121
  ```
155
122
 
123
+ If you need to consume the stream in a Node environment rather than proxying it, you can read from the response body directly:
124
+
125
+ ```typescript
126
+ const response = await ai.streamChatResponse([
127
+ { role: 'user', content: 'Tell me a short story' }
128
+ ]);
129
+
130
+ const reader = response.body?.getReader();
131
+ const decoder = new TextDecoder();
132
+
133
+ if (reader) {
134
+ while (true) {
135
+ const { done, value } = await reader.read();
136
+ if (done) break;
137
+ process.stdout.write(decoder.decode(value, { stream: true }));
138
+ }
139
+ }
140
+ ```
141
+
156
142
  ### Skip validation (useful for tests)
157
143
 
158
144
  ```typescript
@@ -163,7 +149,7 @@ const ai = krutAI({
163
149
  });
164
150
 
165
151
  // No need to call initialize() when validateOnInit is false
166
- const text = await ai.generate('Hello!');
152
+ const text = await ai.chat('Hello!');
167
153
  ```
168
154
 
169
155
  ## Server API Contract
@@ -174,8 +160,7 @@ Your LangChain server must expose these endpoints:
174
160
  |---|---|---|---|
175
161
  | `/validate` | POST | `x-api-key` header | `{ "apiKey": "..." }` |
176
162
  | `/generate` | POST | `Authorization: Bearer <key>` | `{ "prompt": "...", "model": "...", ... }` |
177
- | `/stream` | POST | `Authorization: Bearer <key>` | `{ "prompt": "...", "model": "...", ... }` |
178
- | `/chat` | POST | `Authorization: Bearer <key>` | `{ "messages": [...], "model": "...", ... }` |
163
+ | `/stream` | POST | `Authorization: Bearer <key>` | `{ "messages": [...], "model": "...", ... }` |
179
164
 
180
165
  **Validation response:** `{ "valid": true }` or `{ "valid": false, "message": "reason" }`
181
166
 
package/dist/index.d.mts CHANGED
@@ -146,57 +146,6 @@ declare class KrutAIProvider {
146
146
  private assertInitialized;
147
147
  /** Common request headers sent to the server on every AI call. */
148
148
  private authHeaders;
149
- /**
150
- * Generate a response for a prompt (non-streaming).
151
- *
152
- * Calls: POST {serverUrl}/generate
153
- * Body: { prompt, model, system?, maxTokens?, temperature? }
154
- * Expected response: { text: string } or { content: string } or { message: string }
155
- *
156
- * @param prompt - The user prompt string
157
- * @param options - Optional overrides (model, system, maxTokens, temperature)
158
- * @returns The assistant's response text
159
- */
160
- generate(prompt: string, options?: GenerateOptions): Promise<string>;
161
- /**
162
- * Generate a streaming response for a prompt via Server-Sent Events (SSE).
163
- *
164
- * Calls: POST {serverUrl}/stream
165
- * Body: { prompt, model, system?, maxTokens?, temperature? }
166
- * Expected response: `text/event-stream` with `data: <chunk>` lines.
167
- *
168
- * @param prompt - The user prompt string
169
- * @param options - Optional overrides (model, system, maxTokens, temperature)
170
- * @returns An async generator yielding string chunks from the server
171
- *
172
- * @example
173
- * ```typescript
174
- * const stream = ai.stream('Tell me a story');
175
- * for await (const chunk of stream) {
176
- * process.stdout.write(chunk);
177
- * }
178
- * ```
179
- */
180
- stream(prompt: string, options?: GenerateOptions): AsyncGenerator<string>;
181
- /**
182
- * Similar to stream() but returns the raw fetch Response object.
183
- * Useful when you want to proxy the Server-Sent Events stream directly to a frontend client
184
- * (e.g., returning this directly from a Next.js API route).
185
- *
186
- * @param prompt - The user prompt string
187
- * @param options - Optional overrides (model, system, maxTokens, temperature)
188
- * @returns A Promise resolving to the native fetch Response
189
- */
190
- streamResponse(prompt: string, options?: GenerateOptions): Promise<Response>;
191
- /**
192
- * Multi-turn conversation streaming: pass a full message history.
193
- * Calls POST /stream with the full { messages } payload.
194
- *
195
- * @param messages - Full conversation history
196
- * @param options - Optional overrides (model, maxTokens, temperature)
197
- * @returns An async generator yielding string chunks from the server
198
- */
199
- streamChat(messages: ChatMessage[], options?: GenerateOptions): AsyncGenerator<string>;
200
149
  /**
201
150
  * Similar to streamChat() but returns the raw fetch Response object.
202
151
  * Useful for proxying the Server-Sent Events stream directly to a frontend client.
@@ -207,17 +156,17 @@ declare class KrutAIProvider {
207
156
  */
208
157
  streamChatResponse(messages: ChatMessage[], options?: GenerateOptions): Promise<Response>;
209
158
  /**
210
- * Multi-turn conversation: pass a full message history.
159
+ * Generate a response for a prompt (non-streaming).
211
160
  *
212
- * Calls: POST {serverUrl}/chat
213
- * Body: { messages, model, maxTokens?, temperature? }
161
+ * Calls: POST {serverUrl}/generate
162
+ * Body: { prompt, model, system?, maxTokens?, temperature? }
214
163
  * Expected response: { text: string } or { content: string } or { message: string }
215
164
  *
216
- * @param messages - Full conversation history
217
- * @param options - Optional overrides (model, maxTokens, temperature)
165
+ * @param prompt - The user prompt string
166
+ * @param options - Optional overrides (model, system, maxTokens, temperature)
218
167
  * @returns The assistant's response text
219
168
  */
220
- chat(messages: ChatMessage[], options?: GenerateOptions): Promise<string>;
169
+ chat(prompt: string, options?: GenerateOptions): Promise<string>;
221
170
  }
222
171
 
223
172
  /**
@@ -237,7 +186,7 @@ declare class KrutAIProvider {
237
186
  *
238
187
  * await ai.initialize(); // validates key with server
239
188
  *
240
- * const text = await ai.generate('Write a poem about TypeScript');
189
+ * const text = await ai.chat('Write a poem about TypeScript');
241
190
  * console.log(text);
242
191
  * ```
243
192
  *
@@ -249,7 +198,7 @@ declare class KrutAIProvider {
249
198
  * model: 'gpt-4o',
250
199
  * });
251
200
  * await ai.initialize();
252
- * const text = await ai.generate('Hello!');
201
+ * const text = await ai.chat('Hello!');
253
202
  * ```
254
203
  *
255
204
  * @example Streaming
@@ -260,10 +209,8 @@ declare class KrutAIProvider {
260
209
  * });
261
210
  * await ai.initialize();
262
211
  *
263
- * const stream = ai.stream('Tell me a story');
264
- * for await (const chunk of stream) {
265
- * process.stdout.write(chunk);
266
- * }
212
+ * const response = await ai.streamChatResponse([{ role: 'user', content: 'Tell me a story' }]);
213
+ * // Example assumes you handle the SSE stream from the response body
267
214
  * ```
268
215
  *
269
216
  * @packageDocumentation
@@ -287,7 +234,7 @@ declare class KrutAIProvider {
287
234
  * });
288
235
  *
289
236
  * await ai.initialize();
290
- * const text = await ai.generate('Hello!');
237
+ * const text = await ai.chat('Hello!');
291
238
  * ```
292
239
  */
293
240
  declare function krutAI(config: KrutAIProviderConfig & {
package/dist/index.d.ts CHANGED
@@ -146,57 +146,6 @@ declare class KrutAIProvider {
146
146
  private assertInitialized;
147
147
  /** Common request headers sent to the server on every AI call. */
148
148
  private authHeaders;
149
- /**
150
- * Generate a response for a prompt (non-streaming).
151
- *
152
- * Calls: POST {serverUrl}/generate
153
- * Body: { prompt, model, system?, maxTokens?, temperature? }
154
- * Expected response: { text: string } or { content: string } or { message: string }
155
- *
156
- * @param prompt - The user prompt string
157
- * @param options - Optional overrides (model, system, maxTokens, temperature)
158
- * @returns The assistant's response text
159
- */
160
- generate(prompt: string, options?: GenerateOptions): Promise<string>;
161
- /**
162
- * Generate a streaming response for a prompt via Server-Sent Events (SSE).
163
- *
164
- * Calls: POST {serverUrl}/stream
165
- * Body: { prompt, model, system?, maxTokens?, temperature? }
166
- * Expected response: `text/event-stream` with `data: <chunk>` lines.
167
- *
168
- * @param prompt - The user prompt string
169
- * @param options - Optional overrides (model, system, maxTokens, temperature)
170
- * @returns An async generator yielding string chunks from the server
171
- *
172
- * @example
173
- * ```typescript
174
- * const stream = ai.stream('Tell me a story');
175
- * for await (const chunk of stream) {
176
- * process.stdout.write(chunk);
177
- * }
178
- * ```
179
- */
180
- stream(prompt: string, options?: GenerateOptions): AsyncGenerator<string>;
181
- /**
182
- * Similar to stream() but returns the raw fetch Response object.
183
- * Useful when you want to proxy the Server-Sent Events stream directly to a frontend client
184
- * (e.g., returning this directly from a Next.js API route).
185
- *
186
- * @param prompt - The user prompt string
187
- * @param options - Optional overrides (model, system, maxTokens, temperature)
188
- * @returns A Promise resolving to the native fetch Response
189
- */
190
- streamResponse(prompt: string, options?: GenerateOptions): Promise<Response>;
191
- /**
192
- * Multi-turn conversation streaming: pass a full message history.
193
- * Calls POST /stream with the full { messages } payload.
194
- *
195
- * @param messages - Full conversation history
196
- * @param options - Optional overrides (model, maxTokens, temperature)
197
- * @returns An async generator yielding string chunks from the server
198
- */
199
- streamChat(messages: ChatMessage[], options?: GenerateOptions): AsyncGenerator<string>;
200
149
  /**
201
150
  * Similar to streamChat() but returns the raw fetch Response object.
202
151
  * Useful for proxying the Server-Sent Events stream directly to a frontend client.
@@ -207,17 +156,17 @@ declare class KrutAIProvider {
207
156
  */
208
157
  streamChatResponse(messages: ChatMessage[], options?: GenerateOptions): Promise<Response>;
209
158
  /**
210
- * Multi-turn conversation: pass a full message history.
159
+ * Generate a response for a prompt (non-streaming).
211
160
  *
212
- * Calls: POST {serverUrl}/chat
213
- * Body: { messages, model, maxTokens?, temperature? }
161
+ * Calls: POST {serverUrl}/generate
162
+ * Body: { prompt, model, system?, maxTokens?, temperature? }
214
163
  * Expected response: { text: string } or { content: string } or { message: string }
215
164
  *
216
- * @param messages - Full conversation history
217
- * @param options - Optional overrides (model, maxTokens, temperature)
165
+ * @param prompt - The user prompt string
166
+ * @param options - Optional overrides (model, system, maxTokens, temperature)
218
167
  * @returns The assistant's response text
219
168
  */
220
- chat(messages: ChatMessage[], options?: GenerateOptions): Promise<string>;
169
+ chat(prompt: string, options?: GenerateOptions): Promise<string>;
221
170
  }
222
171
 
223
172
  /**
@@ -237,7 +186,7 @@ declare class KrutAIProvider {
237
186
  *
238
187
  * await ai.initialize(); // validates key with server
239
188
  *
240
- * const text = await ai.generate('Write a poem about TypeScript');
189
+ * const text = await ai.chat('Write a poem about TypeScript');
241
190
  * console.log(text);
242
191
  * ```
243
192
  *
@@ -249,7 +198,7 @@ declare class KrutAIProvider {
249
198
  * model: 'gpt-4o',
250
199
  * });
251
200
  * await ai.initialize();
252
- * const text = await ai.generate('Hello!');
201
+ * const text = await ai.chat('Hello!');
253
202
  * ```
254
203
  *
255
204
  * @example Streaming
@@ -260,10 +209,8 @@ declare class KrutAIProvider {
260
209
  * });
261
210
  * await ai.initialize();
262
211
  *
263
- * const stream = ai.stream('Tell me a story');
264
- * for await (const chunk of stream) {
265
- * process.stdout.write(chunk);
266
- * }
212
+ * const response = await ai.streamChatResponse([{ role: 'user', content: 'Tell me a story' }]);
213
+ * // Example assumes you handle the SSE stream from the response body
267
214
  * ```
268
215
  *
269
216
  * @packageDocumentation
@@ -287,7 +234,7 @@ declare class KrutAIProvider {
287
234
  * });
288
235
  *
289
236
  * await ai.initialize();
290
- * const text = await ai.generate('Hello!');
237
+ * const text = await ai.chat('Hello!');
291
238
  * ```
292
239
  */
293
240
  declare function krutAI(config: KrutAIProviderConfig & {
package/dist/index.js CHANGED
@@ -67,241 +67,6 @@ var KrutAIProvider = class {
67
67
  // ---------------------------------------------------------------------------
68
68
  // Public AI Methods
69
69
  // ---------------------------------------------------------------------------
70
- /**
71
- * Generate a response for a prompt (non-streaming).
72
- *
73
- * Calls: POST {serverUrl}/generate
74
- * Body: { prompt, model, system?, maxTokens?, temperature? }
75
- * Expected response: { text: string } or { content: string } or { message: string }
76
- *
77
- * @param prompt - The user prompt string
78
- * @param options - Optional overrides (model, system, maxTokens, temperature)
79
- * @returns The assistant's response text
80
- */
81
- async generate(prompt, options = {}) {
82
- this.assertInitialized();
83
- const model = options.model ?? this.resolvedModel;
84
- const response = await fetch(`${this.serverUrl}/generate`, {
85
- method: "POST",
86
- headers: this.authHeaders(),
87
- body: JSON.stringify({
88
- prompt,
89
- model,
90
- ...options.system !== void 0 ? { system: options.system } : {},
91
- ...options.images !== void 0 ? { images: options.images } : {},
92
- ...options.documents !== void 0 ? { documents: options.documents } : {},
93
- ...options.pdf !== void 0 ? { pdf: options.pdf } : {},
94
- ...options.maxTokens !== void 0 ? { maxTokens: options.maxTokens } : {},
95
- ...options.temperature !== void 0 ? { temperature: options.temperature } : {}
96
- })
97
- });
98
- if (!response.ok) {
99
- let errorMessage = `AI server returned HTTP ${response.status} for /generate`;
100
- try {
101
- const errorData = await response.json();
102
- if (errorData?.error) errorMessage = errorData.error;
103
- else if (errorData?.message) errorMessage = errorData.message;
104
- } catch {
105
- }
106
- throw new Error(errorMessage);
107
- }
108
- const data = await response.json();
109
- return data.text ?? data.content ?? data.message ?? "";
110
- }
111
- /**
112
- * Generate a streaming response for a prompt via Server-Sent Events (SSE).
113
- *
114
- * Calls: POST {serverUrl}/stream
115
- * Body: { prompt, model, system?, maxTokens?, temperature? }
116
- * Expected response: `text/event-stream` with `data: <chunk>` lines.
117
- *
118
- * @param prompt - The user prompt string
119
- * @param options - Optional overrides (model, system, maxTokens, temperature)
120
- * @returns An async generator yielding string chunks from the server
121
- *
122
- * @example
123
- * ```typescript
124
- * const stream = ai.stream('Tell me a story');
125
- * for await (const chunk of stream) {
126
- * process.stdout.write(chunk);
127
- * }
128
- * ```
129
- */
130
- async *stream(prompt, options = {}) {
131
- this.assertInitialized();
132
- const model = options.model ?? this.resolvedModel;
133
- const response = await fetch(`${this.serverUrl}/stream`, {
134
- method: "POST",
135
- headers: {
136
- ...this.authHeaders(),
137
- Accept: "text/event-stream"
138
- },
139
- body: JSON.stringify({
140
- prompt,
141
- model,
142
- ...options.system !== void 0 ? { system: options.system } : {},
143
- ...options.images !== void 0 ? { images: options.images } : {},
144
- ...options.documents !== void 0 ? { documents: options.documents } : {},
145
- ...options.pdf !== void 0 ? { pdf: options.pdf } : {},
146
- ...options.maxTokens !== void 0 ? { maxTokens: options.maxTokens } : {},
147
- ...options.temperature !== void 0 ? { temperature: options.temperature } : {}
148
- })
149
- });
150
- if (!response.ok) {
151
- let errorMessage = `AI server returned HTTP ${response.status} for /stream`;
152
- try {
153
- const errorData = await response.json();
154
- if (errorData?.error) errorMessage = errorData.error;
155
- else if (errorData?.message) errorMessage = errorData.message;
156
- } catch {
157
- }
158
- throw new Error(errorMessage);
159
- }
160
- if (!response.body) {
161
- throw new Error("AI server returned no response body for /stream");
162
- }
163
- const reader = response.body.getReader();
164
- const decoder = new TextDecoder();
165
- let buffer = "";
166
- try {
167
- while (true) {
168
- const { done, value } = await reader.read();
169
- if (done) break;
170
- buffer += decoder.decode(value, { stream: true });
171
- const lines = buffer.split("\n");
172
- buffer = lines.pop() ?? "";
173
- for (const line of lines) {
174
- if (line.startsWith("data: ")) {
175
- const raw = line.slice(6).trim();
176
- if (raw === "[DONE]") return;
177
- try {
178
- const parsed = JSON.parse(raw);
179
- const chunk = parsed.text ?? parsed.content ?? parsed.delta?.content ?? "";
180
- if (chunk) yield chunk;
181
- } catch {
182
- if (raw) yield raw;
183
- }
184
- }
185
- }
186
- }
187
- } finally {
188
- reader.releaseLock();
189
- }
190
- }
191
- /**
192
- * Similar to stream() but returns the raw fetch Response object.
193
- * Useful when you want to proxy the Server-Sent Events stream directly to a frontend client
194
- * (e.g., returning this directly from a Next.js API route).
195
- *
196
- * @param prompt - The user prompt string
197
- * @param options - Optional overrides (model, system, maxTokens, temperature)
198
- * @returns A Promise resolving to the native fetch Response
199
- */
200
- async streamResponse(prompt, options = {}) {
201
- this.assertInitialized();
202
- const model = options.model ?? this.resolvedModel;
203
- const response = await fetch(`${this.serverUrl}/stream`, {
204
- method: "POST",
205
- headers: {
206
- ...this.authHeaders(),
207
- Accept: "text/event-stream"
208
- },
209
- body: JSON.stringify({
210
- prompt,
211
- model,
212
- ...options.system !== void 0 ? { system: options.system } : {},
213
- ...options.images !== void 0 ? { images: options.images } : {},
214
- ...options.documents !== void 0 ? { documents: options.documents } : {},
215
- ...options.pdf !== void 0 ? { pdf: options.pdf } : {},
216
- ...options.maxTokens !== void 0 ? { maxTokens: options.maxTokens } : {},
217
- ...options.temperature !== void 0 ? { temperature: options.temperature } : {}
218
- })
219
- });
220
- if (!response.ok) {
221
- let errorMessage = `AI server returned HTTP ${response.status} for /stream`;
222
- try {
223
- const errorData = await response.json();
224
- if (errorData?.error) errorMessage = errorData.error;
225
- else if (errorData?.message) errorMessage = errorData.message;
226
- } catch {
227
- }
228
- throw new Error(errorMessage);
229
- }
230
- console.log(response);
231
- return response;
232
- }
233
- /**
234
- * Multi-turn conversation streaming: pass a full message history.
235
- * Calls POST /stream with the full { messages } payload.
236
- *
237
- * @param messages - Full conversation history
238
- * @param options - Optional overrides (model, maxTokens, temperature)
239
- * @returns An async generator yielding string chunks from the server
240
- */
241
- async *streamChat(messages, options = {}) {
242
- this.assertInitialized();
243
- if (!messages.length) {
244
- throw new Error("Messages array cannot be empty for streamChat");
245
- }
246
- const model = options.model ?? this.resolvedModel;
247
- const response = await fetch(`${this.serverUrl}/stream`, {
248
- method: "POST",
249
- headers: {
250
- ...this.authHeaders(),
251
- Accept: "text/event-stream"
252
- },
253
- body: JSON.stringify({
254
- messages,
255
- model,
256
- ...options.system !== void 0 ? { system: options.system } : {},
257
- ...options.images !== void 0 ? { images: options.images } : {},
258
- ...options.documents !== void 0 ? { documents: options.documents } : {},
259
- ...options.pdf !== void 0 ? { pdf: options.pdf } : {},
260
- ...options.maxTokens !== void 0 ? { maxTokens: options.maxTokens } : {},
261
- ...options.temperature !== void 0 ? { temperature: options.temperature } : {}
262
- })
263
- });
264
- if (!response.ok) {
265
- let errorMessage = `AI server returned HTTP ${response.status} for /stream`;
266
- try {
267
- const errorData = await response.json();
268
- if (errorData?.error) errorMessage = errorData.error;
269
- else if (errorData?.message) errorMessage = errorData.message;
270
- } catch {
271
- }
272
- throw new Error(errorMessage);
273
- }
274
- if (!response.body) {
275
- throw new Error("AI server returned no response body for /stream");
276
- }
277
- const reader = response.body.getReader();
278
- const decoder = new TextDecoder();
279
- let buffer = "";
280
- try {
281
- while (true) {
282
- const { done, value } = await reader.read();
283
- if (done) break;
284
- buffer += decoder.decode(value, { stream: true });
285
- const lines = buffer.split("\n");
286
- buffer = lines.pop() ?? "";
287
- for (const line of lines) {
288
- if (line.startsWith("data: ")) {
289
- const raw = line.slice(6).trim();
290
- if (raw === "[DONE]") return;
291
- try {
292
- const parsed = JSON.parse(raw);
293
- const chunk = parsed.text ?? parsed.content ?? parsed.delta?.content ?? "";
294
- if (chunk) yield chunk;
295
- } catch {
296
- if (raw) yield raw;
297
- }
298
- }
299
- }
300
- }
301
- } finally {
302
- reader.releaseLock();
303
- }
304
- }
305
70
  /**
306
71
  * Similar to streamChat() but returns the raw fetch Response object.
307
72
  * Useful for proxying the Server-Sent Events stream directly to a frontend client.
@@ -346,25 +111,26 @@ var KrutAIProvider = class {
346
111
  return response;
347
112
  }
348
113
  /**
349
- * Multi-turn conversation: pass a full message history.
114
+ * Generate a response for a prompt (non-streaming).
350
115
  *
351
- * Calls: POST {serverUrl}/chat
352
- * Body: { messages, model, maxTokens?, temperature? }
116
+ * Calls: POST {serverUrl}/generate
117
+ * Body: { prompt, model, system?, maxTokens?, temperature? }
353
118
  * Expected response: { text: string } or { content: string } or { message: string }
354
119
  *
355
- * @param messages - Full conversation history
356
- * @param options - Optional overrides (model, maxTokens, temperature)
120
+ * @param prompt - The user prompt string
121
+ * @param options - Optional overrides (model, system, maxTokens, temperature)
357
122
  * @returns The assistant's response text
358
123
  */
359
- async chat(messages, options = {}) {
124
+ async chat(prompt, options = {}) {
360
125
  this.assertInitialized();
361
126
  const model = options.model ?? this.resolvedModel;
362
- const response = await fetch(`${this.serverUrl}/chat`, {
127
+ const response = await fetch(`${this.serverUrl}/generate`, {
363
128
  method: "POST",
364
129
  headers: this.authHeaders(),
365
130
  body: JSON.stringify({
366
- messages,
131
+ prompt,
367
132
  model,
133
+ ...options.system !== void 0 ? { system: options.system } : {},
368
134
  ...options.images !== void 0 ? { images: options.images } : {},
369
135
  ...options.documents !== void 0 ? { documents: options.documents } : {},
370
136
  ...options.pdf !== void 0 ? { pdf: options.pdf } : {},
@@ -373,7 +139,7 @@ var KrutAIProvider = class {
373
139
  })
374
140
  });
375
141
  if (!response.ok) {
376
- let errorMessage = `AI server returned HTTP ${response.status} for /chat`;
142
+ let errorMessage = `AI server returned HTTP ${response.status} for /generate`;
377
143
  try {
378
144
  const errorData = await response.json();
379
145
  if (errorData?.error) errorMessage = errorData.error;
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/types.ts","../src/client.ts","../src/index.ts"],"names":["validateApiKeyFormat","validateApiKey"],"mappings":"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","file":"index.js","sourcesContent":["/**\n * Types for @krutai/ai-provider\n */\n\n/**\n * Default model identifier sent to the LangChain server when no model is specified.\n * Your server can use this value to route to its own default model.\n */\nexport const DEFAULT_MODEL = 'default' as const;\n\n/**\n * Default base URL for the LangChain backend server.\n * Used when no serverUrl is provided in the config.\n */\nexport const DEFAULT_SERVER_URL = 'http://localhost:8000' as const;\n\n/**\n * Configuration options for KrutAIProvider\n */\nexport interface KrutAIProviderConfig {\n /**\n * KrutAI API key.\n * Validated against the LangChain server before use.\n * Optional: defaults to process.env.KRUTAI_API_KEY\n */\n apiKey?: string;\n\n /**\n * Base URL of your deployed LangChain backend server.\n * @default \"http://localhost:8000\"\n * @example \"https://ai.krut.ai\"\n */\n serverUrl?: string;\n\n /**\n * The AI model to use (passed to the server).\n * The server decides what to do with this value.\n * @default \"default\"\n */\n model?: string;\n\n /**\n * Whether to validate the API key against the server on initialization.\n * Set to false to skip the validation round-trip (e.g. in tests).\n * @default true\n */\n validateOnInit?: boolean;\n}\n\n/**\n * A part of a multimodal message\n */\nexport interface TextContentPart {\n type: 'text';\n text: string;\n}\n\nexport interface ImageContentPart {\n type: 'image_url';\n image_url: {\n url: string; // Base64 data URI or HTTP(S) URL\n detail?: 'low' | 'high' | 'auto';\n };\n}\n\nexport type ContentPart = TextContentPart | ImageContentPart;\nexport type MessageContent = string | ContentPart[];\n\n/**\n * A single chat message\n */\nexport interface ChatMessage {\n role: 'user' | 'assistant' | 'system';\n content: MessageContent;\n}\n\n/**\n * Options for a single generate / stream / chat call\n */\nexport interface GenerateOptions {\n /**\n * Override the model for this specific call.\n */\n model?: string;\n\n /**\n * System prompt (prepended as a system message).\n */\n system?: string;\n\n /**\n * Maximum tokens to generate.\n */\n maxTokens?: number;\n\n /**\n * Temperature (0–2).\n */\n temperature?: number;\n\n /**\n * Array of image URLs or base64 data URIs to include with the request.\n */\n images?: string[];\n\n /**\n * Array of document URLs or base64 data URIs (e.g. PDFs) to include with the request.\n */\n documents?: string[];\n\n /**\n * Array of PDF URLs or base64 data URIs to include with the request.\n */\n pdf?: string[];\n}\n","import type { KrutAIProviderConfig, GenerateOptions, ChatMessage } from './types';\nimport { DEFAULT_MODEL, DEFAULT_SERVER_URL } from './types';\nimport {\n validateApiKeyWithService as validateApiKey,\n validateApiKeyFormat,\n ApiKeyValidationError as KrutAIKeyValidationError,\n} from 'krutai';\n\nexport { KrutAIKeyValidationError };\n\n/**\n * KrutAIProvider — fetch-based AI provider for KrutAI\n *\n * Calls your deployed LangChain backend server for all AI operations.\n * The API key is validated against the server before use.\n *\n * @example\n * ```typescript\n * import { KrutAIProvider } from '@krutai/ai-provider';\n *\n * // Using local dev server (http://localhost:8000 by default)\n * const ai = new KrutAIProvider({\n * apiKey: process.env.KRUTAI_API_KEY!,\n * });\n *\n * // Or point to a production server\n * const aiProd = new KrutAIProvider({\n * apiKey: process.env.KRUTAI_API_KEY!,\n * serverUrl: 'https://ai.krut.ai',\n * });\n *\n * await ai.initialize(); // validates key against server\n *\n * const text = await ai.generate('Tell me a joke');\n * console.log(text);\n * ```\n */\nexport class KrutAIProvider {\n private readonly apiKey: string;\n private readonly serverUrl: string;\n private readonly resolvedModel: string;\n private readonly config: KrutAIProviderConfig;\n\n private initialized = false;\n\n constructor(config: KrutAIProviderConfig) {\n this.config = config;\n this.apiKey = config.apiKey || process.env.KRUTAI_API_KEY || '';\n this.serverUrl = (config.serverUrl ?? DEFAULT_SERVER_URL).replace(/\\/$/, ''); // strip trailing slash\n this.resolvedModel = config.model ?? DEFAULT_MODEL;\n\n // Basic format check immediately on construction\n validateApiKeyFormat(this.apiKey);\n\n // If validation is disabled, mark as ready immediately\n if (config.validateOnInit === false) {\n this.initialized = true;\n }\n }\n\n /**\n * Initialize the provider.\n * Validates the API key against the LangChain server, then marks provider as ready.\n *\n * @throws {KrutAIKeyValidationError} if the key is rejected or the server is unreachable\n */\n async initialize(): Promise<void> {\n if (this.initialized) return;\n\n if (this.config.validateOnInit !== false) {\n await validateApiKey(this.apiKey, this.serverUrl);\n }\n\n this.initialized = true;\n }\n\n /**\n * Returns the currently configured default model.\n */\n getModel(): string {\n return this.resolvedModel;\n }\n\n /**\n * Returns whether the provider has been initialized.\n */\n isInitialized(): boolean {\n return this.initialized;\n }\n\n // ---------------------------------------------------------------------------\n // Private helpers\n // ---------------------------------------------------------------------------\n\n private assertInitialized(): void {\n if (!this.initialized) {\n throw new Error(\n 'KrutAIProvider not initialized. Call initialize() first or set validateOnInit to false.'\n );\n }\n }\n\n /** Common request headers sent to the server on every AI call. */\n private authHeaders(): Record<string, string> {\n return {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${this.apiKey}`,\n 'x-api-key': this.apiKey,\n };\n }\n\n // ---------------------------------------------------------------------------\n // Public AI Methods\n // ---------------------------------------------------------------------------\n\n /**\n * Generate a response for a prompt (non-streaming).\n *\n * Calls: POST {serverUrl}/generate\n * Body: { prompt, model, system?, maxTokens?, temperature? }\n * Expected response: { text: string } or { content: string } or { message: string }\n *\n * @param prompt - The user prompt string\n * @param options - Optional overrides (model, system, maxTokens, temperature)\n * @returns The assistant's response text\n */\n async generate(prompt: string, options: GenerateOptions = {}): Promise<string> {\n this.assertInitialized();\n const model = options.model ?? this.resolvedModel;\n\n const response = await fetch(`${this.serverUrl}/generate`, {\n method: 'POST',\n headers: this.authHeaders(),\n body: JSON.stringify({\n prompt,\n model,\n ...(options.system !== undefined ? { system: options.system } : {}),\n ...(options.images !== undefined ? { images: options.images } : {}),\n ...(options.documents !== undefined ? { documents: options.documents } : {}),\n ...(options.pdf !== undefined ? { pdf: options.pdf } : {}),\n ...(options.maxTokens !== undefined ? { maxTokens: options.maxTokens } : {}),\n ...(options.temperature !== undefined ? { temperature: options.temperature } : {}),\n }),\n });\n\n if (!response.ok) {\n let errorMessage = `AI server returned HTTP ${response.status} for /generate`;\n try {\n const errorData = (await response.json()) as { message?: string; error?: string };\n if (errorData?.error) errorMessage = errorData.error;\n else if (errorData?.message) errorMessage = errorData.message;\n } catch { }\n throw new Error(errorMessage);\n }\n\n const data = (await response.json()) as {\n text?: string;\n content?: string;\n message?: string;\n };\n\n return data.text ?? data.content ?? data.message ?? '';\n }\n\n /**\n * Generate a streaming response for a prompt via Server-Sent Events (SSE).\n *\n * Calls: POST {serverUrl}/stream\n * Body: { prompt, model, system?, maxTokens?, temperature? }\n * Expected response: `text/event-stream` with `data: <chunk>` lines.\n *\n * @param prompt - The user prompt string\n * @param options - Optional overrides (model, system, maxTokens, temperature)\n * @returns An async generator yielding string chunks from the server\n *\n * @example\n * ```typescript\n * const stream = ai.stream('Tell me a story');\n * for await (const chunk of stream) {\n * process.stdout.write(chunk);\n * }\n * ```\n */\n async *stream(prompt: string, options: GenerateOptions = {}): AsyncGenerator<string> {\n this.assertInitialized();\n const model = options.model ?? this.resolvedModel;\n\n const response = await fetch(`${this.serverUrl}/stream`, {\n method: 'POST',\n headers: {\n ...this.authHeaders(),\n Accept: 'text/event-stream',\n },\n body: JSON.stringify({\n prompt,\n model,\n ...(options.system !== undefined ? { system: options.system } : {}),\n ...(options.images !== undefined ? { images: options.images } : {}),\n ...(options.documents !== undefined ? { documents: options.documents } : {}),\n ...(options.pdf !== undefined ? { pdf: options.pdf } : {}),\n ...(options.maxTokens !== undefined ? { maxTokens: options.maxTokens } : {}),\n ...(options.temperature !== undefined ? { temperature: options.temperature } : {}),\n }),\n });\n\n if (!response.ok) {\n let errorMessage = `AI server returned HTTP ${response.status} for /stream`;\n try {\n const errorData = (await response.json()) as { message?: string; error?: string };\n if (errorData?.error) errorMessage = errorData.error;\n else if (errorData?.message) errorMessage = errorData.message;\n } catch { }\n throw new Error(errorMessage);\n }\n\n if (!response.body) {\n throw new Error('AI server returned no response body for /stream');\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 if (done) break;\n\n buffer += decoder.decode(value, { stream: true });\n const lines = buffer.split('\\n');\n // Keep the last (potentially incomplete) line in the buffer\n buffer = lines.pop() ?? '';\n\n for (const line of lines) {\n if (line.startsWith('data: ')) {\n const raw = line.slice(6).trim();\n if (raw === '[DONE]') return;\n try {\n const parsed = JSON.parse(raw) as {\n text?: string;\n content?: string;\n delta?: { content?: string };\n };\n const chunk =\n parsed.text ??\n parsed.content ??\n parsed.delta?.content ??\n '';\n if (chunk) yield chunk;\n } catch {\n // raw string chunk (non-JSON SSE)\n if (raw) yield raw;\n }\n }\n }\n }\n } finally {\n reader.releaseLock();\n }\n }\n\n /**\n * Similar to stream() but returns the raw fetch Response object.\n * Useful when you want to proxy the Server-Sent Events stream directly to a frontend client\n * (e.g., returning this directly from a Next.js API route).\n *\n * @param prompt - The user prompt string\n * @param options - Optional overrides (model, system, maxTokens, temperature)\n * @returns A Promise resolving to the native fetch Response\n */\n async streamResponse(prompt: string, options: GenerateOptions = {}): Promise<Response> {\n this.assertInitialized();\n const model = options.model ?? this.resolvedModel;\n\n const response = await fetch(`${this.serverUrl}/stream`, {\n method: 'POST',\n headers: {\n ...this.authHeaders(),\n Accept: 'text/event-stream',\n },\n body: JSON.stringify({\n prompt,\n model,\n ...(options.system !== undefined ? { system: options.system } : {}),\n ...(options.images !== undefined ? { images: options.images } : {}),\n ...(options.documents !== undefined ? { documents: options.documents } : {}),\n ...(options.pdf !== undefined ? { pdf: options.pdf } : {}),\n ...(options.maxTokens !== undefined ? { maxTokens: options.maxTokens } : {}),\n ...(options.temperature !== undefined ? { temperature: options.temperature } : {}),\n }),\n });\n\n if (!response.ok) {\n let errorMessage = `AI server returned HTTP ${response.status} for /stream`;\n try {\n const errorData = (await response.json()) as { message?: string; error?: string };\n if (errorData?.error) errorMessage = errorData.error;\n else if (errorData?.message) errorMessage = errorData.message;\n } catch { }\n throw new Error(errorMessage);\n }\n console.log(response)\n return response;\n }\n\n /**\n * Multi-turn conversation streaming: pass a full message history.\n * Calls POST /stream with the full { messages } payload.\n *\n * @param messages - Full conversation history\n * @param options - Optional overrides (model, maxTokens, temperature)\n * @returns An async generator yielding string chunks from the server\n */\n async *streamChat(messages: ChatMessage[], options: GenerateOptions = {}): AsyncGenerator<string> {\n this.assertInitialized();\n\n if (!messages.length) {\n throw new Error('Messages array cannot be empty for streamChat');\n }\n\n const model = options.model ?? this.resolvedModel;\n\n const response = await fetch(`${this.serverUrl}/stream`, {\n method: 'POST',\n headers: {\n ...this.authHeaders(),\n Accept: 'text/event-stream',\n },\n body: JSON.stringify({\n messages,\n model,\n ...(options.system !== undefined ? { system: options.system } : {}),\n ...(options.images !== undefined ? { images: options.images } : {}),\n ...(options.documents !== undefined ? { documents: options.documents } : {}),\n ...(options.pdf !== undefined ? { pdf: options.pdf } : {}),\n ...(options.maxTokens !== undefined ? { maxTokens: options.maxTokens } : {}),\n ...(options.temperature !== undefined ? { temperature: options.temperature } : {}),\n }),\n });\n\n if (!response.ok) {\n let errorMessage = `AI server returned HTTP ${response.status} for /stream`;\n try {\n const errorData = (await response.json()) as { message?: string; error?: string };\n if (errorData?.error) errorMessage = errorData.error;\n else if (errorData?.message) errorMessage = errorData.message;\n } catch { }\n throw new Error(errorMessage);\n }\n\n if (!response.body) {\n throw new Error('AI server returned no response body for /stream');\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 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.startsWith('data: ')) {\n const raw = line.slice(6).trim();\n if (raw === '[DONE]') return;\n try {\n const parsed = JSON.parse(raw) as {\n text?: string;\n content?: string;\n delta?: { content?: string };\n };\n const chunk =\n parsed.text ??\n parsed.content ??\n parsed.delta?.content ??\n '';\n if (chunk) yield chunk;\n } catch {\n if (raw) yield raw;\n }\n }\n }\n }\n } finally {\n reader.releaseLock();\n }\n }\n\n /**\n * Similar to streamChat() but returns the raw fetch Response object.\n * Useful for proxying the Server-Sent Events stream directly to a frontend client.\n *\n * @param messages - Full conversation history\n * @param options - Optional overrides (model, maxTokens, temperature)\n * @returns A Promise resolving to the native fetch Response\n */\n async streamChatResponse(messages: ChatMessage[], options: GenerateOptions = {}): Promise<Response> {\n this.assertInitialized();\n\n if (!messages.length) {\n throw new Error('Messages array cannot be empty for streamChatResponse');\n }\n\n const model = options.model ?? this.resolvedModel;\n\n const response = await fetch(`${this.serverUrl}/stream`, {\n method: 'POST',\n headers: {\n ...this.authHeaders(),\n Accept: 'text/event-stream',\n },\n body: JSON.stringify({\n messages,\n model,\n ...(options.system !== undefined ? { system: options.system } : {}),\n ...(options.images !== undefined ? { images: options.images } : {}),\n ...(options.documents !== undefined ? { documents: options.documents } : {}),\n ...(options.pdf !== undefined ? { pdf: options.pdf } : {}),\n ...(options.maxTokens !== undefined ? { maxTokens: options.maxTokens } : {}),\n ...(options.temperature !== undefined ? { temperature: options.temperature } : {}),\n }),\n });\n\n if (!response.ok) {\n let errorMessage = `AI server returned HTTP ${response.status} for /stream`;\n try {\n const errorData = (await response.json()) as { message?: string; error?: string };\n if (errorData?.error) errorMessage = errorData.error;\n else if (errorData?.message) errorMessage = errorData.message;\n } catch { }\n throw new Error(errorMessage);\n }\n\n return response;\n }\n\n /**\n * Multi-turn conversation: pass a full message history.\n *\n * Calls: POST {serverUrl}/chat\n * Body: { messages, model, maxTokens?, temperature? }\n * Expected response: { text: string } or { content: string } or { message: string }\n *\n * @param messages - Full conversation history\n * @param options - Optional overrides (model, maxTokens, temperature)\n * @returns The assistant's response text\n */\n async chat(messages: ChatMessage[], options: GenerateOptions = {}): Promise<string> {\n this.assertInitialized();\n const model = options.model ?? this.resolvedModel;\n\n const response = await fetch(`${this.serverUrl}/chat`, {\n method: 'POST',\n headers: this.authHeaders(),\n body: JSON.stringify({\n messages,\n model,\n ...(options.images !== undefined ? { images: options.images } : {}),\n ...(options.documents !== undefined ? { documents: options.documents } : {}),\n ...(options.pdf !== undefined ? { pdf: options.pdf } : {}),\n ...(options.maxTokens !== undefined ? { maxTokens: options.maxTokens } : {}),\n ...(options.temperature !== undefined ? { temperature: options.temperature } : {}),\n }),\n });\n\n if (!response.ok) {\n let errorMessage = `AI server returned HTTP ${response.status} for /chat`;\n try {\n const errorData = (await response.json()) as { message?: string; error?: string };\n if (errorData?.error) errorMessage = errorData.error;\n else if (errorData?.message) errorMessage = errorData.message;\n } catch { }\n throw new Error(errorMessage);\n }\n\n const data = (await response.json()) as {\n text?: string;\n content?: string;\n message?: string;\n };\n\n return data.text ?? data.content ?? data.message ?? '';\n }\n}\n","/**\n * @krutai/ai-provider — AI Provider package for KrutAI\n *\n * A fetch-based wrapper that calls your deployed LangChain backend server.\n * The user's API key is validated against the server before any AI call is made.\n *\n * @example Basic usage\n * ```typescript\n * import { krutAI } from '@krutai/ai-provider';\n *\n * const ai = krutAI({\n * apiKey: process.env.KRUTAI_API_KEY!,\n * serverUrl: 'https://krut.ai',\n * });\n *\n * await ai.initialize(); // validates key with server\n *\n * const text = await ai.generate('Write a poem about TypeScript');\n * console.log(text);\n * ```\n *\n * @example With custom model\n * ```typescript\n * const ai = krutAI({\n * apiKey: process.env.KRUTAI_API_KEY!,\n * serverUrl: 'https://krut.ai',\n * model: 'gpt-4o',\n * });\n * await ai.initialize();\n * const text = await ai.generate('Hello!');\n * ```\n *\n * @example Streaming\n * ```typescript\n * const ai = krutAI({\n * apiKey: process.env.KRUTAI_API_KEY!,\n * serverUrl: 'https://krut.ai',\n * });\n * await ai.initialize();\n *\n * const stream = ai.stream('Tell me a story');\n * for await (const chunk of stream) {\n * process.stdout.write(chunk);\n * }\n * ```\n *\n * @packageDocumentation\n */\n\nimport type { KrutAIProviderConfig } from './types';\nimport { DEFAULT_MODEL } from './types';\nimport { KrutAIProvider } from './client';\n\nexport { KrutAIProvider } from './client';\nexport { KrutAIKeyValidationError } from './client';\nexport {\n validateApiKeyWithService as validateApiKey,\n validateApiKeyFormat,\n} from 'krutai';\nexport type { KrutAIProviderConfig, GenerateOptions, ChatMessage } from './types';\nexport { DEFAULT_MODEL } from './types';\n\n/**\n * krutAI — convenience factory (mirrors `krutAuth` in @krutai/auth).\n *\n * Creates a `KrutAIProvider` instance configured to call your LangChain server.\n *\n * @param config - Provider configuration (`apiKey` and `serverUrl` are required)\n * @returns A `KrutAIProvider` instance — call `.initialize()` before use\n *\n * @example\n * ```typescript\n * import { krutAI } from '@krutai/ai-provider';\n *\n * const ai = krutAI({\n * apiKey: process.env.KRUTAI_API_KEY!,\n * serverUrl: 'https://krut.ai',\n * });\n *\n * await ai.initialize();\n * const text = await ai.generate('Hello!');\n * ```\n */\nexport function krutAI(\n config: KrutAIProviderConfig & { model?: string }\n): KrutAIProvider {\n return new KrutAIProvider({\n model: DEFAULT_MODEL,\n ...config,\n });\n}\n\n// Package metadata\nexport const VERSION = '0.2.0';\n"]}
1
+ {"version":3,"sources":["../src/types.ts","../src/client.ts","../src/index.ts"],"names":["validateApiKeyFormat","validateApiKey"],"mappings":"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","file":"index.js","sourcesContent":["/**\n * Types for @krutai/ai-provider\n */\n\n/**\n * Default model identifier sent to the LangChain server when no model is specified.\n * Your server can use this value to route to its own default model.\n */\nexport const DEFAULT_MODEL = 'default' as const;\n\n/**\n * Default base URL for the LangChain backend server.\n * Used when no serverUrl is provided in the config.\n */\nexport const DEFAULT_SERVER_URL = 'http://localhost:8000' as const;\n\n/**\n * Configuration options for KrutAIProvider\n */\nexport interface KrutAIProviderConfig {\n /**\n * KrutAI API key.\n * Validated against the LangChain server before use.\n * Optional: defaults to process.env.KRUTAI_API_KEY\n */\n apiKey?: string;\n\n /**\n * Base URL of your deployed LangChain backend server.\n * @default \"http://localhost:8000\"\n * @example \"https://ai.krut.ai\"\n */\n serverUrl?: string;\n\n /**\n * The AI model to use (passed to the server).\n * The server decides what to do with this value.\n * @default \"default\"\n */\n model?: string;\n\n /**\n * Whether to validate the API key against the server on initialization.\n * Set to false to skip the validation round-trip (e.g. in tests).\n * @default true\n */\n validateOnInit?: boolean;\n}\n\n/**\n * A part of a multimodal message\n */\nexport interface TextContentPart {\n type: 'text';\n text: string;\n}\n\nexport interface ImageContentPart {\n type: 'image_url';\n image_url: {\n url: string; // Base64 data URI or HTTP(S) URL\n detail?: 'low' | 'high' | 'auto';\n };\n}\n\nexport type ContentPart = TextContentPart | ImageContentPart;\nexport type MessageContent = string | ContentPart[];\n\n/**\n * A single chat message\n */\nexport interface ChatMessage {\n role: 'user' | 'assistant' | 'system';\n content: MessageContent;\n}\n\n/**\n * Options for a single generate / stream / chat call\n */\nexport interface GenerateOptions {\n /**\n * Override the model for this specific call.\n */\n model?: string;\n\n /**\n * System prompt (prepended as a system message).\n */\n system?: string;\n\n /**\n * Maximum tokens to generate.\n */\n maxTokens?: number;\n\n /**\n * Temperature (0–2).\n */\n temperature?: number;\n\n /**\n * Array of image URLs or base64 data URIs to include with the request.\n */\n images?: string[];\n\n /**\n * Array of document URLs or base64 data URIs (e.g. PDFs) to include with the request.\n */\n documents?: string[];\n\n /**\n * Array of PDF URLs or base64 data URIs to include with the request.\n */\n pdf?: string[];\n}\n","import type { KrutAIProviderConfig, GenerateOptions, ChatMessage } from './types';\nimport { DEFAULT_MODEL, DEFAULT_SERVER_URL } from './types';\nimport {\n validateApiKeyWithService as validateApiKey,\n validateApiKeyFormat,\n ApiKeyValidationError as KrutAIKeyValidationError,\n} from 'krutai';\n\nexport { KrutAIKeyValidationError };\n\n/**\n * KrutAIProvider — fetch-based AI provider for KrutAI\n *\n * Calls your deployed LangChain backend server for all AI operations.\n * The API key is validated against the server before use.\n *\n * @example\n * ```typescript\n * import { KrutAIProvider } from '@krutai/ai-provider';\n *\n * // Using local dev server (http://localhost:8000 by default)\n * const ai = new KrutAIProvider({\n * apiKey: process.env.KRUTAI_API_KEY!,\n * });\n *\n * // Or point to a production server\n * const aiProd = new KrutAIProvider({\n * apiKey: process.env.KRUTAI_API_KEY!,\n * serverUrl: 'https://ai.krut.ai',\n * });\n *\n * await ai.initialize(); // validates key against server\n *\n * const text = await ai.generate('Tell me a joke');\n * console.log(text);\n * ```\n */\nexport class KrutAIProvider {\n private readonly apiKey: string;\n private readonly serverUrl: string;\n private readonly resolvedModel: string;\n private readonly config: KrutAIProviderConfig;\n\n private initialized = false;\n\n constructor(config: KrutAIProviderConfig) {\n this.config = config;\n this.apiKey = config.apiKey || process.env.KRUTAI_API_KEY || '';\n this.serverUrl = (config.serverUrl ?? DEFAULT_SERVER_URL).replace(/\\/$/, ''); // strip trailing slash\n this.resolvedModel = config.model ?? DEFAULT_MODEL;\n\n // Basic format check immediately on construction\n validateApiKeyFormat(this.apiKey);\n\n // If validation is disabled, mark as ready immediately\n if (config.validateOnInit === false) {\n this.initialized = true;\n }\n }\n\n /**\n * Initialize the provider.\n * Validates the API key against the LangChain server, then marks provider as ready.\n *\n * @throws {KrutAIKeyValidationError} if the key is rejected or the server is unreachable\n */\n async initialize(): Promise<void> {\n if (this.initialized) return;\n\n if (this.config.validateOnInit !== false) {\n await validateApiKey(this.apiKey, this.serverUrl);\n }\n\n this.initialized = true;\n }\n\n /**\n * Returns the currently configured default model.\n */\n getModel(): string {\n return this.resolvedModel;\n }\n\n /**\n * Returns whether the provider has been initialized.\n */\n isInitialized(): boolean {\n return this.initialized;\n }\n\n // ---------------------------------------------------------------------------\n // Private helpers\n // ---------------------------------------------------------------------------\n\n private assertInitialized(): void {\n if (!this.initialized) {\n throw new Error(\n 'KrutAIProvider not initialized. Call initialize() first or set validateOnInit to false.'\n );\n }\n }\n\n /** Common request headers sent to the server on every AI call. */\n private authHeaders(): Record<string, string> {\n return {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${this.apiKey}`,\n 'x-api-key': this.apiKey,\n };\n }\n\n // ---------------------------------------------------------------------------\n // Public AI Methods\n // ---------------------------------------------------------------------------\n\n /**\n * Similar to streamChat() but returns the raw fetch Response object.\n * Useful for proxying the Server-Sent Events stream directly to a frontend client.\n *\n * @param messages - Full conversation history\n * @param options - Optional overrides (model, maxTokens, temperature)\n * @returns A Promise resolving to the native fetch Response\n */\n async streamChatResponse(messages: ChatMessage[], options: GenerateOptions = {}): Promise<Response> {\n this.assertInitialized();\n\n if (!messages.length) {\n throw new Error('Messages array cannot be empty for streamChatResponse');\n }\n\n const model = options.model ?? this.resolvedModel;\n\n const response = await fetch(`${this.serverUrl}/stream`, {\n method: 'POST',\n headers: {\n ...this.authHeaders(),\n Accept: 'text/event-stream',\n },\n body: JSON.stringify({\n messages,\n model,\n ...(options.system !== undefined ? { system: options.system } : {}),\n ...(options.images !== undefined ? { images: options.images } : {}),\n ...(options.documents !== undefined ? { documents: options.documents } : {}),\n ...(options.pdf !== undefined ? { pdf: options.pdf } : {}),\n ...(options.maxTokens !== undefined ? { maxTokens: options.maxTokens } : {}),\n ...(options.temperature !== undefined ? { temperature: options.temperature } : {}),\n }),\n });\n\n if (!response.ok) {\n let errorMessage = `AI server returned HTTP ${response.status} for /stream`;\n try {\n const errorData = (await response.json()) as { message?: string; error?: string };\n if (errorData?.error) errorMessage = errorData.error;\n else if (errorData?.message) errorMessage = errorData.message;\n } catch { }\n throw new Error(errorMessage);\n }\n\n return response;\n }\n\n /**\n * Generate a response for a prompt (non-streaming).\n *\n * Calls: POST {serverUrl}/generate\n * Body: { prompt, model, system?, maxTokens?, temperature? }\n * Expected response: { text: string } or { content: string } or { message: string }\n *\n * @param prompt - The user prompt string\n * @param options - Optional overrides (model, system, maxTokens, temperature)\n * @returns The assistant's response text\n */\n async chat(prompt: string, options: GenerateOptions = {}): Promise<string> {\n this.assertInitialized();\n const model = options.model ?? this.resolvedModel;\n\n const response = await fetch(`${this.serverUrl}/generate`, {\n method: 'POST',\n headers: this.authHeaders(),\n body: JSON.stringify({\n prompt,\n model,\n ...(options.system !== undefined ? { system: options.system } : {}),\n ...(options.images !== undefined ? { images: options.images } : {}),\n ...(options.documents !== undefined ? { documents: options.documents } : {}),\n ...(options.pdf !== undefined ? { pdf: options.pdf } : {}),\n ...(options.maxTokens !== undefined ? { maxTokens: options.maxTokens } : {}),\n ...(options.temperature !== undefined ? { temperature: options.temperature } : {}),\n }),\n });\n\n if (!response.ok) {\n let errorMessage = `AI server returned HTTP ${response.status} for /generate`;\n try {\n const errorData = (await response.json()) as { message?: string; error?: string };\n if (errorData?.error) errorMessage = errorData.error;\n else if (errorData?.message) errorMessage = errorData.message;\n } catch { }\n throw new Error(errorMessage);\n }\n\n const data = (await response.json()) as {\n text?: string;\n content?: string;\n message?: string;\n };\n\n return data.text ?? data.content ?? data.message ?? '';\n }\n}\n","/**\n * @krutai/ai-provider — AI Provider package for KrutAI\n *\n * A fetch-based wrapper that calls your deployed LangChain backend server.\n * The user's API key is validated against the server before any AI call is made.\n *\n * @example Basic usage\n * ```typescript\n * import { krutAI } from '@krutai/ai-provider';\n *\n * const ai = krutAI({\n * apiKey: process.env.KRUTAI_API_KEY!,\n * serverUrl: 'https://krut.ai',\n * });\n *\n * await ai.initialize(); // validates key with server\n *\n * const text = await ai.chat('Write a poem about TypeScript');\n * console.log(text);\n * ```\n *\n * @example With custom model\n * ```typescript\n * const ai = krutAI({\n * apiKey: process.env.KRUTAI_API_KEY!,\n * serverUrl: 'https://krut.ai',\n * model: 'gpt-4o',\n * });\n * await ai.initialize();\n * const text = await ai.chat('Hello!');\n * ```\n *\n * @example Streaming\n * ```typescript\n * const ai = krutAI({\n * apiKey: process.env.KRUTAI_API_KEY!,\n * serverUrl: 'https://krut.ai',\n * });\n * await ai.initialize();\n *\n * const response = await ai.streamChatResponse([{ role: 'user', content: 'Tell me a story' }]);\n * // Example assumes you handle the SSE stream from the response body\n * ```\n *\n * @packageDocumentation\n */\n\nimport type { KrutAIProviderConfig } from './types';\nimport { DEFAULT_MODEL } from './types';\nimport { KrutAIProvider } from './client';\n\nexport { KrutAIProvider } from './client';\nexport { KrutAIKeyValidationError } from './client';\nexport {\n validateApiKeyWithService as validateApiKey,\n validateApiKeyFormat,\n} from 'krutai';\nexport type { KrutAIProviderConfig, GenerateOptions, ChatMessage } from './types';\nexport { DEFAULT_MODEL } from './types';\n\n/**\n * krutAI — convenience factory (mirrors `krutAuth` in @krutai/auth).\n *\n * Creates a `KrutAIProvider` instance configured to call your LangChain server.\n *\n * @param config - Provider configuration (`apiKey` and `serverUrl` are required)\n * @returns A `KrutAIProvider` instance — call `.initialize()` before use\n *\n * @example\n * ```typescript\n * import { krutAI } from '@krutai/ai-provider';\n *\n * const ai = krutAI({\n * apiKey: process.env.KRUTAI_API_KEY!,\n * serverUrl: 'https://krut.ai',\n * });\n *\n * await ai.initialize();\n * const text = await ai.chat('Hello!');\n * ```\n */\nexport function krutAI(\n config: KrutAIProviderConfig & { model?: string }\n): KrutAIProvider {\n return new KrutAIProvider({\n model: DEFAULT_MODEL,\n ...config,\n });\n}\n\n// Package metadata\nexport const VERSION = '0.2.0';\n"]}
package/dist/index.mjs CHANGED
@@ -66,241 +66,6 @@ var KrutAIProvider = class {
66
66
  // ---------------------------------------------------------------------------
67
67
  // Public AI Methods
68
68
  // ---------------------------------------------------------------------------
69
- /**
70
- * Generate a response for a prompt (non-streaming).
71
- *
72
- * Calls: POST {serverUrl}/generate
73
- * Body: { prompt, model, system?, maxTokens?, temperature? }
74
- * Expected response: { text: string } or { content: string } or { message: string }
75
- *
76
- * @param prompt - The user prompt string
77
- * @param options - Optional overrides (model, system, maxTokens, temperature)
78
- * @returns The assistant's response text
79
- */
80
- async generate(prompt, options = {}) {
81
- this.assertInitialized();
82
- const model = options.model ?? this.resolvedModel;
83
- const response = await fetch(`${this.serverUrl}/generate`, {
84
- method: "POST",
85
- headers: this.authHeaders(),
86
- body: JSON.stringify({
87
- prompt,
88
- model,
89
- ...options.system !== void 0 ? { system: options.system } : {},
90
- ...options.images !== void 0 ? { images: options.images } : {},
91
- ...options.documents !== void 0 ? { documents: options.documents } : {},
92
- ...options.pdf !== void 0 ? { pdf: options.pdf } : {},
93
- ...options.maxTokens !== void 0 ? { maxTokens: options.maxTokens } : {},
94
- ...options.temperature !== void 0 ? { temperature: options.temperature } : {}
95
- })
96
- });
97
- if (!response.ok) {
98
- let errorMessage = `AI server returned HTTP ${response.status} for /generate`;
99
- try {
100
- const errorData = await response.json();
101
- if (errorData?.error) errorMessage = errorData.error;
102
- else if (errorData?.message) errorMessage = errorData.message;
103
- } catch {
104
- }
105
- throw new Error(errorMessage);
106
- }
107
- const data = await response.json();
108
- return data.text ?? data.content ?? data.message ?? "";
109
- }
110
- /**
111
- * Generate a streaming response for a prompt via Server-Sent Events (SSE).
112
- *
113
- * Calls: POST {serverUrl}/stream
114
- * Body: { prompt, model, system?, maxTokens?, temperature? }
115
- * Expected response: `text/event-stream` with `data: <chunk>` lines.
116
- *
117
- * @param prompt - The user prompt string
118
- * @param options - Optional overrides (model, system, maxTokens, temperature)
119
- * @returns An async generator yielding string chunks from the server
120
- *
121
- * @example
122
- * ```typescript
123
- * const stream = ai.stream('Tell me a story');
124
- * for await (const chunk of stream) {
125
- * process.stdout.write(chunk);
126
- * }
127
- * ```
128
- */
129
- async *stream(prompt, options = {}) {
130
- this.assertInitialized();
131
- const model = options.model ?? this.resolvedModel;
132
- const response = await fetch(`${this.serverUrl}/stream`, {
133
- method: "POST",
134
- headers: {
135
- ...this.authHeaders(),
136
- Accept: "text/event-stream"
137
- },
138
- body: JSON.stringify({
139
- prompt,
140
- model,
141
- ...options.system !== void 0 ? { system: options.system } : {},
142
- ...options.images !== void 0 ? { images: options.images } : {},
143
- ...options.documents !== void 0 ? { documents: options.documents } : {},
144
- ...options.pdf !== void 0 ? { pdf: options.pdf } : {},
145
- ...options.maxTokens !== void 0 ? { maxTokens: options.maxTokens } : {},
146
- ...options.temperature !== void 0 ? { temperature: options.temperature } : {}
147
- })
148
- });
149
- if (!response.ok) {
150
- let errorMessage = `AI server returned HTTP ${response.status} for /stream`;
151
- try {
152
- const errorData = await response.json();
153
- if (errorData?.error) errorMessage = errorData.error;
154
- else if (errorData?.message) errorMessage = errorData.message;
155
- } catch {
156
- }
157
- throw new Error(errorMessage);
158
- }
159
- if (!response.body) {
160
- throw new Error("AI server returned no response body for /stream");
161
- }
162
- const reader = response.body.getReader();
163
- const decoder = new TextDecoder();
164
- let buffer = "";
165
- try {
166
- while (true) {
167
- const { done, value } = await reader.read();
168
- if (done) break;
169
- buffer += decoder.decode(value, { stream: true });
170
- const lines = buffer.split("\n");
171
- buffer = lines.pop() ?? "";
172
- for (const line of lines) {
173
- if (line.startsWith("data: ")) {
174
- const raw = line.slice(6).trim();
175
- if (raw === "[DONE]") return;
176
- try {
177
- const parsed = JSON.parse(raw);
178
- const chunk = parsed.text ?? parsed.content ?? parsed.delta?.content ?? "";
179
- if (chunk) yield chunk;
180
- } catch {
181
- if (raw) yield raw;
182
- }
183
- }
184
- }
185
- }
186
- } finally {
187
- reader.releaseLock();
188
- }
189
- }
190
- /**
191
- * Similar to stream() but returns the raw fetch Response object.
192
- * Useful when you want to proxy the Server-Sent Events stream directly to a frontend client
193
- * (e.g., returning this directly from a Next.js API route).
194
- *
195
- * @param prompt - The user prompt string
196
- * @param options - Optional overrides (model, system, maxTokens, temperature)
197
- * @returns A Promise resolving to the native fetch Response
198
- */
199
- async streamResponse(prompt, options = {}) {
200
- this.assertInitialized();
201
- const model = options.model ?? this.resolvedModel;
202
- const response = await fetch(`${this.serverUrl}/stream`, {
203
- method: "POST",
204
- headers: {
205
- ...this.authHeaders(),
206
- Accept: "text/event-stream"
207
- },
208
- body: JSON.stringify({
209
- prompt,
210
- model,
211
- ...options.system !== void 0 ? { system: options.system } : {},
212
- ...options.images !== void 0 ? { images: options.images } : {},
213
- ...options.documents !== void 0 ? { documents: options.documents } : {},
214
- ...options.pdf !== void 0 ? { pdf: options.pdf } : {},
215
- ...options.maxTokens !== void 0 ? { maxTokens: options.maxTokens } : {},
216
- ...options.temperature !== void 0 ? { temperature: options.temperature } : {}
217
- })
218
- });
219
- if (!response.ok) {
220
- let errorMessage = `AI server returned HTTP ${response.status} for /stream`;
221
- try {
222
- const errorData = await response.json();
223
- if (errorData?.error) errorMessage = errorData.error;
224
- else if (errorData?.message) errorMessage = errorData.message;
225
- } catch {
226
- }
227
- throw new Error(errorMessage);
228
- }
229
- console.log(response);
230
- return response;
231
- }
232
- /**
233
- * Multi-turn conversation streaming: pass a full message history.
234
- * Calls POST /stream with the full { messages } payload.
235
- *
236
- * @param messages - Full conversation history
237
- * @param options - Optional overrides (model, maxTokens, temperature)
238
- * @returns An async generator yielding string chunks from the server
239
- */
240
- async *streamChat(messages, options = {}) {
241
- this.assertInitialized();
242
- if (!messages.length) {
243
- throw new Error("Messages array cannot be empty for streamChat");
244
- }
245
- const model = options.model ?? this.resolvedModel;
246
- const response = await fetch(`${this.serverUrl}/stream`, {
247
- method: "POST",
248
- headers: {
249
- ...this.authHeaders(),
250
- Accept: "text/event-stream"
251
- },
252
- body: JSON.stringify({
253
- messages,
254
- model,
255
- ...options.system !== void 0 ? { system: options.system } : {},
256
- ...options.images !== void 0 ? { images: options.images } : {},
257
- ...options.documents !== void 0 ? { documents: options.documents } : {},
258
- ...options.pdf !== void 0 ? { pdf: options.pdf } : {},
259
- ...options.maxTokens !== void 0 ? { maxTokens: options.maxTokens } : {},
260
- ...options.temperature !== void 0 ? { temperature: options.temperature } : {}
261
- })
262
- });
263
- if (!response.ok) {
264
- let errorMessage = `AI server returned HTTP ${response.status} for /stream`;
265
- try {
266
- const errorData = await response.json();
267
- if (errorData?.error) errorMessage = errorData.error;
268
- else if (errorData?.message) errorMessage = errorData.message;
269
- } catch {
270
- }
271
- throw new Error(errorMessage);
272
- }
273
- if (!response.body) {
274
- throw new Error("AI server returned no response body for /stream");
275
- }
276
- const reader = response.body.getReader();
277
- const decoder = new TextDecoder();
278
- let buffer = "";
279
- try {
280
- while (true) {
281
- const { done, value } = await reader.read();
282
- if (done) break;
283
- buffer += decoder.decode(value, { stream: true });
284
- const lines = buffer.split("\n");
285
- buffer = lines.pop() ?? "";
286
- for (const line of lines) {
287
- if (line.startsWith("data: ")) {
288
- const raw = line.slice(6).trim();
289
- if (raw === "[DONE]") return;
290
- try {
291
- const parsed = JSON.parse(raw);
292
- const chunk = parsed.text ?? parsed.content ?? parsed.delta?.content ?? "";
293
- if (chunk) yield chunk;
294
- } catch {
295
- if (raw) yield raw;
296
- }
297
- }
298
- }
299
- }
300
- } finally {
301
- reader.releaseLock();
302
- }
303
- }
304
69
  /**
305
70
  * Similar to streamChat() but returns the raw fetch Response object.
306
71
  * Useful for proxying the Server-Sent Events stream directly to a frontend client.
@@ -345,25 +110,26 @@ var KrutAIProvider = class {
345
110
  return response;
346
111
  }
347
112
  /**
348
- * Multi-turn conversation: pass a full message history.
113
+ * Generate a response for a prompt (non-streaming).
349
114
  *
350
- * Calls: POST {serverUrl}/chat
351
- * Body: { messages, model, maxTokens?, temperature? }
115
+ * Calls: POST {serverUrl}/generate
116
+ * Body: { prompt, model, system?, maxTokens?, temperature? }
352
117
  * Expected response: { text: string } or { content: string } or { message: string }
353
118
  *
354
- * @param messages - Full conversation history
355
- * @param options - Optional overrides (model, maxTokens, temperature)
119
+ * @param prompt - The user prompt string
120
+ * @param options - Optional overrides (model, system, maxTokens, temperature)
356
121
  * @returns The assistant's response text
357
122
  */
358
- async chat(messages, options = {}) {
123
+ async chat(prompt, options = {}) {
359
124
  this.assertInitialized();
360
125
  const model = options.model ?? this.resolvedModel;
361
- const response = await fetch(`${this.serverUrl}/chat`, {
126
+ const response = await fetch(`${this.serverUrl}/generate`, {
362
127
  method: "POST",
363
128
  headers: this.authHeaders(),
364
129
  body: JSON.stringify({
365
- messages,
130
+ prompt,
366
131
  model,
132
+ ...options.system !== void 0 ? { system: options.system } : {},
367
133
  ...options.images !== void 0 ? { images: options.images } : {},
368
134
  ...options.documents !== void 0 ? { documents: options.documents } : {},
369
135
  ...options.pdf !== void 0 ? { pdf: options.pdf } : {},
@@ -372,7 +138,7 @@ var KrutAIProvider = class {
372
138
  })
373
139
  });
374
140
  if (!response.ok) {
375
- let errorMessage = `AI server returned HTTP ${response.status} for /chat`;
141
+ let errorMessage = `AI server returned HTTP ${response.status} for /generate`;
376
142
  try {
377
143
  const errorData = await response.json();
378
144
  if (errorData?.error) errorMessage = errorData.error;
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/types.ts","../src/client.ts","../src/index.ts"],"names":["validateApiKey"],"mappings":"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","file":"index.mjs","sourcesContent":["/**\n * Types for @krutai/ai-provider\n */\n\n/**\n * Default model identifier sent to the LangChain server when no model is specified.\n * Your server can use this value to route to its own default model.\n */\nexport const DEFAULT_MODEL = 'default' as const;\n\n/**\n * Default base URL for the LangChain backend server.\n * Used when no serverUrl is provided in the config.\n */\nexport const DEFAULT_SERVER_URL = 'http://localhost:8000' as const;\n\n/**\n * Configuration options for KrutAIProvider\n */\nexport interface KrutAIProviderConfig {\n /**\n * KrutAI API key.\n * Validated against the LangChain server before use.\n * Optional: defaults to process.env.KRUTAI_API_KEY\n */\n apiKey?: string;\n\n /**\n * Base URL of your deployed LangChain backend server.\n * @default \"http://localhost:8000\"\n * @example \"https://ai.krut.ai\"\n */\n serverUrl?: string;\n\n /**\n * The AI model to use (passed to the server).\n * The server decides what to do with this value.\n * @default \"default\"\n */\n model?: string;\n\n /**\n * Whether to validate the API key against the server on initialization.\n * Set to false to skip the validation round-trip (e.g. in tests).\n * @default true\n */\n validateOnInit?: boolean;\n}\n\n/**\n * A part of a multimodal message\n */\nexport interface TextContentPart {\n type: 'text';\n text: string;\n}\n\nexport interface ImageContentPart {\n type: 'image_url';\n image_url: {\n url: string; // Base64 data URI or HTTP(S) URL\n detail?: 'low' | 'high' | 'auto';\n };\n}\n\nexport type ContentPart = TextContentPart | ImageContentPart;\nexport type MessageContent = string | ContentPart[];\n\n/**\n * A single chat message\n */\nexport interface ChatMessage {\n role: 'user' | 'assistant' | 'system';\n content: MessageContent;\n}\n\n/**\n * Options for a single generate / stream / chat call\n */\nexport interface GenerateOptions {\n /**\n * Override the model for this specific call.\n */\n model?: string;\n\n /**\n * System prompt (prepended as a system message).\n */\n system?: string;\n\n /**\n * Maximum tokens to generate.\n */\n maxTokens?: number;\n\n /**\n * Temperature (0–2).\n */\n temperature?: number;\n\n /**\n * Array of image URLs or base64 data URIs to include with the request.\n */\n images?: string[];\n\n /**\n * Array of document URLs or base64 data URIs (e.g. PDFs) to include with the request.\n */\n documents?: string[];\n\n /**\n * Array of PDF URLs or base64 data URIs to include with the request.\n */\n pdf?: string[];\n}\n","import type { KrutAIProviderConfig, GenerateOptions, ChatMessage } from './types';\nimport { DEFAULT_MODEL, DEFAULT_SERVER_URL } from './types';\nimport {\n validateApiKeyWithService as validateApiKey,\n validateApiKeyFormat,\n ApiKeyValidationError as KrutAIKeyValidationError,\n} from 'krutai';\n\nexport { KrutAIKeyValidationError };\n\n/**\n * KrutAIProvider — fetch-based AI provider for KrutAI\n *\n * Calls your deployed LangChain backend server for all AI operations.\n * The API key is validated against the server before use.\n *\n * @example\n * ```typescript\n * import { KrutAIProvider } from '@krutai/ai-provider';\n *\n * // Using local dev server (http://localhost:8000 by default)\n * const ai = new KrutAIProvider({\n * apiKey: process.env.KRUTAI_API_KEY!,\n * });\n *\n * // Or point to a production server\n * const aiProd = new KrutAIProvider({\n * apiKey: process.env.KRUTAI_API_KEY!,\n * serverUrl: 'https://ai.krut.ai',\n * });\n *\n * await ai.initialize(); // validates key against server\n *\n * const text = await ai.generate('Tell me a joke');\n * console.log(text);\n * ```\n */\nexport class KrutAIProvider {\n private readonly apiKey: string;\n private readonly serverUrl: string;\n private readonly resolvedModel: string;\n private readonly config: KrutAIProviderConfig;\n\n private initialized = false;\n\n constructor(config: KrutAIProviderConfig) {\n this.config = config;\n this.apiKey = config.apiKey || process.env.KRUTAI_API_KEY || '';\n this.serverUrl = (config.serverUrl ?? DEFAULT_SERVER_URL).replace(/\\/$/, ''); // strip trailing slash\n this.resolvedModel = config.model ?? DEFAULT_MODEL;\n\n // Basic format check immediately on construction\n validateApiKeyFormat(this.apiKey);\n\n // If validation is disabled, mark as ready immediately\n if (config.validateOnInit === false) {\n this.initialized = true;\n }\n }\n\n /**\n * Initialize the provider.\n * Validates the API key against the LangChain server, then marks provider as ready.\n *\n * @throws {KrutAIKeyValidationError} if the key is rejected or the server is unreachable\n */\n async initialize(): Promise<void> {\n if (this.initialized) return;\n\n if (this.config.validateOnInit !== false) {\n await validateApiKey(this.apiKey, this.serverUrl);\n }\n\n this.initialized = true;\n }\n\n /**\n * Returns the currently configured default model.\n */\n getModel(): string {\n return this.resolvedModel;\n }\n\n /**\n * Returns whether the provider has been initialized.\n */\n isInitialized(): boolean {\n return this.initialized;\n }\n\n // ---------------------------------------------------------------------------\n // Private helpers\n // ---------------------------------------------------------------------------\n\n private assertInitialized(): void {\n if (!this.initialized) {\n throw new Error(\n 'KrutAIProvider not initialized. Call initialize() first or set validateOnInit to false.'\n );\n }\n }\n\n /** Common request headers sent to the server on every AI call. */\n private authHeaders(): Record<string, string> {\n return {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${this.apiKey}`,\n 'x-api-key': this.apiKey,\n };\n }\n\n // ---------------------------------------------------------------------------\n // Public AI Methods\n // ---------------------------------------------------------------------------\n\n /**\n * Generate a response for a prompt (non-streaming).\n *\n * Calls: POST {serverUrl}/generate\n * Body: { prompt, model, system?, maxTokens?, temperature? }\n * Expected response: { text: string } or { content: string } or { message: string }\n *\n * @param prompt - The user prompt string\n * @param options - Optional overrides (model, system, maxTokens, temperature)\n * @returns The assistant's response text\n */\n async generate(prompt: string, options: GenerateOptions = {}): Promise<string> {\n this.assertInitialized();\n const model = options.model ?? this.resolvedModel;\n\n const response = await fetch(`${this.serverUrl}/generate`, {\n method: 'POST',\n headers: this.authHeaders(),\n body: JSON.stringify({\n prompt,\n model,\n ...(options.system !== undefined ? { system: options.system } : {}),\n ...(options.images !== undefined ? { images: options.images } : {}),\n ...(options.documents !== undefined ? { documents: options.documents } : {}),\n ...(options.pdf !== undefined ? { pdf: options.pdf } : {}),\n ...(options.maxTokens !== undefined ? { maxTokens: options.maxTokens } : {}),\n ...(options.temperature !== undefined ? { temperature: options.temperature } : {}),\n }),\n });\n\n if (!response.ok) {\n let errorMessage = `AI server returned HTTP ${response.status} for /generate`;\n try {\n const errorData = (await response.json()) as { message?: string; error?: string };\n if (errorData?.error) errorMessage = errorData.error;\n else if (errorData?.message) errorMessage = errorData.message;\n } catch { }\n throw new Error(errorMessage);\n }\n\n const data = (await response.json()) as {\n text?: string;\n content?: string;\n message?: string;\n };\n\n return data.text ?? data.content ?? data.message ?? '';\n }\n\n /**\n * Generate a streaming response for a prompt via Server-Sent Events (SSE).\n *\n * Calls: POST {serverUrl}/stream\n * Body: { prompt, model, system?, maxTokens?, temperature? }\n * Expected response: `text/event-stream` with `data: <chunk>` lines.\n *\n * @param prompt - The user prompt string\n * @param options - Optional overrides (model, system, maxTokens, temperature)\n * @returns An async generator yielding string chunks from the server\n *\n * @example\n * ```typescript\n * const stream = ai.stream('Tell me a story');\n * for await (const chunk of stream) {\n * process.stdout.write(chunk);\n * }\n * ```\n */\n async *stream(prompt: string, options: GenerateOptions = {}): AsyncGenerator<string> {\n this.assertInitialized();\n const model = options.model ?? this.resolvedModel;\n\n const response = await fetch(`${this.serverUrl}/stream`, {\n method: 'POST',\n headers: {\n ...this.authHeaders(),\n Accept: 'text/event-stream',\n },\n body: JSON.stringify({\n prompt,\n model,\n ...(options.system !== undefined ? { system: options.system } : {}),\n ...(options.images !== undefined ? { images: options.images } : {}),\n ...(options.documents !== undefined ? { documents: options.documents } : {}),\n ...(options.pdf !== undefined ? { pdf: options.pdf } : {}),\n ...(options.maxTokens !== undefined ? { maxTokens: options.maxTokens } : {}),\n ...(options.temperature !== undefined ? { temperature: options.temperature } : {}),\n }),\n });\n\n if (!response.ok) {\n let errorMessage = `AI server returned HTTP ${response.status} for /stream`;\n try {\n const errorData = (await response.json()) as { message?: string; error?: string };\n if (errorData?.error) errorMessage = errorData.error;\n else if (errorData?.message) errorMessage = errorData.message;\n } catch { }\n throw new Error(errorMessage);\n }\n\n if (!response.body) {\n throw new Error('AI server returned no response body for /stream');\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 if (done) break;\n\n buffer += decoder.decode(value, { stream: true });\n const lines = buffer.split('\\n');\n // Keep the last (potentially incomplete) line in the buffer\n buffer = lines.pop() ?? '';\n\n for (const line of lines) {\n if (line.startsWith('data: ')) {\n const raw = line.slice(6).trim();\n if (raw === '[DONE]') return;\n try {\n const parsed = JSON.parse(raw) as {\n text?: string;\n content?: string;\n delta?: { content?: string };\n };\n const chunk =\n parsed.text ??\n parsed.content ??\n parsed.delta?.content ??\n '';\n if (chunk) yield chunk;\n } catch {\n // raw string chunk (non-JSON SSE)\n if (raw) yield raw;\n }\n }\n }\n }\n } finally {\n reader.releaseLock();\n }\n }\n\n /**\n * Similar to stream() but returns the raw fetch Response object.\n * Useful when you want to proxy the Server-Sent Events stream directly to a frontend client\n * (e.g., returning this directly from a Next.js API route).\n *\n * @param prompt - The user prompt string\n * @param options - Optional overrides (model, system, maxTokens, temperature)\n * @returns A Promise resolving to the native fetch Response\n */\n async streamResponse(prompt: string, options: GenerateOptions = {}): Promise<Response> {\n this.assertInitialized();\n const model = options.model ?? this.resolvedModel;\n\n const response = await fetch(`${this.serverUrl}/stream`, {\n method: 'POST',\n headers: {\n ...this.authHeaders(),\n Accept: 'text/event-stream',\n },\n body: JSON.stringify({\n prompt,\n model,\n ...(options.system !== undefined ? { system: options.system } : {}),\n ...(options.images !== undefined ? { images: options.images } : {}),\n ...(options.documents !== undefined ? { documents: options.documents } : {}),\n ...(options.pdf !== undefined ? { pdf: options.pdf } : {}),\n ...(options.maxTokens !== undefined ? { maxTokens: options.maxTokens } : {}),\n ...(options.temperature !== undefined ? { temperature: options.temperature } : {}),\n }),\n });\n\n if (!response.ok) {\n let errorMessage = `AI server returned HTTP ${response.status} for /stream`;\n try {\n const errorData = (await response.json()) as { message?: string; error?: string };\n if (errorData?.error) errorMessage = errorData.error;\n else if (errorData?.message) errorMessage = errorData.message;\n } catch { }\n throw new Error(errorMessage);\n }\n console.log(response)\n return response;\n }\n\n /**\n * Multi-turn conversation streaming: pass a full message history.\n * Calls POST /stream with the full { messages } payload.\n *\n * @param messages - Full conversation history\n * @param options - Optional overrides (model, maxTokens, temperature)\n * @returns An async generator yielding string chunks from the server\n */\n async *streamChat(messages: ChatMessage[], options: GenerateOptions = {}): AsyncGenerator<string> {\n this.assertInitialized();\n\n if (!messages.length) {\n throw new Error('Messages array cannot be empty for streamChat');\n }\n\n const model = options.model ?? this.resolvedModel;\n\n const response = await fetch(`${this.serverUrl}/stream`, {\n method: 'POST',\n headers: {\n ...this.authHeaders(),\n Accept: 'text/event-stream',\n },\n body: JSON.stringify({\n messages,\n model,\n ...(options.system !== undefined ? { system: options.system } : {}),\n ...(options.images !== undefined ? { images: options.images } : {}),\n ...(options.documents !== undefined ? { documents: options.documents } : {}),\n ...(options.pdf !== undefined ? { pdf: options.pdf } : {}),\n ...(options.maxTokens !== undefined ? { maxTokens: options.maxTokens } : {}),\n ...(options.temperature !== undefined ? { temperature: options.temperature } : {}),\n }),\n });\n\n if (!response.ok) {\n let errorMessage = `AI server returned HTTP ${response.status} for /stream`;\n try {\n const errorData = (await response.json()) as { message?: string; error?: string };\n if (errorData?.error) errorMessage = errorData.error;\n else if (errorData?.message) errorMessage = errorData.message;\n } catch { }\n throw new Error(errorMessage);\n }\n\n if (!response.body) {\n throw new Error('AI server returned no response body for /stream');\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 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.startsWith('data: ')) {\n const raw = line.slice(6).trim();\n if (raw === '[DONE]') return;\n try {\n const parsed = JSON.parse(raw) as {\n text?: string;\n content?: string;\n delta?: { content?: string };\n };\n const chunk =\n parsed.text ??\n parsed.content ??\n parsed.delta?.content ??\n '';\n if (chunk) yield chunk;\n } catch {\n if (raw) yield raw;\n }\n }\n }\n }\n } finally {\n reader.releaseLock();\n }\n }\n\n /**\n * Similar to streamChat() but returns the raw fetch Response object.\n * Useful for proxying the Server-Sent Events stream directly to a frontend client.\n *\n * @param messages - Full conversation history\n * @param options - Optional overrides (model, maxTokens, temperature)\n * @returns A Promise resolving to the native fetch Response\n */\n async streamChatResponse(messages: ChatMessage[], options: GenerateOptions = {}): Promise<Response> {\n this.assertInitialized();\n\n if (!messages.length) {\n throw new Error('Messages array cannot be empty for streamChatResponse');\n }\n\n const model = options.model ?? this.resolvedModel;\n\n const response = await fetch(`${this.serverUrl}/stream`, {\n method: 'POST',\n headers: {\n ...this.authHeaders(),\n Accept: 'text/event-stream',\n },\n body: JSON.stringify({\n messages,\n model,\n ...(options.system !== undefined ? { system: options.system } : {}),\n ...(options.images !== undefined ? { images: options.images } : {}),\n ...(options.documents !== undefined ? { documents: options.documents } : {}),\n ...(options.pdf !== undefined ? { pdf: options.pdf } : {}),\n ...(options.maxTokens !== undefined ? { maxTokens: options.maxTokens } : {}),\n ...(options.temperature !== undefined ? { temperature: options.temperature } : {}),\n }),\n });\n\n if (!response.ok) {\n let errorMessage = `AI server returned HTTP ${response.status} for /stream`;\n try {\n const errorData = (await response.json()) as { message?: string; error?: string };\n if (errorData?.error) errorMessage = errorData.error;\n else if (errorData?.message) errorMessage = errorData.message;\n } catch { }\n throw new Error(errorMessage);\n }\n\n return response;\n }\n\n /**\n * Multi-turn conversation: pass a full message history.\n *\n * Calls: POST {serverUrl}/chat\n * Body: { messages, model, maxTokens?, temperature? }\n * Expected response: { text: string } or { content: string } or { message: string }\n *\n * @param messages - Full conversation history\n * @param options - Optional overrides (model, maxTokens, temperature)\n * @returns The assistant's response text\n */\n async chat(messages: ChatMessage[], options: GenerateOptions = {}): Promise<string> {\n this.assertInitialized();\n const model = options.model ?? this.resolvedModel;\n\n const response = await fetch(`${this.serverUrl}/chat`, {\n method: 'POST',\n headers: this.authHeaders(),\n body: JSON.stringify({\n messages,\n model,\n ...(options.images !== undefined ? { images: options.images } : {}),\n ...(options.documents !== undefined ? { documents: options.documents } : {}),\n ...(options.pdf !== undefined ? { pdf: options.pdf } : {}),\n ...(options.maxTokens !== undefined ? { maxTokens: options.maxTokens } : {}),\n ...(options.temperature !== undefined ? { temperature: options.temperature } : {}),\n }),\n });\n\n if (!response.ok) {\n let errorMessage = `AI server returned HTTP ${response.status} for /chat`;\n try {\n const errorData = (await response.json()) as { message?: string; error?: string };\n if (errorData?.error) errorMessage = errorData.error;\n else if (errorData?.message) errorMessage = errorData.message;\n } catch { }\n throw new Error(errorMessage);\n }\n\n const data = (await response.json()) as {\n text?: string;\n content?: string;\n message?: string;\n };\n\n return data.text ?? data.content ?? data.message ?? '';\n }\n}\n","/**\n * @krutai/ai-provider — AI Provider package for KrutAI\n *\n * A fetch-based wrapper that calls your deployed LangChain backend server.\n * The user's API key is validated against the server before any AI call is made.\n *\n * @example Basic usage\n * ```typescript\n * import { krutAI } from '@krutai/ai-provider';\n *\n * const ai = krutAI({\n * apiKey: process.env.KRUTAI_API_KEY!,\n * serverUrl: 'https://krut.ai',\n * });\n *\n * await ai.initialize(); // validates key with server\n *\n * const text = await ai.generate('Write a poem about TypeScript');\n * console.log(text);\n * ```\n *\n * @example With custom model\n * ```typescript\n * const ai = krutAI({\n * apiKey: process.env.KRUTAI_API_KEY!,\n * serverUrl: 'https://krut.ai',\n * model: 'gpt-4o',\n * });\n * await ai.initialize();\n * const text = await ai.generate('Hello!');\n * ```\n *\n * @example Streaming\n * ```typescript\n * const ai = krutAI({\n * apiKey: process.env.KRUTAI_API_KEY!,\n * serverUrl: 'https://krut.ai',\n * });\n * await ai.initialize();\n *\n * const stream = ai.stream('Tell me a story');\n * for await (const chunk of stream) {\n * process.stdout.write(chunk);\n * }\n * ```\n *\n * @packageDocumentation\n */\n\nimport type { KrutAIProviderConfig } from './types';\nimport { DEFAULT_MODEL } from './types';\nimport { KrutAIProvider } from './client';\n\nexport { KrutAIProvider } from './client';\nexport { KrutAIKeyValidationError } from './client';\nexport {\n validateApiKeyWithService as validateApiKey,\n validateApiKeyFormat,\n} from 'krutai';\nexport type { KrutAIProviderConfig, GenerateOptions, ChatMessage } from './types';\nexport { DEFAULT_MODEL } from './types';\n\n/**\n * krutAI — convenience factory (mirrors `krutAuth` in @krutai/auth).\n *\n * Creates a `KrutAIProvider` instance configured to call your LangChain server.\n *\n * @param config - Provider configuration (`apiKey` and `serverUrl` are required)\n * @returns A `KrutAIProvider` instance — call `.initialize()` before use\n *\n * @example\n * ```typescript\n * import { krutAI } from '@krutai/ai-provider';\n *\n * const ai = krutAI({\n * apiKey: process.env.KRUTAI_API_KEY!,\n * serverUrl: 'https://krut.ai',\n * });\n *\n * await ai.initialize();\n * const text = await ai.generate('Hello!');\n * ```\n */\nexport function krutAI(\n config: KrutAIProviderConfig & { model?: string }\n): KrutAIProvider {\n return new KrutAIProvider({\n model: DEFAULT_MODEL,\n ...config,\n });\n}\n\n// Package metadata\nexport const VERSION = '0.2.0';\n"]}
1
+ {"version":3,"sources":["../src/types.ts","../src/client.ts","../src/index.ts"],"names":["validateApiKey"],"mappings":"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","file":"index.mjs","sourcesContent":["/**\n * Types for @krutai/ai-provider\n */\n\n/**\n * Default model identifier sent to the LangChain server when no model is specified.\n * Your server can use this value to route to its own default model.\n */\nexport const DEFAULT_MODEL = 'default' as const;\n\n/**\n * Default base URL for the LangChain backend server.\n * Used when no serverUrl is provided in the config.\n */\nexport const DEFAULT_SERVER_URL = 'http://localhost:8000' as const;\n\n/**\n * Configuration options for KrutAIProvider\n */\nexport interface KrutAIProviderConfig {\n /**\n * KrutAI API key.\n * Validated against the LangChain server before use.\n * Optional: defaults to process.env.KRUTAI_API_KEY\n */\n apiKey?: string;\n\n /**\n * Base URL of your deployed LangChain backend server.\n * @default \"http://localhost:8000\"\n * @example \"https://ai.krut.ai\"\n */\n serverUrl?: string;\n\n /**\n * The AI model to use (passed to the server).\n * The server decides what to do with this value.\n * @default \"default\"\n */\n model?: string;\n\n /**\n * Whether to validate the API key against the server on initialization.\n * Set to false to skip the validation round-trip (e.g. in tests).\n * @default true\n */\n validateOnInit?: boolean;\n}\n\n/**\n * A part of a multimodal message\n */\nexport interface TextContentPart {\n type: 'text';\n text: string;\n}\n\nexport interface ImageContentPart {\n type: 'image_url';\n image_url: {\n url: string; // Base64 data URI or HTTP(S) URL\n detail?: 'low' | 'high' | 'auto';\n };\n}\n\nexport type ContentPart = TextContentPart | ImageContentPart;\nexport type MessageContent = string | ContentPart[];\n\n/**\n * A single chat message\n */\nexport interface ChatMessage {\n role: 'user' | 'assistant' | 'system';\n content: MessageContent;\n}\n\n/**\n * Options for a single generate / stream / chat call\n */\nexport interface GenerateOptions {\n /**\n * Override the model for this specific call.\n */\n model?: string;\n\n /**\n * System prompt (prepended as a system message).\n */\n system?: string;\n\n /**\n * Maximum tokens to generate.\n */\n maxTokens?: number;\n\n /**\n * Temperature (0–2).\n */\n temperature?: number;\n\n /**\n * Array of image URLs or base64 data URIs to include with the request.\n */\n images?: string[];\n\n /**\n * Array of document URLs or base64 data URIs (e.g. PDFs) to include with the request.\n */\n documents?: string[];\n\n /**\n * Array of PDF URLs or base64 data URIs to include with the request.\n */\n pdf?: string[];\n}\n","import type { KrutAIProviderConfig, GenerateOptions, ChatMessage } from './types';\nimport { DEFAULT_MODEL, DEFAULT_SERVER_URL } from './types';\nimport {\n validateApiKeyWithService as validateApiKey,\n validateApiKeyFormat,\n ApiKeyValidationError as KrutAIKeyValidationError,\n} from 'krutai';\n\nexport { KrutAIKeyValidationError };\n\n/**\n * KrutAIProvider — fetch-based AI provider for KrutAI\n *\n * Calls your deployed LangChain backend server for all AI operations.\n * The API key is validated against the server before use.\n *\n * @example\n * ```typescript\n * import { KrutAIProvider } from '@krutai/ai-provider';\n *\n * // Using local dev server (http://localhost:8000 by default)\n * const ai = new KrutAIProvider({\n * apiKey: process.env.KRUTAI_API_KEY!,\n * });\n *\n * // Or point to a production server\n * const aiProd = new KrutAIProvider({\n * apiKey: process.env.KRUTAI_API_KEY!,\n * serverUrl: 'https://ai.krut.ai',\n * });\n *\n * await ai.initialize(); // validates key against server\n *\n * const text = await ai.generate('Tell me a joke');\n * console.log(text);\n * ```\n */\nexport class KrutAIProvider {\n private readonly apiKey: string;\n private readonly serverUrl: string;\n private readonly resolvedModel: string;\n private readonly config: KrutAIProviderConfig;\n\n private initialized = false;\n\n constructor(config: KrutAIProviderConfig) {\n this.config = config;\n this.apiKey = config.apiKey || process.env.KRUTAI_API_KEY || '';\n this.serverUrl = (config.serverUrl ?? DEFAULT_SERVER_URL).replace(/\\/$/, ''); // strip trailing slash\n this.resolvedModel = config.model ?? DEFAULT_MODEL;\n\n // Basic format check immediately on construction\n validateApiKeyFormat(this.apiKey);\n\n // If validation is disabled, mark as ready immediately\n if (config.validateOnInit === false) {\n this.initialized = true;\n }\n }\n\n /**\n * Initialize the provider.\n * Validates the API key against the LangChain server, then marks provider as ready.\n *\n * @throws {KrutAIKeyValidationError} if the key is rejected or the server is unreachable\n */\n async initialize(): Promise<void> {\n if (this.initialized) return;\n\n if (this.config.validateOnInit !== false) {\n await validateApiKey(this.apiKey, this.serverUrl);\n }\n\n this.initialized = true;\n }\n\n /**\n * Returns the currently configured default model.\n */\n getModel(): string {\n return this.resolvedModel;\n }\n\n /**\n * Returns whether the provider has been initialized.\n */\n isInitialized(): boolean {\n return this.initialized;\n }\n\n // ---------------------------------------------------------------------------\n // Private helpers\n // ---------------------------------------------------------------------------\n\n private assertInitialized(): void {\n if (!this.initialized) {\n throw new Error(\n 'KrutAIProvider not initialized. Call initialize() first or set validateOnInit to false.'\n );\n }\n }\n\n /** Common request headers sent to the server on every AI call. */\n private authHeaders(): Record<string, string> {\n return {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${this.apiKey}`,\n 'x-api-key': this.apiKey,\n };\n }\n\n // ---------------------------------------------------------------------------\n // Public AI Methods\n // ---------------------------------------------------------------------------\n\n /**\n * Similar to streamChat() but returns the raw fetch Response object.\n * Useful for proxying the Server-Sent Events stream directly to a frontend client.\n *\n * @param messages - Full conversation history\n * @param options - Optional overrides (model, maxTokens, temperature)\n * @returns A Promise resolving to the native fetch Response\n */\n async streamChatResponse(messages: ChatMessage[], options: GenerateOptions = {}): Promise<Response> {\n this.assertInitialized();\n\n if (!messages.length) {\n throw new Error('Messages array cannot be empty for streamChatResponse');\n }\n\n const model = options.model ?? this.resolvedModel;\n\n const response = await fetch(`${this.serverUrl}/stream`, {\n method: 'POST',\n headers: {\n ...this.authHeaders(),\n Accept: 'text/event-stream',\n },\n body: JSON.stringify({\n messages,\n model,\n ...(options.system !== undefined ? { system: options.system } : {}),\n ...(options.images !== undefined ? { images: options.images } : {}),\n ...(options.documents !== undefined ? { documents: options.documents } : {}),\n ...(options.pdf !== undefined ? { pdf: options.pdf } : {}),\n ...(options.maxTokens !== undefined ? { maxTokens: options.maxTokens } : {}),\n ...(options.temperature !== undefined ? { temperature: options.temperature } : {}),\n }),\n });\n\n if (!response.ok) {\n let errorMessage = `AI server returned HTTP ${response.status} for /stream`;\n try {\n const errorData = (await response.json()) as { message?: string; error?: string };\n if (errorData?.error) errorMessage = errorData.error;\n else if (errorData?.message) errorMessage = errorData.message;\n } catch { }\n throw new Error(errorMessage);\n }\n\n return response;\n }\n\n /**\n * Generate a response for a prompt (non-streaming).\n *\n * Calls: POST {serverUrl}/generate\n * Body: { prompt, model, system?, maxTokens?, temperature? }\n * Expected response: { text: string } or { content: string } or { message: string }\n *\n * @param prompt - The user prompt string\n * @param options - Optional overrides (model, system, maxTokens, temperature)\n * @returns The assistant's response text\n */\n async chat(prompt: string, options: GenerateOptions = {}): Promise<string> {\n this.assertInitialized();\n const model = options.model ?? this.resolvedModel;\n\n const response = await fetch(`${this.serverUrl}/generate`, {\n method: 'POST',\n headers: this.authHeaders(),\n body: JSON.stringify({\n prompt,\n model,\n ...(options.system !== undefined ? { system: options.system } : {}),\n ...(options.images !== undefined ? { images: options.images } : {}),\n ...(options.documents !== undefined ? { documents: options.documents } : {}),\n ...(options.pdf !== undefined ? { pdf: options.pdf } : {}),\n ...(options.maxTokens !== undefined ? { maxTokens: options.maxTokens } : {}),\n ...(options.temperature !== undefined ? { temperature: options.temperature } : {}),\n }),\n });\n\n if (!response.ok) {\n let errorMessage = `AI server returned HTTP ${response.status} for /generate`;\n try {\n const errorData = (await response.json()) as { message?: string; error?: string };\n if (errorData?.error) errorMessage = errorData.error;\n else if (errorData?.message) errorMessage = errorData.message;\n } catch { }\n throw new Error(errorMessage);\n }\n\n const data = (await response.json()) as {\n text?: string;\n content?: string;\n message?: string;\n };\n\n return data.text ?? data.content ?? data.message ?? '';\n }\n}\n","/**\n * @krutai/ai-provider — AI Provider package for KrutAI\n *\n * A fetch-based wrapper that calls your deployed LangChain backend server.\n * The user's API key is validated against the server before any AI call is made.\n *\n * @example Basic usage\n * ```typescript\n * import { krutAI } from '@krutai/ai-provider';\n *\n * const ai = krutAI({\n * apiKey: process.env.KRUTAI_API_KEY!,\n * serverUrl: 'https://krut.ai',\n * });\n *\n * await ai.initialize(); // validates key with server\n *\n * const text = await ai.chat('Write a poem about TypeScript');\n * console.log(text);\n * ```\n *\n * @example With custom model\n * ```typescript\n * const ai = krutAI({\n * apiKey: process.env.KRUTAI_API_KEY!,\n * serverUrl: 'https://krut.ai',\n * model: 'gpt-4o',\n * });\n * await ai.initialize();\n * const text = await ai.chat('Hello!');\n * ```\n *\n * @example Streaming\n * ```typescript\n * const ai = krutAI({\n * apiKey: process.env.KRUTAI_API_KEY!,\n * serverUrl: 'https://krut.ai',\n * });\n * await ai.initialize();\n *\n * const response = await ai.streamChatResponse([{ role: 'user', content: 'Tell me a story' }]);\n * // Example assumes you handle the SSE stream from the response body\n * ```\n *\n * @packageDocumentation\n */\n\nimport type { KrutAIProviderConfig } from './types';\nimport { DEFAULT_MODEL } from './types';\nimport { KrutAIProvider } from './client';\n\nexport { KrutAIProvider } from './client';\nexport { KrutAIKeyValidationError } from './client';\nexport {\n validateApiKeyWithService as validateApiKey,\n validateApiKeyFormat,\n} from 'krutai';\nexport type { KrutAIProviderConfig, GenerateOptions, ChatMessage } from './types';\nexport { DEFAULT_MODEL } from './types';\n\n/**\n * krutAI — convenience factory (mirrors `krutAuth` in @krutai/auth).\n *\n * Creates a `KrutAIProvider` instance configured to call your LangChain server.\n *\n * @param config - Provider configuration (`apiKey` and `serverUrl` are required)\n * @returns A `KrutAIProvider` instance — call `.initialize()` before use\n *\n * @example\n * ```typescript\n * import { krutAI } from '@krutai/ai-provider';\n *\n * const ai = krutAI({\n * apiKey: process.env.KRUTAI_API_KEY!,\n * serverUrl: 'https://krut.ai',\n * });\n *\n * await ai.initialize();\n * const text = await ai.chat('Hello!');\n * ```\n */\nexport function krutAI(\n config: KrutAIProviderConfig & { model?: string }\n): KrutAIProvider {\n return new KrutAIProvider({\n model: DEFAULT_MODEL,\n ...config,\n });\n}\n\n// Package metadata\nexport const VERSION = '0.2.0';\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@krutai/ai-provider",
3
- "version": "0.2.12",
3
+ "version": "0.2.13",
4
4
  "description": "AI provider package for KrutAI — fetch-based client for your deployed LangChain server with API key validation",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",