fix(ai): skipped context management for injected clients
Injected Anthropic clients bypass buildAnthropicClientOptions, so this package cannot add the context-management beta header their SDK instance would need before accepting context_management.clear_thinking_20251015. Omit context_management for options.client requests while preserving thinking itself, and add regression coverage for injected-client payload shaping. Fixes #3288
This commit is contained in:
@@ -4,7 +4,7 @@
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fixed Anthropic-compatible thinking requests sending replayed thinking blocks without `context_management.keep: "all"`, preserving multi-turn reasoning context for API-key providers. API-key requests now also advertise the required `context-management-2025-06-27` beta header so the field is honored instead of rejected. GitHub Copilot's Anthropic proxy is excluded because it strips Anthropic betas and demotes thinking blocks to text upstream. ([#3288](https://github.com/can1357/oh-my-pi/issues/3288))
|
||||
- Fixed Anthropic-compatible thinking requests sending replayed thinking blocks without `context_management.keep: "all"`, preserving multi-turn reasoning context for API-key providers. API-key requests now also advertise the required `context-management-2025-06-27` beta header so the field is honored instead of rejected. Injected SDK clients and GitHub Copilot's Anthropic proxy are excluded because this code path cannot add the beta to caller-owned clients, while Copilot strips Anthropic betas and demotes thinking blocks to text upstream. ([#3288](https://github.com/can1357/oh-my-pi/issues/3288))
|
||||
|
||||
## [16.1.15] - 2026-06-22
|
||||
|
||||
|
||||
@@ -2966,12 +2966,15 @@ function buildParams(
|
||||
// on the wire, so the model loses the prior reasoning chain across turns
|
||||
// and the KV cache misses every turn (#3288). Narrowing this guard back
|
||||
// to `isOAuthToken` regresses every API-key thinking provider. Skip
|
||||
// Copilot — its proxy strips Anthropic betas (so the required
|
||||
// `context-management-2025-06-27` header never lands) and the compat
|
||||
// flag demotes thinking blocks to text, so `keep: "all"` is a no-op
|
||||
// that risks the proxy rejecting an unrecognized field.
|
||||
// injected clients because this code cannot add the required
|
||||
// `context-management-2025-06-27` beta to caller-owned SDK clients. Skip
|
||||
// Copilot because its proxy strips Anthropic betas and demotes thinking
|
||||
// blocks to text upstream, so `keep: "all"` is a no-op that risks proxy
|
||||
// rejection of an unrecognized field.
|
||||
const shouldKeepThinkingContext =
|
||||
model.provider !== "github-copilot" && (thinking?.type === "adaptive" || thinking?.type === "enabled");
|
||||
!options?.client &&
|
||||
model.provider !== "github-copilot" &&
|
||||
(thinking?.type === "adaptive" || thinking?.type === "enabled");
|
||||
const contextManagement = shouldKeepThinkingContext
|
||||
? { edits: [{ type: "clear_thinking_20251015" as const, keep: "all" as const }] }
|
||||
: undefined;
|
||||
|
||||
@@ -576,6 +576,36 @@ describe("anthropic stream envelope handling", () => {
|
||||
expect(capturedParams?.tools?.map(tool => tool.name)).toEqual(["web_search"]);
|
||||
expect(capturedOptions?.headers).toEqual({ "X-Umans-Websearch-Provider": "exa" });
|
||||
});
|
||||
|
||||
it("does not send context_management through injected clients", async () => {
|
||||
type CapturedPayload = {
|
||||
thinking?: { type?: string };
|
||||
context_management?: unknown;
|
||||
};
|
||||
let capturedParams: CapturedPayload | undefined;
|
||||
const client: AnthropicMessagesClientLike = {
|
||||
messages: {
|
||||
create(params) {
|
||||
capturedParams = params as CapturedPayload;
|
||||
return createMockRequest(createTextSuccessEvents("done"));
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const stream = streamAnthropic(model, context, {
|
||||
client,
|
||||
thinkingEnabled: true,
|
||||
});
|
||||
const events: AssistantMessageEvent[] = [];
|
||||
for await (const event of stream) {
|
||||
events.push(event);
|
||||
}
|
||||
const result = await stream.result();
|
||||
|
||||
expect(result.content).toEqual([{ type: "text", text: "done" }]);
|
||||
expect(capturedParams?.thinking?.type).toBe("enabled");
|
||||
expect(capturedParams?.context_management).toBeUndefined();
|
||||
});
|
||||
it("unwraps thinking blocks that Anthropic streams with literal thinking tags", async () => {
|
||||
const wrappedThinking =
|
||||
"<thinking>\n<thinking>\nCheck logs before accepting container health.\n</thinking></thinking>";
|
||||
|
||||
Reference in New Issue
Block a user