Repository navigation
feat: add OpenAI's Decisions API as a provider - #2
Merged
Merged
Conversation
provider: :openai posts to https://api.openai.com/v1/decisions (default model gpt-6-luna, OPENAI_API_KEY). The APIs ask the same three kinds of question with different wire formats, so SystemOneClient.Wire.OpenAIDecisions translates both ways and callers keep the same questions and answer structs: - noul <-> predicate (true/false criteria folded into the instructions), choice criteria <-> choices, score criteria <-> levels labelled "Level N" - state -> text input (non-string state, instructions and descriptions are JSON-encoded) - answers array keyed by name -> map keyed by id; probability arrays -> maps; score legend from OpenAI's labels - per-question refusals -> SystemOneClient.Answer.Refusal - choice/score without criteria rejected locally as :missing_criteria Bumps the version to 0.2.0 and documents the provider. Closes #1. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GCRzC1xV9gCW4bf7LzBGcB
OpenAI requires a label per level, so requests send "Level N". Decoding now restores the caller's original level text into the legend, matching what TypeSafe returns. Found by a live run against /v1/decisions. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GCRzC1xV9gCW4bf7LzBGcB
This was referenced Oct 11, 2026
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.
Closes #1.
Adds
provider: :openaifor OpenAI's Decisions API (POST /v1/decisions,gpt-6-luna). The semantics match System One but the wire format doesn't, so a newSystemOneClient.Wire.OpenAIDecisionsmodule translates requests and responses both ways. Callers keep writing the same questions and get the sameChoice/Noul/Scorestructs.Translation
stateinput(strings pass through, anything else JSON-encoded)%{id => question}[%{name: id, ...}]noul(+ optionaltrue/falsecriteria)predicate, criteria folded intoinstructionschoicecriteria: %{value => description}choices: [%{value, description}]scorecriteria: [description]levels: [%{label: "Level N", description}]name, probability arraysrefusal->%SystemOneClient.Answer.Refusal{}Also
SystemOneClient.Answer.Refusal: a refused question doesn't fail the request; the other answers are still returned. (jido_ai's SystemOne transformer treats it as a missing answer and fails open.)Providerresults now carrywire: :system_one | :openai_decisions. Existing providers are unchanged.Testing
Req.Test.mix format --check-formatted,compile --warnings-as-errors,mix docs(no warnings),mix hex.build.gpt-6-luna): the response's top-level keys areanswers,model,usageas decoded; choice, score and predicate (with criteria) all round-trip; a mixed three-question tool-routing request pickedgcdat 0.99 in ~157 ms; a bad model returns{:error, {:http, 404, _}}and a bad key{:error, {:http, 401, _}}."Level N"labels sent to OpenAI. They now carry the caller's level text, like TypeSafe.Out of scope, tracked separately: image inputs (#3) and
OpenAI-Organization/OpenAI-Projectheaders (#4).🤖 Generated with Claude Code
https://claude.ai/code/session_01GCRzC1xV9gCW4bf7LzBGcB