Repository navigation
v0.2.0
Five more decision-model providers behind the same Client: OpenAI's Decisions API (gpt-6-luna), Cloudflare Workers AI (Clef and Clef-flash), Perplexity (pplx-decider), Databricks ai_decide, and any server that speaks Typesafe's System One API.
gem "ruby_decision_model", "~> 0.2"Tested live on 2026-10-07
- OpenAI, Perplexity, and Cloudflare (Clef and Clef-flash) on their own APIs, with text and with images.
- Jev, Luna, Clef, Clef-flash, and pplx-decider through OpenRouter.
- Databricks and System One servers have unit test coverage only.
Changes
- Added providers
:openai(OpenAI Decisions API,gpt-6-luna),
:cloudflare(Clef and Clef-flash on Workers AI),:perplexity
(pplx-decider-v1.1-27bandpplx-decider-v1-27b),:databricks
(ai_decideover REST), and:system_one(any server speaking
/v1/systemone: Ollama, the autojev server, strands-decider, and others). - The OpenAI provider translates both ways between System One and OpenAI's
wire format. Questions, answers, andAnswers::*types are unchanged for
callers. A noul with true/false criteria goes out as a boolean choice and
comes back as a noul. Client#asktakesimages:, an array of base64 data URLs. Each provider
places them where its API expects. Providers that don't read images raise
RequestErrorbefore sending. AddedRubyDecisionModel::Images.data_url
and.from_file.- Refused questions (OpenAI) raise
MissingAnswersas before, with the new
#refusedlisting the ids the provider declined. - Noul
probabilitiesare always{"true" => p, "false" => 1 - p}. Jev,
Clef, pplx-decider, and most System One servers send only the probability,
so the client fills in the split. A noul with junkprobabilitiesnow gets
the split instead of{}, and a noul outside 0..1 raises
InvalidResponse. - State or questions that cannot be encoded as JSON (invalid UTF-8,
NaN,
nesting deeper than 100 levels) raiseRequestError, with the original
error on#cause. RUBY_DECISION_MODEL_PROVIDERnames a provider from the environment ahead
of key detection (any case;-reads as_), and
Client.new(api_key: ...)sends that key to the named provider rather
than OpenRouter.SYSTEM_ONE_BASE_URLjoins detection after
TYPESAFE_API_KEYandOPENROUTER_API_KEY. Keys likeOPENAI_API_KEYdo
not select a provider by themselves.- New aliases. On OpenRouter:
lunaandgpt-6-lunaresolve to
openai/gpt-6-luna-decisions,clefandclef-flashto the
cloudflare/slugs, andpplx-deciderto
perplexity/pplx-decider-v1-27b. Native providers accept the OpenRouter
slugs for their own models. Typesafe accepts~typesafe/jev-latest. - Each provider names its own request id header:
x-request-idon OpenAI
and Perplexity,cf-rayon Cloudflare, andx-typesafe-request-id
everywhere else, as in 0.1.0. timeout:now defaults tonil, meaning the provider's read timeout: 5
seconds for Typesafe, OpenRouter, and Cloudflare, 30 for OpenAI,
Perplexity, Databricks, and System One servers, with a 5 second open
timeout. Passing a number sets both, as before. The write timeout, which
bounds image uploads, follows the read timeout, and a write timeout is
retried and raised asTimeoutErrorlike the other two. In 0.1.0 an
explicittimeout: nilmeant no limit at all; it now means these
defaults. AddedClient#open_timeout.ApiErrormessages end with the vendor's reason when the error body has
one, for exampleapi error (status 400): Invalid model 'x'.The client
reads OpenAI, Perplexity, and OpenRoutererror.message, Cloudflare
errors[].message, FastAPIdetail[].msg(Typesafe), and Databricks
message. The API key is masked if a vendor echoes it, and the text is
capped at 500 characters.#bodyis unchanged.- API keys are stripped before they go into the
Authorizationheader, so
a key read from a file with its trailing newline works. - Added
rake smoke, an opt-in live check against every provider whose keys
are set. CI never runs it. - Provider hooks for authors:
normalize_response,supports_images?,
request_id_header,requires_api_key?,default_timeout,
error_message,validate!,configured?, andurl(model). Base sends
Authorizationonly when a key is present. Providers written against
0.1.0 keep working: aurloverride without the model argument is still
called without it.