@krutai/ai-provider 0.2.1 → 0.2.3

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
@@ -3,7 +3,7 @@
3
3
  ## Package Overview
4
4
 
5
5
  - **Name**: `@krutai/ai-provider`
6
- - **Version**: `0.2.0`
6
+ - **Version**: `0.2.2`
7
7
  - **Purpose**: AI provider for KrutAI — fetch-based client for your deployed LangChain server with API key validation
8
8
  - **Entry**: `src/index.ts` → `dist/index.{js,mjs,d.ts}`
9
9
  - **Build**: `tsup` (CJS + ESM, no external SDK deps)
@@ -11,7 +11,7 @@
11
11
  ## Architecture
12
12
 
13
13
  ```
14
- @krutai/ai-provider@0.2.0
14
+ @krutai/ai-provider@0.2.2
15
15
  └── peerDep: krutai (core utilities)
16
16
 
17
17
  AI Flow:
@@ -57,7 +57,8 @@ import { krutAI } from '@krutai/ai-provider';
57
57
 
58
58
  const ai = krutAI({
59
59
  apiKey: process.env.KRUTAI_API_KEY!,
60
- serverUrl: 'https://ai.yourapp.com',
60
+ // uses http://localhost:8000 by default for local development
61
+ // serverUrl: 'https://krut.ai',
61
62
  });
62
63
 
63
64
  await ai.initialize(); // validates key with server
@@ -72,7 +73,7 @@ import { KrutAIProvider } from '@krutai/ai-provider';
72
73
 
73
74
  const ai = new KrutAIProvider({
74
75
  apiKey: process.env.KRUTAI_API_KEY!,
75
- serverUrl: 'https://ai.yourapp.com',
76
+ // serverUrl: 'https://krut.ai', // Optional: defaults to localhost:8000
76
77
  model: 'gpt-4o', // optional, default: 'default'
77
78
  validateOnInit: true, // default: true
78
79
  });
@@ -95,7 +96,7 @@ await ai.initialize();
95
96
  ```typescript
96
97
  interface KrutAIProviderConfig {
97
98
  apiKey: string; // KrutAI API key — validated with server (required)
98
- serverUrl: string; // Base URL of deployed LangChain server (required)
99
+ serverUrl?: string; // Base URL of deployed LangChain server (default: 'http://localhost:8000')
99
100
  model?: string; // default: 'default'
100
101
  validateOnInit?: boolean; // default: true
101
102
  }
@@ -141,7 +142,7 @@ export { validateApiKey, validateApiKeyFormat, KrutAIKeyValidationError };
141
142
 
142
143
  ## Important Notes
143
144
 
144
- 1. **`serverUrl` is required**point it at your deployed LangChain backend
145
+ 1. **`serverUrl` defaults to `http://localhost:8000`** make sure to override this with your deployed LangChain backend in production
145
146
  2. **`apiKey` is validated server-side** — the server controls what keys are valid
146
147
  3. **Streaming uses SSE** — server must respond with `Content-Type: text/event-stream`
147
148
  4. **No external SDK needed** — uses native `fetch` only (Node 18+, browser, edge runtimes)
package/README.md CHANGED
@@ -23,7 +23,8 @@ import { krutAI } from '@krutai/ai-provider';
23
23
 
24
24
  const ai = krutAI({
25
25
  apiKey: 'your-krutai-api-key',
26
- serverUrl: 'https://ai.yourapp.com', // your deployed LangChain server
26
+ // Optional: omitted to use the default local dev server ('http://localhost:8000')
27
+ // serverUrl: 'https://krut.ai',
27
28
  });
28
29
 
29
30
  await ai.initialize(); // validates key with your server
@@ -40,7 +41,7 @@ console.log(text);
40
41
  ```typescript
41
42
  const ai = krutAI({
42
43
  apiKey: process.env.KRUTAI_API_KEY!,
43
- serverUrl: 'https://ai.yourapp.com',
44
+ serverUrl: 'https://krut.ai', // Override default for production
44
45
  model: 'gpt-4o', // optional — server's default is used if omitted
45
46
  });
46
47
 
@@ -60,7 +61,7 @@ console.log(text);
60
61
  ```typescript
