From 2e45c9e48b73ca438ce16519dab04c106aa8e773 Mon Sep 17 00:00:00 2001 From: Shahmir Varqha Date: Wed, 24 Jun 2026 18:05:57 +0800 Subject: [PATCH 1/7] chore(ai): migrate to pydantic-ai v2 Bump the minimum pydantic-ai version to 2.0 and drop v1 compatibility shims now that profiles are TypedDicts and model construction validates profiles eagerly. Co-authored-by: Cursor --- examples/ai/chat/pydantic-ai-chat.py | 28 ++++++------- marimo/_ai/llm/_impl.py | 31 +++----------- marimo/_plugins/ui/_impl/chat/chat.py | 10 ----- marimo/_server/ai/providers.py | 41 +++---------------- marimo/_server/ai/tools/code_mode.py | 4 +- tests/_ai/llm/test_impl.py | 13 +++--- tests/_ai/test_pydantic_utils.py | 14 ------- tests/_server/ai/test_ai_config.py | 2 +- tests/_server/ai/test_providers.py | 38 ++++++++--------- .../optional-dependencies-recommended.txt | 2 +- 10 files changed, 51 insertions(+), 132 deletions(-) diff --git a/examples/ai/chat/pydantic-ai-chat.py b/examples/ai/chat/pydantic-ai-chat.py index b3d740e7066..d7eb5507eb3 100644 --- a/examples/ai/chat/pydantic-ai-chat.py +++ b/examples/ai/chat/pydantic-ai-chat.py @@ -9,7 +9,7 @@ import marimo -__generated_with = "0.23.6" +__generated_with = "0.23.10" app = marimo.App(width="medium") with app.setup(hide_code=True): @@ -272,19 +272,6 @@ def _pending_approval(messages) -> dict | None: return None - async def custom_model(messages, config): - del config - - pending = _pending_approval(messages) - if pending is not None: - async for chunk in _resume_after_approval(pending): - yield chunk - return - - async for chunk in _showcase_turn(): - yield chunk - - async def _showcase_turn(): reasoning_id = _new_id("reasoning") search_id = _new_id("tc") @@ -497,6 +484,19 @@ async def _resume_after_approval(pending: dict): yield vercel.FinishChunk(finish_reason="stop") + async def custom_model(messages, config): + del config + + pending = _pending_approval(messages) + if pending is not None: + async for chunk in _resume_after_approval(pending): + yield chunk + return + + async for chunk in _showcase_turn(): + yield chunk + + custom_chat = mo.ui.chat( custom_model, prompts=[ diff --git a/marimo/_ai/llm/_impl.py b/marimo/_ai/llm/_impl.py index 6d838c94cfc..724af4ee115 100644 --- a/marimo/_ai/llm/_impl.py +++ b/marimo/_ai/llm/_impl.py @@ -796,21 +796,6 @@ def _serialize_vercel_ai_chunk( result, ) return result # type: ignore[no-any-return] - except TypeError: - # Fallback for pydantic-ai < 1.52.0 which doesn't have sdk_version param - try: - # by_alias=True: Use camelCase keys expected by Vercel AI SDK. - # exclude_none=True: Remove null values which cause validation errors. - serialized = chunk.model_dump( - mode="json", by_alias=True, exclude_none=True - ) - except Exception as e: - LOGGER.error("Error serializing vercel ai chunk: %s", e) - return None - else: - if serialized.get("type") == "done": - return None - return serialized except Exception as e: LOGGER.error("Error serializing vercel ai chunk: %s", e) return None @@ -838,17 +823,11 @@ async def _stream_response( messages=ui_messages, ) - try: - adapter = VercelAIAdapter( - agent=self.agent, - run_input=run_input, - sdk_version=AI_SDK_VERSION, - ) - except TypeError: - adapter = VercelAIAdapter( - agent=self.agent, - run_input=run_input, - ) + adapter = VercelAIAdapter( + agent=self.agent, + run_input=run_input, + sdk_version=AI_SDK_VERSION, + ) event_stream = adapter.run_stream(model_settings=model_settings) async for event in event_stream: if serialized := self._serialize_vercel_ai_chunk(event): diff --git a/marimo/_plugins/ui/_impl/chat/chat.py b/marimo/_plugins/ui/_impl/chat/chat.py index 556e851cb52..414d8c76668 100644 --- a/marimo/_plugins/ui/_impl/chat/chat.py +++ b/marimo/_plugins/ui/_impl/chat/chat.py @@ -45,13 +45,6 @@ DONE_CHUNK: Final[str] = "[DONE]" -def require_vercel_ai_sdk_support() -> None: - """Only Pydantic AI >=1.52.0 supports AI SDK v6. So, we require it.""" - DependencyManager.pydantic_ai.require_at_version( - why="for Vercel AI SDK support", min_version="1.52.0" - ) - - @dataclass class SendMessageRequest: messages: list[ChatMessage] @@ -416,7 +409,6 @@ def _emit_cancellation_chunks( abort_payload: dict[str, Any] | None = None if DependencyManager.pydantic_ai.imported(): try: - require_vercel_ai_sdk_support() from pydantic_ai.ui.vercel_ai.response_types import ( AbortChunk, ) @@ -580,7 +572,6 @@ def _convert_value(self, value: dict[str, Any]) -> list[ChatMessage]: part_validator_class = None if DependencyManager.pydantic_ai.imported(): - require_vercel_ai_sdk_support() from pydantic_ai.ui.vercel_ai.request_types import UIMessagePart # The frontend sends messages as ChatMessage parts so we use pydantic-ai to cast them @@ -645,7 +636,6 @@ def handle_chunk(self, chunk: Any) -> None: # Handle Pydantic AI's Vercel AI SDK chunks if DependencyManager.pydantic_ai.imported(): - require_vercel_ai_sdk_support() from pydantic_ai.ui.vercel_ai.response_types import ( BaseChunk, ) diff --git a/marimo/_server/ai/providers.py b/marimo/_server/ai/providers.py index 290ce1620bd..475ba57b229 100644 --- a/marimo/_server/ai/providers.py +++ b/marimo/_server/ai/providers.py @@ -27,7 +27,6 @@ from marimo._dependencies.dependencies import Dependency, DependencyManager from marimo._plugins.ui._impl.chat.chat import ( AI_SDK_VERSION, - require_vercel_ai_sdk_support, ) from marimo._server.ai.config import AnyProviderConfig from marimo._server.ai.constants import ANTHROPIC_DEFAULT_MAX_TOKENS @@ -107,7 +106,6 @@ def __init__( *(deps or []), source="server", ) - require_vercel_ai_sdk_support() self.model: str = model self.config: AnyProviderConfig = config @@ -128,7 +126,7 @@ def create_agent( max_tokens: int | None, tools: list[ToolDefinition], system_prompt: str, - ) -> Agent[None, DeferredToolRequests | str]: + ) -> Agent[object, DeferredToolRequests | str]: """Create a Pydantic AI agent""" from pydantic_ai import Agent @@ -150,11 +148,13 @@ def _build_agent_settings(self, model: Model) -> ModelSettings | None: thinking = self._default_thinking(model) if thinking is None: return None + if not ( - model.profile.supports_thinking - or model.profile.thinking_always_enabled + model.profile.get("supports_thinking", False) + or model.profile.get("thinking_always_enabled", False) ): return None + return ModelSettings(thinking=thinking) def _default_thinking(self, model: Model) -> ThinkingLevel | None: @@ -804,7 +804,7 @@ def create_agent( max_tokens: int | None, tools: list[ToolDefinition], system_prompt: str, - ) -> Agent[None, DeferredToolRequests | str]: + ) -> Agent[object, DeferredToolRequests | str]: """Create a Pydantic AI agent""" from pydantic_ai import Agent, UserError from pydantic_ai.models import infer_model @@ -852,15 +852,6 @@ def _default_thinking(self, model: Model) -> ThinkingLevel | None: class AnthropicProvider(PydanticProvider["PydanticAnthropic"]): - # Temperature of 0.2 was recommended for coding and data science in these links: - # https://community.openai.com/t/cheat-sheet-mastering-temperature-and-top-p-in-chatgpt-api/172683 - # https://docs.anthropic.com/en/docs/test-and-evaluate/strengthen-guardrails/reduce-latency?utm_source=chatgpt.com - DEFAULT_TEMPERATURE: float = 0.2 - - # Extended thinking requires temperature of 1. - # https://docs.anthropic.com/en/docs/build-with-claude/extended-thinking - DEFAULT_EXTENDED_THINKING_TEMPERATURE: float = 1 - @override def create_provider(self, config: AnyProviderConfig) -> PydanticAnthropic: from pydantic_ai.providers.anthropic import ( @@ -875,10 +866,6 @@ def create_model(self, max_tokens: int | None) -> Model: AnthropicModel, AnthropicModelSettings, ) - from pydantic_ai.profiles.anthropic import ( - AnthropicModelProfile, - anthropic_model_profile, - ) settings: AnthropicModelSettings = { "max_tokens": max_tokens @@ -886,22 +873,6 @@ def create_model(self, max_tokens: int | None) -> Model: else ANTHROPIC_DEFAULT_MAX_TOKENS } - # Anthropic extended thinking requires temperature=1; non-thinking - # models keep our default coding temperature. Some adaptive-only - # models (Opus 4.7+) reject sampling settings entirely — skip - # `temperature` for them so pydantic-ai doesn't drop it with a warning. - profile = AnthropicModelProfile.from_profile( - anthropic_model_profile(self.model) - ) - if not getattr( - profile, "anthropic_disallows_sampling_settings", False - ): - settings["temperature"] = ( - self.DEFAULT_EXTENDED_THINKING_TEMPERATURE - if profile.supports_thinking - else self.DEFAULT_TEMPERATURE - ) - return AnthropicModel( model_name=self.model, provider=self.provider, diff --git a/marimo/_server/ai/tools/code_mode.py b/marimo/_server/ai/tools/code_mode.py index 6ddceb670ea..36a3f0c566f 100644 --- a/marimo/_server/ai/tools/code_mode.py +++ b/marimo/_server/ai/tools/code_mode.py @@ -20,7 +20,7 @@ def build_execute_code_toolset( session: Session, request: Request, -) -> FunctionToolset[None]: +) -> FunctionToolset: """Build a `FunctionToolset` exposing one tool: `execute_code`. The tool is bound to the caller's *session* and *request*; the model @@ -31,7 +31,7 @@ def build_execute_code_toolset( from pydantic_ai import FunctionToolset - toolset: FunctionToolset[None] = FunctionToolset() + toolset: FunctionToolset = FunctionToolset() async def execute_code(code: str) -> CodeExecutionResult: """Run Python inside the running notebook's kernel scratchpad. diff --git a/tests/_ai/llm/test_impl.py b/tests/_ai/llm/test_impl.py index 9a84c7233d7..d9d8ed10e7d 100644 --- a/tests/_ai/llm/test_impl.py +++ b/tests/_ai/llm/test_impl.py @@ -1673,8 +1673,8 @@ def test_build_ui_messages_preserves_tool_approval_field(self): async def test_stream_response_emits_tool_approval_request(self): """Tools with `requires_approval=True` should surface an approval-request chunk so the frontend can render an Approve/Deny - card. This is the v6-only behavior unlocked by passing - `sdk_version=AI_SDK_VERSION` to the adapter. + approval. This behavior requires passing `sdk_version=AI_SDK_VERSION` + to the adapter. """ from pydantic_ai import Agent, DeferredToolRequests from pydantic_ai.models.function import ( @@ -1725,8 +1725,7 @@ def delete_file(path: str) -> str: for chunk in chunks if chunk.get("type") == "tool-approval-request" ] - # Older pydantic-ai generates a UUID approvalId; newer versions reuse - # toolCallId. Either is fine — assert the shape, not the exact value. + # pydantic-ai may reuse toolCallId as approvalId; assert the shape. assert len(approval_chunks) == 1 chunk = approval_chunks[0] assert chunk["type"] == "tool-approval-request" @@ -1738,10 +1737,8 @@ def delete_file(path: str) -> str: class MockBaseChunkWithError: """Mock BaseChunk that raises on serialization.""" - def model_dump( - self, mode: str, by_alias: bool, exclude_none: bool - ) -> dict[str, Any]: - del mode, by_alias, exclude_none + def encode(self, *, sdk_version: int) -> str: + del sdk_version raise ValueError("Serialization error") diff --git a/tests/_ai/test_pydantic_utils.py b/tests/_ai/test_pydantic_utils.py index 5b2a0d82a59..886bbfa2f07 100644 --- a/tests/_ai/test_pydantic_utils.py +++ b/tests/_ai/test_pydantic_utils.py @@ -35,20 +35,6 @@ def test_generate_id_with_empty_prefix(self): assert result.startswith("_") -def _has_pydantic_function_like() -> bool: - """Check if pydantic has the _function_like attribute required by pydantic-ai.""" - try: - from pydantic._internal import _decorators - - return hasattr(_decorators, "_function_like") - except ImportError: - return False - - -@pytest.mark.skipif( - not _has_pydantic_function_like(), - reason="pydantic version missing _function_like (required by pydantic-ai)", -) class TestFormToolsets: def test_form_toolsets_empty_list(self): tool_invoker = AsyncMock() diff --git a/tests/_server/ai/test_ai_config.py b/tests/_server/ai/test_ai_config.py index a021fe5e590..b5c89969667 100644 --- a/tests/_server/ai/test_ai_config.py +++ b/tests/_server/ai/test_ai_config.py @@ -443,7 +443,7 @@ def test_for_model_openrouter(self) -> None: config: AiConfig = {"openrouter": {"api_key": "test-openrouter-key"}} provider_config = AnyProviderConfig.for_model( - "openrouter/gpt-4", config + "openrouter/openai/gpt-4", config ) assert provider_config.api_key == "test-openrouter-key" diff --git a/tests/_server/ai/test_providers.py b/tests/_server/ai/test_providers.py index d1030698919..2972e574dc7 100644 --- a/tests/_server/ai/test_providers.py +++ b/tests/_server/ai/test_providers.py @@ -35,7 +35,7 @@ "bedrock", id="bedrock", ), - pytest.param("openrouter/gpt-4", "openrouter", id="openrouter"), + pytest.param("openrouter/openai/gpt-4", "openrouter", id="openrouter"), ], ) def test_anyprovider_for_model(model_name: str, provider_name: str) -> None: @@ -89,7 +89,7 @@ def test_anyprovider_for_model(model_name: str, provider_name: str) -> None: id="bedrock", ), pytest.param( - "openrouter/gpt-4", CustomProvider, None, id="openrouter" + "openrouter/openai/gpt-4", CustomProvider, None, id="openrouter" ), ], ) @@ -279,25 +279,25 @@ def test_openai_default_thinking( ), pytest.param( "claude-opus-4-6", - {"max_tokens": 1024, "temperature": 1}, + {"max_tokens": 1024}, True, id="opus_4_6", ), pytest.param( "claude-sonnet-4-6", - {"max_tokens": 1024, "temperature": 1}, + {"max_tokens": 1024}, True, id="sonnet_4_6", ), pytest.param( "claude-opus-4-5-20251101", - {"max_tokens": 1024, "temperature": 1}, + {"max_tokens": 1024}, True, id="opus_4_5", ), pytest.param( "claude-3-7-sonnet-20250219", - {"max_tokens": 1024, "temperature": 1}, + {"max_tokens": 1024}, True, id="sonnet_3_7", ), @@ -309,7 +309,7 @@ def test_openai_default_thinking( # corrected upstream, behavior here will follow automatically. pytest.param( "claude-3-5-sonnet-20241022", - {"max_tokens": 1024, "temperature": 1}, + {"max_tokens": 1024}, True, id="sonnet_3_5_trusts_profile", ), @@ -325,7 +325,7 @@ def test_anthropic_settings_split( expected_model_settings: dict[str, Any], expected_agent_thinking: bool, ) -> None: - """Verify the model-level settings (temperature) and agent-level thinking flag.""" + """Verify model-level settings and agent-level thinking flag.""" config = AnyProviderConfig(api_key="test-key", base_url=None) provider = AnthropicProvider(model_name, config) model = provider.create_model(max_tokens=1024) @@ -377,8 +377,8 @@ def test_anthropic_thinking_payload_translation( model = provider.create_model(max_tokens=1024) assert isinstance(model, AnthropicModel) - # Combine the model-level settings (temperature) with the agent-level - # thinking flag the way pydantic-ai does at request time. + # Combine model-level settings with the agent-level thinking flag the way + # pydantic-ai does at request time. merged = dict(model.settings or {}) merged.update(provider._build_agent_settings(model) or {}) @@ -514,7 +514,7 @@ def test_custom_provider_agent_passes_explicit_max_tokens() -> None: """The chat path builds the agent (not the model), so the agent's model_settings must carry the explicit max_tokens.""" config = AnyProviderConfig(api_key="test-key", base_url="http://test-url") - provider = get_completion_provider(config, "openrouter/gpt-4") + provider = get_completion_provider(config, "openrouter/openai/gpt-4") with patch("marimo._server.ai.providers.get_tool_manager") as mock_get_tm: mock_get_tm.return_value = MagicMock() agent = provider.create_agent( @@ -527,7 +527,7 @@ def test_custom_provider_agent_passes_explicit_max_tokens() -> None: def test_custom_provider_agent_omits_max_tokens_when_none() -> None: """The chat path omits max_tokens from agent model_settings when unset.""" config = AnyProviderConfig(api_key="test-key", base_url="http://test-url") - provider = get_completion_provider(config, "openrouter/gpt-4") + provider = get_completion_provider(config, "openrouter/openai/gpt-4") with patch("marimo._server.ai.providers.get_tool_manager") as mock_get_tm: mock_get_tm.return_value = MagicMock() agent = provider.create_agent( @@ -618,8 +618,6 @@ def test_custom_provider_inherits_profile_from_base_url() -> None: """A custom provider whose name we don't recognize, but whose base URL points at DeepSeek, inherits DeepSeek's profile so `reasoning_content` round-trips. Regression test for #9786.""" - from pydantic_ai.profiles.openai import OpenAIModelProfile - config = AnyProviderConfig( api_key="test-key", base_url="https://api.deepseek.com" ) @@ -632,17 +630,15 @@ def test_custom_provider_inherits_profile_from_base_url() -> None: assert provider.provider.name == "deepseek" model = provider.create_model(max_tokens=None) - profile = OpenAIModelProfile.from_profile(model.profile) - assert profile.openai_chat_thinking_field == "reasoning_content" - assert profile.openai_chat_send_back_thinking_parts == "field" + profile = model.profile + assert profile.get("openai_chat_thinking_field") == "reasoning_content" + assert profile.get("openai_chat_send_back_thinking_parts") == "field" @pytest.mark.requires("pydantic_ai") def test_custom_provider_unknown_base_url_stays_generic() -> None: """An unknown name with an unrecognized base URL falls back to the generic OpenAI provider (no thinking field), preserving prior behavior.""" - from pydantic_ai.profiles.openai import OpenAIModelProfile - config = AnyProviderConfig( api_key="test-key", base_url="https://my.internal.llm/v1" ) @@ -654,8 +650,8 @@ def test_custom_provider_unknown_base_url_stays_generic() -> None: assert provider.provider.name == "openai" model = provider.create_model(max_tokens=None) - profile = OpenAIModelProfile.from_profile(model.profile) - assert profile.openai_chat_thinking_field is None + profile = model.profile + assert profile.get("openai_chat_thinking_field") is None @pytest.mark.requires("pydantic_ai") diff --git a/tests/snapshots/optional-dependencies-recommended.txt b/tests/snapshots/optional-dependencies-recommended.txt index 88c04fc98e2..415dd40a780 100644 --- a/tests/snapshots/optional-dependencies-recommended.txt +++ b/tests/snapshots/optional-dependencies-recommended.txt @@ -3,4 +3,4 @@ marimo[sandbox] marimo[sql] nbformat>=5.7.0 pydantic-ai-slim[openai]>=1.107.0,<2.0.0 -ruff \ No newline at end of file +ruff From 03b0737b73f3be66bae4fe6e4f9471f05d1eafcb Mon Sep 17 00:00:00 2001 From: Shahmir Varqha Date: Wed, 24 Jun 2026 18:09:30 +0800 Subject: [PATCH 2/7] remove hardcoding version --- marimo/_tracer.py | 4 +--- tests/test_tracer.py | 1 - 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/marimo/_tracer.py b/marimo/_tracer.py index 3168915dcec..7f813be96ac 100644 --- a/marimo/_tracer.py +++ b/marimo/_tracer.py @@ -250,9 +250,7 @@ def _instrument_ai(provider: trace.TracerProvider) -> None: from pydantic_ai import Agent from pydantic_ai.models.instrumented import InstrumentationSettings - Agent.instrument_all( - InstrumentationSettings(tracer_provider=provider, version=5) - ) + Agent.instrument_all(InstrumentationSettings(tracer_provider=provider)) LOGGER.debug("Enabled AI instrumentation") except Exception as e: LOGGER.debug("AI instrumentation failed: %s", e) diff --git a/tests/test_tracer.py b/tests/test_tracer.py index d6af9f799c9..de3ef2ef821 100644 --- a/tests/test_tracer.py +++ b/tests/test_tracer.py @@ -432,7 +432,6 @@ def test_instruments_when_pydantic_ai_installed( mock_instrument.assert_called_once() settings = mock_instrument.call_args.args[0] assert isinstance(settings, InstrumentationSettings) - assert settings.version == 5 # InstrumentationSettings builds its tracer from the provider rather # than storing the provider itself, so assert the provider was used. assert settings.tracer is provider.get_tracer.return_value From adcaa1a7d8419ecc878636b55f2951d40690e38b Mon Sep 17 00:00:00 2001 From: Shahmir Varqha Date: Thu, 25 Jun 2026 00:18:13 +0800 Subject: [PATCH 3/7] exclude pydantic-ai packages --- pyproject.toml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pyproject.toml b/pyproject.toml index c263f466fb2..8f036be4093 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -291,6 +291,8 @@ docs = [ # Mirror CI's dependency window (UV_EXCLUDE_NEWER in .github/workflows) so local # resolution reproduces what CI resolves at setup time, including the ruff version. exclude-newer = "7 days" +# Migrating to pydantic-ai v2, to remove in the future. +exclude-newer-package = { "pydantic-ai-slim" = false, "pydantic-graph" = false } [tool.uv.sources] marimo_docs = { path = "./docs", editable = true } From 0e8ac11b85292b00fd140972006994cc6285a565 Mon Sep 17 00:00:00 2001 From: Shahmir Varqha Date: Thu, 25 Jun 2026 00:28:47 +0800 Subject: [PATCH 4/7] no args is acceptable, it's an overloaded method --- marimo/_server/ai/providers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/marimo/_server/ai/providers.py b/marimo/_server/ai/providers.py index 475ba57b229..ff83c22a3c2 100644 --- a/marimo/_server/ai/providers.py +++ b/marimo/_server/ai/providers.py @@ -333,7 +333,7 @@ def create_provider(self, config: AnyProviderConfig) -> PydanticGoogle: ) else: # Try default initialization which may work with environment variables - provider = PydanticGoogle() + provider = PydanticGoogle() # type: ignore[call-overload] return provider @override From 3e34912d0c967f4ced469d019b51675986398b29 Mon Sep 17 00:00:00 2001 From: Shahmir Varqha Date: Thu, 25 Jun 2026 00:50:30 +0800 Subject: [PATCH 5/7] undo exclude-newer-package --- pyproject.toml | 2 -- 1 file changed, 2 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 8f036be4093..c263f466fb2 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -291,8 +291,6 @@ docs = [ # Mirror CI's dependency window (UV_EXCLUDE_NEWER in .github/workflows) so local # resolution reproduces what CI resolves at setup time, including the ruff version. exclude-newer = "7 days" -# Migrating to pydantic-ai v2, to remove in the future. -exclude-newer-package = { "pydantic-ai-slim" = false, "pydantic-graph" = false } [tool.uv.sources] marimo_docs = { path = "./docs", editable = true } From 2c6089a679fa7e16ea684f46988017af7518b84e Mon Sep 17 00:00:00 2001 From: Shahmir Varqha Date: Fri, 26 Jun 2026 12:36:34 +0800 Subject: [PATCH 6/7] cap it to v3 --- marimo/_server/ai/providers.py | 4 ++-- pyproject.toml | 8 ++++---- tests/snapshots/optional-dependencies-recommended.txt | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/marimo/_server/ai/providers.py b/marimo/_server/ai/providers.py index ff83c22a3c2..9285292be6e 100644 --- a/marimo/_server/ai/providers.py +++ b/marimo/_server/ai/providers.py @@ -126,7 +126,7 @@ def create_agent( max_tokens: int | None, tools: list[ToolDefinition], system_prompt: str, - ) -> Agent[object, DeferredToolRequests | str]: + ) -> Agent[None, DeferredToolRequests | str]: """Create a Pydantic AI agent""" from pydantic_ai import Agent @@ -804,7 +804,7 @@ def create_agent( max_tokens: int | None, tools: list[ToolDefinition], system_prompt: str, - ) -> Agent[object, DeferredToolRequests | str]: + ) -> Agent[None, DeferredToolRequests | str]: """Create a Pydantic AI agent""" from pydantic_ai import Agent, UserError from pydantic_ai.models import infer_model diff --git a/pyproject.toml b/pyproject.toml index c263f466fb2..ccad38ca79a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -103,7 +103,7 @@ recommended = [ "marimo[sql]", "marimo[sandbox]", # For `marimo edit --sandbox DIRECTORY` "altair>=5.4.0", # Plotting in datasource viewer - "pydantic-ai-slim[openai]>=1.107.0,<2.0.0", # AI features + "pydantic-ai-slim[openai]>=1.107.0,<3.0.0", # AI features "ruff", # Formatting "nbformat>=5.7.0", # Export as IPYNB ] @@ -142,7 +142,7 @@ dev = [ # For linting "ruff>=0.15.16", # For AI - "pydantic-ai-slim[openai]>=1.107.0,<2.0.0", + "pydantic-ai-slim[openai]>=1.107.0,<3.0.0", ] test = [ @@ -205,7 +205,7 @@ test-optional = [ "anywidget~=0.9.21", "ipython~=8.12.3", # testing gen ai - "pydantic-ai-slim[google,anthropic,bedrock,openai]>=1.107.0,<2.0.0", + "pydantic-ai-slim[google,anthropic,bedrock,openai]>=1.107.0,<3.0.0", # - google-auth uses cachetools, and cachetools<5.0.0 uses collections.MutableMapping (removed in Python 3.10) "cachetools>=5.0.0", "boto3>=1.38.46", @@ -242,7 +242,7 @@ typecheck = [ "sqlalchemy>=2.0.40", "obstore>=0.8.2", "fsspec>=2026.2.0", - "pydantic-ai-slim[google,anthropic,bedrock,openai]>=1.107.0,<2.0.0", + "pydantic-ai-slim[google,anthropic,bedrock,openai]>=1.107.0,<3.0.0", "loro>=1.5.0", "boto3-stubs>=1.38.46", "pandas-stubs>=1.5.3.230321", diff --git a/tests/snapshots/optional-dependencies-recommended.txt b/tests/snapshots/optional-dependencies-recommended.txt index 415dd40a780..075f8cc0c16 100644 --- a/tests/snapshots/optional-dependencies-recommended.txt +++ b/tests/snapshots/optional-dependencies-recommended.txt @@ -2,5 +2,5 @@ altair>=5.4.0 marimo[sandbox] marimo[sql] nbformat>=5.7.0 -pydantic-ai-slim[openai]>=1.107.0,<2.0.0 +pydantic-ai-slim[openai]>=1.107.0,<3.0.0 ruff From 03a965ae31c6c860d29a476419a5cdd766dc5d65 Mon Sep 17 00:00:00 2001 From: Shahmir Varqha Date: Fri, 26 Jun 2026 12:43:30 +0800 Subject: [PATCH 7/7] fix test --- tests/snapshots/optional-dependencies-recommended.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/snapshots/optional-dependencies-recommended.txt b/tests/snapshots/optional-dependencies-recommended.txt index 075f8cc0c16..3c6255ab215 100644 --- a/tests/snapshots/optional-dependencies-recommended.txt +++ b/tests/snapshots/optional-dependencies-recommended.txt @@ -3,4 +3,4 @@ marimo[sandbox] marimo[sql] nbformat>=5.7.0 pydantic-ai-slim[openai]>=1.107.0,<3.0.0 -ruff +ruff \ No newline at end of file