@umituz/web-ai 1.1.0 → 1.2.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 (48) hide show
  1. package/README.md +29 -3
  2. package/dist/{cancellation-error-_39nYWwz.d.cts → cancellation-error-BWb59Wkj.d.cts} +1 -1
  3. package/dist/{cancellation-error-hYcE9AS9.d.ts → cancellation-error-D9LETLCD.d.ts} +1 -1
  4. package/dist/chat/index.cjs +1 -0
  5. package/dist/chat/index.cjs.map +1 -1
  6. package/dist/chat/index.js +1 -0
  7. package/dist/chat/index.js.map +1 -1
  8. package/dist/content/index.cjs +622 -8
  9. package/dist/content/index.cjs.map +1 -1
  10. package/dist/content/index.d.cts +2 -2
  11. package/dist/content/index.d.ts +2 -2
  12. package/dist/content/index.js +622 -8
  13. package/dist/content/index.js.map +1 -1
  14. package/dist/context-manager-CuZz0vnC.d.cts +104 -0
  15. package/dist/context-manager-CuZz0vnC.d.ts +104 -0
  16. package/dist/core/index.cjs +147 -10
  17. package/dist/core/index.cjs.map +1 -1
  18. package/dist/core/index.d.cts +91 -73
  19. package/dist/core/index.d.ts +91 -73
  20. package/dist/core/index.js +141 -11
  21. package/dist/core/index.js.map +1 -1
  22. package/dist/{create-ai-D-oEWzf9.d.ts → create-ai-Cq3HkJxd.d.ts} +61 -1
  23. package/dist/{create-ai-DRysRImc.d.cts → create-ai-DDyTVzef.d.cts} +61 -1
  24. package/dist/hooks/index.cjs +747 -15
  25. package/dist/hooks/index.cjs.map +1 -1
  26. package/dist/hooks/index.d.cts +57 -4
  27. package/dist/hooks/index.d.ts +57 -4
  28. package/dist/hooks/index.js +746 -16
  29. package/dist/hooks/index.js.map +1 -1
  30. package/dist/index.cjs +1180 -242
  31. package/dist/index.cjs.map +1 -1
  32. package/dist/index.d.cts +7 -6
  33. package/dist/index.d.ts +7 -6
  34. package/dist/index.js +1140 -243
  35. package/dist/index.js.map +1 -1
  36. package/dist/providers/index.cjs +1212 -362
  37. package/dist/providers/index.cjs.map +1 -1
  38. package/dist/providers/index.d.cts +422 -10
  39. package/dist/providers/index.d.ts +422 -10
  40. package/dist/providers/index.js +1179 -363
  41. package/dist/providers/index.js.map +1 -1
  42. package/dist/remote-chat-client-LrNlew4n.d.cts +70 -0
  43. package/dist/remote-chat-client-LrNlew4n.d.ts +70 -0
  44. package/dist/{requestQueue-D89wx91Q.d.cts → requestQueue--h2uZ-Q3.d.cts} +1 -1
  45. package/dist/{requestQueue-AP6n_bRH.d.ts → requestQueue-BGeCy4Fd.d.ts} +1 -1
  46. package/package.json +1 -1
  47. package/dist/tool-call-accumulator-C7KhaXZA.d.cts +0 -69
  48. package/dist/tool-call-accumulator-C7KhaXZA.d.ts +0 -69
@@ -1110,7 +1110,7 @@ var AnthropicProvider = class {
1110
1110
  );
1111
1111
  }
1112
1112
  const structuredPrompt = new StructuredPromptBuilder(
1113
- messages[lastUserIndex].content
1113
+ messages[lastUserIndex]?.content ?? ""
1114
1114
  ).withSchema(request.schema).build();
1115
1115
  messages[lastUserIndex] = { role: "user", content: structuredPrompt };
1116
1116
  const wireRequest = new AnthropicRequestBuilder(model).withRequest({ ...request, messages }).withEffort(this.config.effort).build();
@@ -1190,7 +1190,8 @@ function mapAnthropicUsage(usage) {
1190
1190
  }
1191
1191
  function findLastIndex(items, predicate) {
1192
1192
  for (let i = items.length - 1; i >= 0; i--) {
1193
- if (predicate(items[i])) return i;
1193
+ const item = items[i];
1194
+ if (item !== void 0 && predicate(item)) return i;
1194
1195
  }
1195
1196
  return -1;
1196
1197
  }
@@ -1513,6 +1514,26 @@ function validateApiKey(apiKey, rule = DEFAULT_API_KEY_RULE) {
1513
1514
  return { valid: true };
1514
1515
  }
1515
1516
 
1517
+ // src/core/utils/failover.ts
1518
+ var TRANSIENT_ERROR_CODES = /* @__PURE__ */ new Set([
1519
+ "capacity",
1520
+ "rate_limited",
1521
+ "timeout",
1522
+ "network"
1523
+ ]);
1524
+ function isTransientError(err) {
1525
+ if (!err || typeof err !== "object") return false;
1526
+ const candidate = err;
1527
+ if (candidate.code && TRANSIENT_ERROR_CODES.has(candidate.code)) return true;
1528
+ if (candidate.type && TRANSIENT_ERROR_CODES.has(candidate.type)) return true;
1529
+ if (candidate.status === 429 || candidate.status === 503 || candidate.status === 504) return true;
1530
+ return false;
1531
+ }
1532
+ function canFailover(err, streamedChars) {
1533
+ if (!isTransientError(err)) return false;
1534
+ return streamedChars === 0;
1535
+ }
1536
+
1516
1537
  // src/providers/groq/groq.constants.ts
1517
1538
  var GROQ_MODELS = {
1518
1539
  LLAMA_3_1_8B_INSTANT: "llama-3.1-8b-instant",
@@ -2066,7 +2087,7 @@ var GroqProvider = class {
2066
2087
  );
2067
2088
  }
2068
2089
  const structuredPrompt = new StructuredPromptBuilder(
2069
- messages[lastUserIndex].content
2090
+ messages[lastUserIndex]?.content ?? ""
2070
2091
  ).withSchema(request.schema).build();
2071
2092
  messages[lastUserIndex] = { role: "user", content: structuredPrompt };
2072
2093
  const wireRequest = GroqChatRequestBuilder.for(model).withMessages(messages).withConfig(this.generationConfig(request, STRUCTURED_TEMPERATURE)).withJsonResponse(true).build();
@@ -2163,7 +2184,8 @@ function createGroqProvider(config) {
2163
2184
  }
2164
2185
  function findLastIndex2(items, predicate) {
2165
2186
  for (let i = items.length - 1; i >= 0; i--) {
2166
- if (predicate(items[i])) return i;
2187
+ const item = items[i];
2188
+ if (item !== void 0 && predicate(item)) return i;
2167
2189
  }
2168
2190
  return -1;
2169
2191
  }
@@ -2313,7 +2335,7 @@ var GeminiProvider = class extends BaseProvider {
2313
2335
  }
2314
2336
  messages[lastUserIndex] = {
2315
2337
  role: "user",
2316
- content: new StructuredPromptBuilder(messages[lastUserIndex].content).withSchema(request.schema).build()
2338
+ content: new StructuredPromptBuilder(messages[lastUserIndex]?.content ?? "").withSchema(request.schema).build()
2317
2339
  };
2318
2340
  const result = await this.generate({ ...request, messages, temperature: 0.1 });
2319
2341
  return { value: this.jsonParser.parse(result.text), result };
@@ -2419,7 +2441,8 @@ function mapGeminiFinishReason(reason) {
2419
2441
  }
2420
2442
  function findLastIndex3(items, predicate) {
2421
2443
  for (let i = items.length - 1; i >= 0; i--) {
2422
- if (predicate(items[i])) return i;
2444
+ const item = items[i];
2445
+ if (item !== void 0 && predicate(item)) return i;
2423
2446
  }
2424
2447
  return -1;
2425
2448
  }
@@ -3515,190 +3538,748 @@ function createPrunaProvider(config) {
3515
3538
  return new PrunaProvider(config);
3516
3539
  }
3517
3540
 
