Rebase/pattern v3 foundation - #5
Merged
Merged
Conversation
…opriate gemini thinkingLevel LOW/HIGH, fall back on budget if not gemini 3 or other effort
…hing ReasonningEffort)
+ binary - add constructors (from_base64, from_url, from_file)"
…del does not support)
…dding:: for subscription, same Adapter)
Context for the pattern-v3-foundation rebase work. Captures the chosen upstream target (v0.6.0-beta.17), API-surface verification, the list of old-fork patches being dropped (all subsumed upstream or moved to pattern-side), and the new fork patches being added (SystemPrompt enum with per-block cache_control; claude-opus-4-7 adaptive/effort support). Pattern-internal; drop this commit if cherry-picking kept patches upstream.
Introduces `ChatRequest::system_blocks: Option<Vec<SystemBlock>>` alongside the existing `system: Option<String>`. When set, the Anthropic adapter uses the blocks as the authoritative system-prompt source, ignoring `system` and any ChatRole::System messages for system-prompt construction. Each block carries its own optional cache_control, so callers can emit multi-segment system arrays with independent TTLs (1h-before-5m ordering still the caller's responsibility per Anthropic's cache semantics). Other adapters (Gemini, OpenAI, etc.) ignore system_blocks — callers targeting them set `system` as usual. This keeps the blast radius to Anthropic and preserves wire-compat for every other provider. Rationale: pattern's v3 foundation phase 5 three-segment cache layout needs per-block cache_control on the system prompt. Upstream only allows array emission as a side-effect of message-level cache_control, with no per-block TTL granularity. Tests cover: single-block no-cache array emission, multiple blocks with mixed TTLs (1h before 5m), system_blocks overrides system string and system-role messages, empty vec produces no system, and the legacy string-only path is preserved when system_blocks is None.
Pattern primarily targets `claude-opus-4-7`. Upstream's
`ReasoningEffort::XHigh` regex dispatch handles 4.7 for effort-string
resolution (via `is_opus_4_7_or_higher()`), but the three opt-in
support arrays — `SUPPORT_EFFORT_MODELS`,
`SUPPORT_REASONING_MAX_MODELS`, `SUPPORT_ADAPTTIVE_THINK_MODELS` —
still list only 4-6 models, so 4.7 does not currently receive
`output_config.effort` emission or `thinking: {type: adaptive}`
payloads.
Adds `claude-opus-4-7` to each array. Sonnet 4.7 has not shipped —
revisit when it does. Comment above the arrays flags these as
fork-local additions to drop when upstream covers them.
Also pins `ANTHROPIC_VERSION = "2023-06-01"` in a regression test
to make any future bump a deliberate change, not a drive-by.
Tests: `test_opus_4_7_reasoning_support_arrays`,
`test_anthropic_version_constant_pinned`.
Build-metadata suffix marks this as pattern's fork tip so pattern's Cargo.lock captures the delta relative to the upstream v0.6.0-beta.17 base. Cargo ignores `+pattern.1` for semver resolution but preserves the string in lockfiles and registry metadata, which is exactly what we want — the fork is wire-compatible with upstream at the semver level while still being visibly distinct.
Callers downstream (pattern, etc.) need access to response headers on
HTTP failure — specifically:
- Retry-After (RFC 7231) for standard rate-limit back-off timing.
- Provider-specific reset hints (e.g. Anthropic's
anthropic-ratelimit-unified-5h-reset for subscription-tier caps) for
surfacing 'retry at time T' semantics that aren't expressible via
Retry-After alone.
- Request-id correlation headers for debugging provider-side issues.
Currently only status + canonical_reason + body are captured; headers
are dropped at stream-error construction time (src/webc/web_stream.rs).
Adds `headers: Box<reqwest::header::HeaderMap>` to Error::HttpError.
Boxed to keep the enum size minimal (HeaderMap stores its names + values
via hashes, but the struct itself plus an empty table is still on the
order of a cache line). Constructed at the stream-error site by cloning
response.headers() before response.text() consumes the body.
No behaviour change for callers that previously pattern-matched on
`HttpError { status, canonical_reason, body, .. }` with the .. rest
pattern. Exhaustive matches need to be updated to include the field.
Tests: existing 68/68 upstream unit tests still pass.
…c adapters Change ToolResponse.content from String to serde_json::Value so that Anthropic's nested block array format (tool_result.content as array of typed blocks) can be expressed without stringification. - ToolResponse::new() kept for back-compat; wraps text as Value::String - ToolResponse::new_content() added for structured payloads - size() updated to serialize Value for byte count (approximate) - content_as_string() helper added in adapter::adapters::support - OpenAI, OpenAI-Responses, Gemini (both locations), Ollama adapters all use content_as_string() — stringify Value::String verbatim, serialize any other shape to compact JSON - Anthropic adapter passes Value through directly (string OR array both valid per Anthropic docs)
Verify that ToolResponse with content: Value::Array(blocks) serializes
as a native JSON array on the Anthropic wire, NOT as a stringified
"[{...}]" string. This guards the pattern_runtime segment-3 splice
which folds seg3 text into tool_result.content as nested blocks.
Finding documented in the test: json!({"content": value_array}) emits
a native array — the expected behavior.
…s (pattern foundation follow-up)
…response path (follow-up to 116359b3) to_chat_response previously dropped the `signature` field from Anthropic `thinking` content blocks, making non-streaming thinking responses unable to round-trip signed thinking blocks across tool-use turns. Fix: extract the `signature` field alongside the `thinking` text, emit a `ContentPart::ThoughtSignature` (before) and `ContentPart::ReasoningContent` (after) into `ChatResponse.content` — mirroring what the streaming path already does via `InterStreamEnd.captured_thought_signatures`. Also handles the missing-signature case gracefully (no `ThoughtSignature` part emitted, no panic). Two new unit tests cover the fix: - `test_to_chat_response_captures_thinking_signature_and_content`: verifies both parts are present with correct values and correct ordering. - `test_to_chat_response_thinking_without_signature_does_not_emit_signature_part`: verifies graceful degradation when the wire response omits the signature.
…tbound (pattern v3 foundation) Gemini: ThoughtSignature parts in assistant messages are now silently dropped instead of being forwarded as Gemini-native thoughtSignature wire blocks. Anthropic-originated opaque signatures cannot be validated by Gemini and would cause wire rejections. The pending_thought buffer and its re-emission logic are removed; the Gemini-3 skip_thought_signature_validator sentinel is now always injected on the first tool call (the correct fallback when no native thought signature is present). OpenAI: ThoughtSignature was already silently dropped (unchanged). ReasoningContent passthrough is intentionally preserved for DeepSeek/Kimi compatibility (verified by existing test). New unit tests: - gemini: assistant_drops_thought_signature_and_reasoning_content_on_outbound - openai: test_cross_provider_thought_signature_dropped_on_openai_outbound
…ingBlock with provenance (Task F) Replace the split ContentPart::ThoughtSignature(String) + ContentPart::ReasoningContent(String) variants with a unified ContentPart::ThinkingBlock(ThinkingBlock) that carries provenance (AdapterKind), optional text, and optional signature. Add thought_signatures_provenance field to ToolCall. Change InterStreamEnd::captured_thought_signatures to Option<(Vec<String>, AdapterKind)>. Add push_thought_signature/take_thought_signatures helpers to StreamerCapturedData. Update all adapters (Anthropic, Gemini, OpenAI, Ollama, openai_resp) to use the new types with correct outbound gating based on provenance. Restore Gemini native round-trip path gated on provenance == Gemini, fixing the regression from 94e205b.
… common.rs doc syntax Two Phase 6 foundation follow-ups from the adversarial review: 1. Gemini inbound parse: previously collected all thoughtSignatures from the response globally and attached them to the first tool call. Gemini-3 spec is one signature per function_call; the old behaviour broke Gemini→Gemini multi-tool-call replay because subsequent tool calls lost their signatures and Gemini validator rejects round-trips with missing signatures. Fix: pair each thoughtSignature with its adjacent functionCall (signature precedes function_call in the wire format — both fields appear on the same JSON part object; body_to_gemini_chat_response pushes ThoughtSignature immediately before ToolCall for each such part, so the ordering in gemini_content is guaranteed). Use a pending_signature state variable: ThoughtSignature sets it, ToolCall consumes it. Set thought_signatures_provenance = Some(AdapterKind::Gemini) on each recovered ToolCall. Also fix the all_signatures tracking bug in the in-progress working-tree version (was pushing via pending_signature.as_deref() after replace(), which read the new value rather than recording it immediately on arrival). Edge cases (more/fewer signatures than calls) handled with warn+best-effort behaviour. 2. common.rs lines 16,19: confirmed already correct (/// doc comment prefix present) as of bdad78c — no change needed. Unit tests added for the multi-call pairing and the sparse-signature edge cases: - inbound_two_function_calls_each_get_own_signature - inbound_one_signature_two_tool_calls_assigns_to_first - inbound_two_signatures_one_tool_call_attaches_extra_to_last
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
No description provided.