From 879c8e0db62d553042058d94b4edaf3c5c3d65ed Mon Sep 17 00:00:00 2001 From: Andrey Buzin Date: Thu, 19 Feb 2026 18:54:48 -0800 Subject: [PATCH 1/2] Fix lint errors across the entire project --- src/vercel_ai_sdk/__init__.py | 26 ++++++++-------- src/vercel_ai_sdk/agent/__init__.py | 2 +- src/vercel_ai_sdk/agent/local/fs.py | 4 +-- src/vercel_ai_sdk/agent/tools.py | 31 ++++++++++++++++---- src/vercel_ai_sdk/agent/vercel/__init__.py | 2 +- src/vercel_ai_sdk/agent/vercel/filesystem.py | 29 ++++++++---------- src/vercel_ai_sdk/ai_sdk_ui/__init__.py | 4 +-- src/vercel_ai_sdk/ai_sdk_ui/protocol.py | 1 - src/vercel_ai_sdk/ai_sdk_ui/ui_message.py | 5 +++- src/vercel_ai_sdk/anthropic/__init__.py | 4 +-- src/vercel_ai_sdk/core/__init__.py | 2 +- src/vercel_ai_sdk/core/llm.py | 2 +- src/vercel_ai_sdk/openai/__init__.py | 21 ++++++++----- tests/ai_sdk_ui/test_adapter.py | 16 ++++++---- tests/conftest.py | 2 +- tests/core/test_hooks.py | 5 ++-- tests/core/test_llm.py | 3 -- tests/core/test_messages.py | 2 -- tests/core/test_runtime.py | 6 ++-- tests/core/test_streams.py | 4 --- tests/core/test_tools.py | 7 ++--- tests/mcp/test_client.py | 3 +- 22 files changed, 97 insertions(+), 84 deletions(-) diff --git a/src/vercel_ai_sdk/__init__.py b/src/vercel_ai_sdk/__init__.py index 7dff172e..6d6cbe3a 100644 --- a/src/vercel_ai_sdk/__init__.py +++ b/src/vercel_ai_sdk/__init__.py @@ -1,32 +1,32 @@ -from . import anthropic, mcp, openai, ai_sdk_ui +from . import ai_sdk_ui, anthropic, mcp, openai +from .core.checkpoint import Checkpoint +from .core.hooks import Hook, hook +from .core.llm import LanguageModel # Re-export core types from .core.messages import ( + HookPart, Message, Part, PartState, + ReasoningPart, TextPart, - ToolPart, ToolDelta, - ReasoningPart, - HookPart, + ToolPart, make_messages, ) -from .core.tools import ToolLike, ToolSchema, Tool, tool -from .core.llm import LanguageModel -from .core.streams import StreamResult, stream from .core.runtime import ( - Runtime, - RunResult, HookInfo, - stream_step, - stream_loop, + RunResult, + Runtime, execute_tool, get_checkpoint, run, + stream_loop, + stream_step, ) -from .core.hooks import Hook, hook -from .core.checkpoint import Checkpoint +from .core.streams import StreamResult, stream +from .core.tools import Tool, ToolLike, ToolSchema, tool __all__ = [ # Types diff --git a/src/vercel_ai_sdk/agent/__init__.py b/src/vercel_ai_sdk/agent/__init__.py index 697b0384..4b349261 100644 --- a/src/vercel_ai_sdk/agent/__init__.py +++ b/src/vercel_ai_sdk/agent/__init__.py @@ -1,4 +1,4 @@ -from . import proto, tools, local, vercel +from . import local, proto, tools, vercel from .agent import Agent, ToolApproval __all__ = ["Agent", "ToolApproval", "proto", "tools", "local", "vercel"] diff --git a/src/vercel_ai_sdk/agent/local/fs.py b/src/vercel_ai_sdk/agent/local/fs.py index fc2edc60..bb546f72 100644 --- a/src/vercel_ai_sdk/agent/local/fs.py +++ b/src/vercel_ai_sdk/agent/local/fs.py @@ -152,13 +152,13 @@ async def bash(self, command: str, *, timeout: int | None = None) -> str: ) try: stdout, _ = await asyncio.wait_for(proc.communicate(), timeout=timeout) - except asyncio.TimeoutError: + except TimeoutError as exc: proc.kill() await proc.communicate() raise TimeoutError( f"Command timed out after {timeout}s. " "Try increasing the timeout or breaking the command into smaller steps." - ) + ) from exc output = stdout.decode() if stdout else "" if proc.returncode != 0: diff --git a/src/vercel_ai_sdk/agent/tools.py b/src/vercel_ai_sdk/agent/tools.py index 300e0dcf..0ad14574 100644 --- a/src/vercel_ai_sdk/agent/tools.py +++ b/src/vercel_ai_sdk/agent/tools.py @@ -4,6 +4,7 @@ from typing import Any import vercel_ai_sdk as ai + from . import proto _filesystem: contextvars.ContextVar[proto.Filesystem] = contextvars.ContextVar( @@ -20,20 +21,29 @@ def _fs() -> proto.Filesystem: @ai.tool async def read(path: str, offset: int | None = None, limit: int | None = None) -> str: - """Read a file and return its contents with line numbers. For large files, use offset/limit to paginate.""" + """Read file contents with line numbers. + + For large files, use offset/limit to paginate. + """ return await _fs().read(path, offset=offset, limit=limit) @ai.tool async def write(path: str, content: str) -> str: - """Write content to a file. Creates parent directories automatically. Overwrites existing files.""" + """Write content to a file. + + Creates parent directories automatically. Overwrites existing files. + """ await _fs().write(path, content) return f"Wrote {len(content)} bytes to {path}" @ai.tool async def edit(path: str, old_string: str, new_string: str) -> str: - """Edit a file by replacing an exact string match. Fails if old_string is not found or appears multiple times.""" + """Edit a file by replacing an exact string match. + + Fails if old_string is not found or appears multiple times. + """ await _fs().edit(path, old_string, new_string) return f"Edited {path}" @@ -45,7 +55,10 @@ async def ls( pattern: str | None = None, include_hidden: bool = False, ) -> str: - """List directory contents recursively. Control depth to balance detail vs overview.""" + """List directory contents recursively. + + Control depth to balance detail vs overview. + """ return await _fs().ls( path, depth=depth, pattern=pattern, include_hidden=include_hidden ) @@ -69,7 +82,10 @@ async def grep( max_count: int | None = None, case_sensitive: bool = True, ) -> str: - """Search file contents using regex (ripgrep syntax). Use include to filter by file pattern (e.g. '*.py').""" + """Search file contents using regex (ripgrep syntax). + + Use include to filter by file pattern (e.g. '*.py'). + """ return await _fs().grep( pattern, path=path, @@ -82,7 +98,10 @@ async def grep( @ai.tool async def bash(command: str, timeout: int | None = None) -> str: - """Execute a bash command in the workspace. Use timeout (seconds) to limit long-running commands.""" + """Execute a bash command in the workspace. + + Use timeout (seconds) to limit long-running commands. + """ return await _fs().bash(command, timeout=timeout) diff --git a/src/vercel_ai_sdk/agent/vercel/__init__.py b/src/vercel_ai_sdk/agent/vercel/__init__.py index 23f9fd19..24460317 100644 --- a/src/vercel_ai_sdk/agent/vercel/__init__.py +++ b/src/vercel_ai_sdk/agent/vercel/__init__.py @@ -1,3 +1,3 @@ -from .filesystem import VercelSandbox, SandboxError, SandboxGoneError +from .filesystem import SandboxError, SandboxGoneError, VercelSandbox __all__ = ["VercelSandbox", "SandboxError", "SandboxGoneError"] diff --git a/src/vercel_ai_sdk/agent/vercel/filesystem.py b/src/vercel_ai_sdk/agent/vercel/filesystem.py index 703c1258..4f7e625f 100644 --- a/src/vercel_ai_sdk/agent/vercel/filesystem.py +++ b/src/vercel_ai_sdk/agent/vercel/filesystem.py @@ -1,18 +1,15 @@ from __future__ import annotations import asyncio +import contextlib import logging -import time from dataclasses import dataclass, field from typing import Any import httpx - from vercel.sandbox import AsyncSandbox from vercel.sandbox.models import WriteFile -from .. import proto - logger = logging.getLogger(__name__) HOME_DIR = "/home/vercel-sandbox" @@ -34,17 +31,16 @@ def _is_gone_error(exc: BaseException) -> bool: Detect stale/terminated sandbox errors. Mirrors isSandboxGoneError from agent-sdk. """ - if isinstance(exc, httpx.HTTPStatusError): - if exc.response.status_code in (410, 422): - return True + if isinstance(exc, httpx.HTTPStatusError) and exc.response.status_code in ( + 410, + 422, + ): + return True msg = str(exc) if "Expected a stream of command data" in msg: return True - if "Expected a stream of logs" in msg: - return True - - return False + return "Expected a stream of logs" in msg @dataclass @@ -151,10 +147,8 @@ async def stop(self) -> None: """Stop the sandbox VM.""" if self._sandbox is None: return - try: + with contextlib.suppress(Exception): await self._sandbox.stop() - except Exception: - pass self._sandbox = None self._sandbox_task = None @@ -233,11 +227,11 @@ async def _do_run_command( sb.run_command(cmd, args or [], cwd=effective_cwd), timeout=timeout, ) - except asyncio.TimeoutError: + except TimeoutError as exc: raise TimeoutError( f"Command timed out after {timeout}s. " "Try increasing the timeout or breaking the command into smaller steps." - ) + ) from exc except Exception as exc: if _is_gone_error(exc): raise SandboxGoneError(str(exc)) from exc @@ -338,7 +332,8 @@ async def ls( async def glob(self, pattern: str, *, path: str | None = None) -> list[str]: root = self._resolve_path(path or ".") - cmd = f"cd {_shell_quote(root)} && find . -path {_shell_quote(f'./{pattern}')} 2>/dev/null | sort" + find_expr = _shell_quote(f"./{pattern}") + cmd = f"cd {_shell_quote(root)} && find . -path {find_expr} 2>/dev/null | sort" stdout, _, _ = await self._run_command("bash", ["-c", cmd]) if not stdout.strip(): return [] diff --git a/src/vercel_ai_sdk/ai_sdk_ui/__init__.py b/src/vercel_ai_sdk/ai_sdk_ui/__init__.py index 736ff133..4b8bf175 100644 --- a/src/vercel_ai_sdk/ai_sdk_ui/__init__.py +++ b/src/vercel_ai_sdk/ai_sdk_ui/__init__.py @@ -1,6 +1,6 @@ -from .adapter import to_ui_message_stream, filter_by_label, to_sse_stream, to_messages -from .ui_message import UIMessage +from .adapter import filter_by_label, to_messages, to_sse_stream, to_ui_message_stream from .protocol import UI_MESSAGE_STREAM_HEADERS +from .ui_message import UIMessage __all__ = [ "to_ui_message_stream", diff --git a/src/vercel_ai_sdk/ai_sdk_ui/protocol.py b/src/vercel_ai_sdk/ai_sdk_ui/protocol.py index e7ff7af7..32a49266 100644 --- a/src/vercel_ai_sdk/ai_sdk_ui/protocol.py +++ b/src/vercel_ai_sdk/ai_sdk_ui/protocol.py @@ -3,7 +3,6 @@ import dataclasses from typing import Any, Literal - # necessary headers for the streaming integration to work UI_MESSAGE_STREAM_HEADERS = { "Content-Type": "text/event-stream", diff --git a/src/vercel_ai_sdk/ai_sdk_ui/ui_message.py b/src/vercel_ai_sdk/ai_sdk_ui/ui_message.py index a920b0f4..cb87d9d2 100644 --- a/src/vercel_ai_sdk/ai_sdk_ui/ui_message.py +++ b/src/vercel_ai_sdk/ai_sdk_ui/ui_message.py @@ -100,7 +100,10 @@ class UIToolPart(pydantic.BaseModel): @property def tool_name(self) -> str: - """Extract tool name from the type string (e.g., 'tool-get_weather' -> 'get_weather').""" + """Extract tool name from the type string. + + E.g., 'tool-get_weather' -> 'get_weather'. + """ if self.type.startswith("tool-"): return self.type[5:] return self.type diff --git a/src/vercel_ai_sdk/anthropic/__init__.py b/src/vercel_ai_sdk/anthropic/__init__.py index 714228c1..27ec7c77 100644 --- a/src/vercel_ai_sdk/anthropic/__init__.py +++ b/src/vercel_ai_sdk/anthropic/__init__.py @@ -119,7 +119,7 @@ async def stream_events( self, messages: list[core.messages.Message], tools: Sequence[core.tools.ToolLike] | None = None, - ) -> AsyncGenerator[core.llm.StreamEvent, None]: + ) -> AsyncGenerator[core.llm.StreamEvent]: """Yield raw stream events from Anthropic API.""" system_prompt, anthropic_messages = _messages_to_anthropic(messages) anthropic_tools = _tools_to_anthropic(tools) if tools else None @@ -208,7 +208,7 @@ async def stream( self, messages: list[core.messages.Message], tools: Sequence[core.tools.ToolLike] | None = None, - ) -> AsyncGenerator[core.messages.Message, None]: + ) -> AsyncGenerator[core.messages.Message]: """Stream Messages (uses StreamProcessor internally).""" handler = core.llm.StreamHandler() async for event in self.stream_events(messages, tools): diff --git a/src/vercel_ai_sdk/core/__init__.py b/src/vercel_ai_sdk/core/__init__.py index 7683e461..05ac1604 100644 --- a/src/vercel_ai_sdk/core/__init__.py +++ b/src/vercel_ai_sdk/core/__init__.py @@ -1,3 +1,3 @@ -from . import messages, tools, runtime, hooks, llm +from . import hooks, llm, messages, runtime, tools __all__ = ["messages", "tools", "runtime", "hooks", "llm"] diff --git a/src/vercel_ai_sdk/core/llm.py b/src/vercel_ai_sdk/core/llm.py index ee8bcaa2..c1f1f916 100644 --- a/src/vercel_ai_sdk/core/llm.py +++ b/src/vercel_ai_sdk/core/llm.py @@ -216,7 +216,7 @@ async def stream( self, messages: list[messages_.Message], tools: Sequence[tools_.ToolLike] | None = None, - ) -> AsyncGenerator[messages_.Message, None]: + ) -> AsyncGenerator[messages_.Message]: raise NotImplementedError yield diff --git a/src/vercel_ai_sdk/openai/__init__.py b/src/vercel_ai_sdk/openai/__init__.py index 6e095661..2e8d2124 100644 --- a/src/vercel_ai_sdk/openai/__init__.py +++ b/src/vercel_ai_sdk/openai/__init__.py @@ -62,7 +62,8 @@ def _messages_to_openai(messages: list[core.messages.Message]) -> list[dict[str, }, } ) - # If tool has completed (success or error), collect for tool messages + # If tool has completed (success or error), + # collect for tool messages if part.status in ("result", "error") and part.result is not None: tool_results.append( { @@ -114,13 +115,17 @@ def __init__( """Initialize OpenAI model adapter. Args: - model: Model identifier (e.g., 'openai/gpt-5.2', 'anthropic/claude-sonnet-4.5') - base_url: API base URL (e.g., 'https://ai-gateway.vercel.sh/v1') + model: Model identifier + (e.g., 'openai/gpt-5.2', 'anthropic/claude-sonnet-4.5') + base_url: API base URL + (e.g., 'https://ai-gateway.vercel.sh/v1') api_key: API key for authentication thinking: Enable reasoning/thinking output - budget_tokens: Max tokens for reasoning (mutually exclusive with reasoning_effort) - reasoning_effort: Effort level - 'none', 'minimal', 'low', 'medium', 'high', 'xhigh' - (mutually exclusive with budget_tokens) + budget_tokens: Max tokens for reasoning + (mutually exclusive with reasoning_effort) + reasoning_effort: Effort level — 'none', 'minimal', + 'low', 'medium', 'high', 'xhigh' + (mutually exclusive with budget_tokens) """ self._model = model self._thinking = thinking @@ -133,7 +138,7 @@ async def stream_events( self, messages: list[core.messages.Message], tools: Sequence[core.tools.ToolLike] | None = None, - ) -> AsyncGenerator[core.llm.StreamEvent, None]: + ) -> AsyncGenerator[core.llm.StreamEvent]: """Yield raw stream events from OpenAI API.""" openai_messages = _messages_to_openai(messages) openai_tools = _tools_to_openai(tools) if tools else None @@ -244,7 +249,7 @@ async def stream( self, messages: list[core.messages.Message], tools: Sequence[core.tools.ToolLike] | None = None, - ) -> AsyncGenerator[core.messages.Message, None]: + ) -> AsyncGenerator[core.messages.Message]: """Stream Messages (uses StreamHandler internally).""" handler = core.llm.StreamHandler() async for event in self.stream_events(messages, tools): diff --git a/tests/ai_sdk_ui/test_adapter.py b/tests/ai_sdk_ui/test_adapter.py index 4770dabe..94de6bf7 100644 --- a/tests/ai_sdk_ui/test_adapter.py +++ b/tests/ai_sdk_ui/test_adapter.py @@ -16,7 +16,7 @@ async def get_event_types(msgs: list[messages.Message]) -> list[str]: """Stream messages through adapter and return event type sequence.""" - async def stream() -> AsyncGenerator[messages.Message, None]: + async def stream() -> AsyncGenerator[messages.Message]: for m in msgs: yield m @@ -205,7 +205,8 @@ async def test_text_then_tool_then_text() -> None: # Per AI SDK protocol, tool-input-available and tool-output-available # are in the SAME step (one LLM turn). Reference: - # process-ui-message-stream.test.ts "server-side tool roundtrip with multiple assistant texts" + # process-ui-message-stream.test.ts + # "server-side tool roundtrip with multiple assistant texts" assert await get_event_types(msgs) == [ "start", "start-step", @@ -305,8 +306,10 @@ async def test_runtime_tool_roundtrip() -> None: ] # This is what SHOULD happen: - # 1. First step yields tool call with status="pending" -> tool-input-start, tool-input-available - # 2. After tool execution, we yield the same message with status="result" -> tool-output-available + # 1. First step yields tool call with status="pending" + # -> tool-input-start, tool-input-available + # 2. After tool execution, we yield the same message with + # status="result" -> tool-output-available # (same step because same message ID) # 3. Second LLM step yields final text -> text-start, text-end expected = [ @@ -329,7 +332,7 @@ async def test_runtime_tool_roundtrip() -> None: async def _async_iter( items: list[messages.Message], -) -> AsyncGenerator[messages.Message, None]: +) -> AsyncGenerator[messages.Message]: """Helper to convert a list to an async generator.""" for item in items: yield item @@ -362,7 +365,8 @@ def test_ui_to_internal_two_turn_with_tool() -> None: {"type": "step-start"}, { "type": "text", - "text": "I'll check with the mothership about this important question.", + "text": "I'll check with the mothership " + "about this important question.", "state": "done", }, { diff --git a/tests/conftest.py b/tests/conftest.py index 14e2a025..636136b0 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -18,7 +18,7 @@ async def stream( self, messages: list[messages.Message], tools: Sequence[ai.ToolLike] | None = None, - ) -> AsyncGenerator[messages.Message, None]: + ) -> AsyncGenerator[messages.Message]: if self._call_index >= len(self._responses): raise RuntimeError("MockLLM: no more responses configured") self.call_count += 1 diff --git a/tests/core/test_hooks.py b/tests/core/test_hooks.py index 06c6bd40..87966a92 100644 --- a/tests/core/test_hooks.py +++ b/tests/core/test_hooks.py @@ -7,7 +7,6 @@ import pytest import vercel_ai_sdk as ai -from vercel_ai_sdk.core.hooks import _live_hooks, _pending_resolutions from ..conftest import MockLLM, text_msg @@ -106,7 +105,7 @@ async def graph(llm: ai.LanguageModel) -> Any: llm = MockLLM([[text_msg("OK")]]) run_result = ai.run(graph, llm) - msgs = [m async for m in run_result] + [m async for m in run_result] # Should have completed with no pending hooks assert len(run_result.pending_hooks) == 0 @@ -163,7 +162,7 @@ async def graph(llm: ai.LanguageModel) -> None: await Confirmation.create("meta_test", metadata={"tool": "rm -rf", "path": "/"}) # type: ignore[attr-defined] run_result = ai.run(graph, MockLLM([[text_msg("OK")]]), cancel_on_hooks=True) - msgs = [m async for m in run_result] + [m async for m in run_result] info = run_result.pending_hooks["meta_test"] assert info.metadata == {"tool": "rm -rf", "path": "/"} diff --git a/tests/core/test_llm.py b/tests/core/test_llm.py index 3bae1c81..30baff5c 100644 --- a/tests/core/test_llm.py +++ b/tests/core/test_llm.py @@ -1,7 +1,5 @@ """StreamHandler: event accumulation, state transitions, message building.""" -import pytest - from vercel_ai_sdk.core.llm import ( MessageDone, ReasoningDelta, @@ -17,7 +15,6 @@ ) from vercel_ai_sdk.core.messages import ReasoningPart, TextPart, ToolPart - # -- Text streaming -------------------------------------------------------- diff --git a/tests/core/test_messages.py b/tests/core/test_messages.py index 7d4a991a..c2b9b339 100644 --- a/tests/core/test_messages.py +++ b/tests/core/test_messages.py @@ -5,12 +5,10 @@ Message, ReasoningPart, TextPart, - ToolDelta, ToolPart, make_messages, ) - # -- is_done --------------------------------------------------------------- diff --git a/tests/core/test_runtime.py b/tests/core/test_runtime.py index b5bc2905..03dd786d 100644 --- a/tests/core/test_runtime.py +++ b/tests/core/test_runtime.py @@ -10,7 +10,6 @@ from ..conftest import MockLLM, text_msg, tool_msg - # -- Tool definitions for tests -------------------------------------------- @@ -156,7 +155,10 @@ async def graph(llm: ai.LanguageModel) -> ai.StreamResult: @pytest.mark.asyncio async def test_execute_tool_missing_raises() -> None: - """execute_tool with unknown tool name raises ValueError (wrapped in ExceptionGroup by TaskGroup).""" + """execute_tool with unknown tool name raises ValueError. + + Wrapped in ExceptionGroup by TaskGroup. + """ tc = messages.ToolPart( tool_call_id="tc-1", tool_name="nonexistent_tool_zzz", tool_args="{}" ) diff --git a/tests/core/test_streams.py b/tests/core/test_streams.py index 31803244..d4463a98 100644 --- a/tests/core/test_streams.py +++ b/tests/core/test_streams.py @@ -1,8 +1,5 @@ """@stream decorator: context requirement, replay, queue submission.""" -import asyncio -from collections.abc import AsyncGenerator - import pytest import vercel_ai_sdk as ai @@ -11,7 +8,6 @@ from ..conftest import MockLLM, text_msg - # -- StreamResult properties ----------------------------------------------- diff --git a/tests/core/test_tools.py b/tests/core/test_tools.py index 563fa5f0..480339d5 100644 --- a/tests/core/test_tools.py +++ b/tests/core/test_tools.py @@ -1,13 +1,10 @@ """@tool decorator: schema extraction, registry, Runtime parameter handling.""" -from typing import Optional - import pytest import vercel_ai_sdk as ai from vercel_ai_sdk.core.runtime import Runtime -from vercel_ai_sdk.core.tools import _tool_registry, get_tool - +from vercel_ai_sdk.core.tools import get_tool # -- Schema extraction from type hints ------------------------------------ @@ -28,7 +25,7 @@ async def greet(name: str, count: int) -> str: def test_optional_param_not_required() -> None: @ai.tool - async def search(query: str, limit: Optional[int] = None) -> str: + async def search(query: str, limit: int | None = None) -> str: """Search.""" return query diff --git a/tests/mcp/test_client.py b/tests/mcp/test_client.py index 3cf88123..1e63dd30 100644 --- a/tests/mcp/test_client.py +++ b/tests/mcp/test_client.py @@ -1,6 +1,5 @@ """MCP client: tool registration in global registry, end-to-end execution.""" -import asyncio import contextlib from typing import Any @@ -70,7 +69,7 @@ def test_mcp_tool_to_native_schema_preserved() -> None: @pytest.mark.asyncio async def test_mcp_tool_executes_through_stream_loop() -> None: - """An MCP-style tool registered via _mcp_tool_to_native can be called by the agent loop.""" + """MCP-style tool via _mcp_tool_to_native can be called by the agent loop.""" call_log: list[dict[str, str]] = [] async def fake_fn(**kwargs: str) -> str: From b56b180d630deb47549bbc59a4f331f8454023d7 Mon Sep 17 00:00:00 2001 From: Andrey Buzin Date: Thu, 19 Feb 2026 18:55:11 -0800 Subject: [PATCH 2/2] Update the CI to enforce lint, format, and typecheck --- .github/workflows/ci.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 332cf8d6..f05ecb49 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -18,4 +18,8 @@ jobs: - run: uv sync + - run: uv run ruff format --check src tests + - run: uv run ruff check src tests + - run: uv run mypy src tests + - run: uv run pytest