@framers/agentos 0.10.22 → 0.10.24
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.
- package/README.md +1 -1
- package/dist/api/AgentOS.d.ts.map +1 -1
- package/dist/api/AgentOS.js +3 -2
- package/dist/api/AgentOS.js.map +1 -1
- package/dist/api/agent.d.ts.map +1 -1
- package/dist/api/agent.js +25 -10
- package/dist/api/agent.js.map +1 -1
- package/dist/api/embedText.d.ts.map +1 -1
- package/dist/api/embedText.js +53 -17
- package/dist/api/embedText.js.map +1 -1
- package/dist/api/generateImage.d.ts.map +1 -1
- package/dist/api/generateImage.js +51 -7
- package/dist/api/generateImage.js.map +1 -1
- package/dist/api/generateText.d.ts +50 -7
- package/dist/api/generateText.d.ts.map +1 -1
- package/dist/api/generateText.js +265 -35
- package/dist/api/generateText.js.map +1 -1
- package/dist/api/model.d.ts +25 -1
- package/dist/api/model.d.ts.map +1 -1
- package/dist/api/model.js +108 -3
- package/dist/api/model.js.map +1 -1
- package/dist/api/runtime/SelfImprovementSessionManager.d.ts +34 -2
- package/dist/api/runtime/SelfImprovementSessionManager.d.ts.map +1 -1
- package/dist/api/runtime/SelfImprovementSessionManager.js +119 -33
- package/dist/api/runtime/SelfImprovementSessionManager.js.map +1 -1
- package/dist/api/runtime/global-default.d.ts +9 -1
- package/dist/api/runtime/global-default.d.ts.map +1 -1
- package/dist/api/runtime/global-default.js.map +1 -1
- package/dist/api/runtime/provider-defaults.d.ts.map +1 -1
- package/dist/api/runtime/provider-defaults.js +10 -4
- package/dist/api/runtime/provider-defaults.js.map +1 -1
- package/dist/api/runtime/tool-emulation/history.d.ts +16 -0
- package/dist/api/runtime/tool-emulation/history.d.ts.map +1 -0
- package/dist/api/runtime/tool-emulation/history.js +89 -0
- package/dist/api/runtime/tool-emulation/history.js.map +1 -0
- package/dist/api/runtime/tool-emulation/index.d.ts +1 -0
- package/dist/api/runtime/tool-emulation/index.d.ts.map +1 -1
- package/dist/api/runtime/tool-emulation/index.js +1 -0
- package/dist/api/runtime/tool-emulation/index.js.map +1 -1
- package/dist/api/runtime/tool-emulation/loop.d.ts +6 -0
- package/dist/api/runtime/tool-emulation/loop.d.ts.map +1 -1
- package/dist/api/runtime/tool-emulation/loop.js +1 -0
- package/dist/api/runtime/tool-emulation/loop.js.map +1 -1
- package/dist/api/sessionTranscript.d.ts +34 -0
- package/dist/api/sessionTranscript.d.ts.map +1 -1
- package/dist/api/sessionTranscript.js +61 -1
- package/dist/api/sessionTranscript.js.map +1 -1
- package/dist/api/streamText.d.ts.map +1 -1
- package/dist/api/streamText.js +120 -24
- package/dist/api/streamText.js.map +1 -1
- package/dist/api/types/AgentOSInput.d.ts +11 -0
- package/dist/api/types/AgentOSInput.d.ts.map +1 -1
- package/dist/api/types.d.ts +14 -6
- package/dist/api/types.d.ts.map +1 -1
- package/dist/api/types.js.map +1 -1
- package/dist/cognition/emergent/AdaptPersonalityTool.d.ts +13 -4
- package/dist/cognition/emergent/AdaptPersonalityTool.d.ts.map +1 -1
- package/dist/cognition/emergent/AdaptPersonalityTool.js +32 -23
- package/dist/cognition/emergent/AdaptPersonalityTool.js.map +1 -1
- package/dist/cognition/emergent/EmergentCapabilityEngine.d.ts +18 -6
- package/dist/cognition/emergent/EmergentCapabilityEngine.d.ts.map +1 -1
- package/dist/cognition/emergent/EmergentCapabilityEngine.js.map +1 -1
- package/dist/cognition/emergent/SelfEvaluateTool.d.ts +10 -3
- package/dist/cognition/emergent/SelfEvaluateTool.d.ts.map +1 -1
- package/dist/cognition/emergent/SelfEvaluateTool.js +3 -2
- package/dist/cognition/emergent/SelfEvaluateTool.js.map +1 -1
- package/dist/cognition/substrate/CognitiveMemoryBridge.d.ts +51 -2
- package/dist/cognition/substrate/CognitiveMemoryBridge.d.ts.map +1 -1
- package/dist/cognition/substrate/CognitiveMemoryBridge.js +84 -2
- package/dist/cognition/substrate/CognitiveMemoryBridge.js.map +1 -1
- package/dist/cognition/substrate/ConversationHistoryManager.d.ts +8 -4
- package/dist/cognition/substrate/ConversationHistoryManager.d.ts.map +1 -1
- package/dist/cognition/substrate/ConversationHistoryManager.js +17 -5
- package/dist/cognition/substrate/ConversationHistoryManager.js.map +1 -1
- package/dist/cognition/substrate/GMI.d.ts +92 -5
- package/dist/cognition/substrate/GMI.d.ts.map +1 -1
- package/dist/cognition/substrate/GMI.js +291 -65
- package/dist/cognition/substrate/GMI.js.map +1 -1
- package/dist/cognition/substrate/GMIManager.d.ts +12 -0
- package/dist/cognition/substrate/GMIManager.d.ts.map +1 -1
- package/dist/cognition/substrate/GMIManager.js +22 -16
- package/dist/cognition/substrate/GMIManager.js.map +1 -1
- package/dist/cognition/substrate/IGMI.d.ts +23 -0
- package/dist/cognition/substrate/IGMI.d.ts.map +1 -1
- package/dist/cognition/substrate/IGMI.js.map +1 -1
- package/dist/cognition/substrate/MetapromptExecutor.d.ts +183 -28
- package/dist/cognition/substrate/MetapromptExecutor.d.ts.map +1 -1
- package/dist/cognition/substrate/MetapromptExecutor.js +431 -108
- package/dist/cognition/substrate/MetapromptExecutor.js.map +1 -1
- package/dist/cognition/substrate/personas/IPersonaDefinition.d.ts +5 -0
- package/dist/cognition/substrate/personas/IPersonaDefinition.d.ts.map +1 -1
- package/dist/cognition/substrate/personas/PersonaValidation.d.ts.map +1 -1
- package/dist/cognition/substrate/personas/PersonaValidation.js +30 -0
- package/dist/cognition/substrate/personas/PersonaValidation.js.map +1 -1
- package/dist/cognition/substrate/personas/SoulLoader.d.ts +14 -1
- package/dist/cognition/substrate/personas/SoulLoader.d.ts.map +1 -1
- package/dist/cognition/substrate/personas/SoulLoader.js +45 -12
- package/dist/cognition/substrate/personas/SoulLoader.js.map +1 -1
- package/dist/cognition/substrate/personas/definitions/default_assistant_persona.json +3 -35
- package/dist/cognition/substrate/personas/hexaco.d.ts +34 -0
- package/dist/cognition/substrate/personas/hexaco.d.ts.map +1 -0
- package/dist/cognition/substrate/personas/hexaco.js +66 -0
- package/dist/cognition/substrate/personas/hexaco.js.map +1 -0
- package/dist/cognition/substrate/userFeedback.d.ts +69 -0
- package/dist/cognition/substrate/userFeedback.d.ts.map +1 -0
- package/dist/cognition/substrate/userFeedback.js +107 -0
- package/dist/cognition/substrate/userFeedback.js.map +1 -0
- package/dist/core/conversation/ConversationMessage.d.ts +9 -0
- package/dist/core/conversation/ConversationMessage.d.ts.map +1 -1
- package/dist/core/conversation/ConversationMessage.js.map +1 -1
- package/dist/core/llm/IPromptEngine.d.ts +9 -0
- package/dist/core/llm/IPromptEngine.d.ts.map +1 -1
- package/dist/core/llm/IPromptEngine.js.map +1 -1
- package/dist/core/llm/PromptEngine.d.ts +30 -1
- package/dist/core/llm/PromptEngine.d.ts.map +1 -1
- package/dist/core/llm/PromptEngine.js +229 -92
- package/dist/core/llm/PromptEngine.js.map +1 -1
- package/dist/core/llm/providers/AIModelProviderManager.d.ts +20 -0
- package/dist/core/llm/providers/AIModelProviderManager.d.ts.map +1 -1
- package/dist/core/llm/providers/AIModelProviderManager.js +35 -5
- package/dist/core/llm/providers/AIModelProviderManager.js.map +1 -1
- package/dist/core/llm/providers/IProvider.d.ts +28 -8
- package/dist/core/llm/providers/IProvider.d.ts.map +1 -1
- package/dist/core/llm/providers/implementations/AnthropicProvider.d.ts +135 -49
- package/dist/core/llm/providers/implementations/AnthropicProvider.d.ts.map +1 -1
- package/dist/core/llm/providers/implementations/AnthropicProvider.js +323 -114
- package/dist/core/llm/providers/implementations/AnthropicProvider.js.map +1 -1
- package/dist/core/llm/providers/implementations/ClaudeCodeProvider.d.ts.map +1 -1
- package/dist/core/llm/providers/implementations/ClaudeCodeProvider.js +15 -1
- package/dist/core/llm/providers/implementations/ClaudeCodeProvider.js.map +1 -1
- package/dist/core/llm/providers/implementations/GeminiCLIProvider.d.ts +1 -1
- package/dist/core/llm/providers/implementations/GeminiCLIProvider.d.ts.map +1 -1
- package/dist/core/llm/providers/implementations/GeminiCLIProvider.js +51 -24
- package/dist/core/llm/providers/implementations/GeminiCLIProvider.js.map +1 -1
- package/dist/core/llm/providers/implementations/GeminiProvider.d.ts +25 -6
- package/dist/core/llm/providers/implementations/GeminiProvider.d.ts.map +1 -1
- package/dist/core/llm/providers/implementations/GeminiProvider.js +108 -90
- package/dist/core/llm/providers/implementations/GeminiProvider.js.map +1 -1
- package/dist/core/llm/providers/implementations/MistralProvider.d.ts +11 -0
- package/dist/core/llm/providers/implementations/MistralProvider.d.ts.map +1 -1
- package/dist/core/llm/providers/implementations/MistralProvider.js +32 -2
- package/dist/core/llm/providers/implementations/MistralProvider.js.map +1 -1
- package/dist/core/llm/providers/implementations/OllamaProvider.js +7 -7
- package/dist/core/llm/providers/implementations/OllamaProvider.js.map +1 -1
- package/dist/core/llm/providers/implementations/OpenAIProvider.d.ts +53 -2
- package/dist/core/llm/providers/implementations/OpenAIProvider.d.ts.map +1 -1
- package/dist/core/llm/providers/implementations/OpenAIProvider.js +84 -17
- package/dist/core/llm/providers/implementations/OpenAIProvider.js.map +1 -1
- package/dist/core/llm/providers/implementations/OpenRouterProvider.d.ts.map +1 -1
- package/dist/core/llm/providers/implementations/OpenRouterProvider.js +8 -3
- package/dist/core/llm/providers/implementations/OpenRouterProvider.js.map +1 -1
- package/dist/core/llm/providers/implementations/TogetherProvider.d.ts +3 -3
- package/dist/core/llm/providers/implementations/TogetherProvider.d.ts.map +1 -1
- package/dist/core/llm/providers/implementations/TogetherProvider.js +25 -13
- package/dist/core/llm/providers/implementations/TogetherProvider.js.map +1 -1
- package/dist/core/llm/providers/implementations/cacheLeakDetector.d.ts +15 -7
- package/dist/core/llm/providers/implementations/cacheLeakDetector.d.ts.map +1 -1
- package/dist/core/llm/providers/implementations/cacheLeakDetector.js +12 -7
- package/dist/core/llm/providers/implementations/cacheLeakDetector.js.map +1 -1
- package/dist/core/llm/providers/model-cache-capabilities.d.ts +24 -9
- package/dist/core/llm/providers/model-cache-capabilities.d.ts.map +1 -1
- package/dist/core/llm/providers/model-cache-capabilities.js +59 -44
- package/dist/core/llm/providers/model-cache-capabilities.js.map +1 -1
- package/dist/core/llm/providers/model-effort.d.ts +14 -0
- package/dist/core/llm/providers/model-effort.d.ts.map +1 -1
- package/dist/core/llm/providers/model-effort.js +22 -0
- package/dist/core/llm/providers/model-effort.js.map +1 -1
- package/dist/core/llm/providers/model-forced-tool-choice.d.ts +13 -6
- package/dist/core/llm/providers/model-forced-tool-choice.d.ts.map +1 -1
- package/dist/core/llm/providers/model-forced-tool-choice.js +14 -7
- package/dist/core/llm/providers/model-forced-tool-choice.js.map +1 -1
- package/dist/core/llm/providers/model-thinking.d.ts +64 -13
- package/dist/core/llm/providers/model-thinking.d.ts.map +1 -1
- package/dist/core/llm/providers/model-thinking.js +52 -12
- package/dist/core/llm/providers/model-thinking.js.map +1 -1
- package/dist/core/llm/providers/openrouter-only-params.d.ts +26 -7
- package/dist/core/llm/providers/openrouter-only-params.d.ts.map +1 -1
- package/dist/core/llm/providers/openrouter-only-params.js +58 -9
- package/dist/core/llm/providers/openrouter-only-params.js.map +1 -1
- package/dist/core/llm/providers/url-secrets.d.ts +42 -0
- package/dist/core/llm/providers/url-secrets.d.ts.map +1 -0
- package/dist/core/llm/providers/url-secrets.js +64 -0
- package/dist/core/llm/providers/url-secrets.js.map +1 -0
- package/dist/core/llm/streaming/StreamingBatcher.d.ts +4 -3
- package/dist/core/llm/streaming/StreamingBatcher.d.ts.map +1 -1
- package/dist/core/llm/streaming/StreamingBatcher.js +8 -3
- package/dist/core/llm/streaming/StreamingBatcher.js.map +1 -1
- package/dist/core/safety/LLMProviderHealthRegistry.d.ts +4 -0
- package/dist/core/safety/LLMProviderHealthRegistry.d.ts.map +1 -1
- package/dist/core/safety/LLMProviderHealthRegistry.js +26 -0
- package/dist/core/safety/LLMProviderHealthRegistry.js.map +1 -1
- package/dist/core/utils/sha256.d.ts +19 -0
- package/dist/core/utils/sha256.d.ts.map +1 -0
- package/dist/core/utils/sha256.js +101 -0
- package/dist/core/utils/sha256.js.map +1 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/orchestration/pipeline/query/types.js +3 -3
- package/dist/orchestration/pipeline/query/types.js.map +1 -1
- package/dist/orchestration/planning/ProviderAssignmentEngine.js +2 -2
- package/dist/orchestration/planning/ProviderAssignmentEngine.js.map +1 -1
- package/package.json +1 -1
package/dist/api/generateText.js
CHANGED
|
@@ -17,7 +17,7 @@ import { attachGenAiAttributes, attachUsageAttributes, toTurnMetricUsage } from
|
|
|
17
17
|
import { fireLlmUsageObserver } from './observers.js';
|
|
18
18
|
import { hostPolicyToRouteParams, mergeRequiredCapabilities, } from './runtime/hostPolicy.js';
|
|
19
19
|
import { adaptTools } from './runtime/toolAdapter.js';
|
|
20
|
-
import { runEmulatedToolLoop } from './runtime/tool-emulation/index.js';
|
|
20
|
+
import { runEmulatedToolLoop, toShimMessages } from './runtime/tool-emulation/index.js';
|
|
21
21
|
import { resolveDynamicToolCalls } from './runtime/dynamicToolCalling.js';
|
|
22
22
|
import { recordAgentOSTurnMetrics, withAgentOSSpan } from '../safety/evaluation/observability/otel.js';
|
|
23
23
|
import { createLogger } from '../core/logging/loggerFactory.js';
|
|
@@ -66,6 +66,7 @@ class LLMProviderCircuitOpenError extends Error {
|
|
|
66
66
|
this.name = 'LLMProviderCircuitOpenError';
|
|
67
67
|
}
|
|
68
68
|
}
|
|
69
|
+
import { toProviderReplayMessage } from './sessionTranscript.js';
|
|
69
70
|
async function recordAgentOSUsageLazy(input) {
|
|
70
71
|
const { recordAgentOSUsage } = await import('./runtime/usageLedger.js');
|
|
71
72
|
return recordAgentOSUsage(input);
|
|
@@ -168,6 +169,7 @@ export async function createPlan(provider, modelId, userMessages, toolNames, con
|
|
|
168
169
|
// Inherited (or planning-specific) cache control: a cache:false root
|
|
169
170
|
// call's planning sub-call must not auto-cache behind the caller's back.
|
|
170
171
|
...(config?.cache !== undefined ? { cache: config.cache } : {}),
|
|
172
|
+
...(config?.thinking === false ? { thinking: false } : {}),
|
|
171
173
|
});
|
|
172
174
|
// Accumulate planning call usage
|
|
173
175
|
if (response.usage) {
|
|
@@ -252,6 +254,124 @@ function formatPlanForPrompt(plan) {
|
|
|
252
254
|
* @internal
|
|
253
255
|
*/
|
|
254
256
|
const RETRYABLE_HTTP_STATUSES = new Set([401, 402, 403, 429, 500, 502, 503, 504]);
|
|
257
|
+
/**
|
|
258
|
+
* Whether the tool round a continuation answers was requested without
|
|
259
|
+
* thinking blocks, as it is when another provider's model ran it.
|
|
260
|
+
*/
|
|
261
|
+
function toolTurnLacksThinking(messages) {
|
|
262
|
+
for (let i = messages.length - 1; i >= 0; i--) {
|
|
263
|
+
const message = messages[i];
|
|
264
|
+
if (message.role !== 'assistant' || !Array.isArray(message.tool_calls) || message.tool_calls.length === 0) {
|
|
265
|
+
continue;
|
|
266
|
+
}
|
|
267
|
+
return !Array.isArray(message.thinkingBlocks) || message.thinkingBlocks.length === 0;
|
|
268
|
+
}
|
|
269
|
+
return false;
|
|
270
|
+
}
|
|
271
|
+
/** Property key that marks an error thrown after the call's tools ran. */
|
|
272
|
+
const TOOLS_RAN = Symbol.for('agentos.generateText.toolsRan');
|
|
273
|
+
/**
|
|
274
|
+
* Marks `error` as thrown after the call's tools ran, so a fallback walker
|
|
275
|
+
* that called this call as a leg stops instead of restarting on another
|
|
276
|
+
* provider, which would run the tools again. A non-object is wrapped in an
|
|
277
|
+
* Error first.
|
|
278
|
+
*
|
|
279
|
+
* @returns The marked error.
|
|
280
|
+
*/
|
|
281
|
+
function markToolsRan(error) {
|
|
282
|
+
const target = error !== null && typeof error === 'object' ? error : new Error(String(error));
|
|
283
|
+
try {
|
|
284
|
+
Object.defineProperty(target, TOOLS_RAN, { value: true, configurable: true });
|
|
285
|
+
}
|
|
286
|
+
catch {
|
|
287
|
+
// A frozen error cannot carry the mark.
|
|
288
|
+
}
|
|
289
|
+
return target;
|
|
290
|
+
}
|
|
291
|
+
/**
|
|
292
|
+
* Adds a provider's usage report (`ModelUsage`) to a call's running
|
|
293
|
+
* {@link TokenUsage}, as each completed step is added.
|
|
294
|
+
*
|
|
295
|
+
* @internal Shared with streamText.
|
|
296
|
+
*/
|
|
297
|
+
export function addModelUsage(target, usage) {
|
|
298
|
+
if (!usage || typeof usage !== 'object')
|
|
299
|
+
return;
|
|
300
|
+
const u = usage;
|
|
301
|
+
const num = (value) => typeof value === 'number' && Number.isFinite(value) ? value : undefined;
|
|
302
|
+
target.promptTokens += num(u.promptTokens) ?? 0;
|
|
303
|
+
target.completionTokens += num(u.completionTokens) ?? 0;
|
|
304
|
+
target.totalTokens += num(u.totalTokens) ?? 0;
|
|
305
|
+
const cost = num(u.costUSD);
|
|
306
|
+
if (cost !== undefined)
|
|
307
|
+
target.costUSD = (target.costUSD ?? 0) + cost;
|
|
308
|
+
const cacheRead = num(u.cacheReadInputTokens);
|
|
309
|
+
if (cacheRead !== undefined)
|
|
310
|
+
target.cacheReadTokens = (target.cacheReadTokens ?? 0) + cacheRead;
|
|
311
|
+
const cacheWrite = num(u.cacheCreationInputTokens);
|
|
312
|
+
if (cacheWrite !== undefined)
|
|
313
|
+
target.cacheCreationTokens = (target.cacheCreationTokens ?? 0) + cacheWrite;
|
|
314
|
+
const inclusive = num(u.inclusiveInputTokens);
|
|
315
|
+
if (inclusive !== undefined)
|
|
316
|
+
target.inclusiveInputTokens = (target.inclusiveInputTokens ?? 0) + inclusive;
|
|
317
|
+
}
|
|
318
|
+
/**
|
|
319
|
+
* The usage a provider error reports for the request it ended, such as the
|
|
320
|
+
* billed tokens of a refused turn (`details.usage`).
|
|
321
|
+
*
|
|
322
|
+
* @internal Shared with streamText.
|
|
323
|
+
*/
|
|
324
|
+
export function usageOfError(error) {
|
|
325
|
+
return error?.details?.usage;
|
|
326
|
+
}
|
|
327
|
+
/**
|
|
328
|
+
* Whether a usage report shows billable consumption.
|
|
329
|
+
*
|
|
330
|
+
* @internal Shared with streamText.
|
|
331
|
+
*/
|
|
332
|
+
export function hasBillableUsage(usage) {
|
|
333
|
+
return (usage.promptTokens > 0 ||
|
|
334
|
+
usage.completionTokens > 0 ||
|
|
335
|
+
(usage.cacheReadTokens ?? 0) > 0 ||
|
|
336
|
+
(usage.cacheCreationTokens ?? 0) > 0);
|
|
337
|
+
}
|
|
338
|
+
/** The sum of two usage reports; an optional counter stays absent when neither has it. */
|
|
339
|
+
function sumTokenUsage(a, b) {
|
|
340
|
+
const optional = (x, y) => x === undefined && y === undefined ? undefined : (x ?? 0) + (y ?? 0);
|
|
341
|
+
const costUSD = optional(a.costUSD, b.costUSD);
|
|
342
|
+
const inclusiveInputTokens = optional(a.inclusiveInputTokens, b.inclusiveInputTokens);
|
|
343
|
+
const cacheReadTokens = optional(a.cacheReadTokens, b.cacheReadTokens);
|
|
344
|
+
const cacheCreationTokens = optional(a.cacheCreationTokens, b.cacheCreationTokens);
|
|
345
|
+
return {
|
|
346
|
+
promptTokens: a.promptTokens + b.promptTokens,
|
|
347
|
+
completionTokens: a.completionTokens + b.completionTokens,
|
|
348
|
+
totalTokens: a.totalTokens + b.totalTokens,
|
|
349
|
+
...(costUSD !== undefined ? { costUSD } : {}),
|
|
350
|
+
...(inclusiveInputTokens !== undefined ? { inclusiveInputTokens } : {}),
|
|
351
|
+
...(cacheReadTokens !== undefined ? { cacheReadTokens } : {}),
|
|
352
|
+
...(cacheCreationTokens !== undefined ? { cacheCreationTokens } : {}),
|
|
353
|
+
};
|
|
354
|
+
}
|
|
355
|
+
/** Whether `error` was marked by {@link markToolsRan}. */
|
|
356
|
+
function toolsRanBefore(error) {
|
|
357
|
+
return (error !== null &&
|
|
358
|
+
typeof error === 'object' &&
|
|
359
|
+
error[TOOLS_RAN] === true);
|
|
360
|
+
}
|
|
361
|
+
/**
|
|
362
|
+
* Provider error codes for request-level failures that another provider may
|
|
363
|
+
* not share: unreachable endpoints, request timeouts, and retries exhausted
|
|
364
|
+
* inside the provider. Mid-stream codes (STREAM_IDLE_TIMEOUT,
|
|
365
|
+
* STREAM_INCOMPLETE) are left out, because a stream that already delivered
|
|
366
|
+
* text must not be restarted on another provider.
|
|
367
|
+
*/
|
|
368
|
+
const RETRYABLE_PROVIDER_ERROR_CODES = new Set([
|
|
369
|
+
'NETWORK_ERROR',
|
|
370
|
+
'REQUEST_TIMEOUT',
|
|
371
|
+
'REQUEST_HARD_TIMEOUT',
|
|
372
|
+
'TIMEOUT',
|
|
373
|
+
'MAX_RETRIES_REACHED',
|
|
374
|
+
]);
|
|
255
375
|
/**
|
|
256
376
|
* Detect content-policy refusals across providers so the fallback chain
|
|
257
377
|
* can route them to a more permissive model (typically uncensored
|
|
@@ -325,19 +445,29 @@ export function isContentPolicyRefusal(error) {
|
|
|
325
445
|
export function isRetryableError(error) {
|
|
326
446
|
if (!(error instanceof Error))
|
|
327
447
|
return false;
|
|
448
|
+
// A primary that cannot initialize (rejected key, unreachable endpoint)
|
|
449
|
+
// is unusable for this call whatever the cause; the next provider may not be.
|
|
450
|
+
if (error.name === 'ProviderInitializationError')
|
|
451
|
+
return true;
|
|
328
452
|
// Typed provider errors carry the HTTP status as a numeric field. Prefer that
|
|
329
453
|
// over message-grepping, since providers often substitute the body description
|
|
330
454
|
// (e.g. "This request requires more credits...") for the status code.
|
|
331
455
|
const status = error.httpStatus;
|
|
332
456
|
if (typeof status === 'number' && RETRYABLE_HTTP_STATUSES.has(status))
|
|
333
457
|
return true;
|
|
458
|
+
// Typed provider errors name request-level network failures and timeouts
|
|
459
|
+
// by code; their messages vary by provider (OpenAIProvider rewrites an
|
|
460
|
+
// exhausted network failure as "Network error: unable to reach ...").
|
|
461
|
+
const code = error.code;
|
|
462
|
+
if (typeof code === 'string' && RETRYABLE_PROVIDER_ERROR_CODES.has(code))
|
|
463
|
+
return true;
|
|
334
464
|
const msg = error.message;
|
|
335
465
|
// HTTP status codes that warrant a provider switch (string-grepped fallback
|
|
336
466
|
// when the error type is not a typed provider error).
|
|
337
467
|
if (/\b(402|429|500|502|503|504|401|403)\b/.test(msg))
|
|
338
468
|
return true;
|
|
339
469
|
// Network-level failures
|
|
340
|
-
if (/fetch failed|ECONNREFUSED|ETIMEDOUT|ENOTFOUND/i.test(msg))
|
|
470
|
+
if (/fetch failed|ECONNREFUSED|ECONNRESET|ETIMEDOUT|ENOTFOUND|EAI_AGAIN|socket hang up|network error/i.test(msg))
|
|
341
471
|
return true;
|
|
342
472
|
// Provider-specific phrases that always imply a retryable condition.
|
|
343
473
|
// `credit balance` covers Anthropic's billing message ("Your credit
|
|
@@ -597,6 +727,19 @@ export async function generateText(opts) {
|
|
|
597
727
|
let metricUsage;
|
|
598
728
|
let metricProviderId;
|
|
599
729
|
let metricModelId;
|
|
730
|
+
// What this attempt did that a failover must not repeat. Written inside
|
|
731
|
+
// the span callback and read by the fallback walk below, so it lives on an
|
|
732
|
+
// object rather than in narrowed locals.
|
|
733
|
+
// What this attempt consumed, planning and completed steps included. It
|
|
734
|
+
// lives outside the span callback so a failed attempt still reports the
|
|
735
|
+
// tokens it was billed for.
|
|
736
|
+
const attemptUsage = { promptTokens: 0, completionTokens: 0, totalTokens: 0 };
|
|
737
|
+
const toolProgress = { shimRanTool: false };
|
|
738
|
+
// The tool run this call's tool execution contexts belong to, and the
|
|
739
|
+
// step it starts at. A fallback leg that continues the call keeps both,
|
|
740
|
+
// so tools see one session across providers and steps keep counting.
|
|
741
|
+
const helperToolRunId = opts._continuation?.helperToolRunId ?? randomUUID();
|
|
742
|
+
const stepOffset = opts._continuation?.stepOffset ?? 0;
|
|
600
743
|
try {
|
|
601
744
|
const successResult = await withAgentOSSpan('agentos.api.generate_text', async (span) => {
|
|
602
745
|
let { providerId, modelId } = resolveModelOption(opts, 'text');
|
|
@@ -668,7 +811,6 @@ export async function generateText(opts) {
|
|
|
668
811
|
const toolMap = new Map();
|
|
669
812
|
for (const t of tools)
|
|
670
813
|
toolMap.set(t.name, t);
|
|
671
|
-
const helperToolRunId = randomUUID();
|
|
672
814
|
// Build messages
|
|
673
815
|
const messages = [];
|
|
674
816
|
// --- Chain-of-thought injection ---
|
|
@@ -705,15 +847,27 @@ export async function generateText(opts) {
|
|
|
705
847
|
}
|
|
706
848
|
messages.push({ role: 'system', content: parts });
|
|
707
849
|
}
|
|
850
|
+
// Everything above is built from opts (system prompt, chain-of-thought
|
|
851
|
+
// instruction); a failover continuation rebuilds it, so it is left out
|
|
852
|
+
// of the conversation a continuation carries.
|
|
853
|
+
const generatedSystemCount = messages.length;
|
|
708
854
|
if (opts.messages) {
|
|
855
|
+
// Session history replays through here, so keep the tool pairing
|
|
856
|
+
// and thinking fields (see toProviderReplayMessage).
|
|
709
857
|
for (const m of opts.messages)
|
|
710
|
-
messages.push(
|
|
858
|
+
messages.push(toProviderReplayMessage(m));
|
|
711
859
|
}
|
|
712
860
|
// Transcript delta capture (sessions, spec 2026-07-20 §1b): everything
|
|
713
861
|
// from here on is THIS call's contribution. Callers that carry their
|
|
714
862
|
// new user turn inside opts.messages mark how many trailing caller
|
|
715
863
|
// messages belong to the delta.
|
|
716
|
-
|
|
864
|
+
let transcriptDeltaStart = messages.length - (opts._transcriptIncludeTrailingCallerMessages ?? 0);
|
|
865
|
+
// Caller history (its system messages included) that precedes this
|
|
866
|
+
// call's contribution.
|
|
867
|
+
const callerHistoryCount = Math.max(0, transcriptDeltaStart - generatedSystemCount);
|
|
868
|
+
// The plan's system message, when planning adds one; a continuation
|
|
869
|
+
// leaves it out (the fallback plans again if planning is on).
|
|
870
|
+
let planMessage;
|
|
717
871
|
if (opts.prompt)
|
|
718
872
|
messages.push({ role: 'user', content: opts.prompt });
|
|
719
873
|
span?.setAttribute('agentos.api.tool_count', tools.length);
|
|
@@ -724,7 +878,7 @@ export async function generateText(opts) {
|
|
|
724
878
|
}))
|
|
725
879
|
: undefined;
|
|
726
880
|
const allToolCalls = [];
|
|
727
|
-
const totalUsage =
|
|
881
|
+
const totalUsage = attemptUsage;
|
|
728
882
|
// Provider-reported model id of the final step (spec batch-1 C1);
|
|
729
883
|
// additive — the public `model` field keeps the resolved requested id.
|
|
730
884
|
let lastResponseModelId;
|
|
@@ -755,6 +909,7 @@ export async function generateText(opts) {
|
|
|
755
909
|
...(planConfig?.cache !== undefined || opts.cache !== undefined
|
|
756
910
|
? { cache: planConfig?.cache ?? opts.cache }
|
|
757
911
|
: {}),
|
|
912
|
+
...(planConfig?.thinking === false || opts.thinking === false ? { thinking: false } : {}),
|
|
758
913
|
}, totalUsage);
|
|
759
914
|
if (resolvedPlan) {
|
|
760
915
|
// Inject the plan as a system message right after any existing
|
|
@@ -762,7 +917,12 @@ export async function generateText(opts) {
|
|
|
762
917
|
const planPrompt = formatPlanForPrompt(resolvedPlan);
|
|
763
918
|
const firstNonSystem = messages.findIndex((m) => m.role !== 'system');
|
|
764
919
|
const insertIdx = firstNonSystem === -1 ? messages.length : firstNonSystem;
|
|
765
|
-
|
|
920
|
+
planMessage = { role: 'system', content: planPrompt };
|
|
921
|
+
messages.splice(insertIdx, 0, planMessage);
|
|
922
|
+
// The plan lands ahead of this call's transcript delta; keep the
|
|
923
|
+
// delta starting at the same message.
|
|
924
|
+
if (insertIdx <= transcriptDeltaStart)
|
|
925
|
+
transcriptDeltaStart += 1;
|
|
766
926
|
span?.setAttribute('agentos.api.plan_steps', resolvedPlan.steps.length);
|
|
767
927
|
}
|
|
768
928
|
}
|
|
@@ -776,10 +936,12 @@ export async function generateText(opts) {
|
|
|
776
936
|
const runShim = async () => {
|
|
777
937
|
const loopResult = await runEmulatedToolLoop({
|
|
778
938
|
tools: Array.from(toolMap.values()),
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
939
|
+
onToolExecute: () => {
|
|
940
|
+
toolProgress.shimRanTool = true;
|
|
941
|
+
},
|
|
942
|
+
// Native tool turns (session history, a failover continuation)
|
|
943
|
+
// become the shim's own <tool_call> / <tool_response> text.
|
|
944
|
+
messages: toShimMessages(messages),
|
|
783
945
|
maxRoundtrips: shimMaxRoundtrips,
|
|
784
946
|
callModel: async (msgs) => {
|
|
785
947
|
const r = await provider.generateCompletion(resolved.modelId, msgs, {
|
|
@@ -820,12 +982,13 @@ export async function generateText(opts) {
|
|
|
820
982
|
...(opts.requestTimeout !== undefined ? { requestTimeout: opts.requestTimeout } : {}),
|
|
821
983
|
});
|
|
822
984
|
// Aggregate the COMPLETE normalized usage from every shim
|
|
823
|
-
// roundtrip (spec batch-1 review fold)
|
|
824
|
-
//
|
|
825
|
-
//
|
|
826
|
-
//
|
|
827
|
-
// final response identity is captured for telemetry.
|
|
985
|
+
// roundtrip (spec batch-1 review fold) as each call returns, so
|
|
986
|
+
// an attempt that fails on a later round still reports what its
|
|
987
|
+
// earlier rounds consumed. The loop's own {totalTokens} sum is
|
|
988
|
+
// left unused, or it would count twice.
|
|
828
989
|
if (r.usage) {
|
|
990
|
+
if (typeof r.usage.totalTokens === 'number')
|
|
991
|
+
totalUsage.totalTokens += r.usage.totalTokens;
|
|
829
992
|
if (typeof r.usage.promptTokens === 'number')
|
|
830
993
|
totalUsage.promptTokens += r.usage.promptTokens;
|
|
831
994
|
if (typeof r.usage.completionTokens === 'number')
|
|
@@ -853,10 +1016,7 @@ export async function generateText(opts) {
|
|
|
853
1016
|
};
|
|
854
1017
|
},
|
|
855
1018
|
});
|
|
856
|
-
const shimUsage = {
|
|
857
|
-
...totalUsage,
|
|
858
|
-
totalTokens: (totalUsage.totalTokens ?? 0) + loopResult.totalTokens,
|
|
859
|
-
};
|
|
1019
|
+
const shimUsage = { ...totalUsage };
|
|
860
1020
|
metricUsage = shimUsage;
|
|
861
1021
|
// Dual-emit the same root-span attribute pairs as the native
|
|
862
1022
|
// terminals below — the shim early-return is a first-class chat
|
|
@@ -917,6 +1077,9 @@ export async function generateText(opts) {
|
|
|
917
1077
|
let lastCacheDiagnostics;
|
|
918
1078
|
try {
|
|
919
1079
|
for (let step = 0; step < maxSteps; step++) {
|
|
1080
|
+
// The step's index in the whole call: a fallback leg that continues
|
|
1081
|
+
// after completed tool rounds numbers on from them.
|
|
1082
|
+
const runStep = stepOffset + step;
|
|
920
1083
|
// --- onBeforeGeneration hook ---
|
|
921
1084
|
let effectiveMessages = messages;
|
|
922
1085
|
if (opts.onBeforeGeneration) {
|
|
@@ -927,8 +1090,8 @@ export async function generateText(opts) {
|
|
|
927
1090
|
tools: Array.from(toolMap.values()),
|
|
928
1091
|
model: resolved.modelId,
|
|
929
1092
|
provider: resolved.providerId,
|
|
930
|
-
step,
|
|
931
|
-
prompt: opts.prompt,
|
|
1093
|
+
step: runStep,
|
|
1094
|
+
prompt: opts.prompt ?? opts._continuation?.prompt,
|
|
932
1095
|
};
|
|
933
1096
|
const modified = await opts.onBeforeGeneration(hookCtx);
|
|
934
1097
|
if (modified) {
|
|
@@ -942,7 +1105,7 @@ export async function generateText(opts) {
|
|
|
942
1105
|
const response = await withAgentOSSpan('agentos.api.generate_text.step', async (stepSpan) => {
|
|
943
1106
|
stepSpan?.setAttribute('llm.provider', resolved.providerId);
|
|
944
1107
|
stepSpan?.setAttribute('llm.model', resolved.modelId);
|
|
945
|
-
stepSpan?.setAttribute('agentos.api.step',
|
|
1108
|
+
stepSpan?.setAttribute('agentos.api.step', runStep + 1);
|
|
946
1109
|
stepSpan?.setAttribute('agentos.api.tool_count', tools.length);
|
|
947
1110
|
const stepResponse = await provider.generateCompletion(resolved.modelId, effectiveMessages, {
|
|
948
1111
|
tools: toolSchemas,
|
|
@@ -1069,7 +1232,7 @@ export async function generateText(opts) {
|
|
|
1069
1232
|
let textContent = typeof content === 'string' ? content : (content?.text ?? '');
|
|
1070
1233
|
let toolCallsInChoice = resolveDynamicToolCalls(choice.message?.tool_calls, {
|
|
1071
1234
|
text: textContent,
|
|
1072
|
-
step,
|
|
1235
|
+
step: runStep,
|
|
1073
1236
|
toolsAvailable: tools.length > 0,
|
|
1074
1237
|
});
|
|
1075
1238
|
// --- onAfterGeneration hook ---
|
|
@@ -1091,7 +1254,7 @@ export async function generateText(opts) {
|
|
|
1091
1254
|
text: textContent,
|
|
1092
1255
|
toolCalls: toolCallRecords,
|
|
1093
1256
|
usage: stepUsage,
|
|
1094
|
-
step,
|
|
1257
|
+
step: runStep,
|
|
1095
1258
|
...(response.cacheDiagnostics !== undefined
|
|
1096
1259
|
? { cacheDiagnostics: response.cacheDiagnostics }
|
|
1097
1260
|
: {}),
|
|
@@ -1139,6 +1302,9 @@ export async function generateText(opts) {
|
|
|
1139
1302
|
...((choice.message?.thinking) !== undefined
|
|
1140
1303
|
? { thinking: choice.message.thinking }
|
|
1141
1304
|
: {}),
|
|
1305
|
+
...(choice.message?.thinkingBlocks?.length
|
|
1306
|
+
? { thinkingBlocks: choice.message.thinkingBlocks }
|
|
1307
|
+
: {}),
|
|
1142
1308
|
});
|
|
1143
1309
|
return {
|
|
1144
1310
|
transcriptDelta: messages.slice(transcriptDeltaStart),
|
|
@@ -1208,7 +1374,7 @@ export async function generateText(opts) {
|
|
|
1208
1374
|
name: fnName,
|
|
1209
1375
|
args: parsedArgs,
|
|
1210
1376
|
id: tcId || '',
|
|
1211
|
-
step,
|
|
1377
|
+
step: runStep,
|
|
1212
1378
|
};
|
|
1213
1379
|
const hookResult = await opts.onBeforeToolExecution(hookInfo);
|
|
1214
1380
|
if (hookResult === null) {
|
|
@@ -1229,7 +1395,7 @@ export async function generateText(opts) {
|
|
|
1229
1395
|
}
|
|
1230
1396
|
if (tool) {
|
|
1231
1397
|
try {
|
|
1232
|
-
const result = await tool.execute(parsedArgs, buildHelperToolExecutionContext('generateText', helperToolRunId,
|
|
1398
|
+
const result = await tool.execute(parsedArgs, buildHelperToolExecutionContext('generateText', helperToolRunId, runStep, tcId || undefined));
|
|
1233
1399
|
record.result = result.output;
|
|
1234
1400
|
record.error = result.success ? undefined : result.error;
|
|
1235
1401
|
messages.push({
|
|
@@ -1257,6 +1423,12 @@ export async function generateText(opts) {
|
|
|
1257
1423
|
}
|
|
1258
1424
|
allToolCalls.push(record);
|
|
1259
1425
|
}
|
|
1426
|
+
toolProgress.completedToolRounds = {
|
|
1427
|
+
messages: messages.slice(generatedSystemCount).filter((m) => m !== planMessage),
|
|
1428
|
+
callerHistoryCount,
|
|
1429
|
+
toolCalls: [...allToolCalls],
|
|
1430
|
+
steps: step + 1,
|
|
1431
|
+
};
|
|
1260
1432
|
continue;
|
|
1261
1433
|
}
|
|
1262
1434
|
metricUsage = totalUsage;
|
|
@@ -1285,6 +1457,9 @@ export async function generateText(opts) {
|
|
|
1285
1457
|
...((choice.message?.thinking) !== undefined
|
|
1286
1458
|
? { thinking: choice.message.thinking }
|
|
1287
1459
|
: {}),
|
|
1460
|
+
...(choice.message?.thinkingBlocks?.length
|
|
1461
|
+
? { thinkingBlocks: choice.message.thinkingBlocks }
|
|
1462
|
+
: {}),
|
|
1288
1463
|
});
|
|
1289
1464
|
return {
|
|
1290
1465
|
transcriptDelta: messages.slice(transcriptDeltaStart),
|
|
@@ -1372,8 +1547,25 @@ export async function generateText(opts) {
|
|
|
1372
1547
|
if (metricProviderId && !(error instanceof LLMProviderCircuitOpenError)) {
|
|
1373
1548
|
globalLLMProviderHealth.recordFailure(metricProviderId, error);
|
|
1374
1549
|
}
|
|
1375
|
-
//
|
|
1376
|
-
//
|
|
1550
|
+
// The failed attempt is billed for its completed steps and for a step
|
|
1551
|
+
// the provider ended with usage attached (a refused turn). It is metered
|
|
1552
|
+
// here, once: this call's ledger row and a usage event of its own; a
|
|
1553
|
+
// fallback leg meters itself.
|
|
1554
|
+
addModelUsage(attemptUsage, usageOfError(error));
|
|
1555
|
+
metricUsage = attemptUsage;
|
|
1556
|
+
if (hasBillableUsage(attemptUsage)) {
|
|
1557
|
+
fireLlmUsageObserver({
|
|
1558
|
+
provider: metricProviderId ?? 'unknown',
|
|
1559
|
+
model: metricModelId ?? 'unknown',
|
|
1560
|
+
usage: { ...attemptUsage },
|
|
1561
|
+
source: opts.source,
|
|
1562
|
+
...(opts.__fallbackDepth ? { fallbackDepth: opts.__fallbackDepth } : {}),
|
|
1563
|
+
finishReason: 'error',
|
|
1564
|
+
surface: 'generateText',
|
|
1565
|
+
durationMs: Date.now() - rootStartedAt,
|
|
1566
|
+
});
|
|
1567
|
+
}
|
|
1568
|
+
// The failed primary heads the fallback trail.
|
|
1377
1569
|
const primaryProviderId = metricProviderId;
|
|
1378
1570
|
const primaryModelId = metricModelId;
|
|
1379
1571
|
const fallbackHops = [
|
|
@@ -1390,8 +1582,11 @@ export async function generateText(opts) {
|
|
|
1390
1582
|
const effectiveFallbacks = opts.fallbackProviders === undefined
|
|
1391
1583
|
? buildPolicyAwareFallbackChain(opts.policyTier, metricProviderId)
|
|
1392
1584
|
: opts.fallbackProviders;
|
|
1585
|
+
// A call whose prompt-shim tools already ran cannot be continued, and a
|
|
1586
|
+
// restart would run them again; it surfaces the error instead.
|
|
1393
1587
|
if (effectiveFallbacks.length &&
|
|
1394
|
-
isRetryableError(error)
|
|
1588
|
+
isRetryableError(error) &&
|
|
1589
|
+
!toolProgress.shimRanTool) {
|
|
1395
1590
|
let lastError = error;
|
|
1396
1591
|
let attempt = 0;
|
|
1397
1592
|
for (const fb of effectiveFallbacks) {
|
|
@@ -1482,6 +1677,34 @@ export async function generateText(opts) {
|
|
|
1482
1677
|
// end-to-end. The final entry passes [] -> explicit opt-out -> throw.
|
|
1483
1678
|
fallbackProviders: effectiveFallbacks.slice(attempt),
|
|
1484
1679
|
onFallback: undefined,
|
|
1680
|
+
// Continue after the tool rounds this attempt completed: the leg
|
|
1681
|
+
// receives the conversation so far (prompt included, so no new
|
|
1682
|
+
// prompt), counts all of it as this call's transcript delta, and
|
|
1683
|
+
// keeps the steps that remain.
|
|
1684
|
+
_continuation: {
|
|
1685
|
+
helperToolRunId,
|
|
1686
|
+
stepOffset: stepOffset + (toolProgress.completedToolRounds?.steps ?? 0),
|
|
1687
|
+
prompt: opts.prompt ?? opts._continuation?.prompt,
|
|
1688
|
+
},
|
|
1689
|
+
...(toolProgress.completedToolRounds
|
|
1690
|
+
? {
|
|
1691
|
+
messages: toolProgress.completedToolRounds.messages,
|
|
1692
|
+
prompt: undefined,
|
|
1693
|
+
_transcriptIncludeTrailingCallerMessages: toolProgress.completedToolRounds.messages.length - toolProgress.completedToolRounds.callerHistoryCount,
|
|
1694
|
+
maxSteps: Math.max(1, (opts.maxSteps ?? 1) - toolProgress.completedToolRounds.steps),
|
|
1695
|
+
// Claude's budgeted thinking requires the tool turn being
|
|
1696
|
+
// answered to open with its signed thinking, and a turn
|
|
1697
|
+
// another model ran has none, so the continuation runs
|
|
1698
|
+
// without a thinking budget. Adaptive thinking turns itself
|
|
1699
|
+
// off for such a turn. `thinking: false` stays as asked.
|
|
1700
|
+
...(typeof opts.thinking === 'object' &&
|
|
1701
|
+
opts.thinking !== null &&
|
|
1702
|
+
(fb.provider === 'anthropic' || /claude/i.test(fb.model ?? '')) &&
|
|
1703
|
+
toolTurnLacksThinking(toolProgress.completedToolRounds.messages)
|
|
1704
|
+
? { thinking: undefined }
|
|
1705
|
+
: {}),
|
|
1706
|
+
}
|
|
1707
|
+
: {}),
|
|
1485
1708
|
});
|
|
1486
1709
|
fallbackLogger.info('provider fallback succeeded', {
|
|
1487
1710
|
event: 'fallback_succeeded',
|
|
@@ -1492,14 +1715,16 @@ export async function generateText(opts) {
|
|
|
1492
1715
|
attempt,
|
|
1493
1716
|
});
|
|
1494
1717
|
metricStatus = 'ok';
|
|
1495
|
-
metricUsage = fallbackResult.usage;
|
|
1496
|
-
metricProviderId = fallbackResult.provider;
|
|
1497
|
-
metricModelId = fallbackResult.model;
|
|
1498
1718
|
fallbackHops.push(...(fallbackResult.fallback?.hops ?? [
|
|
1499
1719
|
{ provider: fallbackResult.provider, model: fallbackResult.model, ok: true },
|
|
1500
1720
|
]));
|
|
1501
1721
|
return {
|
|
1502
1722
|
...fallbackResult,
|
|
1723
|
+
// The call's usage covers the failed attempt and the leg.
|
|
1724
|
+
usage: sumTokenUsage(attemptUsage, fallbackResult.usage),
|
|
1725
|
+
...(toolProgress.completedToolRounds
|
|
1726
|
+
? { toolCalls: [...toolProgress.completedToolRounds.toolCalls, ...fallbackResult.toolCalls] }
|
|
1727
|
+
: {}),
|
|
1503
1728
|
fallback: {
|
|
1504
1729
|
fired: true,
|
|
1505
1730
|
finalProvider: fallbackResult.provider,
|
|
@@ -1511,6 +1736,10 @@ export async function generateText(opts) {
|
|
|
1511
1736
|
catch (fbError) {
|
|
1512
1737
|
lastError = fbError;
|
|
1513
1738
|
fallbackHops.push({ provider: fb.provider, model: fb.model, ok: false });
|
|
1739
|
+
// The leg ran tools before it failed. A later leg would start
|
|
1740
|
+
// from a conversation that does not show them and run them again.
|
|
1741
|
+
if (toolsRanBefore(fbError))
|
|
1742
|
+
break;
|
|
1514
1743
|
}
|
|
1515
1744
|
}
|
|
1516
1745
|
// All fallbacks exhausted: fall through to throw
|
|
@@ -1524,10 +1753,11 @@ export async function generateText(opts) {
|
|
|
1524
1753
|
errorMessage: lastErr.message.slice(0, 200),
|
|
1525
1754
|
});
|
|
1526
1755
|
metricStatus = 'error';
|
|
1527
|
-
throw lastError;
|
|
1756
|
+
throw toolProgress.shimRanTool || toolProgress.completedToolRounds ? markToolsRan(lastError) : lastError;
|
|
1528
1757
|
}
|
|
1529
1758
|
metricStatus = 'error';
|
|
1530
|
-
|
|
1759
|
+
// Marked so a walker that called this one as a fallback leg stops too.
|
|
1760
|
+
throw toolProgress.shimRanTool || toolProgress.completedToolRounds ? markToolsRan(error) : error;
|
|
1531
1761
|
}
|
|
1532
1762
|
finally {
|
|
1533
1763
|
try {
|