@krutai/ai-provider 0.2.11 → 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 +37 -10
- package/README.md +36 -45
- package/dist/index.d.mts +19 -64
- package/dist/index.d.ts +19 -64
- package/dist/index.js +14 -235
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +14 -235
- package/dist/index.mjs.map +1 -1
- package/package.json +4 -3
package/dist/index.js
CHANGED
|
@@ -67,232 +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.maxTokens !== void 0 ? { maxTokens: options.maxTokens } : {},
|
|
93
|
-
...options.temperature !== void 0 ? { temperature: options.temperature } : {}
|
|
94
|
-
})
|
|
95
|
-
});
|
|
96
|
-
if (!response.ok) {
|
|
97
|
-
let errorMessage = `AI server returned HTTP ${response.status} for /generate`;
|
|
98
|
-
try {
|
|
99
|
-
const errorData = await response.json();
|
|
100
|
-
if (errorData?.error) errorMessage = errorData.error;
|
|
101
|
-
else if (errorData?.message) errorMessage = errorData.message;
|
|
102
|
-
} catch {
|
|
103
|
-
}
|
|
104
|
-
throw new Error(errorMessage);
|
|
105
|
-
}
|
|
106
|
-
const data = await response.json();
|
|
107
|
-
return data.text ?? data.content ?? data.message ?? "";
|
|
108
|
-
}
|
|
109
|
-
/**
|
|
110
|
-
* Generate a streaming response for a prompt via Server-Sent Events (SSE).
|
|
111
|
-
*
|
|
112
|
-
* Calls: POST {serverUrl}/stream
|
|
113
|
-
* Body: { prompt, model, system?, maxTokens?, temperature? }
|
|
114
|
-
* Expected response: `text/event-stream` with `data: <chunk>` lines.
|
|
115
|
-
*
|
|
116
|
-
* @param prompt - The user prompt string
|
|
117
|
-
* @param options - Optional overrides (model, system, maxTokens, temperature)
|
|
118
|
-
* @returns An async generator yielding string chunks from the server
|
|
119
|
-
*
|
|
120
|
-
* @example
|
|
121
|
-
* ```typescript
|
|
122
|
-
* const stream = ai.stream('Tell me a story');
|
|
123
|
-
* for await (const chunk of stream) {
|
|
124
|
-
* process.stdout.write(chunk);
|
|
125
|
-
* }
|
|
126
|
-
* ```
|
|
127
|
-
*/
|
|
128
|
-
async *stream(prompt, options = {}) {
|
|
129
|
-
this.assertInitialized();
|
|
130
|
-
const model = options.model ?? this.resolvedModel;
|
|
131
|
-
const response = await fetch(`${this.serverUrl}/stream`, {
|
|
132
|
-
method: "POST",
|
|
133
|
-
headers: {
|
|
134
|
-
...this.authHeaders(),
|
|
135
|
-
Accept: "text/event-stream"
|
|
136
|
-
},
|
|
137
|
-
body: JSON.stringify({
|
|
138
|
-
prompt,
|
|
139
|
-
model,
|
|
140
|
-
...options.system !== void 0 ? { system: options.system } : {},
|
|
141
|
-
...options.images !== void 0 ? { images: options.images } : {},
|
|
142
|
-
...options.maxTokens !== void 0 ? { maxTokens: options.maxTokens } : {},
|
|
143
|
-
...options.temperature !== void 0 ? { temperature: options.temperature } : {}
|
|
144
|
-
})
|
|
145
|
-
});
|
|
146
|
-
if (!response.ok) {
|
|
147
|
-
let errorMessage = `AI server returned HTTP ${response.status} for /stream`;
|
|
148
|
-
try {
|
|
149
|
-
const errorData = await response.json();
|
|
150
|
-
if (errorData?.error) errorMessage = errorData.error;
|
|
151
|
-
else if (errorData?.message) errorMessage = errorData.message;
|
|
152
|
-
} catch {
|
|
153
|
-
}
|
|
154
|
-
throw new Error(errorMessage);
|
|
155
|
-
}
|
|
156
|
-
if (!response.body) {
|
|
157
|
-
throw new Error("AI server returned no response body for /stream");
|
|
158
|
-
}
|
|
159
|
-
const reader = response.body.getReader();
|
|
160
|
-
const decoder = new TextDecoder();
|
|
161
|
-
let buffer = "";
|
|
162
|
-
try {
|
|
163
|
-
while (true) {
|
|
164
|
-
const { done, value } = await reader.read();
|
|
165
|
-
if (done) break;
|
|
166
|
-
buffer += decoder.decode(value, { stream: true });
|
|
167
|
-
const lines = buffer.split("\n");
|
|
168
|
-
buffer = lines.pop() ?? "";
|
|
169
|
-
for (const line of lines) {
|
|
170
|
-
if (line.startsWith("data: ")) {
|
|
171
|
-
const raw = line.slice(6).trim();
|
|
172
|
-
if (raw === "[DONE]") return;
|
|
173
|
-
try {
|
|
174
|
-
const parsed = JSON.parse(raw);
|
|
175
|
-
const chunk = parsed.text ?? parsed.content ?? parsed.delta?.content ?? "";
|
|
176
|
-
if (chunk) yield chunk;
|
|
177
|
-
} catch {
|
|
178
|
-
if (raw) yield raw;
|
|
179
|
-
}
|
|
180
|
-
}
|
|
181
|
-
}
|
|
182
|
-
}
|
|
183
|
-
} finally {
|
|
184
|
-
reader.releaseLock();
|
|
185
|
-
}
|
|
186
|
-
}
|
|
187
|
-
/**
|
|
188
|
-
* Similar to stream() but returns the raw fetch Response object.
|
|
189
|
-
* Useful when you want to proxy the Server-Sent Events stream directly to a frontend client
|
|
190
|
-
* (e.g., returning this directly from a Next.js API route).
|
|
191
|
-
*
|
|
192
|
-
* @param prompt - The user prompt string
|
|
193
|
-
* @param options - Optional overrides (model, system, maxTokens, temperature)
|
|
194
|
-
* @returns A Promise resolving to the native fetch Response
|
|
195
|
-
*/
|
|
196
|
-
async streamResponse(prompt, options = {}) {
|
|
197
|
-
this.assertInitialized();
|
|
198
|
-
const model = options.model ?? this.resolvedModel;
|
|
199
|
-
const response = await fetch(`${this.serverUrl}/stream`, {
|
|
200
|
-
method: "POST",
|
|
201
|
-
headers: {
|
|
202
|
-
...this.authHeaders(),
|
|
203
|
-
Accept: "text/event-stream"
|
|
204
|
-
},
|
|
205
|
-
body: JSON.stringify({
|
|
206
|
-
prompt,
|
|
207
|
-
model,
|
|
208
|
-
...options.system !== void 0 ? { system: options.system } : {},
|
|
209
|
-
...options.images !== void 0 ? { images: options.images } : {},
|
|
210
|
-
...options.maxTokens !== void 0 ? { maxTokens: options.maxTokens } : {},
|
|
211
|
-
...options.temperature !== void 0 ? { temperature: options.temperature } : {}
|
|
212
|
-
})
|
|
213
|
-
});
|
|
214
|
-
if (!response.ok) {
|
|
215
|
-
let errorMessage = `AI server returned HTTP ${response.status} for /stream`;
|
|
216
|
-
try {
|
|
217
|
-
const errorData = await response.json();
|
|
218
|
-
if (errorData?.error) errorMessage = errorData.error;
|
|
219
|
-
else if (errorData?.message) errorMessage = errorData.message;
|
|
220
|
-
} catch {
|
|
221
|
-
}
|
|
222
|
-
throw new Error(errorMessage);
|
|
223
|
-
}
|
|
224
|
-
return response;
|
|
225
|
-
}
|
|
226
|
-
/**
|
|
227
|
-
* Multi-turn conversation streaming: pass a full message history.
|
|
228
|
-
* Calls POST /stream with the full { messages } payload.
|
|
229
|
-
*
|
|
230
|
-
* @param messages - Full conversation history
|
|
231
|
-
* @param options - Optional overrides (model, maxTokens, temperature)
|
|
232
|
-
* @returns An async generator yielding string chunks from the server
|
|
233
|
-
*/
|
|
234
|
-
async *streamChat(messages, options = {}) {
|
|
235
|
-
this.assertInitialized();
|
|
236
|
-
if (!messages.length) {
|
|
237
|
-
throw new Error("Messages array cannot be empty for streamChat");
|
|
238
|
-
}
|
|
239
|
-
const model = options.model ?? this.resolvedModel;
|
|
240
|
-
const response = await fetch(`${this.serverUrl}/stream`, {
|
|
241
|
-
method: "POST",
|
|
242
|
-
headers: {
|
|
243
|
-
...this.authHeaders(),
|
|
244
|
-
Accept: "text/event-stream"
|
|
245
|
-
},
|
|
246
|
-
body: JSON.stringify({
|
|
247
|
-
messages,
|
|
248
|
-
model,
|
|
249
|
-
...options.system !== void 0 ? { system: options.system } : {},
|
|
250
|
-
...options.images !== void 0 ? { images: options.images } : {},
|
|
251
|
-
...options.maxTokens !== void 0 ? { maxTokens: options.maxTokens } : {},
|
|
252
|
-
...options.temperature !== void 0 ? { temperature: options.temperature } : {}
|
|
253
|
-
})
|
|
254
|
-
});
|
|
255
|
-
if (!response.ok) {
|
|
256
|
-
let errorMessage = `AI server returned HTTP ${response.status} for /stream`;
|
|
257
|
-
try {
|
|
258
|
-
const errorData = await response.json();
|
|
259
|
-
if (errorData?.error) errorMessage = errorData.error;
|
|
260
|
-
else if (errorData?.message) errorMessage = errorData.message;
|
|
261
|
-
} catch {
|
|
262
|
-
}
|
|
263
|
-
throw new Error(errorMessage);
|
|
264
|
-
}
|
|
265
|
-
if (!response.body) {
|
|
266
|
-
throw new Error("AI server returned no response body for /stream");
|
|
267
|
-
}
|
|
268
|
-
const reader = response.body.getReader();
|
|
269
|
-
const decoder = new TextDecoder();
|
|
270
|
-
let buffer = "";
|
|
271
|
-
try {
|
|
272
|
-
while (true) {
|
|
273
|
-
const { done, value } = await reader.read();
|
|
274
|
-
if (done) break;
|
|
275
|
-
buffer += decoder.decode(value, { stream: true });
|
|
276
|
-
const lines = buffer.split("\n");
|
|
277
|
-
buffer = lines.pop() ?? "";
|
|
278
|
-
for (const line of lines) {
|
|
279
|
-
if (line.startsWith("data: ")) {
|
|
280
|
-
const raw = line.slice(6).trim();
|
|
281
|
-
if (raw === "[DONE]") return;
|
|
282
|
-
try {
|
|
283
|
-
const parsed = JSON.parse(raw);
|
|
284
|
-
const chunk = parsed.text ?? parsed.content ?? parsed.delta?.content ?? "";
|
|
285
|
-
if (chunk) yield chunk;
|
|
286
|
-
} catch {
|
|
287
|
-
if (raw) yield raw;
|
|
288
|
-
}
|
|
289
|
-
}
|
|
290
|
-
}
|
|
291
|
-
}
|
|
292
|
-
} finally {
|
|
293
|
-
reader.releaseLock();
|
|
294
|
-
}
|
|
295
|
-
}
|
|
296
70
|
/**
|
|
297
71
|
* Similar to streamChat() but returns the raw fetch Response object.
|
|
298
72
|
* Useful for proxying the Server-Sent Events stream directly to a frontend client.
|
|
@@ -318,6 +92,8 @@ var KrutAIProvider = class {
|
|
|
318
92
|
model,
|
|
319
93
|
...options.system !== void 0 ? { system: options.system } : {},
|
|
320
94
|
...options.images !== void 0 ? { images: options.images } : {},
|
|
95
|
+
...options.documents !== void 0 ? { documents: options.documents } : {},
|
|
96
|
+
...options.pdf !== void 0 ? { pdf: options.pdf } : {},
|
|
321
97
|
...options.maxTokens !== void 0 ? { maxTokens: options.maxTokens } : {},
|
|
322
98
|
...options.temperature !== void 0 ? { temperature: options.temperature } : {}
|
|
323
99
|
})
|
|
@@ -335,32 +111,35 @@ var KrutAIProvider = class {
|
|
|
335
111
|
return response;
|
|
336
112
|
}
|
|
337
113
|
/**
|
|
338
|
-
*
|
|
114
|
+
* Generate a response for a prompt (non-streaming).
|
|
339
115
|
*
|
|
340
|
-
* Calls: POST {serverUrl}/
|
|
341
|
-
* Body: {
|
|
116
|
+
* Calls: POST {serverUrl}/generate
|
|
117
|
+
* Body: { prompt, model, system?, maxTokens?, temperature? }
|
|
342
118
|
* Expected response: { text: string } or { content: string } or { message: string }
|
|
343
119
|
*
|
|
344
|
-
* @param
|
|
345
|
-
* @param options - Optional overrides (model, maxTokens, temperature)
|
|
120
|
+
* @param prompt - The user prompt string
|
|
121
|
+
* @param options - Optional overrides (model, system, maxTokens, temperature)
|
|
346
122
|
* @returns The assistant's response text
|
|
347
123
|
*/
|
|
348
|
-
async chat(
|
|
124
|
+
async chat(prompt, options = {}) {
|
|
349
125
|
this.assertInitialized();
|
|
350
126
|
const model = options.model ?? this.resolvedModel;
|
|
351
|
-
const response = await fetch(`${this.serverUrl}/
|
|
127
|
+
const response = await fetch(`${this.serverUrl}/generate`, {
|
|
352
128
|
method: "POST",
|
|
353
129
|
headers: this.authHeaders(),
|
|
354
130
|
body: JSON.stringify({
|
|
355
|
-
|
|
131
|
+
prompt,
|
|
356
132
|
model,
|
|
133
|
+
...options.system !== void 0 ? { system: options.system } : {},
|
|
357
134
|
...options.images !== void 0 ? { images: options.images } : {},
|
|
135
|
+
...options.documents !== void 0 ? { documents: options.documents } : {},
|
|
136
|
+
...options.pdf !== void 0 ? { pdf: options.pdf } : {},
|
|
358
137
|
...options.maxTokens !== void 0 ? { maxTokens: options.maxTokens } : {},
|
|
359
138
|
...options.temperature !== void 0 ? { temperature: options.temperature } : {}
|
|
360
139
|
})
|
|
361
140
|
});
|
|
362
141
|
if (!response.ok) {
|
|
363
|
-
let errorMessage = `AI server returned HTTP ${response.status} for /
|
|
142
|
+
let errorMessage = `AI server returned HTTP ${response.status} for /generate`;
|
|
364
143
|
try {
|
|
365
144
|
const errorData = await response.json();
|
|
366
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","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.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.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.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 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.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.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.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"]}
|