Skip to content

feat(core): add prepareStep to AgentOptions for per-step tool control#1192

Merged
omeraplak merged 1 commit intoVoltAgent:mainfrom
ravyg:feat/agent-prepare-step
Apr 8, 2026
Merged

feat(core): add prepareStep to AgentOptions for per-step tool control#1192
omeraplak merged 1 commit intoVoltAgent:mainfrom
ravyg:feat/agent-prepare-step

Conversation

@ravyg
Copy link
Copy Markdown
Contributor

@ravyg ravyg commented Apr 6, 2026

Surfaces the AI SDK's prepareStep callback as a top-level AgentOptions property. Users can now set a default step preparation callback at agent creation time to control tool availability per step.

Per-call prepareStep in method options overrides the agent-level default. The agent-level default is applied before applyForcedToolChoice so both features compose correctly.

const agent = new Agent({
  name: "my-agent",
  model: openai("gpt-4o"),
  tools: [dataTool, analysisTool],
  prepareStep: ({ steps }) => (steps.length > 0 ? { toolChoice: "none" } : {}),
});

Fixes #1187

PR Checklist

Please check if your PR fulfills the following requirements:

Bugs / Features

What is the current behavior?

prepareStep can only be passed per-call via method options (e.g. agent.generateText({ prepareStep: ... })). There is no way to set a default at agent creation time, forcing users to repeat the callback on every call.

What is the new behavior?

prepareStep is now available as a top-level AgentOptions property. It follows the same pattern as stopWhen — agent-level default, per-call override.

Changes:

  • ai-types.ts — new PrepareStep type alias (follows existing StopWhen pattern)
  • types.ts — added prepareStep to AgentOptions
  • agent.ts — added prepareStep to BaseGenerationOptions, stored on Agent instance, applied in both generateText and streamText paths before applyForcedToolChoice
  • index.ts — exported PrepareStep type

fixes #1187

Notes for reviewers

Follows the exact same pattern as stopWhen (agent-level default, per-call override). The agent-level prepareStep is applied before applyForcedToolChoice wraps it, so forced tool choice still works correctly on the first step. Build passes across all 28 packages. No new dependencies.


Summary by cubic

Expose prepareStep on AgentOptions in @voltagent/core so agents can set a default per-step callback for tool and step control. Per-call prepareStep still overrides and runs before forced tool choice; fixes #1187.

  • New Features
    • Default prepareStep is applied in generateText and streamText.
    • Exported PrepareStep; added tests and a migration guide example.

Written for commit 1c617bb. Summary will update on new commits.

Summary by CodeRabbit

  • New Features
    • Agents can have a default prepareStep callback that runs before each step; per-call prepareStep overrides the agent default.
    • PrepareStep is publicly exported to enable custom step preparation in agent workflows.
  • Tests
    • Added tests validating agent-level and per-call prepareStep behavior for both generation and streaming.
  • Documentation
    • Migration guide updated with examples and guidance for using prepareStep.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Apr 6, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 2179b8e6-5807-4eee-8175-f3fe29e91fc3

📥 Commits

Reviewing files that changed from the base of the PR and between 3e219d9 and 1c617bb.

📒 Files selected for processing (7)
  • .changeset/feat-agent-prepare-step.md
  • packages/core/src/agent/agent.ts
  • packages/core/src/agent/prepare-step.spec.ts
  • packages/core/src/agent/types.ts
  • packages/core/src/ai-types.ts
  • packages/core/src/index.ts
  • website/docs/getting-started/migration-guide.md
✅ Files skipped from review due to trivial changes (4)
  • .changeset/feat-agent-prepare-step.md
  • website/docs/getting-started/migration-guide.md
  • packages/core/src/ai-types.ts
  • packages/core/src/agent/prepare-step.spec.ts
🚧 Files skipped from review as they are similar to previous changes (3)
  • packages/core/src/index.ts
  • packages/core/src/agent/types.ts
  • packages/core/src/agent/agent.ts

📝 Walkthrough

Walkthrough

Adds a top-level prepareStep option to AgentOptions and Agent, exports a PrepareStep type, uses the agent-level prepareStep as a default in generateText and streamText when per-call prepareStep is absent, and adds tests, docs, and a changeset.

Changes

Cohort / File(s) Summary
Types & Exports
packages/core/src/ai-types.ts, packages/core/src/index.ts
Add exported PrepareStep type and re-export it from the package entry point.
Agent Types
packages/core/src/agent/types.ts
Add prepareStep?: PrepareStep to AgentOptions with JSDoc explaining agent-level default and per-call precedence.
Agent Implementation
packages/core/src/agent/agent.ts
Add readonly prepareStep?: PrepareStep on Agent; extend base generation options; inject agent-level prepareStep into aiSDKOptions.prepareStep for generateText and streamText only when per-call prepareStep is not provided.
Tests
packages/core/src/agent/prepare-step.spec.ts
New Vitest tests verifying agent stores prepareStep, agent-level prepareStep is invoked during generation/streaming, and per-call prepareStep overrides the agent-level default.
Docs & Changeset
website/docs/getting-started/migration-guide.md, .changeset/feat-agent-prepare-step.md
Document prepareStep usage and precedence; add changeset entry for the minor release.

Sequence Diagram(s)