61
62
  const ai = krutAI({
62
63
  apiKey: process.env.KRUTAI_API_KEY!,
63
- serverUrl: 'https://ai.yourapp.com',
64
+ // uses http://localhost:8000 by default
64
65
  });
65
66
 
66
67
  await ai.initialize();
@@ -76,7 +77,6 @@ for await (const chunk of ai.stream('Tell me a short story')) {
76
77
  ```typescript
77
78
  const ai = krutAI({
78
79
  apiKey: process.env.KRUTAI_API_KEY!,
79
- serverUrl: 'https://ai.yourapp.com',
80
80
  });
81
81
 
82
82
  await ai.initialize();
@@ -129,9 +129,9 @@ Factory function — preferred way to create a provider.
129
129
 
130
130
  ```typescript
131
131
  const ai = krutAI({
132
- apiKey: string; // required
133
- serverUrl: string; // requiredbase URL of your LangChain server
134
- model?: string; // optional — passed to server (default: 'default')
132
+ apiKey: string; // required — KrutAI API key
133
+ serverUrl?: string; // optionaldefaults to 'http://localhost:8000'
134
+ model?: string; // optional — passed to server (default: 'default')
135
135
  validateOnInit?: boolean; // optional — default: true
136
136
  });
137
137
  ```
package/dist/index.d.mts CHANGED
@@ -1,3 +1,5 @@
1
+ export { ApiKeyValidationError as KrutAIKeyValidationError, validateApiKeyWithService as validateApiKey, validateApiKeyFormat } from 'krutai';
2
+
1
3
  /**
2
4
  * Types for @krutai/ai-provider
3
5
  */
@@ -13,9 +15,9 @@ interface KrutAIProviderConfig {
13
15
  /**
14
16
  * KrutAI API key.
15
17
  * Validated against the LangChain server before use.
16
- * @required
18
+ * Optional: defaults to process.env.KRUTAI_API_KEY
17
19
  */
18
- apiKey: string;
20
+ apiKey?: string;
19
21
  /**
20
22
  * Base URL of your deployed LangChain backend server.
21
23
  * @default "http://localhost:8000"
@@ -64,40 +66,6 @@ interface GenerateOptions {
64
66
  temperature?: number;
65
67
  }
66
68
 
67
- /**
68
- * API Key Validator for @krutai/ai-provider
69
- *
70
- * Validates the KrutAI API key by calling the deployed LangChain server's
71
- * validation endpoint. The server is expected to respond with { valid: true }
72
- * for a valid key and { valid: false } (or a non-2xx status) for an invalid one.
73
- *
74
- * Validation endpoint called:
75
- * POST {serverUrl}/validate
76
- * Headers: { 'Content-Type': 'application/json', 'x-api-key': apiKey }
77
- * Body: { "apiKey": "<key>" }
78
- * Expected Response: { "valid": true }
79
- */
80
- declare class KrutAIKeyValidationError extends Error {
81
- constructor(message: string);
82
- }
83
- /**
84
- * Basic sanity check — ensures the key is a non-empty string.
85
- * @throws {KrutAIKeyValidationError}
86
- */
87
- declare function validateApiKeyFormat(apiKey: string): void;
88
- /**
89
- * Validates the API key against the deployed LangChain server.
90
- *
91
- * Sends a POST request to `{serverUrl}/validate` and expects:
92
- * - HTTP 2xx status
93
- * - JSON body: `{ "valid": true }`
94
- *
95
- * @param apiKey - The API key to validate
96
- * @param serverUrl - Base URL of the LangChain backend (e.g. "https://ai.yourapp.com")
97
- * @throws {KrutAIKeyValidationError}
98
- */
99
- declare function validateApiKey(apiKey: string, serverUrl: string): Promise<boolean>;
100
-
101
69
  /**
102
70
  * KrutAIProvider — fetch-based AI provider for KrutAI
103
71
  *
@@ -271,4 +239,4 @@ declare function krutAI(config: KrutAIProviderConfig & {
271
239
  }): KrutAIProvider;
272
240
  declare const VERSION = "0.2.0";
273
241
 
274
- export { type ChatMessage, DEFAULT_MODEL, type GenerateOptions, KrutAIKeyValidationError, KrutAIProvider, type KrutAIProviderConfig, VERSION, krutAI, validateApiKey, validateApiKeyFormat };
242
+ export { type ChatMessage, DEFAULT_MODEL, type GenerateOptions, KrutAIProvider, type KrutAIProviderConfig, VERSION, krutAI };
package/dist/index.d.ts CHANGED
@@ -1,3 +1,5 @@
1
+ export { ApiKeyValidationError as KrutAIKeyValidationError, validateApiKeyWithService as validateApiKey, validateApiKeyFormat } from 'krutai';
2
+
1
3
  /**
2
4
  * Types for @krutai/ai-provider
3
5
  */
@@ -13,9 +15,9 @@ interface KrutAIProviderConfig {
13
15
  /**
14
16
  * KrutAI API key.
15
17
  * Validated against the LangChain server before use.
16
- * @required
18
+ * Optional: defaults to process.env.KRUTAI_API_KEY
17
19
  */
18
- apiKey: string;
20
+ apiKey?: string;
19
21
  /**
20
22
  * Base URL of your deployed LangChain backend server.
21
23
  * @default "http://localhost:8000"
@@ -64,40 +66,6 @@ interface GenerateOptions {
64
66
  temperature?: number;
65
67
  }
66
68
 
67
- /**
68
- * API Key Validator for @krutai/ai-provider
69
- *
70
- * Validates the KrutAI API key by calling the deployed LangChain server's
71
- * validation endpoint. The server is expected to respond with { valid: true }
72
- * for a valid key and { valid: false } (or a non-2xx status) for an invalid one.
73
- *
74
- * Validation endpoint called:
75
- * POST {serverUrl}/validate
76
- * Headers: { 'Content-Type': 'application/json', 'x-api-key': apiKey }
77
- * Body: { "apiKey": "<key>" }
78
- * Expected Response: { "valid": true }
79
- */
80
- declare class KrutAIKeyValidationError extends Error {
81
- constructor(message: string);
82
- }
83
- /**
84
- * Basic sanity check — ensures the key is a non-empty string.
85
- * @throws {KrutAIKeyValidationError}
86
- */
87
- declare function validateApiKeyFormat(apiKey: string): void;
88
- /**
89
- * Validates the API key against the deployed LangChain server.
90
- *
91
- * Sends a POST request to `{serverUrl}/validate` and expects:
92
- * - HTTP 2xx status
93
- * - JSON body: `{ "valid": true }`
94
- *
95
- * @param apiKey - The API key to validate
96
- * @param serverUrl - Base URL of the LangChain backend (e.g. "https://ai.yourapp.com")
97
- * @throws {KrutAIKeyValidationError}
98
- */
99
- declare function validateApiKey(apiKey: string, serverUrl: string): Promise<boolean>;
100
-
101
69
  /**
102
70
  * KrutAIProvider — fetch-based AI provider for KrutAI
103
71
  *
@@ -271,4 +239,4 @@ declare function krutAI(config: KrutAIProviderConfig & {
271
239
  }): KrutAIProvider;
272
240
  declare const VERSION = "0.2.0";
273
241
 
274
- export { type ChatMessage, DEFAULT_MODEL, type GenerateOptions, KrutAIKeyValidationError, KrutAIProvider, type KrutAIProviderConfig, VERSION, krutAI, validateApiKey, validateApiKeyFormat };
242
+ export { type ChatMessage, DEFAULT_MODEL, type GenerateOptions, KrutAIProvider, type KrutAIProviderConfig, VERSION, krutAI };
package/dist/index.js CHANGED
@@ -1,60 +1,10 @@
1
1
  'use strict';
2
2
 
3
+ var krutai = require('krutai');
4
+
3
5
  // src/types.ts
4
6
  var DEFAULT_MODEL = "default";
5
7
  var DEFAULT_SERVER_URL = "http://localhost:8000";
6
-
7
- // src/validator.ts
8
- var KrutAIKeyValidationError = class extends Error {
9
- constructor(message) {
10
- super(message);
11
- this.name = "KrutAIKeyValidationError";
12
- }
13
- };
14
- function validateApiKeyFormat(apiKey) {
15
- if (!apiKey || typeof apiKey !== "string") {
16
- throw new KrutAIKeyValidationError("API key must be a non-empty string");
17
- }
18
- if (apiKey.trim().length === 0) {
19
- throw new KrutAIKeyValidationError("API key cannot be empty or whitespace");
20
- }
21
- }
22
- async function validateApiKey(apiKey, serverUrl) {
23
- validateApiKeyFormat(apiKey);
24
- if (!serverUrl || typeof serverUrl !== "string" || serverUrl.trim().length === 0) {
25
- throw new KrutAIKeyValidationError("serverUrl must be a non-empty string");
26
- }
27
- const url = `${serverUrl.replace(/\/$/, "")}/validate`;
28
- try {
29
- const response = await fetch(url, {
30
- method: "POST",
31
- headers: {
32
- "Content-Type": "application/json",
33
- "x-api-key": apiKey
34
- },
35
- body: JSON.stringify({ apiKey })
36
- });
37
- if (!response.ok) {
38
- throw new KrutAIKeyValidationError(
39
- `API key validation failed: server responded with HTTP ${response.status}`
40
- );
41
- }
42
- const data = await response.json();
43
- if (data.valid === false) {
44
- throw new KrutAIKeyValidationError(
45
- data.message ?? "API key rejected by server"
46
- );
47
- }
48
- return true;
49
- } catch (error) {
50
- if (error instanceof KrutAIKeyValidationError) throw error;
51
- throw new KrutAIKeyValidationError(
52
- `Failed to reach validation endpoint at ${url}: ${error instanceof Error ? error.message : "Unknown error"}`
53
- );
54
- }
55
- }
56
-
57
- // src/client.ts
58
8
  var KrutAIProvider = class {
59
9
  apiKey;
60
10
  serverUrl;
@@ -63,10 +13,10 @@ var KrutAIProvider = class {
63
13
  initialized = false;
64
14
  constructor(config) {
65
15
  this.config = config;
66
- this.apiKey = config.apiKey;
16
+ this.apiKey = config.apiKey || process.env.KRUTAI_API_KEY || "";
67
17
  this.serverUrl = (config.serverUrl ?? DEFAULT_SERVER_URL).replace(/\/$/, "");
68
18
  this.resolvedModel = config.model ?? DEFAULT_MODEL;
69
- validateApiKeyFormat(this.apiKey);
19
+ krutai.validateApiKeyFormat(this.apiKey);
70
20
  if (config.validateOnInit === false) {
71
21
  this.initialized = true;
72
22
  }
@@ -80,7 +30,7 @@ var KrutAIProvider = class {
80
30
  async initialize() {
81
31
  if (this.initialized) return;
82
32
  if (this.config.validateOnInit !== false) {
83
- await validateApiKey(this.apiKey, this.serverUrl);
33
+ await krutai.validateApiKeyWithService(this.apiKey, this.serverUrl);
84
34
  }
85
35
  this.initialized = true;
86
36
  }
@@ -255,8 +205,6 @@ var KrutAIProvider = class {
255
205
  return data.text ?? data.content ?? data.message ?? "";
256
206
  }
257
207
  };
258
-
259
- // src/index.ts
260
208
  function krutAI(config) {
261
209
  return new KrutAIProvider({
262
210
  model: DEFAULT_MODEL,
@@ -265,12 +213,21 @@ function krutAI(config) {
265
213
  }
266
214
  var VERSION = "0.2.0";
267
215
 
216
+ Object.defineProperty(exports, "KrutAIKeyValidationError", {
217
+ enumerable: true,
218
+ get: function () { return krutai.ApiKeyValidationError; }
219
+ });
220
+ Object.defineProperty(exports, "validateApiKey", {
221
+ enumerable: true,
222
+ get: function () { return krutai.validateApiKeyWithService; }
223
+ });
224
+ Object.defineProperty(exports, "validateApiKeyFormat", {
225
+ enumerable: true,
226
+ get: function () { return krutai.validateApiKeyFormat; }
227
+ });
268
228
  exports.DEFAULT_MODEL = DEFAULT_MODEL;
269
- exports.KrutAIKeyValidationError = KrutAIKeyValidationError;
270
229
  exports.KrutAIProvider = KrutAIProvider;
271
230
  exports.VERSION = VERSION;
272
231
  exports.krutAI = krutAI;
273
- exports.validateApiKey = validateApiKey;
274
- exports.validateApiKeyFormat = validateApiKeyFormat;
275
232
  //# sourceMappingURL=index.js.map
276
233
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/types.ts","../src/validator.ts","../src/client.ts","../src/index.ts"],"names":[],"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 * @required\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 single chat message\n */\nexport interface ChatMessage {\n role: 'user' | 'assistant' | 'system';\n content: string;\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 * API Key Validator for @krutai/ai-provider\n *\n * Validates the KrutAI API key by calling the deployed LangChain server's\n * validation endpoint. The server is expected to respond with { valid: true }\n * for a valid key and { valid: false } (or a non-2xx status) for an invalid one.\n *\n * Validation endpoint called:\n * POST {serverUrl}/validate\n * Headers: { 'Content-Type': 'application/json', 'x-api-key': apiKey }\n * Body: { \"apiKey\": \"<key>\" }\n * Expected Response: { \"valid\": true }\n */\n\nexport class KrutAIKeyValidationError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'KrutAIKeyValidationError';\n }\n}\n\n/**\n * Basic sanity check — ensures the key is a non-empty string.\n * @throws {KrutAIKeyValidationError}\n */\nexport function validateApiKeyFormat(apiKey: string): void {\n if (!apiKey || typeof apiKey !== 'string') {\n throw new KrutAIKeyValidationError('API key must be a non-empty string');\n }\n if (apiKey.trim().length === 0) {\n throw new KrutAIKeyValidationError('API key cannot be empty or whitespace');\n }\n}\n\n/**\n * Validates the API key against the deployed LangChain server.\n *\n * Sends a POST request to `{serverUrl}/validate` and expects:\n * - HTTP 2xx status\n * - JSON body: `{ \"valid\": true }`\n *\n * @param apiKey - The API key to validate\n * @param serverUrl - Base URL of the LangChain backend (e.g. \"https://ai.yourapp.com\")\n * @throws {KrutAIKeyValidationError}\n */\nexport async function validateApiKey(\n apiKey: string,\n serverUrl: string\n): Promise<boolean> {\n // Basic format check first\n validateApiKeyFormat(apiKey);\n\n if (!serverUrl || typeof serverUrl !== 'string' || serverUrl.trim().length === 0) {\n throw new KrutAIKeyValidationError('serverUrl must be a non-empty string');\n }\n\n const url = `${serverUrl.replace(/\\/$/, '')}/validate`;\n\n try {\n const response = await fetch(url, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'x-api-key': apiKey,\n },\n body: JSON.stringify({ apiKey }),\n });\n\n if (!response.ok) {\n throw new KrutAIKeyValidationError(\n `API key validation failed: server responded with HTTP ${response.status}`\n );\n }\n\n const data = (await response.json()) as { valid?: boolean; message?: string };\n\n if (data.valid === false) {\n throw new KrutAIKeyValidationError(\n data.message ?? 'API key rejected by server'\n );\n }\n\n return true;\n } catch (error) {\n if (error instanceof KrutAIKeyValidationError) throw error;\n throw new KrutAIKeyValidationError(\n `Failed to reach validation endpoint at ${url}: ${error instanceof Error ? error.message : 'Unknown error'\n }`\n );\n }\n}\n","import type { KrutAIProviderConfig, GenerateOptions, ChatMessage } from './types';\nimport { DEFAULT_MODEL, DEFAULT_SERVER_URL } from './types';\nimport {\n validateApiKey,\n validateApiKeyFormat,\n KrutAIKeyValidationError,\n} from './validator';\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;\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.maxTokens !== undefined ? { maxTokens: options.maxTokens } : {}),\n ...(options.temperature !== undefined ? { temperature: options.temperature } : {}),\n }),\n });\n\n if (!response.ok) {\n throw new Error(\n `AI server returned HTTP ${response.status} for /generate`\n );\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.maxTokens !== undefined ? { maxTokens: options.maxTokens } : {}),\n ...(options.temperature !== undefined ? { temperature: options.temperature } : {}),\n }),\n });\n\n if (!response.ok) {\n throw new Error(\n `AI server returned HTTP ${response.status} for /stream`\n );\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 * 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.maxTokens !== undefined ? { maxTokens: options.maxTokens } : {}),\n ...(options.temperature !== undefined ? { temperature: options.temperature } : {}),\n }),\n });\n\n if (!response.ok) {\n throw new Error(\n `AI server returned HTTP ${response.status} for /chat`\n );\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://ai.yourapp.com',\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://ai.yourapp.com',\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://ai.yourapp.com',\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 { validateApiKey, validateApiKeyFormat } from './validator';\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://ai.yourapp.com',\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 single chat message\n */\nexport interface ChatMessage {\n role: 'user' | 'assistant' | 'system';\n content: string;\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","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.maxTokens !== undefined ? { maxTokens: options.maxTokens } : {}),\n ...(options.temperature !== undefined ? { temperature: options.temperature } : {}),\n }),\n });\n\n if (!response.ok) {\n throw new Error(\n `AI server returned HTTP ${response.status} for /generate`\n );\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.maxTokens !== undefined ? { maxTokens: options.maxTokens } : {}),\n ...(options.temperature !== undefined ? { temperature: options.temperature } : {}),\n }),\n });\n\n if (!response.ok) {\n throw new Error(\n `AI server returned HTTP ${response.status} for /stream`\n );\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 * 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.maxTokens !== undefined ? { maxTokens: options.maxTokens } : {}),\n ...(options.temperature !== undefined ? { temperature: options.temperature } : {}),\n }),\n });\n\n if (!response.ok) {\n throw new Error(\n `AI server returned HTTP ${response.status} for /chat`\n );\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://ai.yourapp.com',\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://ai.yourapp.com',\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://ai.yourapp.com',\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://ai.yourapp.com',\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"]}
package/dist/index.mjs CHANGED
@@ -1,58 +1,9 @@
1
+ import { validateApiKeyFormat, validateApiKeyWithService } from 'krutai';
2
+ export { ApiKeyValidationError as KrutAIKeyValidationError, validateApiKeyWithService as validateApiKey, validateApiKeyFormat } from 'krutai';
3
+
1
4
  // src/types.ts