3518
- // src/providers/factory/create-ai.ts
3519
- function createAI(config = {}) {
3520
- const factory = new ProviderFactory({
3521
- priority: config.priority,
3522
- fallbackEnabled: config.fallbackEnabled,
3523
- defaultTier: config.defaultTier
3524
- });
3525
- if (config.anthropic?.apiKey) {
3526
- factory.register(new AnthropicProvider(config.anthropic));
3541
+ // src/core/sse/tool-call-accumulator.ts
3542
+ var ToolCallAccumulator = class {
3543
+ slots = /* @__PURE__ */ new Map();
3544
+ push(delta) {
3545
+ const index = delta.index ?? 0;
3546
+ const slot = this.slots.get(index) ?? { id: "", name: "", args: "" };
3547
+ if (delta.id) slot.id = delta.id;
3548
+ if (delta.function?.name) slot.name = delta.function.name;
3549
+ if (delta.function?.arguments) slot.args += delta.function.arguments;
3550
+ this.slots.set(index, slot);
3527
3551
  }
3528
- if (config.groq?.apiKey) {
3529
- factory.register(new GroqProvider(config.groq));
3552
+ finish() {
3553
+ return Array.from(this.slots.entries()).sort(([a], [b]) => a - b).map(([, slot]) => ({
3554
+ id: slot.id,
3555
+ name: slot.name,
3556
+ arguments: slot.args
3557
+ })).filter((call) => call.id.length > 0 || call.name.length > 0);
3530
3558
  }
3531
- if (config.gemini?.apiKey) {
3532
- factory.register(new GeminiProvider({ ...config.gemini, enabled: true }));
3559
+ };
3560
+
3561
+ // src/providers/adapters/openai-compatible.adapter.ts
3562
+ function toWireMessage(message) {
3563
+ if (message.role === "tool") {
3564
+ return { role: "tool", content: message.content, tool_call_id: message.toolCallId ?? "" };
3533
3565
  }
3534
- if (config.fal?.apiKey) {
3535
- factory.register(new FalProvider({ ...config.fal, enabled: true }));
3566
+ if (message.role === "assistant" && message.toolCalls && message.toolCalls.length > 0) {
3567
+ return {
3568
+ role: "assistant",
3569
+ content: message.content,
3570
+ tool_calls: message.toolCalls.map((call) => ({
3571
+ id: call.id,
3572
+ type: "function",
3573
+ function: { name: call.name, arguments: call.arguments }
3574
+ }))
3575
+ };
3536
3576
  }
3537
- if (config.pruna?.apiKey) {
3538
- factory.register(new PrunaProvider({ ...config.pruna, enabled: true }));
3577
+ if (message.role === "user") return { role: "user", content: message.content };
3578
+ return { role: message.role, content: message.content };
3579
+ }
3580
+ function mergeImages(messages, images) {
3581
+ if (images.length === 0) return [...messages];
3582
+ const parts = images.map((image) => ({
3583
+ type: "image_url",
3584
+ image_url: { url: `data:${image.mimeType};base64,${image.dataBase64}` }
3585
+ }));
3586
+ for (let index = messages.length - 1; index >= 0; index -= 1) {
3587
+ const message = messages[index];
3588
+ if (!message || message.role !== "user") continue;
3589
+ const textParts = typeof message.content === "string" ? message.content.length > 0 ? [{ type: "text", text: message.content }] : [] : message.content;
3590
+ return [
3591
+ ...messages.slice(0, index),
3592
+ { role: "user", content: [...textParts, ...parts] },
3593
+ ...messages.slice(index + 1)
3594
+ ];
3539
3595
  }
3540
- return factory;
3596
+ return [...messages, { role: "user", content: [...parts] }];
3541
3597
  }
3542
-
3543
- // src/providers/groq/groq-messages.ts
3544
- var createUserMessage = (content) => ({
3545
- role: "user",
3546
- content
3547
- });
3548
- var createAssistantMessage = (content) => ({
3549
- role: "assistant",
3550
- content
3551
- });
3552
- var createSystemMessage = (content) => ({
3553
- role: "system",
3554
- content
3555
- });
3556
- var createMessage = (role, content) => ({
3557
- role,
3558
- content
3559
- });
3560
- var promptToMessages = (prompt) => [
3561
- createUserMessage(prompt)
3562
- ];
3563
- var extractTextFromMessages = (messages) => messages.map((m) => m.content).join("\n\n");
3564
- var formatMessagesForDisplay = (messages) => messages.map((m) => `[${m.role.toUpperCase()}]: ${m.content}`).join("\n\n---\n\n");
3565
-
3566
- // src/providers/groq/groq-structured-json.ts
3567
- async function requestStructuredJson(client, request) {
3568
- const chatRequest = {
3569
- model: request.model,
3570
- messages: [createUserMessage(request.instructions)],
3571
- temperature: request.temperature,
3572
- max_tokens: request.maxTokens,
3573
- response_format: { type: "json_object" },
3574
- ...request.reasoningEffort !== void 0 ? { reasoning_effort: request.reasoningEffort } : {}
3575
- };
3576
- const response = await client.postChatCompletion(chatRequest);
3577
- const content = response.choices[0]?.message?.content ?? "";
3578
- const candidate = extractJsonCandidate(content);
3598
+ async function extractErrorDetail(response) {
3579
3599
  try {
3580
- return JSON.parse(candidate);
3581
- } catch (err) {
3582
- throw new GroqError(
3583
- "UNKNOWN_ERROR" /* UNKNOWN_ERROR */,
3584
- "model response contained no parsable json object",
3585
- err instanceof Error ? err : void 0
3586
- );
3600
+ const body = await response.json();
3601
+ return body.error;
3602
+ } catch {
3603
+ return void 0;
3587
3604
  }
3588
3605
  }
3589
-
3590
- // src/providers/groq/text-generation.service.ts
3591
- var STRUCTURED_OUTPUT_TEMPERATURE = 0.1;
3592
- var TextGenerationService = class {
3593
- constructor(httpClient, cache = new CacheManager(), defaultModel = DEFAULT_MODELS.TEXT) {
3594
- this.httpClient = httpClient;
3595
- this.cache = cache;
3596
- this.defaultModel = defaultModel;
3597
- }
3598
- httpClient;
3599
- cache;
3600
- defaultModel;
3601
- id = "groq-text-generation";
3602
- name = "Groq Text Generation";
3603
- capabilities = ["text"];
3604
- jsonParser = new JsonResponseParser();
3605
- generateCompletion(prompt, options = {}) {
3606
- const model = options.model ?? this.defaultModel;
3607
- return this.executeWithCache(
3608
- {
3609
- type: CACHE_KEY_TYPE_COMPLETION,
3610
- model,
3611
- prompt,
3612
- config: options.generationConfig
3613
- },
3614
- async () => {
3615
- const request = GroqChatRequestBuilder.for(model).withMessages([createUserMessage(prompt)]).withConfig(options.generationConfig).build();
3616
- const response = await this.httpClient.postChatCompletion(request);
3617
- return this.extractContent(response.choices[0]?.message?.content);
3618
- }
3619
- );
3620
- }
3621
- generateChatCompletion(messages, options = {}) {
3622
- const model = options.model ?? this.defaultModel;
3623
- return this.executeWithCache(
3624
- {
3625
- type: CACHE_KEY_TYPE_CHAT,
3626
- model,
3627
- messages: [...messages],
3628
- config: options.generationConfig
3629
- },
3630
- async () => {
3631
- const request = GroqChatRequestBuilder.for(model).withMessages(messages).withConfig(options.generationConfig).build();
3632
- const response = await this.httpClient.postChatCompletion(request);
3633
- return this.extractContent(response.choices[0]?.message?.content);
3634
- }
3635
- );
3636
- }
3637
- generateStructured(promptOrRequest, options) {
3638
- if (typeof promptOrRequest === "string") {
3639
- const model2 = options?.model ?? this.defaultModel;
3640
- const jsonPrompt = StructuredPromptBuilder.for(promptOrRequest).withSchema(options?.schema).build();
3641
- const request = GroqChatRequestBuilder.for(model2).withMessages([createUserMessage(jsonPrompt)]).withConfig({
3642
- ...options?.generationConfig,
3643
- temperature: STRUCTURED_OUTPUT_TEMPERATURE
3644
- }).build();
3645
- return this.httpClient.postChatCompletion(request).then((response) => {
3646
- const content = this.extractContent(response.choices[0]?.message?.content);
3647
- return this.jsonParser.parse(content);
3648
- });
3606
+ async function* parseSseEvent(rawEvent, toolCallAccumulator) {
3607
+ for (const line of rawEvent.split("\n")) {
3608
+ if (!line.startsWith("data:")) continue;
3609
+ const payload = line.slice(5).trim();
3610
+ if (payload.length === 0 || payload === "[DONE]") continue;
3611
+ let chunk;
3612
+ try {
3613
+ chunk = JSON.parse(payload);
3614
+ } catch {
3615
+ continue;
3649
3616
  }
3650
- const req = promptOrRequest;
3651
- const model = req.model ?? (req.tier ? this.resolveModel(req.tier) : this.defaultModel);
3652
- const schemaObj = req.schema;
3653
- const messages = [];
3654
- if (req.systemPrompt) {
3655
- messages.push({ role: "system", content: req.systemPrompt });
3617
+ const delta = chunk.choices?.[0]?.delta;
3618
+ if (delta?.reasoning) {
3619
+ yield { type: "delta", channel: "reasoning", text: delta.reasoning };
3656
3620
  }
3657
- for (const m of req.messages) {
3658
- messages.push({ role: m.role, content: m.content });
3621
+ if (delta?.content) {
3622
+ yield { type: "delta", channel: "answer", text: delta.content };
3659
3623
  }
3660
- const lastMsg = messages[messages.length - 1];
3661
- if (lastMsg) {
3662
- const structuredPrompt = StructuredPromptBuilder.for(lastMsg.content).withSchema(schemaObj).build();
3663
- messages[messages.length - 1] = { ...lastMsg, content: structuredPrompt };
3624
+ if (delta?.tool_calls) {
3625
+ for (const toolCallDelta of delta.tool_calls) toolCallAccumulator.push(toolCallDelta);
3664
3626
  }
3665
- const groqReq = GroqChatRequestBuilder.for(model).withMessages(messages).withConfig({
3666
- temperature: req.temperature ?? STRUCTURED_OUTPUT_TEMPERATURE,
3667
- maxTokens: req.maxTokens
3668
- }).build();
3669
- return this.httpClient.postChatCompletion(groqReq).then((response) => {
3670
- const content = this.extractContent(response.choices[0]?.message?.content);
3671
- const parsed = this.jsonParser.parse(content);
3672
- const genResult = {
3673
- text: content,
3674
- model: response.model,
3675
- providerId: this.id,
3676
- finishReason: response.choices[0]?.finish_reason ?? "stop",
3677
- usage: response.usage ? {
3678
- inputTokens: response.usage.prompt_tokens,
3679
- outputTokens: response.usage.completion_tokens,
3680
- totalTokens: response.usage.total_tokens
3681
- } : void 0
3627
+ if (chunk.usage) {
3628
+ yield {
3629
+ type: "usage",
3630
+ usage: {
3631
+ inputTokens: chunk.usage.prompt_tokens ?? 0,
3632
+ outputTokens: chunk.usage.completion_tokens ?? 0,
3633
+ reasoningTokens: chunk.usage.completion_tokens_details?.reasoning_tokens
3634
+ }
3682
3635
  };
3683
- return { value: parsed, result: genResult };
3684
- });
3685
- }
3686
- streamCompletion(prompt, callbacks, options = {}) {
3687
- return this.streamChatCompletion([createUserMessage(prompt)], callbacks, options);
3636
+ }
3688
3637
  }
3689
- async streamChatCompletion(messages, callbacks, options = {}) {
3690
- const model = options.model ?? this.defaultModel;
3691
- const request = GroqChatRequestBuilder.for(model).withMessages(messages).withConfig(options.generationConfig).withStream(true).build();
3692
- const parser = new StreamingResponseParser(
3693
- {
3694
- onChunk: (chunk) => callbacks.onChunk?.(chunk)
3695
- },
3696
- extractContentFromSsePayload
3697
- );
3698
- try {
3699
- const stream = await this.httpClient.streamChatCompletion(request, {
3700
- signal: options.signal
3701
- });
3638
+ }
3639
+ async function* parseSseStream(body) {
3640
+ const reader = body.getReader();
3641
+ const decoder = new TextDecoder();
3642
+ const toolCallAccumulator = new ToolCallAccumulator();
3643
+ let buffer = "";
3644
+ try {
3645
+ while (true) {
3646
+ const { done, value } = await reader.read();
3647
+ if (done) break;
3648
+ buffer += decoder.decode(value, { stream: true });
3649
+ let separator = buffer.indexOf("\n\n");
3650
+ while (separator !== -1) {
3651
+ const rawEvent = buffer.slice(0, separator);
3652
+ buffer = buffer.slice(separator + 2);
3653
+ separator = buffer.indexOf("\n\n");
3654
+ yield* parseSseEvent(rawEvent, toolCallAccumulator);
3655
+ }
3656
+ }
3657
+ } catch (err) {
3658
+ throw errorFromUnknown(err);
3659
+ } finally {
3660
+ reader.releaseLock();
3661
+ }
3662
+ const calls = toolCallAccumulator.finish();
3663
+ if (calls.length > 0) yield { type: "tool_calls", calls };
3664
+ }
3665
+ function createOpenAiCompatibleAdapter(config) {
3666
+ if (!config.baseUrl && !config.endpointUrl) {
3667
+ throw new Error("createOpenAiCompatibleAdapter requires baseUrl or endpointUrl");
3668
+ }
3669
+ const endpoint = config.endpointUrl ?? `${config.baseUrl}/chat/completions`;
3670
+ return {
3671
+ async *stream(req) {
3672
+ const timeout = AbortSignal.timeout(config.timeoutMs);
3673
+ const signal = req.signal ? AbortSignal.any([req.signal, timeout]) : timeout;
3674
+ let response;
3675
+ try {
3676
+ response = await fetch(endpoint, {
3677
+ method: "POST",
3678
+ headers: {
3679
+ Authorization: `Bearer ${config.apiKey}`,
3680
+ "Content-Type": "application/json"
3681
+ },
3682
+ body: JSON.stringify({
3683
+ model: req.externalModelId,
3684
+ messages: mergeImages(req.messages.map(toWireMessage), req.images ?? []),
3685
+ temperature: req.temperature,
3686
+ max_completion_tokens: req.maxOutputTokens,
3687
+ top_p: req.topP,
3688
+ stream: true,
3689
+ stream_options: { include_usage: true },
3690
+ ...req.tools && req.tools.length > 0 ? {
3691
+ tools: req.tools.map((tool) => ({ type: "function", function: tool })),
3692
+ tool_choice: "auto"
3693
+ } : {}
3694
+ }),
3695
+ signal
3696
+ });
3697
+ } catch (err) {
3698
+ throw errorFromUnknown(err);
3699
+ }
3700
+ if (!response.ok || !response.body) {
3701
+ const detail = await extractErrorDetail(response);
3702
+ throw errorFromStatus(response.status, detail?.message, detail);
3703
+ }
3704
+ yield* parseSseStream(response.body);
3705
+ }
3706
+ };
3707
+ }
3708
+
3709
+ // src/providers/openai-compatible/openai-compatible.provider.ts
3710
+ var DEFAULT_TIMEOUT_MS = 3e4;
3711
+ var STRUCTURED_TEMPERATURE2 = 0.1;
3712
+ var OpenAiCompatibleProvider = class {
3713
+ constructor(config) {
3714
+ this.config = config;
3715
+ if (!config.baseUrl && !config.endpointUrl) {
3716
+ throw new AIError(
3717
+ "MISSING_CONFIG" /* MISSING_CONFIG */,
3718
+ "OpenAiCompatibleProvider requires baseUrl or endpointUrl"
3719
+ );
3720
+ }
3721
+ this.adapter = createOpenAiCompatibleAdapter({
3722
+ baseUrl: config.baseUrl,
3723
+ endpointUrl: config.endpointUrl,
3724
+ apiKey: config.apiKey,
3725
+ timeoutMs: config.timeoutMs ?? DEFAULT_TIMEOUT_MS
3726
+ });
3727
+ this.tiers = mergeTierOverrides(
3728
+ config.model ? { fast: config.model, balanced: config.model, powerful: config.model } : {},
3729
+ config.models
3730
+ );
3731
+ }
3732
+ config;
3733
+ id = "openai-compatible";
3734
+ name = "OpenAI-compatible";
3735
+ capabilities = ["text"];
3736
+ adapter;
3737
+ tiers;
3738
+ jsonParser = new JsonResponseParser();
3739
+ resolveModel(tier) {
3740
+ return resolveModel(this.tiers, tier, this.id);
3741
+ }
3742
+ async healthCheck() {
3743
+ return this.config.apiKey && (this.config.baseUrl || this.config.endpointUrl) ? "healthy" : "unavailable";
3744
+ }
3745
+ async estimateCost() {
3746
+ return 0;
3747
+ }
3748
+ async generate(request) {
3749
+ const model = this.modelFor(request);
3750
+ const outcome = await this.consume(this.toAdapterRequest(model, request));
3751
+ if (outcome.text.length === 0) {
3752
+ throw this.noContentError(model);
3753
+ }
3754
+ return {
3755
+ text: outcome.text,
3756
+ usage: outcome.usage,
3757
+ model,
3758
+ providerId: this.id,
3759
+ finishReason: "stop"
3760
+ };
3761
+ }
3762
+ async generateStructured(request) {
3763
+ const model = this.modelFor(request);
3764
+ const messages = this.toWireMessages(request.messages, request.systemPrompt);
3765
+ const lastUserIndex = findLastIndex4(messages, (m) => m.role === "user");
3766
+ if (lastUserIndex === -1) {
3767
+ throw new AIError(
3768
+ "UNKNOWN_ERROR" /* UNKNOWN_ERROR */,
3769
+ "generateStructured requires at least one user message"
3770
+ );
3771
+ }
3772
+ const structuredPrompt = new StructuredPromptBuilder(
3773
+ messages[lastUserIndex]?.content ?? ""
3774
+ ).withSchema(request.schema).build();
3775
+ messages[lastUserIndex] = { role: "user", content: structuredPrompt };
3776
+ const outcome = await this.consume({
3777
+ externalModelId: model,
3778
+ messages,
3779
+ temperature: STRUCTURED_TEMPERATURE2,
3780
+ signal: request.signal
3781
+ });
3782
+ if (outcome.text.length === 0) {
3783
+ throw this.noContentError(model);
3784
+ }
3785
+ return {
3786
+ value: this.jsonParser.parse(outcome.text),
3787
+ result: {
3788
+ text: outcome.text,
3789
+ usage: outcome.usage,
3790
+ model,
3791
+ providerId: this.id,
3792
+ finishReason: "stop"
3793
+ }
3794
+ };
3795
+ }
3796
+ async stream(request, callbacks) {
3797
+ const model = this.modelFor(request);
3798
+ try {
3799
+ const outcome = await this.consume(this.toAdapterRequest(model, request), (text) => {
3800
+ callbacks.onChunk?.(text);
3801
+ });
3802
+ if (outcome.text.length === 0) {
3803
+ throw this.noContentError(model);
3804
+ }
3805
+ const result = {
3806
+ text: outcome.text,
3807
+ usage: outcome.usage,
3808
+ model,
3809
+ providerId: this.id,
3810
+ finishReason: "stop"
3811
+ };
3812
+ callbacks.onComplete?.(result);
3813
+ return result;
3814
+ } catch (error) {
3815
+ callbacks.onError?.(error instanceof Error ? error : new Error(String(error)));
3816
+ throw error;
3817
+ }
3818
+ }
3819
+ /* ---------- internals ---------- */
3820
+ /** Drain one adapter stream to its end. `onChunk`, when given, sees answer
3821
+ * deltas as they arrive — the streaming surface is this same drain. */
3822
+ async consume(req, onChunk) {
3823
+ let text = "";
3824
+ let usage;
3825
+ for await (const event of this.adapter.stream(req)) {
3826
+ if (event.type === "delta" && event.channel === "answer") {
3827
+ text += event.text;
3828
+ onChunk?.(event.text);
3829
+ } else if (event.type === "usage") {
3830
+ usage = event.usage;
3831
+ }
3832
+ }
3833
+ return { text, usage };
3834
+ }
3835
+ modelFor(request) {
3836
+ return request.model ?? this.resolveModel(request.tier ?? "balanced");
3837
+ }
3838
+ noContentError(model) {
3839
+ return new AIError(
3840
+ "SERVER_ERROR" /* SERVER_ERROR */,
3841
+ "No content generated from the OpenAI-compatible endpoint",
3842
+ void 0,
3843
+ { model }
3844
+ );
3845
+ }
3846
+ toAdapterRequest(model, request) {
3847
+ return {
3848
+ externalModelId: model,
3849
+ messages: this.toWireMessages(request.messages, request.systemPrompt),
3850
+ ...request.temperature !== void 0 ? { temperature: request.temperature } : {},
3851
+ ...request.maxTokens !== void 0 ? { maxOutputTokens: request.maxTokens } : {},
3852
+ ...request.topP !== void 0 ? { topP: request.topP } : {},
3853
+ ...request.signal ? { signal: request.signal } : {}
3854
+ };
3855
+ }
3856
+ /** Neutral messages onto the adapter's shape; systemPrompt becomes the
3857
+ * leading system message (in-band, the OpenAI way). */
3858
+ toWireMessages(messages, systemPrompt) {
3859
+ const wire = [];
3860
+ if (systemPrompt && systemPrompt.length > 0) {
3861
+ wire.push({ role: "system", content: systemPrompt });
3862
+ }
3863
+ for (const message of messages) {
3864
+ wire.push({ role: message.role, content: message.content });
3865
+ }
3866
+ return wire;
3867
+ }
3868
+ };
3869
+ function createOpenAiCompatibleProvider(config) {
3870
+ return new OpenAiCompatibleProvider(config);
3871
+ }
3872
+ function findLastIndex4(items, predicate) {
3873
+ for (let index = items.length - 1; index >= 0; index -= 1) {
3874
+ if (predicate(items[index])) return index;
3875
+ }
3876
+ return -1;
3877
+ }
3878
+
3879
+ // src/providers/workers-ai/workers-ai.entities.ts
3880
+ var WorkersAIError = class extends Error {
3881
+ status;
3882
+ constructor(message, status) {
3883
+ super(message);
3884
+ this.name = "WorkersAIError";
3885
+ this.status = status;
3886
+ }
3887
+ };
3888
+
3889
+ // src/providers/workers-ai/workers-ai.text.ts
3890
+ var WORKERS_AI_LLM_MODEL = "@cf/meta/llama-3.3-70b-instruct-fp8-fast";
3891
+ var WORKERS_AI_TEXT_TIMEOUT_MS = 25e3;
3892
+ function workersAIConfigured(env) {
3893
+ return typeof env.AI?.run === "function";
3894
+ }
3895
+ function requireAi(env) {
3896
+ const ai = env.AI;
3897
+ if (!ai || typeof ai.run !== "function") throw new WorkersAIError("Workers AI is not configured");
3898
+ return ai;
3899
+ }
3900
+ async function runWorkersAIText(env, messages, opts = {}) {
3901
+ const ai = requireAi(env);
3902
+ const model = opts.model ?? WORKERS_AI_LLM_MODEL;
3903
+ let result;
3904
+ try {
3905
+ result = await Promise.race([
3906
+ ai.run(model, {
3907
+ messages: messages.map((m) => ({ role: m.role, content: m.content })),
3908
+ max_tokens: opts.maxTokens ?? 900,
3909
+ temperature: opts.temperature ?? 0.7
3910
+ }),
3911
+ new Promise(
3912
+ (_, reject) => setTimeout(() => reject(new WorkersAIError("Workers AI timed out")), WORKERS_AI_TEXT_TIMEOUT_MS)
3913
+ )
3914
+ ]);
3915
+ } catch (err) {
3916
+ throw err instanceof WorkersAIError ? err : new WorkersAIError(err instanceof Error ? err.message : "Workers AI request failed");
3917
+ }
3918
+ const response = result ?? {};
3919
+ let text = response.response;
3920
+ if (typeof text !== "string" || text.length === 0) {
3921
+ text = response.choices?.[0]?.message?.content;
3922
+ }
3923
+ if (typeof text !== "string" || text.length === 0) {
3924
+ throw new WorkersAIError("Workers AI returned no text");
3925
+ }
3926
+ return text;
3927
+ }
3928
+ async function* streamWorkersAIText(env, messages, opts = {}) {
3929
+ const ai = requireAi(env);
3930
+ const model = opts.model ?? WORKERS_AI_LLM_MODEL;
3931
+ let body;
3932
+ try {
3933
+ body = await Promise.race([
3934
+ ai.run(model, {
3935
+ messages: messages.map((m) => ({ role: m.role, content: m.content })),
3936
+ stream: true,
3937
+ max_tokens: opts.maxTokens ?? 600,
3938
+ temperature: opts.temperature ?? 0.7
3939
+ }),
3940
+ new Promise(
3941
+ (_, reject) => setTimeout(() => reject(new WorkersAIError("Workers AI timed out")), WORKERS_AI_TEXT_TIMEOUT_MS)
3942
+ )
3943
+ ]);
3944
+ } catch (err) {
3945
+ throw err instanceof WorkersAIError ? err : new WorkersAIError(err instanceof Error ? err.message : "Workers AI request failed");
3946
+ }
3947
+ const stream = body;
3948
+ const getReader = typeof stream === "object" && stream !== null && "getReader" in stream && typeof stream.getReader === "function" ? stream.getReader : void 0;
3949
+ if (!getReader) throw new WorkersAIError("Workers AI stream was not readable");
3950
+ const reader = getReader.call(stream);
3951
+ const decoder = new TextDecoder();
3952
+ let buffer = "";
3953
+ try {
3954
+ for (; ; ) {
3955
+ const { done, value } = await reader.read();
3956
+ if (done) break;
3957
+ buffer += decoder.decode(value, { stream: true });
3958
+ let newlineAt;
3959
+ while ((newlineAt = buffer.indexOf("\n")) !== -1) {
3960
+ let line = buffer.slice(0, newlineAt);
3961
+ buffer = buffer.slice(newlineAt + 1);
3962
+ if (line.endsWith("\r")) line = line.slice(0, -1);
3963
+ if (line.startsWith(":") || line.trim() === "") continue;
3964
+ if (!line.startsWith("data: ")) continue;
3965
+ const payload = line.slice(6).trim();
3966
+ if (payload === "[DONE]") return;
3967
+ try {
3968
+ const parsed = JSON.parse(payload);
3969
+ if (typeof parsed.response === "string" && parsed.response.length > 0) yield parsed.response;
3970
+ } catch {
3971
+ buffer = `${line}
3972
+ ${buffer}`;
3973
+ break;
3974
+ }
3975
+ }
3976
+ }
3977
+ } finally {
3978
+ reader.releaseLock();
3979
+ }
3980
+ }
3981
+
3982
+ // src/providers/workers-ai/workers-ai.provider.ts
3983
+ var WORKERS_AI_TIERS = {
3984
+ fast: "@cf/meta/llama-3.1-8b-instruct-fp8-fast",
3985
+ balanced: WORKERS_AI_LLM_MODEL,
3986
+ powerful: WORKERS_AI_LLM_MODEL
3987
+ };
3988
+ var WorkersAIProvider = class {
3989
+ id = "workers-ai";
3990
+ name = "Cloudflare Workers AI";
3991
+ capabilities = ["text"];
3992
+ binding;
3993
+ tiers;
3994
+ jsonParser = new JsonResponseParser();
3995
+ constructor(config) {
3996
+ this.binding = config.binding;
3997
+ this.tiers = mergeTierOverrides(WORKERS_AI_TIERS, config.models);
3998
+ }
3999
+ resolveModel(tier) {
4000
+ return resolveModel(this.tiers, tier, this.id);
4001
+ }
4002
+ async healthCheck() {
4003
+ return typeof this.binding?.run === "function" ? "healthy" : "unavailable";
4004
+ }
4005
+ /** Workers AI is allocation-priced (neurons per deployment), not per
4006
+ * request — there is no per-call cost to estimate. */
4007
+ async estimateCost(_request) {
4008
+ return 0;
4009
+ }
4010
+ async generate(request) {
4011
+ const model = request.model ?? this.resolveModel(request.tier ?? "balanced");
4012
+ const messages = withSystemPrompt(request);
4013
+ try {
4014
+ const text = await runWorkersAIText(
4015
+ { AI: this.binding },
4016
+ messages,
4017
+ {
4018
+ model,
4019
+ maxTokens: request.maxTokens,
4020
+ temperature: request.temperature
4021
+ }
4022
+ );
4023
+ return { text, model, providerId: this.id, finishReason: "stop" };
4024
+ } catch (error) {
4025
+ throw toAIError(error);
4026
+ }
4027
+ }
4028
+ async generateStructured(request) {
4029
+ const messages = [...request.messages];
4030
+ const lastUserIndex = findLastIndex5(messages, (m) => m.role === "user");
4031
+ if (lastUserIndex === -1) {
4032
+ throw new AIError("UNKNOWN_ERROR" /* UNKNOWN_ERROR */, "generateStructured requires at least one user message");
4033
+ }
4034
+ messages[lastUserIndex] = {
4035
+ role: "user",
4036
+ content: new StructuredPromptBuilder(messages[lastUserIndex]?.content ?? "").withSchema(request.schema).build()
4037
+ };
4038
+ const result = await this.generate({ ...request, messages, temperature: 0.1 });
4039
+ return { value: this.jsonParser.parse(result.text), result };
4040
+ }
4041
+ async stream(request, callbacks) {
4042
+ const model = request.model ?? this.resolveModel(request.tier ?? "balanced");
4043
+ const messages = withSystemPrompt(request);
4044
+ let text = "";
4045
+ try {
4046
+ for await (const chunk of streamWorkersAIText({ AI: this.binding }, messages, {
4047
+ model,
4048
+ maxTokens: request.maxTokens,
4049
+ temperature: request.temperature
4050
+ })) {
4051
+ text += chunk;
4052
+ callbacks.onChunk?.(chunk);
4053
+ }
4054
+ } catch (error) {
4055
+ const aiError = toAIError(error);
4056
+ callbacks.onError?.(aiError);
4057
+ throw aiError;
4058
+ }
4059
+ const result = { text, model, providerId: this.id, finishReason: "stop" };
4060
+ callbacks.onComplete?.(result);
4061
+ return result;
4062
+ }
4063
+ };
4064
+ function withSystemPrompt(request) {
4065
+ if (request.systemPrompt && request.systemPrompt.length > 0) {
4066
+ return [
4067
+ { role: "system", content: request.systemPrompt },
4068
+ ...request.messages.filter((m) => m.role !== "system")
4069
+ ];
4070
+ }
4071
+ return request.messages;
4072
+ }
4073
+ function toAIError(error) {
4074
+ if (error instanceof AIError) return error;
4075
+ const message = error instanceof Error ? error.message : "Workers AI request failed";
4076
+ const type = error instanceof WorkersAIError && error.message.includes("not configured") ? "INVALID_API_KEY" /* INVALID_API_KEY */ : "SERVER_ERROR" /* SERVER_ERROR */;
4077
+ return new AIError(type, message, error instanceof Error ? error : void 0);
4078
+ }
4079
+ function findLastIndex5(items, predicate) {
4080
+ for (let i = items.length - 1; i >= 0; i--) {
4081
+ const item = items[i];
4082
+ if (item !== void 0 && predicate(item)) return i;
4083
+ }
4084
+ return -1;
4085
+ }
4086
+ function createWorkersAIProvider(config) {
4087
+ return new WorkersAIProvider(config);
4088
+ }
4089
+
4090
+ // src/providers/factory/create-ai.ts
4091
+ function createAI(config = {}) {
4092
+ const factory = new ProviderFactory({
4093
+ priority: config.priority,
4094
+ fallbackEnabled: config.fallbackEnabled,
4095
+ defaultTier: config.defaultTier
4096
+ });
4097
+ if (config.anthropic?.apiKey) {
4098
+ factory.register(new AnthropicProvider(config.anthropic));
4099
+ }
4100
+ if (config.groq?.apiKey) {
4101
+ factory.register(new GroqProvider(config.groq));
4102
+ }
4103
+ if (config.gemini?.apiKey) {
4104
+ factory.register(new GeminiProvider({ ...config.gemini, enabled: true }));
4105
+ }
4106
+ if (config.fal?.apiKey) {
4107
+ factory.register(new FalProvider({ ...config.fal, enabled: true }));
4108
+ }
4109
+ if (config.pruna?.apiKey) {
4110
+ factory.register(new PrunaProvider({ ...config.pruna, enabled: true }));
4111
+ }
4112
+ const openaiCompatible = config.openaiCompatible;
4113
+ if (openaiCompatible?.apiKey && (openaiCompatible.baseUrl || openaiCompatible.endpointUrl)) {
4114
+ factory.register(
4115
+ new OpenAiCompatibleProvider({ ...openaiCompatible, apiKey: openaiCompatible.apiKey })
4116
+ );
4117
+ }
4118
+ if (config.workersAI?.binding) {
4119
+ factory.register(new WorkersAIProvider(config.workersAI));
4120
+ }
4121
+ return factory;
4122
+ }
4123
+
4124
+ // src/providers/groq/groq-messages.ts
4125
+ var createUserMessage = (content) => ({
4126
+ role: "user",
4127
+ content
4128
+ });
4129
+ var createAssistantMessage = (content) => ({
4130
+ role: "assistant",
4131
+ content
4132
+ });
4133
+ var createSystemMessage = (content) => ({
4134
+ role: "system",
4135
+ content
4136
+ });
4137
+ var createMessage = (role, content) => ({
4138
+ role,
4139
+ content
4140
+ });
4141
+ var promptToMessages = (prompt) => [
4142
+ createUserMessage(prompt)
4143
+ ];
4144
+ var extractTextFromMessages = (messages) => messages.map((m) => m.content).join("\n\n");
4145
+ var formatMessagesForDisplay = (messages) => messages.map((m) => `[${m.role.toUpperCase()}]: ${m.content}`).join("\n\n---\n\n");
4146
+
4147
+ // src/providers/groq/groq-structured-json.ts
4148
+ async function requestStructuredJson(client, request) {
4149
+ const chatRequest = {
4150
+ model: request.model,
4151
+ messages: [createUserMessage(request.instructions)],
4152
+ temperature: request.temperature,
4153
+ max_tokens: request.maxTokens,
4154
+ response_format: { type: "json_object" },
4155
+ ...request.reasoningEffort !== void 0 ? { reasoning_effort: request.reasoningEffort } : {}
4156
+ };
4157
+ const response = await client.postChatCompletion(chatRequest);
4158
+ const content = response.choices[0]?.message?.content ?? "";
4159
+ const candidate = extractJsonCandidate(content);
4160
+ try {
4161
+ return JSON.parse(candidate);
4162
+ } catch (err) {
4163
+ throw new GroqError(
4164
+ "UNKNOWN_ERROR" /* UNKNOWN_ERROR */,
4165
+ "model response contained no parsable json object",
4166
+ err instanceof Error ? err : void 0
4167
+ );
4168
+ }
4169
+ }
4170
+
4171
+ // src/providers/groq/text-generation.service.ts
4172
+ var STRUCTURED_OUTPUT_TEMPERATURE = 0.1;
4173
+ var TextGenerationService = class {
4174
+ constructor(httpClient, cache = new CacheManager(), defaultModel = DEFAULT_MODELS.TEXT) {
4175
+ this.httpClient = httpClient;
4176
+ this.cache = cache;
4177
+ this.defaultModel = defaultModel;
4178
+ }
4179
+ httpClient;
4180
+ cache;
4181
+ defaultModel;
4182
+ id = "groq-text-generation";
4183
+ name = "Groq Text Generation";
4184
+ capabilities = ["text"];
4185
+ jsonParser = new JsonResponseParser();
4186
+ generateCompletion(prompt, options = {}) {
4187
+ const model = options.model ?? this.defaultModel;
4188
+ return this.executeWithCache(
4189
+ {
4190
+ type: CACHE_KEY_TYPE_COMPLETION,
4191
+ model,
4192
+ prompt,
4193
+ config: options.generationConfig
4194
+ },
4195
+ async () => {
4196
+ const request = GroqChatRequestBuilder.for(model).withMessages([createUserMessage(prompt)]).withConfig(options.generationConfig).build();
4197
+ const response = await this.httpClient.postChatCompletion(request);
4198
+ return this.extractContent(response.choices[0]?.message?.content);
4199
+ }
4200
+ );
4201
+ }
4202
+ generateChatCompletion(messages, options = {}) {
4203
+ const model = options.model ?? this.defaultModel;
4204
+ return this.executeWithCache(
4205
+ {
4206
+ type: CACHE_KEY_TYPE_CHAT,
4207
+ model,
4208
+ messages: [...messages],
4209
+ config: options.generationConfig
4210
+ },
4211
+ async () => {
4212
+ const request = GroqChatRequestBuilder.for(model).withMessages(messages).withConfig(options.generationConfig).build();
4213
+ const response = await this.httpClient.postChatCompletion(request);
4214
+ return this.extractContent(response.choices[0]?.message?.content);
4215
+ }
4216
+ );
4217
+ }
4218
+ generateStructured(promptOrRequest, options) {
4219
+ if (typeof promptOrRequest === "string") {
4220
+ const model2 = options?.model ?? this.defaultModel;
4221
+ const jsonPrompt = StructuredPromptBuilder.for(promptOrRequest).withSchema(options?.schema).build();
4222
+ const request = GroqChatRequestBuilder.for(model2).withMessages([createUserMessage(jsonPrompt)]).withConfig({
4223
+ ...options?.generationConfig,
4224
+ temperature: STRUCTURED_OUTPUT_TEMPERATURE
4225
+ }).build();
4226
+ return this.httpClient.postChatCompletion(request).then((response) => {
4227
+ const content = this.extractContent(response.choices[0]?.message?.content);
4228
+ return this.jsonParser.parse(content);
4229
+ });
4230
+ }
4231
+ const req = promptOrRequest;
4232
+ const model = req.model ?? (req.tier ? this.resolveModel(req.tier) : this.defaultModel);
4233
+ const schemaObj = req.schema;
4234
+ const messages = [];
4235
+ if (req.systemPrompt) {
4236
+ messages.push({ role: "system", content: req.systemPrompt });
4237
+ }
4238
+ for (const m of req.messages) {
4239
+ messages.push({ role: m.role, content: m.content });
4240
+ }
4241
+ const lastMsg = messages[messages.length - 1];
4242
+ if (lastMsg) {
4243
+ const structuredPrompt = StructuredPromptBuilder.for(lastMsg.content).withSchema(schemaObj).build();
4244
+ messages[messages.length - 1] = { ...lastMsg, content: structuredPrompt };
4245
+ }
4246
+ const groqReq = GroqChatRequestBuilder.for(model).withMessages(messages).withConfig({
4247
+ temperature: req.temperature ?? STRUCTURED_OUTPUT_TEMPERATURE,
4248
+ maxTokens: req.maxTokens
4249
+ }).build();
4250
+ return this.httpClient.postChatCompletion(groqReq).then((response) => {
4251
+ const content = this.extractContent(response.choices[0]?.message?.content);
4252
+ const parsed = this.jsonParser.parse(content);
4253
+ const genResult = {
4254
+ text: content,
4255
+ model: response.model,
4256
+ providerId: this.id,
4257
+ finishReason: response.choices[0]?.finish_reason ?? "stop",
4258
+ usage: response.usage ? {
4259
+ inputTokens: response.usage.prompt_tokens,
4260
+ outputTokens: response.usage.completion_tokens,
4261
+ totalTokens: response.usage.total_tokens
4262
+ } : void 0
4263
+ };
4264
+ return { value: parsed, result: genResult };
4265
+ });
4266
+ }
4267
+ streamCompletion(prompt, callbacks, options = {}) {
4268
+ return this.streamChatCompletion([createUserMessage(prompt)], callbacks, options);
4269
+ }
4270
+ async streamChatCompletion(messages, callbacks, options = {}) {
4271
+ const model = options.model ?? this.defaultModel;
4272
+ const request = GroqChatRequestBuilder.for(model).withMessages(messages).withConfig(options.generationConfig).withStream(true).build();
4273
+ const parser = new StreamingResponseParser(
4274
+ {
4275
+ onChunk: (chunk) => callbacks.onChunk?.(chunk)
4276
+ },
4277
+ extractContentFromSsePayload
4278
+ );
4279
+ try {
4280
+ const stream = await this.httpClient.streamChatCompletion(request, {
4281
+ signal: options.signal
4282
+ });
3702
4283
  const fullText = await parser.parse(stream);
3703
4284
  callbacks.onComplete?.(fullText);
3704
4285
  return fullText;
@@ -3920,6 +4501,7 @@ var ChatService = class {
3920
4501
  if (messages.length === 0) return null;
3921
4502
  const last = messages[messages.length - 1];
3922
4503
  const first = messages[0];
4504
+ if (!last || !first) return null;
3923
4505
  const companionId = String(first.metadata?.["companionId"] ?? "unknown");
3924
4506
  const companionName = String(first.metadata?.["companionName"] ?? "Unknown");
3925
4507
  return {
@@ -4012,30 +4594,235 @@ var ChatService = class {
4012
4594
  }
4013
4595
  return this.storage;
4014
4596
  }
4015
- };
4597
+ };
4598
+
4599
+ // src/providers/groq/groq-container.ts
4600
+ var container = null;
4601
+ function configureProvider(config) {
4602
+ const configStore = new GroqConfigStore();
4603
+ const transport = new GroqHttpTransport(configStore);
4604
+ const httpClient = new GroqHttpClient({ configStore, transport });
4605
+ httpClient.initialize(config);
4606
+ const textGeneration = new TextGenerationService(
4607
+ httpClient,
4608
+ new CacheManager(),
4609
+ config.textModel
4610
+ );
4611
+ const chat = new ChatService(textGeneration);
4612
+ container = { groqHttpClient: httpClient, textGeneration, chat };
4613
+ return container;
4614
+ }
4615
+ function getProvider() {
4616
+ return container;
4617
+ }
4618
+ function resetProvider() {
4619
+ container?.groqHttpClient.reset();
4620
+ container = null;
4621
+ }
4622
+
4623
+ // src/providers/workers-ai/workers-ai.speech.ts
4624
+ var WORKERS_AI_SPEECH_TIMEOUT_MS = 45e3;
4625
+ async function readAll(stream) {
4626
+ const reader = stream.getReader();
4627
+ const chunks = [];
4628
+ try {
4629
+ for (; ; ) {
4630
+ const { done, value } = await reader.read();
4631
+ if (done) break;
4632
+ if (value) chunks.push(value);
4633
+ }
4634
+ } finally {
4635
+ reader.releaseLock();
4636
+ }
4637
+ const combined = new Uint8Array(chunks.reduce((acc, chunk) => acc + chunk.length, 0));
4638
+ let offset = 0;
4639
+ for (const chunk of chunks) {
4640
+ combined.set(chunk, offset);
4641
+ offset += chunk.length;
4642
+ }
4643
+ return combined;
4644
+ }
4645
+ function base64ToBytes2(base64) {
4646
+ const binary = atob(base64);
4647
+ const bytes = new Uint8Array(binary.length);
4648
+ for (let i = 0; i < binary.length; i += 1) bytes[i] = binary.charCodeAt(i);
4649
+ return bytes;
4650
+ }
4651
+ async function speakWithWorkersAI(env, engine, text) {
4652
+ const ai = env.AI;
4653
+ if (!ai || typeof ai.run !== "function") throw new WorkersAIError("Workers AI is not configured");
4654
+ const input = engine.langParam ? { prompt: text, lang: engine.langParam } : { text };
4655
+ let result;
4656
+ try {
4657
+ result = await Promise.race([
4658
+ ai.run(engine.model, input),
4659
+ new Promise(
4660
+ (_, reject) => setTimeout(() => reject(new WorkersAIError("The speech engine timed out")), WORKERS_AI_SPEECH_TIMEOUT_MS)
4661
+ )
4662
+ ]);
4663
+ } catch (err) {
4664
+ throw err instanceof WorkersAIError ? err : new WorkersAIError(err instanceof Error ? err.message : "Workers AI speech request failed");
4665
+ }
4666
+ if (result instanceof ReadableStream) {
4667
+ const bytes = await readAll(result);
4668
+ if (bytes.length === 0) throw new WorkersAIError("The speech engine returned no audio");
4669
+ return { bytes };
4670
+ }
4671
+ const audio = result ?? {};
4672
+ if (typeof audio.audio === "string" && audio.audio.length > 0) {
4673
+ return { bytes: base64ToBytes2(audio.audio) };
4674
+ }
4675
+ throw new WorkersAIError("The speech engine returned no audio");
4676
+ }
4677
+
4678
+ // src/providers/fal/queue/fal-queue.entities.ts
4679
+ var FalQueueError = class extends Error {
4680
+ status;
4681
+ constructor(message, status) {
4682
+ super(message);
4683
+ this.name = "FalQueueError";
4684
+ this.status = status;
4685
+ }
4686
+ };
4687
+ var FAL_ASPECT_IMAGE_SIZES = {
4688
+ "1:1": "square_hd",
4689
+ "16:9": "landscape_16_9",
4690
+ "9:16": "portrait_16_9"
4691
+ };
4692
+
4693
+ // src/providers/fal/queue/fal-queue.client.ts
4694
+ var FAL_QUEUE_BASE = "https://queue.fal.run";
4695
+ var FAL_SUBMIT_TIMEOUT_MS = 15e3;
4696
+ var FAL_POLL_TIMEOUT_MS = 1e4;
4697
+ function falQueueConfigured(env) {
4698
+ return Boolean(env.FAL_KEY);
4699
+ }
4700
+ async function falFetch(env, url, timeoutMs, body) {
4701
+ if (!falQueueConfigured(env)) throw new FalQueueError("fal.ai is not configured");
4702
+ const request = async () => fetch(url, {
4703
+ method: body !== void 0 ? "POST" : "GET",
4704
+ headers: {
4705
+ Authorization: `Key ${env.FAL_KEY}`,
4706
+ ...body !== void 0 ? { "Content-Type": "application/json" } : {}
4707
+ },
4708
+ body: body !== void 0 ? JSON.stringify(body) : void 0,
4709
+ signal: AbortSignal.timeout(timeoutMs)
4710
+ });
4711
+ let res;
4712
+ try {
4713
+ res = await request();
4714
+ if (res.status >= 500) res = await request();
4715
+ } catch (err) {
4716
+ throw new FalQueueError(err instanceof Error ? err.message : "fal.ai request failed");
4717
+ }
4718
+ if (!res.ok) {
4719
+ const text = await res.text().catch(() => "");
4720
+ throw new FalQueueError(`fal ${res.status}: ${text.slice(0, 200)}`, res.status);
4721
+ }
4722
+ return await res.json();
4723
+ }
4724
+ function createFalQueue(env) {
4725
+ return {
4726
+ async submit(model, input) {
4727
+ const res = await falFetch(env, `${FAL_QUEUE_BASE}/${model}`, FAL_SUBMIT_TIMEOUT_MS, input);
4728
+ return { requestId: res.request_id, statusUrl: res.status_url, responseUrl: res.response_url };
4729
+ },
4730
+ async checkStatus(statusUrl) {
4731
+ const res = await falFetch(env, statusUrl, FAL_POLL_TIMEOUT_MS);
4732
+ return {
4733
+ status: res.status,
4734
+ queuePosition: res.queue_position,
4735
+ logs: res.logs?.map((l) => l.message ?? "").filter(Boolean)
4736
+ };
4737
+ },
4738
+ fetchResult(responseUrl) {
4739
+ return falFetch(env, responseUrl, FAL_POLL_TIMEOUT_MS);
4740
+ }
4741
+ };
4742
+ }
4016
4743
 
4017
- // src/providers/groq/groq-container.ts
4018
- var container = null;
4019
- function configureProvider(config) {
4020
- const configStore = new GroqConfigStore();
4021
- const transport = new GroqHttpTransport(configStore);
4022
- const httpClient = new GroqHttpClient({ configStore, transport });
4023
- httpClient.initialize(config);
4024
- const textGeneration = new TextGenerationService(
4025
- httpClient,
4026
- new CacheManager(),
4027
- config.textModel
4028
- );
4029
- const chat = new ChatService(textGeneration);
4030
- container = { groqHttpClient: httpClient, textGeneration, chat };
4031
- return container;
4744
+ // src/providers/fal/queue/fal-wire.ts
4745
+ function buildFalImageInput(model, input) {
4746
+ const base = { prompt: input.prompt, num_images: input.numImages };
4747
+ if (model.includes("flux")) {
4748
+ const imageSize = input.aspectRatio ? FAL_ASPECT_IMAGE_SIZES[input.aspectRatio] : void 0;
4749
+ if (imageSize) base.image_size = imageSize;
4750
+ }
4751
+ return base;
4032
4752
  }
4033
- function getProvider() {
4034
- return container;
4753
+ function parseFalImageOutput(raw) {
4754
+ const obj = raw ?? {};
4755
+ const entries = Array.isArray(obj.images) ? obj.images : obj.image && typeof obj.image === "object" ? [obj.image] : [];
4756
+ const imageUrls = entries.map((e) => typeof e.url === "string" ? e.url : "").filter((url) => url.length > 0);
4757
+ const first = entries.find((e) => typeof e.width === "number" || typeof e.height === "number");
4758
+ return {
4759
+ imageUrls,
4760
+ ...typeof obj.seed === "number" ? { seed: obj.seed } : {},
4761
+ ...typeof first?.width === "number" ? { width: first.width } : {},
4762
+ ...typeof first?.height === "number" ? { height: first.height } : {}
4763
+ };
4035
4764
  }
4036
- function resetProvider() {
4037
- container?.groqHttpClient.reset();
4038
- container = null;
4765
+ function buildFalVideoInput(spec, input) {
4766
+ const base = { prompt: input.prompt };
4767
+ if (spec.durationFormat !== "none" && typeof input.durationSeconds === "number") {
4768
+ base.duration = spec.durationFormat === "s-suffix" ? `${input.durationSeconds}s` : String(input.durationSeconds);
4769
+ }
4770
+ if (spec.aspectRatios && input.aspectRatio) base.aspect_ratio = input.aspectRatio;
4771
+ if (spec.resolutions && input.resolution) base.resolution = input.resolution;
4772
+ if (input.imageUrl) base.image_url = input.imageUrl;
4773
+ if (spec.supportsNegativePrompt && input.negativePrompt) base.negative_prompt = input.negativePrompt;
4774
+ if (spec.generateAudio) base.generate_audio = true;
4775
+ return base;
4776
+ }
4777
+ function parseFalVideoOutput(raw) {
4778
+ const obj = raw ?? {};
4779
+ const video = obj.video && typeof obj.video === "object" ? obj.video : void 0;
4780
+ if (!video || typeof video.url !== "string" || video.url.length === 0) return null;
4781
+ return {
4782
+ videoUrl: video.url,
4783
+ ...typeof video.content_type === "string" ? { contentType: video.content_type } : {},
4784
+ ...typeof video.file_size === "number" ? { fileSize: video.file_size } : {}
4785
+ };
4786
+ }
4787
+ function buildFalTagPrompt(prompt) {
4788
+ return prompt.toLowerCase().split(/[,\s]+/).filter((word) => word.length > 3).slice(0, 5).join(", ");
4789
+ }
4790
+ function buildFalAudioLyrics(input) {
4791
+ if (input.instrumental) return "[inst]";
4792
+ const lyrics = input.lyrics?.trim();
4793
+ if (!lyrics) return void 0;
4794
+ if (lyrics.includes("[verse]") || lyrics.includes("[chorus]")) return lyrics;
4795
+ return `[verse]
4796
+ ${lyrics}
4797
+ [/verse]`;
4798
+ }
4799
+ function buildFalAudioInput(spec, input) {
4800
+ const tags = buildFalTagPrompt(input.prompt);
4801
+ const base = { prompt: tags, duration: input.durationSeconds };
4802
+ if (spec.supportsLyrics) {
4803
+ const lyrics = buildFalAudioLyrics(input);
4804
+ if (lyrics) base.lyrics = lyrics;
4805
+ }
4806
+ if (spec.supportsInstrumental && input.instrumental !== void 0) {
4807
+ base.instrumental = input.instrumental;
4808
+ }
4809
+ return base;
4810
+ }
4811
+ function parseFalAudioOutput(raw) {
4812
+ const obj = raw ?? {};
4813
+ const nested = (key) => {
4814
+ const value = obj[key];
4815
+ if (!value || typeof value !== "object") return void 0;
4816
+ const url = value.url;
4817
+ return typeof url === "string" && url.length > 0 ? url : void 0;
4818
+ };
4819
+ const flat = (key) => {
4820
+ const value = obj[key];
4821
+ return typeof value === "string" && value.length > 0 ? value : void 0;
4822
+ };
4823
+ const audioUrl = nested("audio_url") ?? nested("audio_file") ?? nested("audio") ?? flat("audio_url") ?? flat("audio");
4824
+ if (!audioUrl) return null;
4825
+ return { audioUrl };
4039
4826
  }
4040
4827
 
4041
4828
  // src/providers/embeddings/openai-embeddings.ts
@@ -4225,170 +5012,6 @@ function createOpenAiImageAdapter(config) {
4225
5012
  };
4226
5013
  }
4227
5014
 
4228
- // src/core/sse/tool-call-accumulator.ts
4229
- var ToolCallAccumulator = class {
4230
- slots = /* @__PURE__ */ new Map();
4231
- push(delta) {
4232
- const index = delta.index ?? 0;
4233
- const slot = this.slots.get(index) ?? { id: "", name: "", args: "" };
4234
- if (delta.id) slot.id = delta.id;
4235
- if (delta.function?.name) slot.name = delta.function.name;
4236
- if (delta.function?.arguments) slot.args += delta.function.arguments;
4237
- this.slots.set(index, slot);
4238
- }
4239
- finish() {
4240
- return Array.from(this.slots.entries()).sort(([a], [b]) => a - b).map(([, slot]) => ({
4241
- id: slot.id,
4242
- name: slot.name,
4243
- arguments: slot.args
4244
- })).filter((call) => call.id.length > 0 || call.name.length > 0);
4245
- }
4246
- };
4247
-
4248
- // src/providers/adapters/openai-compatible.adapter.ts
4249
- function toWireMessage(message) {
4250
- if (message.role === "tool") {
4251
- return { role: "tool", content: message.content, tool_call_id: message.toolCallId ?? "" };
4252
- }
4253
- if (message.role === "assistant" && message.toolCalls && message.toolCalls.length > 0) {
4254
- return {
4255
- role: "assistant",
4256
- content: message.content,
4257
- tool_calls: message.toolCalls.map((call) => ({
4258
- id: call.id,
4259
- type: "function",
4260
- function: { name: call.name, arguments: call.arguments }
4261
- }))
4262
- };
4263
- }
4264
- if (message.role === "user") return { role: "user", content: message.content };
4265
- return { role: message.role, content: message.content };
4266
- }
4267
- function mergeImages(messages, images) {
4268
- if (images.length === 0) return [...messages];
4269
- const parts = images.map((image) => ({
4270
- type: "image_url",
4271
- image_url: { url: `data:${image.mimeType};base64,${image.dataBase64}` }
4272
- }));
4273
- for (let index = messages.length - 1; index >= 0; index -= 1) {
4274
- const message = messages[index];
4275
- if (message.role !== "user") continue;
4276
- const textParts = typeof message.content === "string" ? message.content.length > 0 ? [{ type: "text", text: message.content }] : [] : message.content;
4277
- return [
4278
- ...messages.slice(0, index),
4279
- { role: "user", content: [...textParts, ...parts] },
4280
- ...messages.slice(index + 1)
4281
- ];
4282
- }
4283
- return [...messages, { role: "user", content: [...parts] }];
4284
- }
4285
- async function extractErrorDetail(response) {
4286
- try {
4287
- const body = await response.json();
4288
- return body.error;
4289
- } catch {
4290
- return void 0;
4291
- }
4292
- }
4293
- async function* parseSseEvent(rawEvent, toolCallAccumulator) {
4294
- for (const line of rawEvent.split("\n")) {
4295
- if (!line.startsWith("data:")) continue;
4296
- const payload = line.slice(5).trim();
4297
- if (payload.length === 0 || payload === "[DONE]") continue;
4298
- let chunk;
4299
- try {
4300
- chunk = JSON.parse(payload);
4301
- } catch {
4302
- continue;
4303
- }
4304
- const delta = chunk.choices?.[0]?.delta;
4305
- if (delta?.reasoning) {
4306
- yield { type: "delta", channel: "reasoning", text: delta.reasoning };
4307
- }
4308
- if (delta?.content) {
4309
- yield { type: "delta", channel: "answer", text: delta.content };
4310
- }
4311
- if (delta?.tool_calls) {
4312
- for (const toolCallDelta of delta.tool_calls) toolCallAccumulator.push(toolCallDelta);
4313
- }
4314
- if (chunk.usage) {
4315
- yield {
4316
- type: "usage",
4317
- usage: {
4318
- inputTokens: chunk.usage.prompt_tokens ?? 0,
4319
- outputTokens: chunk.usage.completion_tokens ?? 0,
4320
- reasoningTokens: chunk.usage.completion_tokens_details?.reasoning_tokens
4321
- }
4322
- };
4323
- }
4324
- }
4325
- }
4326
- async function* parseSseStream(body) {
4327
- const reader = body.getReader();
4328
- const decoder = new TextDecoder();
4329
- const toolCallAccumulator = new ToolCallAccumulator();
4330
- let buffer = "";
4331
- try {
4332
- while (true) {
4333
- const { done, value } = await reader.read();
4334
- if (done) break;
4335
- buffer += decoder.decode(value, { stream: true });
4336
- let separator = buffer.indexOf("\n\n");
4337
- while (separator !== -1) {
4338
- const rawEvent = buffer.slice(0, separator);
4339
- buffer = buffer.slice(separator + 2);
4340
- separator = buffer.indexOf("\n\n");
4341
- yield* parseSseEvent(rawEvent, toolCallAccumulator);
4342
- }
4343
- }
4344
- } catch (err) {
4345
- throw errorFromUnknown(err);
4346
- } finally {
4347
- reader.releaseLock();
4348
- }
4349
- const calls = toolCallAccumulator.finish();
4350
- if (calls.length > 0) yield { type: "tool_calls", calls };
4351
- }
4352
- function createOpenAiCompatibleAdapter(config) {
4353
- return {
4354
- async *stream(req) {
4355
- const timeout = AbortSignal.timeout(config.timeoutMs);
4356
- const signal = req.signal ? AbortSignal.any([req.signal, timeout]) : timeout;
4357
- let response;
4358
- try {
4359
- response = await fetch(`${config.baseUrl}/chat/completions`, {
4360
- method: "POST",
4361
- headers: {
4362
- Authorization: `Bearer ${config.apiKey}`,
4363
- "Content-Type": "application/json"
4364
- },
4365
- body: JSON.stringify({
4366
- model: req.externalModelId,
4367
- messages: mergeImages(req.messages.map(toWireMessage), req.images ?? []),
4368
- temperature: req.temperature,
4369
- max_completion_tokens: req.maxOutputTokens,
4370
- top_p: req.topP,
4371
- stream: true,
4372
- stream_options: { include_usage: true },
4373
- ...req.tools && req.tools.length > 0 ? {
4374
- tools: req.tools.map((tool) => ({ type: "function", function: tool })),
4375
- tool_choice: "auto"
4376
- } : {}
4377
- }),
4378
- signal
4379
- });
4380
- } catch (err) {
4381
- throw errorFromUnknown(err);
4382
- }
4383
- if (!response.ok || !response.body) {
4384
- const detail = await extractErrorDetail(response);
4385
- throw errorFromStatus(response.status, detail?.message, detail);
4386
- }
4387
- yield* parseSseStream(response.body);
4388
- }
4389
- };
4390
- }
4391
-
4392
5015
  // src/providers/adapters/anthropic.adapter.ts
4393
5016
  var ANTHROPIC_VERSION = "2023-06-01";
4394
5017
  var ERROR_TYPE_TO_CODE = {
@@ -4409,7 +5032,7 @@ function mergeImages2(messages, images) {
4409
5032
  }));
4410
5033
  for (let index = messages.length - 1; index >= 0; index -= 1) {
4411
5034
  const message = messages[index];
4412
- if (message.role !== "user") continue;
5035
+ if (!message || message.role !== "user") continue;
4413
5036
  const textBlocks = typeof message.content === "string" ? message.content.length > 0 ? [{ type: "text", text: message.content }] : [] : message.content;
4414
5037
  return [
4415
5038
  ...messages.slice(0, index),
@@ -4525,7 +5148,7 @@ function createAnthropicAdapter(config) {
4525
5148
  model: req.externalModelId,
4526
5149
  messages,
4527
5150
  ...system.length > 0 ? { system } : {},
4528
- max_tokens: req.maxOutputTokens,
5151
+ max_tokens: req.maxOutputTokens ?? 4096,
4529
5152
  stream: true
4530
5153
  }),
4531
5154
  signal
@@ -4542,6 +5165,201 @@ function createAnthropicAdapter(config) {
4542
5165
  };
4543
5166
  }
4544
5167
 
5168
+ // src/providers/adapters/adapter-resolver.ts
5169
+ var ADAPTER_TIMEOUT_MS = 6e4;
5170
+ var ADAPTER_MEDIA_TIMEOUT_MS = 12e4;
5171
+ function resolveApiKey(env, apiKeyEnv) {
5172
+ const value = env[apiKeyEnv];
5173
+ if (typeof value !== "string" || value.trim().length === 0) {
5174
+ throw new AiError("missing_key");
5175
+ }
5176
+ return value.trim();
5177
+ }
5178
+ function apiKeyAvailable(env, apiKeyEnv) {
5179
+ try {
5180
+ resolveApiKey(env, apiKeyEnv);
5181
+ return true;
5182
+ } catch {
5183
+ return false;
5184
+ }
5185
+ }
5186
+ function normalizeBaseUrl(url) {
5187
+ return url.replace(/\/$/, "");
5188
+ }
5189
+ function openAiApiKey(binding, capability) {
5190
+ if (binding.adapter !== "openai-compatible") {
5191
+ throw new AiError(
5192
+ "model_unavailable",
5193
+ `Adapter kind does not support ${capability}: ${binding.adapter}`
5194
+ );
5195
+ }
5196
+ return binding.apiKey;
5197
+ }
5198
+ function chatAdapterFor(binding, timing = {}) {
5199
+ const timeoutMs = timing.timeoutMs ?? ADAPTER_TIMEOUT_MS;
5200
+ if (binding.adapter === "openai-compatible") {
5201
+ return createOpenAiCompatibleAdapter({
5202
+ baseUrl: normalizeBaseUrl(binding.baseUrl),
5203
+ apiKey: binding.apiKey,
5204
+ timeoutMs
5205
+ });
5206
+ }
5207
+ if (binding.adapter === "anthropic") {
5208
+ return createAnthropicAdapter({
5209
+ baseUrl: normalizeBaseUrl(binding.baseUrl),
5210
+ apiKey: binding.apiKey,
5211
+ timeoutMs
5212
+ });
5213
+ }
5214
+ throw new AiError("model_unavailable", `Unsupported adapter kind: ${binding.adapter}`);
5215
+ }
5216
+ function imageAdapterFor(binding, timing = {}) {
5217
+ return createOpenAiImageAdapter({
5218
+ baseUrl: normalizeBaseUrl(binding.baseUrl),
5219
+ apiKey: openAiApiKey(binding, "images"),
5220
+ timeoutMs: timing.mediaTimeoutMs ?? ADAPTER_MEDIA_TIMEOUT_MS
5221
+ });
5222
+ }
5223
+ function embeddingsAdapterFor(binding, timing = {}) {
5224
+ return createOpenAiEmbeddingsAdapter({
5225
+ baseUrl: normalizeBaseUrl(binding.baseUrl),
5226
+ apiKey: openAiApiKey(binding, "embeddings"),
5227
+ timeoutMs: timing.timeoutMs ?? ADAPTER_TIMEOUT_MS
5228
+ });
5229
+ }
5230
+ function audioTranscriptionAdapterFor(binding, timing = {}) {
5231
+ return createOpenAiAudioTranscriptionAdapter({
5232
+ baseUrl: normalizeBaseUrl(binding.baseUrl),
5233
+ apiKey: openAiApiKey(binding, "audio transcription"),
5234
+ timeoutMs: timing.timeoutMs ?? ADAPTER_TIMEOUT_MS
5235
+ });
5236
+ }
5237
+ function speechAdapterFor(binding, timing = {}) {
5238
+ return createOpenAiSpeechAdapter({
5239
+ baseUrl: normalizeBaseUrl(binding.baseUrl),
5240
+ apiKey: openAiApiKey(binding, "speech synthesis"),
5241
+ timeoutMs: timing.mediaTimeoutMs ?? ADAPTER_MEDIA_TIMEOUT_MS
5242
+ });
5243
+ }
5244
+ function createAdapterResolver(env, timing = {}) {
5245
+ const toProviderBinding = (binding) => ({
5246
+ adapter: binding.adapter,
5247
+ baseUrl: binding.baseUrl,
5248
+ apiKey: resolveApiKey(env, binding.apiKeyEnv)
5249
+ });
5250
+ return {
5251
+ chat: (binding) => chatAdapterFor(toProviderBinding(binding), timing),
5252
+ image: (binding) => imageAdapterFor(toProviderBinding(binding), timing),
5253
+ embeddings: (binding) => embeddingsAdapterFor(toProviderBinding(binding), timing),
5254
+ audioTranscription: (binding) => audioTranscriptionAdapterFor(toProviderBinding(binding), timing),
5255
+ speech: (binding) => speechAdapterFor(toProviderBinding(binding), timing),
5256
+ available: (binding) => apiKeyAvailable(env, binding.apiKeyEnv)
5257
+ };
5258
+ }
5259
+
5260
+ // src/core/context/context-manager.ts
5261
+ var CHARS_PER_TOKEN = 4;
5262
+ var SAFETY_MARGIN_TOKENS = 256;
5263
+ function estimateTokens(text) {
5264
+ return Math.ceil(text.length / CHARS_PER_TOKEN);
5265
+ }
5266
+ function estimateMessageTokens(message) {
5267
+ const toolStructure = message.toolCalls && message.toolCalls.length > 0 ? estimateTokens(JSON.stringify(message.toolCalls)) : 0;
5268
+ return estimateTokens(message.content) + toolStructure + 8;
5269
+ }
5270
+ function buildContext(history, systemPrompt, budget) {
5271
+ const available = budget.contextWindow - budget.maxOutputTokens - SAFETY_MARGIN_TOKENS;
5272
+ let used = estimateTokens(systemPrompt);
5273
+ const selected = [];
5274
+ for (let i = history.length - 1; i >= 0; i -= 1) {
5275
+ const message = history[i];
5276
+ if (!message) continue;
5277
+ const cost = estimateMessageTokens(message);
5278
+ if (used + cost > available && selected.length > 0) break;
5279
+ used += cost;
5280
+ selected.push(message);
5281
+ }
5282
+ selected.reverse();
5283
+ while (selected.length > 1 && (selected[0]?.role === "assistant" || selected[0]?.role === "tool")) {
5284
+ selected.shift();
5285
+ }
5286
+ return [{ role: "system", content: systemPrompt }, ...selected];
5287
+ }
5288
+
5289
+ // src/providers/execution/run-conversation.ts
5290
+ async function* runConversation(input) {
5291
+ const tools = input.tools && input.tools.length > 0 ? { tools: input.tools } : {};
5292
+ const images = input.images && input.images.length > 0 ? { images: input.images } : {};
5293
+ for (const [index, attempt] of input.attempts.entries()) {
5294
+ let answer = "";
5295
+ let reasoning = "";
5296
+ let usage = null;
5297
+ let toolCalls = [];
5298
+ const startedAt = Date.now();
5299
+ const context = buildContext(input.history, input.systemPrompt, {
5300
+ contextWindow: attempt.model.contextWindow,
5301
+ // No explicit output reservation → keep the whole window for input.
5302
+ maxOutputTokens: attempt.settings.maxOutputTokens ?? 0
5303
+ });
5304
+ yield { type: "attempt", index };
5305
+ try {
5306
+ for await (const event of attempt.adapter.stream({
5307
+ externalModelId: attempt.model.externalModelId,
5308
+ messages: context,
5309
+ temperature: attempt.settings.temperature,
5310
+ maxOutputTokens: attempt.settings.maxOutputTokens,
5311
+ topP: attempt.settings.topP,
5312
+ ...tools,
5313
+ ...images,
5314
+ signal: input.signal
5315
+ })) {
5316
+ if (event.type === "delta") {
5317
+ if (event.channel === "reasoning") reasoning += event.text;
5318
+ else answer += event.text;
5319
+ yield { type: "delta", channel: event.channel, text: event.text };
5320
+ } else if (event.type === "tool_calls") {
5321
+ toolCalls = [...event.calls];
5322
+ } else {
5323
+ usage = event.usage;
5324
+ }
5325
+ }
5326
+ } catch (err) {
5327
+ const result2 = {
5328
+ answer,
5329
+ reasoning,
5330
+ usage,
5331
+ toolCalls,
5332
+ durationMs: Date.now() - startedAt
5333
+ };
5334
+ if (input.signal?.aborted === true) {
5335
+ yield { type: "stopped", result: result2 };
5336
+ return;
5337
+ }
5338
+ if (index + 1 < input.attempts.length && canFailover(err, answer.length + reasoning.length)) {
5339
+ continue;
5340
+ }
5341
+ const aiError = err instanceof AiError ? err : errorFromUnknown(err);
5342
+ yield { type: "failed", code: aiError.code, message: aiError.userMessage, result: result2 };
5343
+ return;
5344
+ }
5345
+ const result = {
5346
+ answer,
5347
+ reasoning,
5348
+ usage,
5349
+ toolCalls,
5350
+ durationMs: Date.now() - startedAt
5351
+ };
5352
+ if (toolCalls.length > 0 && input.tools && input.tools.length > 0) {
5353
+ yield { type: "tool_calls", result };
5354
+ return;
5355
+ }
5356
+ yield { type: "done", result };
5357
+ return;
5358
+ }
5359
+ }
5360
+
5361
+ exports.ADAPTER_MEDIA_TIMEOUT_MS = ADAPTER_MEDIA_TIMEOUT_MS;
5362
+ exports.ADAPTER_TIMEOUT_MS = ADAPTER_TIMEOUT_MS;
4545
5363
  exports.ANTHROPIC_API_VERSION = ANTHROPIC_API_VERSION;
4546
5364
  exports.ANTHROPIC_DEFAULT_MAX_TOKENS = ANTHROPIC_DEFAULT_MAX_TOKENS;
4547
5365
  exports.ANTHROPIC_MODELS = ANTHROPIC_MODELS;
@@ -4557,7 +5375,9 @@ exports.CancellationError = CancellationError;
4557
5375
  exports.DEFAULT_BASE_URL = DEFAULT_BASE_URL;
4558
5376
  exports.DEFAULT_GENERATION_CONFIG = DEFAULT_GENERATION_CONFIG;
4559
5377
  exports.DEFAULT_MODELS = DEFAULT_MODELS;
5378
+ exports.FAL_ASPECT_IMAGE_SIZES = FAL_ASPECT_IMAGE_SIZES;
4560
5379
  exports.FalProvider = FalProvider;
5380
+ exports.FalQueueError = FalQueueError;
4561
5381
  exports.GEMINI_TIERS = GEMINI_TIERS;
4562
5382
  exports.GROQ_MODELS = GROQ_MODELS;
4563
5383
  exports.GROQ_TIERS = GROQ_TIERS;
@@ -4571,6 +5391,7 @@ exports.GroqProvider = GroqProvider;
4571
5391
  exports.IMAGE_B64_LIMIT = IMAGE_B64_LIMIT;
4572
5392
  exports.IMAGE_QUALITIES = IMAGE_QUALITIES;
4573
5393
  exports.IMAGE_SIZES = IMAGE_SIZES;
5394
+ exports.OpenAiCompatibleProvider = OpenAiCompatibleProvider;
4574
5395
  exports.ProviderError = ProviderError;
4575
5396
  exports.ProviderFactory = ProviderFactory;
4576
5397
  exports.PrunaProvider = PrunaProvider;
@@ -4580,18 +5401,33 @@ exports.TIMEOUTS = TIMEOUTS;
4580
5401
  exports.TextGenerationService = TextGenerationService;
4581
5402
  exports.TimeoutError = TimeoutError;
4582
5403
  exports.ValidationError = ValidationError;
5404
+ exports.WORKERS_AI_LLM_MODEL = WORKERS_AI_LLM_MODEL;
5405
+ exports.WORKERS_AI_TIERS = WORKERS_AI_TIERS;
5406
+ exports.WorkersAIError = WorkersAIError;
5407
+ exports.WorkersAIProvider = WorkersAIProvider;
5408
+ exports.apiKeyAvailable = apiKeyAvailable;
5409
+ exports.audioTranscriptionAdapterFor = audioTranscriptionAdapterFor;
5410
+ exports.buildFalAudioInput = buildFalAudioInput;
5411
+ exports.buildFalAudioLyrics = buildFalAudioLyrics;
5412
+ exports.buildFalImageInput = buildFalImageInput;
5413
+ exports.buildFalTagPrompt = buildFalTagPrompt;
5414
+ exports.buildFalVideoInput = buildFalVideoInput;
5415
+ exports.chatAdapterFor = chatAdapterFor;
4583
5416
  exports.configureProvider = configureProvider;
4584
5417
  exports.consumeAnthropicStream = consumeAnthropicStream;
4585
5418
  exports.consumeGroqStream = consumeGroqStream;
4586
5419
  exports.createAI = createAI;
5420
+ exports.createAdapterResolver = createAdapterResolver;
4587
5421
  exports.createAnthropicAdapter = createAnthropicAdapter;
4588
5422
  exports.createAssistantMessage = createAssistantMessage;
4589
5423
  exports.createFalProvider = createFalProvider;
5424
+ exports.createFalQueue = createFalQueue;
4590
5425
  exports.createGeminiProvider = createGeminiProvider;
4591
5426
  exports.createGroqProvider = createGroqProvider;
4592
5427
  exports.createMessage = createMessage;
4593
5428
  exports.createOpenAiAudioTranscriptionAdapter = createOpenAiAudioTranscriptionAdapter;
4594
5429
  exports.createOpenAiCompatibleAdapter = createOpenAiCompatibleAdapter;
5430
+ exports.createOpenAiCompatibleProvider = createOpenAiCompatibleProvider;
4595
5431
  exports.createOpenAiEmbeddingsAdapter = createOpenAiEmbeddingsAdapter;
4596
5432
  exports.createOpenAiImageAdapter = createOpenAiImageAdapter;
4597
5433
  exports.createOpenAiSpeechAdapter = createOpenAiSpeechAdapter;
@@ -4599,23 +5435,37 @@ exports.createProviderFactory = createProviderFactory;
4599
5435
  exports.createPrunaProvider = createPrunaProvider;
4600
5436
  exports.createSystemMessage = createSystemMessage;
4601
5437
  exports.createUserMessage = createUserMessage;
5438
+ exports.createWorkersAIProvider = createWorkersAIProvider;
4602
5439
  exports.defaultEffortForModel = defaultEffortForModel;
5440
+ exports.embeddingsAdapterFor = embeddingsAdapterFor;
4603
5441
  exports.extractContentFromSsePayload = extractContentFromSsePayload;
4604
5442
  exports.extractTextFromMessages = extractTextFromMessages;
5443
+ exports.falQueueConfigured = falQueueConfigured;
4605
5444
  exports.formatMessagesForDisplay = formatMessagesForDisplay;
4606
5445
  exports.generateImageThenVideo = generateImageThenVideo;
4607
5446
  exports.generatePruna = generate;
4608
5447
  exports.generateWithPruna = generateWithPruna;
4609
5448
  exports.getProvider = getProvider;
5449
+ exports.imageAdapterFor = imageAdapterFor;
4610
5450
  exports.isMediaProvider = isMediaProvider;
4611
5451
  exports.isTextProvider = isTextProvider;
4612
5452
  exports.mapAnthropicStopReason = mapAnthropicStopReason;
5453
+ exports.parseFalAudioOutput = parseFalAudioOutput;
5454
+ exports.parseFalImageOutput = parseFalImageOutput;
5455
+ exports.parseFalVideoOutput = parseFalVideoOutput;
4613
5456
  exports.pollForResult = pollForResult;
4614
5457
  exports.promptToMessages = promptToMessages;
4615
5458
  exports.requestStructuredJson = requestStructuredJson;
4616
5459
  exports.resetProvider = resetProvider;
5460
+ exports.resolveApiKey = resolveApiKey;
5461
+ exports.runConversation = runConversation;
5462
+ exports.runWorkersAIText = runWorkersAIText;
5463
+ exports.speakWithWorkersAI = speakWithWorkersAI;
5464
+ exports.speechAdapterFor = speechAdapterFor;
5465
+ exports.streamWorkersAIText = streamWorkersAIText;
4617
5466
  exports.submitPrediction = submitPrediction;
4618
5467
  exports.toGroqMessages = toGroqMessages;
4619
5468
  exports.uploadImage = uploadImage;
5469
+ exports.workersAIConfigured = workersAIConfigured;
4620
5470
  //# sourceMappingURL=index.cjs.map
4621
5471
  //# sourceMappingURL=index.cjs.map