sequenceDiagram
    participant Client as Client
    participant Agent as Agent
    participant Prepare as PrepareStep
    participant AI as AI_SDK

    Client->>Agent: generateText(options)
    Agent->>Prepare: invoke (options.prepareStep || agent.prepareStep) with { steps }
    Prepare-->>Agent: return stepOptions
    Agent->>AI: call generateText(aiSDKOptions + stepOptions)
    AI-->>Agent: response / stream
    Agent-->>Client: return result
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Poem

🐇 I tweak each step with a twitch and peep,
A prepareStep guard before the leap,
Agent sets the default, calls can override,
Tests, docs, and types — I cleared the stride. 🥕

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the main change: adding prepareStep to AgentOptions for per-step tool control, which is the primary focus of the PR.
Description check ✅ Passed The description fully covers all required checklist items: linked issue, tests added, docs updated, changesets created, current/new behavior explained, and reviewer notes included.
Linked Issues check ✅ Passed The PR fully implements the requirement from #1187: exposes prepareStep as an AgentOptions property to control tool availability per step, supporting the exact use case described (toolChoice control across steps).
Out of Scope Changes check ✅ Passed All changes are directly related to implementing prepareStep support in AgentOptions: type definitions, agent implementation, tests, documentation, and exports are all within scope of the issue objective.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@changeset-bot
Copy link
Copy Markdown

changeset-bot bot commented Apr 6, 2026

🦋 Changeset detected

Latest commit: 1c617bb

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
@voltagent/core Minor

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

@ravyg ravyg marked this pull request as ready for review April 6, 2026 18:33
Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No issues found across 5 files

@ravyg ravyg force-pushed the feat/agent-prepare-step branch from 12f7aec to f9fabf7 Compare April 6, 2026 18:41
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
packages/core/src/agent/prepare-step.spec.ts (1)

74-98: Add a matching per-call override test for streamText.

Override precedence is only asserted for generateText; adding the same check for streamText would fully cover the stated contract.

Suggested test addition
+  it("should allow per-call prepareStep to override agent-level in streamText", async () => {
+    const agentPrepareStep = vi.fn(() => ({}));
+    const callPrepareStep = vi.fn(() => ({}));
+    const model = createMockLanguageModel();
+
+    const agent = new Agent({
+      name: "test-agent",
+      instructions: "test",
+      model,
+      prepareStep: agentPrepareStep,
+    });
+
+    const result = await agent.streamText("hello", {
+      prepareStep: callPrepareStep,
+    });
+    for await (const _part of result.textStream) {
+      // drain
+    }
+
+    expect(callPrepareStep).toHaveBeenCalled();
+    expect(agentPrepareStep).not.toHaveBeenCalled();
+  });
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/core/src/agent/prepare-step.spec.ts` around lines 74 - 98, Add a
parallel test to the existing "should allow per-call prepareStep to override
agent-level" that calls agent.streamText instead of agent.generateText: create
the same agentPrepareStep and callPrepareStep mocks, instantiate Agent with
prepareStep: agentPrepareStep, call agent.streamText("hello", { prepareStep:
callPrepareStep }) (awaiting the stream completion as needed), and assert that
callPrepareStep was called and agentPrepareStep was not; reference Agent,
streamText, generateText, and prepareStep to mirror the existing test structure.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@packages/core/src/agent/prepare-step.spec.ts`:
- Around line 74-98: Add a parallel test to the existing "should allow per-call
prepareStep to override agent-level" that calls agent.streamText instead of
agent.generateText: create the same agentPrepareStep and callPrepareStep mocks,
instantiate Agent with prepareStep: agentPrepareStep, call
agent.streamText("hello", { prepareStep: callPrepareStep }) (awaiting the stream
completion as needed), and assert that callPrepareStep was called and
agentPrepareStep was not; reference Agent, streamText, generateText, and
prepareStep to mirror the existing test structure.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: d7eeedc7-2097-4a04-b323-cf555cf1f970

📥 Commits

Reviewing files that changed from the base of the PR and between 12f7aec and f9fabf7.

📒 Files selected for processing (6)
  • .changeset/feat-agent-prepare-step.md
  • packages/core/src/agent/agent.ts
  • packages/core/src/agent/prepare-step.spec.ts
  • packages/core/src/agent/types.ts
  • packages/core/src/ai-types.ts
  • packages/core/src/index.ts
✅ Files skipped from review due to trivial changes (1)
  • packages/core/src/ai-types.ts
🚧 Files skipped from review as they are similar to previous changes (4)
  • .changeset/feat-agent-prepare-step.md
  • packages/core/src/index.ts
  • packages/core/src/agent/types.ts
  • packages/core/src/agent/agent.ts

@ravyg ravyg force-pushed the feat/agent-prepare-step branch 2 times, most recently from 4a29c11 to 3e219d9 Compare April 6, 2026 18:52
Surfaces the AI SDK's prepareStep callback as a top-level AgentOptions
property. Users can now set a default step preparation callback at
agent creation time to control tool availability per step.

Per-call prepareStep in method options overrides the agent-level default.
The agent-level default is applied before applyForcedToolChoice so both
features compose correctly.

Fixes VoltAgent#1187
Copy link
Copy Markdown
Member

@omeraplak omeraplak left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey @ravyg ,
Thank you so much 🔥

@omeraplak omeraplak merged commit 0dc2935 into VoltAgent:main Apr 8, 2026
22 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[FEAT]: add missing prepareStep callback from AI SDK

2 participants