Skip to content

feat(OMN-20910): native Anthropic Messages protocol with prompt caching for the review gateway - #4209

Merged
jonahgabriel merged 5 commits into
devfrom
omn-20910-native-messages-cache
Oct 19, 2026
Merged

jonahgabriel merged 5 commits into
devfrom
omn-20910-native-messages-cache

Conversation

@jonahgabriel

Copy link
Copy Markdown
Contributor

This change adds a native Anthropic Messages protocol with prompt caching in the one sanctioned model caller, used by the Claude review gateway. Requests gain an inference_protocol switch, typed system blocks and messages with optional 5m or 1h cache TTLs, and results gain a typed stop reason with cache creation and read token counts. The BYOK catalogue gains an api_native anthropic row, and the gateway resolves that plan, splits its system and user messages into cached and uncached blocks, and returns cache usage.

What changed

  • node_llm_delegation_call_effect: the request gains inference_protocol (openai_chat default, anthropic_messages new), typed system_blocks and messages (text blocks with optional 5m or 1h cache TTL), tool_schema, tool_choice, and provider_headers. Blocks refuse prompt and system_prompt. More than four cache breakpoints, a credential header in provider_headers, response_format on the native protocol, and native fields on the chat protocol are each refused.
  • The result gains a typed stop_reason (EnumAnthropicStopReason), cache_creation_input_tokens (with 5m and 1h parts), and cache_read_input_tokens; the finished observation carries stop_reason and the cache token counts, and finish_reason is mapped from stop_reason (max_tokens gives length).
  • The native call sends the key in x-api-key plus an anthropic-version header, returns the forced tool input as content, and prices cache writes at 1.25x (5m) and 2x (1h) and reads at 0.1x of the input price.
  • byok_provider_backends.v1.yaml: a new anthropic row with plan api_native, backend byok-anthropic-native, endpoint https://api.anthropic.com/v1/messages; the existing api row is now default_plan true, so a caller naming no plan keeps the chat-completions row.
  • node_claude_review_gateway_effect: resolves api_native explicitly; splits the system message into cached blocks and the user message into a diff block (5m cache) and a persona block (uncached) on a boundary marker; a json_schema response_format becomes a forced tool; a max_tokens stop answers finish_reason length; cache usage is returned in usage; a review with no user message answers 400.
  • Contract contracts/OMN-20910.yaml gains requirement req-native-messages-protocol with AC30, AC31, AC32, three dod_evidence entries bound to them, and a container deploy check.

Tests

  • New tests/test_omn20910_native_messages_protocol.py (23 tests): a fake provider simulates the prompt cache from the wire payload (tools, then system, then messages, up to a block with cache_control). Two calls differing only in the persona block: the second reports cache_read_input_tokens equal to the tool, both system blocks and the diff, through the call effect and through the gateway. A max_tokens reply reaches the compact-strict parser as the typed stop reason and the parser reports truncated. A call naming no plan resolves the compatibility row and posts the chat shape with a Bearer key. At the merge base, 18 of the 23 tests fail.
  • Response fixtures follow the documented /v1/messages response shape; they are not recordings from a live key, because the lane holds no key.
  • Updated the provider catalogue test file (two anthropic rows) and the gateway tests.
  • Local checks: ruff format and check clean, node metadata dependency check passes, runtime sweep import check clean, pre-push strict mypy passed.

Lab: host h201, lane hr-native-messages-cache-0410, command onex-lab-run --via bus --host h201 --kind test -- uv run pytest tests/test_omn20910_native_messages_protocol.py tests/unit/nodes/node_claude_review_gateway_effect tests/test_omn17353_provider_catalogue.py tests/test_omn17372_byok_routing_overlay_bridge.py tests/test_byok_gemini_glm_plans.py tests/test_omn19205_c29_byok_throttle_re_resolve.py tests/test_omn20157_ac5_byok_gemini_receipt_names_model.py tests/test_golden_chain_node_delegate_skill_orchestrator.py tests/ci at sha 7332678, exit 0, 2003 passed and 2 skipped; tests/ci/test_ruleset_readback_omn20070.py deselected because it reads GitHub directly and a lane's raw GitHub reads are refused. No live Anthropic call was made.

Not in this change

  • Sticky key per review run, admission before each call, declared topics for the gateway, model allowed list, and the prompt builder that emits the boundary marker.

