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