2
5
  var DEFAULT_MODEL = "default";
3
6
  var DEFAULT_SERVER_URL = "http://localhost:8000";
4
-
5
- // src/validator.ts
6
- var KrutAIKeyValidationError = class extends Error {
7
- constructor(message) {
8
- super(message);
9
- this.name = "KrutAIKeyValidationError";
10
- }
11
- };
12
- function validateApiKeyFormat(apiKey) {
13
- if (!apiKey || typeof apiKey !== "string") {
14
- throw new KrutAIKeyValidationError("API key must be a non-empty string");
15
- }
16
- if (apiKey.trim().length === 0) {
17
- throw new KrutAIKeyValidationError("API key cannot be empty or whitespace");
18
- }
19
- }
20
- async function validateApiKey(apiKey, serverUrl) {
21
- validateApiKeyFormat(apiKey);
22
- if (!serverUrl || typeof serverUrl !== "string" || serverUrl.trim().length === 0) {
23
- throw new KrutAIKeyValidationError("serverUrl must be a non-empty string");
24
- }
25
- const url = `${serverUrl.replace(/\/$/, "")}/validate`;
26
- try {
27
- const response = await fetch(url, {
28
- method: "POST",
29
- headers: {
30
- "Content-Type": "application/json",
31
- "x-api-key": apiKey
32
- },
33
- body: JSON.stringify({ apiKey })
34
- });
35
- if (!response.ok) {
36
- throw new KrutAIKeyValidationError(
37
- `API key validation failed: server responded with HTTP ${response.status}`
38
- );
39
- }
40
- const data = await response.json();
41
- if (data.valid === false) {
42
- throw new KrutAIKeyValidationError(
43
- data.message ?? "API key rejected by server"
44
- );
45
- }
46
- return true;
47
- } catch (error) {
48
- if (error instanceof KrutAIKeyValidationError) throw error;
49
- throw new KrutAIKeyValidationError(
50
- `Failed to reach validation endpoint at ${url}: ${error instanceof Error ? error.message : "Unknown error"}`
51
- );
52
- }
53
- }
54
-
55
- // src/client.ts
56
7
  var KrutAIProvider = class {
57
8
  apiKey;
58
9
  serverUrl;
@@ -61,7 +12,7 @@ var KrutAIProvider = class {
61
12
  initialized = false;
62
13
  constructor(config) {
63
14
  this.config = config;
64
- this.apiKey = config.apiKey;
15
+ this.apiKey = config.apiKey || process.env.KRUTAI_API_KEY || "";
65
16
  this.serverUrl = (config.serverUrl ?? DEFAULT_SERVER_URL).replace(/\/$/, "");
66
17
  this.resolvedModel = config.model ?? DEFAULT_MODEL;
67
18
  validateApiKeyFormat(this.apiKey);
@@ -78,7 +29,7 @@ var KrutAIProvider = class {
78
29
  async initialize() {
79
30
  if (this.initialized) return;
80
31
  if (this.config.validateOnInit !== false) {
81
- await validateApiKey(this.apiKey, this.serverUrl);
32
+ await validateApiKeyWithService(this.apiKey, this.serverUrl);
82
33
  }
83
34
  this.initialized = true;
84
35
  }
@@ -253,8 +204,6 @@ var KrutAIProvider = class {
253
204
  return data.text ?? data.content ?? data.message ?? "";
254
205
  }
255
206
  };
256
-
257
- // src/index.ts
258
207
  function krutAI(config) {
259
208
  return new KrutAIProvider({
260
209
  model: DEFAULT_MODEL,
@@ -263,6 +212,6 @@ function krutAI(config) {
263
212
  }
264
213
  var VERSION = "0.2.0";
265
214
 
266
- export { DEFAULT_MODEL, KrutAIKeyValidationError, KrutAIProvider, VERSION, krutAI, validateApiKey, validateApiKeyFormat };
215
+ export { DEFAULT_MODEL, KrutAIProvider, VERSION, krutAI };
267
216
  //# sourceMappingURL=index.mjs.map
268
217
  //# sourceMappingURL=index.mjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/types.ts","../src/validator.ts","../src/client.ts","../src/index.ts"],"names":[],"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 * @required\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 single chat message\n */\nexport interface ChatMessage {\n role: 'user' | 'assistant' | 'system';\n content: string;\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 * API Key Validator for @krutai/ai-provider\n *\n * Validates the KrutAI API key by calling the deployed LangChain server's\n * validation endpoint. The server is expected to respond with { valid: true }\n * for a valid key and { valid: false } (or a non-2xx status) for an invalid one.\n *\n * Validation endpoint called:\n * POST {serverUrl}/validate\n * Headers: { 'Content-Type': 'application/json', 'x-api-key': apiKey }\n * Body: { \"apiKey\": \"<key>\" }\n * Expected Response: { \"valid\": true }\n */\n\nexport class KrutAIKeyValidationError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'KrutAIKeyValidationError';\n }\n}\n\n/**\n * Basic sanity check — ensures the key is a non-empty string.\n * @throws {KrutAIKeyValidationError}\n */\nexport function validateApiKeyFormat(apiKey: string): void {\n if (!apiKey || typeof apiKey !== 'string') {\n throw new KrutAIKeyValidationError('API key must be a non-empty string');\n }\n if (apiKey.trim().length === 0) {\n throw new KrutAIKeyValidationError('API key cannot be empty or whitespace');\n }\n}\n\n/**\n * Validates the API key against the deployed LangChain server.\n *\n * Sends a POST request to `{serverUrl}/validate` and expects:\n * - HTTP 2xx status\n * - JSON body: `{ \"valid\": true }`\n *\n * @param apiKey - The API key to validate\n * @param serverUrl - Base URL of the LangChain backend (e.g. \"https://ai.yourapp.com\")\n * @throws {KrutAIKeyValidationError}\n */\nexport async function validateApiKey(\n apiKey: string,\n serverUrl: string\n): Promise<boolean> {\n // Basic format check first\n validateApiKeyFormat(apiKey);\n\n if (!serverUrl || typeof serverUrl !== 'string' || serverUrl.trim().length === 0) {\n throw new KrutAIKeyValidationError('serverUrl must be a non-empty string');\n }\n\n const url = `${serverUrl.replace(/\\/$/, '')}/validate`;\n\n try {\n const response = await fetch(url, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n 'x-api-key': apiKey,\n },\n body: JSON.stringify({ apiKey }),\n });\n\n if (!response.ok) {\n throw new KrutAIKeyValidationError(\n `API key validation failed: server responded with HTTP ${response.status}`\n );\n }\n\n const data = (await response.json()) as { valid?: boolean; message?: string };\n\n if (data.valid === false) {\n throw new KrutAIKeyValidationError(\n data.message ?? 'API key rejected by server'\n );\n }\n\n return true;\n } catch (error) {\n if (error instanceof KrutAIKeyValidationError) throw error;\n throw new KrutAIKeyValidationError(\n `Failed to reach validation endpoint at ${url}: ${error instanceof Error ? error.message : 'Unknown error'\n }`\n );\n }\n}\n","import type { KrutAIProviderConfig, GenerateOptions, ChatMessage } from './types';\nimport { DEFAULT_MODEL, DEFAULT_SERVER_URL } from './types';\nimport {\n validateApiKey,\n validateApiKeyFormat,\n KrutAIKeyValidationError,\n} from './validator';\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;\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.maxTokens !== undefined ? { maxTokens: options.maxTokens } : {}),\n ...(options.temperature !== undefined ? { temperature: options.temperature } : {}),\n }),\n });\n\n if (!response.ok) {\n throw new Error(\n `AI server returned HTTP ${response.status} for /generate`\n );\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.maxTokens !== undefined ? { maxTokens: options.maxTokens } : {}),\n ...(options.temperature !== undefined ? { temperature: options.temperature } : {}),\n }),\n });\n\n if (!response.ok) {\n throw new Error(\n `AI server returned HTTP ${response.status} for /stream`\n );\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 * 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.maxTokens !== undefined ? { maxTokens: options.maxTokens } : {}),\n ...(options.temperature !== undefined ? { temperature: options.temperature } : {}),\n }),\n });\n\n if (!response.ok) {\n throw new Error(\n `AI server returned HTTP ${response.status} for /chat`\n );\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://ai.yourapp.com',\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://ai.yourapp.com',\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://ai.yourapp.com',\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 { validateApiKey, validateApiKeyFormat } from './validator';\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://ai.yourapp.com',\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 single chat message\n */\nexport interface ChatMessage {\n role: 'user' | 'assistant' | 'system';\n content: string;\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","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.maxTokens !== undefined ? { maxTokens: options.maxTokens } : {}),\n ...(options.temperature !== undefined ? { temperature: options.temperature } : {}),\n }),\n });\n\n if (!response.ok) {\n throw new Error(\n `AI server returned HTTP ${response.status} for /generate`\n );\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.maxTokens !== undefined ? { maxTokens: options.maxTokens } : {}),\n ...(options.temperature !== undefined ? { temperature: options.temperature } : {}),\n }),\n });\n\n if (!response.ok) {\n throw new Error(\n `AI server returned HTTP ${response.status} for /stream`\n );\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 * 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.maxTokens !== undefined ? { maxTokens: options.maxTokens } : {}),\n ...(options.temperature !== undefined ? { temperature: options.temperature } : {}),\n }),\n });\n\n if (!response.ok) {\n throw new Error(\n `AI server returned HTTP ${response.status} for /chat`\n );\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://ai.yourapp.com',\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://ai.yourapp.com',\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://ai.yourapp.com',\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://ai.yourapp.com',\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"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@krutai/ai-provider",
3
- "version": "0.2.1",
3
+ "version": "0.2.3",
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",
@@ -34,10 +34,10 @@
34
34
  "author": "",
35
35
  "license": "MIT",
36
36
  "dependencies": {
37
- "krutai": ">=0.1.2"
37
+ "krutai": ">=0.1.4"
38
38
  },
39
39
  "peerDependencies": {
40
- "krutai": ">=0.1.2"
40
+ "krutai": ">=0.1.4"
41
41
  },
42
42
  "devDependencies": {
43
43
  "@types/node": "^20.11.0",