feat(catalog): added capability flags and thinking support for Claude Sonnet 5
- Added an `isAnthropicAdaptiveGenAtLeast` utility to classify adaptive-thinking Claude generations at or above a given version threshold. - Enabled adaptive thinking display, sampling restrictions, and mid-conversation system message flags for Claude Sonnet 5+ models. - Updated Bedrock and OpenRouter adaptive reasoning effort maps to support five-tier scales on Sonnet 5+ models.
This commit is contained in:
@@ -310,4 +310,68 @@ describe("AgentSession thinking-loop retry", () => {
|
||||
expect(assistants).toHaveLength(1);
|
||||
expect(assistants[0].content).toEqual([{ type: "text", text: "Recovered after retry." }]);
|
||||
});
|
||||
|
||||
it("injects a redirect notice on each consecutive thinking-loop retry", async () => {
|
||||
const model = createMockModel({ provider: "openrouter", id: "google/gemini-3.5-flash" }).model;
|
||||
const modelRegistry = new ModelRegistry(authStorage);
|
||||
const calls: string[] = [];
|
||||
const contexts: Context[] = [];
|
||||
const agent = new Agent({
|
||||
getApiKey: requestedModel => `${requestedModel.provider}-test-key`,
|
||||
initialState: {
|
||||
model,
|
||||
systemPrompt: ["Test"],
|
||||
tools: [],
|
||||
messages: [],
|
||||
},
|
||||
convertToLlm,
|
||||
streamFn: (requestedModel, context, _options?: SimpleStreamOptions) => {
|
||||
calls.push(`${requestedModel.provider}/${requestedModel.id}`);
|
||||
contexts.push(context);
|
||||
return calls.length <= 2 ? errorIdOnlyThinkingLoopStream(requestedModel) : successStream(requestedModel);
|
||||
},
|
||||
});
|
||||
const settings = Settings.isolated({
|
||||
"compaction.enabled": false,
|
||||
"retry.enabled": true,
|
||||
"retry.baseDelayMs": 0,
|
||||
"retry.maxDelayMs": 5_000,
|
||||
"retry.maxRetries": 2,
|
||||
"retry.modelFallback": false,
|
||||
"todo.enabled": false,
|
||||
"model.loopGuard.enabled": true,
|
||||
});
|
||||
settings.setModelRole("default", `${model.provider}/${model.id}`);
|
||||
session = new AgentSession({
|
||||
agent,
|
||||
sessionManager: SessionManager.inMemory(),
|
||||
settings,
|
||||
modelRegistry,
|
||||
});
|
||||
vi.spyOn(scheduler, "wait").mockResolvedValue(undefined);
|
||||
|
||||
await session.prompt("Trigger redirect injection after two thinking loops");
|
||||
await session.waitForIdle();
|
||||
|
||||
expect(calls).toHaveLength(3);
|
||||
const redirects = session.agent.state.messages.filter(
|
||||
(message): message is CustomMessage =>
|
||||
message.role === "custom" && message.customType === "thinking-loop-redirect",
|
||||
);
|
||||
expect(redirects).toHaveLength(2);
|
||||
const extractText = (content: string | Array<{ type: string; text?: string }>): string =>
|
||||
typeof content === "string"
|
||||
? content
|
||||
: content.map(part => (part.type === "text" ? (part.text ?? "") : "")).join("");
|
||||
const thirdAttemptRedirectDevMsgs = contexts[2].messages.filter(
|
||||
message => message.role === "developer" && extractText(message.content).includes("thinking_loop_detected"),
|
||||
);
|
||||
expect(thirdAttemptRedirectDevMsgs).toHaveLength(2);
|
||||
|
||||
const assistants = session.agent.state.messages.filter(
|
||||
(message): message is AssistantMessage => message.role === "assistant",
|
||||
);
|
||||
expect(assistants).toHaveLength(1);
|
||||
expect(assistants[0].content).toEqual([{ type: "text", text: "Recovered after retry." }]);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user