[STG-2515] feat(evals): vercel_ai_sdk bare-loop harness adapter#2338
Open
shrey150 wants to merge 4 commits into
Open
[STG-2515] feat(evals): vercel_ai_sdk bare-loop harness adapter#2338shrey150 wants to merge 4 commits into
shrey150 wants to merge 4 commits into
Conversation
|
225e69a to
1fabf2b
Compare
Contributor
There was a problem hiding this comment.
All reported issues were addressed across 5 files
Confidence score: 5/5
- Safe to merge after the addressed issues were fixed.
Architecture diagram
sequenceDiagram
participant CLI as eval CLI
participant Registry as Harness Registry
participant Runner as VercelAiSdkRunner
participant AISDK as AI SDK (ai pkg)
participant Stagehand as @stagehand/getAISDKLanguageModel
participant Adapter as ExternalHarnessToolAdapter
participant Browse as browse CLI (temp bin)
participant Recorder as BareLoopToolRecorder
participant Verifier as Verifier (gradeExternalTrajectory)
Note over CLI,Verifier: NEW: vercel_ai_sdk bare-loop harness flow
CLI->>CLI: Parse --harness vercel_ai_sdk
CLI->>Registry: getBenchHarness("vercel_ai_sdk")
Registry-->>CLI: vercelAiSdkHarness adapter
CLI->>Adapter: prepareExternalHarnessAdapter(plan, model)
Adapter->>Adapter: Build browse CLI path, env, system prompt
Adapter-->>CLI: PreparedExternalHarnessAdapter
CLI->>Runner: runVercelAiSdkAgent(input)
Note over Runner: Resolve model provider
Runner->>Runner: Check model includes "/"
alt Missing provider prefix
Runner->>Runner: Throw EvalsError("provider-prefixed model required")
end
Runner->>Stagehand: getAISDKLanguageModel(provider, modelName)
Stagehand-->>Runner: LanguageModel instance
Note over Runner: Read step cap config
Runner->>Runner: readBareLoopMaxSteps(MAX_STEPS_ENV)
Note right of Runner: Default 40, override with EVAL_VERCEL_AI_SDK_MAX_STEPS
Note over Runner: Build tool recorder
Runner->>Recorder: createBareLoopToolRecorder(adapter, logger)
Recorder-->>Runner: recorder instance
Note over Runner: Execute generateText loop
Runner->>AISDK: import { generateText, tool, stepCountIs }
Runner->>AISDK: generateText(options)
Note right of Runner: model, system prompt, user prompt, stopWhen, tools
AISDK->>AISDK: Run multi-step loop (stepCountIs(N) max)
loop Per step (up to N)
alt Model calls browse tool
AISDK->>Runner: Execute tools.browse.execute({ args })
Runner->>Recorder: recorder.execute(args)
Recorder->>Browse: browse <args>
Browse-->>Recorder: stdout result
Recorder->>Recorder: Record normalized tool call
Recorder-->>AISDK: tool output string
else Model produces final text
AISDK->>AISDK: finishReason = "stop"
end
end
Note over AISDK: stopWhen cuts loop mid-tool-call
AISDK-->>Runner: { text, steps, totalUsage, finishReason }
Note over Runner: Detect step cap truncation
alt stepsUsed >= maxSteps AND finishReason === "tool-calls"
Runner->>Runner: cappedOut = true (step cap reached)
else
Runner->>Runner: Normal completion or clean stop
end
alt Loop threw error
Runner->>Runner: capture error, log warn
Runner->>Runner: status = "error", stopReason = error string
end
Note over Runner: Finalize result
Runner->>Runner: finalizeBareLoopResult(...)
Runner->>Recorder: recorder.calls (normalized tool calls)
Runner->>Verifier: gradeExternalTrajectory(toolCalls)
Verifier-->>Runner: verifier scores
Runner-->>CLI: TaskResult with metrics
Note right of Runner: vercelAiSdkStatus, vercelAiSdkStopReason, usage tokens
CLI->>CLI: Write trajectory + scores
Reply with feedback, questions, or to request a fix.
Re-trigger cubic
c02cd46 to
baf8b91
Compare
Registers --harness vercel_ai_sdk: a bare loop via the ai package (generateText + stopWhen: stepCountIs(N)) with a single gated browse tool. The AI SDK's own multi-step loop IS the harness — zero additional scaffolding, mirroring the JS-default bare loop found in the wild (and the Modal sandbox template used in the 2026-07-09 smoke). Model resolution goes through stagehand's getAISDKLanguageModel provider map, so -m provider/model means the same thing as on the stagehand harness. Step cap defaults to the shared 40 (EVAL_VERCEL_AI_SDK_MAX_STEPS override). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…runner generateText returns normally whether stopWhen: stepCountIs(maxSteps) cut the loop off or the model actually finished -- finishReason was declared but never read, so a cap-truncated run and a clean stop were indistinguishable. Read finishReason: "tool-calls" at the step cap means the model still wanted another tool call when the cap cut it off; report the same `step cap reached (N)` stopReason shape anthropic_sdk already uses so every bare-loop harness is comparable. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Drops the dated-event rationale from the module docblock in favor of a present-tense description of the loop shape. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
baf8b91 to
1fe1fb5
Compare
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.
What
Part 2 of 5 — stacked on #2337 (base:
shrey/evals-bareloop-harnesses; GitHub shows only this PR's delta). When #2337 merges, retarget this PR's base tomainand rebase.Registers
--harness vercel_ai_sdk: a bare loop via theaipackage —generateText+stopWhen: stepCountIs(N)with a single gatedbrowsetool. The AI SDK's own multi-step loop IS the harness; zero additional scaffolding. This mirrors the JS-default bare loop found in the wild (15.1M npm dl/wk) and the Modal sandbox template used in the 2026-07-09 smoke that surfaced 5 SKILL.md issues.getAISDKLanguageModelprovider map, so-m provider/modelmeans the same thing as on the stagehand harness.EVAL_VERCEL_AI_SDK_MAX_STEPSoverride.NormalizedToolCalls →gradeExternalTrajectoryunchanged.generateTextFninjection seam — unit tests drive the loop and invoke the real tool wiring against a tempbrowsebinary.External harnesses design + usage:
packages/evals/README.md#external-harnesses(in #2337). Linear: STG-2515.Comparability fix (2026-07-09 audit)
generateTextreturns normally whetherstopWhen: stepCountIs(maxSteps)cut the loop off mid tool-call or the model actually finished —finishReasonwas declared on the result type but never read, so a cap-truncated run and a clean stop were indistinguishable invercelAiSdkStatus/vercelAiSdkStopReason. Now readsfinishReason === "tool-calls"atstepsUsed >= maxStepsas the step-cap signal and reports the samestep cap reached (N)stopReason shapeanthropic_sdk(#2339) already uses, so every bare-loop harness surfaces the cap identically. Two new tests cover both the capped case and a clean stop that happens to land exactly at the max step count (to prove the fix keys offfinishReason, not just the step count).E2E Test Matrix
Live smoke ran on the pre-split combined branch (content identical to this stack's tip — verified by diff); results transplanted, not rerun.
evals run b:webtailbench --harness vercel_ai_sdk -m anthropic/claude-haiku-4-5-20251001 -l 1 -t 1 -c 1(local build, real ANTHROPIC + GEMINI keys, local Chrome via browse CLI)browsetool calls recorded (first action:open https://www.google.com); trajectory persisted; verifier graded:outcomeSuccess=false,processScore=0.79, 5 rubric criteria, no verifierError. Usage: 440k in / 1.3k out tokenspnpm exec vitest run(this branch)Test Files 50 passed, Tests 371 passed— addsvercelAiSdkRunner.test.ts(system prompt = verbatim bare one-liner, tool wiring against a real temp binary, step/token metrics, error-to-TaskResult folding) + the registry testtsc --noEmit+ prettier + eslintpnpm exec vitest run(this branch, post-fix)Test Files 50 passed, Tests 373 passed— adds the two step-cap tests (capped-with-tool-calls-finishReason vs. clean-stop-at-max-steps)pnpm -w exec tsc --noEmit+ prettier + eslint (post-fix)The 440k cumulative input tokens in the smoke is what motivated the 20k-char tool-output clip that ships in the base PR (
EVAL_BARE_LOOP_TOOL_OUTPUT_LIMIT) — the smoke ran pre-clip and still completed, but largersnapshotoutputs would have overflowed context.No changeset:
packages/evalsis private.🤖 Generated with Claude Code
Summary by cubic
Adds a
vercel_ai_sdkbare-loop harness that runsai.generateTextwithstopWhen: stepCountIs(N)and a single gatedbrowsetool. Registers it as an executable bench harness; aligns model mapping with@browserbasehq/stagehandand reports step-cap truncation consistently (STG-2515).New Features
--harness vercel_ai_sdkas executable (agent/suite).generateTextwithstopWhen: stepCountIs(N); default max steps 40 viaEVAL_VERCEL_AI_SDK_MAX_STEPS.@browserbasehq/stagehandgetAISDKLanguageModel; requiresprovider/model.browsetool; records tool calls; returns usage metrics (input/output plus optionalcached_inputandreasoning).generateTextFnfor tests.Bug Fixes
finishReason === "tool-calls"at the cap and reportsstep cap reached (N), matching other bare-loop harnesses.Written for commit 1fe1fb5. Summary will update on new commits.