@krutai/ai-provider 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,170 @@
1
+ # @krutai/ai-provider — AI Assistant Reference Guide
2
+
3
+ ## Package Overview
4
+
5
+ - **Name**: `@krutai/ai-provider`
6
+ - **Version**: `0.1.0`
7
+ - **Purpose**: AI provider for KrutAI — wraps `@openrouter/sdk` with a `krutAI()` factory, key validation, and a configurable default model
8
+ - **Entry**: `src/index.ts` → `dist/index.{js,mjs,d.ts}`
9
+ - **Build**: `tsup` (CJS + ESM, all deps external)
10
+
11
+ ## Dependency Architecture
12
+
13
+ ```
14
+ @krutai/ai-provider@0.1.0
15
+ ├── dependency: @openrouter/sdk ← Official OpenRouter TypeScript SDK (external in tsup)
16
+ └── peerDep: krutai ← Core utilities
17
+ ```
18
+
19
+ > **Important for AI**: Do NOT bundle `@openrouter/sdk` inline. It must stay external in tsup.
20
+
21
+ ## File Structure
22
+
23
+ ```
24
+ packages/ai-provider/
25
+ ├── src/
26
+ │ ├── index.ts # krutAI() factory + all exports
27
+ │ ├── client.ts # KrutAIProvider class
28
+ │ ├── types.ts # KrutAIProviderConfig, GenerateOptions, ChatMessage, DEFAULT_MODEL
29
+ │ └── validator.ts # OpenRouter key format + service validation
30
+ ├── package.json
31
+ ├── tsconfig.json
32
+ └── tsup.config.ts
33
+ ```
34
+
35
+ ## Default Model
36
+
37
+ ```
38
+ qwen/qwen3-235b-a22b-thinking-2507
39
+ ```
40
+
41
+ Exported as `DEFAULT_MODEL` constant. Users override via `config.model` or per-call `options.model`.
42
+
43
+ ## Main Exports
44
+
45
+ ### `krutAI(config?)` ← PRIMARY API
46
+
47
+ Drop-in factory. Mirrors `krutAuth` from `@krutai/auth`.
48
+
49
+ ```typescript
50
+ import { krutAI } from '@krutai/ai-provider';
51
+
52
+ const ai = krutAI(); // OPENROUTER_API_KEY from env, default model
53
+ await ai.initialize();
54
+
55
+ const text = await ai.generate('Hello!');
56
+ ```
57
+
58
+ ### `KrutAIProvider` class ← FULL CLASS API
59
+
60
+ ```typescript
61
+ import { KrutAIProvider } from '@krutai/ai-provider';
62
+
63
+ const ai = new KrutAIProvider({
64
+ apiKey: process.env.KRUTAI_API_KEY!,
65
+ openRouterApiKey: process.env.OPENROUTER_API_KEY!,
66
+ model: 'openai/gpt-4o', // optional
67
+ validateOnInit: true, // default
68
+ validationEndpoint: undefined, // TODO: wire up POST route
69
+ });
70
+
71
+ await ai.initialize();
72
+ ```
73
+
74
+ **Methods:**
75
+ - `initialize(): Promise<void>` — validates key + sets up OpenRouter client
76
+ - `generate(prompt, opts?): Promise<string>` — single response (non-streaming)
77
+ - `stream(prompt, opts?)` — async iterable of SSE chunks (`chunk.choices[0].delta.content`)
78
+ - `chat(messages, opts?): Promise<string>` — multi-turn conversation
79
+ - `getModel(): string` — active model name
80
+ - `getClient(): OpenRouter` — raw `@openrouter/sdk` client (advanced)
81
+ - `isInitialized(): boolean`
82
+
83
+ ## Underlying SDK Call
84
+
85
+ The package calls `@openrouter/sdk` using the following structure:
86
+
87
+ ```typescript
88
+ // Non-streaming
89
+ client.chat.send({
90
+ chatGenerationParams: { model, messages, stream: false, maxTokens?, temperature? }
91
+ });
92
+
93
+ // Streaming
94
+ client.chat.send({
95
+ chatGenerationParams: { model, messages, stream: true, maxTokens?, temperature? }
96
+ });
97
+ ```
98
+
99
+ ## Types
100
+
101
+ ### `KrutAIProviderConfig`
102
+
103
+ ```typescript
104
+ interface KrutAIProviderConfig {
105
+ apiKey: string; // KrutAI API key (required)
106
+ openRouterApiKey?: string; // falls back to process.env.OPENROUTER_API_KEY
107
+ model?: string; // default: DEFAULT_MODEL
108
+ validateOnInit?: boolean; // default: true
109
+ validationEndpoint?: string; // POST URL for key validation (future)
110
+ }
111
+ ```
112
+
113
+ ### `ChatMessage`
114
+
115
+ ```typescript
116
+ interface ChatMessage {
117
+ role: 'user' | 'assistant' | 'system';
118
+ content: string;
119
+ }
120
+ ```
121
+
122
+ ### `GenerateOptions`
123
+
124
+ ```typescript
125
+ interface GenerateOptions {
126
+ model?: string; // override model for this call
127
+ system?: string; // system prompt
128
+ maxTokens?: number;
129
+ temperature?: number;
130
+ }
131
+ ```
132
+
133
+ ## Validator
134
+
135
+ Defined in `src/validator.ts` (NOT imported from `krutai` — OpenRouter-specific).
136
+
137
+ ```typescript
138
+ export { validateOpenRouterKeyFormat, validateOpenRouterKeyWithService, OpenRouterKeyValidationError };
139
+ ```
140
+
141
+ ### Validation Flow
142
+
143
+ 1. **Format check** (sync, on construction): key must start with `sk-or-v1-` and be ≥ 20 chars
144
+ 2. **Service check** (async, on `initialize()`): if `validationEndpoint` is set, sends `POST { apiKey }` and checks response; otherwise placeholder returns `true`
145
+
146
+ ## tsup Configuration Notes
147
+
148
+ - `@openrouter/sdk` → **external** (real dependency, NOT bundled)
149
+ - `krutai` → **external** (peer dep, NOT bundled)
150
+
151
+ ## Important Notes
152
+
153
+ 1. **`krutAI()` is the primary API** — prefer it over `new KrutAIProvider()` for simple setups
154
+ 2. **Default model is `qwen/qwen3-235b-a22b-thinking-2507`** — override via `config.model` or `opts.model`
155
+ 3. **OpenRouter key from env** — set `OPENROUTER_API_KEY` and omit `openRouterApiKey` in config
156
+ 4. **Validation endpoint is a placeholder** — wire up the POST route when deployed
157
+ 5. **Do NOT bundle `@openrouter/sdk`** — must stay external in tsup
158
+
159
+ ## Related Packages
160
+
161
+ - `krutai` — Core utilities and API validation
162
+ - `@krutai/auth` — Authentication (wraps better-auth)
163
+ - `@krutai/rbac` — Role-Based Access Control
164
+
165
+ ## Links
166
+
167
+ - OpenRouter SDK Docs: https://openrouter.ai/docs/sdks/typescript
168
+ - OpenRouter Models: https://openrouter.ai/models
169
+ - GitHub: https://github.com/AccountantAIOrg/krut_packages
170
+ - npm: https://www.npmjs.com/package/@krutai/ai-provider
package/README.md ADDED
@@ -0,0 +1,163 @@
1
+ # @krutai/ai-provider
2
+
3
+ AI Provider package for KrutAI — a thin wrapper around [`@openrouter/sdk`](https://www.npmjs.com/package/@openrouter/sdk), following the same patterns as `@krutai/auth`.
4
+
5
+ ## Features
6
+
7
+ - **Default model**: `qwen/qwen3-235b-a22b-thinking-2507` (change any time)
8
+ - **OpenRouter key validation**: format check (`sk-or-v1-` prefix) on construction, optional service validation
9
+ - **Pluggable validation endpoint**: set your own POST route once it's ready
10
+ - **Text generation & streaming**: `generate()`, `stream()`, and `chat()` built on `@openrouter/sdk`
11
+ - **Mirrors `@krutai/auth` patterns**: `krutAI()` factory + `KrutAIProvider` class
12
+
13
+ ---
14
+
15
+ ## Installation
16
+
17
+ ```bash
18
+ npm install @krutai/ai-provider
19
+ ```
20
+
21
+ ---
22
+
23
+ ## Quick Start
24
+
25
+ ### Simplest usage (no setup needed)
26
+
27
+ The OpenRouter API key is built-in by default — just install and call:
28
+
29
+ ```typescript
30
+ import { krutAI } from '@krutai/ai-provider';
31
+
32
+ const ai = krutAI();
33
+ await ai.initialize();
34
+
35
+ const text = await ai.generate('Write a haiku about coding');
36
+ console.log(text);
37
+ ```
38
+
39
+ > **Note:** The built-in key is temporary. It will be removed once the validation endpoint is live, at which point you will need to provide your own key via `OPENROUTER_API_KEY` env var or `openRouterApiKey` config.
40
+
41
+ ### Change the model
42
+
43
+ ```typescript
44
+ const ai = krutAI({ model: 'openai/gpt-4o-mini' });
45
+ await ai.initialize();
46
+
47
+ const text = await ai.generate('Hello!');
48
+ ```
49
+
50
+ ### Streaming
51
+
52
+ ```typescript
53
+ const ai = krutAI();
54
+ await ai.initialize();
55
+
56
+ const stream = await ai.stream('Tell me a story');
57
+ for await (const chunk of stream) {
58
+ process.stdout.write(chunk.choices?.[0]?.delta?.content ?? '');
59
+ }
60
+ ```
61
+
62
+ ### Multi-turn conversation
63
+
64
+ ```typescript
65
+ const reply = await ai.chat([
66
+ { role: 'user', content: 'Hi!' },
67
+ { role: 'assistant', content: 'Hello! How can I help?' },
68
+ { role: 'user', content: 'What is TypeScript?' },
69
+ ]);
70
+ ```
71
+
72
+ ### Override model per call
73
+
74
+ ```typescript
75
+ const text = await ai.generate('Summarise this', {
76
+ model: 'anthropic/claude-3.5-sonnet',
77
+ maxTokens: 512,
78
+ temperature: 0.7,
79
+ });
80
+ ```
81
+
82
+ ### Using the class directly
83
+
84
+ ```typescript
85
+ import { KrutAIProvider } from '@krutai/ai-provider';
86
+
87
+ const ai = new KrutAIProvider({
88
+ apiKey: process.env.KRUTAI_API_KEY!,
89
+ openRouterApiKey: process.env.OPENROUTER_API_KEY!,
90
+ model: 'google/gemini-flash-1.5', // optional override
91
+ validateOnInit: true, // default
92
+ });
93
+
94
+ await ai.initialize();
95
+ const text = await ai.generate('Hi!');
96
+ ```
97
+
98
+ ---
99
+
100
+ ## API Reference
101
+
102
+ ### `krutAI(config?)` — factory function
103
+
104
+ | Field | Type | Default |
105
+ |---|---|---|
106
+ | `openRouterApiKey` | `string` | `process.env.OPENROUTER_API_KEY` |
107
+ | `model` | `string` | `"qwen/qwen3-235b-a22b-thinking-2507"` |
108
+ | `validateOnInit` | `boolean` | `true` |
109
+ | `validationEndpoint` | `string` | `undefined` (placeholder) |
110
+
111
+ ### `KrutAIProvider` class
112
+
113
+ | Method | Returns | Description |
114
+ |---|---|---|
115
+ | `initialize()` | `Promise<void>` | Validates key + sets up OpenRouter client |
116
+ | `generate(prompt, opts?)` | `Promise<string>` | Single response |
117
+ | `stream(prompt, opts?)` | async iterable | Streaming response |
118
+ | `chat(messages, opts?)` | `Promise<string>` | Multi-turn conversation |
119
+ | `getModel()` | `string` | Active model name |
120
+ | `getClient()` | `OpenRouter` | Raw `@openrouter/sdk` client (advanced) |
121
+ | `isInitialized()` | `boolean` | Ready check |
122
+
123
+ ---
124
+
125
+ ## Default Model
126
+
127
+ ```
128
+ qwen/qwen3-235b-a22b-thinking-2507
129
+ ```
130
+
131
+ Pass `model` in the constructor or per-call to override.
132
+ Browse all available models at https://openrouter.ai/models.
133
+
134
+ ---
135
+
136
+ ## Validation
137
+
138
+ The OpenRouter key is validated for format (`sk-or-v1-` prefix) on construction.
139
+
140
+ When a `validationEndpoint` is provided, `initialize()` sends a `POST` request:
141
+ ```json
142
+ { "apiKey": "sk-or-v1-..." }
143
+ ```
144
+ Expected response: `{ "valid": true }` (or any `2xx` without `valid: false`).
145
+
146
+ > The live endpoint will be wired in once you deploy the POST route.
147
+
148
+ ---
149
+
150
+ ## Related Packages
151
+
152
+ - [`krutai`](https://www.npmjs.com/package/krutai) — Core utilities & API validation
153
+ - [`@krutai/auth`](https://www.npmjs.com/package/@krutai/auth) — Authentication (wraps better-auth)
154
+ - [`@krutai/rbac`](https://www.npmjs.com/package/@krutai/rbac) — Role-Based Access Control
155
+
156
+ ---
157
+
158
+ ## Links
159
+
160
+ - OpenRouter SDK: https://openrouter.ai/docs/sdks/typescript
161
+ - Available Models: https://openrouter.ai/models
162
+ - GitHub: https://github.com/AccountantAIOrg/krut_packages
163
+ - npm: https://www.npmjs.com/package/@krutai/ai-provider
@@ -0,0 +1,248 @@
1
+ import * as _openrouter_sdk_lib_event_streams_js from '@openrouter/sdk/lib/event-streams.js';
2
+ import * as _openrouter_sdk_models from '@openrouter/sdk/models';
3
+ import { OpenRouter } from '@openrouter/sdk';
4
+
5
+ /**
6
+ * Types for @krutai/ai-provider
7
+ */
8
+ /**
9
+ * Default model used when no model is specified
10
+ */
11
+ declare const DEFAULT_MODEL: "qwen/qwen3-235b-a22b-thinking-2507";
12
+ /**
13
+ * Configuration options for KrutAIProvider
14
+ */
15
+ interface KrutAIProviderConfig {
16
+ /**
17
+ * KrutAI API key for service validation.
18
+ * @required
19
+ */
20
+ apiKey: string;
21
+ /**
22
+ * OpenRouter API key.
23
+ * Falls back to process.env.OPENROUTER_API_KEY if not provided.
24
+ */
25
+ openRouterApiKey?: string;
26
+ /**
27
+ * The AI model to use.
28
+ * @default "qwen/qwen3-235b-a22b-thinking-2507"
29
+ * @see https://openrouter.ai/models
30
+ */
31
+ model?: string;
32
+ /**
33
+ * Whether to validate the OpenRouter API key on initialization.
34
+ * @default true
35
+ */
36
+ validateOnInit?: boolean;
37
+ /**
38
+ * Custom POST endpoint for OpenRouter API key validation.
39
+ * Will be wired in once you deploy the route.
40
+ */
41
+ validationEndpoint?: string;
42
+ }
43
+ /**
44
+ * A single chat message (OpenRouter format)
45
+ */
46
+ interface ChatMessage {
47
+ role: 'user' | 'assistant' | 'system';
48
+ content: string;
49
+ }
50
+ /**
51
+ * Options for a single generate / stream call
52
+ */
53
+ interface GenerateOptions {
54
+ /**
55
+ * Override the model for this specific call.
56
+ */
57
+ model?: string;
58
+ /**
59
+ * System prompt (prepended as a system message).
60
+ */
61
+ system?: string;
62
+ /**
63
+ * Maximum tokens to generate.
64
+ */
65
+ maxTokens?: number;
66
+ /**
67
+ * Temperature (0–2).
68
+ */
69
+ temperature?: number;
70
+ }
71
+
72
+ /**
73
+ * OpenRouter API Key Validator
74
+ *
75
+ * Validates the OpenRouter API key format and (optionally) its validity
76
+ * via a configurable POST endpoint.
77
+ *
78
+ * The user will later provide a live POST route; until then the
79
+ * service-level check is a placeholder that accepts any well-formed key.
80
+ */
81
+ declare class OpenRouterKeyValidationError extends Error {
82
+ constructor(message: string);
83
+ }
84
+ /**
85
+ * Validates the format of an OpenRouter API key.
86
+ * @throws {OpenRouterKeyValidationError}
87
+ */
88
+ declare function validateOpenRouterKeyFormat(apiKey: string): void;
89
+ /**
90
+ * Validates the OpenRouter API key with the KrutAI validation service.
91
+ *
92
+ * The user will provide a live POST route later. Until then this is a
93
+ * placeholder that accepts any key that passes format validation.
94
+ *
95
+ * @param apiKey - OpenRouter API key to validate
96
+ * @param validationEndpoint - Optional POST URL to validate against
97
+ */
98
+ declare function validateOpenRouterKeyWithService(apiKey: string, validationEndpoint?: string): Promise<boolean>;
99
+
100
+ /**
101
+ * KrutAIProvider — AI provider for KrutAI
102
+ *
103
+ * Wraps `@openrouter/sdk` and adds:
104
+ * - OpenRouter API key format validation
105
+ * - Configurable default model (defaults to qwen/qwen3-235b-a22b-thinking-2507)
106
+ * - Optional pluggable validation endpoint
107
+ *
108
+ * @example
109
+ * ```typescript
110
+ * import { KrutAIProvider } from '@krutai/ai-provider';
111
+ *
112
+ * const ai = new KrutAIProvider({
113
+ * apiKey: process.env.KRUTAI_API_KEY!,
114
+ * openRouterApiKey: process.env.OPENROUTER_API_KEY!, // or set in env
115
+ * });
116
+ *
117
+ * await ai.initialize();
118
+ *
119
+ * const text = await ai.generate('Tell me a joke');
120
+ * console.log(text);
121
+ * ```
122
+ */
123
+ declare class KrutAIProvider {
124
+ private readonly resolvedOpenRouterKey;
125
+ private readonly resolvedModel;
126
+ private readonly config;
127
+ private openRouterClient;
128
+ private initialized;
129
+ constructor(config: KrutAIProviderConfig);
130
+ /**
131
+ * Initialize the provider.
132
+ * Validates the OpenRouter API key (optionally against a service endpoint)
133
+ * and sets up the underlying OpenRouter client.
134
+ *
135
+ * @throws {OpenRouterKeyValidationError}
136
+ */
137
+ initialize(): Promise<void>;
138
+ /** @private */
139
+ private setupClient;
140
+ /**
141
+ * Get the raw OpenRouter SDK client instance.
142
+ * @throws {Error} If not initialized
143
+ */
144
+ getClient(): OpenRouter;
145
+ /**
146
+ * Get the currently configured default model.
147
+ */
148
+ getModel(): string;
149
+ /**
150
+ * Check whether the provider has been initialized.
151
+ */
152
+ isInitialized(): boolean;
153
+ /**
154
+ * Generate a response for a prompt (non-streaming).
155
+ *
156
+ * @param prompt - The user prompt string
157
+ * @param options - Optional overrides (model, system, maxTokens, temperature)
158
+ * @returns The assistant's response text
159
+ */
160
+ generate(prompt: string, options?: GenerateOptions): Promise<string>;
161
+ /**
162
+ * Generate a streaming response for a prompt.
163
+ *
164
+ * @param prompt - The user prompt string
165
+ * @param options - Optional overrides (model, system, maxTokens, temperature)
166
+ * @returns An async iterable of server-sent event chunks
167
+ *
168
+ * @example
169
+ * ```typescript
170
+ * const stream = await ai.stream('Tell me a story');
171
+ * for await (const chunk of stream) {
172
+ * process.stdout.write(chunk.choices?.[0]?.delta?.content ?? '');
173
+ * }
174
+ * ```
175
+ */
176
+ stream(prompt: string, options?: GenerateOptions): Promise<_openrouter_sdk_lib_event_streams_js.EventStream<_openrouter_sdk_models.ChatStreamingResponseChunkData>>;
177
+ /**
178
+ * Multi-turn conversation: pass a full message history.
179
+ *
180
+ * @param messages - Full conversation history
181
+ * @param options - Optional overrides (model, maxTokens, temperature)
182
+ */
183
+ chat(messages: ChatMessage[], options?: GenerateOptions): Promise<string>;
184
+ }
185
+
186
+ /**
187
+ * @krutai/ai-provider — AI Provider package for KrutAI
188
+ *
189
+ * A thin wrapper around `@openrouter/sdk`, mirroring the patterns from `@krutai/auth`.
190
+ *
191
+ * Default model: `qwen/qwen3-235b-a22b-thinking-2507`
192
+ *
193
+ * @example Basic usage
194
+ * ```typescript
195
+ * import { krutAI } from '@krutai/ai-provider';
196
+ *
197
+ * const ai = krutAI(); // uses OPENROUTER_API_KEY env var
198
+ * await ai.initialize();
199
+ *
200
+ * const text = await ai.generate('Write a poem about TypeScript');
201
+ * console.log(text);
202
+ * ```
203
+ *
204
+ * @example With custom model
205
+ * ```typescript
206
+ * const ai = krutAI({ model: 'openai/gpt-4o' });
207
+ * await ai.initialize();
208
+ * const text = await ai.generate('Hello!');
209
+ * ```
210
+ *
211
+ * @example Streaming
212
+ * ```typescript
213
+ * const ai = krutAI();
214
+ * await ai.initialize();
215
+ *
216
+ * const stream = await ai.stream('Tell me a story');
217
+ * for await (const chunk of stream) {
218
+ * process.stdout.write(chunk.choices[0]?.delta?.content ?? '');
219
+ * }
220
+ * ```
221
+ *
222
+ * @packageDocumentation
223
+ */
224
+
225
+ /**
226
+ * krutAI — convenience factory (mirrors `krutAuth` in @krutai/auth).
227
+ *
228
+ * Creates a `KrutAIProvider` instance. OpenRouter API key is read from
229
+ * `config.openRouterApiKey` or falls back to `process.env.OPENROUTER_API_KEY`.
230
+ *
231
+ * @param config - Provider configuration (all fields optional except apiKey)
232
+ * @returns A `KrutAIProvider` instance (call `.initialize()` before use)
233
+ *
234
+ * @example
235
+ * ```typescript
236
+ * import { krutAI } from '@krutai/ai-provider';
237
+ *
238
+ * const ai = krutAI(); // env OPENROUTER_API_KEY + default model
239
+ * await ai.initialize();
240
+ * const text = await ai.generate('Hello!');
241
+ * ```
242
+ */
243
+ declare function krutAI(config?: Partial<KrutAIProviderConfig> & {
244
+ apiKey?: string;
245
+ }): KrutAIProvider;
246
+ declare const VERSION = "0.1.0";
247
+
248
+ export { type ChatMessage, DEFAULT_MODEL, type GenerateOptions, KrutAIProvider, type KrutAIProviderConfig, OpenRouterKeyValidationError, VERSION, krutAI, validateOpenRouterKeyFormat, validateOpenRouterKeyWithService };