@node-llm/core 0.2.2 → 0.3.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.
Files changed (65) hide show
  1. package/README.md +125 -4
  2. package/dist/chat/Chat.d.ts +32 -16
  3. package/dist/chat/Chat.d.ts.map +1 -1
  4. package/dist/chat/Chat.js +102 -32
  5. package/dist/chat/ChatOptions.d.ts +5 -0
  6. package/dist/chat/ChatOptions.d.ts.map +1 -1
  7. package/dist/chat/ChatResponse.d.ts +18 -0
  8. package/dist/chat/ChatResponse.d.ts.map +1 -0
  9. package/dist/chat/ChatResponse.js +26 -0
  10. package/dist/chat/Stream.d.ts.map +1 -1
  11. package/dist/chat/Stream.js +10 -0
  12. package/dist/index.d.ts +1 -1
  13. package/dist/index.d.ts.map +1 -1
  14. package/dist/index.js +1 -1
  15. package/dist/llm.d.ts +25 -3
  16. package/dist/llm.d.ts.map +1 -1
  17. package/dist/llm.js +48 -1
  18. package/dist/models/ModelRegistry.d.ts +23 -0
  19. package/dist/models/ModelRegistry.d.ts.map +1 -0
  20. package/dist/models/ModelRegistry.js +54 -0
  21. package/dist/moderation/Moderation.d.ts +56 -0
  22. package/dist/moderation/Moderation.d.ts.map +1 -0
  23. package/dist/moderation/Moderation.js +92 -0
  24. package/dist/providers/Provider.d.ts +39 -0
  25. package/dist/providers/Provider.d.ts.map +1 -1
  26. package/dist/providers/openai/Chat.d.ts.map +1 -1
  27. package/dist/providers/openai/Chat.js +1 -0
  28. package/dist/providers/openai/Moderation.d.ts +8 -0
  29. package/dist/providers/openai/Moderation.d.ts.map +1 -0
  30. package/dist/providers/openai/Moderation.js +26 -0
  31. package/dist/providers/openai/OpenAIProvider.d.ts +6 -1
  32. package/dist/providers/openai/OpenAIProvider.d.ts.map +1 -1
  33. package/dist/providers/openai/OpenAIProvider.js +12 -0
  34. package/dist/providers/openai/Streaming.d.ts.map +1 -1
  35. package/dist/providers/openai/Streaming.js +19 -8
  36. package/dist/providers/openai/Transcription.d.ts +10 -0
  37. package/dist/providers/openai/Transcription.d.ts.map +1 -0
  38. package/dist/providers/openai/Transcription.js +161 -0
  39. package/dist/providers/openai/index.d.ts +8 -0
  40. package/dist/providers/openai/index.d.ts.map +1 -1
  41. package/dist/providers/openai/index.js +12 -0
  42. package/dist/transcription/Transcription.d.ts +11 -0
  43. package/dist/transcription/Transcription.d.ts.map +1 -0
  44. package/dist/transcription/Transcription.js +21 -0
  45. package/dist/utils/FileLoader.d.ts.map +1 -1
  46. package/dist/utils/FileLoader.js +12 -1
  47. package/dist/utils/audio.d.ts +10 -0
  48. package/dist/utils/audio.d.ts.map +1 -0
  49. package/dist/utils/audio.js +46 -0
  50. package/package.json +17 -7
  51. package/dist/providers/openai/register.d.ts +0 -2
  52. package/dist/providers/openai/register.d.ts.map +0 -1
  53. package/dist/providers/openai/register.js +0 -15
  54. package/dist/tools/Tool.d.ts +0 -8
  55. package/dist/tools/Tool.d.ts.map +0 -1
  56. package/dist/tools/Tool.js +0 -1
  57. package/dist/tools/ToolSet.d.ts +0 -15
  58. package/dist/tools/ToolSet.d.ts.map +0 -1
  59. package/dist/tools/ToolSet.js +0 -29
  60. package/dist/tools/index.d.ts +0 -2
  61. package/dist/tools/index.d.ts.map +0 -1
  62. package/dist/tools/index.js +0 -1
  63. package/dist/tools/runCommandTool.d.ts +0 -8
  64. package/dist/tools/runCommandTool.d.ts.map +0 -1
  65. package/dist/tools/runCommandTool.js +0 -19
package/README.md CHANGED
@@ -15,6 +15,7 @@ A provider-agnostic LLM core for Node.js, heavily inspired by the elegant design
15
15
  - **Provider-Agnostic**: Switch between OpenAI, Anthropic, and others with a single line of config.
16
16
  - **Streaming-First**: Native `AsyncIterator` support for real-time token delivery.
17
17
  - **Tool Calling**: Automatic execution loop for model-requested functions.
18
+ - **Content Moderation**: Built-in safety checks for user input and model output.
18
19
  - **Multi-modal & Smart Files**: Built-in support for Vision (images), Audio, and Text files.
19
20
  - **Fluent API**: Chainable methods like `.withTool()` for dynamic registration.
20
21
  - **Resilient**: Configurable retry logic at the execution layer.
@@ -42,7 +43,8 @@ import "dotenv/config";
42
43
 
43
44
  LLM.configure({
44
45
  provider: "openai", // Uses OPENAI_API_KEY from env
45
- retry: { attempts: 3, delayMs: 500 }
46
+ retry: { attempts: 3, delayMs: 500 },
47
+ defaultModerationModel: "text-moderation-latest"
46
48
  });
47
49
  ```
48
50
 
@@ -103,6 +105,71 @@ console.log(response.output_tokens); // 5
103
105
  console.log(chat.totalUsage.total_tokens);
104
106
  ```
105
107
 
