fix(studio): evaluate agents over chat completions - #1219
Open
marcusds wants to merge 1 commit into
Open
Conversation
Contributor
|
marcusds
force-pushed
the
astd-410-fabric-eval-endpoint/mschwab
branch
from
August 10, 2026 23:03
5ab5ab9 to
cc7d51a
Compare
15 tasks
marcusds
force-pushed
the
astd-410-fabric-eval-endpoint/mschwab
branch
from
August 11, 2026 19:50
cc7d51a to
5b8ad11
Compare
15 tasks
Every evaluation Studio submitted targeted the agent proxy's /generate, which
only NAT's FastAPI front end serves. A `nemo-agents-spec-v1` agent is served by
the Platform-owned Fabric server, which exposes /health, /v1/chat/completions
and DELETE /v1/sessions/{id} and nothing else, so the request 404s and the run
fails with nothing to show for it.
That is the format `nemo-build-agent` produces by default, and since #1223 it is
also what Studio's Create Example Agent produces — SAMPLE_AGENTS now holds a
single Fabric entry — so the documented way to build an agent yields one that
cannot be evaluated.
The target now posts OpenAI chat completions for every agent rather than
branching on config_format. Both formats serve that path: Fabric natively, and
NAT through its FastAPI front end, whose default workflow endpoint sets
openai_api_v1_path to /v1/chat/completions. No agent config in this repo
overrides general.front_end, and both launchers (`nat start fastapi` in the
subprocess and container backends) take those defaults. Studio's own chat
playground already relies on this, posting chat completions to every deployment
without regard to format.
Branching on the agent entity would have meant resolving it before submit, and
an unresolved or failed lookup would have to pick a wire format anyway —
defaulting to the one that 404s for the agents this fixes. One shape for both
formats removes the lookup, the fallback, and the race between them.
The generic agent target needed no evaluator or SDK change: it already takes a
URL, a Jinja body template and a JSONPath. render_template recurses through
dicts and lists, so `{{ instruction }}` substitutes inside the nested messages
entry, and _extract_jsonpath returns matches[0].value, so
$.choices[0].message.content resolves to the text.
DatasetEvalRowResultsPanel read the rendered prompt out of the request body at
`input_message`, a key the new body does not have; it would have silently fallen
back to dumping the raw dataset row. It now reads the last chat message and
keeps `input_message` as a fallback so jobs submitted before this still render.
Also drops AgentEvaluationsRoute/components/submitEvaluationSpec.ts, an
unreferenced second copy of the submission logic still building the /generate
target, and updates the route's AGENTS.md, which prescribed /generate as the
eval target.
ASTD-410
Signed-off-by: mschwab <mschwab@nvidia.com>
marcusds
force-pushed
the
astd-410-fabric-eval-endpoint/mschwab
branch
from
August 11, 2026 20:16
5b8ad11 to
cfdd326
Compare
marcusds
marked this pull request as ready for review
August 11, 2026 20:20
Contributor
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Enterprise Run ID: 📒 Files selected for processing (6)
💤 Files with no reviewable changes (1)
📝 WalkthroughWalkthroughEvaluation targets now use non-streaming chat completions for task and dataset submissions. Dataset result panels support chat-completion messages and legacy inputs. Agent evaluation guidance and related specification code were updated. ChangesEvaluation chat-completions migration
Sequence Diagram(s)sequenceDiagram
participant EvaluationBuilder
participant EvaluationRunner
participant AgentChatCompletions
EvaluationBuilder->>EvaluationRunner: render instruction or prompt as a user message
EvaluationRunner->>AgentChatCompletions: send non-streaming chat-completions request
AgentChatCompletions-->>EvaluationRunner: return choices[0].message.content
Suggested reviewers: 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches 💡 1🛠️ Fix failing CI checks 💡
📝 Generate docstrings
🧪 Generate unit tests (beta)
Comment |
steramae-nvidia
approved these changes
Aug 11, 2026
htolentino-nvidia
approved these changes
Aug 12, 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.
Summary
Every evaluation Studio submitted targeted the agent proxy's
/generate, which only NAT's FastAPI front end serves. Anemo-agents-spec-v1agent is served by the Platform-owned Fabric server, which exposes/health,/v1/chat/completionsandDELETE /v1/sessions/{id}and nothing else, so the request 404s and the run fails with nothing to show for it. Nothing in the submission path branched onconfig_format, so this was true for every Fabric agent.This matters because
nemo-agents-spec-v1is the formatnemo-build-agentproduces by default, and since #1223 it is also what Studio's Create Example Agent produces —SAMPLE_AGENTSnow holds a single Fabric entry. The documented way to build an agent, and Studio's own one-click path, both yield an agent that cannot be evaluated. This is broken onmaintoday, not pending some future change.After this change the target posts OpenAI chat completions for every agent, with no branch on
config_format.Related Issue
ASTD-410 (Linear).
Why one shape rather than a per-format branch
Both agent config formats already serve
/v1/chat/completions:fabric/server.py).workflowendpoint setsopenai_api_v1_path="/v1/chat/completions"alongside the legacylegacy_path="/generate". No agent config in this repo overridesgeneral.front_end, and both launchers —nat start fastapiin the subprocess backend and the container backend — take those defaults.Studio already depends on this: the chat playground posts chat completions to every deployment via
/-/v1, without regard to format, and the gateway carries anunknown-modelpatch written specifically for NAT'sChatResponse.Only chat completions is common to both formats, so branching buys nothing and costs a lookup. Resolving
config_formatwould mean fetching the agent entity before submit, gating submission on that fetch, and still choosing a wire format when the lookup is in flight or has failed — and the only safe-looking default,nat, is exactly the one that 404s for the agents this fixes. One shape removes the lookup, the fallback, and the race between them.Changes
agentEndpointbuilds one target for both formats: POST{ model, messages: [{ role: 'user', content: '{{ instruction }}' }], stream: false }to/-/v1/chat/completions, reading$.choices[0].message.content.buildAgentTargetandbuildDatasetAgentTargetshare it; the latter renders{{ prompt }}.DatasetEvalRowResultsPanelread the rendered prompt out of the logged request body atinput_message, a key the new body does not have — it would have silently fallen back to dumping the raw dataset row. It now reads the last chat message, keepinginput_messageas a fallback so jobs submitted before this still render.AgentEvaluationsRoute/components/submitEvaluationSpec.ts, an unreferenced second copy of the submission logic still building the/generatetarget.AgentEvaluationsRoute/AGENTS.md, which prescribed/generateas the eval target and told contributors to keep using it.DatasetEvalRowResultsPaneltest covering the chat body, a multi-message transcript, theinput_messagefallback, and the raw-row fallback.No evaluator or SDK change was required: the generic agent target already accepts a URL, a Jinja
bodytemplate, and aresponse_pathJSONPath.Type of Change
Quality Gates
AgentEvaluationsRoute/AGENTS.mdis contributor documentation and is updated here.Verification
Signed-off-by:traileruv run pre-commit run -apasses, or any blocked checks are identified belowTargeted validation, all at the current head after rebasing onto
main:pnpm --filter nemo-studio-ui test— passed, 310 files / 2840 tests.pnpm --filter nemo-studio-ui typecheck— passed.pnpm --filter nemo-studio-ui lint— passed (--max-warnings 0).uv run pre-commit run -a— every hook passed exceptuv-lock, which failed on a local toolchain mismatch (uv.lock must be checked or updated with uv 0.9.14; this machine has 0.9.30). This PR changes no Python and no dependency metadata, and the separateCheck for uv.lock drifthook passed. Not marked as passing above.nemo_evaluator_sdk:render_templaterecurses through dicts and lists, so{{ instruction }}substitutes inside the nestedmessagesentry rather than being sent literally.jsonpath_ng.parse— the exact parseragent_inference.pyimports, not the.extvariant._extract_jsonpathreturnsmatches[0].value, and$.choices[0].message.contentreturns the content.Known limitation
A full evaluation job has not been run end to end. The transport, the body templating and the response extraction are each verified; that a job completes and scores is not.
Separately, every eval request opens a new Fabric session. Fabric starts a fresh runtime for each chat-completions call that carries no
X-Nemo-Session-Id, the evaluator sends none, and sessions are reclaimed only by the 30-minute idle sweep — so a long task list leaves that many runtimes alive and pays a cold start per task. That is a platform-side concern rather than a Studio one; it is recorded in the route'sAGENTS.mdand addressed separately.Summary by CodeRabbit
New Features
Bug Fixes
input_messageremain supported.Documentation