Closes OMN-20910
Ticket: OMN-20910

jonahgabriel and others added 3 commits October 19, 2026 04:11
…ng for the review gateway

- node_llm_delegation_call_effect gains inference_protocol on its request (openai_chat default, anthropic_messages new) plus typed system_blocks and messages (text blocks, optional 5m or 1h cache TTL), tool_schema, tool_choice and provider_headers.
- Setting blocks refuses prompt and system_prompt; more than four cache breakpoints, a credential in provider_headers, response_format on the native protocol and native fields on the chat protocol are each refused.
- The result gains a typed stop_reason (EnumAnthropicStopReason), cache_creation_input_tokens (with 5m and 1h parts) and cache_read_input_tokens; the finished observation carries stop_reason and the cache token counts.
- finish_reason is mapped from stop_reason; max_tokens maps to length.
- The native call posts the key in x-api-key with an anthropic-version header, returns the forced tool's input as content, and bills cache writes at 1.25x (5m) and 2x (1h) and cache reads at 0.1x of the input price.
- byok_provider_backends.v1.yaml gains a second anthropic row (plan api_native, backend_id byok-anthropic-native, endpoint https://api.anthropic.com/v1/messages); the existing api row becomes default_plan true.
- node_claude_review_gateway_effect resolves the api_native plan, splits the system message into cached blocks and the user message into a diff block (5m cache) and a persona block (not cached) on a boundary marker.
- It turns a json_schema response_format into a forced tool, answers finish_reason length on a max_tokens stop, and returns cache usage in the reply's usage; a review with no user message answers 400.
- Tests: tests/test_omn20910_native_messages_protocol.py (23 tests) and the updated gateway and catalogue tests.
- Not in this change: sticky key per review run, admission before each call, declared topics, model allowed list.
- merge origin/dev (903e33f) into omnimarket#4209 branch omn-20910-native-messages-cache (head 7332678)
- conflicts in five files: contracts/OMN-20910.yaml, src/omnimarket/events/llm_delegation_call.py, the delegation call effect handler, its observation model, and the Claude review gateway handler
- resolve each conflict by keeping both sides: dev's review-call cost events (review provenance on the request, per-attempt observation fields) and this branch's native Anthropic Messages protocol (inference_protocol, system and message blocks, stop_reason, cache token counts)
- contract keeps dev's AC7, AC10, AC11, AC12, AC13 and this branch's AC30, AC31, AC32 with their dod_evidence entries
- native reply path also fills cache_read_tokens, cache_write_5m_tokens and cache_write_1h_tokens from the reply's usage, so a native review call's cost record carries its cache usage like the OpenAI-compatible path
- native call keeps skipping the served-model probe; dev's served-model refresh applies to the other calls
- tests/test_omn20910_review_gateway_provenance.py reads the user text from the request's message blocks instead of prompt, since the gateway sends the native protocol
- tests/test_omn20910_native_messages_protocol.py gains assertions that the finished observation's cache_read_tokens and cache write figures match the native cache counts
@github-actions

github-actions Bot commented Oct 19, 2026 •

Copy link
Copy Markdown

⚠️ Architectural Review — WARNINGS

Errors: 0
Warnings: 1

What this checks

Rule Description
ARCH-TOPIC-001 No hardcoded Kafka topic strings in handler code
ARCH-DI-001 No event_bus=None bypass
ARCH-DI-002 No direct Handler instantiation outside workflow_runner/adapters
ARCH-DI-003 No reinvented DI containers
ARCH-TOPIC-002 contract.yaml topics follow `onex.{cmd

ARCH-DI-002: Direct Handler instantiation (warning)\n\n```\nsrc/omnimarket/cli/cli_secret.py:447: return HandlerSecretStore()

src/omnimarket/cli/cli_secret.py:1116: result = HandlerLocalSecretStore().handle(
src/omnimarket/cli/cli_secret.py:1176: result = HandlerLocalSecretStore().handle(
src/omnimarket/cli/cli_secret.py:1206: result = HandlerCapturedSecretStore(onex_home=Path.home() / ".onex").handle(
src/omnimarket/cli/cli_secret.py:1244: result = HandlerCapturedSecretResolve(store=reader).handle(request)
src/omnimarket/cli/cli_secret.py:1286: written = HandlerLocalSecretStore().handle(
src/omnimarket/cli/cli_secret.py:1300: return HandlerModelSetup(write_model_list=_write_model_list)
src/omnimarket/cli/cli_secret.py:1769: handler = HandlerClaudeReviewGateway(
src/omnimarket/cli/cli_secret.py:1774: call=HandlerLlmDelegationCall(),\n```\n\nHandler classes should be wired only in workflow_runner.py or adapter modules. Direct instantiation elsewhere bypasses the contract system.\n\n

Static architectural lint — no model inference (OMN-14176).

@jonahgabriel

Copy link
Copy Markdown
Contributor Author

The branch was brought current with dev by merge commit c3964d5 (parents 7332678 and dev 903e33f); the PR body's lab line names the earlier head 7332678, and the line below is for the new head. Five files had conflicts with dev's OMN-20910 review cost events, judge flag, replay set and batch nodes; each was resolved by keeping both sides. The native Messages reply now also fills cache_read_tokens, cache_write_5m_tokens and cache_write_1h_tokens, so a native review call's cost record carries its cache usage, and tests/test_omn20910_review_gateway_provenance.py reads the user text from the message blocks instead of prompt.

Lab: host=h201 lane=fill-pf-drive-omnimarket-4209-20261019T0938Z head=c3964d58094a99488d9e6bf1cae9bc54275dc55e work_unit=lw-31923eca582b4994 command=uv run pytest tests/test_omn20910_native_messages_protocol.py tests/unit/nodes/node_claude_review_gateway_effect tests/test_omn17353_provider_catalogue.py tests/test_omn17372_byok_routing_overlay_bridge.py tests/test_byok_gemini_glm_plans.py tests/test_omn19205_c29_byok_throttle_re_resolve.py tests/test_omn20157_ac5_byok_gemini_receipt_names_model.py tests/test_golden_chain_node_delegate_skill_orchestrator.py tests/test_omn20910_review_call_cost_events.py tests/test_omn20910_review_gateway_provenance.py tests/unit/delegation tests/unit/nodes/node_llm_delegation_call_effect tests/unit/nodes/node_review_replay_set_effect tests/unit/nodes/node_claude_review_batch_effect tests/ci --deselect tests/ci/test_ruleset_readback_omn20070.py -q observed: exit 0, 6091 passed, 5 skipped, 6 deselected. No live Anthropic call was made.

@github-actions

github-actions Bot commented Oct 19, 2026 •

Copy link
Copy Markdown

✅ Hostile Reviewer — PASSED

Blocking findings (critical/error): 0
Total findings: 94
Models succeeded: correctness,sonnet


Gate semantics

Verdict Meaning Blocks merge?
passed >=2 models succeeded, no critical/error findings No
blocked CRITICAL or ERROR findings found Yes
degraded Fewer than 2 models succeeded (infra unavailable/timeout) Yes (OMN-15110)

Powered by omniintelligence.review_pairing.cli_review — node-based adversarial review via HandlerLlmCliSubprocess (OMN-8524)

@property
def tokens_out(self) -> int: ...
@property
def stop_reason(self) -> EnumAnthropicStopReason | None: ...
@property
def stop_reason(self) -> EnumAnthropicStopReason | None: ...
@property
def cache_creation_input_tokens(self) -> int: ...
@property
def cache_creation_input_tokens(self) -> int: ...
@property
def cache_read_input_tokens(self) -> int: ...
@jonahgabriel
jonahgabriel enabled auto-merge (squash) October 19, 2026 10:24
jonahgabriel and others added 2 commits October 19, 2026 06:41
Merge current dev c2eb05b into the existing native Messages branch,
retaining both sides of the OMN-20910 evidence contract conflict. This also
includes the landed dev-tag release staleness fix.

Declare the native protocol row explicit-only for plan detection. Both
credential intake paths keep model discovery and key refusal on the default
Anthropic plan; native selection stays available explicitly. Default and
prohibited detection-only rows cannot opt out of detection.

Validation: 704 passed, 1 skipped; registration regressions 54 passed with
skips forbidden; strict mypy passed for 5910 source files; ruff check/format,
node metadata dependency scope, and evidence binding checks passed.

Closes OMN-17427
@jonahgabriel
jonahgabriel merged commit 27c957b into dev Oct 19, 2026
152 checks passed
@jonahgabriel
jonahgabriel deleted the omn-20910-native-messages-cache branch October 19, 2026 13:55
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.

2 participants