DurableAgent: various compatibility fixes#1385
Conversation
… fix, telemetry spans - Add InferDurableAgentTools and InferDurableAgentUIMessage type helpers (#1302) - Support prepareStep on DurableAgent constructor with stream-level override (#1303) - Fix AI_DownloadError for image URLs by defaulting to no-op download (#1376) - Add telemetry span support (ai.streamText.doStream, ai.toolCall) via optional @opentelemetry/api (#1296) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
🦋 Changeset detectedLatest commit: 094fe61 The changes in this PR will be included in the next version bump. This PR includes changesets to release 1 package
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
🧪 E2E Test Results❌ Some tests failed Summary
❌ Failed Tests🌍 Community Worlds (55 failed)mongodb (3 failed):
redis (2 failed):
turso (50 failed):
Details by Category✅ ▲ Vercel Production
✅ 💻 Local Development
✅ 📦 Local Production
✅ 🐘 Local Postgres
✅ 🪟 Windows
❌ 🌍 Community Worlds
✅ 📋 Other
|
📊 Benchmark Results
workflow with no steps💻 Local Development
▲ Production (Vercel)
🔍 Observability: Next.js (Turbopack) | Express | Nitro workflow with 1 step💻 Local Development
▲ Production (Vercel)
🔍 Observability: Next.js (Turbopack) | Express | Nitro workflow with 10 sequential steps💻 Local Development
▲ Production (Vercel)
🔍 Observability: Express | Nitro | Next.js (Turbopack) workflow with 25 sequential steps💻 Local Development
▲ Production (Vercel)
🔍 Observability: Next.js (Turbopack) | Express | Nitro workflow with 50 sequential steps💻 Local Development
▲ Production (Vercel)
🔍 Observability: Next.js (Turbopack) | Express | Nitro Promise.all with 10 concurrent steps💻 Local Development
▲ Production (Vercel)
🔍 Observability: Express | Next.js (Turbopack) | Nitro Promise.all with 25 concurrent steps💻 Local Development
▲ Production (Vercel)
🔍 Observability: Nitro | Express | Next.js (Turbopack) Promise.all with 50 concurrent steps💻 Local Development
▲ Production (Vercel)
🔍 Observability: Express | Nitro | Next.js (Turbopack) Promise.race with 10 concurrent steps💻 Local Development
▲ Production (Vercel)
🔍 Observability: Express | Next.js (Turbopack) | Nitro Promise.race with 25 concurrent steps💻 Local Development
▲ Production (Vercel)
🔍 Observability: Express | Nitro | Next.js (Turbopack) Promise.race with 50 concurrent steps💻 Local Development
▲ Production (Vercel)
🔍 Observability: Express | Nitro | Next.js (Turbopack) Stream Benchmarks (includes TTFB metrics)workflow with stream💻 Local Development
▲ Production (Vercel)
🔍 Observability: Next.js (Turbopack) | Express | Nitro SummaryFastest Framework by WorldWinner determined by most benchmark wins
Fastest World by FrameworkWinner determined by most benchmark wins
Column Definitions
Worlds:
|
- Use space separator in operation.name (not dot) to match AI SDK - Add resource.name attribute from functionId - Use recordException() + setStatus() for error spans (preserves stack traces) - Add context.with() for proper parent-child span propagation - Make recordSpan self-initializing (remove initTelemetry two-phase requirement) - Add recordInputs/recordOutputs to TelemetrySettings for sensitive data gating - Add gen_ai.* semantic convention attributes to doStream span - Add ai.toolCall.args gated by recordOutputs Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Pass through LanguageModelV3ToolResultOutput from tool execute when the result is already a valid output type (text, json, content, error-text, error-json, execution-denied). This enables tools to return multimodal content (images, files) that models can process via vision. (#848) - Improve sendStart JSDoc to document the messageId conflict when writing custom chunks before agent.stream(), guiding users to set sendStart: false in that scenario. (#975) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Convert contextStorage AsyncLocalStorage from module-scoped to a globalThis singleton using Symbol.for(). This prevents esbuild's module scope duplication from creating separate ALS instances, which caused AsyncLocalStorage.getStore() to return undefined inside tool execute() callbacks. Cherry-picked from 8ea3f89 (peter/v2-flow), excluding unrelated v2-flow runtime changes. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Signed-off-by: Peter Wielander <mittgfu@gmail.com>
Signed-off-by: Peter Wielander <mittgfu@gmail.com>
Next.js 16.2.0-canary.100+ has a regression where @workflow/ai step files are missing from the step bundle, causing "doStreamStep not found" errors that hang the agent tests until timeout. Signed-off-by: Peter Wielander <mittgfu@gmail.com> Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
DEV_TEST_CONFIG was only set in the dev test job, so prod and postgres canary jobs didn't skip agent tests. Add NEXT_CANARY env var to all three local e2e test jobs (dev, prod, postgres) and use it directly. Signed-off-by: Peter Wielander <mittgfu@gmail.com> Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
karthikscale3
left a comment
There was a problem hiding this comment.
Changes look good to me.
- o11y reporting matches 1:1 to aisdk
- prepareStep addition looks good
pranaygp
left a comment
There was a problem hiding this comment.
we should really update the e2e tests and the DurableAgent/ToolLoopAgent integration compat tests to prevent regressions here and to validate tat the features actually work
thank you so much!!
This reverts commit bd32306.
- agentConstructorPrepareStepE2e: verifies agent-level prepareStep is called for each LLM step through the full workflow runtime - agentStreamPrepareStepOverrideE2e: verifies stream-level prepareStep overrides constructor-level - agentMultimodalToolResultE2e: verifies tools returning LanguageModelV3ToolResultOutput (type: 'content') pass through the serialization/deserialization boundary correctly Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add three tests to the ToolLoopAgent compat suite verifying: - Constructor-level prepareStep is called on each LLM step - Stream-level prepareStep overrides constructor-level - Constructor prepareStep fires for every step in multi-step tool loops Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
| prepareStep: ({ stepNumber }) => { | ||
| stepNumbers.push(stepNumber); | ||
| return {}; | ||
| }, |
There was a problem hiding this comment.
I'm curious if prepareStep is typically used as a step function, or used inline in the workflow context itself?
I did a quick scan on AI sdk docs and looks like your example is exactly what it's typically used for :)
There was a problem hiding this comment.
no action needed - just curious
| { type: 'text', text: 'Here is the image' }, | ||
| { type: 'file-data', data: 'iVBORw0KGgo=', mediaType: 'image/png' }, | ||
| ], | ||
| }; |
There was a problem hiding this comment.
https://ai-sdk.dev/docs/ai-sdk-core/tools-and-tool-calling#multi-modal-tool-results
nit: should we actually use toModelOutput? looks like that's the intended way to use it since tool authors typically wouldn't write a tool to return in the model message format, but would use the converter
just to make the e2e tests more real? claude, you might want to validate that ToolLoopAgent also propagates this toModelOutput since that example was taken from generateText
pranaygp
left a comment
There was a problem hiding this comment.
lgtm - thanks for adding the tests. left some comments :)
Closes #1303 : Support prepareStep on DurableAgent constructor with stream-level override
Closes #1302 : Add InferDurableAgentTools and InferDurableAgentUIMessage type helpers
Closes #1376 : Fix AI_DownloadError for image URLs by defaulting to no-op download
Closes #1296 : Add telemetry span support (ai.streamText.doStream, ai.toolCall) via optional @opentelemetry/api
Closes #848: Allow LanguageModelV3ToolResultOutput results to pass through without json parsin
Closes #975: Documents the necessity of
sendStart: falsewhen writing customUIMessageChunksCo-Authored-By: @nicoalbanese