@umituz/web-ai 0.0.0-stage → 1.0.1

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/LICENSE +21 -0
  2. package/README.md +98 -2
  3. package/dist/AIErrors-B2ogd1PB.d.ts +197 -0
  4. package/dist/AIErrors-C5EasV_c.d.cts +197 -0
  5. package/dist/ContentGeneration-B6iK2B33.d.cts +93 -0
  6. package/dist/ContentGeneration-B6iK2B33.d.ts +93 -0
  7. package/dist/ProviderConfig-DIFa9nmG.d.cts +108 -0
  8. package/dist/ProviderConfig-DIFa9nmG.d.ts +108 -0
  9. package/dist/cache-manager-Df9LLYGA.d.cts +31 -0
  10. package/dist/cache-manager-Df9LLYGA.d.ts +31 -0
  11. package/dist/chat/index.cjs +625 -0
  12. package/dist/chat/index.cjs.map +1 -0
  13. package/dist/chat/index.d.cts +25 -0
  14. package/dist/chat/index.d.ts +25 -0
  15. package/dist/chat/index.js +618 -0
  16. package/dist/chat/index.js.map +1 -0
  17. package/dist/chat.service-CL1qtYxe.d.cts +124 -0
  18. package/dist/chat.service-q05GnhEm.d.ts +124 -0
  19. package/dist/content/index.cjs +3976 -0
  20. package/dist/content/index.cjs.map +1 -0
  21. package/dist/content/index.d.cts +224 -0
  22. package/dist/content/index.d.ts +224 -0
  23. package/dist/content/index.js +3945 -0
  24. package/dist/content/index.js.map +1 -0
  25. package/dist/core/index.cjs +1137 -0
  26. package/dist/core/index.cjs.map +1 -0
  27. package/dist/core/index.d.cts +204 -0
  28. package/dist/core/index.d.ts +204 -0
  29. package/dist/core/index.js +1064 -0
  30. package/dist/core/index.js.map +1 -0
  31. package/dist/create-ai-DOI9jD3v.d.cts +251 -0
  32. package/dist/create-ai-DXEoke8-.d.ts +251 -0
  33. package/dist/hooks/index.cjs +5246 -0
  34. package/dist/hooks/index.cjs.map +1 -0
  35. package/dist/hooks/index.d.cts +210 -0
  36. package/dist/hooks/index.d.ts +210 -0
  37. package/dist/hooks/index.js +5235 -0
  38. package/dist/hooks/index.js.map +1 -0
  39. package/dist/index-DIHz8_OJ.d.ts +48 -0
  40. package/dist/index-DKHNcMbl.d.cts +48 -0
  41. package/dist/index-fXjxTR5W.d.cts +66 -0
  42. package/dist/index-fXjxTR5W.d.ts +66 -0
  43. package/dist/index.cjs +3309 -0
  44. package/dist/index.cjs.map +1 -0
  45. package/dist/index.d.cts +11 -0
  46. package/dist/index.d.ts +11 -0
  47. package/dist/index.js +3197 -0
  48. package/dist/index.js.map +1 -0
  49. package/dist/provider.factory-BxsfK4WW.d.cts +110 -0
  50. package/dist/provider.factory-DfDfk0dK.d.ts +110 -0
  51. package/dist/providers/index.cjs +2567 -0
  52. package/dist/providers/index.cjs.map +1 -0
  53. package/dist/providers/index.d.cts +332 -0
  54. package/dist/providers/index.d.ts +332 -0
  55. package/dist/providers/index.js +2533 -0
  56. package/dist/providers/index.js.map +1 -0
  57. package/dist/request-deduplicator.util-NDo0_n1y.d.cts +50 -0
  58. package/dist/request-deduplicator.util-NDo0_n1y.d.ts +50 -0
  59. package/dist/wizard/index.cjs +230 -0
  60. package/dist/wizard/index.cjs.map +1 -0
  61. package/dist/wizard/index.d.cts +86 -0
  62. package/dist/wizard/index.d.ts +86 -0
  63. package/dist/wizard/index.js +223 -0
  64. package/dist/wizard/index.js.map +1 -0
  65. package/package.json +114 -4
