Skip to content

Send jev decisions in the Decisions API's documented shape - #33

Merged
Deep-Sixed merged 1 commit into
mainfrom
claude/keepassxc-openrouter-setup-upjxix
Oct 6, 2026
Merged

Deep-Sixed merged 1 commit into
mainfrom
claude/keepassxc-openrouter-setup-upjxix

Conversation

@Deep-Sixed

Copy link
Copy Markdown
Owner

What the live probe found

Running scripts/probe-jev-decisions.py against OpenRouter got this:

response: HTTP 400
"questions": Invalid input: expected record, received array

The jev adapter sent questions as a list. The Decisions API expects a record keyed by question id. OpenRouter's published reference gives the rest of the contract:

  • Choice question: {type: "choice", instructions, criteria}. criteria maps every offered option to its guidance, or to null.
  • Choice answer: {type, choice, confidence?, probabilities?}, keyed by question id.
  • Usage: {input_tokens, output_tokens, cost}.

Changes

  • build_body sends questions: {route: {type, instructions, criteria: {m1: null, …}}, effort: {…}}. The options carry no guidance of their own because state already describes each model. Nothing new leaves Cerberus.
  • Usage keys are now input_tokens/output_tokens/cost. The old chat-completions names never matched, so only cost was ever recorded.
  • Probe verdict: a 401/402/403 now reads KEY REFUSED instead of REQUEST REJECTED, which wrongly pointed at the request shape.
  • Probe recording transport: it re-served the decoded body under the original content-encoding header, so httpx decoded it twice and any compressed response showed up as unreachable. Reproduced offline with a gzip 403, then fixed and covered by a test.
  • Updated the docs (architecture.md, README) and the tests to the documented shape.

Verification

  • scripts/ci.sh: ruff clean, 443 tests passed.
  • Live probe after the fix: the same key that got the 400 now gets HTTP 403 Key limit exceeded (total limit). OpenRouter validated the old body and rejected it before reaching the budget check, so the new shape passes validation. The key has a $0 limit, so no decision was billed and the answer shape isn't confirmed live yet. It's still read tolerantly, as before.
  • To finish the confirmation, run the probe once with a key whose credit limit is above zero; one decision costs about $0.00002.

🤖 Generated with Claude Code

https://claude.ai/code/session_01NB8ieibwKFDVRzG8jXS6F6


Generated by Claude Code

A live probe against OpenRouter rejected the adapter's request with HTTP 400:
`questions` must be a record keyed by question id, not a list. OpenRouter's
published reference gives the rest of the contract: each choice question is
{type, instructions, criteria}, with criteria mapping every offered option to
its guidance or null, and usage reports input_tokens, output_tokens and cost.

- build_body sends questions as {id: {type, instructions, criteria}}; the
  options carry no guidance of their own since the state describes each one.
  The live validator accepts this form (the next refusal was the key's budget).
- Usage is read from input_tokens/output_tokens/cost; the old chat-completions
  names never matched and only cost survived.
- The probe reports a 401/402/403 as a refused key or credit, not a rejected
  request shape.
- The probe's recording transport re-served a decoded body under its original
  content-encoding, so any compressed response surfaced as "unreachable".

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NB8ieibwKFDVRzG8jXS6F6
@Deep-Sixed
Deep-Sixed marked this pull request as ready for review October 6, 2026 06:42
@Deep-Sixed
Deep-Sixed merged commit ef6429b into main Oct 6, 2026
2 checks passed
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