2525 generate_id ,
2626)
2727from marimo ._dependencies .dependencies import Dependency , DependencyManager
28- from marimo ._plugins .ui ._impl .chat .chat import AI_SDK_VERSION
28+ from marimo ._plugins .ui ._impl .chat .chat import (
29+ AI_SDK_VERSION ,
30+ require_vercel_ai_sdk_support ,
31+ )
2932from marimo ._server .ai .config import AnyProviderConfig
3033from marimo ._server .ai .ids import AiModelId
3134from marimo ._server .ai .tools .tool_manager import get_tool_manager
4043 from openai import AsyncOpenAI
4144 from openai .types .shared .reasoning_effort import ReasoningEffort
4245 from pydantic_ai import Agent , DeferredToolRequests , FunctionToolset
43- from pydantic_ai .messages import ThinkingPart
4446 from pydantic_ai .models import Model
4547 from pydantic_ai .models .bedrock import BedrockConverseModel
4648 from pydantic_ai .models .google import GoogleModel
5456 )
5557 from pydantic_ai .providers .google import GoogleProvider as PydanticGoogle
5658 from pydantic_ai .providers .openai import OpenAIProvider as PydanticOpenAI
57- from pydantic_ai .ui .vercel_ai import VercelAIAdapter
5859 from pydantic_ai .ui .vercel_ai .request_types import UIMessage , UIMessagePart
59- from pydantic_ai .ui .vercel_ai .response_types import BaseChunk
6060 from starlette .responses import StreamingResponse
6161
6262
@@ -101,6 +101,7 @@ def __init__(
101101 * (deps or []),
102102 source = "server" ,
103103 )
104+ require_vercel_ai_sdk_support ()
104105
105106 self .model = model
106107 self .config = config
@@ -132,12 +133,6 @@ def create_agent(
132133 output_type = output_type ,
133134 )
134135
135- def get_vercel_adapter (self ) -> type [VercelAIAdapter [Any , Any ]]:
136- """Return the Vercel AI adapter for the given provider."""
137- from pydantic_ai .ui .vercel_ai import VercelAIAdapter
138-
139- return VercelAIAdapter
140-
141136 def convert_messages (
142137 self , messages : list [ServerUIMessage ]
143138 ) -> list [UIMessage ]:
@@ -153,6 +148,7 @@ async def stream_completion(
153148 stream_options : Optional [StreamOptions ] = None ,
154149 ) -> StreamingResponse :
155150 """Return a streaming response from the given messages. The response are AI SDK events."""
151+ from pydantic_ai .ui .vercel_ai import VercelAIAdapter
156152 from pydantic_ai .ui .vercel_ai .request_types import SubmitMessage
157153
158154 tools = (self .config .tools or []) + additional_tools
@@ -169,18 +165,12 @@ async def stream_completion(
169165 # TODO: Text only and format stream are not supported yet
170166 stream_options = stream_options or StreamOptions ()
171167
172- vercel_adapter = self .get_vercel_adapter ()
173- if DependencyManager .pydantic_ai .has_at_version (min_version = "1.52.0" ):
174- adapter = vercel_adapter (
175- agent = agent ,
176- run_input = run_input ,
177- accept = stream_options .accept ,
178- sdk_version = AI_SDK_VERSION ,
179- )
180- else :
181- adapter = vercel_adapter (
182- agent = agent , run_input = run_input , accept = stream_options .accept
183- )
168+ adapter = VercelAIAdapter (
169+ agent = agent ,
170+ run_input = run_input ,
171+ accept = stream_options .accept ,
172+ sdk_version = AI_SDK_VERSION ,
173+ )
184174 event_stream = adapter .run_stream ()
185175 return adapter .streaming_response (event_stream )
186176
@@ -193,16 +183,16 @@ async def stream_text(
193183 additional_tools : list [ToolDefinition ],
194184 ) -> AsyncGenerator [str ]:
195185 """Return a stream of text from the given messages."""
186+ from pydantic_ai .ui .vercel_ai import VercelAIAdapter
196187
197188 tools = (self .config .tools or []) + additional_tools
198189 agent = self .create_agent (
199190 max_tokens = max_tokens , tools = tools , system_prompt = system_prompt
200191 )
201- vercel_adapter = self .get_vercel_adapter ()
202192
203193 async with agent .run_stream (
204194 user_prompt = user_prompt ,
205- message_history = vercel_adapter .load_messages (
195+ message_history = VercelAIAdapter .load_messages (
206196 self .convert_messages (messages )
207197 ),
208198 ) as result :
@@ -800,73 +790,6 @@ def process_part(self, part: UIMessagePart) -> UIMessagePart:
800790 )
801791 return part
802792
803- def get_vercel_adapter (
804- self ,
805- ) -> type [VercelAIAdapter [None , DeferredToolRequests | str ]]:
806- """
807- Return a custom adapter that includes thinking signatures in ReasoningEndChunk.
808-
809- pydantic_ai's VercelAIEventStream.handle_thinking_end doesn't pass the signature
810- from ThinkingPart to ReasoningEndChunk, which breaks Anthropic's extended thinking
811- on follow-up messages (Anthropic requires signatures on thinking blocks).
812-
813- This is a patch for pydantic-ai <1.47.0, which doesn't include the signature in the ReasoningEndChunk.
814- """
815- if DependencyManager .pydantic_ai .has_at_version (min_version = "1.47.0" ):
816- return super ().get_vercel_adapter ()
817-
818- from pydantic_ai import DeferredToolRequests
819- from pydantic_ai .ui .vercel_ai import VercelAIAdapter
820- from pydantic_ai .ui .vercel_ai ._event_stream import VercelAIEventStream
821- from pydantic_ai .ui .vercel_ai .response_types import ReasoningEndChunk
822-
823- AnthropicOutputType = DeferredToolRequests | str
824-
825- # Custom event stream that includes signature in ReasoningEndChunk
826- class AnthropicVercelAIEventStream (
827- VercelAIEventStream [None , AnthropicOutputType ]
828- ):
829- async def handle_thinking_end (
830- self , part : ThinkingPart , followed_by_thinking : bool = False
831- ) -> AsyncIterator [BaseChunk ]:
832- """Override to include signature in provider_metadata."""
833- try :
834- provider_metadata = None
835- if part .signature :
836- pydantic_ai_meta : dict [str , Any ] = {
837- "signature" : part .signature
838- }
839- if part .provider_name :
840- pydantic_ai_meta ["provider_name" ] = (
841- part .provider_name
842- )
843- if part .id :
844- pydantic_ai_meta ["id" ] = part .id
845- provider_metadata = {"pydantic_ai" : pydantic_ai_meta }
846-
847- yield ReasoningEndChunk (
848- id = self .message_id , provider_metadata = provider_metadata
849- )
850- except Exception as e :
851- LOGGER .warning (
852- f"Error in AnthropicVercelAIEventStream.handle_thinking_end: { e } "
853- )
854- async for chunk in super ().handle_thinking_end (
855- part , followed_by_thinking
856- ):
857- yield chunk
858-
859- # Custom adapter that uses the custom event stream
860- class AnthropicVercelAIAdapter (
861- VercelAIAdapter [None , AnthropicOutputType ]
862- ):
863- def build_event_stream (self ) -> AnthropicVercelAIEventStream :
864- return AnthropicVercelAIEventStream (
865- self .run_input , accept = self .accept
866- )
867-
868- return AnthropicVercelAIAdapter
869-
870793
871794class BedrockProvider (PydanticProvider ["PydanticBedrock" ]):
872795 def setup_credentials (self , config : AnyProviderConfig ) -> None :
0 commit comments