Skip to content

Rebase/pattern v3 foundation - #5

Merged
orual merged 361 commits into
mainfrom
rebase/pattern-v3-foundation
Apr 21, 2026
Merged

Rebase/pattern v3 foundation#5
orual merged 361 commits into
mainfrom
rebase/pattern-v3-foundation

Conversation

@orual

@orual orual commented Apr 21, 2026

Copy link
Copy Markdown
Owner

No description provided.

jeremychone and others added 30 commits November 24, 2025 15:04
…opriate gemini thinkingLevel LOW/HIGH, fall back on budget if not gemini 3 or other effort
+ binary - add constructors (from_base64, from_url, from_file)"
jeremychone and others added 29 commits April 4, 2026 17:15
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.
…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
@orual
orual merged commit 8ed73c7 into main Apr 21, 2026
1 check failed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.