Five fixes for latent regressions surfaced during the v1.13.x.cosmetic revert investigation. None alter schema or compaction; all cleanup against the v1.13.1-A AI SDK migration's hidden surface. (1) provider.ts — includeUsage: true on createOpenAICompatible. @ai-sdk/openai-compatible defaults this false, omitting stream_options.include_usage from the request body; llama-swap never emitted the usage block, so result.usage.inputTokens/outputTokens resolved undefined and tokens_used / ctx_used landed NULL in every assistant row since v1.13.1-A. No historical backfill. (2) MessageList.tsx — hasText = m.content.trim().length > 0. AI SDK v6 streaming occasionally emits a leading "\n" text-delta on tool-call-only turns; the literal newline passed length > 0 and rendered an empty bubble + ActionRow between every tool call. Trim catches it without changing semantics for genuine content. (3) MessageBubble.tsx — same trim on hasContent for the no-tool-calls path. Defensive symmetry with MessageList.flatten. (4) payload.ts — buildMessagesPayload skips assistant rows with status='failed' AND assistant rows with status='complete' + empty content + no tool_calls. Without this, a trailing empty/failed assistant + the next attempt's placeholder produced "Cannot have 2 or more assistant messages at the end of the list" rejections from the OpenAI-compatible upstream after cap-hit + Continue. (5) budget.ts — BUDGET_NO_AGENT 15 → 30. Every tool in ALL_TOOLS is read-only today; the 15-cap was forward-looking for write tools that haven't landed. No-agent mode now matches BUDGET_READ_ONLY. 47 LoC across 5 files. 190/190 server tests pass. Verified live: new assistant turns populate StatsLine token data; single-tool-call turns no longer render the stray empty-bubble + ActionRow between tool calls; Continue after cap-hit no longer hits the trailing-assistant API rejection.
26 lines
1.3 KiB
TypeScript
26 lines
1.3 KiB
TypeScript
import type { Agent } from '../../types/api.js';
|
|
import { READ_ONLY_TOOL_NAMES } from '../tools.js';
|
|
|
|
// v1.8.2: tool-call budget defaults. Resolved per-turn by resolveToolBudget.
|
|
// - Agent with explicit max_tool_calls: that value.
|
|
// - Agent with read-only-only tools: BUDGET_READ_ONLY (30).
|
|
// - Agent with any non-read-only tool: BUDGET_NON_READ_ONLY (10).
|
|
// - No agent (raw chat): BUDGET_NO_AGENT (30).
|
|
// v1.13.7: bumped BUDGET_NO_AGENT 15→30 to match BUDGET_READ_ONLY. Every tool
|
|
// in ALL_TOOLS today is read-only (see services/tools.ts comment at
|
|
// READ_ONLY_TOOL_NAMES); the cautious 15-cap was a forward-looking guard for
|
|
// write tools that haven't landed yet. No-agent mode gets the same toolset as
|
|
// an all-read-only agent at runtime, so they should share the same budget.
|
|
export const BUDGET_READ_ONLY = 30;
|
|
export const BUDGET_NON_READ_ONLY = 10;
|
|
export const BUDGET_NO_AGENT = 30;
|
|
|
|
const READ_ONLY_SET: ReadonlySet<string> = new Set(READ_ONLY_TOOL_NAMES);
|
|
|
|
export function resolveToolBudget(agent: Agent | null): number {
|
|
if (agent?.max_tool_calls != null) return agent.max_tool_calls;
|
|
if (!agent) return BUDGET_NO_AGENT;
|
|
const allReadOnly = agent.tools.every((t) => READ_ONLY_SET.has(t));
|
|
return allReadOnly ? BUDGET_READ_ONLY : BUDGET_NON_READ_ONLY;
|
|
}
|