@@ -0,0 +1,625 @@
1
+ 'use strict';
2
+
3
+ // src/core/errors/ai-error-types.ts
4
+ var AIErrorType = /* @__PURE__ */ ((AIErrorType2) => {
5
+ AIErrorType2["INVALID_API_KEY"] = "INVALID_API_KEY";
6
+ AIErrorType2["MISSING_CONFIG"] = "MISSING_CONFIG";
7
+ AIErrorType2["NETWORK_ERROR"] = "NETWORK_ERROR";
8
+ AIErrorType2["ABORT_ERROR"] = "ABORT_ERROR";
9
+ AIErrorType2["RATE_LIMIT_ERROR"] = "RATE_LIMIT_ERROR";
10
+ AIErrorType2["SERVER_ERROR"] = "SERVER_ERROR";
11
+ AIErrorType2["CLIENT_ERROR"] = "CLIENT_ERROR";
12
+ AIErrorType2["UNKNOWN_ERROR"] = "UNKNOWN_ERROR";
13
+ return AIErrorType2;
14
+ })(AIErrorType || {});
15
+
16
+ // src/core/errors/ai-error.ts
17
+ var KNOWN_ERROR_TYPES = new Set(Object.values(AIErrorType));
18
+ function mapCodeToType(code) {
19
+ switch (code) {
20
+ case "VALIDATION_ERROR":
21
+ case "CLIENT_ERROR":
22
+ return "CLIENT_ERROR" /* CLIENT_ERROR */;
23
+ case "INVALID_API_KEY":
24
+ return "INVALID_API_KEY" /* INVALID_API_KEY */;
25
+ case "MISSING_CONFIG":
26
+ case "PROVIDER_UNAVAILABLE":
27
+ return "MISSING_CONFIG" /* MISSING_CONFIG */;
28
+ case "PROVIDER_QUOTA_EXHAUSTED":
29
+ case "RATE_LIMIT_ERROR":
30
+ return "RATE_LIMIT_ERROR" /* RATE_LIMIT_ERROR */;
31
+ case "NETWORK_ERROR":
32
+ case "TIMEOUT":
33
+ return "NETWORK_ERROR" /* NETWORK_ERROR */;
34
+ case "ABORT_ERROR":
35
+ case "ABORTED":
36
+ return "ABORT_ERROR" /* ABORT_ERROR */;
37
+ case "SERVER_ERROR":
38
+ case "PROVIDER_ERROR":
39
+ case "NO_RESULT":
40
+ return "SERVER_ERROR" /* SERVER_ERROR */;
41
+ default:
42
+ return "UNKNOWN_ERROR" /* UNKNOWN_ERROR */;
43
+ }
44
+ }
45
+ function mapMessageToCode(message, fallback = "UNKNOWN") {
46
+ const lower = message.toLowerCase();
47
+ if (lower.includes("validation")) return "VALIDATION_ERROR";
48
+ if (lower.includes("quota")) return "PROVIDER_QUOTA_EXHAUSTED";
49
+ if (lower.includes("unavailable")) return "PROVIDER_UNAVAILABLE";
50
+ if (lower.includes("timeout") || lower.includes("aborted")) {
51
+ return lower.includes("aborted") ? "ABORTED" : "TIMEOUT";
52
+ }
53
+ if (lower.includes("parse") || lower.includes("json")) return "PARSE_ERROR";
54
+ if (lower.includes("no result")) return "NO_RESULT";
55
+ if (lower.includes("network") || lower.includes("fetch")) return "NETWORK_ERROR";
56
+ return fallback;
57
+ }
58
+ var AIError = class _AIError extends Error {
59
+ type;
60
+ code;
61
+ cause;
62
+ metadata;
63
+ constructor(typeOrMessage, messageOrCode, cause, metadata = {}) {
64
+ let effectiveType;
65
+ let effectiveMessage;
66
+ let effectiveCode;
67
+ if (KNOWN_ERROR_TYPES.has(typeOrMessage)) {
68
+ effectiveType = typeOrMessage;
69
+ effectiveMessage = messageOrCode ?? "";
70
+ effectiveCode = effectiveType;
71
+ } else {
72
+ effectiveMessage = typeOrMessage;
73
+ effectiveCode = messageOrCode ?? "UNKNOWN";
74
+ effectiveType = mapCodeToType(effectiveCode);
75
+ }
76
+ super(effectiveMessage);
77
+ this.name = "AIError";
78
+ this.type = effectiveType;
79
+ this.code = effectiveCode;
80
+ this.cause = cause;
81
+ this.metadata = metadata;
82
+ }
83
+ static from(error, fallbackCode = "UNKNOWN") {
84
+ if (error instanceof _AIError) return error;
85
+ if (error instanceof Error) {
86
+ const code = mapMessageToCode(error.message, fallbackCode);
87
+ return new _AIError(error.message, code, error);
88
+ }
89
+ return new _AIError(String(error), fallbackCode, error);
90
+ }
91
+ toString() {
92
+ const statusPart = typeof this.metadata.status === "number" ? ` (HTTP ${this.metadata.status})` : "";
93
+ return `${this.name} [${this.type}]: ${this.message}${statusPart}`;
94
+ }
95
+ toJSON() {
96
+ return {
97
+ name: this.name,
98
+ type: this.type,
99
+ code: this.code,
100
+ message: this.message,
101
+ metadata: this.metadata
102
+ };
103
+ }
104
+ };
105
+
106
+ // src/providers/base/provider.types.ts
107
+ function isTextProvider(provider) {
108
+ return provider.capabilities.includes("text");
109
+ }
110
+
111
+ // src/content/domain/limits/ProviderTimingConfig.ts
112
+ var ProviderTimingConfig = {
113
+ HEALTH_CHECK_TTL_MS: 5 * 60 * 1e3};
114
+
115
+ // src/providers/factory/provider.factory.ts
116
+ var ProviderFactory = class {
117
+ providers = /* @__PURE__ */ new Map();
118
+ healthCache = /* @__PURE__ */ new Map();
119
+ priority;
120
+ fallbackEnabled;
121
+ defaultTier;
122
+ constructor(config = {}) {
123
+ this.priority = config.priority ?? [];
124
+ this.fallbackEnabled = config.fallbackEnabled ?? true;
125
+ this.defaultTier = config.defaultTier ?? "balanced";
126
+ }
127
+ register(provider) {
128
+ this.providers.set(provider.id, provider);
129
+ }
130
+ getProvider(id) {
131
+ return this.providers.get(id);
132
+ }
133
+ getAllProviders() {
134
+ return Array.from(this.providers.values());
135
+ }
136
+ getByCapability(capability) {
137
+ return this.getAllProviders().filter(
138
+ (provider) => provider.capabilities.includes(capability)
139
+ );
140
+ }
141
+ getTextProviders() {
142
+ return this.getAllProviders().filter(isTextProvider);
143
+ }
144
+ hasTextProvider() {
145
+ return this.getByCapability("text").length > 0;
146
+ }
147
+ /* ---------- neutral text surface ---------- */
148
+ /**
149
+ * Single-chat entry point: resolves a healthy text provider by priority,
150
+ * injects the default tier when the request names neither model nor tier,
151
+ * and walks the fallback chain on failure. Providers map tiers onto their
152
+ * own models, so switching Groq→Anthropic is a config change.
153
+ */
154
+ async chat(request) {
155
+ const effective = request.model || request.tier ? request : { ...request, tier: this.defaultTier };
156
+ return this.executeWithFallback(
157
+ "text",
158
+ (provider) => provider.generate(effective)
159
+ );
160
+ }
161
+ async chatStream(request, callbacks) {
162
+ const effective = request.model || request.tier ? request : { ...request, tier: this.defaultTier };
163
+ let lastError;
164
+ const attemptCallbacks = {
165
+ onChunk: callbacks.onChunk,
166
+ onComplete: callbacks.onComplete
167
+ };
168
+ const providers = this.getByCapability("text");
169
+ if (providers.length === 0) {
170
+ throw this.noProviderError("text");
171
+ }
172
+ const ordered = this.orderByPriority(providers);
173
+ for (const provider of ordered) {
174
+ if (!this.isProviderHealthy(provider.id)) continue;
175
+ try {
176
+ return await provider.stream(effective, attemptCallbacks);
177
+ } catch (error) {
178
+ lastError = error;
179
+ this.markDegraded(provider.id);
180
+ if (!this.fallbackEnabled) {
181
+ callbacks.onError?.(error instanceof Error ? error : new Error(String(error)));
182
+ throw error;
183
+ }
184
+ }
185
+ }
186
+ const failure = new AIError(
187
+ "SERVER_ERROR" /* SERVER_ERROR */,
188
+ `All providers failed for capability 'text'`,
189
+ lastError instanceof Error ? lastError : void 0
190
+ );
191
+ callbacks.onError?.(lastError instanceof Error ? lastError : failure);
192
+ throw failure;
193
+ }
194
+ /** AsyncGenerator adapter over chatStream — A's familiar consumption shape. */
195
+ async *streamText(request) {
196
+ const queue = [];
197
+ let wake = null;
198
+ let finished = false;
199
+ let failure;
200
+ const controller = new AbortController();
201
+ const onAbort = () => controller.abort();
202
+ request.signal?.addEventListener("abort", onAbort);
203
+ const effectiveRequest = { ...request, signal: controller.signal };
204
+ const notify = () => {
205
+ const resolve = wake;
206
+ wake = null;
207
+ resolve?.();
208
+ };
209
+ const completion = this.chatStream(effectiveRequest, {
210
+ onChunk: (chunk) => {
211
+ queue.push(chunk);
212
+ notify();
213
+ }
214
+ }).then(
215
+ () => {
216
+ finished = true;
217
+ notify();
218
+ },
219
+ (error) => {
220
+ failure = error;
221
+ finished = true;
222
+ notify();
223
+ }
224
+ );
225
+ try {
226
+ while (!finished || queue.length > 0) {
227
+ if (queue.length === 0 && !finished) {
228
+ await new Promise((resolve) => {
229
+ wake = resolve;
230
+ });
231
+ }
232
+ while (queue.length > 0) {
233
+ yield queue.shift();
234
+ }
235
+ if (failure) throw failure;
236
+ }
237
+ await completion;
238
+ } finally {
239
+ controller.abort();
240
+ request.signal?.removeEventListener("abort", onAbort);
241
+ }
242
+ }
243
+ async generateJson(request) {
244
+ const effective = request.model || request.tier ? request : { ...request, tier: this.defaultTier };
245
+ return this.executeWithFallback(
246
+ "text",
247
+ (provider) => provider.generateStructured(effective)
248
+ );
249
+ }
250
+ /* ---------- content-layer string facade (ITextGenerator) ---------- */
251
+ async generateText(prompt, options = {}) {
252
+ const result = await this.chat({
253
+ messages: [{ role: "user", content: prompt }],
254
+ model: options.model,
255
+ maxTokens: options.maxTokens,
256
+ temperature: options.temperature,
257
+ topP: options.topP
258
+ });
259
+ return result.text;
260
+ }
261
+ generateTextStream(prompt, options = {}) {
262
+ return this.streamText({
263
+ messages: [{ role: "user", content: prompt }],
264
+ model: options.model,
265
+ maxTokens: options.maxTokens,
266
+ temperature: options.temperature,
267
+ topP: options.topP
268
+ });
269
+ }
270
+ /* ---------- media surface ---------- */
271
+ async generateImage(request) {
272
+ return this.executeWithFallback(
273
+ "image",
274
+ (provider) => provider.generateImage(request)
275
+ );
276
+ }
277
+ async generateVideo(request) {
278
+ return this.executeWithFallback(
279
+ "video",
280
+ (provider) => provider.generateVideo(request)
281
+ );
282
+ }
283
+ async imageToVideo(request) {
284
+ return this.executeWithFallback(
285
+ "video",
286
+ (provider) => provider.imageToVideo(request)
287
+ );
288
+ }
289
+ async videoToVideo(request) {
290
+ return this.executeWithFallback(
291
+ "video",
292
+ (provider) => provider.videoToVideo(request)
293
+ );
294
+ }
295
+ /* ---------- health ---------- */
296
+ async checkProviderHealth(providerId, forceRefresh = false) {
297
+ const provider = this.providers.get(providerId);
298
+ if (!provider) {
299
+ return "unavailable";
300
+ }
301
+ const cached = this.healthCache.get(providerId);
302
+ const now = Date.now();
303
+ const ttl = ProviderTimingConfig.HEALTH_CHECK_TTL_MS;
304
+ if (!forceRefresh && cached && now - cached.timestamp < ttl) {
305
+ return cached.health;
306
+ }
307
+ try {
308
+ const health = await provider.healthCheck();
309
+ this.healthCache.set(providerId, { health, timestamp: now });
310
+ return health;
311
+ } catch {
312
+ this.healthCache.set(providerId, { health: "unavailable", timestamp: now });
313
+ return "unavailable";
314
+ }
315
+ }
316
+ isProviderHealthy(providerId) {
317
+ const cached = this.healthCache.get(providerId);
318
+ if (!cached) {
319
+ return true;
320
+ }
321
+ return cached.health === "healthy" || cached.health === "degraded";
322
+ }
323
+ async refreshAllHealth() {
324
+ await Promise.all(
325
+ Array.from(this.providers.keys()).map((id) => this.checkProviderHealth(id))
326
+ );
327
+ }
328
+ /* ---------- internals ---------- */
329
+ async executeWithFallback(capability, fn) {
330
+ const providers = this.getByCapability(capability);
331
+ if (providers.length === 0) {
332
+ throw this.noProviderError(capability);
333
+ }
334
+ let lastError;
335
+ for (const provider of this.orderByPriority(providers)) {
336
+ if (!this.isProviderHealthy(provider.id)) {
337
+ continue;
338
+ }
339
+ try {
340
+ return await fn(provider);
341
+ } catch (error) {
342
+ lastError = error;
343
+ this.markDegraded(provider.id);
344
+ if (!this.fallbackEnabled) {
345
+ throw error;
346
+ }
347
+ }
348
+ }
349
+ throw new AIError(
350
+ "SERVER_ERROR" /* SERVER_ERROR */,
351
+ `All providers failed for capability '${capability}'`,
352
+ lastError instanceof Error ? lastError : void 0
353
+ );
354
+ }
355
+ orderByPriority(providers) {
356
+ if (this.priority.length === 0) {
357
+ return providers;
358
+ }
359
+ const ordered = [];
360
+ for (const providerId of this.priority) {
361
+ const provider = providers.find((p) => p.id === providerId);
362
+ if (provider) ordered.push(provider);
363
+ }
364
+ for (const provider of providers) {
365
+ if (!this.priority.includes(provider.id)) {
366
+ ordered.push(provider);
367
+ }
368
+ }
369
+ return ordered;
370
+ }
371
+ markDegraded(providerId) {
372
+ const cached = this.healthCache.get(providerId);
373
+ if (cached) {
374
+ this.healthCache.set(providerId, { health: "degraded", timestamp: cached.timestamp });
375
+ }
376
+ }
377
+ noProviderError(capability) {
378
+ return new AIError(
379
+ "MISSING_CONFIG" /* MISSING_CONFIG */,
380
+ `No provider registered for capability '${capability}' \u2014 create one with createAI() and include its config block`
381
+ );
382
+ }
383
+ };
384
+
385
+ // src/core/calculations/random-base36-suffix.ts
386
+ var BASE_36_RADIX = 36;
387
+ var DECIMAL_OFFSET = 2;
388
+ function generateRandomBase36Suffix(length, random = Math.random) {
389
+ if (length <= 0) return "";
390
+ const raw = random().toString(BASE_36_RADIX).slice(DECIMAL_OFFSET);
391
+ if (raw.length >= length) return raw.slice(0, length);
392
+ let result = raw;
393
+ while (result.length < length) {
394
+ result += random().toString(BASE_36_RADIX).slice(DECIMAL_OFFSET);
395
+ }
396
+ return result.slice(0, length);
397
+ }
398
+
399
+ // src/core/constants/identifier.constants.ts
400
+ var MESSAGE_ID_PREFIX = "msg-";
401
+ var CHAT_MESSAGE_ID_SUFFIX_LENGTH = 9;
402
+ var USER_ROLE = "user";
403
+ var ASSISTANT_ROLE = "assistant";
404
+
405
+ // src/chat/utils/chat-id-generator.ts
406
+ var ID_PART_DELIMITER = "-";
407
+ var ChatIdGenerator = class {
408
+ counter = 0;
409
+ randomLength;
410
+ constructor(randomLength = CHAT_MESSAGE_ID_SUFFIX_LENGTH) {
411
+ this.randomLength = randomLength;
412
+ }
413
+ next(sender) {
414
+ this.counter += 1;
415
+ const random = generateRandomBase36Suffix(this.randomLength);
416
+ return [
417
+ MESSAGE_ID_PREFIX,
418
+ Date.now(),
419
+ sender,
420
+ this.counter,
421
+ random
422
+ ].join(ID_PART_DELIMITER);
423
+ }
424
+ };
425
+ var chatIdGenerator = new ChatIdGenerator();
426
+
427
+ // src/chat/utils/message-formatter.ts
428
+ var MessageFormatter = class {
429
+ constructor(idGenerator = chatIdGenerator) {
430
+ this.idGenerator = idGenerator;
431
+ }
432
+ idGenerator;
433
+ /** Maps chat-domain history to neutral core messages; system senders are
434
+ * dropped — the system prompt travels on the request's own slot. */
435
+ toCoreMessages(messages) {
436
+ const coreMessages = [];
437
+ for (const message of messages) {
438
+ if (message.sender === "system") continue;
439
+ coreMessages.push({
440
+ role: message.sender === "user" ? USER_ROLE : ASSISTANT_ROLE,
441
+ content: message.content
442
+ });
443
+ }
444
+ return coreMessages;
445
+ }
446
+ toChatMessage(content, sender) {
447
+ return {
448
+ id: this.idGenerator.next(sender),
449
+ sender,
450
+ content: content.trim(),
451
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
452
+ type: "text"
453
+ };
454
+ }
455
+ };
456
+ var messageFormatter = new MessageFormatter();
457
+
458
+ // src/chat/utils/prompt-strategy.ts
459
+ var DEFAULT_PROMPT_STRATEGY = {
460
+ resolve: (config) => {
461
+ if (config.systemPrompt && config.systemPrompt.length > 0) {
462
+ return config.systemPrompt;
463
+ }
464
+ return void 0;
465
+ }
466
+ };
467
+
468
+ // src/chat/services/chat.service.ts
469
+ var DEFAULT_CONFIG = {
470
+ temperature: 0.8,
471
+ maxTokens: 500
472
+ };
473
+ var ChatService = class {
474
+ constructor(engine) {
475
+ this.engine = engine;
476
+ }
477
+ engine;
478
+ config = DEFAULT_CONFIG;
479
+ promptStrategy = DEFAULT_PROMPT_STRATEGY;
480
+ storage = null;
481
+ initialize(config) {
482
+ this.config = { ...DEFAULT_CONFIG, ...config };
483
+ }
484
+ updateConfig(config) {
485
+ this.config = { ...this.config, ...config };
486
+ }
487
+ setPromptStrategy(strategy) {
488
+ this.promptStrategy = strategy;
489
+ }
490
+ setStorage(storage) {
491
+ this.storage = storage;
492
+ }
493
+ async getConversation(conversationId) {
494
+ const storage = this.requireStorage();
495
+ const messages = await storage.getMessages(conversationId);
496
+ if (messages.length === 0) return null;
497
+ const last = messages[messages.length - 1];
498
+ const first = messages[0];
499
+ const companionId = String(first.metadata?.["companionId"] ?? "unknown");
500
+ const companionName = String(first.metadata?.["companionName"] ?? "Unknown");
501
+ return {
502
+ id: conversationId,
503
+ companionId,
504
+ companionName,
505
+ lastMessage: last,
506
+ messageCount: messages.length,
507
+ updatedAt: last.timestamp
508
+ };
509
+ }
510
+ async getConversations() {
511
+ if (this.storage && "getConversations" in this.storage && typeof this.storage.getConversations === "function") {
512
+ return this.storage.getConversations();
513
+ }
514
+ return [];
515
+ }
516
+ async sendMessage(input, signal) {
517
+ const userMessage = {
518
+ ...messageFormatter.toChatMessage(input.text, "user"),
519
+ type: input.type ?? "text",
520
+ ...input.imageUrl !== void 0 ? { imageUrl: input.imageUrl } : {}
521
+ };
522
+ const aiResponse = await this.generateAIResponse(
523
+ input.companionId,
524
+ input.text,
525
+ [],
526
+ signal
527
+ );
528
+ return { userMessage, aiResponse };
529
+ }
530
+ async generateAIResponse(companionId, userMessage, context = [], signal) {
531
+ const result = await this.run({
532
+ ...this.buildRequest(userMessage, context, signal)
533
+ });
534
+ return {
535
+ ...messageFormatter.toChatMessage(result.text, "assistant"),
536
+ metadata: {
537
+ companionId,
538
+ model: result.model,
539
+ usage: {
540
+ input_tokens: result.usage?.inputTokens,
541
+ output_tokens: result.usage?.outputTokens,
542
+ total_tokens: result.usage?.totalTokens
543
+ },
544
+ finishReason: result.finishReason
545
+ }
546
+ };
547
+ }
548
+ async generateStreamingResponse(userMessage, context, onChunk, signal) {
549
+ const request = this.buildRequest(userMessage, context, signal);
550
+ const result = await this.runStream(request, { onChunk });
551
+ return result.text;
552
+ }
553
+ buildRequest(userMessage, context, signal) {
554
+ const messages = messageFormatter.toCoreMessages(context);
555
+ messages.push({ role: "user", content: userMessage });
556
+ return {
557
+ messages,
558
+ systemPrompt: this.resolveSystemPrompt(),
559
+ model: this.config.model,
560
+ tier: this.config.tier,
561
+ temperature: this.config.temperature,
562
+ maxTokens: this.config.maxTokens,
563
+ ...signal !== void 0 ? { signal } : {}
564
+ };
565
+ }
566
+ resolveSystemPrompt() {
567
+ const prompt = this.promptStrategy.resolve(this.config);
568
+ return prompt && prompt.length > 0 ? prompt : void 0;
569
+ }
570
+ async run(request) {
571
+ if (this.engine instanceof ProviderFactory) {
572
+ return this.engine.chat(request);
573
+ }
574
+ return this.engine.generate(request);
575
+ }
576
+ async runStream(request, callbacks) {
577
+ if (this.engine instanceof ProviderFactory) {
578
+ return this.engine.chatStream(request, callbacks);
579
+ }
580
+ return this.engine.stream(request, callbacks);
581
+ }
582
+ requireStorage() {
583
+ if (!this.storage) {
584
+ throw new AIError(
585
+ "MISSING_CONFIG" /* MISSING_CONFIG */,
586
+ "ChatService.getConversation requires setStorage() to be called first."
587
+ );
588
+ }
589
+ return this.storage;
590
+ }
591
+ };
592
+ function extractTokenUsage(message) {
593
+ const meta = message.metadata;
594
+ if (!meta) return null;
595
+ const usage = meta["usage"];
596
+ if (!usage || typeof usage !== "object") return null;
597
+ const u = usage;
598
+ const inputTokens = readNumber(u, ["input_tokens", "prompt_tokens"]);
599
+ const outputTokens = readNumber(u, ["output_tokens", "completion_tokens"]);
600
+ const totalTokens = readNumber(u, ["total_tokens"]);
601
+ if (inputTokens === null || outputTokens === null || totalTokens === null) {
602
+ return null;
603
+ }
604
+ return {
605
+ promptTokens: inputTokens,
606
+ completionTokens: outputTokens,
607
+ totalTokens
608
+ };
609
+ }
610
+ function readNumber(source, keys) {
611
+ for (const key of keys) {
612
+ const value = source[key];
613
+ if (typeof value === "number") return value;
614
+ }
615
+ return null;
616
+ }
617
+
618
+ exports.ChatIdGenerator = ChatIdGenerator;
619
+ exports.ChatService = ChatService;
620
+ exports.DEFAULT_PROMPT_STRATEGY = DEFAULT_PROMPT_STRATEGY;
621
+ exports.chatIdGenerator = chatIdGenerator;
622
+ exports.extractTokenUsage = extractTokenUsage;
623
+ exports.messageFormatter = messageFormatter;
624
+ //# sourceMappingURL=index.cjs.map
625
+ //# sourceMappingURL=index.cjs.map