@pellux/goodvibes-sdk 0.34.0 → 0.34.2

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 (195) hide show
  1. package/dist/contracts/artifacts/operator-contract.json +1 -1
  2. package/dist/events/workflows.d.ts +4 -0
  3. package/dist/events/workflows.d.ts.map +1 -1
  4. package/dist/platform/agents/message-bus-core.d.ts +11 -0
  5. package/dist/platform/agents/message-bus-core.d.ts.map +1 -1
  6. package/dist/platform/agents/message-bus-core.js +28 -21
  7. package/dist/platform/agents/orchestrator-runner.d.ts.map +1 -1
  8. package/dist/platform/agents/orchestrator-runner.js +6 -4
  9. package/dist/platform/agents/wrfc-config.d.ts.map +1 -1
  10. package/dist/platform/agents/wrfc-config.js +16 -10
  11. package/dist/platform/agents/wrfc-controller.d.ts +26 -0
  12. package/dist/platform/agents/wrfc-controller.d.ts.map +1 -1
  13. package/dist/platform/agents/wrfc-controller.js +134 -138
  14. package/dist/platform/agents/wrfc-runtime-events.d.ts +1 -0
  15. package/dist/platform/agents/wrfc-runtime-events.d.ts.map +1 -1
  16. package/dist/platform/agents/wrfc-runtime-events.js +4 -1
  17. package/dist/platform/automation/delivery-manager.d.ts.map +1 -1
  18. package/dist/platform/automation/delivery-manager.js +1 -6
  19. package/dist/platform/batch/manager.d.ts.map +1 -1
  20. package/dist/platform/batch/manager.js +1 -3
  21. package/dist/platform/channels/policy-manager.d.ts.map +1 -1
  22. package/dist/platform/channels/policy-manager.js +1 -3
  23. package/dist/platform/channels/reply-pipeline.d.ts.map +1 -1
  24. package/dist/platform/channels/reply-pipeline.js +4 -0
  25. package/dist/platform/config/schema-domain-core.d.ts +1 -1
  26. package/dist/platform/config/schema-domain-core.d.ts.map +1 -1
  27. package/dist/platform/config/schema-domain-core.js +12 -12
  28. package/dist/platform/config/schema-domain-runtime.d.ts +1 -1
  29. package/dist/platform/config/schema-domain-runtime.d.ts.map +1 -1
  30. package/dist/platform/config/schema-domain-runtime.js +19 -18
  31. package/dist/platform/config/schema-domain-surfaces.d.ts +1 -1
  32. package/dist/platform/config/schema-domain-surfaces.d.ts.map +1 -1
  33. package/dist/platform/config/schema-domain-surfaces.js +4 -3
  34. package/dist/platform/config/schema-shared.d.ts +6 -0
  35. package/dist/platform/config/schema-shared.d.ts.map +1 -1
  36. package/dist/platform/config/schema-shared.js +21 -1
  37. package/dist/platform/control-plane/approval-broker.d.ts.map +1 -1
  38. package/dist/platform/control-plane/approval-broker.js +1 -3
  39. package/dist/platform/control-plane/session-broker-state.d.ts.map +1 -1
  40. package/dist/platform/control-plane/session-broker-state.js +1 -3
  41. package/dist/platform/core/compaction-sections.d.ts +3 -1
  42. package/dist/platform/core/compaction-sections.d.ts.map +1 -1
  43. package/dist/platform/core/compaction-sections.js +70 -66
  44. package/dist/platform/core/context-compaction.d.ts.map +1 -1
  45. package/dist/platform/core/context-compaction.js +10 -16
  46. package/dist/platform/core/orchestrator-context-runtime.d.ts +2 -2
  47. package/dist/platform/core/orchestrator-context-runtime.d.ts.map +1 -1
  48. package/dist/platform/core/orchestrator-context-runtime.js +24 -21
  49. package/dist/platform/core/orchestrator-tool-runtime.d.ts.map +1 -1
  50. package/dist/platform/core/orchestrator-tool-runtime.js +2 -1
  51. package/dist/platform/core/session-lineage.d.ts +2 -5
  52. package/dist/platform/core/session-lineage.d.ts.map +1 -1
  53. package/dist/platform/core/session-lineage.js +4 -16
  54. package/dist/platform/core/wrfc-routing.d.ts.map +1 -1
  55. package/dist/platform/core/wrfc-routing.js +1 -3
  56. package/dist/platform/daemon/http/mcp-routes.d.ts.map +1 -1
  57. package/dist/platform/daemon/http/mcp-routes.js +1 -3
  58. package/dist/platform/daemon/http/openai-compatible-routes.d.ts.map +1 -1
  59. package/dist/platform/daemon/http/openai-compatible-routes.js +1 -3
  60. package/dist/platform/daemon/http/runtime-routes.d.ts.map +1 -1
  61. package/dist/platform/daemon/http/runtime-routes.js +1 -3
  62. package/dist/platform/hooks/runners/http.d.ts.map +1 -1
  63. package/dist/platform/hooks/runners/http.js +9 -19
  64. package/dist/platform/integrations/webhooks.js +2 -11
  65. package/dist/platform/knowledge/semantic/self-improvement.d.ts.map +1 -1
  66. package/dist/platform/knowledge/semantic/self-improvement.js +2 -21
  67. package/dist/platform/mcp/client.d.ts.map +1 -1
  68. package/dist/platform/mcp/client.js +1 -3
  69. package/dist/platform/mcp/config.d.ts.map +1 -1
  70. package/dist/platform/mcp/config.js +1 -3
  71. package/dist/platform/media/builtin-generation-providers.d.ts.map +1 -1
  72. package/dist/platform/media/builtin-generation-providers.js +1 -6
  73. package/dist/platform/providers/anthropic-compat.d.ts.map +1 -1
  74. package/dist/platform/providers/anthropic-compat.js +11 -131
  75. package/dist/platform/providers/anthropic-sdk-provider.d.ts.map +1 -1
  76. package/dist/platform/providers/anthropic-sdk-provider.js +8 -42
  77. package/dist/platform/providers/anthropic-sse-assembler.d.ts +80 -0
  78. package/dist/platform/providers/anthropic-sse-assembler.d.ts.map +1 -0
  79. package/dist/platform/providers/anthropic-sse-assembler.js +146 -0
  80. package/dist/platform/providers/anthropic-stream.d.ts +30 -0
  81. package/dist/platform/providers/anthropic-stream.d.ts.map +1 -0
  82. package/dist/platform/providers/anthropic-stream.js +44 -0
  83. package/dist/platform/providers/anthropic-vertex.d.ts.map +1 -1
  84. package/dist/platform/providers/anthropic-vertex.js +1 -3
  85. package/dist/platform/providers/anthropic.d.ts.map +1 -1
  86. package/dist/platform/providers/anthropic.js +15 -151
  87. package/dist/platform/providers/capabilities.d.ts.map +1 -1
  88. package/dist/platform/providers/capabilities.js +11 -7
  89. package/dist/platform/providers/context-discovery.d.ts.map +1 -1
  90. package/dist/platform/providers/context-discovery.js +9 -16
  91. package/dist/platform/providers/context-window-fallback.d.ts +24 -0
  92. package/dist/platform/providers/context-window-fallback.d.ts.map +1 -0
  93. package/dist/platform/providers/context-window-fallback.js +48 -0
  94. package/dist/platform/providers/gemini.d.ts.map +1 -1
  95. package/dist/platform/providers/gemini.js +3 -6
  96. package/dist/platform/providers/json-ttl-cache.d.ts +35 -0
  97. package/dist/platform/providers/json-ttl-cache.d.ts.map +1 -0
  98. package/dist/platform/providers/json-ttl-cache.js +52 -0
  99. package/dist/platform/providers/llama-cpp.d.ts.map +1 -1
  100. package/dist/platform/providers/llama-cpp.js +2 -23
  101. package/dist/platform/providers/lm-studio-helpers.d.ts +1 -1
  102. package/dist/platform/providers/lm-studio-helpers.d.ts.map +1 -1
  103. package/dist/platform/providers/lm-studio-helpers.js +5 -25
  104. package/dist/platform/providers/model-benchmarks.d.ts.map +1 -1
  105. package/dist/platform/providers/model-benchmarks.js +11 -34
  106. package/dist/platform/providers/model-catalog-cache.d.ts.map +1 -1
  107. package/dist/platform/providers/model-catalog-cache.js +15 -38
  108. package/dist/platform/providers/model-catalog-synthetic.d.ts.map +1 -1
  109. package/dist/platform/providers/model-catalog-synthetic.js +3 -2
  110. package/dist/platform/providers/model-catalog.d.ts.map +1 -1
  111. package/dist/platform/providers/model-catalog.js +3 -2
  112. package/dist/platform/providers/model-limits.d.ts.map +1 -1
  113. package/dist/platform/providers/model-limits.js +18 -42
  114. package/dist/platform/providers/ollama.d.ts.map +1 -1
  115. package/dist/platform/providers/ollama.js +3 -24
  116. package/dist/platform/providers/openai-codex.d.ts.map +1 -1
  117. package/dist/platform/providers/openai-codex.js +8 -9
  118. package/dist/platform/providers/openai-compat.d.ts.map +1 -1
  119. package/dist/platform/providers/openai-compat.js +13 -40
  120. package/dist/platform/providers/openai-stream-helpers.d.ts +62 -0
  121. package/dist/platform/providers/openai-stream-helpers.d.ts.map +1 -0
  122. package/dist/platform/providers/openai-stream-helpers.js +73 -0
  123. package/dist/platform/providers/openai.d.ts.map +1 -1
  124. package/dist/platform/providers/openai.js +13 -41
  125. package/dist/platform/providers/provider-error.d.ts +17 -0
  126. package/dist/platform/providers/provider-error.d.ts.map +1 -0
  127. package/dist/platform/providers/provider-error.js +33 -0
  128. package/dist/platform/providers/provider-stop-reason.d.ts +15 -0
  129. package/dist/platform/providers/provider-stop-reason.d.ts.map +1 -0
  130. package/dist/platform/providers/provider-stop-reason.js +15 -0
  131. package/dist/platform/providers/sse-line-buffer.d.ts +13 -0
  132. package/dist/platform/providers/sse-line-buffer.d.ts.map +1 -0
  133. package/dist/platform/providers/sse-line-buffer.js +23 -0
  134. package/dist/platform/providers/tool-formats.d.ts +5 -0
  135. package/dist/platform/providers/tool-formats.d.ts.map +1 -1
  136. package/dist/platform/providers/tool-formats.js +14 -0
  137. package/dist/platform/runtime/bootstrap-runtime-events.d.ts.map +1 -1
  138. package/dist/platform/runtime/bootstrap-runtime-events.js +6 -0
  139. package/dist/platform/runtime/bootstrap-services.d.ts.map +1 -1
  140. package/dist/platform/runtime/bootstrap-services.js +4 -5
  141. package/dist/platform/runtime/emitters/workflows.d.ts +4 -0
  142. package/dist/platform/runtime/emitters/workflows.d.ts.map +1 -1
  143. package/dist/platform/runtime/emitters/workflows.js +3 -0
  144. package/dist/platform/runtime/remote/distributed-runtime-store.d.ts.map +1 -1
  145. package/dist/platform/runtime/remote/distributed-runtime-store.js +1 -3
  146. package/dist/platform/runtime/retention/pruner.js +2 -5
  147. package/dist/platform/runtime/store/helpers/reducers/lifecycle.d.ts.map +1 -1
  148. package/dist/platform/runtime/store/helpers/reducers/lifecycle.js +15 -14
  149. package/dist/platform/runtime/store/helpers/reducers/shared.d.ts +6 -0
  150. package/dist/platform/runtime/store/helpers/reducers/shared.d.ts.map +1 -1
  151. package/dist/platform/runtime/store/helpers/reducers/shared.js +8 -1
  152. package/dist/platform/runtime/telemetry/exporters/queue.d.ts.map +1 -1
  153. package/dist/platform/runtime/telemetry/exporters/queue.js +1 -7
  154. package/dist/platform/runtime/transports/http-helpers.d.ts +0 -1
  155. package/dist/platform/runtime/transports/http-helpers.d.ts.map +1 -1
  156. package/dist/platform/runtime/transports/http-helpers.js +1 -3
  157. package/dist/platform/runtime/ui-read-model-helpers.d.ts +2 -0
  158. package/dist/platform/runtime/ui-read-model-helpers.d.ts.map +1 -1
  159. package/dist/platform/runtime/ui-read-model-helpers.js +8 -0
  160. package/dist/platform/runtime/ui-read-models-core.d.ts.map +1 -1
  161. package/dist/platform/runtime/ui-read-models-core.js +3 -5
  162. package/dist/platform/runtime/ui-read-models-observability-remote.d.ts.map +1 -1
  163. package/dist/platform/runtime/ui-read-models-observability-remote.js +2 -4
  164. package/dist/platform/runtime/ui-read-models-operations.d.ts.map +1 -1
  165. package/dist/platform/runtime/ui-read-models-operations.js +8 -26
  166. package/dist/platform/scheduler/scheduler.d.ts.map +1 -1
  167. package/dist/platform/scheduler/scheduler.js +1 -3
  168. package/dist/platform/tools/agent/index.d.ts +1 -0
  169. package/dist/platform/tools/agent/index.d.ts.map +1 -1
  170. package/dist/platform/tools/agent/index.js +1 -0
  171. package/dist/platform/tools/agent/predicates.d.ts +15 -0
  172. package/dist/platform/tools/agent/predicates.d.ts.map +1 -0
  173. package/dist/platform/tools/agent/predicates.js +15 -0
  174. package/dist/platform/tools/exec/runtime.d.ts.map +1 -1
  175. package/dist/platform/tools/exec/runtime.js +1 -7
  176. package/dist/platform/tools/fetch/runtime.d.ts.map +1 -1
  177. package/dist/platform/tools/fetch/runtime.js +9 -20
  178. package/dist/platform/tools/shared/process-manager.d.ts.map +1 -1
  179. package/dist/platform/tools/shared/process-manager.js +1 -6
  180. package/dist/platform/utils/concurrency.d.ts +1 -0
  181. package/dist/platform/utils/concurrency.d.ts.map +1 -1
  182. package/dist/platform/utils/concurrency.js +1 -0
  183. package/dist/platform/utils/error-display.d.ts +9 -0
  184. package/dist/platform/utils/error-display.d.ts.map +1 -1
  185. package/dist/platform/utils/error-display.js +11 -0
  186. package/dist/platform/utils/fetch-with-timeout.d.ts +22 -2
  187. package/dist/platform/utils/fetch-with-timeout.d.ts.map +1 -1
  188. package/dist/platform/utils/fetch-with-timeout.js +33 -15
  189. package/dist/platform/utils/record-coerce.d.ts +5 -0
  190. package/dist/platform/utils/record-coerce.d.ts.map +1 -1
  191. package/dist/platform/utils/record-coerce.js +7 -0
  192. package/dist/platform/utils/retry.d.ts.map +1 -1
  193. package/dist/platform/utils/retry.js +2 -4
  194. package/dist/platform/version.js +1 -1
  195. package/package.json +9 -9
