Repository navigation
v0.2.0 feat: OpenAI, Cloudflare, Perplexity, Databricks and System One providers - #22
Merged
Merged
Conversation
…roviders The client keeps one shape for questions and answers while each provider speaks its vendor's wire format. - OpenAI Decisions (gpt-6-luna) translates both ways between System One and OpenAI's named-array format, including refusals and boolean choices for noul criteria. Ambiguous responses (an id answered twice, duplicate probability values) raise InvalidResponse. - Cloudflare Clef and Clef-flash on Workers AI, Perplexity pplx-decider, Databricks ai_decide, and any /v1/systemone server (Ollama, autojev, strands-decider). - Client#ask takes images: as data URLs; Images builds them. - Providers own the request id header, default timeout, vendor error text (key masked, control characters removed, capped), and response normalization. Noul answers always carry a true/false split. - RUBY_DECISION_MODEL_PROVIDER names a provider, including for api_key:. - Write timeouts retry as TimeoutError; unencodable state raises RequestError. 0.1.0 custom providers keep working. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Lz7fQbXMHCS4fYAm6p9n97
Sends one noul, noul-with-criteria, choice and score request to every provider whose keys are set (or the provider:model pairs in TARGETS) and prints the answers. Never part of the default task or CI. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Lz7fQbXMHCS4fYAm6p9n97
Covers each provider's endpoint, env vars and wire differences, the cross-provider alias table, image support, per-provider timeouts, RUBY_DECISION_MODEL_PROVIDER, the expanded error table, and rake smoke in the release steps. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Lz7fQbXMHCS4fYAm6p9n97
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Lz7fQbXMHCS4fYAm6p9n97
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.
Summary
0.2.0 adds the decision models that shipped after Jev, behind the same vendor-neutral
Client. Questions and answers keep one shape whichever provider serves them.:openai(OpenAI Decisions API,gpt-6-luna),:cloudflare(Clef and Clef-flash on Workers AI),:perplexity(pplx-decider-v1.1-27b),:databricks(ai_decideover REST), and:system_onefor any/v1/systemoneserver (Ollama, autojev, strands-decider).MissingAnswers#refused. Ambiguous responses raiseInvalidResponserather than guess.luna,clef,clef-flash,pplx-deciderresolve on OpenRouter and on each vendor's own API; OpenRouter slugs resolve on native providers.ask(images:)with data URLs; each provider places them where its API expects.Images.from_file/.data_url.TimeoutError, request id headers, vendor error text inApiErrormessages (key masked, control characters removed, capped), noul probability split filled in everywhere.RUBY_DECISION_MODEL_PROVIDERnames a provider (also forapi_key:alone);SYSTEM_ONE_BASE_URLjoins env detection; general-purpose keys likeOPENAI_API_KEYnever pick a provider by themselves.rake smoke, an opt-in live check per configured provider, now step 3 of the README release steps.url, three-keywordrequest_body, request id header). The companiondecidegem needs no changes.Wire formats come from vendor docs, SDK source, and verbatim examples, then live checks: Jev, Clef, Clef-flash, gpt-6-luna and pplx-decider answered through OpenRouter with this code, and Cloudflare's native Workers AI endpoint was called once to confirm its response envelope.
Test Coverage
Tests: 5 → 7 test files (+2 new); 102 → 199 tests, 613 assertions.
Coverage: 90% value-weighted at the first gate (93% after review fixes; gate PASS against an 80% target).
Test value: 5 tests written by the coverage audit, 0 rejected by the authoring gate, 0 existing tests extended, 0 paths weakly covered. Review fixes added a regression test for each fix.
Regression proof: base control unavailable (no package.json-based runner for this Ruby repo); each review regression was reproduced at HEAD before its fix where noted below.
Pre-Landing Review
Nine review rounds ran (six specialists plus Red Team each round), with Claude and Codex adversarial passes. The final round on the frozen tree was clean from every reviewer, and the Step 9 and Step 11 records bind to the same tree.
Fixed during review (each with a regression test):
api_key:alone ignoredRUBY_DECISION_MODEL_PROVIDERand sent the key to OpenRouter.urloverride, or one with keyword-only options, raisedArgumentError.Basehad also stopped reading the request id header.true/"true", overwrote each other.Encoding::CompatibilityErrorinstead ofApiError.TimeoutError.JSON::GeneratorErrorandJSON::NestingErrorescaped the gem's error classes.configure.CLOUDFLARE_API_TOKENhidCLOUDFLARE_AUTH_TOKEN.success:falsebody reported missing answers instead of the vendor error.RUBY_DECISION_MODEL_PROVIDERwas case-sensitive.Outside review: the Codex adversarial passes (gpt-6-astra) completed, and the last two were clean. The Codex structured review (
codex review --base main) ran once and came back unverified (its prose said "no actionable defects" but it lacked the required markers, and it only diffs tracked files, so it never saw the new provider files). That gate counts as missing coverage, not a pass.Decisions made without asking (
/goaldirective: ship without pausing)http://base URLs are allowed (local System One servers use them).MissingAnswerswith the other answers on#answers.timeout:for large inputs.Providers.env_var_forspecial-casing System One.without_provider_envtest helper.OpenAI#normalize_responseand the Cloudflare checks.Exploratory QA
Functional surface, report-only, 4 probes per round, rerun on every changed tree (6 runs, all pass). Final run on the shipped tree:
rake smoke TARGETS=open_router,open_router:clef,open_router:luna,open_router:pplx-deciderApiErrorapi error (status 400): Model nope/not-a-decision-model does not existUnauthorized, key not echoedunauthorized: User not found., key absentRequestError, 0 transport callsNot probed: native OpenAI, Perplexity and Databricks endpoints (no credentials here); System One servers (local Ollama is 0.18, System One needs 0.35+). Cloudflare's native endpoint was verified separately through the Cloudflare API.
Design Review
No frontend files changed. Design review skipped.
Eval Results
No prompt-related files changed. Evals skipped.
Scope Drift
Scope Check: CLEAN. Intent: add the newest decision-model vendors behind the vendor-neutral client, plus pre-release fixes (provider timeouts, vendor error text, live smoke task). Delivered: exactly that, plus review hardening.
Plan Completion
Plan completion audit: not run (no plan is bound to this branch and no docs/designs/ file matches). Fix: add "Plan: " to the PR body, or run /autoplan.
Verification Results
No plan-specific checks (no plan). Automatic QA smoke: 4 PASS, 0 FAIL, 0 BLOCKED, 0 NOT RUN on the shipped tree.
TODOS
No TODO items completed in this PR. The repo has no TODOS.md; follow-ups are listed here instead.
Follow-ups for 0.2.1
InvalidResponseunsanitized (pre-existing since 0.1.0).{"true", "false"}instead of raising.InvalidResponsehas no#refused.base_url:alone sends the env-selected provider's key to that host (pre-existing).decidegem: a refusal surfaces asAskFailed, and the answers that did arrive are dropped;decidecould pass#answersthrough. Its gemspec description still says "Typesafe Jev via OpenRouter".Documentation
Status: updated. One factual fix in README.md, no blockers.
Scope:
feat/new-decision-providersagainst basef79a890(0.1.0 to 0.2.0). Nothing is committed past the base, so the audit covered 17 unstaged and 8 untracked candidate files, all hash-verified. The repo's docs are README.md, CHANGELOG.md and LICENSE.txt. It has no CLAUDE.md, ARCHITECTURE.md, CONTRIBUTING.md, TODOS.md or nested doc roots.The README's provider sections, env-based provider selection, timeouts, images, errors, retries and
rake smokesteps were checked against the source and the release workflow. Apart from the alias table, they match.Documentation Debt
Critical gaps (no coverage):
error_messageand noresponseraisesInvalidResponse, andDATABRICKS_HOSTwithout a scheme getshttps://. Needs reference in the Databricks section.Images.data_urlandImages.from_fileraiseArgumentErrorfor a non-image type or an unknown file extension. Needs reference in the Images section.RequestErrorfor a hand-built question with no instructions. Needs a line in the Errors table.Client#open_timeoutis a new public reader that the README never names. Needs reference under Options.Common gaps:
configured?is listed as a provider hook in CHANGELOG but not in README's Writing a provider.Diagram drift: none. The docs contain no ASCII or Mermaid diagrams.
These are small enough to fill by hand or with
/document-generate.Test plan
bundle exec rake: 199 runs, 613 assertions, 0 failures (Ruby 3.3.4)ruby -won Ruby 3.2.3, 3.4.8 and 4.0.1: 199 runs, 0 failures, no warningsbundle exec rake release:build:gem build --strictproduces ruby_decision_model-0.2.0.gem with every lib filerake smokethrough OpenRouter: Jev, Clef, Clef-flash, gpt-6-luna and pplx-decider answered liveclef-flashconfirmed the{"result": ...}envelope the provider unwraps🤖 Generated with Claude Code
https://claude.ai/code/session_01Lz7fQbXMHCS4fYAm6p9n97