108
+ ### 6. Audio Transcription (Transcribe)
109
+
110
+ Convert audio files to text using specialized models like Whisper.
111
+
112
+ ```ts
113
+ const text = await LLM.transcribe("meeting.mp3");
114
+ console.log(text);
115
+ ```
116
+
117
+ ### 7. Content Moderation (Moderate)
118
+
119
+ Check if text content violates safety policies.
120
+
121
+ ```ts
122
+ const result = await LLM.moderate("I want to help everyone!");
123
+ if (result.flagged) {
124
+ console.log(`❌ Flagged for: ${result.flaggedCategories.join(", ")}`);
125
+ } else {
126
+ console.log("✅ Content appears safe");
127
+ }
128
+ ```
129
+
130
+ Learn how to implement [custom risk thresholds](https://github.com/eshaiju/node-llm/blob/main/examples/openai/12-risk-assessment.mjs) for more granular control.
131
+
132
+ ### 8. Chat Event Handlers
133
+
134
+ Hook into the chat lifecycle for logging, UI updates, or auditing.
135
+
136
+ ```ts
137
+ chat
138
+ .onNewMessage(() => console.log("AI started typing..."))
139
+ .onToolCall((tool) => console.log(`Calling ${tool.function.name}...`))
140
+ .onToolResult((result) => console.log(`Tool returned: ${result}`))
141
+ .onEndMessage((response) => console.log(`Done. Usage: ${response.total_tokens}`));
142
+
143
+ await chat.ask("What's the weather?");
144
+ ```
145
+
146
+ ### 9. System Prompts (Instructions)
147
+
148
+ Guide the AI's behavior, personality, or constraints.
149
+
150
+ ```ts
151
+ // Set initial instructions
152
+ chat.withInstructions("You are a helpful assistant that explains simply.");
153
+
154
+ // Update instructions mid-conversation (replace: true removes previous ones)
155
+ chat.withInstructions("Now assume the persona of a pirate.", { replace: true });
156
+
157
+ await chat.ask("Hello");
158
+ // => "Ahoy matey!"
159
+ ```
160
+
161
+ ### 10. Temperature Control (Creativity)
162
+
163
+ Adjust the randomness of the model's responses.
164
+
165
+ ```ts
166
+ // Factual (0.0 - 0.3)
167
+ const factual = LLM.chat("gpt-4o").withTemperature(0.2);
168
+
169
+ // Creative (0.7 - 1.0)
170
+ const creative = LLM.chat("gpt-4o").withTemperature(0.9);
171
+ ```
172
+
106
173
  ---
107
174
 
108
175
  ## 📚 Examples
@@ -120,6 +187,14 @@ Check the [examples](./examples) directory for focused scripts organized by prov
120
187
  | [Paint](https://github.com/eshaiju/node-llm/blob/main/examples/openai/06-paint.mjs) | Image generation with DALL-E |
121
188
  | [Image Features](https://github.com/eshaiju/node-llm/blob/main/examples/openai/07-image-features.mjs) | Saving and processing generated images |
122
189
  | [Token Usage](https://github.com/eshaiju/node-llm/blob/main/examples/openai/08-token-usage.mjs) | Detailed stats for turns and conversations |
190
+ | [Transcribe](https://github.com/eshaiju/node-llm/blob/main/examples/openai/09-transcribe.mjs) | Audio to text transcription |
191
+ | [Capabilities](https://github.com/eshaiju/node-llm/blob/main/examples/openai/10-capabilities.mjs) | Dynamic model specs and pricing |
192
+ | [Moderate](https://github.com/eshaiju/node-llm/blob/main/examples/openai/11-moderate.mjs) | Content safety moderation |
193
+ | [Risk Assessment](https://github.com/eshaiju/node-llm/blob/main/examples/openai/12-risk-assessment.mjs) | Custom thresholds and risk levels |
194
+ | [Chat Events](https://github.com/eshaiju/node-llm/blob/main/examples/openai/13-chat-events.mjs) | Lifecycle hooks (onNewMessage, onToolCall etc) |
195
+ | [System Prompts](https://github.com/eshaiju/node-llm/blob/main/examples/openai/15-system-prompts.mjs) | Dynamic system instructions |
196
+ | [Temperature](https://github.com/eshaiju/node-llm/blob/main/examples/openai/16-temperature.mjs) | Control creativity vs determinism |
197
+ | [Multi-File](https://github.com/eshaiju/node-llm/blob/main/examples/openai/17-multi-file.mjs) | Analyze multiple files at once |
123
198
 
124
199
  To run an example (from the project root):
125
200
  ```bash
@@ -157,7 +232,15 @@ const reply = await chat
157
232
 
158
233
  ### Multi-modal & File Support
159
234
 
160
- Pass local paths or URLs directly. The library handles reading, MIME detection, and encoding.
235
+ Pass local paths or URLs directly. The library handles reading, MIME detection, and encoding for a wide variety of file types.
236
+
237
+ **Supported File Types:**
238
+ - **Images**: `.jpg`, `.jpeg`, `.png`, `.gif`, `.webp`
239
+ - **Videos**: `.mp4`, `.mpeg`, `.mov`
240
+ - **Audio**: `.wav`, `.mp3`
241
+ - **Documents**: `.csv`, `.json`
242
+ - **Code**: `.js`, `.mjs`, `.cjs`, `.ts`, `.py`, `.rb`, `.go`, `.java`, `.c`, `.cpp`, `.rs`, `.swift`, `.kt`
243
+ - **Text**: `.txt`, `.md`, `.html`, `.css`, `.xml`, `.yml`, `.yaml`
161
244
 
162
245
  ```ts
163
246
  // Vision
@@ -170,10 +253,28 @@ await chat.ask("Transcribe this", {
170
253
  files: ["./meeting.mp3"]
171
254
  });
172
255
 
173
- // Text/Code Analysis
256
+ // Code Analysis
174
257
  await chat.ask("Explain this code", {
175
258
  files: ["./app.ts"]
176
259
  });
260
+
261
+ // Multiple files at once
262
+ await chat.ask("Analyze these files", {
263
+ files: ["diagram.png", "data.json", "notes.txt"]
264
+ });
265
+ ```
266
+
267
+ ### Custom HTTP Headers (Proxies/Observability)
268
+
269
+ Inject custom headers into requests, useful for tools like Helicone or Portkey.
270
+
271
+ ```ts
272
+ chat.withRequestOptions({
273
+ headers: {
274
+ "Helicone-Auth": "Bearer my-key",
275
+ "X-Custom-Trace": "123"
276
+ }
277
+ });
177
278
  ```
178
279
 
179
280
  ---
@@ -182,7 +283,7 @@ await chat.ask("Explain this code", {
182
283
 
183
284
  | Provider | Status | Notes |
184
285
  | :--- | :--- | :--- |
185
- | **OpenAI** | ✅ Supported | Chat, Streaming, Tools, Vision, Audio, Images (DALL-E) |
286
+ | **OpenAI** | ✅ Supported | Chat, Streaming, Tools, Vision, Audio, Images, Transcription, Moderation |
186
287
  | **Anthropic** | 🏗️ Roadmap | Coming soon |
187
288
  | **Azure OpenAI** | 🏗️ Roadmap | Coming soon |
188
289
 
@@ -197,6 +298,26 @@ await chat.ask("Explain this code", {
197
298
 
198
299
  ---
199
300
 
301
+ ## 🧪 Testing
302
+
303
+ `node-llm` uses VCR-style testing (via Polly.js) for robust, deterministic integration tests. This allows us to record real LLM provider interactions once and replay them during tests without making actual API calls.
304
+
305
+ ### Running Tests
306
+
307
+ - **Replay Mode (Default)**: Runs tests using recorded cassettes. Fast, deterministic, and requires no API keys.
308
+ ```bash
309
+ npm test
310
+ ```
311
+
312
+ - **Record Mode**: Hits real APIs and updates cassettes. Requires a valid API key.
313
+ ```bash
314
+ VCR_MODE=record OPENAI_API_KEY=your_key npm test
315
+ ```
316
+
317
+ *All recordings are automatically scrubbed of sensitive data (API keys, org IDs) before being saved to disk.*
318
+
319
+ ---
320
+
200
321
  ## 📄 License
201
322
 
202
323
  MIT © [node-llm contributors]
@@ -6,23 +6,9 @@ export interface AskOptions {
6
6
  files?: string[];
7
7
  temperature?: number;
8
8
  maxTokens?: number;
9
+ headers?: Record<string, string>;
9
10
  }
10
- /**
11
- * Enhanced string that includes token usage metadata.
12
- * Behaves like a regular string but has .usage and .input_tokens etc.
13
- */
14
- export declare class ChatResponseString extends String {
15
- readonly usage: Usage;
16
- readonly model: string;
17
- constructor(content: string, usage: Usage, model: string);
18
- get input_tokens(): number;
19
- get output_tokens(): number;
20
- get total_tokens(): number;
21
- get cached_tokens(): number | undefined;
22
- get content(): string;
23
- get model_id(): string;
24
- toString(): string;
25
- }
11
+ import { ChatResponseString } from "./ChatResponse.js";
26
12
  export declare class Chat {
27
13
  private readonly provider;
28
14
  private readonly model;
@@ -42,6 +28,36 @@ export declare class Chat {
42
28
  * Add a tool to the chat session (fluent API)
43
29
  */
44
30
  withTool(tool: any): this;
31
+ /**
32
+ * Add instructions (system prompt) to the chat.
33
+ * By default, it appends a new system message.
34
+ * If { replace: true } is passed, it removes all previous system messages first.
35
+ */
36
+ withInstructions(instruction: string, options?: {
37
+ replace?: boolean;
38
+ }): this;
39
+ /**
40
+ * Alias for withInstructions
41
+ */
42
+ withSystemPrompt(instruction: string, options?: {
43
+ replace?: boolean;
44
+ }): this;
45
+ /**
46
+ * Set the temperature for the chat session.
47
+ * Controls randomness: 0.0 (deterministic) to 1.0 (creative).
48
+ */
49
+ withTemperature(temp: number): this;
50
+ /**
51
+ * Set custom headers for the chat session.
52
+ * Merges with existing headers.
53
+ */
54
+ withRequestOptions(options: {
55
+ headers?: Record<string, string>;
56
+ }): this;
57
+ onNewMessage(handler: () => void): this;
58
+ onEndMessage(handler: (message: ChatResponseString) => void): this;
59
+ onToolCall(handler: (toolCall: any) => void): this;
60
+ onToolResult(handler: (result: any) => void): this;
45
61
  /**
46
62
  * Ask the model a question
47
63
  */
@@ -1 +1 @@
1
- {"version":3,"file":"Chat.d.ts","sourceRoot":"","sources":["../../src/chat/Chat.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AACvC,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAC/C,OAAO,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,0BAA0B,CAAC;AAK3D,MAAM,WAAW,UAAU;IACzB,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;;GAGG;AACH,qBAAa,kBAAmB,SAAQ,MAAM;aAG1B,KAAK,EAAE,KAAK;aACZ,KAAK,EAAE,MAAM;gBAF7B,OAAO,EAAE,MAAM,EACC,KAAK,EAAE,KAAK,EACZ,KAAK,EAAE,MAAM;IAK/B,IAAI,YAAY,WAAsC;IACtD,IAAI,aAAa,WAAuC;IACxD,IAAI,YAAY,WAAsC;IACtD,IAAI,aAAa,uBAAuC;IAExD,IAAI,OAAO,IAAI,MAAM,CAEpB;IAED,IAAI,QAAQ,IAAI,MAAM,CAErB;IAED,QAAQ;CAGT;AAED,qBAAa,IAAI;IAKb,OAAO,CAAC,QAAQ,CAAC,QAAQ;IACzB,OAAO,CAAC,QAAQ,CAAC,KAAK;IACtB,OAAO,CAAC,QAAQ,CAAC,OAAO;IAN1B,OAAO,CAAC,QAAQ,CAAiB;IACjC,OAAO,CAAC,QAAQ,CAAW;gBAGR,QAAQ,EAAE,QAAQ,EAClB,KAAK,EAAE,MAAM,EACb,OAAO,GAAE,WAAgB;IAmB5C;;OAEG;IACH,IAAI,OAAO,IAAI,SAAS,OAAO,EAAE,CAEhC;IAED;;OAEG;IACH,IAAI,UAAU,IAAI,KAAK,CAatB;IAED;;OAEG;IACH,QAAQ,CAAC,IAAI,EAAE,GAAG,GAAG,IAAI;IAQzB;;OAEG;IACG,GAAG,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,UAAU,GAAG,OAAO,CAAC,kBAAkB,CAAC;IA6G7E;;OAEG;IACI,MAAM,CAAC,OAAO,EAAE,MAAM;CAI9B"}
1
+ {"version":3,"file":"Chat.d.ts","sourceRoot":"","sources":["../../src/chat/Chat.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AACvC,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAC/C,OAAO,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,0BAA0B,CAAC;AAK3D,MAAM,WAAW,UAAU;IACzB,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAClC;AAED,OAAO,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AAEvD,qBAAa,IAAI;IAKb,OAAO,CAAC,QAAQ,CAAC,QAAQ;IACzB,OAAO,CAAC,QAAQ,CAAC,KAAK;IACtB,OAAO,CAAC,QAAQ,CAAC,OAAO;IAN1B,OAAO,CAAC,QAAQ,CAAiB;IACjC,OAAO,CAAC,QAAQ,CAAW;gBAGR,QAAQ,EAAE,QAAQ,EAClB,KAAK,EAAE,MAAM,EACb,OAAO,GAAE,WAAgB;IAmB5C;;OAEG;IACH,IAAI,OAAO,IAAI,SAAS,OAAO,EAAE,CAEhC;IAED;;OAEG;IACH,IAAI,UAAU,IAAI,KAAK,CAatB;IAED;;OAEG;IACH,QAAQ,CAAC,IAAI,EAAE,GAAG,GAAG,IAAI;IAQzB;;;;OAIG;IACH,gBAAgB,CAAC,WAAW,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;QAAE,OAAO,CAAC,EAAE,OAAO,CAAA;KAAE,GAAG,IAAI;IAmB5E;;OAEG;IACH,gBAAgB,CAAC,WAAW,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;QAAE,OAAO,CAAC,EAAE,OAAO,CAAA;KAAE,GAAG,IAAI;IAI5E;;;OAGG;IACH,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IAKnC;;;OAGG;IACH,kBAAkB,CAAC,OAAO,EAAE;QAAE,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;KAAE,GAAG,IAAI;IASvE,YAAY,CAAC,OAAO,EAAE,MAAM,IAAI,GAAG,IAAI;IAKvC,YAAY,CAAC,OAAO,EAAE,CAAC,OAAO,EAAE,kBAAkB,KAAK,IAAI,GAAG,IAAI;IAKlE,UAAU,CAAC,OAAO,EAAE,CAAC,QAAQ,EAAE,GAAG,KAAK,IAAI,GAAG,IAAI;IAKlD,YAAY,CAAC,OAAO,EAAE,CAAC,MAAM,EAAE,GAAG,KAAK,IAAI,GAAG,IAAI;IAKlD;;OAEG;IACG,GAAG,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,UAAU,GAAG,OAAO,CAAC,kBAAkB,CAAC;IAwJ7E;;OAEG;IACI,MAAM,CAAC,OAAO,EAAE,MAAM;CAI9B"}
package/dist/chat/Chat.js CHANGED
@@ -2,32 +2,7 @@ import { FileLoader } from "../utils/FileLoader.js";
2
2
  import { Executor } from "../executor/Executor.js";
3
3
  import { LLM } from "../llm.js";
4
4
  import { Stream } from "./Stream.js";
5
- /**
6
- * Enhanced string that includes token usage metadata.
7
- * Behaves like a regular string but has .usage and .input_tokens etc.
8
- */
9
- export class ChatResponseString extends String {
10
- usage;
11
- model;
12
- constructor(content, usage, model) {
13
- super(content);
14
- this.usage = usage;
15
- this.model = model;
16
- }
17
- get input_tokens() { return this.usage.input_tokens; }
18
- get output_tokens() { return this.usage.output_tokens; }
19
- get total_tokens() { return this.usage.total_tokens; }
20
- get cached_tokens() { return this.usage.cached_tokens; }
21
- get content() {
22
- return this.valueOf();
23
- }
24
- get model_id() {
25
- return this.model;
26
- }
27
- toString() {
28
- return this.valueOf();
29
- }
30
- }
5
+ import { ChatResponseString } from "./ChatResponse.js";
31
6
  export class Chat {
32
7
  provider;
33
8
  model;
@@ -79,6 +54,69 @@ export class Chat {
79
54
  this.options.tools.push(tool);
80
55
  return this;
81
56
  }
57
+ /**
58
+ * Add instructions (system prompt) to the chat.
59
+ * By default, it appends a new system message.
60
+ * If { replace: true } is passed, it removes all previous system messages first.
61
+ */
62
+ withInstructions(instruction, options) {
63
+ if (options?.replace) {
64
+ this.messages = this.messages.filter((m) => m.role !== "system");
65
+ }
66
+ // System messages usually go first, but for "appending" behavior
67
+ // mid-conversation, most providers handle them fine in history.
68
+ // Ideally, if it's "replace", we might want to unshift it to index 0,
69
+ // but simply pushing a new system message works for "updating" context too.
70
+ // For consistency with "replace" meaning "this is THE system prompt":
71
+ if (options?.replace) {
72
+ this.messages.unshift({ role: "system", content: instruction });
73
+ }
74
+ else {
75
+ this.messages.push({ role: "system", content: instruction });
76
+ }
77
+ return this;
78
+ }
79
+ /**
80
+ * Alias for withInstructions
81
+ */
82
+ withSystemPrompt(instruction, options) {
83
+ return this.withInstructions(instruction, options);
84
+ }
85
+ /**
86
+ * Set the temperature for the chat session.
87
+ * Controls randomness: 0.0 (deterministic) to 1.0 (creative).
88
+ */
89
+ withTemperature(temp) {
90
+ this.options.temperature = temp;
91
+ return this;
92
+ }
93
+ /**
94
+ * Set custom headers for the chat session.
95
+ * Merges with existing headers.
96
+ */
97
+ withRequestOptions(options) {
98
+ if (options.headers) {
99
+ this.options.headers = { ...this.options.headers, ...options.headers };
100
+ }
101
+ return this;
102
+ }
103
+ // --- Event Handlers ---
104
+ onNewMessage(handler) {
105
+ this.options.onNewMessage = handler;
106
+ return this;
107
+ }
108
+ onEndMessage(handler) {
109
+ this.options.onEndMessage = handler;
110
+ return this;
111
+ }
112
+ onToolCall(handler) {
113
+ this.options.onToolCall = handler;
114
+ return this;
115
+ }
116
+ onToolResult(handler) {
117
+ this.options.onToolResult = handler;
118
+ return this;
119
+ }
82
120
  /**
83
121
  * Ask the model a question
84
122
  */
@@ -91,10 +129,25 @@ export class Chat {
91
129
  if (hasBinary && this.provider.capabilities && !this.provider.capabilities.supportsVision(this.model)) {
92
130
  throw new Error(`Model ${this.model} does not support vision/binary files.`);
93
131
  }
94
- messageContent = [
95
- { type: "text", text: content },
96
- ...processedFiles
97
- ];
132
+ // Separate text files from binary files
133
+ const textFiles = processedFiles.filter(p => p.type === "text");
134
+ const binaryFiles = processedFiles.filter(p => p.type !== "text");
135
+ // Concatenate text files into the main content
136
+ let fullText = content;
137
+ if (textFiles.length > 0) {
138
+ fullText += "\n" + textFiles.map(f => f.text).join("\n");
139
+ }
140
+ // If we have binary files, create multimodal content
141
+ if (binaryFiles.length > 0) {
142
+ messageContent = [
143
+ { type: "text", text: fullText },
144
+ ...binaryFiles
145
+ ];
146
+ }
147
+ else {
148
+ // Only text files, keep as string
149
+ messageContent = fullText;
150
+ }
98
151
  }
99
152
  if (this.options.tools && this.options.tools.length > 0) {
100
153
  if (this.provider.capabilities && !this.provider.capabilities.supportsTools(this.model)) {
@@ -111,6 +164,7 @@ export class Chat {
111
164
  tools: this.options.tools,
112
165
  temperature: options?.temperature ?? this.options.temperature,
113
166
  max_tokens: options?.maxTokens ?? this.options.maxTokens,
167
+ headers: { ...this.options.headers, ...options?.headers },
114
168
  };
115
169
  let totalUsage = { input_tokens: 0, output_tokens: 0, total_tokens: 0 };
116
170
  const trackUsage = (u) => {
@@ -123,21 +177,32 @@ export class Chat {
123
177
  }
124
178
  }
125
179
  };
180
+ // First round
181
+ if (this.options.onNewMessage)
182
+ this.options.onNewMessage();
126
183
  let response = await this.executor.executeChat(executeOptions);
127
184
  trackUsage(response.usage);
185
+ const firstAssistantMessage = new ChatResponseString(response.content ?? "", response.usage ?? { input_tokens: 0, output_tokens: 0, total_tokens: 0 }, this.model);
128
186
  this.messages.push({
129
187
  role: "assistant",
130
- content: new ChatResponseString(response.content ?? "", response.usage ?? { input_tokens: 0, output_tokens: 0, total_tokens: 0 }, this.model),
188
+ content: firstAssistantMessage,
131
189
  tool_calls: response.tool_calls,
132
190
  usage: response.usage,
133
191
  });
192
+ if (this.options.onEndMessage && (!response.tool_calls || response.tool_calls.length === 0)) {
193
+ this.options.onEndMessage(firstAssistantMessage);
194
+ }
134
195
  while (response.tool_calls && response.tool_calls.length > 0) {
135
196
  for (const toolCall of response.tool_calls) {
197
+ if (this.options.onToolCall)
198
+ this.options.onToolCall(toolCall);
136
199
  const tool = this.options.tools?.find((t) => t.function.name === toolCall.function.name);
137
200
  if (tool?.handler) {
138
201
  try {
139
202
  const args = JSON.parse(toolCall.function.arguments);
140
203
  const result = await tool.handler(args);
204
+ if (this.options.onToolResult)
205
+ this.options.onToolResult(result);
141
206
  this.messages.push({
142
207
  role: "tool",
143
208
  tool_call_id: toolCall.id,
@@ -164,14 +229,19 @@ export class Chat {
164
229
  model: this.model,
165
230
  messages: this.messages,
166
231
  tools: this.options.tools,
232
+ headers: this.options.headers,
167
233
  });
168
234
  trackUsage(response.usage);
235
+ const assistantMessage = new ChatResponseString(response.content ?? "", response.usage ?? { input_tokens: 0, output_tokens: 0, total_tokens: 0 }, this.model);
169
236
  this.messages.push({
170
237
  role: "assistant",
171
- content: new ChatResponseString(response.content ?? "", response.usage ?? { input_tokens: 0, output_tokens: 0, total_tokens: 0 }, this.model),
238
+ content: assistantMessage,
172
239
  tool_calls: response.tool_calls,
173
240
  usage: response.usage,
174
241
  });
242
+ if (this.options.onEndMessage && (!response.tool_calls || response.tool_calls.length === 0)) {
243
+ this.options.onEndMessage(assistantMessage);
244
+ }
175
245
  }
176
246
  return new ChatResponseString(response.content ?? "", totalUsage, this.model);
177
247
  }
@@ -6,5 +6,10 @@ export interface ChatOptions {
6
6
  tools?: Tool[];
7
7
  temperature?: number;
8
8
  maxTokens?: number;
9
+ onNewMessage?: () => void;
10
+ onEndMessage?: (message: any) => void;
11
+ onToolCall?: (toolCall: any) => void;
12
+ onToolResult?: (result: any) => void;
13
+ headers?: Record<string, string>;
9
14
  }
10
15
  //# sourceMappingURL=ChatOptions.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"ChatOptions.d.ts","sourceRoot":"","sources":["../../src/chat/ChatOptions.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AACvC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEjC,MAAM,WAAW,WAAW;IAC1B,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,EAAE,OAAO,EAAE,CAAC;IACrB,KAAK,CAAC,EAAE,IAAI,EAAE,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB"}
1
+ {"version":3,"file":"ChatOptions.d.ts","sourceRoot":"","sources":["../../src/chat/ChatOptions.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AACvC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEjC,MAAM,WAAW,WAAW;IAC1B,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,EAAE,OAAO,EAAE,CAAC;IACrB,KAAK,CAAC,EAAE,IAAI,EAAE,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,YAAY,CAAC,EAAE,MAAM,IAAI,CAAC;IAC1B,YAAY,CAAC,EAAE,CAAC,OAAO,EAAE,GAAG,KAAK,IAAI,CAAC;IACtC,UAAU,CAAC,EAAE,CAAC,QAAQ,EAAE,GAAG,KAAK,IAAI,CAAC;IACrC,YAAY,CAAC,EAAE,CAAC,MAAM,EAAE,GAAG,KAAK,IAAI,CAAC;IACrC,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAClC"}
@@ -0,0 +1,18 @@
1
+ import { Usage } from "../providers/Provider.js";
2
+ /**
3
+ * Enhanced string that includes token usage metadata.
4
+ * Behaves like a regular string but has .usage and .input_tokens etc.
5
+ */
6
+ export declare class ChatResponseString extends String {
7
+ readonly usage: Usage;
8
+ readonly model: string;
9
+ constructor(content: string, usage: Usage, model: string);
10
+ get input_tokens(): number;
11
+ get output_tokens(): number;
12
+ get total_tokens(): number;
13
+ get cached_tokens(): number | undefined;
14
+ get content(): string;
15
+ get model_id(): string;
16
+ toString(): string;
17
+ }
18
+ //# sourceMappingURL=ChatResponse.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ChatResponse.d.ts","sourceRoot":"","sources":["../../src/chat/ChatResponse.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,0BAA0B,CAAC;AAEjD;;;GAGG;AACH,qBAAa,kBAAmB,SAAQ,MAAM;aAG1B,KAAK,EAAE,KAAK;aACZ,KAAK,EAAE,MAAM;gBAF7B,OAAO,EAAE,MAAM,EACC,KAAK,EAAE,KAAK,EACZ,KAAK,EAAE,MAAM;IAK/B,IAAI,YAAY,WAAsC;IACtD,IAAI,aAAa,WAAuC;IACxD,IAAI,YAAY,WAAsC;IACtD,IAAI,aAAa,uBAAuC;IAExD,IAAI,OAAO,IAAI,MAAM,CAEpB;IAED,IAAI,QAAQ,IAAI,MAAM,CAErB;IAED,QAAQ;CAGT"}
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Enhanced string that includes token usage metadata.
3
+ * Behaves like a regular string but has .usage and .input_tokens etc.
4
+ */
5
+ export class ChatResponseString extends String {
6
+ usage;
7
+ model;
8
+ constructor(content, usage, model) {
9
+ super(content);
10
+ this.usage = usage;
11
+ this.model = model;
12
+ }
13
+ get input_tokens() { return this.usage.input_tokens; }
14
+ get output_tokens() { return this.usage.output_tokens; }
15
+ get total_tokens() { return this.usage.total_tokens; }
16
+ get cached_tokens() { return this.usage.cached_tokens; }
17
+ get content() {
18
+ return this.valueOf();
19
+ }
20
+ get model_id() {
21
+ return this.model;
22
+ }
23
+ toString() {
24
+ return this.valueOf();
25
+ }
26
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"Stream.d.ts","sourceRoot":"","sources":["../../src/chat/Stream.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AACvC,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAC/C,OAAO,EAAE,QAAQ,EAAE,MAAM,0BAA0B,CAAC;AAEpD,qBAAa,MAAM;IAIf,OAAO,CAAC,QAAQ,CAAC,QAAQ;IACzB,OAAO,CAAC,QAAQ,CAAC,KAAK;IACtB,OAAO,CAAC,QAAQ,CAAC,OAAO;IAL1B,OAAO,CAAC,QAAQ,CAAY;gBAGT,QAAQ,EAAE,QAAQ,EAClB,KAAK,EAAE,MAAM,EACb,OAAO,GAAE,WAAgB,EAC1C,QAAQ,CAAC,EAAE,OAAO,EAAE;IAmBtB;;OAEG;IACH,IAAI,OAAO,IAAI,SAAS,OAAO,EAAE,CAEhC;IAED;;;;OAIG;IACI,MAAM,CAAC,OAAO,EAAE,MAAM;CA0B9B"}
1
+ {"version":3,"file":"Stream.d.ts","sourceRoot":"","sources":["../../src/chat/Stream.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AACvC,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAC/C,OAAO,EAAE,QAAQ,EAAE,MAAM,0BAA0B,CAAC;AAGpD,qBAAa,MAAM;IAIf,OAAO,CAAC,QAAQ,CAAC,QAAQ;IACzB,OAAO,CAAC,QAAQ,CAAC,KAAK;IACtB,OAAO,CAAC,QAAQ,CAAC,OAAO;IAL1B,OAAO,CAAC,QAAQ,CAAY;gBAGT,QAAQ,EAAE,QAAQ,EAClB,KAAK,EAAE,MAAM,EACb,OAAO,GAAE,WAAgB,EAC1C,QAAQ,CAAC,EAAE,OAAO,EAAE;IAmBtB;;OAEG;IACH,IAAI,OAAO,IAAI,SAAS,OAAO,EAAE,CAEhC;IAED;;;;OAIG;IACI,MAAM,CAAC,OAAO,EAAE,MAAM;CAwC9B"}
@@ -1,3 +1,4 @@
1
+ import { ChatResponseString } from "./ChatResponse.js";
1
2
  export class Stream {
2
3
  provider;
3
4
  model;
@@ -38,12 +39,18 @@ export class Stream {
38
39
  throw new Error("Streaming not supported by provider");
39
40
  }
40
41
  let full = "";
42
+ let isFirst = true;
41
43
  for await (const chunk of this.provider.stream({
42
44
  model: this.model,
43
45
  messages: this.messages,
44
46
  temperature: this.options.temperature,
45
47
  max_tokens: this.options.maxTokens,
46
48
  })) {
49
+ if (isFirst) {
50
+ if (this.options.onNewMessage)
51
+ this.options.onNewMessage();
52
+ isFirst = false;
53
+ }
47
54
  if (chunk.content) {
48
55
  full += chunk.content;
49
56
  }
@@ -53,5 +60,8 @@ export class Stream {
53
60
  role: "assistant",
54
61
  content: full,
55
62
  });
63
+ if (this.options.onEndMessage) {
64
+ this.options.onEndMessage(new ChatResponseString(full, { input_tokens: 0, output_tokens: 0, total_tokens: 0 }, this.model));
65
+ }
56
66
  }
57
67
  }
package/dist/index.d.ts CHANGED
@@ -6,7 +6,7 @@ export type { Role } from "./chat/Role.js";
6
6
  export type { ChatOptions } from "./chat/ChatOptions.js";
7
7
  export type { Tool, ToolCall } from "./chat/Tool.js";
8
8
  export type { MessageContent, ContentPart } from "./chat/Content.js";
9
- export { LLM } from "./llm.js";
9
+ export { LLM, Transcription, Moderation } from "./llm.js";
10
10
  export { providerRegistry } from "./providers/registry.js";
11
11
  export { OpenAIProvider } from "./providers/openai/OpenAIProvider.js";
12
12
  export { registerOpenAIProvider } from "./providers/openai/index.js";
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,gBAAgB,CAAC;AACtC,OAAO,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAC1C,OAAO,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAC3D,YAAY,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAC;AACjD,YAAY,EAAE,IAAI,EAAE,MAAM,gBAAgB,CAAC;AAC3C,YAAY,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AACzD,YAAY,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AACrD,YAAY,EAAE,cAAc,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAErE,OAAO,EAAE,GAAG,EAAE,MAAM,UAAU,CAAC;AAC/B,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAE3D,OAAO,EAAE,cAAc,EAAE,MAAM,sCAAsC,CAAC;AACtE,OAAO,EAAE,sBAAsB,EAAE,MAAM,6BAA6B,CAAC;AACrE,YAAY,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,yBAAyB,CAAC;AAC3E,cAAc,mBAAmB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,gBAAgB,CAAC;AACtC,OAAO,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAC1C,OAAO,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAC3D,YAAY,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAC;AACjD,YAAY,EAAE,IAAI,EAAE,MAAM,gBAAgB,CAAC;AAC3C,YAAY,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AACzD,YAAY,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AACrD,YAAY,EAAE,cAAc,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAErE,OAAO,EAAE,GAAG,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AAC1D,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAE3D,OAAO,EAAE,cAAc,EAAE,MAAM,sCAAsC,CAAC;AACtE,OAAO,EAAE,sBAAsB,EAAE,MAAM,6BAA6B,CAAC;AACrE,YAAY,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,yBAAyB,CAAC;AAC3E,cAAc,mBAAmB,CAAC"}
package/dist/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  export { Chat } from "./chat/Chat.js";
2
2
  export { Stream } from "./chat/Stream.js";
3
3
  export { GeneratedImage } from "./image/GeneratedImage.js";
4
- export { LLM } from "./llm.js";
4
+ export { LLM, Transcription, Moderation } from "./llm.js";
5
5
  export { providerRegistry } from "./providers/registry.js";
6
6
  export { OpenAIProvider } from "./providers/openai/OpenAIProvider.js";
7
7
  export { registerOpenAIProvider } from "./providers/openai/index.js";
package/dist/llm.d.ts CHANGED
@@ -1,7 +1,10 @@
1
1
  import { Chat } from "./chat/Chat.js";
2
2
  import { ChatOptions } from "./chat/ChatOptions.js";
3
- import { Provider } from "./providers/Provider.js";
3
+ import { Provider, ModelInfo } from "./providers/Provider.js";
4
4
  import { GeneratedImage } from "./image/GeneratedImage.js";
5
+ import { ModelRegistry } from "./models/ModelRegistry.js";
6
+ import { Transcription } from "./transcription/Transcription.js";
7
+ import { Moderation } from "./moderation/Moderation.js";
5
8
  export interface RetryOptions {
6
9
  attempts?: number;
7
10
  delayMs?: number;
@@ -9,23 +12,42 @@ export interface RetryOptions {
9
12
  type LLMConfig = {
10
13
  provider: Provider;
11
14
  retry?: RetryOptions;
15
+ defaultTranscriptionModel?: string;
16
+ defaultModerationModel?: string;
12
17
  } | {
13
18
  provider: string;
14
19
  retry?: RetryOptions;
20
+ defaultTranscriptionModel?: string;
21
+ defaultModerationModel?: string;
15
22
  };
16
23
  declare class LLMCore {
24
+ readonly models: ModelRegistry;
17
25
  private provider?;
26
+ private defaultTranscriptionModelId?;
27
+ private defaultModerationModelId?;
18
28
  private retry;
19
29
  configure(config: LLMConfig): void;
20
30
  chat(model: string, options?: ChatOptions): Chat;
21
- listModels(): Promise<import("./providers/Provider.js").ModelInfo[]>;
31
+ listModels(): Promise<ModelInfo[]>;
22
32
  paint(prompt: string, options?: {
23
33
  model?: string;
24
34
  size?: string;
25
35
  quality?: string;
26
36
  }): Promise<GeneratedImage>;
37
+ transcribe(file: string, options?: {
38
+ model?: string;
39
+ prompt?: string;
40
+ language?: string;
41
+ speakerNames?: string[];
42
+ speakerReferences?: string[];
43
+ }): Promise<Transcription>;
44
+ get defaultTranscriptionModel(): string | undefined;
45
+ get defaultModerationModel(): string | undefined;
27
46
  getRetryConfig(): Required<RetryOptions>;
47
+ moderate(input: string | string[], options?: {
48
+ model?: string;
49
+ }): Promise<Moderation>;
28
50
  }
51
+ export { Transcription, Moderation };
29
52
  export declare const LLM: LLMCore;
30
- export {};
31
53
  //# sourceMappingURL=llm.d.ts.map
package/dist/llm.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"llm.d.ts","sourceRoot":"","sources":["../src/llm.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,gBAAgB,CAAC;AAEtC,OAAO,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AACpD,OAAO,EAAE,QAAQ,EAAE,MAAM,yBAAyB,CAAC;AAGnD,OAAO,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAE3D,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,KAAK,SAAS,GACV;IAAE,QAAQ,EAAE,QAAQ,CAAC;IAAC,KAAK,CAAC,EAAE,YAAY,CAAA;CAAE,GAC5C;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,YAAY,CAAA;CAAE,CAAC;AAE/C,cAAM,OAAO;IACX,OAAO,CAAC,QAAQ,CAAC,CAAW;IAE5B,OAAO,CAAC,KAAK,CAGX;IAEF,SAAS,CAAC,MAAM,EAAE,SAAS;IAmB3B,IAAI,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,WAAW,GAAG,IAAI;IAQ1C,UAAU;IAUV,KAAK,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,cAAc,CAAC;IAgBnH,cAAc;CAIf;AAED,eAAO,MAAM,GAAG,SAAgB,CAAC"}
1
+ {"version":3,"file":"llm.d.ts","sourceRoot":"","sources":["../src/llm.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,gBAAgB,CAAC;AACtC,OAAO,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AACpD,OAAO,EACL,QAAQ,EACR,SAAS,EAKV,MAAM,yBAAyB,CAAC;AAGjC,OAAO,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAC3D,OAAO,EAAU,aAAa,EAAE,MAAM,2BAA2B,CAAC;AAClE,OAAO,EAAE,aAAa,EAAE,MAAM,kCAAkC,CAAC;AACjE,OAAO,EAAE,UAAU,EAAE,MAAM,4BAA4B,CAAC;AAExD,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,KAAK,SAAS,GACV;IAAE,QAAQ,EAAE,QAAQ,CAAC;IAAC,KAAK,CAAC,EAAE,YAAY,CAAC;IAAC,yBAAyB,CAAC,EAAE,MAAM,CAAC;IAAC,sBAAsB,CAAC,EAAE,MAAM,CAAA;CAAE,GACjH;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,YAAY,CAAC;IAAC,yBAAyB,CAAC,EAAE,MAAM,CAAC;IAAC,sBAAsB,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AAEpH,cAAM,OAAO;IACX,SAAgB,MAAM,EAAE,aAAa,CAAU;IAC/C,OAAO,CAAC,QAAQ,CAAC,CAAW;IAC5B,OAAO,CAAC,2BAA2B,CAAC,CAAS;IAC7C,OAAO,CAAC,wBAAwB,CAAC,CAAS;IAE1C,OAAO,CAAC,KAAK,CAGX;IAEF,SAAS,CAAC,MAAM,EAAE,SAAS;IA2B3B,IAAI,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,WAAW,GAAG,IAAI;IAQ1C,UAAU,IAAI,OAAO,CAAC,SAAS,EAAE,CAAC;IAUlC,KAAK,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,cAAc,CAAC;IAgB7G,UAAU,CACd,IAAI,EAAE,MAAM,EACZ,OAAO,CAAC,EAAE;QACR,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;QACxB,iBAAiB,CAAC,EAAE,MAAM,EAAE,CAAC;KAC9B,GACA,OAAO,CAAC,aAAa,CAAC;IAiBzB,IAAI,yBAAyB,IAAI,MAAM,GAAG,SAAS,CAElD;IAED,IAAI,sBAAsB,IAAI,MAAM,GAAG,SAAS,CAE/C;IAED,cAAc;IAIR,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,EAAE,OAAO,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,UAAU,CAAC;CAgB5F;AAED,OAAO,EAAE,aAAa,EAAE,UAAU,EAAE,CAAC;AAErC,eAAO,MAAM,GAAG,SAAgB,CAAC"}