Repository navigation
feat(playground): add a decision model mode to the playground - #16876
Merged
RogerHYang merged 42 commits intoOct 19, 2026
Merged
RogerHYang merged 42 commits into
RogerHYang merged 42 commits into
Conversation
cephalization
force-pushed
the
tonypowell/phx-1374-decision-playground
branch
3 times, most recently
from
October 9, 2026 01:21
b83ab70 to
016f1bf
Compare
cephalization
force-pushed
the
tonypowell/phx-1373-typesafe-decision-provider
branch
2 times, most recently
from
October 9, 2026 14:43
7a59aef to
50bcaaf
Compare
cephalization
force-pushed
the
tonypowell/phx-1374-decision-playground
branch
6 times, most recently
from
October 15, 2026 17:20
455b93d to
b975d38
Compare
Introduce decision editing and execution in the playground. Fold the immediately following tab-sizing fix into the model-menu change it completes.
Decision instances now share one request (state plus typed questions) and differ only by model, so comparing instances means asking the same questions over the same evidence. Model name and endpoint move into the model parameters popover, matching chat providers. - Typed question editor: option/description rows for Choice, ordered levels for Score, true/false descriptions for Noul. Field-level errors in each field's error slot; Run is enabled only when the request is valid. - State editor with a Text/JSON toggle and template variables; the Inputs panel and variable substitution work as they do for prompts. - Import accepts a TypeSafe System One or OpenAI Decisions body (format detected) and loads it into the editor. Export shows the exact body each instance sends, as sent or as a template, with a copy button. - Output is one comparison grid when every instance is a decision model: one row per question, one column per model, probability bars per option or level, confidence, usage, and trace link. Single-instance output shows the same distributions. - The request is stored once in canonical System One shape and converted per provider at the boundary, mirroring the server's conversion. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…the design system Addresses an independent design review of the decision playground. - Request and each question are Cards, matching the chat playground's tool cards; remove/add controls use Trash and quiet buttons consistently. - Type select shows plain text in its trigger; type descriptions move to the field description slot. Switching type seeds valid criteria so the editor never opens on an error. - Section-level errors use Alert; helper text stacks under labels. - Probability bars use ProgressBar with gray fill for non-chosen rows and the primary fill for the answer; no duplicated headline for choices. - Comparison grid is a real table built on the shared table styles, with a fixed question column, skeleton loading cells, the shared repetition selector, footer cells that let RunMetadataFooter own its padding, and a collapsed "How to read this" disclosure. - Dialogs use DialogFooter and default-size buttons; export uses a banner warning and an EmptyState; monospace terms use Text fontFamily="mono". - Base URL helper text lives in the field's description slot. - Compare mode wraps decision headers instead of forcing horizontal scroll. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Apply the shared template layout and model-row spacing as one visual change. Consolidate the small spacing follow-up instead of preserving a separate correction commit.
Decision instances no longer share one request. Each owns its state and questions like a prompt, so instances can be compared on different requests and mixed with chat instances in the same playground. The request editor renders in the instance column with Import and Export where the prompt menu sits, and output is one card per instance; the comparison grid is gone with the shared request. Every instance header gains a duplicate button that compares against a copy of that instance; the page's Compare button still copies the first. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…asets The decision output is now an Output card holding an answers card, like the chat output's AI message, with a Pretty/Raw toggle in its header in place of the raw-response disclosure that shifted the layout and left a floating rule. Raw shows the pretty-printed response body and the header copy button copies it. With a dataset loaded the model menu still shows the Decision tab, but disabled, with a tooltip saying why. The dataset picker was already disabled once a decision instance exists. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Display decision usage and cost through the persisted span attributes and keep the dataset-run guard explicit. Consolidate the initial cost path, its immediate correction, and the explanatory note so the history presents the final tracing contract.
The Playground button in the span slideover is enabled for DECISION spans as well as LLM spans. The span-to-instance transform recognizes a decision span and rebuilds a decision instance from it: the span's input body is parsed with the import parser, so both System One and OpenAI Decisions shapes work, the provider and requested model come from the decision attributes, and the response becomes the instance's first output. Anything it cannot recover is reported in the parsing banner and the editor still opens on a usable request. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…conventions - Show chat providers by the server-reported model types instead of catalog membership, so a provisioned provider with no built-in model list (Fireworks) stays in the LLM picker and decision-only providers stay out of it - Strip modelType from saved model defaults and ignore it when a saved default is applied, so saving from a decision instance cannot turn chat instances into decision instances - Stay in LLM mode when the URL asks for decisions but the catalog has none, and write decision URL params only for the first instance, which is the one the URL recreates - Read credentials from the store when a run starts rather than subscribing, so a key edited mid-run never restarts the run - Fetch span attributes only for decision footers and the decision catalog only for menus that offer decisions - Parse the trailing GraphQL errors array in Relay network errors in the shared utility, so no caller needs to slice around request variables - Take the provider's decision wire format from the server for export instead of naming OpenAI on the client - Restore the chat base URL when a decision instance returns to chat without a remembered chat model - Conventions: drop useMemo under the React Compiler, object parameters for multi-argument utilities, no single-letter identifiers, verb-prefixed booleans, Partial records for sparse lookups, shared limit constants, one ambient ModelType, chat message tints and semantic tokens for the state card and bars, and template-level props off the shared instance props - Tests: hover the blocked Run button and assert the tooltip, assert the footer mounts for a failed call's span, cover the wire-format switch and the Relay error parser Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…locked state - Keep chat invocation-parameter parsing out of decision instances in updateModel, so the model name and base URL of a TypeSafe decision instance can be edited again - Keep template placeholders in Export > Template by not applying the template format there - Drop "save as default" from decision instances, whose model name and endpoint would send new chat instances to a decision endpoint - Enforce the 255-question cap on the client and name the first invalid instance and problem in a tooltip on the disabled Run button; explain the disabled dataset picker the same way - Sync the first instance's decision target to the URL from the page, so prompt-backed switches, deletions, and rejected URL params all leave a truthful URL - Replay a failed decision span as a failed run with its recorded error instead of an empty output; load span status for that - Explain a JSON state that stops parsing once variables are filled in, name the field in import schema errors, label the copy buttons, and keep focus after deleting a question, option, or level - Mark decision-only providers in the settings providers table - Tests: store update on a decision-only provider, question cap, JSON state error, import issue paths, failed-span replay, and the invalid-request tooltip Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Dogfooding asked for the import and export dialogs to go, and for the decision search params to go with them; playground state will be persisted holistically in follow-up work. The span replay parser stays, since it reads a recorded request back into a draft. "Compare" becomes "Add task", and both it and the per-task duplicate button use a copy-plus glyph so they no longer share the copy-to-clipboard icon. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
… clearing a dataset Dogfooding found the LLM / Decision tabs above the search field clunky, so the search now comes first and the tabs sit under it. The Decision tab is no longer disabled while a dataset is loaded; it shows a notice that choosing a decision model clears the dataset, and choosing one does so through the same path as the dataset picker's clear button. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…ves them A new question opened with its instructions already flagged, and every added option or level did the same. Required-field errors now wait until the field has been visited and left, while duplicate, count, and JSON errors still show at once; Run keeps refusing an invalid request and naming the problem. Score levels report an empty description per level rather than as one banner. The import-only `revision` counter goes with the import dialog, and the design skill records the rule. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…ields Option, level, and true/false descriptions are substituted before the request is sent, but were plain inputs, so a variable typed there had no highlight while the same variable in the instructions did. They are now one-line CodeMirror template inputs: the editor gains a singleLine mode that drops any edit adding a second line, and TemplateInput dresses it in the design system's field chrome with an error slot. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Provide duplication into an LLM prompt while preserving the original decision task. Consolidate the superseded in-place conversion and per-question copy menu so reviewers see the final interaction.
The Compare menu replaced the Add task button, so its Button and Icon imports are unused; canRun already folds in hasInvalidDecision, so it is no longer a toggleRunning dependency.
- Build runDecision's inputs, state, and inputValues from the draft: a text state with template variables is sent as a template the server renders, a JSON object as an object state, and anything else whole through one input. - Send questions as @OneOf predicate, choice, and scale inputs. - Select the typed result and store it in the answer shape the result view already reads, so live runs and replayed spans display the same way.
Shared pieces for creating and editing decision evaluators: a scoring-policy editor that also edits a dataset binding's per-question override, a saved decision and version picker, and a read-only judge summary, with policy helpers mirroring the GraphQL input shape.
A decision binding's details page shows its decision version, model, inputs, questions with effective scoring (overrides marked), input mapping, and annotation names. Evaluator tables show the decision and version where LLM rows show the prompt. Editing a decision binding comes next.
Parse an annotation's provisional metadata.decision in one place. A refused question reads as Refused rather than n/a, and annotation details show the answer's probability, expected level, confidence, per-option probabilities, and model.
A decision judge's output configs are named after its questions, but its annotations are named after the binding (one question) or binding.question (several), so result columns now follow that rule. Opening a decision judge as an evaluator task stays unavailable until calibration lands.
Name decision binding annotations in one helper next to the other kinds' annotation configs, merge binding overrides with the policy helpers, read decision names through DecisionVersion.decision instead of a second query, and give the DECISION kind its own token color.
Add Evaluator offers a decision evaluator: reuse a saved judge or create one from a decision version, then map its inputs and optionally override its scoring for the dataset. Editing covers the binding and the judge (repin within the decision, scoring), warns that judge changes reach every dataset using it, and previews the saved judge. A failed binding after a created judge keeps that judge, so a retry doesn't duplicate it.
Label versions v{n} with their tags instead of an id fragment, keep the decision select full width, collapse the optional scoring sections behind a summary, lay choice scores out as a compact grid with narrow threshold and direction fields, and show a field's empty-name error only once the user has edited it or tried to save. The details page drops the judge's internal tag and the repeated direction, and decision rows no longer offer Open in playground, which decision judges can't run as yet.
The edit slideover's test now passes the binding, so the preview is named after it and scored with its override, as experiments run it.
…bject Saved decisions with an object state, such as the datagen accuracy judge, failed to load. The state editor gains an Inputs mode listing the input names; it runs, saves, and runs over datasets as an object state.
…ssages - Replaying an OpenAI decision span reads its answer list by name, not by array index. - A custom provider's span replays on the built-in provider of its SDK (decision.system), with a note naming the custom provider. - The save dialog takes default values, so picking a decision while typing no longer resets the description, tags, or name. - Pruning a binding's override also drops scores for removed choices. - Decision page actions show the server's message, not Relay's envelope.
…, object state query, and save dialog
RogerHYang
force-pushed
the
tonypowell/phx-1373-typesafe-decision-provider
branch
from
October 19, 2026 21:49
5848e9b to
8541808
Compare
RogerHYang
force-pushed
the
tonypowell/phx-1374-decision-playground
branch
from
October 19, 2026 22:02
6d023e9 to
640b1ad
Compare
RogerHYang
force-pushed
the
tonypowell/phx-1373-typesafe-decision-provider
branch
from
October 19, 2026 22:02
8541808 to
2157113
Compare
Contributor
Card links checkNo broken Card links found. Checked external links in 5.4s |
@arizeai/phoenix-cli
@arizeai/phoenix-client
@arizeai/phoenix-config
@arizeai/phoenix-evals
@arizeai/phoenix-mcp
@arizeai/phoenix-otel
commit: |
RogerHYang
marked this pull request as ready for review
October 19, 2026 22:10
RogerHYang
merged commit Oct 19, 2026
33eeb0e
into
tonypowell/phx-1373-typesafe-decision-provider
72 of 110 checks passed
7 tasks
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.
resolves #16711
What
Adds a Decision tab to the playground model picker; choosing a decision model swaps that instance's chat template for a decision request, laid out like a chat template, and runs it through
createDecision. Stacked on #16875.A TypeSafe
jev-latestrun: the request as State and question cards above, and the Output card below with a probability bar per option, confidence, a Pretty/Raw toggle, and token counts in the footer.decision.token_count.*attributes; decision usage is not folded into LLM token or cost rollups. Nothing about decision mode is encoded in the URL; persisting playground state is follow-up work.Try it
departmentand View Trace opening aDECISIONspan.Decisionspan, press Playground; observe the same state and questions loaded, then press Run again.How this PR was made
Starting point: review and test the implemented decision playground, with the meeting transcript as spec; then a Notion page of dogfooding notes and meeting decisions
Steering: approach (capability guards, catalog defaults, per-instance requests), scope (drop import/export and URL encoding, keep Compare as Add task, duplicate as an LLM prompt rather than convert in place, skip label renames), style (design review, chat-like layout, CodeMirror inputs for substituted fields), correctness (tab padding)
Abandoned: JSON criteria textareas; a nested request card; one shared request; folding decision tokens into LLM cost rollups; import/export dialogs; decision mode in the URL; a per-question copy-as menu, superseded by duplicate as LLM
Verified: tsc, oxlint, oxfmt, relay, vitest, 8-domain scorecard and Playwright checks on an isolated instance, dev-server checks of the picker, template inputs, validation timing, and duplicate as LLM. Not verified: OpenAI Decisions live, PostgreSQL, screenshot predates the fix passes
Look hardest at:
useModelMenuDataprovider visibility,PlaygroundModelMenudataset clearing,useDecisionRunner.tsrun lifecycle,maskUntouchedErrors,buildChatFromDecisionDraft,duplicateInstancereturning the copy's id,TemplateEditorsingleLine modeStats as of 6d023e9, counted from the Claude Code transcripts; the opencode session is recall.
🤖 Generated with Claude Code