Fix prepareStep system message lost when messages is also returned#1389
Fix prepareStep system message lost when messages is also returned#1389TooTallNate merged 2 commits intomainfrom
Conversation
🦋 Changeset detectedLatest commit: 93cf09e 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: Nitro | Express | Next.js (Turbopack) workflow with 1 step💻 Local Development
▲ Production (Vercel)
🔍 Observability: Nitro | Express | Next.js (Turbopack) workflow with 10 sequential steps💻 Local Development
▲ Production (Vercel)
🔍 Observability: Express | Next.js (Turbopack) | Nitro workflow with 25 sequential steps💻 Local Development
▲ Production (Vercel)
🔍 Observability: Express | Next.js (Turbopack) | Nitro workflow with 50 sequential steps💻 Local Development
▲ Production (Vercel)
🔍 Observability: Nitro | Next.js (Turbopack) | Express Promise.all with 10 concurrent steps💻 Local Development
▲ Production (Vercel)
🔍 Observability: Nitro | Express | Next.js (Turbopack) Promise.all with 25 concurrent steps💻 Local Development
▲ Production (Vercel)
🔍 Observability: Nitro | Next.js (Turbopack) | Express Promise.all with 50 concurrent steps💻 Local Development
▲ Production (Vercel)
🔍 Observability: Express | Next.js (Turbopack) | Nitro Promise.race with 10 concurrent steps💻 Local Development
▲ Production (Vercel)
🔍 Observability: Next.js (Turbopack) | Nitro | Express Promise.race with 25 concurrent steps💻 Local Development
▲ Production (Vercel)
🔍 Observability: Next.js (Turbopack) | Nitro | Express 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) | Nitro | Express SummaryFastest Framework by WorldWinner determined by most benchmark wins
Fastest World by FrameworkWinner determined by most benchmark wins
Column Definitions
Worlds:
❌ Some benchmark jobs failed:
Check the workflow run for details. |
There was a problem hiding this comment.
Pull request overview
Fixes a DurableAgent/streamTextIterator bug where a prepareStep callback returning both messages and system would inadvertently drop the system message due to override ordering.
Changes:
- Apply
prepareStep.messagesoverride before applyingprepareStep.system, ensuring the system message is preserved. - Add a changeset to publish the fix as a patch for
@workflow/ai.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
| packages/ai/src/agent/stream-text-iterator.ts | Reorders prepareStep override application so system is applied after messages. |
| .changeset/fix-prepare-step-system-ordering.md | Adds a patch changeset entry documenting the bug fix. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
You can also share your feedback on Copilot code review. Take the survey.
| @@ -167,9 +172,6 @@ export async function* streamTextIterator({ | |||
| }); | |||
| } | |||
| } | |||
Apply messages override before system override so that the system message is prepended to the new prompt instead of being discarded. Previously, system was applied first then messages replaced the entire conversation prompt, losing the system message. Add tests for prepareStep system/messages ordering: - system only: prepended to prompt - system + messages: system prepended to replaced messages - system + messages with existing system: replaces existing - system updates across multi-step tool call rounds
02cdd28 to
9426b01
Compare
| '@workflow/ai': patch | ||
| --- | ||
|
|
||
| Fix `prepareStep` system message being discarded when `messages` is also returned |
There was a problem hiding this comment.
can we add an e2e test for this to prevent regressions @TooTallNate? since we now have the DurableAgent e2e test infra as of this week :)
There was a problem hiding this comment.
also when relevant/possible, we should update the DurableAgent chat app to use the feature - for example when adding/fixing provider tools and client side tools, I would've been good to add that feature to the app as an easy way to try it and see if it actually works and the DX for it (the app didn't exist at the time and the friction to do a tarball and test with flight booking app is a bit annoying)
maybe this time we should have something in the DurableAgent test app that uses prepareStep?
Summary
DurableAgent'sprepareStepwhere returning bothsystemandmessagescaused the system message to be discardedsystemoverride was applied first (prepended toconversationPrompt), thenmessagesreplacedconversationPromptentirely, losing the system messagemessagesfirst, thensystem, so the system message is prepended to the new promptDurableAgentusage whereprepareStepreturned a dynamic system prompt alongside modified messages (e.g. for context window management or message injection)