@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
package/dist/index.js CHANGED
@@ -303,6 +303,23 @@ function errorFromUnknown(err) {
303
303
  return new AiError("upstream_error");
304
304
  }
305
305
 
306
+ // src/core/errors/error-status.ts
307
+ var AI_ERROR_STATUS = {
308
+ invalid_key: 502,
309
+ missing_key: 503,
310
+ rate_limited: 429,
311
+ capacity: 503,
312
+ timeout: 504,
313
+ invalid_request: 400,
314
+ model_unavailable: 503,
315
+ upstream_error: 502,
316
+ network: 502,
317
+ cancelled: 400
318
+ };
319
+ function aiErrorStatus(code) {
320
+ return AI_ERROR_STATUS[code];
321
+ }
322
+
306
323
  // src/core/executor/retry-executor.strategy.ts
307
324
  var RetryExecutor = class {
308
325
  constructor(retryManager2, options) {
@@ -1426,13 +1443,14 @@ function buildContext(history, systemPrompt, budget) {
1426
1443
  const selected = [];
1427
1444
  for (let i = history.length - 1; i >= 0; i -= 1) {
1428
1445
  const message = history[i];
1446
+ if (!message) continue;
1429
1447
  const cost = estimateMessageTokens(message);
1430
1448
  if (used + cost > available && selected.length > 0) break;
1431
1449
  used += cost;
1432
1450
  selected.push(message);
1433
1451
  }
1434
1452
  selected.reverse();
1435
- while (selected.length > 1 && (selected[0].role === "assistant" || selected[0].role === "tool")) {
1453
+ while (selected.length > 1 && (selected[0]?.role === "assistant" || selected[0]?.role === "tool")) {
1436
1454
  selected.shift();
1437
1455
  }
1438
1456
  return [{ role: "system", content: systemPrompt }, ...selected];
@@ -1635,7 +1653,10 @@ function diffLines(before, after) {
1635
1653
  const dp = Array.from({ length: m + 1 }, () => new Array(n + 1).fill(0));
1636
1654
  for (let i2 = m - 1; i2 >= 0; i2 -= 1) {
1637
1655
  for (let j2 = n - 1; j2 >= 0; j2 -= 1) {
1638
- dp[i2][j2] = a[i2] === b[j2] ? dp[i2 + 1][j2 + 1] + 1 : Math.max(dp[i2 + 1][j2], dp[i2][j2 + 1]);
1656
+ const row = dp[i2];
1657
+ if (row) {
1658
+ row[j2] = a[i2] === b[j2] ? (dp[i2 + 1]?.[j2 + 1] ?? 0) + 1 : Math.max(dp[i2 + 1]?.[j2] ?? 0, row[j2 + 1] ?? 0);
1659
+ }
1639
1660
  }
1640
1661
  }
1641
1662
  const out = [];
@@ -1643,23 +1664,23 @@ function diffLines(before, after) {
1643
1664
  let j = 0;
1644
1665
  while (i < m && j < n) {
1645
1666
  if (a[i] === b[j]) {
1646
- out.push({ type: "same", text: a[i] });
1667
+ out.push({ type: "same", text: a[i] ?? "" });
1647
1668
  i += 1;
1648
1669
  j += 1;
1649
- } else if (dp[i + 1][j] >= dp[i][j + 1]) {
1650
- out.push({ type: "remove", text: a[i] });
1670
+ } else if ((dp[i + 1]?.[j] ?? 0) >= (dp[i]?.[j + 1] ?? 0)) {
1671
+ out.push({ type: "remove", text: a[i] ?? "" });
1651
1672
  i += 1;
1652
1673
  } else {
1653
- out.push({ type: "add", text: b[j] });
1674
+ out.push({ type: "add", text: b[j] ?? "" });
1654
1675
  j += 1;
1655
1676
  }
1656
1677
  }
1657
1678
  while (i < m) {
1658
- out.push({ type: "remove", text: a[i] });
1679
+ out.push({ type: "remove", text: a[i] ?? "" });
1659
1680
  i += 1;
1660
1681
  }
1661
1682
  while (j < n) {
1662
- out.push({ type: "add", text: b[j] });
1683
+ out.push({ type: "add", text: b[j] ?? "" });
1663
1684
  j += 1;
1664
1685
  }
1665
1686
  return out;
@@ -1774,14 +1795,14 @@ function dotProduct(a, b) {
1774
1795
  const length = Math.min(a.length, b.length);
1775
1796
  let sum = 0;
1776
1797
  for (let i = 0; i < length; i += 1) {
1777
- sum += a[i] * b[i];
1798
+ sum += (a[i] ?? 0) * (b[i] ?? 0);
1778
1799
  }
1779
1800
  return sum;
1780
1801
  }
1781
1802
  function magnitude(v) {
1782
1803
  let sum = 0;
1783
1804
  for (let i = 0; i < v.length; i += 1) {
1784
- sum += v[i] * v[i];
1805
+ sum += (v[i] ?? 0) * (v[i] ?? 0);
1785
1806
  }
1786
1807
  return Math.sqrt(sum);
1787
1808
  }
@@ -1926,6 +1947,115 @@ function validateToolArgs(toolKey, args, schemas) {
1926
1947
  return { ok: false, error: `Invalid schema definition for tool: ${toolKey}` };
1927
1948
  }
1928
1949
 
1950
+ // src/core/analytics/usage-series.ts
1951
+ function num(value) {
1952
+ const parsed = typeof value === "number" ? value : Number(value ?? 0);
1953
+ return Number.isFinite(parsed) ? parsed : 0;
1954
+ }
1955
+ function utcDateKey(base, offsetDays) {
1956
+ const shifted = new Date(base.getTime() - offsetDays * 864e5);
1957
+ return shifted.toISOString().slice(0, 10);
1958
+ }
1959
+ function buildUsageDailySeries(days, usageRows, messageRows, today = /* @__PURE__ */ new Date()) {
1960
+ const usageByDate = new Map(usageRows.map((row) => [row.date, row]));
1961
+ const messageByDate = new Map(messageRows.map((row) => [row.date, row]));
1962
+ const series = [];
1963
+ for (let offset = days - 1; offset >= 0; offset -= 1) {
1964
+ const date = utcDateKey(today, offset);
1965
+ const usage = usageByDate.get(date);
1966
+ const message = messageByDate.get(date);
1967
+ series.push({
1968
+ date,
1969
+ requests: num(usage?.requests),
1970
+ attempts: num(message?.attempts),
1971
+ errors: num(message?.errors),
1972
+ inputTokens: num(usage?.inputTokens),
1973
+ outputTokens: num(usage?.outputTokens),
1974
+ reasoningTokens: num(usage?.reasoningTokens),
1975
+ costMicros: num(usage?.costMicros)
1976
+ });
1977
+ }
1978
+ return series;
1979
+ }
1980
+ function usageWindowTotals(series, from, to) {
1981
+ const slice = series.slice(from, to);
1982
+ return slice.reduce(
1983
+ (acc, day) => ({
1984
+ requests: acc.requests + day.requests,
1985
+ attempts: acc.attempts + day.attempts,
1986
+ errors: acc.errors + day.errors,
1987
+ inputTokens: acc.inputTokens + day.inputTokens,
1988
+ outputTokens: acc.outputTokens + day.outputTokens,
1989
+ costMicros: acc.costMicros + day.costMicros
1990
+ }),
1991
+ { requests: 0, attempts: 0, errors: 0, inputTokens: 0, outputTokens: 0, costMicros: 0 }
1992
+ );
1993
+ }
1994
+ function usageTrendOf(current, previous) {
1995
+ return {
1996
+ current,
1997
+ previous,
1998
+ deltaPct: previous > 0 ? (current - previous) / previous * 100 : null
1999
+ };
2000
+ }
2001
+ function errorRatePct(errors, attempts) {
2002
+ if (attempts <= 0) return 0;
2003
+ return errors / attempts * 100;
2004
+ }
2005
+
2006
+ // src/core/remote/remote-chat-client.ts
2007
+ async function surfaceHttpError(response, callbacks) {
2008
+ const payload = await response.json().catch(() => null);
2009
+ callbacks.onError?.(payload?.code ?? "request_failed", payload?.error ?? "Request failed");
2010
+ }
2011
+ function createRemoteChatClient(options) {
2012
+ const doFetch = options.fetchImpl ?? fetch;
2013
+ const { urls } = options;
2014
+ return {
2015
+ streamChat(conversationId, body, callbacks) {
2016
+ const controller = new AbortController();
2017
+ const promise = (async () => {
2018
+ const response = await doFetch(urls.stream(conversationId), {
2019
+ method: "POST",
2020
+ headers: { "content-type": "application/json" },
2021
+ body: JSON.stringify(body),
2022
+ signal: controller.signal
2023
+ });
2024
+ if (!response.ok) {
2025
+ await surfaceHttpError(response, callbacks);
2026
+ return;
2027
+ }
2028
+ if (!response.body) {
2029
+ callbacks.onError?.("request_failed", "The server returned an empty stream.");
2030
+ return;
2031
+ }
2032
+ await readSseResponse(response, callbacks);
2033
+ })();
2034
+ return {
2035
+ promise,
2036
+ abort: () => controller.abort()
2037
+ };
2038
+ },
2039
+ async resolveToolCall(conversationId, toolCallId, approved, callbacks) {
2040
+ const response = await doFetch(urls.resolveToolCall(conversationId, toolCallId), {
2041
+ method: "POST",
2042
+ headers: { "content-type": "application/json" },
2043
+ body: JSON.stringify({ approved })
2044
+ });
2045
+ if (!response.ok) {
2046
+ await surfaceHttpError(response, callbacks);
2047
+ return false;
2048
+ }
2049
+ const contentType = response.headers.get("content-type") ?? "";
2050
+ if (!contentType.includes("text/event-stream") || !response.body) {
2051
+ return false;
2052
+ }
2053
+ await readSseResponse(response, callbacks);
2054
+ return true;
2055
+ }
2056
+ };
2057
+ }
2058
+
1929
2059
  // src/providers/base/provider.types.ts
1930
2060
  function isTextProvider(provider) {
1931
2061
  return provider.capabilities.includes("text");
@@ -2656,7 +2786,7 @@ var AnthropicProvider = class {
2656
2786
  );
2657
2787
  }
2658
2788
  const structuredPrompt = new StructuredPromptBuilder(
2659
- messages[lastUserIndex].content
2789
+ messages[lastUserIndex]?.content ?? ""
2660
2790
  ).withSchema(request.schema).build();
2661
2791
  messages[lastUserIndex] = { role: "user", content: structuredPrompt };
2662
2792
  const wireRequest = new AnthropicRequestBuilder(model).withRequest({ ...request, messages }).withEffort(this.config.effort).build();
@@ -2736,7 +2866,8 @@ function mapAnthropicUsage(usage) {
2736
2866
  }
2737
2867
  function findLastIndex(items, predicate) {
2738
2868
  for (let i = items.length - 1; i >= 0; i--) {
2739
- if (predicate(items[i])) return i;
2869
+ const item = items[i];
2870
+ if (item !== void 0 && predicate(item)) return i;
2740
2871
  }
2741
2872
  return -1;
2742
2873
  }
@@ -3236,7 +3367,7 @@ var GroqProvider = class {
3236
3367
  );
3237
3368
  }
3238
3369
  const structuredPrompt = new StructuredPromptBuilder(
3239
- messages[lastUserIndex].content
3370
+ messages[lastUserIndex]?.content ?? ""
3240
3371
  ).withSchema(request.schema).build();
3241
3372
  messages[lastUserIndex] = { role: "user", content: structuredPrompt };
3242
3373
  const wireRequest = GroqChatRequestBuilder.for(model).withMessages(messages).withConfig(this.generationConfig(request, STRUCTURED_TEMPERATURE)).withJsonResponse(true).build();
@@ -3333,7 +3464,8 @@ function createGroqProvider(config) {
3333
3464
  }
3334
3465
  function findLastIndex2(items, predicate) {
3335
3466
  for (let i = items.length - 1; i >= 0; i--) {
3336
- if (predicate(items[i])) return i;
3467
+ const item = items[i];
3468
+ if (item !== void 0 && predicate(item)) return i;
3337
3469
  }
3338
3470
  return -1;
3339
3471
  }
@@ -3483,7 +3615,7 @@ var GeminiProvider = class extends BaseProvider {
3483
3615
  }
3484
3616
  messages[lastUserIndex] = {
3485
3617
  role: "user",
3486
- content: new StructuredPromptBuilder(messages[lastUserIndex].content).withSchema(request.schema).build()
3618
+ content: new StructuredPromptBuilder(messages[lastUserIndex]?.content ?? "").withSchema(request.schema).build()
3487
3619
  };
3488
3620
  const result = await this.generate({ ...request, messages, temperature: 0.1 });
3489
3621
  return { value: this.jsonParser.parse(result.text), result };
@@ -3589,7 +3721,8 @@ function mapGeminiFinishReason(reason) {
3589
3721
  }
3590
3722
  function findLastIndex3(items, predicate) {
3591
3723
  for (let i = items.length - 1; i >= 0; i--) {
3592
- if (predicate(items[i])) return i;
3724
+ const item = items[i];
3725
+ if (item !== void 0 && predicate(item)) return i;
3593
3726
  }
3594
3727
  return -1;
3595
3728
  }
@@ -4807,98 +4940,636 @@ function createPrunaProvider(config) {
4807
4940
  return new PrunaProvider(config);
4808
4941
  }
4809
4942
 
4810
- // src/providers/factory/create-ai.ts
4811
- function createAI(config = {}) {
4812
- const factory = new ProviderFactory({
4813
- priority: config.priority,
4814
- fallbackEnabled: config.fallbackEnabled,
4815
- defaultTier: config.defaultTier
4816
- });
4817
- if (config.anthropic?.apiKey) {
4818
- factory.register(new AnthropicProvider(config.anthropic));
4943
+ // src/providers/adapters/openai-compatible.adapter.ts
4944
+ function toWireMessage(message) {
4945
+ if (message.role === "tool") {
4946
+ return { role: "tool", content: message.content, tool_call_id: message.toolCallId ?? "" };
4819
4947
  }
4820
- if (config.groq?.apiKey) {
4821
- factory.register(new GroqProvider(config.groq));
4948
+ if (message.role === "assistant" && message.toolCalls && message.toolCalls.length > 0) {
4949
+ return {
4950
+ role: "assistant",
4951
+ content: message.content,
4952
+ tool_calls: message.toolCalls.map((call) => ({
4953
+ id: call.id,
4954
+ type: "function",
4955
+ function: { name: call.name, arguments: call.arguments }
4956
+ }))
4957
+ };
4822
4958
  }
4823
- if (config.gemini?.apiKey) {
4824
- factory.register(new GeminiProvider({ ...config.gemini, enabled: true }));
4959
+ if (message.role === "user") return { role: "user", content: message.content };
4960
+ return { role: message.role, content: message.content };
4961
+ }
4962
+ function mergeImages(messages, images) {
4963
+ if (images.length === 0) return [...messages];
4964
+ const parts = images.map((image) => ({
4965
+ type: "image_url",
4966
+ image_url: { url: `data:${image.mimeType};base64,${image.dataBase64}` }
4967
+ }));
4968
+ for (let index = messages.length - 1; index >= 0; index -= 1) {
4969
+ const message = messages[index];
4970
+ if (!message || message.role !== "user") continue;
4971
+ const textParts = typeof message.content === "string" ? message.content.length > 0 ? [{ type: "text", text: message.content }] : [] : message.content;
4972
+ return [
4973
+ ...messages.slice(0, index),
4974
+ { role: "user", content: [...textParts, ...parts] },
4975
+ ...messages.slice(index + 1)
4976
+ ];
4825
4977
  }
4826
- if (config.fal?.apiKey) {
4827
- factory.register(new FalProvider({ ...config.fal, enabled: true }));
4978
+ return [...messages, { role: "user", content: [...parts] }];
4979
+ }
4980
+ async function extractErrorDetail(response) {
4981
+ try {
4982
+ const body = await response.json();
4983
+ return body.error;
4984
+ } catch {
4985
+ return void 0;
4828
4986
  }
4829
- if (config.pruna?.apiKey) {
4830
- factory.register(new PrunaProvider({ ...config.pruna, enabled: true }));
4987
+ }
4988
+ async function* parseSseEvent2(rawEvent, toolCallAccumulator) {
4989
+ for (const line of rawEvent.split("\n")) {
4990
+ if (!line.startsWith("data:")) continue;
4991
+ const payload = line.slice(5).trim();
4992
+ if (payload.length === 0 || payload === "[DONE]") continue;
4993
+ let chunk;
4994
+ try {
4995
+ chunk = JSON.parse(payload);
4996
+ } catch {
4997
+ continue;
4998
+ }
4999
+ const delta = chunk.choices?.[0]?.delta;
5000
+ if (delta?.reasoning) {
5001
+ yield { type: "delta", channel: "reasoning", text: delta.reasoning };
5002
+ }
5003
+ if (delta?.content) {
5004
+ yield { type: "delta", channel: "answer", text: delta.content };
5005
+ }
5006
+ if (delta?.tool_calls) {
5007
+ for (const toolCallDelta of delta.tool_calls) toolCallAccumulator.push(toolCallDelta);
5008
+ }
5009
+ if (chunk.usage) {
5010
+ yield {
5011
+ type: "usage",
5012
+ usage: {
5013
+ inputTokens: chunk.usage.prompt_tokens ?? 0,
5014
+ outputTokens: chunk.usage.completion_tokens ?? 0,
5015
+ reasoningTokens: chunk.usage.completion_tokens_details?.reasoning_tokens
5016
+ }
5017
+ };
5018
+ }
4831
5019
  }
4832
- return factory;
4833
5020
  }
4834
-
4835
- // src/providers/groq/groq-messages.ts
4836
- var createUserMessage = (content) => ({
4837
- role: "user",
4838
- content
4839
- });
4840
- var createAssistantMessage = (content) => ({
4841
- role: "assistant",
4842
- content
4843
- });
4844
- var createSystemMessage = (content) => ({
4845
- role: "system",
4846
- content
4847
- });
4848
- var createMessage = (role, content) => ({
4849
- role,
4850
- content
4851
- });
4852
- var promptToMessages = (prompt) => [
4853
- createUserMessage(prompt)
4854
- ];
4855
- var extractTextFromMessages = (messages) => messages.map((m) => m.content).join("\n\n");
4856
- var formatMessagesForDisplay = (messages) => messages.map((m) => `[${m.role.toUpperCase()}]: ${m.content}`).join("\n\n---\n\n");
4857
-
4858
- // src/providers/groq/groq-structured-json.ts
4859
- async function requestStructuredJson(client, request) {
4860
- const chatRequest = {
4861
- model: request.model,
4862
- messages: [createUserMessage(request.instructions)],
4863
- temperature: request.temperature,
4864
- max_tokens: request.maxTokens,
4865
- response_format: { type: "json_object" },
4866
- ...request.reasoningEffort !== void 0 ? { reasoning_effort: request.reasoningEffort } : {}
4867
- };
4868
- const response = await client.postChatCompletion(chatRequest);
4869
- const content = response.choices[0]?.message?.content ?? "";
4870
- const candidate = extractJsonCandidate(content);
5021
+ async function* parseSseStream(body) {
5022
+ const reader = body.getReader();
5023
+ const decoder = new TextDecoder();
5024
+ const toolCallAccumulator = new ToolCallAccumulator();
5025
+ let buffer = "";
4871
5026
  try {
4872
- return JSON.parse(candidate);
5027
+ while (true) {
5028
+ const { done, value } = await reader.read();
5029
+ if (done) break;
5030
+ buffer += decoder.decode(value, { stream: true });
5031
+ let separator = buffer.indexOf("\n\n");
5032
+ while (separator !== -1) {
5033
+ const rawEvent = buffer.slice(0, separator);
5034
+ buffer = buffer.slice(separator + 2);
5035
+ separator = buffer.indexOf("\n\n");
5036
+ yield* parseSseEvent2(rawEvent, toolCallAccumulator);
5037
+ }
5038
+ }
4873
5039
  } catch (err) {
4874
- throw new GroqError(
4875
- "UNKNOWN_ERROR" /* UNKNOWN_ERROR */,
4876
- "model response contained no parsable json object",
4877
- err instanceof Error ? err : void 0
4878
- );
5040
+ throw errorFromUnknown(err);
5041
+ } finally {
5042
+ reader.releaseLock();
5043
+ }
5044
+ const calls = toolCallAccumulator.finish();
5045
+ if (calls.length > 0) yield { type: "tool_calls", calls };
5046
+ }
5047
+ function createOpenAiCompatibleAdapter(config) {
5048
+ if (!config.baseUrl && !config.endpointUrl) {
5049
+ throw new Error("createOpenAiCompatibleAdapter requires baseUrl or endpointUrl");
4879
5050
  }
5051
+ const endpoint = config.endpointUrl ?? `${config.baseUrl}/chat/completions`;
5052
+ return {
5053
+ async *stream(req) {
5054
+ const timeout = AbortSignal.timeout(config.timeoutMs);
5055
+ const signal = req.signal ? AbortSignal.any([req.signal, timeout]) : timeout;
5056
+ let response;
5057
+ try {
5058
+ response = await fetch(endpoint, {
5059
+ method: "POST",
5060
+ headers: {
5061
+ Authorization: `Bearer ${config.apiKey}`,
5062
+ "Content-Type": "application/json"
5063
+ },
5064
+ body: JSON.stringify({
5065
+ model: req.externalModelId,
5066
+ messages: mergeImages(req.messages.map(toWireMessage), req.images ?? []),
5067
+ temperature: req.temperature,
5068
+ max_completion_tokens: req.maxOutputTokens,
5069
+ top_p: req.topP,
5070
+ stream: true,
5071
+ stream_options: { include_usage: true },
5072
+ ...req.tools && req.tools.length > 0 ? {
5073
+ tools: req.tools.map((tool) => ({ type: "function", function: tool })),
5074
+ tool_choice: "auto"
5075
+ } : {}
5076
+ }),
5077
+ signal
5078
+ });
5079
+ } catch (err) {
5080
+ throw errorFromUnknown(err);
5081
+ }
5082
+ if (!response.ok || !response.body) {
5083
+ const detail = await extractErrorDetail(response);
5084
+ throw errorFromStatus(response.status, detail?.message, detail);
5085
+ }
5086
+ yield* parseSseStream(response.body);
5087
+ }
5088
+ };
4880
5089
  }
4881
5090
 
4882
- // src/providers/groq/text-generation.service.ts
4883
- var STRUCTURED_OUTPUT_TEMPERATURE = 0.1;
4884
- var TextGenerationService = class {
4885
- constructor(httpClient, cache = new CacheManager(), defaultModel = DEFAULT_MODELS.TEXT) {
4886
- this.httpClient = httpClient;
4887
- this.cache = cache;
4888
- this.defaultModel = defaultModel;
5091
+ // src/providers/openai-compatible/openai-compatible.provider.ts
5092
+ var DEFAULT_TIMEOUT_MS = 3e4;
5093
+ var STRUCTURED_TEMPERATURE2 = 0.1;
5094
+ var OpenAiCompatibleProvider = class {
5095
+ constructor(config) {
5096
+ this.config = config;
5097
+ if (!config.baseUrl && !config.endpointUrl) {
5098
+ throw new AIError(
5099
+ "MISSING_CONFIG" /* MISSING_CONFIG */,
5100
+ "OpenAiCompatibleProvider requires baseUrl or endpointUrl"
5101
+ );
5102
+ }
5103
+ this.adapter = createOpenAiCompatibleAdapter({
5104
+ baseUrl: config.baseUrl,
5105
+ endpointUrl: config.endpointUrl,
5106
+ apiKey: config.apiKey,
5107
+ timeoutMs: config.timeoutMs ?? DEFAULT_TIMEOUT_MS
5108
+ });
5109
+ this.tiers = mergeTierOverrides(
5110
+ config.model ? { fast: config.model, balanced: config.model, powerful: config.model } : {},
5111
+ config.models
5112
+ );
4889
5113
  }
4890
- httpClient;
4891
- cache;
4892
- defaultModel;
4893
- id = "groq-text-generation";
4894
- name = "Groq Text Generation";
5114
+ config;
5115
+ id = "openai-compatible";
5116
+ name = "OpenAI-compatible";
4895
5117
  capabilities = ["text"];
5118
+ adapter;
5119
+ tiers;
4896
5120
  jsonParser = new JsonResponseParser();
4897
- generateCompletion(prompt, options = {}) {
4898
- const model = options.model ?? this.defaultModel;
4899
- return this.executeWithCache(
4900
- {
4901
- type: CACHE_KEY_TYPE_COMPLETION,
5121
+ resolveModel(tier) {
5122
+ return resolveModel(this.tiers, tier, this.id);
5123
+ }
5124
+ async healthCheck() {
5125
+ return this.config.apiKey && (this.config.baseUrl || this.config.endpointUrl) ? "healthy" : "unavailable";
5126
+ }
5127
+ async estimateCost() {
5128
+ return 0;
5129
+ }
5130
+ async generate(request) {
5131
+ const model = this.modelFor(request);
5132
+ const outcome = await this.consume(this.toAdapterRequest(model, request));
5133
+ if (outcome.text.length === 0) {
5134
+ throw this.noContentError(model);
5135
+ }
5136
+ return {
5137
+ text: outcome.text,
5138
+ usage: outcome.usage,
5139
+ model,
5140
+ providerId: this.id,
5141
+ finishReason: "stop"
5142
+ };
5143
+ }
5144
+ async generateStructured(request) {
5145
+ const model = this.modelFor(request);
5146
+ const messages = this.toWireMessages(request.messages, request.systemPrompt);
5147
+ const lastUserIndex = findLastIndex4(messages, (m) => m.role === "user");
5148
+ if (lastUserIndex === -1) {
5149
+ throw new AIError(
5150
+ "UNKNOWN_ERROR" /* UNKNOWN_ERROR */,
5151
+ "generateStructured requires at least one user message"
5152
+ );
5153
+ }
5154
+ const structuredPrompt = new StructuredPromptBuilder(
5155
+ messages[lastUserIndex]?.content ?? ""
5156
+ ).withSchema(request.schema).build();
5157
+ messages[lastUserIndex] = { role: "user", content: structuredPrompt };
5158
+ const outcome = await this.consume({
5159
+ externalModelId: model,
5160
+ messages,
5161
+ temperature: STRUCTURED_TEMPERATURE2,
5162
+ signal: request.signal
5163
+ });
5164
+ if (outcome.text.length === 0) {
5165
+ throw this.noContentError(model);
5166
+ }
5167
+ return {
5168
+ value: this.jsonParser.parse(outcome.text),
5169
+ result: {
5170
+ text: outcome.text,
5171
+ usage: outcome.usage,
5172
+ model,
5173
+ providerId: this.id,
5174
+ finishReason: "stop"
5175
+ }
5176
+ };
5177
+ }
5178
+ async stream(request, callbacks) {
5179
+ const model = this.modelFor(request);
5180
+ try {
5181
+ const outcome = await this.consume(this.toAdapterRequest(model, request), (text) => {
5182
+ callbacks.onChunk?.(text);
5183
+ });
5184
+ if (outcome.text.length === 0) {
5185
+ throw this.noContentError(model);
5186
+ }
5187
+ const result = {
5188
+ text: outcome.text,
5189
+ usage: outcome.usage,
5190
+ model,
5191
+ providerId: this.id,
5192
+ finishReason: "stop"
5193
+ };
5194
+ callbacks.onComplete?.(result);
5195
+ return result;
5196
+ } catch (error) {
5197
+ callbacks.onError?.(error instanceof Error ? error : new Error(String(error)));
5198
+ throw error;
5199
+ }
5200
+ }
5201
+ /* ---------- internals ---------- */
5202
+ /** Drain one adapter stream to its end. `onChunk`, when given, sees answer
5203
+ * deltas as they arrive — the streaming surface is this same drain. */
5204
+ async consume(req, onChunk) {
5205
+ let text = "";
5206
+ let usage;
5207
+ for await (const event of this.adapter.stream(req)) {
5208
+ if (event.type === "delta" && event.channel === "answer") {
5209
+ text += event.text;
5210
+ onChunk?.(event.text);
5211
+ } else if (event.type === "usage") {
5212
+ usage = event.usage;
5213
+ }
5214
+ }
5215
+ return { text, usage };
5216
+ }
5217
+ modelFor(request) {
5218
+ return request.model ?? this.resolveModel(request.tier ?? "balanced");
5219
+ }
5220
+ noContentError(model) {
5221
+ return new AIError(
5222
+ "SERVER_ERROR" /* SERVER_ERROR */,
5223
+ "No content generated from the OpenAI-compatible endpoint",
5224
+ void 0,
5225
+ { model }
5226
+ );
5227
+ }
5228
+ toAdapterRequest(model, request) {
5229
+ return {
5230
+ externalModelId: model,
5231
+ messages: this.toWireMessages(request.messages, request.systemPrompt),
5232
+ ...request.temperature !== void 0 ? { temperature: request.temperature } : {},
5233
+ ...request.maxTokens !== void 0 ? { maxOutputTokens: request.maxTokens } : {},
5234
+ ...request.topP !== void 0 ? { topP: request.topP } : {},
5235
+ ...request.signal ? { signal: request.signal } : {}
5236
+ };
5237
+ }
5238
+ /** Neutral messages onto the adapter's shape; systemPrompt becomes the
5239
+ * leading system message (in-band, the OpenAI way). */
5240
+ toWireMessages(messages, systemPrompt) {
5241
+ const wire = [];
5242
+ if (systemPrompt && systemPrompt.length > 0) {
5243
+ wire.push({ role: "system", content: systemPrompt });
5244
+ }
5245
+ for (const message of messages) {
5246
+ wire.push({ role: message.role, content: message.content });
5247
+ }
5248
+ return wire;
5249
+ }
5250
+ };
5251
+ function createOpenAiCompatibleProvider(config) {
5252
+ return new OpenAiCompatibleProvider(config);
5253
+ }
5254
+ function findLastIndex4(items, predicate) {
5255
+ for (let index = items.length - 1; index >= 0; index -= 1) {
5256
+ if (predicate(items[index])) return index;
5257
+ }
5258
+ return -1;
5259
+ }
5260
+
5261
+ // src/providers/workers-ai/workers-ai.entities.ts
5262
+ var WorkersAIError = class extends Error {
5263
+ status;
5264
+ constructor(message, status) {
5265
+ super(message);
5266
+ this.name = "WorkersAIError";
5267
+ this.status = status;
5268
+ }
5269
+ };
5270
+
5271
+ // src/providers/workers-ai/workers-ai.text.ts
5272
+ var WORKERS_AI_LLM_MODEL = "@cf/meta/llama-3.3-70b-instruct-fp8-fast";
5273
+ var WORKERS_AI_TEXT_TIMEOUT_MS = 25e3;
5274
+ function workersAIConfigured(env) {
5275
+ return typeof env.AI?.run === "function";
5276
+ }
5277
+ function requireAi(env) {
5278
+ const ai = env.AI;
5279
+ if (!ai || typeof ai.run !== "function") throw new WorkersAIError("Workers AI is not configured");
5280
+ return ai;
5281
+ }
5282
+ async function runWorkersAIText(env, messages, opts = {}) {
5283
+ const ai = requireAi(env);
5284
+ const model = opts.model ?? WORKERS_AI_LLM_MODEL;
5285
+ let result;
5286
+ try {
5287
+ result = await Promise.race([
5288
+ ai.run(model, {
5289
+ messages: messages.map((m) => ({ role: m.role, content: m.content })),
5290
+ max_tokens: opts.maxTokens ?? 900,
5291
+ temperature: opts.temperature ?? 0.7
5292
+ }),
5293
+ new Promise(
5294
+ (_, reject) => setTimeout(() => reject(new WorkersAIError("Workers AI timed out")), WORKERS_AI_TEXT_TIMEOUT_MS)
5295
+ )
5296
+ ]);
5297
+ } catch (err) {
5298
+ throw err instanceof WorkersAIError ? err : new WorkersAIError(err instanceof Error ? err.message : "Workers AI request failed");
5299
+ }
5300
+ const response = result ?? {};
5301
+ let text = response.response;
5302
+ if (typeof text !== "string" || text.length === 0) {
5303
+ text = response.choices?.[0]?.message?.content;
5304
+ }
5305
+ if (typeof text !== "string" || text.length === 0) {
5306
+ throw new WorkersAIError("Workers AI returned no text");
5307
+ }
5308
+ return text;
5309
+ }
5310
+ async function* streamWorkersAIText(env, messages, opts = {}) {
5311
+ const ai = requireAi(env);
5312
+ const model = opts.model ?? WORKERS_AI_LLM_MODEL;
5313
+ let body;
5314
+ try {
5315
+ body = await Promise.race([
5316
+ ai.run(model, {
5317
+ messages: messages.map((m) => ({ role: m.role, content: m.content })),
5318
+ stream: true,
5319
+ max_tokens: opts.maxTokens ?? 600,
5320
+ temperature: opts.temperature ?? 0.7
5321
+ }),
5322
+ new Promise(
5323
+ (_, reject) => setTimeout(() => reject(new WorkersAIError("Workers AI timed out")), WORKERS_AI_TEXT_TIMEOUT_MS)
5324
+ )
5325
+ ]);
5326
+ } catch (err) {
5327
+ throw err instanceof WorkersAIError ? err : new WorkersAIError(err instanceof Error ? err.message : "Workers AI request failed");
5328
+ }
5329
+ const stream = body;
5330
+ const getReader = typeof stream === "object" && stream !== null && "getReader" in stream && typeof stream.getReader === "function" ? stream.getReader : void 0;
5331
+ if (!getReader) throw new WorkersAIError("Workers AI stream was not readable");
5332
+ const reader = getReader.call(stream);
5333
+ const decoder = new TextDecoder();
5334
+ let buffer = "";
5335
+ try {
5336
+ for (; ; ) {
5337
+ const { done, value } = await reader.read();
5338
+ if (done) break;
5339
+ buffer += decoder.decode(value, { stream: true });
5340
+ let newlineAt;
5341
+ while ((newlineAt = buffer.indexOf("\n")) !== -1) {
5342
+ let line = buffer.slice(0, newlineAt);
5343
+ buffer = buffer.slice(newlineAt + 1);
5344
+ if (line.endsWith("\r")) line = line.slice(0, -1);
5345
+ if (line.startsWith(":") || line.trim() === "") continue;
5346
+ if (!line.startsWith("data: ")) continue;
5347
+ const payload = line.slice(6).trim();
5348
+ if (payload === "[DONE]") return;
5349
+ try {
5350
+ const parsed = JSON.parse(payload);
5351
+ if (typeof parsed.response === "string" && parsed.response.length > 0) yield parsed.response;
5352
+ } catch {
5353
+ buffer = `${line}
5354
+ ${buffer}`;
5355
+ break;
5356
+ }
5357
+ }
5358
+ }
5359
+ } finally {
5360
+ reader.releaseLock();
5361
+ }
5362
+ }
5363
+
5364
+ // src/providers/workers-ai/workers-ai.provider.ts
5365
+ var WORKERS_AI_TIERS = {
5366
+ fast: "@cf/meta/llama-3.1-8b-instruct-fp8-fast",
5367
+ balanced: WORKERS_AI_LLM_MODEL,
5368
+ powerful: WORKERS_AI_LLM_MODEL
5369
+ };
5370
+ var WorkersAIProvider = class {
5371
+ id = "workers-ai";
5372
+ name = "Cloudflare Workers AI";
5373
+ capabilities = ["text"];
5374
+ binding;
5375
+ tiers;
5376
+ jsonParser = new JsonResponseParser();
5377
+ constructor(config) {
5378
+ this.binding = config.binding;
5379
+ this.tiers = mergeTierOverrides(WORKERS_AI_TIERS, config.models);
5380
+ }
5381
+ resolveModel(tier) {
5382
+ return resolveModel(this.tiers, tier, this.id);
5383
+ }
5384
+ async healthCheck() {
5385
+ return typeof this.binding?.run === "function" ? "healthy" : "unavailable";
5386
+ }
5387
+ /** Workers AI is allocation-priced (neurons per deployment), not per
5388
+ * request — there is no per-call cost to estimate. */
5389
+ async estimateCost(_request) {
5390
+ return 0;
5391
+ }
5392
+ async generate(request) {
5393
+ const model = request.model ?? this.resolveModel(request.tier ?? "balanced");
5394
+ const messages = withSystemPrompt(request);
5395
+ try {
5396
+ const text = await runWorkersAIText(
5397
+ { AI: this.binding },
5398
+ messages,
5399
+ {
5400
+ model,
5401
+ maxTokens: request.maxTokens,
5402
+ temperature: request.temperature
5403
+ }
5404
+ );
5405
+ return { text, model, providerId: this.id, finishReason: "stop" };
5406
+ } catch (error) {
5407
+ throw toAIError(error);
5408
+ }
5409
+ }
5410
+ async generateStructured(request) {
5411
+ const messages = [...request.messages];
5412
+ const lastUserIndex = findLastIndex5(messages, (m) => m.role === "user");
5413
+ if (lastUserIndex === -1) {
5414
+ throw new AIError("UNKNOWN_ERROR" /* UNKNOWN_ERROR */, "generateStructured requires at least one user message");
5415
+ }
5416
+ messages[lastUserIndex] = {
5417
+ role: "user",
5418
+ content: new StructuredPromptBuilder(messages[lastUserIndex]?.content ?? "").withSchema(request.schema).build()
5419
+ };
5420
+ const result = await this.generate({ ...request, messages, temperature: 0.1 });
5421
+ return { value: this.jsonParser.parse(result.text), result };
5422
+ }
5423
+ async stream(request, callbacks) {
5424
+ const model = request.model ?? this.resolveModel(request.tier ?? "balanced");
5425
+ const messages = withSystemPrompt(request);
5426
+ let text = "";
5427
+ try {
5428
+ for await (const chunk of streamWorkersAIText({ AI: this.binding }, messages, {
5429
+ model,
5430
+ maxTokens: request.maxTokens,
5431
+ temperature: request.temperature
5432
+ })) {
5433
+ text += chunk;
5434
+ callbacks.onChunk?.(chunk);
5435
+ }
5436
+ } catch (error) {
5437
+ const aiError = toAIError(error);
5438
+ callbacks.onError?.(aiError);
5439
+ throw aiError;
5440
+ }
5441
+ const result = { text, model, providerId: this.id, finishReason: "stop" };
5442
+ callbacks.onComplete?.(result);
5443
+ return result;
5444
+ }
5445
+ };
5446
+ function withSystemPrompt(request) {
5447
+ if (request.systemPrompt && request.systemPrompt.length > 0) {
5448
+ return [
5449
+ { role: "system", content: request.systemPrompt },
5450
+ ...request.messages.filter((m) => m.role !== "system")
5451
+ ];
5452
+ }
5453
+ return request.messages;
5454
+ }
5455
+ function toAIError(error) {
5456
+ if (error instanceof AIError) return error;
5457
+ const message = error instanceof Error ? error.message : "Workers AI request failed";
5458
+ const type = error instanceof WorkersAIError && error.message.includes("not configured") ? "INVALID_API_KEY" /* INVALID_API_KEY */ : "SERVER_ERROR" /* SERVER_ERROR */;
5459
+ return new AIError(type, message, error instanceof Error ? error : void 0);
5460
+ }
5461
+ function findLastIndex5(items, predicate) {
5462
+ for (let i = items.length - 1; i >= 0; i--) {
5463
+ const item = items[i];
5464
+ if (item !== void 0 && predicate(item)) return i;
5465
+ }
5466
+ return -1;
5467
+ }
5468
+ function createWorkersAIProvider(config) {
5469
+ return new WorkersAIProvider(config);
5470
+ }
5471
+
5472
+ // src/providers/factory/create-ai.ts
5473
+ function createAI(config = {}) {
5474
+ const factory = new ProviderFactory({
5475
+ priority: config.priority,
5476
+ fallbackEnabled: config.fallbackEnabled,
5477
+ defaultTier: config.defaultTier
5478
+ });
5479
+ if (config.anthropic?.apiKey) {
5480
+ factory.register(new AnthropicProvider(config.anthropic));
5481
+ }
5482
+ if (config.groq?.apiKey) {
5483
+ factory.register(new GroqProvider(config.groq));
5484
+ }
5485
+ if (config.gemini?.apiKey) {
5486
+ factory.register(new GeminiProvider({ ...config.gemini, enabled: true }));
5487
+ }
5488
+ if (config.fal?.apiKey) {
5489
+ factory.register(new FalProvider({ ...config.fal, enabled: true }));
5490
+ }
5491
+ if (config.pruna?.apiKey) {
5492
+ factory.register(new PrunaProvider({ ...config.pruna, enabled: true }));
5493
+ }
5494
+ const openaiCompatible = config.openaiCompatible;
5495
+ if (openaiCompatible?.apiKey && (openaiCompatible.baseUrl || openaiCompatible.endpointUrl)) {
5496
+ factory.register(
5497
+ new OpenAiCompatibleProvider({ ...openaiCompatible, apiKey: openaiCompatible.apiKey })
5498
+ );
5499
+ }
5500
+ if (config.workersAI?.binding) {
5501
+ factory.register(new WorkersAIProvider(config.workersAI));
5502
+ }
5503
+ return factory;
5504
+ }
5505
+
5506
+ // src/providers/groq/groq-messages.ts
5507
+ var createUserMessage = (content) => ({
5508
+ role: "user",
5509
+ content
5510
+ });
5511
+ var createAssistantMessage = (content) => ({
5512
+ role: "assistant",
5513
+ content
5514
+ });
5515
+ var createSystemMessage = (content) => ({
5516
+ role: "system",
5517
+ content
5518
+ });
5519
+ var createMessage = (role, content) => ({
5520
+ role,
5521
+ content
5522
+ });
5523
+ var promptToMessages = (prompt) => [
5524
+ createUserMessage(prompt)
5525
+ ];
5526
+ var extractTextFromMessages = (messages) => messages.map((m) => m.content).join("\n\n");
5527
+ var formatMessagesForDisplay = (messages) => messages.map((m) => `[${m.role.toUpperCase()}]: ${m.content}`).join("\n\n---\n\n");
5528
+
5529
+ // src/providers/groq/groq-structured-json.ts
5530
+ async function requestStructuredJson(client, request) {
5531
+ const chatRequest = {
5532
+ model: request.model,
5533
+ messages: [createUserMessage(request.instructions)],
5534
+ temperature: request.temperature,
5535
+ max_tokens: request.maxTokens,
5536
+ response_format: { type: "json_object" },
5537
+ ...request.reasoningEffort !== void 0 ? { reasoning_effort: request.reasoningEffort } : {}
5538
+ };
5539
+ const response = await client.postChatCompletion(chatRequest);
5540
+ const content = response.choices[0]?.message?.content ?? "";
5541
+ const candidate = extractJsonCandidate(content);
5542
+ try {
5543
+ return JSON.parse(candidate);
5544
+ } catch (err) {
5545
+ throw new GroqError(
5546
+ "UNKNOWN_ERROR" /* UNKNOWN_ERROR */,
5547
+ "model response contained no parsable json object",
5548
+ err instanceof Error ? err : void 0
5549
+ );
5550
+ }
5551
+ }
5552
+
5553
+ // src/providers/groq/text-generation.service.ts
5554
+ var STRUCTURED_OUTPUT_TEMPERATURE = 0.1;
5555
+ var TextGenerationService = class {
5556
+ constructor(httpClient, cache = new CacheManager(), defaultModel = DEFAULT_MODELS.TEXT) {
5557
+ this.httpClient = httpClient;
5558
+ this.cache = cache;
5559
+ this.defaultModel = defaultModel;
5560
+ }
5561
+ httpClient;
5562
+ cache;
5563
+ defaultModel;
5564
+ id = "groq-text-generation";
5565
+ name = "Groq Text Generation";
5566
+ capabilities = ["text"];
5567
+ jsonParser = new JsonResponseParser();
5568
+ generateCompletion(prompt, options = {}) {
5569
+ const model = options.model ?? this.defaultModel;
5570
+ return this.executeWithCache(
5571
+ {
5572
+ type: CACHE_KEY_TYPE_COMPLETION,
4902
5573
  model,
4903
5574
  prompt,
4904
5575
  config: options.generationConfig
@@ -5198,6 +5869,7 @@ var ChatService = class {
5198
5869
  if (messages.length === 0) return null;
5199
5870
  const last = messages[messages.length - 1];
5200
5871
  const first = messages[0];
5872
+ if (!last || !first) return null;
5201
5873
  const companionId = String(first.metadata?.["companionId"] ?? "unknown");
5202
5874
  const companionName = String(first.metadata?.["companionName"] ?? "Unknown");
5203
5875
  return {
@@ -5341,6 +6013,211 @@ function resetProvider() {
5341
6013
  container = null;
5342
6014
  }
5343
6015
 
6016
+ // src/providers/workers-ai/workers-ai.speech.ts
6017
+ var WORKERS_AI_SPEECH_TIMEOUT_MS = 45e3;
6018
+ async function readAll(stream) {
6019
+ const reader = stream.getReader();
6020
+ const chunks = [];
6021
+ try {
6022
+ for (; ; ) {
6023
+ const { done, value } = await reader.read();
6024
+ if (done) break;
6025
+ if (value) chunks.push(value);
6026
+ }
6027
+ } finally {
6028
+ reader.releaseLock();
6029
+ }
6030
+ const combined = new Uint8Array(chunks.reduce((acc, chunk) => acc + chunk.length, 0));
6031
+ let offset = 0;
6032
+ for (const chunk of chunks) {
6033
+ combined.set(chunk, offset);
6034
+ offset += chunk.length;
6035
+ }
6036
+ return combined;
6037
+ }
6038
+ function base64ToBytes2(base64) {
6039
+ const binary = atob(base64);
6040
+ const bytes = new Uint8Array(binary.length);
6041
+ for (let i = 0; i < binary.length; i += 1) bytes[i] = binary.charCodeAt(i);
6042
+ return bytes;
6043
+ }
6044
+ async function speakWithWorkersAI(env, engine, text) {
6045
+ const ai = env.AI;
6046
+ if (!ai || typeof ai.run !== "function") throw new WorkersAIError("Workers AI is not configured");
6047
+ const input = engine.langParam ? { prompt: text, lang: engine.langParam } : { text };
6048
+ let result;
6049
+ try {
6050
+ result = await Promise.race([
6051
+ ai.run(engine.model, input),
6052
+ new Promise(
6053
+ (_, reject) => setTimeout(() => reject(new WorkersAIError("The speech engine timed out")), WORKERS_AI_SPEECH_TIMEOUT_MS)
6054
+ )
6055
+ ]);
6056
+ } catch (err) {
6057
+ throw err instanceof WorkersAIError ? err : new WorkersAIError(err instanceof Error ? err.message : "Workers AI speech request failed");
6058
+ }
6059
+ if (result instanceof ReadableStream) {
6060
+ const bytes = await readAll(result);
6061
+ if (bytes.length === 0) throw new WorkersAIError("The speech engine returned no audio");
6062
+ return { bytes };
6063
+ }
6064
+ const audio = result ?? {};
6065
+ if (typeof audio.audio === "string" && audio.audio.length > 0) {
6066
+ return { bytes: base64ToBytes2(audio.audio) };
6067
+ }
6068
+ throw new WorkersAIError("The speech engine returned no audio");
6069
+ }
6070
+
6071
+ // src/providers/fal/queue/fal-queue.entities.ts
6072
+ var FalQueueError = class extends Error {
6073
+ status;
6074
+ constructor(message, status) {
6075
+ super(message);
6076
+ this.name = "FalQueueError";
6077
+ this.status = status;
6078
+ }
6079
+ };
6080
+ var FAL_ASPECT_IMAGE_SIZES = {
6081
+ "1:1": "square_hd",
6082
+ "16:9": "landscape_16_9",
6083
+ "9:16": "portrait_16_9"
6084
+ };
6085
+
6086
+ // src/providers/fal/queue/fal-queue.client.ts
6087
+ var FAL_QUEUE_BASE = "https://queue.fal.run";
6088
+ var FAL_SUBMIT_TIMEOUT_MS = 15e3;
6089
+ var FAL_POLL_TIMEOUT_MS = 1e4;
6090
+ function falQueueConfigured(env) {
6091
+ return Boolean(env.FAL_KEY);
6092
+ }
6093
+ async function falFetch(env, url, timeoutMs, body) {
6094
+ if (!falQueueConfigured(env)) throw new FalQueueError("fal.ai is not configured");
6095
+ const request = async () => fetch(url, {
6096
+ method: body !== void 0 ? "POST" : "GET",
6097
+ headers: {
6098
+ Authorization: `Key ${env.FAL_KEY}`,
6099
+ ...body !== void 0 ? { "Content-Type": "application/json" } : {}
6100
+ },
6101
+ body: body !== void 0 ? JSON.stringify(body) : void 0,
6102
+ signal: AbortSignal.timeout(timeoutMs)
6103
+ });
6104
+ let res;
6105
+ try {
6106
+ res = await request();
6107
+ if (res.status >= 500) res = await request();
6108
+ } catch (err) {
6109
+ throw new FalQueueError(err instanceof Error ? err.message : "fal.ai request failed");
6110
+ }
6111
+ if (!res.ok) {
6112
+ const text = await res.text().catch(() => "");
6113
+ throw new FalQueueError(`fal ${res.status}: ${text.slice(0, 200)}`, res.status);
6114
+ }
6115
+ return await res.json();
6116
+ }
6117
+ function createFalQueue(env) {
6118
+ return {
6119
+ async submit(model, input) {
6120
+ const res = await falFetch(env, `${FAL_QUEUE_BASE}/${model}`, FAL_SUBMIT_TIMEOUT_MS, input);
6121
+ return { requestId: res.request_id, statusUrl: res.status_url, responseUrl: res.response_url };
6122
+ },
6123
+ async checkStatus(statusUrl) {
6124
+ const res = await falFetch(env, statusUrl, FAL_POLL_TIMEOUT_MS);
6125
+ return {
6126
+ status: res.status,
6127
+ queuePosition: res.queue_position,
6128
+ logs: res.logs?.map((l) => l.message ?? "").filter(Boolean)
6129
+ };
6130
+ },
6131
+ fetchResult(responseUrl) {
6132
+ return falFetch(env, responseUrl, FAL_POLL_TIMEOUT_MS);
6133
+ }
6134
+ };
6135
+ }
6136
+
6137
+ // src/providers/fal/queue/fal-wire.ts
6138
+ function buildFalImageInput(model, input) {
6139
+ const base = { prompt: input.prompt, num_images: input.numImages };
6140
+ if (model.includes("flux")) {
6141
+ const imageSize = input.aspectRatio ? FAL_ASPECT_IMAGE_SIZES[input.aspectRatio] : void 0;
6142
+ if (imageSize) base.image_size = imageSize;
6143
+ }
6144
+ return base;
6145
+ }
6146
+ function parseFalImageOutput(raw) {
6147
+ const obj = raw ?? {};
6148
+ const entries = Array.isArray(obj.images) ? obj.images : obj.image && typeof obj.image === "object" ? [obj.image] : [];
6149
+ const imageUrls = entries.map((e) => typeof e.url === "string" ? e.url : "").filter((url) => url.length > 0);
6150
+ const first = entries.find((e) => typeof e.width === "number" || typeof e.height === "number");
6151
+ return {
6152
+ imageUrls,
6153
+ ...typeof obj.seed === "number" ? { seed: obj.seed } : {},
6154
+ ...typeof first?.width === "number" ? { width: first.width } : {},
6155
+ ...typeof first?.height === "number" ? { height: first.height } : {}
6156
+ };
6157
+ }
6158
+ function buildFalVideoInput(spec, input) {
6159
+ const base = { prompt: input.prompt };
6160
+ if (spec.durationFormat !== "none" && typeof input.durationSeconds === "number") {
6161
+ base.duration = spec.durationFormat === "s-suffix" ? `${input.durationSeconds}s` : String(input.durationSeconds);
6162
+ }
6163
+ if (spec.aspectRatios && input.aspectRatio) base.aspect_ratio = input.aspectRatio;
6164
+ if (spec.resolutions && input.resolution) base.resolution = input.resolution;
6165
+ if (input.imageUrl) base.image_url = input.imageUrl;
6166
+ if (spec.supportsNegativePrompt && input.negativePrompt) base.negative_prompt = input.negativePrompt;
6167
+ if (spec.generateAudio) base.generate_audio = true;
6168
+ return base;
6169
+ }
6170
+ function parseFalVideoOutput(raw) {
6171
+ const obj = raw ?? {};
6172
+ const video = obj.video && typeof obj.video === "object" ? obj.video : void 0;
6173
+ if (!video || typeof video.url !== "string" || video.url.length === 0) return null;
6174
+ return {
6175
+ videoUrl: video.url,
6176
+ ...typeof video.content_type === "string" ? { contentType: video.content_type } : {},
6177
+ ...typeof video.file_size === "number" ? { fileSize: video.file_size } : {}
6178
+ };
6179
+ }
6180
+ function buildFalTagPrompt(prompt) {
6181
+ return prompt.toLowerCase().split(/[,\s]+/).filter((word) => word.length > 3).slice(0, 5).join(", ");
6182
+ }
6183
+ function buildFalAudioLyrics(input) {
6184
+ if (input.instrumental) return "[inst]";
6185
+ const lyrics = input.lyrics?.trim();
6186
+ if (!lyrics) return void 0;
6187
+ if (lyrics.includes("[verse]") || lyrics.includes("[chorus]")) return lyrics;
6188
+ return `[verse]
6189
+ ${lyrics}
6190
+ [/verse]`;
6191
+ }
6192
+ function buildFalAudioInput(spec, input) {
6193
+ const tags = buildFalTagPrompt(input.prompt);
6194
+ const base = { prompt: tags, duration: input.durationSeconds };
6195
+ if (spec.supportsLyrics) {
6196
+ const lyrics = buildFalAudioLyrics(input);
6197
+ if (lyrics) base.lyrics = lyrics;
6198
+ }
6199
+ if (spec.supportsInstrumental && input.instrumental !== void 0) {
6200
+ base.instrumental = input.instrumental;
6201
+ }
6202
+ return base;
6203
+ }
6204
+ function parseFalAudioOutput(raw) {
6205
+ const obj = raw ?? {};
6206
+ const nested = (key) => {
6207
+ const value = obj[key];
6208
+ if (!value || typeof value !== "object") return void 0;
6209
+ const url = value.url;
6210
+ return typeof url === "string" && url.length > 0 ? url : void 0;
6211
+ };
6212
+ const flat = (key) => {
6213
+ const value = obj[key];
6214
+ return typeof value === "string" && value.length > 0 ? value : void 0;
6215
+ };
6216
+ const audioUrl = nested("audio_url") ?? nested("audio_file") ?? nested("audio") ?? flat("audio_url") ?? flat("audio");
6217
+ if (!audioUrl) return null;
6218
+ return { audioUrl };
6219
+ }
6220
+
5344
6221
  // src/providers/embeddings/openai-embeddings.ts
5345
6222
  var DEFAULT_EMBED_TIMEOUT_MS = 6e4;
5346
6223
  function createOpenAiEmbeddingsAdapter(config) {
@@ -5528,150 +6405,6 @@ function createOpenAiImageAdapter(config) {
5528
6405
  };
5529
6406
  }
5530
6407
 
5531
- // src/providers/adapters/openai-compatible.adapter.ts
5532
- function toWireMessage(message) {
5533
- if (message.role === "tool") {
5534
- return { role: "tool", content: message.content, tool_call_id: message.toolCallId ?? "" };
5535
- }
5536
- if (message.role === "assistant" && message.toolCalls && message.toolCalls.length > 0) {
5537
- return {
5538
- role: "assistant",
5539
- content: message.content,
5540
- tool_calls: message.toolCalls.map((call) => ({
5541
- id: call.id,
5542
- type: "function",
5543
- function: { name: call.name, arguments: call.arguments }
5544
- }))
5545
- };
5546
- }
5547
- if (message.role === "user") return { role: "user", content: message.content };
5548
- return { role: message.role, content: message.content };
5549
- }
5550
- function mergeImages(messages, images) {
5551
- if (images.length === 0) return [...messages];
5552
- const parts = images.map((image) => ({
5553
- type: "image_url",
5554
- image_url: { url: `data:${image.mimeType};base64,${image.dataBase64}` }
5555
- }));
5556
- for (let index = messages.length - 1; index >= 0; index -= 1) {
5557
- const message = messages[index];
5558
- if (message.role !== "user") continue;
5559
- const textParts = typeof message.content === "string" ? message.content.length > 0 ? [{ type: "text", text: message.content }] : [] : message.content;
5560
- return [
5561
- ...messages.slice(0, index),
5562
- { role: "user", content: [...textParts, ...parts] },
5563
- ...messages.slice(index + 1)
5564
- ];
5565
- }
5566
- return [...messages, { role: "user", content: [...parts] }];
5567
- }
5568
- async function extractErrorDetail(response) {
5569
- try {
5570
- const body = await response.json();
5571
- return body.error;
5572
- } catch {
5573
- return void 0;
5574
- }
5575
- }
5576
- async function* parseSseEvent2(rawEvent, toolCallAccumulator) {
5577
- for (const line of rawEvent.split("\n")) {
5578
- if (!line.startsWith("data:")) continue;
5579
- const payload = line.slice(5).trim();
5580
- if (payload.length === 0 || payload === "[DONE]") continue;
5581
- let chunk;
5582
- try {
5583
- chunk = JSON.parse(payload);
5584
- } catch {
5585
- continue;
5586
- }
5587
- const delta = chunk.choices?.[0]?.delta;
5588
- if (delta?.reasoning) {
5589
- yield { type: "delta", channel: "reasoning", text: delta.reasoning };
5590
- }
5591
- if (delta?.content) {
5592
- yield { type: "delta", channel: "answer", text: delta.content };
5593
- }
5594
- if (delta?.tool_calls) {
5595
- for (const toolCallDelta of delta.tool_calls) toolCallAccumulator.push(toolCallDelta);
5596
- }
5597
- if (chunk.usage) {
5598
- yield {
5599
- type: "usage",
5600
- usage: {
5601
- inputTokens: chunk.usage.prompt_tokens ?? 0,
5602
- outputTokens: chunk.usage.completion_tokens ?? 0,
5603
- reasoningTokens: chunk.usage.completion_tokens_details?.reasoning_tokens
5604
- }
5605
- };
5606
- }
5607
- }
5608
- }
5609
- async function* parseSseStream(body) {
5610
- const reader = body.getReader();
5611
- const decoder = new TextDecoder();
5612
- const toolCallAccumulator = new ToolCallAccumulator();
5613
- let buffer = "";
5614
- try {
5615
- while (true) {
5616
- const { done, value } = await reader.read();
5617
- if (done) break;
5618
- buffer += decoder.decode(value, { stream: true });
5619
- let separator = buffer.indexOf("\n\n");
5620
- while (separator !== -1) {
5621
- const rawEvent = buffer.slice(0, separator);
5622
- buffer = buffer.slice(separator + 2);
5623
- separator = buffer.indexOf("\n\n");
5624
- yield* parseSseEvent2(rawEvent, toolCallAccumulator);
5625
- }
5626
- }
5627
- } catch (err) {
5628
- throw errorFromUnknown(err);
5629
- } finally {
5630
- reader.releaseLock();
5631
- }
5632
- const calls = toolCallAccumulator.finish();
5633
- if (calls.length > 0) yield { type: "tool_calls", calls };
5634
- }
5635
- function createOpenAiCompatibleAdapter(config) {
5636
- return {
5637
- async *stream(req) {
5638
- const timeout = AbortSignal.timeout(config.timeoutMs);
5639
- const signal = req.signal ? AbortSignal.any([req.signal, timeout]) : timeout;
5640
- let response;
5641
- try {
5642
- response = await fetch(`${config.baseUrl}/chat/completions`, {
5643
- method: "POST",
5644
- headers: {
5645
- Authorization: `Bearer ${config.apiKey}`,
5646
- "Content-Type": "application/json"
5647
- },
5648
- body: JSON.stringify({
5649
- model: req.externalModelId,
5650
- messages: mergeImages(req.messages.map(toWireMessage), req.images ?? []),
5651
- temperature: req.temperature,
5652
- max_completion_tokens: req.maxOutputTokens,
5653
- top_p: req.topP,
5654
- stream: true,
5655
- stream_options: { include_usage: true },
5656
- ...req.tools && req.tools.length > 0 ? {
5657
- tools: req.tools.map((tool) => ({ type: "function", function: tool })),
5658
- tool_choice: "auto"
5659
- } : {}
5660
- }),
5661
- signal
5662
- });
5663
- } catch (err) {
5664
- throw errorFromUnknown(err);
5665
- }
5666
- if (!response.ok || !response.body) {
5667
- const detail = await extractErrorDetail(response);
5668
- throw errorFromStatus(response.status, detail?.message, detail);
5669
- }
5670
- yield* parseSseStream(response.body);
5671
- }
5672
- };
5673
- }
5674
-
5675
6408
  // src/providers/adapters/anthropic.adapter.ts
5676
6409
  var ANTHROPIC_VERSION = "2023-06-01";
5677
6410
  var ERROR_TYPE_TO_CODE = {
@@ -5692,7 +6425,7 @@ function mergeImages2(messages, images) {
5692
6425
  }));
5693
6426
  for (let index = messages.length - 1; index >= 0; index -= 1) {
5694
6427
  const message = messages[index];
5695
- if (message.role !== "user") continue;
6428
+ if (!message || message.role !== "user") continue;
5696
6429
  const textBlocks = typeof message.content === "string" ? message.content.length > 0 ? [{ type: "text", text: message.content }] : [] : message.content;
5697
6430
  return [
5698
6431
  ...messages.slice(0, index),
@@ -5808,7 +6541,7 @@ function createAnthropicAdapter(config) {
5808
6541
  model: req.externalModelId,
5809
6542
  messages,
5810
6543
  ...system.length > 0 ? { system } : {},
5811
- max_tokens: req.maxOutputTokens,
6544
+ max_tokens: req.maxOutputTokens ?? 4096,
5812
6545
  stream: true
5813
6546
  }),
5814
6547
  signal
@@ -5825,6 +6558,170 @@ function createAnthropicAdapter(config) {
5825
6558
  };
5826
6559
  }
5827
6560
 
6561
+ // src/providers/adapters/adapter-resolver.ts
6562
+ var ADAPTER_TIMEOUT_MS = 6e4;
6563
+ var ADAPTER_MEDIA_TIMEOUT_MS = 12e4;
6564
+ function resolveApiKey(env, apiKeyEnv) {
6565
+ const value = env[apiKeyEnv];
6566
+ if (typeof value !== "string" || value.trim().length === 0) {
6567
+ throw new AiError("missing_key");
6568
+ }
6569
+ return value.trim();
6570
+ }
6571
+ function apiKeyAvailable(env, apiKeyEnv) {
6572
+ try {
6573
+ resolveApiKey(env, apiKeyEnv);
6574
+ return true;
6575
+ } catch {
6576
+ return false;
6577
+ }
6578
+ }
6579
+ function normalizeBaseUrl(url) {
6580
+ return url.replace(/\/$/, "");
6581
+ }
6582
+ function openAiApiKey(binding, capability) {
6583
+ if (binding.adapter !== "openai-compatible") {
6584
+ throw new AiError(
6585
+ "model_unavailable",
6586
+ `Adapter kind does not support ${capability}: ${binding.adapter}`
6587
+ );
6588
+ }
6589
+ return binding.apiKey;
6590
+ }
6591
+ function chatAdapterFor(binding, timing = {}) {
6592
+ const timeoutMs = timing.timeoutMs ?? ADAPTER_TIMEOUT_MS;
6593
+ if (binding.adapter === "openai-compatible") {
6594
+ return createOpenAiCompatibleAdapter({
6595
+ baseUrl: normalizeBaseUrl(binding.baseUrl),
6596
+ apiKey: binding.apiKey,
6597
+ timeoutMs
6598
+ });
6599
+ }
6600
+ if (binding.adapter === "anthropic") {
6601
+ return createAnthropicAdapter({
6602
+ baseUrl: normalizeBaseUrl(binding.baseUrl),
6603
+ apiKey: binding.apiKey,
6604
+ timeoutMs
6605
+ });
6606
+ }
6607
+ throw new AiError("model_unavailable", `Unsupported adapter kind: ${binding.adapter}`);
6608
+ }
6609
+ function imageAdapterFor(binding, timing = {}) {
6610
+ return createOpenAiImageAdapter({
6611
+ baseUrl: normalizeBaseUrl(binding.baseUrl),
6612
+ apiKey: openAiApiKey(binding, "images"),
6613
+ timeoutMs: timing.mediaTimeoutMs ?? ADAPTER_MEDIA_TIMEOUT_MS
6614
+ });
6615
+ }
6616
+ function embeddingsAdapterFor(binding, timing = {}) {
6617
+ return createOpenAiEmbeddingsAdapter({
6618
+ baseUrl: normalizeBaseUrl(binding.baseUrl),
6619
+ apiKey: openAiApiKey(binding, "embeddings"),
6620
+ timeoutMs: timing.timeoutMs ?? ADAPTER_TIMEOUT_MS
6621
+ });
6622
+ }
6623
+ function audioTranscriptionAdapterFor(binding, timing = {}) {
6624
+ return createOpenAiAudioTranscriptionAdapter({
6625
+ baseUrl: normalizeBaseUrl(binding.baseUrl),
6626
+ apiKey: openAiApiKey(binding, "audio transcription"),
6627
+ timeoutMs: timing.timeoutMs ?? ADAPTER_TIMEOUT_MS
6628
+ });
6629
+ }
6630
+ function speechAdapterFor(binding, timing = {}) {
6631
+ return createOpenAiSpeechAdapter({
6632
+ baseUrl: normalizeBaseUrl(binding.baseUrl),
6633
+ apiKey: openAiApiKey(binding, "speech synthesis"),
6634
+ timeoutMs: timing.mediaTimeoutMs ?? ADAPTER_MEDIA_TIMEOUT_MS
6635
+ });
6636
+ }
6637
+ function createAdapterResolver(env, timing = {}) {
6638
+ const toProviderBinding = (binding) => ({
6639
+ adapter: binding.adapter,
6640
+ baseUrl: binding.baseUrl,
6641
+ apiKey: resolveApiKey(env, binding.apiKeyEnv)
6642
+ });
6643
+ return {
6644
+ chat: (binding) => chatAdapterFor(toProviderBinding(binding), timing),
6645
+ image: (binding) => imageAdapterFor(toProviderBinding(binding), timing),
6646
+ embeddings: (binding) => embeddingsAdapterFor(toProviderBinding(binding), timing),
6647
+ audioTranscription: (binding) => audioTranscriptionAdapterFor(toProviderBinding(binding), timing),
6648
+ speech: (binding) => speechAdapterFor(toProviderBinding(binding), timing),
6649
+ available: (binding) => apiKeyAvailable(env, binding.apiKeyEnv)
6650
+ };
6651
+ }
6652
+
6653
+ // src/providers/execution/run-conversation.ts
6654
+ async function* runConversation(input) {
6655
+ const tools = input.tools && input.tools.length > 0 ? { tools: input.tools } : {};
6656
+ const images = input.images && input.images.length > 0 ? { images: input.images } : {};
6657
+ for (const [index, attempt] of input.attempts.entries()) {
6658
+ let answer = "";
6659
+ let reasoning = "";
6660
+ let usage = null;
6661
+ let toolCalls = [];
6662
+ const startedAt = Date.now();
6663
+ const context = buildContext(input.history, input.systemPrompt, {
6664
+ contextWindow: attempt.model.contextWindow,
6665
+ // No explicit output reservation → keep the whole window for input.
6666
+ maxOutputTokens: attempt.settings.maxOutputTokens ?? 0
6667
+ });
6668
+ yield { type: "attempt", index };
6669
+ try {
6670
+ for await (const event of attempt.adapter.stream({
6671
+ externalModelId: attempt.model.externalModelId,
6672
+ messages: context,
6673
+ temperature: attempt.settings.temperature,
6674
+ maxOutputTokens: attempt.settings.maxOutputTokens,
6675
+ topP: attempt.settings.topP,
6676
+ ...tools,
6677
+ ...images,
6678
+ signal: input.signal
6679
+ })) {
6680
+ if (event.type === "delta") {
6681
+ if (event.channel === "reasoning") reasoning += event.text;
6682
+ else answer += event.text;
6683
+ yield { type: "delta", channel: event.channel, text: event.text };
6684
+ } else if (event.type === "tool_calls") {
6685
+ toolCalls = [...event.calls];
6686
+ } else {
6687
+ usage = event.usage;
6688
+ }
6689
+ }
6690
+ } catch (err) {
6691
+ const result2 = {
6692
+ answer,
6693
+ reasoning,
6694
+ usage,
6695
+ toolCalls,
6696
+ durationMs: Date.now() - startedAt
6697
+ };
6698
+ if (input.signal?.aborted === true) {
6699
+ yield { type: "stopped", result: result2 };
6700
+ return;
6701
+ }
6702
+ if (index + 1 < input.attempts.length && canFailover(err, answer.length + reasoning.length)) {
6703
+ continue;
6704
+ }
6705
+ const aiError = err instanceof AiError ? err : errorFromUnknown(err);
6706
+ yield { type: "failed", code: aiError.code, message: aiError.userMessage, result: result2 };
6707
+ return;
6708
+ }
6709
+ const result = {
6710
+ answer,
6711
+ reasoning,
6712
+ usage,
6713
+ toolCalls,
6714
+ durationMs: Date.now() - startedAt
6715
+ };
6716
+ if (toolCalls.length > 0 && input.tools && input.tools.length > 0) {
6717
+ yield { type: "tool_calls", result };
6718
+ return;
6719
+ }
6720
+ yield { type: "done", result };
6721
+ return;
6722
+ }
6723
+ }
6724
+
5828
6725
  // src/content/domain/calculations/ContentCreditCost.ts
5829
6726
  var CREDIT_COSTS = {
5830
6727
  "text-to-image": { standard: 1, hd: 2, "4k": 4 },
@@ -6459,7 +7356,7 @@ async function batchRequest(requests, options = {}) {
6459
7356
  errors.push(result.reason);
6460
7357
  if (stopOnError) {
6461
7358
  throw new Error(
6462
- `Batch request failed: ${errors[0].message}`
7359
+ `Batch request failed: ${errors[0]?.message}`
6463
7360
  );
6464
7361
  }
6465
7362
  }
@@ -6921,7 +7818,7 @@ function safeJSONParse(text, fallback, onError) {
6921
7818
  }
6922
7819
  if (error instanceof SyntaxError) {
6923
7820
  const positionMatch = error.message.match(/position (\d+)/);
6924
- const position = positionMatch ? parseInt(positionMatch[1], 10) : -1;
7821
+ const position = positionMatch ? parseInt(positionMatch[1] ?? "", 10) : -1;
6925
7822
  if (position > 0) {
6926
7823
  const context = text.substring(Math.max(0, position - 50), position + 50);
6927
7824
  onError?.(`JSON parse error at position ${position}: "${context}"`);
@@ -7419,6 +8316,6 @@ function buildVideoRequest(data) {
7419
8316
  }
7420
8317
  }
7421
8318
 
7422
- export { ABTestService, AIContentService, AIError, AIErrorType, ANTHROPIC_API_VERSION, ANTHROPIC_DEFAULT_MAX_TOKENS, ANTHROPIC_MODELS, ANTHROPIC_TIERS, API_ENDPOINTS, ASPECT_RATIO_OPTIONS, ASPECT_RATIO_VALUES, AiError, AnalysisService, AnthropicHttpTransport, AnthropicProvider, AnthropicRequestBuilder, AuthenticationError, BaseProvider as BaseAIProvider, BaseProvider, BlogService, CHUNK_MAX_CHARS, CHUNK_OVERLAP_CHARS, CHUNK_TEXT_LIMIT, CODE_MESSAGES, CONTENT_WIZARD_STEPS, CacheManager, CancellationError, ChatIdGenerator, ChatService, CompositeExecutor, ContentCalendarService, DEFAULT_AI_IDENTITY, DEFAULT_API_KEY_RULE, DEFAULT_BASE_URL, DEFAULT_EMBED_BATCH_SIZE, DEFAULT_ERROR_CLASSIFICATION_RULE, DEFAULT_GENERATION, DEFAULT_GENERATION_CONFIG, DEFAULT_HTTP_STATUS_RULE, DEFAULT_MODELS, DEFAULT_PROMPT_STRATEGY, DEFAULT_RETRY_BACKOFF_MULTIPLIER, DEFAULT_RETRY_INITIAL_DELAY_MS, DEFAULT_RETRY_JITTER_FACTOR, DEFAULT_RETRY_MAX_ATTEMPTS, DEFAULT_RETRY_MAX_DELAY_MS, DEFAULT_TASK_POLICIES, DebounceManager, DedupeExecutor, FalProvider, GEMINI_TIERS, GENERATION_BOUNDS, CREDIT_COSTS as GENERATION_COSTS, GROQ_MODELS, GROQ_TIERS, GeminiProvider, GenerationExecutor, GenerationFailedError, GroqChatRequestBuilder, GroqConfigStore, GroqError, AIErrorType as GroqErrorType, GroqHttpClient, GroqHttpTransport, GroqProvider, IMAGE_B64_LIMIT, IMAGE_QUALITIES, IMAGE_QUALITY_OPTIONS, IMAGE_QUALITY_VALUES, IMAGE_SIZES, IMAGE_WIZARD_STEPS, ImagePromptService, JSON_CODE_BLOCK_PATTERN, JSON_OBJECT_PATTERN, JsonResponseParser, MEMORY_MAX_ITEMS, MEMORY_TOTAL_LIMIT, MICROS_PER_UNIT, MOTION_OPTIONS, MediaGenerationService, PromptBuilder, ProviderError, ProviderFactory, ProviderQuotaExhaustedError, ProviderUnavailableError, PrunaProvider, QueueExecutor, RESEARCH_MAX_OUTPUT_TOKENS, RESEARCH_QUERY_MAX, RESEARCH_QUERY_MIN, RESEARCH_REPORT_MIN_CHARS, RESEARCH_SNIPPET_MAX_CHARS, RESEARCH_TEMPERATURE, RequestDeduplicator, RequestPriority, RequestQueue, RetryExecutor, RetryManager, SEARCH_MAX_RESULTS, SPEECH_INPUT_MAX, SPEECH_VOICES, SSE_BRIDGE_EVENTS, SSE_DONE_MARKER, SSE_PREFIX, STATUS_TO_CODE, STYLE_PRESETS, SYSTEM_PROMPT_VERSION, SeoService, SocialService, StreamingResponseParser, StructuredPromptBuilder, TEXT_EXTRACT_PER_FILE_LIMIT, TEXT_EXTRACT_TOTAL_LIMIT, TEXT_TIERS, TIMEOUTS, TRANSIENT_ERROR_CODES, TextGenerationService, ThrottleManager, TimeoutError, ToolCallAccumulator, USER_FRIENDLY_ERROR_MESSAGES, VIDEO_DURATION_OPTIONS, VIDEO_DURATION_OPTIONS_SECONDS, VIDEO_QUALITY_OPTIONS, VIDEO_QUALITY_VALUES, VIDEO_WIZARD_STEPS, ValidationError2 as ValidationError, VideoScriptService, accumulateReport, aiErrorMessage, asObjectArray, asRecord, asStringArray, asTrimmedOptionalString, batchEmbed, batchRequest, buildCacheKey, buildContentRequest, buildContext, buildHttpStatusFallback, buildImageRequest, buildResearchMessages, buildSystemPrompt, buildUsageData, buildVideoRequest, cacheManager, calculateCacheHitRate, calculateContentCreditCost as calculateCreditCost, calculateExponentialBackoff, calculateTrailingDelay, canFailover, canStartTask, capText, chatIdGenerator, chunkText, clampScore, classifyHttpStatus, computeCostMicros, configureProvider, consumeAnthropicStream, consumeGroqStream, cosineSimilarity, countChanges, createAI, createAnthropicAdapter, createAssistantMessage, createFalProvider, createGeminiProvider, createGroqProvider, createMessage, createOpenAiAudioTranscriptionAdapter, createOpenAiCompatibleAdapter, createOpenAiEmbeddingsAdapter, createOpenAiImageAdapter, createOpenAiSpeechAdapter, createProviderFactory, createPrunaProvider, createSseResponse, createSystemMessage, createUserMessage, debounce, debounceManager, defaultEffortForModel, defaultSseDeltaExtractor, deriveSnippetTitle, describeAIErrorType, diffLines, dotProduct, errorFromStatus, errorFromUnknown, estimateMessageTokens, estimateTokens, extractContentFromSsePayload, extractErrorMessageFromJsonBody, extractErrorMessageFromResponse, extractJSON, extractJsonArray, extractJsonCandidate, extractJsonFromCodeFence, extractJsonObject, extractSseDataPayload, extractText, extractTextFromMessages, extractTokenUsage, failoverCandidate, findTopKSimilar, formatCost, formatErrorForLogging, formatMessagesForDisplay, formatSseEvent, generateImageThenVideo, generate as generatePruna, generateRandomBase36Suffix, generateWithPruna, getDimensionsForAspectRatio, getProvider, getRecommendedQuality, isAuthErrorType, isMediaProvider, isRetryableErrorType, isSseDataLine, isSseDoneMarker, isTextLike, isTextProvider, isTextTier, isTransientError, isValidApiKey, isValidJSON, isWithinWindow, magnitude, mapAnthropicStopReason, mergeTierOverrides, messageFormatter, nextAttemptDelayMs, normalizeGenerationSettings, normalizeSources, normalizeSseChunk, oneOfCatalog, parseAIResponse, parseSseEvent, planTaskFailure, pollForResult, promptToMessages, readSseResponse, repairCommonJsonDefects, replayFirst, requestDeduplicator, requestQueue, requestStructuredJson, resetProvider, resolveModel, resolveUserFriendlyErrorMessage, resolveUserFriendlyMessage, retryManager, safeJSONParse, searchWeb, selectLeastRecentlyUsedKey, selectMemories, shouldCallOnLeadingEdge, splitMarkdown, splitSseLines, sseStreamResponse, submitPrediction, throttle, throttleManager, toGroqMessages, uploadImage, validateApiKey, validateToolArgs, withRetry };
8319
+ export { ABTestService, ADAPTER_MEDIA_TIMEOUT_MS, ADAPTER_TIMEOUT_MS, AIContentService, AIError, AIErrorType, AI_ERROR_STATUS, ANTHROPIC_API_VERSION, ANTHROPIC_DEFAULT_MAX_TOKENS, ANTHROPIC_MODELS, ANTHROPIC_TIERS, API_ENDPOINTS, ASPECT_RATIO_OPTIONS, ASPECT_RATIO_VALUES, AiError, AnalysisService, AnthropicHttpTransport, AnthropicProvider, AnthropicRequestBuilder, AuthenticationError, BaseProvider as BaseAIProvider, BaseProvider, BlogService, CHUNK_MAX_CHARS, CHUNK_OVERLAP_CHARS, CHUNK_TEXT_LIMIT, CODE_MESSAGES, CONTENT_WIZARD_STEPS, CacheManager, CancellationError, ChatIdGenerator, ChatService, CompositeExecutor, ContentCalendarService, DEFAULT_AI_IDENTITY, DEFAULT_API_KEY_RULE, DEFAULT_BASE_URL, DEFAULT_EMBED_BATCH_SIZE, DEFAULT_ERROR_CLASSIFICATION_RULE, DEFAULT_GENERATION, DEFAULT_GENERATION_CONFIG, DEFAULT_HTTP_STATUS_RULE, DEFAULT_MODELS, DEFAULT_PROMPT_STRATEGY, DEFAULT_RETRY_BACKOFF_MULTIPLIER, DEFAULT_RETRY_INITIAL_DELAY_MS, DEFAULT_RETRY_JITTER_FACTOR, DEFAULT_RETRY_MAX_ATTEMPTS, DEFAULT_RETRY_MAX_DELAY_MS, DEFAULT_TASK_POLICIES, DebounceManager, DedupeExecutor, FAL_ASPECT_IMAGE_SIZES, FalProvider, FalQueueError, GEMINI_TIERS, GENERATION_BOUNDS, CREDIT_COSTS as GENERATION_COSTS, GROQ_MODELS, GROQ_TIERS, GeminiProvider, GenerationExecutor, GenerationFailedError, GroqChatRequestBuilder, GroqConfigStore, GroqError, AIErrorType as GroqErrorType, GroqHttpClient, GroqHttpTransport, GroqProvider, IMAGE_B64_LIMIT, IMAGE_QUALITIES, IMAGE_QUALITY_OPTIONS, IMAGE_QUALITY_VALUES, IMAGE_SIZES, IMAGE_WIZARD_STEPS, ImagePromptService, JSON_CODE_BLOCK_PATTERN, JSON_OBJECT_PATTERN, JsonResponseParser, MEMORY_MAX_ITEMS, MEMORY_TOTAL_LIMIT, MICROS_PER_UNIT, MOTION_OPTIONS, MediaGenerationService, OpenAiCompatibleProvider, PromptBuilder, ProviderError, ProviderFactory, ProviderQuotaExhaustedError, ProviderUnavailableError, PrunaProvider, QueueExecutor, RESEARCH_MAX_OUTPUT_TOKENS, RESEARCH_QUERY_MAX, RESEARCH_QUERY_MIN, RESEARCH_REPORT_MIN_CHARS, RESEARCH_SNIPPET_MAX_CHARS, RESEARCH_TEMPERATURE, RequestDeduplicator, RequestPriority, RequestQueue, RetryExecutor, RetryManager, SEARCH_MAX_RESULTS, SPEECH_INPUT_MAX, SPEECH_VOICES, SSE_BRIDGE_EVENTS, SSE_DONE_MARKER, SSE_PREFIX, STATUS_TO_CODE, STYLE_PRESETS, SYSTEM_PROMPT_VERSION, SeoService, SocialService, StreamingResponseParser, StructuredPromptBuilder, TEXT_EXTRACT_PER_FILE_LIMIT, TEXT_EXTRACT_TOTAL_LIMIT, TEXT_TIERS, TIMEOUTS, TRANSIENT_ERROR_CODES, TextGenerationService, ThrottleManager, TimeoutError, ToolCallAccumulator, USER_FRIENDLY_ERROR_MESSAGES, VIDEO_DURATION_OPTIONS, VIDEO_DURATION_OPTIONS_SECONDS, VIDEO_QUALITY_OPTIONS, VIDEO_QUALITY_VALUES, VIDEO_WIZARD_STEPS, ValidationError2 as ValidationError, VideoScriptService, WORKERS_AI_LLM_MODEL, WORKERS_AI_TIERS, WorkersAIError, WorkersAIProvider, accumulateReport, aiErrorMessage, aiErrorStatus, apiKeyAvailable, asObjectArray, asRecord, asStringArray, asTrimmedOptionalString, audioTranscriptionAdapterFor, batchEmbed, batchRequest, buildCacheKey, buildContentRequest, buildContext, buildFalAudioInput, buildFalAudioLyrics, buildFalImageInput, buildFalTagPrompt, buildFalVideoInput, buildHttpStatusFallback, buildImageRequest, buildResearchMessages, buildSystemPrompt, buildUsageDailySeries, buildUsageData, buildVideoRequest, cacheManager, calculateCacheHitRate, calculateContentCreditCost as calculateCreditCost, calculateExponentialBackoff, calculateTrailingDelay, canFailover, canStartTask, capText, chatAdapterFor, chatIdGenerator, chunkText, clampScore, classifyHttpStatus, computeCostMicros, configureProvider, consumeAnthropicStream, consumeGroqStream, cosineSimilarity, countChanges, createAI, createAdapterResolver, createAnthropicAdapter, createAssistantMessage, createFalProvider, createFalQueue, createGeminiProvider, createGroqProvider, createMessage, createOpenAiAudioTranscriptionAdapter, createOpenAiCompatibleAdapter, createOpenAiCompatibleProvider, createOpenAiEmbeddingsAdapter, createOpenAiImageAdapter, createOpenAiSpeechAdapter, createProviderFactory, createPrunaProvider, createRemoteChatClient, createSseResponse, createSystemMessage, createUserMessage, createWorkersAIProvider, debounce, debounceManager, defaultEffortForModel, defaultSseDeltaExtractor, deriveSnippetTitle, describeAIErrorType, diffLines, dotProduct, embeddingsAdapterFor, errorFromStatus, errorFromUnknown, errorRatePct, estimateMessageTokens, estimateTokens, extractContentFromSsePayload, extractErrorMessageFromJsonBody, extractErrorMessageFromResponse, extractJSON, extractJsonArray, extractJsonCandidate, extractJsonFromCodeFence, extractJsonObject, extractSseDataPayload, extractText, extractTextFromMessages, extractTokenUsage, failoverCandidate, falQueueConfigured, findTopKSimilar, formatCost, formatErrorForLogging, formatMessagesForDisplay, formatSseEvent, generateImageThenVideo, generate as generatePruna, generateRandomBase36Suffix, generateWithPruna, getDimensionsForAspectRatio, getProvider, getRecommendedQuality, imageAdapterFor, isAuthErrorType, isMediaProvider, isRetryableErrorType, isSseDataLine, isSseDoneMarker, isTextLike, isTextProvider, isTextTier, isTransientError, isValidApiKey, isValidJSON, isWithinWindow, magnitude, mapAnthropicStopReason, mergeTierOverrides, messageFormatter, nextAttemptDelayMs, normalizeGenerationSettings, normalizeSources, normalizeSseChunk, oneOfCatalog, parseAIResponse, parseFalAudioOutput, parseFalImageOutput, parseFalVideoOutput, parseSseEvent, planTaskFailure, pollForResult, promptToMessages, readSseResponse, repairCommonJsonDefects, replayFirst, requestDeduplicator, requestQueue, requestStructuredJson, resetProvider, resolveApiKey, resolveModel, resolveUserFriendlyErrorMessage, resolveUserFriendlyMessage, retryManager, runConversation, runWorkersAIText, safeJSONParse, searchWeb, selectLeastRecentlyUsedKey, selectMemories, shouldCallOnLeadingEdge, speakWithWorkersAI, speechAdapterFor, splitMarkdown, splitSseLines, sseStreamResponse, streamWorkersAIText, submitPrediction, throttle, throttleManager, toGroqMessages, uploadImage, usageTrendOf, usageWindowTotals, validateApiKey, validateToolArgs, withRetry, workersAIConfigured };
7423
8320
  //# sourceMappingURL=index.js.map
7424
8321
  //# sourceMappingURL=index.js.map