Skip to content

v0.2.0

Choose a tag to compare

@obie obie released this 07 Oct 17:38
· 2 commits to main since this release
5154d43

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-27b and pplx-decider-v1-27b), :databricks
    (ai_decide over 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, and Answers::* types are unchanged for
    callers. A noul with true/false criteria goes out as a boolean choice and
    comes back as a noul.
  • Client#ask takes images:, an array of base64 data URLs. Each provider
    places them where its API expects. Providers that don't read images raise
    RequestError before sending. Added RubyDecisionModel::Images.data_url
    and .from_file.
  • Refused questions (OpenAI) raise MissingAnswers as before, with the new
    #refused listing the ids the provider declined.
  • Noul probabilities are 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 junk probabilities now 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) raise RequestError, with the original
    error on #cause.
  • RUBY_DECISION_MODEL_PROVIDER names 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_URL joins detection after
    TYPESAFE_API_KEY and OPENROUTER_API_KEY. Keys like OPENAI_API_KEY do
    not select a provider by themselves.
  • New aliases. On OpenRouter: luna and gpt-6-luna resolve to
    openai/gpt-6-luna-decisions, clef and clef-flash to the
    cloudflare/ slugs, and pplx-decider to
    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-id on OpenAI
    and Perplexity, cf-ray on Cloudflare, and x-typesafe-request-id
    everywhere else, as in 0.1.0.
  • timeout: now defaults to nil, 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 as TimeoutError like the other two. In 0.1.0 an
    explicit timeout: nil meant no limit at all; it now means these
    defaults. Added Client#open_timeout.
  • ApiError messages end with the vendor's reason when the error body has
    one, for example api error (status 400): Invalid model 'x'. The client
    reads OpenAI, Perplexity, and OpenRouter error.message, Cloudflare
    errors[].message, FastAPI detail[].msg (Typesafe), and Databricks
    message. The API key is masked if a vendor echoes it, and the text is
    capped at 500 characters. #body is unchanged.
  • API keys are stripped before they go into the Authorization header, 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?, and url(model). Base sends
    Authorization only when a key is present. Providers written against
    0.1.0 keep working: a url override without the model argument is still
    called without it.