diff --git a/README.md b/README.md index 1b1f5e6..2c8031c 100644 --- a/README.md +++ b/README.md @@ -162,6 +162,44 @@ Options: Your choice: ``` +### Skills + +Deep Code Agent can load DeepAgents-native skills from a local skills directory when using the filesystem backend. +By default, it looks for project skills at: + +```plaintext +/.agents/skills +``` + +Each skill is a directory with a `SKILL.md` file: + +```plaintext +.agents/skills/ +└── my-skill/ + └── SKILL.md +``` + +`SKILL.md` must include YAML frontmatter with a skill name and description: + +```markdown +--- +name: my-skill +description: Specialized workflow for a project task +--- + +# My Skill + +Use these instructions when the task matches this workflow. +``` + +To load explicit skill directories, pass `--skills-dir`. This option can be provided multiple times, and explicit directories replace the default auto-discovery path: + +```bash +uv run deep-code-agent --backend-type filesystem --skills-dir .agents/skills +``` + +Skills currently require `--backend-type filesystem`; the state backend does not load local skill directories. + ### Working with Subagents The Deep Code Agent includes specialized subagents that can be used independently or as part of the main workflow: diff --git a/pyproject.toml b/pyproject.toml index 354e3df..c5b8512 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -6,7 +6,7 @@ readme = "README.md" requires-python = ">=3.13,<4.0" license-files = ["LICENSE"] dependencies = [ - "deepagents>=0.2.8", + "deepagents>=0.6.7", "langchain>=1.1.0", "langchain-openai>=1.1.0", "langgraph>=1.0.4", diff --git a/src/deep_code_agent/cli.py b/src/deep_code_agent/cli.py index 79742e2..1b3ea3b 100644 --- a/src/deep_code_agent/cli.py +++ b/src/deep_code_agent/cli.py @@ -1,6 +1,7 @@ """Command-line interface for Deep Code Agent.""" import argparse +from pathlib import Path from typing import TYPE_CHECKING, Any from deep_code_agent import __version__ @@ -20,6 +21,18 @@ def _format_args(args: dict[str, Any], max_length: int = 200) -> str: return "\n".join(formatted) +def _resolve_skills(args, codebase_dir: str) -> list[str] | None: + """Resolve skill directories from CLI args or the project default.""" + if args.skills_dir: + return [Path(skill_dir).expanduser().absolute().as_posix() for skill_dir in args.skills_dir] + + default_skills = Path(codebase_dir) / ".agents" / "skills" + if default_skills.exists(): + return [default_skills.absolute().as_posix()] + + return None + + def _initialize_agent(args, codebase_dir: str) -> Any: """Initialize model and agent for both CLI and TUI modes. @@ -36,6 +49,7 @@ def _initialize_agent(args, codebase_dir: str) -> Any: from deep_code_agent.models.llms.langchain_chat import create_chat_model load_dotenv() + skills = _resolve_skills(args, codebase_dir) model = None if any([args.model_name, args.api_key, args.base_url]) or args.model_provider != "openai": @@ -51,6 +65,7 @@ def _initialize_agent(args, codebase_dir: str) -> Any: model=model, checkpointer=InMemorySaver(), backend_type=args.backend_type, + skills=skills, ) @@ -194,6 +209,12 @@ def main() -> None: parser.add_argument("--base-url", default=None, help="Base URL for model service") parser.add_argument("--thread-id", default="1", help="Thread ID for session") parser.add_argument("--tui", action="store_true", help="Use TUI mode (experimental)") + parser.add_argument( + "--skills-dir", + action="append", + default=None, + help="Skill directory source. Can be provided multiple times. Defaults to /.agents/skills if it exists.", + ) args = parser.parse_args() @@ -283,6 +304,7 @@ def agent_factory(): "model": args.model_name or "default", "session_id": args.thread_id, "codebase_dir": codebase_dir, + "skills": _resolve_skills(args, codebase_dir) or [], } app = DeepCodeAgentApp( diff --git a/src/deep_code_agent/code_agent.py b/src/deep_code_agent/code_agent.py index 9c9525c..060d8aa 100644 --- a/src/deep_code_agent/code_agent.py +++ b/src/deep_code_agent/code_agent.py @@ -27,6 +27,7 @@ def create_code_agent( checkpointer: Any = None, backend_type: str = "state", interrupt_on: dict[str, bool | Any] | None = DEFAULT_INTERRUPT_ON, + skills: list[str] | None = None, ): """ Create a DeepAgents-based Code Agent for software development tasks. @@ -44,6 +45,8 @@ def create_code_agent( backend_type: Backend type to use. Must be either "state" or "filesystem". interrupt_on: Configuration for human-in-the-loop approval. Maps tool names to approval settings. Set to None to disable all approvals. + skills: Optional list of local skill directory sources. Skills are only + supported with the filesystem backend in this project. Returns: A fully configured Code Agent instance ready to handle software development tasks. @@ -53,6 +56,9 @@ def create_code_agent( ValueError: If an unsupported backend type is provided. """ path = Path(codebase_dir) + if backend_type == "state" and skills: + raise ValueError("Skills require filesystem backend; use backend_type='filesystem'.") + if backend_type == "filesystem" and not path.exists(): path.mkdir(parents=True, exist_ok=True) @@ -64,7 +70,7 @@ def create_code_agent( if backend_type == "filesystem": from deepagents.backends.filesystem import FilesystemBackend - backend = FilesystemBackend(root_dir=codebase_dir) + backend = FilesystemBackend(root_dir=codebase_dir, virtual_mode=False) tools = [make_terminal_tool(codebase_dir)] elif backend_type == "state": from deepagents.backends.state import StateBackend @@ -85,6 +91,7 @@ def backend(rt): checkpointer=checkpointer, backend=backend, interrupt_on=interrupt_on, + skills=skills, ) except Exception as exc: raise RuntimeError(f"Error creating DeepAgent: {exc}") from exc diff --git a/src/deep_code_agent/tui/bridge/agent_bridge.py b/src/deep_code_agent/tui/bridge/agent_bridge.py index 8627cfe..7e85ee2 100644 --- a/src/deep_code_agent/tui/bridge/agent_bridge.py +++ b/src/deep_code_agent/tui/bridge/agent_bridge.py @@ -55,6 +55,11 @@ def _reset_streaming_state(self) -> None: self._pending_tool_widgets.clear() self._tool_widgets_by_id.clear() + def _finish_streaming_message_segment(self) -> None: + """Detach the current assistant bubble so later chunks start a new one.""" + self._streaming_chunks.clear() + self._streaming_bubble = None + def _extract_tool_name_from_interrupt(self, interrupt_data: dict) -> str: """Extract tool name from interrupt data. @@ -346,6 +351,7 @@ def handle_event() -> None: input_box.focus_input() elif event.type == EventType.TOOL_CALL: + self._finish_streaming_message_segment() tool_data = event.data or {} tool_name = ( tool_data.get("name", "unknown") @@ -414,6 +420,7 @@ def handle_event() -> None: self._active_tool_widget.update_status("running") elif event.type == EventType.TOOL_SUCCESS: + self._finish_streaming_message_segment() result = event.data or "" meta = event.metadata or {} tool_call_id = meta.get("tool_call_id") if isinstance(meta, dict) else None @@ -449,6 +456,7 @@ def handle_event() -> None: self._active_tool_widget = None elif event.type == EventType.TOOL_ERROR: + self._finish_streaming_message_segment() error = event.data or "Unknown error" meta = event.metadata or {} tool_call_id = meta.get("tool_call_id") if isinstance(meta, dict) else None diff --git a/tests/test_cli.py b/tests/test_cli.py index ee6cc4f..24e1df3 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -6,7 +6,7 @@ import pytest from deep_code_agent import __version__ -from deep_code_agent.cli import _initialize_agent, _run_tui_mode, main +from deep_code_agent.cli import _initialize_agent, _resolve_skills, _run_tui_mode, main def test_main_prints_version_and_exits(capsys): @@ -37,6 +37,7 @@ def test_initialize_agent_skips_model_creation_for_default_provider( api_key=None, base_url=None, backend_type="state", + skills_dir=None, ) _initialize_agent(args, "/tmp/project") @@ -46,6 +47,7 @@ def test_initialize_agent_skips_model_creation_for_default_provider( assert mock_create_code_agent.call_args.kwargs["model"] is None assert mock_create_code_agent.call_args.kwargs["backend_type"] == "state" assert mock_create_code_agent.call_args.kwargs["checkpointer"] is mock_checkpointer.return_value + assert mock_create_code_agent.call_args.kwargs["skills"] is None @patch("dotenv.load_dotenv") @@ -65,6 +67,7 @@ def test_initialize_agent_builds_model_for_explicit_provider( api_key=None, base_url=None, backend_type="filesystem", + skills_dir=None, ) mock_create_chat_model.return_value = object() @@ -81,12 +84,72 @@ def test_initialize_agent_builds_model_for_explicit_provider( assert mock_create_code_agent.call_args.kwargs["checkpointer"] is mock_checkpointer.return_value +def test_resolve_skills_uses_explicit_dirs_in_order(tmp_path): + """Explicit skill dirs should be absolutized and keep user order.""" + first = tmp_path / "first" + second = tmp_path / "second" + + result = _resolve_skills(SimpleNamespace(skills_dir=[str(first), str(second)]), str(tmp_path)) + + assert result == [first.absolute().as_posix(), second.absolute().as_posix()] + + +def test_resolve_skills_finds_default_agents_skills(tmp_path): + """Default skills path is /.agents/skills.""" + default_skills = tmp_path / ".agents" / "skills" + default_skills.mkdir(parents=True) + + result = _resolve_skills(SimpleNamespace(skills_dir=None), str(tmp_path)) + + assert result == [default_skills.absolute().as_posix()] + + +def test_resolve_skills_returns_none_when_default_missing(tmp_path): + """No explicit dirs and no default directory should disable skills.""" + result = _resolve_skills(SimpleNamespace(skills_dir=None), str(tmp_path)) + + assert result is None + + +@patch("dotenv.load_dotenv") +@patch("deep_code_agent.code_agent.create_code_agent") +@patch("deep_code_agent.models.llms.langchain_chat.create_chat_model") +@patch("langgraph.checkpoint.memory.InMemorySaver") +def test_initialize_agent_passes_resolved_skills( + mock_checkpointer, + mock_create_chat_model, + mock_create_code_agent, + _mock_load_dotenv, + tmp_path, +): + """Agent initialization should pass resolved skill directories.""" + skills = tmp_path / ".agents" / "skills" + skills.mkdir(parents=True) + args = SimpleNamespace( + model_name=None, + model_provider="openai", + api_key=None, + base_url=None, + backend_type="filesystem", + skills_dir=None, + ) + + _initialize_agent(args, str(tmp_path)) + + assert mock_create_code_agent.call_args.kwargs["skills"] == [skills.absolute().as_posix()] + + @patch("deep_code_agent.tui.DeepCodeAgentApp") @patch("deep_code_agent.cli._initialize_agent") def test_tui_mode_defers_agent_initialization_until_after_app_starts(mock_initialize_agent, mock_app_class): """TUI mode should render before doing slow agent initialization.""" args = SimpleNamespace( model_name=None, + model_provider="openai", + api_key=None, + base_url=None, + backend_type="filesystem", + skills_dir=["/tmp/custom-skills"], thread_id="test-thread", ) app_instance = mock_app_class.return_value @@ -96,4 +159,6 @@ def test_tui_mode_defers_agent_initialization_until_after_app_starts(mock_initia mock_initialize_agent.assert_not_called() mock_app_class.assert_called_once() assert "agent_factory" in mock_app_class.call_args.kwargs + mock_app_class.call_args.kwargs["agent_factory"]() + assert mock_initialize_agent.call_args.args == (args, mock_app_class.call_args.kwargs["session_info"]["codebase_dir"]) app_instance.run.assert_called_once_with() diff --git a/tests/test_code_agent.py b/tests/test_code_agent.py index eae3161..d6a015c 100644 --- a/tests/test_code_agent.py +++ b/tests/test_code_agent.py @@ -191,11 +191,46 @@ def test_filesystem_backend_creates_directory_and_mounts_terminal( assert target_dir.exists() call_kwargs = mock_create_agent.call_args.kwargs - mock_filesystem_backend.assert_called_once_with(root_dir=target_dir.absolute().as_posix()) + mock_filesystem_backend.assert_called_once_with(root_dir=target_dir.absolute().as_posix(), virtual_mode=False) mock_make_terminal_tool.assert_called_once_with(target_dir.absolute().as_posix()) assert call_kwargs["backend"] is mock_filesystem_backend.return_value assert call_kwargs["tools"] == [mock_terminal_tool] assert call_kwargs["tools"][0] is not terminal + assert call_kwargs["skills"] is None + + @patch("deepagents.backends.filesystem.FilesystemBackend") + @patch("deep_code_agent.code_agent.make_terminal_tool") + @patch("deep_code_agent.code_agent.create_deep_agent") + @patch("deep_code_agent.code_agent.create_chat_model") + @patch("deep_code_agent.code_agent.get_system_prompt") + @patch("deep_code_agent.code_agent.create_subagent_configurations") + def test_filesystem_backend_passes_skills( + self, + mock_subagents, + mock_prompt, + mock_create_chat_model, + mock_create_agent, + mock_make_terminal_tool, + mock_filesystem_backend, + tmp_path, + ): + """Test filesystem backend passes skill directories to DeepAgents.""" + mock_prompt.return_value = "Root: {codebase_dir}" + mock_subagents.return_value = [] + mock_create_chat_model.return_value = MagicMock() + mock_create_agent.return_value = MagicMock() + mock_make_terminal_tool.return_value = MagicMock() + skills = [tmp_path.joinpath(".agents", "skills").as_posix()] + + create_code_agent(str(tmp_path), backend_type="filesystem", skills=skills) + + call_kwargs = mock_create_agent.call_args.kwargs + assert call_kwargs["skills"] == skills + + def test_state_backend_rejects_skills(self): + """State backend should not accept local skill directories.""" + with pytest.raises(ValueError, match="Skills require filesystem backend"): + create_code_agent("/tmp/test", backend_type="state", skills=["/tmp/test/.agents/skills"]) @patch("deep_code_agent.code_agent.create_deep_agent") @patch("deep_code_agent.code_agent.create_chat_model") diff --git a/tests/tui/test_agent_bridge_helpers.py b/tests/tui/test_agent_bridge_helpers.py index 9c40ba3..7e74b4a 100644 --- a/tests/tui/test_agent_bridge_helpers.py +++ b/tests/tui/test_agent_bridge_helpers.py @@ -1,5 +1,7 @@ """Tests for AgentBridge helper extraction methods.""" +import asyncio + def test_agent_bridge_extracts_tool_name_from_action_requests(): from deep_code_agent.tui.bridge.agent_bridge import AgentBridge @@ -53,3 +55,66 @@ def test_agent_bridge_helper_fallbacks_and_config(): assert bridge._extract_action_requests_from_interrupt({"unexpected": True}) == [] assert bridge._extract_num_action_requests({"unexpected": True}) == 1 assert bridge.cancel_current() is None + + +def test_agent_bridge_starts_new_bubble_after_tool_call(): + from deep_code_agent.tui.bridge.agent_bridge import AgentBridge + from deep_code_agent.tui.bridge.stream_handler import AgentEvent, EventType + + class FakeBubble: + def __init__(self, content): + self.content = content + + def update_content(self, content): + self.content = content + + class FakeChatLog: + def __init__(self): + self.messages = [] + + def add_agent_message(self, content): + bubble = FakeBubble(content) + self.messages.append(bubble) + return bubble + + def add_tool_call_widget(self, **kwargs): + return object() + + class FakeStatusBar: + def set_streaming(self): + pass + + class FakeInputBox: + pass + + class FakeSidePanel: + def add_tool_call(self, tool_name, args): + pass + + class FakeApp: + debug_tool_calls = False + + def __init__(self): + self._chat_log = FakeChatLog() + self._status_bar = FakeStatusBar() + self._input_box = FakeInputBox() + self._side_panel = FakeSidePanel() + + def call_from_thread(self, func, *args, **kwargs): + func(*args, **kwargs) + + def notify(self, *args, **kwargs): + pass + + app = FakeApp() + bridge = AgentBridge(agent=object(), app=app) + + asyncio.run(bridge._dispatch_event(AgentEvent(type=EventType.MESSAGE_CHUNK, data="Before tools."))) + asyncio.run( + bridge._dispatch_event( + AgentEvent(type=EventType.TOOL_CALL, data={"name": "ls", "args": {}, "id": "call_1"}) + ) + ) + asyncio.run(bridge._dispatch_event(AgentEvent(type=EventType.MESSAGE_CHUNK, data="After tools."))) + + assert [message.content for message in app._chat_log.messages] == ["Before tools.", "After tools."] diff --git a/uv.lock b/uv.lock index 088e027..74b7e74 100644 --- a/uv.lock +++ b/uv.lock @@ -13,7 +13,7 @@ wheels = [ [[package]] name = "anthropic" -version = "0.75.0" +version = "0.105.2" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "anyio" }, @@ -25,9 +25,9 @@ dependencies = [ { name = "sniffio" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/04/1f/08e95f4b7e2d35205ae5dcbb4ae97e7d477fc521c275c02609e2931ece2d/anthropic-0.75.0.tar.gz", hash = "sha256:e8607422f4ab616db2ea5baacc215dd5f028da99ce2f022e33c7c535b29f3dfb", size = 439565, upload-time = "2025-11-24T20:41:45.28Z" } +sdist = { url = "https://files.pythonhosted.org/packages/46/46/47581b8c689c743ceabf6a0f9ff48472160900ce802d26c0fb50423997b3/anthropic-0.105.2.tar.gz", hash = "sha256:0e26b90841c2dced7cc6e98d21d5517d0be33f1876b8e779f478202e28bcaa07", size = 853789, upload-time = "2026-05-29T00:21:14.104Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/60/1c/1cd02b7ae64302a6e06724bf80a96401d5313708651d277b1458504a1730/anthropic-0.75.0-py3-none-any.whl", hash = "sha256:ea8317271b6c15d80225a9f3c670152746e88805a7a61e14d4a374577164965b", size = 388164, upload-time = "2025-11-24T20:41:43.587Z" }, + { url = "https://files.pythonhosted.org/packages/83/75/be0c357e33a5a56c8f9db5b4212f886138d2bf59c0952d858f6b75d710ef/anthropic-0.105.2-py3-none-any.whl", hash = "sha256:e53ed5f6bf36fb1ecb9b25d8634cfd30e02fab9fb3374a0c2d5c585874757230", size = 837507, upload-time = "2026-05-29T00:21:15.528Z" }, ] [[package]] @@ -333,7 +333,7 @@ dev = [ [package.metadata] requires-dist = [ - { name = "deepagents", specifier = ">=0.2.8" }, + { name = "deepagents", specifier = ">=0.6.7" }, { name = "langchain", specifier = ">=1.1.0" }, { name = "langchain-openai", specifier = ">=1.1.0" }, { name = "langgraph", specifier = ">=1.0.4" }, @@ -351,17 +351,19 @@ dev = [ [[package]] name = "deepagents" -version = "0.3.0" +version = "0.6.7" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "langchain" }, { name = "langchain-anthropic" }, { name = "langchain-core" }, + { name = "langchain-google-genai" }, + { name = "langsmith" }, { name = "wcmatch" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/cc/c7/d3c2840bd0e66b6cd5948aa69625e129328ad261308e18fcb9a9420709da/deepagents-0.3.0.tar.gz", hash = "sha256:3dd4d2ed53efb1ef78aeb1020a5696c0ec7e58e627b305a6665d33fe6fbdedff", size = 51387, upload-time = "2025-12-08T21:38:44.077Z" } +sdist = { url = "https://files.pythonhosted.org/packages/51/bb/bb837a2c51631fe9d7eedf6aca7629ddca6336831801e75efcd2f5fa9c27/deepagents-0.6.7.tar.gz", hash = "sha256:af7b5857b28e29a847a4ced4cc7aaa809d33d42107696b1ca5d978d17e96b831", size = 194236, upload-time = "2026-05-30T04:42:14.591Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/22/e9/60bab7f37ff38bf982ea578e457ed1878ded613a3425462bcd07b00487e9/deepagents-0.3.0-py3-none-any.whl", hash = "sha256:9e23532d8d535dc2b0b4e0834453a1223a6a8f81b77947c0faf54537d05ce89a", size = 54065, upload-time = "2025-12-08T21:38:42.956Z" }, + { url = "https://files.pythonhosted.org/packages/97/25/3a7f23d04778ccb95542f1b1ed39826290916221cc8203d0fb8b26c3edc1/deepagents-0.6.7-py3-none-any.whl", hash = "sha256:3518c1e5f4b9f6588ba39912b668b42b5c864b99a638e94c166d1c7176c7388e", size = 218740, upload-time = "2026-05-30T04:42:13.225Z" }, ] [[package]] @@ -391,12 +393,60 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/c1/ea/53f2148663b321f21b5a606bd5f191517cf40b7072c0497d3c92c4a13b1e/executing-2.2.1-py2.py3-none-any.whl", hash = "sha256:760643d3452b4d777d295bb167ccc74c64a81df23fb5e08eff250c425a4b2017", size = 28317, upload-time = "2025-09-01T09:48:08.5Z" }, ] +[[package]] +name = "filetype" +version = "1.2.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/bb/29/745f7d30d47fe0f251d3ad3dc2978a23141917661998763bebb6da007eb1/filetype-1.2.0.tar.gz", hash = "sha256:66b56cd6474bf41d8c54660347d37afcc3f7d1970648de365c102ef77548aadb", size = 998020, upload-time = "2022-11-02T17:34:04.141Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/18/79/1b8fa1bb3568781e84c9200f951c735f3f157429f44be0495da55894d620/filetype-1.2.0-py2.py3-none-any.whl", hash = "sha256:7ce71b6880181241cf7ac8697a2f1eb6a8bd9b429f7ad6d27b8db9ba5f1c2d25", size = 19970, upload-time = "2022-11-02T17:34:01.425Z" }, +] + [[package]] name = "forbiddenfruit" version = "0.1.4" source = { registry = "https://pypi.org/simple" } sdist = { url = "https://files.pythonhosted.org/packages/e6/79/d4f20e91327c98096d605646bdc6a5ffedae820f38d378d3515c42ec5e60/forbiddenfruit-0.1.4.tar.gz", hash = "sha256:e3f7e66561a29ae129aac139a85d610dbf3dd896128187ed5454b6421f624253", size = 43756, upload-time = "2021-01-16T21:03:35.401Z" } +[[package]] +name = "google-auth" +version = "2.53.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "cryptography" }, + { name = "pyasn1-modules" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/c6/ad/ff781329bbbdc0974a098d996e89c9e1f7024262f9e3eec442fbb9ad1ac6/google_auth-2.53.0.tar.gz", hash = "sha256:e7e6aa16f6bee7b2b264830fd04f08087a1d5a836df516251a5d15327b246c9c", size = 335844, upload-time = "2026-05-15T20:53:07.928Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/4a/c9/db44165ba7c581268c6d46017ef63339110378305062830104fc7fa144cb/google_auth-2.53.0-py3-none-any.whl", hash = "sha256:6e7449917c599b35126a99ec268ec6880301f2fea41dce198fe8fd83ff642b68", size = 246071, upload-time = "2026-05-15T20:53:05.609Z" }, +] + +[package.optional-dependencies] +requests = [ + { name = "requests" }, +] + +[[package]] +name = "google-genai" +version = "2.7.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "anyio" }, + { name = "distro" }, + { name = "google-auth", extra = ["requests"] }, + { name = "httpx" }, + { name = "pydantic" }, + { name = "requests" }, + { name = "sniffio" }, + { name = "tenacity" }, + { name = "typing-extensions" }, + { name = "websockets" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/a7/7b/6eb3b3d545b6bb4c374acba1ccf91b0f33b605e551536a6243cfcef2f07f/google_genai-2.7.0.tar.gz", hash = "sha256:3c6f32f5ced9877ededd1b384b5e5b7f09c20046ec3390b662b16d8cd1882ac5", size = 555853, upload-time = "2026-05-28T15:39:24.58Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3c/dd/7a8be39e9d698e80e9db796514efbc6083dbd787bdb9a101e8ba47248e5e/google_genai-2.7.0-py3-none-any.whl", hash = "sha256:21cac381e09a869151706aba797b6a4f96cfe92c484e13204d092caee7ff11cb", size = 822545, upload-time = "2026-05-28T15:39:22.907Z" }, +] + [[package]] name = "googleapis-common-protos" version = "1.72.0" @@ -674,38 +724,39 @@ wheels = [ [[package]] name = "langchain" -version = "1.2.0" +version = "1.3.2" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "langchain-core" }, { name = "langgraph" }, { name = "pydantic" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/b1/12/3a74c22abdfddd877dfc2ee666d516f9132877fcd25eb4dd694835c59c79/langchain-1.2.0.tar.gz", hash = "sha256:a087d1e2b2969819e29a91a6d5f98302aafe31bd49ba377ecee3bf5a5dcfe14a", size = 536126, upload-time = "2025-12-15T14:51:42.24Z" } +sdist = { url = "https://files.pythonhosted.org/packages/d5/d0/c7f9d3d26c0e3f8bb146c6d707ee0fc1d30d8da65a59626e8a580085e929/langchain-1.3.2.tar.gz", hash = "sha256:ffd5f204a46b5fa1a38bf89ba3b45ca0902c02d18fa7d2a2eaeaeb1f5bf19d0a", size = 600598, upload-time = "2026-05-26T18:17:57.715Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/23/00/4e3fa0d90f5a5c376ccb8ca983d0f0f7287783dfac48702e18f01d24673b/langchain-1.2.0-py3-none-any.whl", hash = "sha256:82f0d17aa4fbb11560b30e1e7d4aeb75e3ad71ce09b85c90ab208b181a24ffac", size = 102828, upload-time = "2025-12-15T14:51:40.802Z" }, + { url = "https://files.pythonhosted.org/packages/f8/82/a54edcd1c48163de5642eb10fa2cb58b13a8889c659964f63f0306b58b1e/langchain-1.3.2-py3-none-any.whl", hash = "sha256:900f6b3f4ee08b9ba3cdbe667dbf42525bd6f66a4a07a7f1db26262673e41ed6", size = 121225, upload-time = "2026-05-26T18:17:56.075Z" }, ] [[package]] name = "langchain-anthropic" -version = "1.3.0" +version = "1.4.4" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "anthropic" }, { name = "langchain-core" }, { name = "pydantic" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/de/50/cc3b3e0410d86de457d7a100dde763fc1c33c4ce884e883659aa4cf95538/langchain_anthropic-1.3.0.tar.gz", hash = "sha256:497a937ee0310c588196bff37f39f02d43d87bff3a12d16278bdbc3bd0e9a80b", size = 707207, upload-time = "2025-12-12T20:20:57.417Z" } +sdist = { url = "https://files.pythonhosted.org/packages/03/f6/113cf54064b5847367b5a535d80ca2bee34edaea9fddb56ac4466de5e595/langchain_anthropic-1.4.4.tar.gz", hash = "sha256:9ea39ed345f8b13f13bb37549130eedcec2d032eb08b4942642e758c5ba3ece5", size = 687917, upload-time = "2026-05-28T20:20:20.342Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/29/ca/0725bc347a9c226da9d76f85bf7d03115caec7dbc87876af68579c4ab24e/langchain_anthropic-1.3.0-py3-none-any.whl", hash = "sha256:3823560e1df15d6082636baa04f87cb59052ba70aada0eba381c4679b1ce0eba", size = 45724, upload-time = "2025-12-12T20:20:56.287Z" }, + { url = "https://files.pythonhosted.org/packages/c5/e6/d13340529db7fc52ffb1436b18e988df29ebfdd207a2944975136cc82093/langchain_anthropic-1.4.4-py3-none-any.whl", hash = "sha256:10a5d2915e8d4fd8a9f169774e760b089c7f14de044e13259db5f1c2d3d59d9b", size = 51263, upload-time = "2026-05-28T20:20:18.757Z" }, ] [[package]] name = "langchain-core" -version = "1.2.1" +version = "1.4.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "jsonpatch" }, + { name = "langchain-protocol" }, { name = "langsmith" }, { name = "packaging" }, { name = "pydantic" }, @@ -714,9 +765,24 @@ dependencies = [ { name = "typing-extensions" }, { name = "uuid-utils" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/f5/a0/2177f4ef4dfbea8edeba377b7b4889d177b8356ce186640e4651b240fd4d/langchain_core-1.2.1.tar.gz", hash = "sha256:131e6ad105b47ec2adc4d4d973f569276688f48cd890ba44603d48e76d9993ce", size = 802986, upload-time = "2025-12-15T14:32:50.845Z" } +sdist = { url = "https://files.pythonhosted.org/packages/59/de/679a53472c25860837e32c0442c962fa86e95317a36460e2c9d5c91b17c2/langchain_core-1.4.0.tar.gz", hash = "sha256:1dc341eed802ed9c117c0df3923c991e5e9e226571e5725c194eeb5bd93d1a7f", size = 920260, upload-time = "2026-05-11T18:42:35.919Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/cc/95/98c47dbb4b6098934ff70e0f52efef3a85505dbcccc9eb63587e21fde4c9/langchain_core-1.2.1-py3-none-any.whl", hash = "sha256:2f63859f85dc3d95f768e35fed605702e3ff5aa3e92c7b253103119613e79768", size = 475972, upload-time = "2025-12-15T14:32:49.698Z" }, + { url = "https://files.pythonhosted.org/packages/0f/1a/86c38c27b81913a1c6c12448cab55defb5a1097c7dc9a4cea83f55477a2d/langchain_core-1.4.0-py3-none-any.whl", hash = "sha256:23cbbdb46e38ddd1dd5247e6167e96013eae74bea4c5949c550809970a9e565c", size = 548120, upload-time = "2026-05-11T18:42:33.992Z" }, +] + +[[package]] +name = "langchain-google-genai" +version = "4.2.4" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "filetype" }, + { name = "google-genai" }, + { name = "langchain-core" }, + { name = "pydantic" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/54/52/de168715eb092c920531d418b8b9aafdff9e37ee80e5fc88106211ccbd47/langchain_google_genai-4.2.4.tar.gz", hash = "sha256:2f5de7a8a6552ffb64b907aca7503fd5e34d1a3240e280abcdc5f7eef480edd5", size = 270054, upload-time = "2026-05-28T21:23:00.503Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2a/a2/5feaf21cfe6fac80eae944f3ac5348d9e5e986813256f74f8dd104617474/langchain_google_genai-4.2.4-py3-none-any.whl", hash = "sha256:0e2c1021a15c91e60b68d813bb3e793bd1d9396b3f8639b943ab4e56e5652e04", size = 68832, upload-time = "2026-05-28T21:22:59.291Z" }, ] [[package]] @@ -733,9 +799,21 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/d1/11/2b3b4973495fc5f0456ed5c8c88a6ded7ca34c8608c72faafa87088acf5a/langchain_openai-1.1.3-py3-none-any.whl", hash = "sha256:58945d9e87c1ab3a91549c3f3744c6c9571511cdc3cf875b8842aaec5b3e32a6", size = 84585, upload-time = "2025-12-12T22:28:07.066Z" }, ] +[[package]] +name = "langchain-protocol" +version = "0.0.16" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/36/e7/8300ba22d968653051fd06e3117d783872dddf3dcebdd6b1d386836eb43c/langchain_protocol-0.0.16.tar.gz", hash = "sha256:806c7cdd951b1c4f692fa40fce60821ff0f221d4360e27673ddf2c2b99c2b7ff", size = 5969, upload-time = "2026-05-28T23:05:11.121Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1f/9c/06dfcc88d02a6364e8d864c421ddd3736305cb0a6c853f75c302c80fe17c/langchain_protocol-0.0.16-py3-none-any.whl", hash = "sha256:3658c142c5d0fb3a023a4be442ce4c15c6d626aab6135eb79a76dc64ad19c3c3", size = 7037, upload-time = "2026-05-28T23:05:10.163Z" }, +] + [[package]] name = "langgraph" -version = "1.0.5" +version = "1.2.2" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "langchain-core" }, @@ -745,9 +823,9 @@ dependencies = [ { name = "pydantic" }, { name = "xxhash" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/7d/47/28f4d4d33d88f69de26f7a54065961ac0c662cec2479b36a2db081ef5cb6/langgraph-1.0.5.tar.gz", hash = "sha256:7f6ae59622386b60fe9fa0ad4c53f42016b668455ed604329e7dc7904adbf3f8", size = 493969, upload-time = "2025-12-12T23:05:48.224Z" } +sdist = { url = "https://files.pythonhosted.org/packages/e6/5a/ffc12434ee8aecab830d58b4d204ddea45073eae7639c963310f671a5bf5/langgraph-1.2.2.tar.gz", hash = "sha256:f54a98458976b3ff0774683867df125fb52d8dbedeb2441d0b0656a51331cee5", size = 695730, upload-time = "2026-05-26T18:07:28.49Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/23/1b/e318ee76e42d28f515d87356ac5bd7a7acc8bad3b8f54ee377bef62e1cbf/langgraph-1.0.5-py3-none-any.whl", hash = "sha256:b4cfd173dca3c389735b47228ad8b295e6f7b3df779aba3a1e0c23871f81281e", size = 157056, upload-time = "2025-12-12T23:05:46.499Z" }, + { url = "https://files.pythonhosted.org/packages/42/9b/b08d578bba73e25351152dfd3d6d21e81210a5fff1b6f26e56f33197c8f5/langgraph-1.2.2-py3-none-any.whl", hash = "sha256:0a851bf4ba5939c5474a2fd57e6b439b5315283e254e42943bd392c2d71a5e03", size = 236376, upload-time = "2026-05-26T18:07:26.577Z" }, ] [[package]] @@ -789,15 +867,15 @@ wheels = [ [[package]] name = "langgraph-checkpoint" -version = "3.0.1" +version = "4.1.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "langchain-core" }, { name = "ormsgpack" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/0f/07/2b1c042fa87d40cf2db5ca27dc4e8dd86f9a0436a10aa4361a8982718ae7/langgraph_checkpoint-3.0.1.tar.gz", hash = "sha256:59222f875f85186a22c494aedc65c4e985a3df27e696e5016ba0b98a5ed2cee0", size = 137785, upload-time = "2025-11-04T21:55:47.774Z" } +sdist = { url = "https://files.pythonhosted.org/packages/83/47/886af6f886f0bff2273164a45f008694e48a96ff3cd25ff0228f2aa9480e/langgraph_checkpoint-4.1.1.tar.gz", hash = "sha256:6c2bdb530c91f91d7d9c1bd100925d0fc4f498d418c17f3587d1526279482a25", size = 184020, upload-time = "2026-05-22T16:57:38.503Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/48/e3/616e3a7ff737d98c1bbb5700dd62278914e2a9ded09a79a1fa93cf24ce12/langgraph_checkpoint-3.0.1-py3-none-any.whl", hash = "sha256:9b04a8d0edc0474ce4eaf30c5d731cee38f11ddff50a6177eead95b5c4e4220b", size = 46249, upload-time = "2025-11-04T21:55:46.472Z" }, + { url = "https://files.pythonhosted.org/packages/bd/b4/71425e3e38be92611300b9cc5e46a5bf98ab23f5ea8a75b73d02a2f1413c/langgraph_checkpoint-4.1.1-py3-none-any.whl", hash = "sha256:25d29144b082827218e7bc3f1e9b0566a4bb007895cd6cc26f66a8428739f56e", size = 56212, upload-time = "2026-05-22T16:57:37.203Z" }, ] [[package]] @@ -822,15 +900,15 @@ inmem = [ [[package]] name = "langgraph-prebuilt" -version = "1.0.5" +version = "1.1.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "langchain-core" }, { name = "langgraph-checkpoint" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/46/f9/54f8891b32159e4542236817aea2ee83de0de18bce28e9bdba08c7f93001/langgraph_prebuilt-1.0.5.tar.gz", hash = "sha256:85802675ad778cc7240fd02d47db1e0b59c0c86d8369447d77ce47623845db2d", size = 144453, upload-time = "2025-11-20T16:47:39.23Z" } +sdist = { url = "https://files.pythonhosted.org/packages/29/66/ed9b93f56bc17ef22d551892f0ac2b225a97fe0fcf23a511b857f70d590b/langgraph_prebuilt-1.1.0.tar.gz", hash = "sha256:3c579cf6eed2d17f9c157c2d0fcaddcd8688524e7022d3b22b37a3bf4589d528", size = 178833, upload-time = "2026-05-12T03:37:49.332Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/87/5e/aeba4a5b39fe6e874e0dd003a82da71c7153e671312671a8dacc5cb7c1af/langgraph_prebuilt-1.0.5-py3-none-any.whl", hash = "sha256:22369563e1848862ace53fbc11b027c28dd04a9ac39314633bb95f2a7e258496", size = 35072, upload-time = "2025-11-20T16:47:38.187Z" }, + { url = "https://files.pythonhosted.org/packages/e9/43/3fe1a700b8490ed02679cdbbc8c915eb23a092faf496c9c1118abcd10be3/langgraph_prebuilt-1.1.0-py3-none-any.whl", hash = "sha256:51e311747d755b751d5c6b39b0c1446124d3a7643d2515017e6714b323508fc9", size = 41043, upload-time = "2026-05-12T03:37:48.007Z" }, ] [[package]] @@ -865,7 +943,7 @@ wheels = [ [[package]] name = "langsmith" -version = "0.4.59" +version = "0.8.8" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "httpx" }, @@ -875,11 +953,13 @@ dependencies = [ { name = "requests" }, { name = "requests-toolbelt" }, { name = "uuid-utils" }, + { name = "websockets" }, + { name = "xxhash" }, { name = "zstandard" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/61/71/d61524c3205bde7ec90423d997cf1a228d8adf2811110ec91ed40c8e8a34/langsmith-0.4.59.tar.gz", hash = "sha256:6b143214c2303dafb29ab12dcd05ac50bdfc60dac01c6e0450e50cee1d2415e0", size = 992784, upload-time = "2025-12-11T02:40:52.231Z" } +sdist = { url = "https://files.pythonhosted.org/packages/2f/93/28df12b3b3c776077983b92f1299c623592b5999695af2a755fb90ff048b/langsmith-0.8.8.tar.gz", hash = "sha256:9d00e54f54d833c1914003527ff03ad0364741034330da72f0adbeaba852b6cf", size = 4468035, upload-time = "2026-05-31T22:14:57.698Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/63/54/4577ef9424debea2fa08af338489d593276520d2e2f8950575d292be612c/langsmith-0.4.59-py3-none-any.whl", hash = "sha256:97c26399286441a7b7b06b912e2801420fbbf3a049787e609d49dc975ab10bc5", size = 413051, upload-time = "2025-12-11T02:40:50.523Z" }, + { url = "https://files.pythonhosted.org/packages/8d/71/94a8f2b573278a0b0b7dfd37663c0ddd36867f9e2bba69addd183de0cd56/langsmith-0.8.8-py3-none-any.whl", hash = "sha256:9d60d724c0d187c036e184b3ffdf9fa5c6822aa0bb88144a5fb898e79be645af", size = 402712, upload-time = "2026-05-31T22:14:55.908Z" }, ] [[package]] @@ -1205,6 +1285,27 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/8e/37/efad0257dc6e593a18957422533ff0f87ede7c9c6ea010a2177d738fb82f/pure_eval-0.2.3-py3-none-any.whl", hash = "sha256:1db8e35b67b3d218d818ae653e27f06c3aa420901fa7b081ca98cbedc874e0d0", size = 11842, upload-time = "2024-07-21T12:58:20.04Z" }, ] +[[package]] +name = "pyasn1" +version = "0.6.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/5c/5f/6583902b6f79b399c9c40674ac384fd9cd77805f9e6205075f828ef11fb2/pyasn1-0.6.3.tar.gz", hash = "sha256:697a8ecd6d98891189184ca1fa05d1bb00e2f84b5977c481452050549c8a72cf", size = 148685, upload-time = "2026-03-17T01:06:53.382Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5d/a0/7d793dce3fa811fe047d6ae2431c672364b462850c6235ae306c0efd025f/pyasn1-0.6.3-py3-none-any.whl", hash = "sha256:a80184d120f0864a52a073acc6fc642847d0be408e7c7252f31390c0f4eadcde", size = 83997, upload-time = "2026-03-17T01:06:52.036Z" }, +] + +[[package]] +name = "pyasn1-modules" +version = "0.4.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pyasn1" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/e9/e6/78ebbb10a8c8e4b61a59249394a4a594c1a7af95593dc933a349c8d00964/pyasn1_modules-0.4.2.tar.gz", hash = "sha256:677091de870a80aae844b1ca6134f54652fa2c8c5a52aa396440ac3106e941e6", size = 307892, upload-time = "2025-03-28T02:41:22.17Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/47/8d/d529b5d697919ba8c11ad626e835d4039be708a35b0d22de83a269a6682c/pyasn1_modules-0.4.2-py3-none-any.whl", hash = "sha256:29253a9207ce32b64c3ac6600edc75368f98473906e8fd1043bd6b5b1de2c14a", size = 181259, upload-time = "2025-03-28T02:41:19.028Z" }, +] + [[package]] name = "pycparser" version = "2.23" @@ -1794,6 +1895,42 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/af/b5/123f13c975e9f27ab9c0770f514345bd406d0e8d3b7a0723af9d43f710af/wcwidth-0.2.14-py2.py3-none-any.whl", hash = "sha256:a7bb560c8aee30f9957e5f9895805edd20602f2d7f720186dfd906e82b4982e1", size = 37286, upload-time = "2025-09-22T16:29:51.641Z" }, ] +[[package]] +name = "websockets" +version = "16.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/04/24/4b2031d72e840ce4c1ccb255f693b15c334757fc50023e4db9537080b8c4/websockets-16.0.tar.gz", hash = "sha256:5f6261a5e56e8d5c42a4497b364ea24d94d9563e8fbd44e78ac40879c60179b5", size = 179346, upload-time = "2026-01-10T09:23:47.181Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/cc/9c/baa8456050d1c1b08dd0ec7346026668cbc6f145ab4e314d707bb845bf0d/websockets-16.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:878b336ac47938b474c8f982ac2f7266a540adc3fa4ad74ae96fea9823a02cc9", size = 177364, upload-time = "2026-01-10T09:22:59.333Z" }, + { url = "https://files.pythonhosted.org/packages/7e/0c/8811fc53e9bcff68fe7de2bcbe75116a8d959ac699a3200f4847a8925210/websockets-16.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:52a0fec0e6c8d9a784c2c78276a48a2bdf099e4ccc2a4cad53b27718dbfd0230", size = 175039, upload-time = "2026-01-10T09:23:01.171Z" }, + { url = "https://files.pythonhosted.org/packages/aa/82/39a5f910cb99ec0b59e482971238c845af9220d3ab9fa76dd9162cda9d62/websockets-16.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:e6578ed5b6981005df1860a56e3617f14a6c307e6a71b4fff8c48fdc50f3ed2c", size = 175323, upload-time = "2026-01-10T09:23:02.341Z" }, + { url = "https://files.pythonhosted.org/packages/bd/28/0a25ee5342eb5d5f297d992a77e56892ecb65e7854c7898fb7d35e9b33bd/websockets-16.0-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:95724e638f0f9c350bb1c2b0a7ad0e83d9cc0c9259f3ea94e40d7b02a2179ae5", size = 184975, upload-time = "2026-01-10T09:23:03.756Z" }, + { url = "https://files.pythonhosted.org/packages/f9/66/27ea52741752f5107c2e41fda05e8395a682a1e11c4e592a809a90c6a506/websockets-16.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c0204dc62a89dc9d50d682412c10b3542d748260d743500a85c13cd1ee4bde82", size = 186203, upload-time = "2026-01-10T09:23:05.01Z" }, + { url = "https://files.pythonhosted.org/packages/37/e5/8e32857371406a757816a2b471939d51c463509be73fa538216ea52b792a/websockets-16.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:52ac480f44d32970d66763115edea932f1c5b1312de36df06d6b219f6741eed8", size = 185653, upload-time = "2026-01-10T09:23:06.301Z" }, + { url = "https://files.pythonhosted.org/packages/9b/67/f926bac29882894669368dc73f4da900fcdf47955d0a0185d60103df5737/websockets-16.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6e5a82b677f8f6f59e8dfc34ec06ca6b5b48bc4fcda346acd093694cc2c24d8f", size = 184920, upload-time = "2026-01-10T09:23:07.492Z" }, + { url = "https://files.pythonhosted.org/packages/3c/a1/3d6ccdcd125b0a42a311bcd15a7f705d688f73b2a22d8cf1c0875d35d34a/websockets-16.0-cp313-cp313-win32.whl", hash = "sha256:abf050a199613f64c886ea10f38b47770a65154dc37181bfaff70c160f45315a", size = 178255, upload-time = "2026-01-10T09:23:09.245Z" }, + { url = "https://files.pythonhosted.org/packages/6b/ae/90366304d7c2ce80f9b826096a9e9048b4bb760e44d3b873bb272cba696b/websockets-16.0-cp313-cp313-win_amd64.whl", hash = "sha256:3425ac5cf448801335d6fdc7ae1eb22072055417a96cc6b31b3861f455fbc156", size = 178689, upload-time = "2026-01-10T09:23:10.483Z" }, + { url = "https://files.pythonhosted.org/packages/f3/1d/e88022630271f5bd349ed82417136281931e558d628dd52c4d8621b4a0b2/websockets-16.0-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:8cc451a50f2aee53042ac52d2d053d08bf89bcb31ae799cb4487587661c038a0", size = 177406, upload-time = "2026-01-10T09:23:12.178Z" }, + { url = "https://files.pythonhosted.org/packages/f2/78/e63be1bf0724eeb4616efb1ae1c9044f7c3953b7957799abb5915bffd38e/websockets-16.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:daa3b6ff70a9241cf6c7fc9e949d41232d9d7d26fd3522b1ad2b4d62487e9904", size = 175085, upload-time = "2026-01-10T09:23:13.511Z" }, + { url = "https://files.pythonhosted.org/packages/bb/f4/d3c9220d818ee955ae390cf319a7c7a467beceb24f05ee7aaaa2414345ba/websockets-16.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:fd3cb4adb94a2a6e2b7c0d8d05cb94e6f1c81a0cf9dc2694fb65c7e8d94c42e4", size = 175328, upload-time = "2026-01-10T09:23:14.727Z" }, + { url = "https://files.pythonhosted.org/packages/63/bc/d3e208028de777087e6fb2b122051a6ff7bbcca0d6df9d9c2bf1dd869ae9/websockets-16.0-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:781caf5e8eee67f663126490c2f96f40906594cb86b408a703630f95550a8c3e", size = 185044, upload-time = "2026-01-10T09:23:15.939Z" }, + { url = "https://files.pythonhosted.org/packages/ad/6e/9a0927ac24bd33a0a9af834d89e0abc7cfd8e13bed17a86407a66773cc0e/websockets-16.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:caab51a72c51973ca21fa8a18bd8165e1a0183f1ac7066a182ff27107b71e1a4", size = 186279, upload-time = "2026-01-10T09:23:17.148Z" }, + { url = "https://files.pythonhosted.org/packages/b9/ca/bf1c68440d7a868180e11be653c85959502efd3a709323230314fda6e0b3/websockets-16.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:19c4dc84098e523fd63711e563077d39e90ec6702aff4b5d9e344a60cb3c0cb1", size = 185711, upload-time = "2026-01-10T09:23:18.372Z" }, + { url = "https://files.pythonhosted.org/packages/c4/f8/fdc34643a989561f217bb477cbc47a3a07212cbda91c0e4389c43c296ebf/websockets-16.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:a5e18a238a2b2249c9a9235466b90e96ae4795672598a58772dd806edc7ac6d3", size = 184982, upload-time = "2026-01-10T09:23:19.652Z" }, + { url = "https://files.pythonhosted.org/packages/dd/d1/574fa27e233764dbac9c52730d63fcf2823b16f0856b3329fc6268d6ae4f/websockets-16.0-cp314-cp314-win32.whl", hash = "sha256:a069d734c4a043182729edd3e9f247c3b2a4035415a9172fd0f1b71658a320a8", size = 177915, upload-time = "2026-01-10T09:23:21.458Z" }, + { url = "https://files.pythonhosted.org/packages/8a/f1/ae6b937bf3126b5134ce1f482365fde31a357c784ac51852978768b5eff4/websockets-16.0-cp314-cp314-win_amd64.whl", hash = "sha256:c0ee0e63f23914732c6d7e0cce24915c48f3f1512ec1d079ed01fc629dab269d", size = 178381, upload-time = "2026-01-10T09:23:22.715Z" }, + { url = "https://files.pythonhosted.org/packages/06/9b/f791d1db48403e1f0a27577a6beb37afae94254a8c6f08be4a23e4930bc0/websockets-16.0-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:a35539cacc3febb22b8f4d4a99cc79b104226a756aa7400adc722e83b0d03244", size = 177737, upload-time = "2026-01-10T09:23:24.523Z" }, + { url = "https://files.pythonhosted.org/packages/bd/40/53ad02341fa33b3ce489023f635367a4ac98b73570102ad2cdd770dacc9a/websockets-16.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:b784ca5de850f4ce93ec85d3269d24d4c82f22b7212023c974c401d4980ebc5e", size = 175268, upload-time = "2026-01-10T09:23:25.781Z" }, + { url = "https://files.pythonhosted.org/packages/74/9b/6158d4e459b984f949dcbbb0c5d270154c7618e11c01029b9bbd1bb4c4f9/websockets-16.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:569d01a4e7fba956c5ae4fc988f0d4e187900f5497ce46339c996dbf24f17641", size = 175486, upload-time = "2026-01-10T09:23:27.033Z" }, + { url = "https://files.pythonhosted.org/packages/e5/2d/7583b30208b639c8090206f95073646c2c9ffd66f44df967981a64f849ad/websockets-16.0-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:50f23cdd8343b984957e4077839841146f67a3d31ab0d00e6b824e74c5b2f6e8", size = 185331, upload-time = "2026-01-10T09:23:28.259Z" }, + { url = "https://files.pythonhosted.org/packages/45/b0/cce3784eb519b7b5ad680d14b9673a31ab8dcb7aad8b64d81709d2430aa8/websockets-16.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:152284a83a00c59b759697b7f9e9cddf4e3c7861dd0d964b472b70f78f89e80e", size = 186501, upload-time = "2026-01-10T09:23:29.449Z" }, + { url = "https://files.pythonhosted.org/packages/19/60/b8ebe4c7e89fb5f6cdf080623c9d92789a53636950f7abacfc33fe2b3135/websockets-16.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:bc59589ab64b0022385f429b94697348a6a234e8ce22544e3681b2e9331b5944", size = 186062, upload-time = "2026-01-10T09:23:31.368Z" }, + { url = "https://files.pythonhosted.org/packages/88/a8/a080593f89b0138b6cba1b28f8df5673b5506f72879322288b031337c0b8/websockets-16.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:32da954ffa2814258030e5a57bc73a3635463238e797c7375dc8091327434206", size = 185356, upload-time = "2026-01-10T09:23:32.627Z" }, + { url = "https://files.pythonhosted.org/packages/c2/b6/b9afed2afadddaf5ebb2afa801abf4b0868f42f8539bfe4b071b5266c9fe/websockets-16.0-cp314-cp314t-win32.whl", hash = "sha256:5a4b4cc550cb665dd8a47f868c8d04c8230f857363ad3c9caf7a0c3bf8c61ca6", size = 178085, upload-time = "2026-01-10T09:23:33.816Z" }, + { url = "https://files.pythonhosted.org/packages/9f/3e/28135a24e384493fa804216b79a6a6759a38cc4ff59118787b9fb693df93/websockets-16.0-cp314-cp314t-win_amd64.whl", hash = "sha256:b14dc141ed6d2dde437cddb216004bcac6a1df0935d79656387bd41632ba0bbd", size = 178531, upload-time = "2026-01-10T09:23:35.016Z" }, + { url = "https://files.pythonhosted.org/packages/6f/28/258ebab549c2bf3e64d2b0217b973467394a9cea8c42f70418ca2c5d0d2e/websockets-16.0-py3-none-any.whl", hash = "sha256:1637db62fad1dc833276dded54215f2c7fa46912301a24bd94d45d46a011ceec", size = 171598, upload-time = "2026-01-10T09:23:45.395Z" }, +] + [[package]] name = "xxhash" version = "3.6.0"