@@ -2,10 +2,10 @@ import { dirname, join } from 'node:path';
2
2
  import { mkdirSync, readFileSync, writeFileSync } from 'node:fs';
3
3
  import { logger } from '../utils/logger.js';
4
4
  import { summarizeError } from '../utils/error-display.js';
5
- import { instrumentedFetch } from '../utils/fetch-with-timeout.js';
5
+ import { TTL_24H_MS, isTtlCacheStale, validateTtlCacheEnvelope } from './json-ttl-cache.js';
6
+ import { instrumentedFetch, fetchWithTimeout } from '../utils/fetch-with-timeout.js';
6
7
  const OPENROUTER_MODELS_URL = 'https://openrouter.ai/api/v1/models';
7
8
  const FETCH_TIMEOUT_MS = 15_000;
8
- const CACHE_TTL_MS = 86_400_000;
9
9
  const DEFAULT_TOKEN_LIMITS = {
10
10
  maxOutputTokens: 8192,
11
11
  maxToolResultTokens: 50_000,
@@ -16,22 +16,7 @@ export function getModelLimitsCachePath(cacheDir) {
16
16
  return join(cacheDir, 'model-limits.json');
17
17
  }
18
18
  function validateModelLimitsCache(value) {
19
- if (!value || typeof value !== 'object' || Array.isArray(value)) {
20
- return { cache: null, reason: 'root value is not an object' };
21
- }
22
- const parsed = value;
23
- if (parsed.version !== 1)
24
- return { cache: null, reason: 'unsupported cache version' };
25
- if (typeof parsed.fetchedAt !== 'number' || !Number.isFinite(parsed.fetchedAt)) {
26
- return { cache: null, reason: 'fetchedAt must be a finite number' };
27
- }
28
- if (typeof parsed.ttlMs !== 'number' || !Number.isFinite(parsed.ttlMs)) {
29
- return { cache: null, reason: 'ttlMs must be a finite number' };
30
- }
31
- if (!parsed.models || typeof parsed.models !== 'object' || Array.isArray(parsed.models)) {
32
- return { cache: null, reason: 'models must be an object' };
33
- }
34
- return { cache: parsed };
19
+ return validateTtlCacheEnvelope(value, 'models', 'object');
35
20
  }
36
21
  function loadCachedLimits(cachePath) {
37
22
  try {
@@ -65,34 +50,25 @@ function saveCachedLimits(cache, cachePath) {
65
50
  }
66
51
  }
67
52
  function isCacheStale(cache) {
68
- return Date.now() - cache.fetchedAt > cache.ttlMs;
53
+ return isTtlCacheStale(cache);
69
54
  }
70
55
  async function fetchOpenRouterModels() {
71
- const controller = new AbortController();
72
- const timer = setTimeout(() => controller.abort(), FETCH_TIMEOUT_MS);
73
- timer.unref?.();
74
- try {
75
- const response = await instrumentedFetch(OPENROUTER_MODELS_URL, {
76
- signal: controller.signal,
77
- headers: { Accept: 'application/json' },
78
- });
79
- if (!response.ok) {
80
- throw new Error(`OpenRouter API returned ${response.status}`);
81
- }
82
- const json = await response.json();
83
- const map = new Map();
84
- if (Array.isArray(json.data)) {
85
- for (const model of json.data) {
86
- if (typeof model.id === 'string') {
87
- map.set(model.id, model);
88
- }
56
+ const response = await fetchWithTimeout(OPENROUTER_MODELS_URL, {
57
+ headers: { Accept: 'application/json' },
58
+ }, FETCH_TIMEOUT_MS, instrumentedFetch);
59
+ if (!response.ok) {
60
+ throw new Error(`OpenRouter API returned ${response.status}`);
61
+ }
62
+ const json = await response.json();
63
+ const map = new Map();
64
+ if (Array.isArray(json.data)) {
65
+ for (const model of json.data) {
66
+ if (typeof model.id === 'string') {
67
+ map.set(model.id, model);
89
68
  }
90
69
  }
91
- return map;
92
- }
93
- finally {
94
- clearTimeout(timer);
95
70
  }
71
+ return map;
96
72
  }
97
73
  function getModelStem(modelId) {
98
74
  return modelId
@@ -239,7 +215,7 @@ export class ModelLimitsService {
239
215
  const newCache = {
240
216
  version: 1,
241
217
  fetchedAt: Date.now(),
242
- ttlMs: CACHE_TTL_MS,
218
+ ttlMs: TTL_24H_MS,
243
219
  models,
244
220
  };
245
221
  saveCachedLimits(newCache, this.options.cachePath);
@@ -1 +1 @@
1
- {"version":3,"file":"ollama.d.ts","sourceRoot":"","sources":["../../../src/platform/providers/ollama.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AAC5D,OAAO,KAAK,EACV,WAAW,EACX,YAAY,EAEZ,WAAW,EAEX,wBAAwB,EACxB,uBAAuB,EAEvB,uBAAuB,EACvB,2BAA2B,EAC5B,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAAwB,KAAK,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AAMpF,KAAK,WAAW,GAAG,CACjB,KAAK,EAAE,WAAW,GAAG,GAAG,EACxB,IAAI,CAAC,EAAE,WAAW,KACf,OAAO,CAAC,QAAQ,CAAC,CAAC;AAqBvB,MAAM,WAAW,qBAAsB,SAAQ,mBAAmB;IAChE,WAAW,CAAC,EAAE,WAAW,GAAG,SAAS,CAAC;IACtC,cAAc,CAAC,EAAE,WAAW,GAAG,SAAS,CAAC;CAC1C;AAED,qBAAa,cAAe,YAAW,WAAW;IAChD,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC;IAC1B,QAAQ,CAAC,YAAY,CAAC,EAAE,OAAO,CAAC,kBAAkB,CAAC,GAAG,SAAS,CAAC;IAEhE,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;IACjC,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAS;IACtC,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAS;IACvC,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAS;IACxC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAc;IAC1C,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAc;IAC7C,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAoB;gBAEvC,IAAI,EAAE,qBAAqB;IAYvC,YAAY,IAAI,OAAO;IAIjB,IAAI,CAAC,MAAM,EAAE,WAAW,GAAG,OAAO,CAAC,YAAY,CAAC;IAmBhD,KAAK,CAAC,OAAO,EAAE,wBAAwB,GAAG,OAAO,CAAC,uBAAuB,CAAC;IAuC1E,eAAe,CAAC,IAAI,EAAE,2BAA2B,GAAG,OAAO,CAAC,uBAAuB,CAAC;YA4C5E,mBAAmB;CAmGlC"}
1
+ {"version":3,"file":"ollama.d.ts","sourceRoot":"","sources":["../../../src/platform/providers/ollama.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AAC5D,OAAO,KAAK,EACV,WAAW,EACX,YAAY,EAEZ,WAAW,EAEX,wBAAwB,EACxB,uBAAuB,EAEvB,uBAAuB,EACvB,2BAA2B,EAC5B,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAAwB,KAAK,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AAOpF,KAAK,WAAW,GAAG,CACjB,KAAK,EAAE,WAAW,GAAG,GAAG,EACxB,IAAI,CAAC,EAAE,WAAW,KACf,OAAO,CAAC,QAAQ,CAAC,CAAC;AAqBvB,MAAM,WAAW,qBAAsB,SAAQ,mBAAmB;IAChE,WAAW,CAAC,EAAE,WAAW,GAAG,SAAS,CAAC;IACtC,cAAc,CAAC,EAAE,WAAW,GAAG,SAAS,CAAC;CAC1C;AAED,qBAAa,cAAe,YAAW,WAAW;IAChD,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC;IAC1B,QAAQ,CAAC,YAAY,CAAC,EAAE,OAAO,CAAC,kBAAkB,CAAC,GAAG,SAAS,CAAC;IAEhE,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;IACjC,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAS;IACtC,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAS;IACvC,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAS;IACxC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAc;IAC1C,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAc;IAC7C,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAoB;gBAEvC,IAAI,EAAE,qBAAqB;IAYvC,YAAY,IAAI,OAAO;IAIjB,IAAI,CAAC,MAAM,EAAE,WAAW,GAAG,OAAO,CAAC,YAAY,CAAC;IAmBhD,KAAK,CAAC,OAAO,EAAE,wBAAwB,GAAG,OAAO,CAAC,uBAAuB,CAAC;IAuC1E,eAAe,CAAC,IAAI,EAAE,2BAA2B,GAAG,OAAO,CAAC,uBAAuB,CAAC;YA4C5E,mBAAmB;CAmGlC"}
@@ -3,7 +3,8 @@ import { instrumentedLlmCall } from '../runtime/llm-observability.js';
3
3
  import { ProviderError } from '../types/errors.js';
4
4
  import { OpenAICompatProvider } from './openai-compat.js';
5
5
  import { toOpenAITools } from './tool-formats.js';
6
- import { summarizeError, toProviderError } from '../utils/error-display.js';
6
+ import { summarizeError } from '../utils/error-display.js';
7
+ import { getErrorStatus, normalizeProviderError } from './provider-error.js';
7
8
  import { mapOllamaStopReason } from './stop-reason-maps.js';
8
9
  import { instrumentedFetch } from '../utils/fetch-with-timeout.js';
9
10
  export class OllamaProvider {
@@ -347,35 +348,13 @@ async function buildHttpError(prefix, response, provider, operation, phase) {
347
348
  }
348
349
  function shouldFallbackFromNative(err) {
349
350
  const status = getErrorStatus(err);
350
- const message = getErrorMessage(err);
351
+ const message = summarizeError(err);
351
352
  if (status === 404 || status === 405 || status === 501)
352
353
  return true;
353
354
  if (status === 400 && /tool|messages|unsupported/i.test(message))
354
355
  return true;
355
356
  return /not implemented|unsupported|unknown endpoint/i.test(message);
356
357
  }
357
- function getErrorStatus(err) {
358
- if (err && typeof err === 'object') {
359
- const record = err;
360
- if (typeof record.status === 'number')
361
- return record.status;
362
- if (typeof record.statusCode === 'number')
363
- return record.statusCode;
364
- }
365
- return undefined;
366
- }
367
- function getErrorMessage(err) {
368
- return summarizeError(err);
369
- }
370
- function normalizeProviderError(err, provider, operation, phase = 'request') {
371
- const status = getErrorStatus(err);
372
- return toProviderError(err, {
373
- ...(status !== undefined ? { statusCode: status } : {}),
374
- provider,
375
- operation,
376
- phase,
377
- });
378
- }
379
358
  function toRecord(value) {
380
359
  return value && typeof value === 'object' ? value : {};
381
360
  }
@@ -1 +1 @@
1
- {"version":3,"file":"openai-codex.d.ts","sourceRoot":"","sources":["../../../src/platform/providers/openai-codex.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,WAAW,EACX,YAAY,EACZ,WAAW,EAGX,uBAAuB,EACvB,2BAA2B,EAC5B,MAAM,gBAAgB,CAAC;AAMxB,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,4BAA4B,CAAC;AA6ItE,wBAAsB,mBAAmB,CACvC,WAAW,EAAE,MAAM,EACnB,MAAM,EAAE,WAAW,GAClB,OAAO,CAAC,YAAY,CAAC,CA8PvB;AAED,qBAAa,mBAAoB,YAAW,WAAW;IAKnD,OAAO,CAAC,QAAQ,CAAC,mBAAmB;IAJtC,QAAQ,CAAC,IAAI,uBAA8B;IAC3C,QAAQ,CAAC,MAAM,EAAE,MAAM,EAAE,CAAM;gBAGZ,mBAAmB,EAAE,IAAI,CAAC,mBAAmB,EAAE,KAAK,GAAG,kBAAkB,GAAG,oBAAoB,CAAC;IAG9G,IAAI,CAAC,MAAM,EAAE,WAAW,GAAG,OAAO,CAAC,YAAY,CAAC;IAgBhD,eAAe,CAAC,IAAI,EAAE,2BAA2B,GAAG,OAAO,CAAC,uBAAuB,CAAC;CAgC3F"}
1
+ {"version":3,"file":"openai-codex.d.ts","sourceRoot":"","sources":["../../../src/platform/providers/openai-codex.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,WAAW,EACX,YAAY,EACZ,WAAW,EAGX,uBAAuB,EACvB,2BAA2B,EAC5B,MAAM,gBAAgB,CAAC;AAMxB,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,4BAA4B,CAAC;AA8ItE,wBAAsB,mBAAmB,CACvC,WAAW,EAAE,MAAM,EACnB,MAAM,EAAE,WAAW,GAClB,OAAO,CAAC,YAAY,CAAC,CA2PvB;AAED,qBAAa,mBAAoB,YAAW,WAAW;IAKnD,OAAO,CAAC,QAAQ,CAAC,mBAAmB;IAJtC,QAAQ,CAAC,IAAI,uBAA8B;IAC3C,QAAQ,CAAC,MAAM,EAAE,MAAM,EAAE,CAAM;gBAGZ,mBAAmB,EAAE,IAAI,CAAC,mBAAmB,EAAE,KAAK,GAAG,kBAAkB,GAAG,oBAAoB,CAAC;IAG9G,IAAI,CAAC,MAAM,EAAE,WAAW,GAAG,OAAO,CAAC,YAAY,CAAC;IAgBhD,eAAe,CAAC,IAAI,EAAE,2BAA2B,GAAG,OAAO,CAAC,uBAAuB,CAAC;CAgC3F"}
@@ -7,6 +7,7 @@ import { instrumentedLlmCall } from '../runtime/llm-observability.js';
7
7
  import { mapCodexStopReason } from './stop-reason-maps.js';
8
8
  import { instrumentedFetch } from '../utils/fetch-with-timeout.js';
9
9
  import { parseToolCallArguments } from './tool-formats.js';
10
+ import { SseLineBuffer } from './sse-line-buffer.js';
10
11
  const OPENAI_CODEX_BASE_URL = 'https://chatgpt.com/backend-api';
11
12
  const OPENAI_CODEX_PROVIDER_NAME = 'openai-subscriber';
12
13
  function getOpenAICodexUserAgent() {
@@ -192,8 +193,7 @@ export async function chatWithOpenAICodex(accessToken, params) {
192
193
  });
193
194
  }
194
195
  try {
195
- const decoder = new TextDecoder();
196
- let buffer = '';
196
+ const sseBuffer = new SseLineBuffer();
197
197
  let text = '';
198
198
  const toolStarts = new Map();
199
199
  const toolItemIds = new Map();
@@ -346,10 +346,7 @@ export async function chatWithOpenAICodex(accessToken, params) {
346
346
  break;
347
347
  if (!value)
348
348
  continue;
349
- buffer += decoder.decode(value, { stream: true });
350
- const lines = buffer.split(/\r?\n/);
351
- buffer = lines.pop() ?? '';
352
- for (const rawLine of lines) {
349
+ for (const rawLine of sseBuffer.feed(value)) {
353
350
  const line = rawLine.trim();
354
351
  if (!line.startsWith('data: '))
355
352
  continue;
@@ -359,9 +356,11 @@ export async function chatWithOpenAICodex(accessToken, params) {
359
356
  handleDataPayload(data);
360
357
  }
361
358
  }
362
- const trailing = buffer.trim();
363
- if (trailing.startsWith('data: ')) {
364
- const data = trailing.slice(6).trim();
359
+ for (const rawLine of sseBuffer.flush()) {
360
+ const line = rawLine.trim();
361
+ if (!line.startsWith('data: '))
362
+ continue;
363
+ const data = line.slice(6).trim();
365
364
  if (data && data !== '[DONE]') {
366
365
  handleDataPayload(data);
367
366
  }
@@ -1 +1 @@
1
- {"version":3,"file":"openai-compat.d.ts","sourceRoot":"","sources":["../../../src/platform/providers/openai-compat.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"openai-compat.d.ts","sourceRoot":"","sources":["../../../src/platform/providers/openai-compat.ts"],"names":[],"mappings":"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"}
@@ -2,7 +2,9 @@ import OpenAI from 'openai';
2
2
  import { ProviderError } from '../types/errors.js';
3
3
  import { withRetry } from '../utils/retry.js';
4
4
  import { instrumentedLlmCall } from '../runtime/llm-observability.js';
5
- import { toOpenAITools, toOpenAIMessages, fromOpenAIToolCalls, extractTextToolCalls, } from './tool-formats.js';
5
+ import { toOpenAITools, toOpenAIMessages, } from './tool-formats.js';
6
+ import { accumOpenAIToolCall, finalizeOpenAIToolCalls, applyOpenAIChunkUsage, resolveOpenAIToolCallsAndFallback } from './openai-stream-helpers.js';
7
+ import { resolveCompletedStopReason, withProviderStopReason } from './provider-stop-reason.js';
6
8
  import { getCacheCapability } from './cache-capability.js';
7
9
  import { extractOpenAIStreamTextDelta } from './openai-stream-delta.js';
8
10
  import { logger } from '../utils/logger.js';
@@ -324,20 +326,7 @@ export class OpenAICompatProvider {
324
326
  // Accumulate streaming tool_calls deltas
325
327
  if (delta?.tool_calls) {
326
328
  for (const tc of delta.tool_calls) {
327
- const idx = tc.index;
328
- if (!accToolCalls.has(idx)) {
329
- accToolCalls.set(idx, { id: tc.id ?? '', name: tc.function?.name ?? '', args: '' });
330
- }
331
- const entry = accToolCalls.get(idx);
332
- if (tc.id)
333
- entry.id = tc.id;
334
- if (tc.function?.name)
335
- entry.name = tc.function.name;
336
- if (tc.function?.arguments)
337
- entry.args += tc.function.arguments;
338
- if (onDelta) {
339
- onDelta({ toolCalls: [{ index: idx, id: tc.id, name: tc.function?.name, arguments: tc.function?.arguments }] });
340
- }
329
+ accumOpenAIToolCall(accToolCalls, tc, onDelta);
341
330
  }
342
331
  }
343
332
  const finishReason = raw.choices[0]?.finish_reason;
@@ -345,20 +334,9 @@ export class OpenAICompatProvider {
345
334
  rawStopReason = finishReason;
346
335
  stopReason = mapOpenAIStopReason(finishReason);
347
336
  }
348
- if (raw.usage) {
349
- const rawUsage = raw.usage;
350
- inputTokens = rawUsage.prompt_tokens ?? 0;
351
- outputTokens = rawUsage.completion_tokens ?? 0;
352
- cacheReadTokens = rawUsage.prompt_tokens_details?.cached_tokens ?? cacheReadTokens;
353
- }
354
- }
355
- for (const [, tc] of [...accToolCalls.entries()].sort(([a], [b]) => a - b)) {
356
- rawToolCalls.push({
357
- id: tc.id,
358
- type: 'function',
359
- function: { name: tc.name, arguments: tc.args },
360
- });
337
+ ({ inputTokens, outputTokens, cacheReadTokens } = applyOpenAIChunkUsage(raw.usage, { inputTokens, outputTokens, cacheReadTokens }));
361
338
  }
339
+ rawToolCalls = finalizeOpenAIToolCalls(accToolCalls);
362
340
  }
363
341
  catch (err) {
364
342
  const diagnostic = extractOpenAICompatErrorDiagnostic(err);
@@ -393,16 +371,11 @@ export class OpenAICompatProvider {
393
371
  // Some models (e.g. kimi-k2-thinking via ollama-cloud) emit tool calls as
394
372
  // raw text tokens instead of the OpenAI function-calling wire format.
395
373
  // Fall back to text extraction when no structured tool calls were found.
396
- let toolCalls = rawToolCalls.length > 0 ? fromOpenAIToolCalls(rawToolCalls) : [];
397
- if (toolCalls.length === 0 && (responseText.includes('<|toolcallbegin|>') || responseText.includes('<|tool_call_begin|>'))) {
398
- const extracted = extractTextToolCalls(responseText);
399
- if (extracted.toolCalls.length > 0) {
400
- toolCalls = extracted.toolCalls;
401
- responseText = extracted.cleanedContent;
402
- stopReason = 'tool_call';
403
- rawStopReason = rawStopReason ?? 'tool_calls';
404
- }
405
- }
374
+ const resolved = resolveOpenAIToolCallsAndFallback(rawToolCalls, responseText, stopReason, rawStopReason);
375
+ const toolCalls = resolved.toolCalls;
376
+ responseText = resolved.responseText;
377
+ stopReason = resolved.stopReason;
378
+ rawStopReason = resolved.rawStopReason;
406
379
  const response = {
407
380
  content: responseText,
408
381
  toolCalls,
@@ -411,8 +384,8 @@ export class OpenAICompatProvider {
411
384
  outputTokens,
412
385
  ...(cacheReadTokens > 0 ? { cacheReadTokens } : {}),
413
386
  },
414
- stopReason: stopReason === 'unknown' && responseText ? 'completed' : stopReason,
415
- ...(rawStopReason !== undefined ? { providerStopReason: rawStopReason } : {}),
387
+ stopReason: resolveCompletedStopReason(stopReason, responseText),
388
+ ...withProviderStopReason(rawStopReason),
416
389
  };
417
390
  if (reasoningSummaryText) {
418
391
  response.reasoningSummary = reasoningSummaryText;
@@ -0,0 +1,62 @@
1
+ import type { ChatStopReason, StreamDelta } from './interface.js';
2
+ import type { OpenAIToolCall } from './tool-formats.js';
3
+ import { fromOpenAIToolCalls } from './tool-formats.js';
4
+ /** Accumulated state for one streaming tool-call slot. */
5
+ export interface OpenAIToolCallSlot {
6
+ id: string;
7
+ name: string;
8
+ args: string;
9
+ }
10
+ /** Usage fields found on OpenAI streaming chunks. */
11
+ export interface OpenAIChunkUsage {
12
+ prompt_tokens?: number | undefined;
13
+ completion_tokens?: number | undefined;
14
+ prompt_tokens_details?: {
15
+ cached_tokens?: number;
16
+ } | undefined;
17
+ }
18
+ /**
19
+ * Update the tool-call accumulator map with one streaming tool_call delta.
20
+ * Fires onDelta if provided.
21
+ */
22
+ export declare function accumOpenAIToolCall(acc: Map<number, OpenAIToolCallSlot>, tc: {
23
+ index: number;
24
+ id?: string | undefined;
25
+ function?: {
26
+ name?: string | undefined;
27
+ arguments?: string | undefined;
28
+ } | undefined;
29
+ }, onDelta: ((delta: StreamDelta) => void) | undefined): void;
30
+ /**
31
+ * Finalise accumulated tool-call slots into the raw OpenAI wire format,
32
+ * sorted by stream index.
33
+ */
34
+ export declare function finalizeOpenAIToolCalls(acc: Map<number, OpenAIToolCallSlot>): OpenAIToolCall[];
35
+ /**
36
+ * Apply token counts from a streaming chunk's usage field.
37
+ * Absent fields keep the value from current (so initial 0 stays 0
38
+ * if the provider never sends those fields).
39
+ */
40
+ export declare function applyOpenAIChunkUsage(usage: OpenAIChunkUsage | null | undefined, current: {
41
+ inputTokens: number;
42
+ outputTokens: number;
43
+ cacheReadTokens: number;
44
+ }): {
45
+ inputTokens: number;
46
+ outputTokens: number;
47
+ cacheReadTokens: number;
48
+ };
49
+ /**
50
+ * Convert finalized raw tool-calls to canonical form and apply the text-based
51
+ * tool-call fallback for models that emit tool calls as raw text tokens.
52
+ *
53
+ * Returns the final toolCalls list plus (possibly mutated) response text,
54
+ * stop reason, and raw stop reason.
55
+ */
56
+ export declare function resolveOpenAIToolCallsAndFallback(rawToolCalls: OpenAIToolCall[], responseText: string, stopReason: ChatStopReason, rawStopReason: string | undefined): {
57
+ toolCalls: ReturnType<typeof fromOpenAIToolCalls>;
58
+ responseText: string;
59
+ stopReason: ChatStopReason;
60
+ rawStopReason: string | undefined;
61
+ };
62
+ //# sourceMappingURL=openai-stream-helpers.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"openai-stream-helpers.d.ts","sourceRoot":"","sources":["../../../src/platform/providers/openai-stream-helpers.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAClE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AACxD,OAAO,EAAE,mBAAmB,EAAwB,MAAM,mBAAmB,CAAC;AAE9E,0DAA0D;AAC1D,MAAM,WAAW,kBAAkB;IACjC,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;CACd;AAED,qDAAqD;AACrD,MAAM,WAAW,gBAAgB;IAC/B,aAAa,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACnC,iBAAiB,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACvC,qBAAqB,CAAC,EAAE;QAAE,aAAa,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,SAAS,CAAC;CAChE;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,CACjC,GAAG,EAAE,GAAG,CAAC,MAAM,EAAE,kBAAkB,CAAC,EACpC,EAAE,EAAE;IACF,KAAK,EAAE,MAAM,CAAC;IACd,EAAE,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACxB,QAAQ,CAAC,EAAE;QAAE,IAAI,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;QAAC,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;KAAE,GAAG,SAAS,CAAC;CACtF,EACD,OAAO,EAAE,CAAC,CAAC,KAAK,EAAE,WAAW,KAAK,IAAI,CAAC,GAAG,SAAS,GAClD,IAAI,CAcN;AAED;;;GAGG;AACH,wBAAgB,uBAAuB,CAAC,GAAG,EAAE,GAAG,CAAC,MAAM,EAAE,kBAAkB,CAAC,GAAG,cAAc,EAAE,CAU9F;AAED;;;;GAIG;AACH,wBAAgB,qBAAqB,CACnC,KAAK,EAAE,gBAAgB,GAAG,IAAI,GAAG,SAAS,EAC1C,OAAO,EAAE;IAAE,WAAW,EAAE,MAAM,CAAC;IAAC,YAAY,EAAE,MAAM,CAAC;IAAC,eAAe,EAAE,MAAM,CAAA;CAAE,GAC9E;IAAE,WAAW,EAAE,MAAM,CAAC;IAAC,YAAY,EAAE,MAAM,CAAC;IAAC,eAAe,EAAE,MAAM,CAAA;CAAE,CAOxE;AAED;;;;;;GAMG;AACH,wBAAgB,iCAAiC,CAC/C,YAAY,EAAE,cAAc,EAAE,EAC9B,YAAY,EAAE,MAAM,EACpB,UAAU,EAAE,cAAc,EAC1B,aAAa,EAAE,MAAM,GAAG,SAAS,GAChC;IACD,SAAS,EAAE,UAAU,CAAC,OAAO,mBAAmB,CAAC,CAAC;IAClD,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,cAAc,CAAC;IAC3B,aAAa,EAAE,MAAM,GAAG,SAAS,CAAC;CACnC,CAeA"}
@@ -0,0 +1,73 @@
1
+ import { fromOpenAIToolCalls, extractTextToolCalls } from './tool-formats.js';
2
+ /**
3
+ * Update the tool-call accumulator map with one streaming tool_call delta.
4
+ * Fires onDelta if provided.
5
+ */
6
+ export function accumOpenAIToolCall(acc, tc, onDelta) {
7
+ const idx = tc.index;
8
+ if (!acc.has(idx)) {
9
+ acc.set(idx, { id: tc.id ?? '', name: tc.function?.name ?? '', args: '' });
10
+ }
11
+ const entry = acc.get(idx);
12
+ if (tc.id)
13
+ entry.id = tc.id;
14
+ if (tc.function?.name)
15
+ entry.name = tc.function.name;
16
+ if (tc.function?.arguments)
17
+ entry.args += tc.function.arguments;
18
+ if (onDelta) {
19
+ onDelta({
20
+ toolCalls: [{ index: idx, id: tc.id, name: tc.function?.name, arguments: tc.function?.arguments }],
21
+ });
22
+ }
23
+ }
24
+ /**
25
+ * Finalise accumulated tool-call slots into the raw OpenAI wire format,
26
+ * sorted by stream index.
27
+ */
28
+ export function finalizeOpenAIToolCalls(acc) {
29
+ const result = [];
30
+ for (const [, tc] of [...acc.entries()].sort(([a], [b]) => a - b)) {
31
+ result.push({
32
+ id: tc.id,
33
+ type: 'function',
34
+ function: { name: tc.name, arguments: tc.args },
35
+ });
36
+ }
37
+ return result;
38
+ }
39
+ /**
40
+ * Apply token counts from a streaming chunk's usage field.
41
+ * Absent fields keep the value from current (so initial 0 stays 0
42
+ * if the provider never sends those fields).
43
+ */
44
+ export function applyOpenAIChunkUsage(usage, current) {
45
+ if (!usage)
46
+ return current;
47
+ return {
48
+ inputTokens: usage.prompt_tokens ?? current.inputTokens,
49
+ outputTokens: usage.completion_tokens ?? current.outputTokens,
50
+ cacheReadTokens: usage.prompt_tokens_details?.cached_tokens ?? current.cacheReadTokens,
51
+ };
52
+ }
53
+ /**
54
+ * Convert finalized raw tool-calls to canonical form and apply the text-based
55
+ * tool-call fallback for models that emit tool calls as raw text tokens.
56
+ *
57
+ * Returns the final toolCalls list plus (possibly mutated) response text,
58
+ * stop reason, and raw stop reason.
59
+ */
60
+ export function resolveOpenAIToolCallsAndFallback(rawToolCalls, responseText, stopReason, rawStopReason) {
61
+ let toolCalls = rawToolCalls.length > 0 ? fromOpenAIToolCalls(rawToolCalls) : [];
62
+ if (toolCalls.length === 0 &&
63
+ (responseText.includes('<|toolcallbegin|>') || responseText.includes('<|tool_call_begin|>'))) {
64
+ const extracted = extractTextToolCalls(responseText);
65
+ if (extracted.toolCalls.length > 0) {
66
+ toolCalls = extracted.toolCalls;
67
+ responseText = extracted.cleanedContent;
68
+ stopReason = 'tool_call';
69
+ rawStopReason = rawStopReason ?? 'tool_calls';
70
+ }
71
+ }
72
+ return { toolCalls, responseText, stopReason, rawStopReason };
73
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"openai.d.ts","sourceRoot":"","sources":["../../../src/platform/providers/openai.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,WAAW,EACX,WAAW,EACX,YAAY,EAEZ,wBAAwB,EACxB,uBAAuB,EACvB,oBAAoB,EAKpB,uBAAuB,EACvB,2BAA2B,EAC5B,MAAM,gBAAgB,CAAC;AAYxB,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAQ3D;;;GAGG;AACH,qBAAa,cAAe,YAAW,WAAW;IAChD,QAAQ,CAAC,IAAI,YAAY;IACzB,QAAQ,CAAC,MAAM,EAAE,MAAM,EAAE,CAAM;IAC/B,QAAQ,CAAC,KAAK,EAAE,oBAAoB,CAAC;IAErC,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;IAChC,OAAO,CAAC,QAAQ,CAAC,cAAc,CAA4B;IAC3D,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAsC;gBAE1D,MAAM,EAAE,MAAM,EAAE,eAAe,GAAE,IAAI,CAAC,eAAe,EAAE,YAAY,CAA0B;IAcnG,IAAI,CAAC,MAAM,EAAE,WAAW,GAAG,OAAO,CAAC,YAAY,CAAC;IA8HhD,KAAK,CAAC,OAAO,EAAE,wBAAwB,GAAG,OAAO,CAAC,uBAAuB,CAAC;IA8BhF,YAAY,IAAI,OAAO;IAIjB,eAAe,CAAC,IAAI,EAAE,2BAA2B,GAAG,OAAO,CAAC,uBAAuB,CAAC;YA+C5E,eAAe;YAyBf,aAAa;YAUb,WAAW;YAUX,eAAe;IAY7B,OAAO,CAAC,qBAAqB;IAY7B,OAAO,CAAC,oBAAoB;YASd,yBAAyB;IAuBvC,OAAO,CAAC,sBAAsB;IA2B9B,OAAO,CAAC,6BAA6B;CAkCtC"}
1
+ {"version":3,"file":"openai.d.ts","sourceRoot":"","sources":["../../../src/platform/providers/openai.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,WAAW,EACX,WAAW,EACX,YAAY,EAEZ,wBAAwB,EACxB,uBAAuB,EACvB,oBAAoB,EAKpB,uBAAuB,EACvB,2BAA2B,EAC5B,MAAM,gBAAgB,CAAC;AAaxB,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAQ3D;;;GAGG;AACH,qBAAa,cAAe,YAAW,WAAW;IAChD,QAAQ,CAAC,IAAI,YAAY;IACzB,QAAQ,CAAC,MAAM,EAAE,MAAM,EAAE,CAAM;IAC/B,QAAQ,CAAC,KAAK,EAAE,oBAAoB,CAAC;IAErC,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;IAChC,OAAO,CAAC,QAAQ,CAAC,cAAc,CAA4B;IAC3D,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAsC;gBAE1D,MAAM,EAAE,MAAM,EAAE,eAAe,GAAE,IAAI,CAAC,eAAe,EAAE,YAAY,CAA0B;IAcnG,IAAI,CAAC,MAAM,EAAE,WAAW,GAAG,OAAO,CAAC,YAAY,CAAC;IAsGhD,KAAK,CAAC,OAAO,EAAE,wBAAwB,GAAG,OAAO,CAAC,uBAAuB,CAAC;IA8BhF,YAAY,IAAI,OAAO;IAIjB,eAAe,CAAC,IAAI,EAAE,2BAA2B,GAAG,OAAO,CAAC,uBAAuB,CAAC;YA+C5E,eAAe;YAyBf,aAAa;YAUb,WAAW;YAUX,eAAe;IAY7B,OAAO,CAAC,qBAAqB;IAY7B,OAAO,CAAC,oBAAoB;YASd,yBAAyB;IAuBvC,OAAO,CAAC,sBAAsB;IA2B9B,OAAO,CAAC,6BAA6B;CAkCtC"}
@@ -2,7 +2,9 @@ import OpenAI, { toFile } from 'openai';
2
2
  import { mapOpenAIStopReason } from './stop-reason-maps.js';
3
3
  import { ProviderError } from '../types/errors.js';
4
4
  import { withRetry } from '../utils/retry.js';
5
- import { toOpenAITools, toOpenAIMessages, fromOpenAIToolCalls, extractTextToolCalls, } from './tool-formats.js';
5
+ import { toOpenAITools, toOpenAIMessages, fromOpenAIToolCalls, } from './tool-formats.js';
6
+ import { accumOpenAIToolCall, finalizeOpenAIToolCalls, applyOpenAIChunkUsage, resolveOpenAIToolCallsAndFallback } from './openai-stream-helpers.js';
7
+ import { resolveCompletedStopReason, withProviderStopReason } from './provider-stop-reason.js';
6
8
  import { extractOpenAIStreamTextDelta } from './openai-stream-delta.js';
7
9
  import { summarizeError, toProviderError } from '../utils/error-display.js';
8
10
  const NOOP_CACHE_HIT_TRACKER = {
@@ -71,20 +73,7 @@ export class OpenAIProvider {
71
73
  // Accumulate streaming tool_calls deltas
72
74
  if (delta?.tool_calls) {
73
75
  for (const tc of delta.tool_calls) {
74
- const idx = tc.index;
75
- if (!accToolCalls.has(idx)) {
76
- accToolCalls.set(idx, { id: tc.id ?? '', name: tc.function?.name ?? '', args: '' });
77
- }
78
- const entry = accToolCalls.get(idx);
79
- if (tc.id)
80
- entry.id = tc.id;
81
- if (tc.function?.name)
82
- entry.name = tc.function.name;
83
- if (tc.function?.arguments)
84
- entry.args += tc.function.arguments;
85
- if (onDelta) {
86
- onDelta({ toolCalls: [{ index: idx, id: tc.id, name: tc.function?.name, arguments: tc.function?.arguments }] });
87
- }
76
+ accumOpenAIToolCall(accToolCalls, tc, onDelta);
88
77
  }
89
78
  }
90
79
  const finishReason = chunk.choices[0]?.finish_reason;
@@ -92,21 +81,9 @@ export class OpenAIProvider {
92
81
  rawStopReason = finishReason;
93
82
  stopReason = mapOpenAIStopReason(finishReason);
94
83
  }
95
- const usage = chunk.usage;
96
- if (usage) {
97
- inputTokens = usage.prompt_tokens ?? 0;
98
- outputTokens = usage.completion_tokens ?? 0;
99
- cacheReadTokens = usage.prompt_tokens_details?.cached_tokens ?? cacheReadTokens;
100
- }
101
- }
102
- // Finalise accumulated tool calls
103
- for (const [, tc] of [...accToolCalls.entries()].sort(([a], [b]) => a - b)) {
104
- rawToolCalls.push({
105
- id: tc.id,
106
- type: 'function',
107
- function: { name: tc.name, arguments: tc.args },
108
- });
84
+ ({ inputTokens, outputTokens, cacheReadTokens } = applyOpenAIChunkUsage(chunk.usage, { inputTokens, outputTokens, cacheReadTokens }));
109
85
  }
86
+ rawToolCalls = finalizeOpenAIToolCalls(accToolCalls);
110
87
  }
111
88
  catch (err) {
112
89
  const { hasStatus } = await import('../utils/retry.js');
@@ -120,16 +97,11 @@ export class OpenAIProvider {
120
97
  }
121
98
  // Some models may emit tool calls as raw text tokens instead of the
122
99
  // OpenAI function-calling wire format. Fall back to text extraction.
123
- let toolCalls = rawToolCalls.length > 0 ? fromOpenAIToolCalls(rawToolCalls) : [];
124
- if (toolCalls.length === 0 && (responseText.includes('<|toolcallbegin|>') || responseText.includes('<|tool_call_begin|>'))) {
125
- const extracted = extractTextToolCalls(responseText);
126
- if (extracted.toolCalls.length > 0) {
127
- toolCalls = extracted.toolCalls;
128
- responseText = extracted.cleanedContent;
129
- stopReason = 'tool_call';
130
- rawStopReason = rawStopReason ?? 'tool_calls';
131
- }
132
- }
100
+ const resolved = resolveOpenAIToolCallsAndFallback(rawToolCalls, responseText, stopReason, rawStopReason);
101
+ const toolCalls = resolved.toolCalls;
102
+ responseText = resolved.responseText;
103
+ stopReason = resolved.stopReason;
104
+ rawStopReason = resolved.rawStopReason;
133
105
  this.cacheHitTracker.recordTurn({
134
106
  inputTokens,
135
107
  cacheReadTokens,
@@ -142,8 +114,8 @@ export class OpenAIProvider {
142
114
  outputTokens,
143
115
  ...(cacheReadTokens > 0 ? { cacheReadTokens } : {}),
144
116
  },
145
- stopReason: stopReason === 'unknown' && responseText ? 'completed' : stopReason,
146
- ...(rawStopReason !== undefined ? { providerStopReason: rawStopReason } : {}),
117
+ stopReason: resolveCompletedStopReason(stopReason, responseText),
118
+ ...withProviderStopReason(rawStopReason),
147
119
  };
148
120
  });
149
121
  }
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Shared provider-error helpers.
3
+ *
4
+ * Consolidated from three verbatim copies in lm-studio-helpers.ts,
5
+ * llama-cpp.ts, and ollama.ts into a single definition.
6
+ */
7
+ import type { ProviderError } from '../types/errors.js';
8
+ /**
9
+ * Extract an HTTP status code from an error object.
10
+ * Checks `.status` before `.statusCode` to preserve provider-error convention.
11
+ */
12
+ export declare function getErrorStatus(err: unknown): number | undefined;
13
+ /**
14
+ * Wrap an error into a ProviderError, attaching the HTTP status if present.
15
+ */
16
+ export declare function normalizeProviderError(err: unknown, provider: string, operation: string, phase?: string): ProviderError;
17
+ //# sourceMappingURL=provider-error.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"provider-error.d.ts","sourceRoot":"","sources":["../../../src/platform/providers/provider-error.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAExD;;;GAGG;AACH,wBAAgB,cAAc,CAAC,GAAG,EAAE,OAAO,GAAG,MAAM,GAAG,SAAS,CAO/D;AAED;;GAEG;AACH,wBAAgB,sBAAsB,CACpC,GAAG,EAAE,OAAO,EACZ,QAAQ,EAAE,MAAM,EAChB,SAAS,EAAE,MAAM,EACjB,KAAK,SAAY,GAChB,aAAa,CAQf"}
@@ -0,0 +1,33 @@
1
+ /**
2
+ * Shared provider-error helpers.
3
+ *
4
+ * Consolidated from three verbatim copies in lm-studio-helpers.ts,
5
+ * llama-cpp.ts, and ollama.ts into a single definition.
6
+ */
7
+ import { toProviderError } from '../utils/error-display.js';
8
+ /**
9
+ * Extract an HTTP status code from an error object.
10
+ * Checks `.status` before `.statusCode` to preserve provider-error convention.
11
+ */
12
+ export function getErrorStatus(err) {
13
+ if (err && typeof err === 'object') {
14
+ const record = err;
15
+ if (typeof record.status === 'number')
16
+ return record.status;
17
+ if (typeof record.statusCode === 'number')
18
+ return record.statusCode;
19
+ }
20
+ return undefined;
21
+ }
22
+ /**
23
+ * Wrap an error into a ProviderError, attaching the HTTP status if present.
24
+ */
25
+ export function normalizeProviderError(err, provider, operation, phase = 'request') {
26
+ const status = getErrorStatus(err);
27
+ return toProviderError(err, {
28
+ ...(status !== undefined ? { statusCode: status } : {}),
29
+ provider,
30
+ operation,
31
+ phase,
32
+ });
33
+ }
@@ -0,0 +1,15 @@
1
+ import type { ChatStopReason } from './interface.js';
2
+ /**
3
+ * Resolve an 'unknown' stop reason to 'completed' when the model produced text.
4
+ * Providers use this when the stream ended without an explicit finish reason
5
+ * but content was generated.
6
+ */
7
+ export declare function resolveCompletedStopReason(stopReason: ChatStopReason, content: string | undefined): ChatStopReason;
8
+ /**
9
+ * Build the providerStopReason spread for ChatResponse.
10
+ * Returns { providerStopReason } when the raw value is known, otherwise {}.
11
+ */
12
+ export declare function withProviderStopReason(rawStopReason: string | undefined): {
13
+ providerStopReason?: string;
14
+ };
15
+ //# sourceMappingURL=provider-stop-reason.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"provider-stop-reason.d.ts","sourceRoot":"","sources":["../../../src/platform/providers/provider-stop-reason.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAErD;;;;GAIG;AACH,wBAAgB,0BAA0B,CACxC,UAAU,EAAE,cAAc,EAC1B,OAAO,EAAE,MAAM,GAAG,SAAS,GAC1B,cAAc,CAEhB;AAED;;;GAGG;AACH,wBAAgB,sBAAsB,CACpC,aAAa,EAAE,MAAM,GAAG,SAAS,GAChC;IAAE,kBAAkB,CAAC,EAAE,MAAM,CAAA;CAAE,CAEjC"}
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Resolve an 'unknown' stop reason to 'completed' when the model produced text.
3
+ * Providers use this when the stream ended without an explicit finish reason
4
+ * but content was generated.
5
+ */
6
+ export function resolveCompletedStopReason(stopReason, content) {
7
+ return stopReason === 'unknown' && content ? 'completed' : stopReason;
8
+ }
9
+ /**
10
+ * Build the providerStopReason spread for ChatResponse.
11
+ * Returns { providerStopReason } when the raw value is known, otherwise {}.
12
+ */
13
+ export function withProviderStopReason(rawStopReason) {
14
+ return rawStopReason !== undefined ? { providerStopReason: rawStopReason } : {};
15
+ }