[build-system] requires = ["uv_build>=0.9.2,<0.13"] build-backend = "uv_build" [tool.uv.build-backend] # Distribution is named "ag2" but the importable module lives in ./ag2 module-root = "." module-name = "ag2" source-exclude = ["test", "notebook"] [[tool.uv.index]] name = "pytorch-cpu" url = "https://download.pytorch.org/whl/cpu" explicit = true [tool.uv.sources] torch = [{ index = "pytorch-cpu", marker = "sys_platform == 'linux'" }] [project.urls] Homepage = "https://ag2.ai/" Documentation = "https://docs.ag2.ai" Tracker = "https://github.com/ag2ai/ag2/issues" Source = "https://github.com/ag2ai/ag2" Discord = "https://discord.gg/pAbnFJrkgZ" [project] name = "ag2" description = "A programming framework for agentic AI" readme = "README.md" license = "Apache-2.0" license-files = ["LICENSE"] authors = [{ name = "Chi Wang & Qingyun Wu", email = "support@ag2.ai" }] keywords = ["ai", "agent", "autogen", "ag2", "ag2.ai", "ag2ai", "agentic"] requires-python = ">=3.11" version = "1.3.1" classifiers = [ "Development Status :: 5 - Production/Stable", "Programming Language :: Python", "Programming Language :: Python :: Implementation :: CPython", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3 :: Only", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", "Programming Language :: Python :: 3.13", "Programming Language :: Python :: 3.14", "Operating System :: OS Independent", "Topic :: Software Development :: Libraries :: Application Frameworks", "Topic :: Software Development :: Libraries :: Python Modules", "Topic :: Software Development :: Libraries", "Topic :: Software Development", "Intended Audience :: Developers", "Intended Audience :: Information Technology", "Framework :: Pydantic", "Framework :: Pydantic :: 2", ] dependencies = [ "pydantic>=2.6.1,<3", "httpx>=0.28.1,<1", "anyio>=4.0.0,<5.0.0", # SKILL.md frontmatter (ag2.tools.skills) "pyyaml>=6.0.2", # for tools "fast-depends[pydantic]>=3.0.8,<4.0.0", ] [project.optional-dependencies] # Each extra below corresponds to a `missing_optional_dependency(..., "", ...)` # fallback in the codebase. Extension packages (docker, daytona, e2b, sounddevice, exa-py, # tinyfish, ...) use `missing_additional_dependency` and are installed directly, not as extras. # === Agent protocols === a2a = ["a2a-sdk>=1.3.0,<2"] a2ui = ["jsonschema>=4.18.0,<5", "starlette>=0.40,<2"] acp = [ "agent-client-protocol>=0.12.1,<0.13", # Tool exposure to CLI agents runs an in-process HTTP MCP server, also requiring starlette and uvicorn "ag2[mcp]", "starlette>=0.40,<2", "uvicorn>=0.51.0,<1", ] ag-ui = ["ag-ui-protocol>=1.0,<2"] # === LLM providers (ag2.config) === openai = ["openai>=3.45.0,<4"] anthropic = ["anthropic>=1.15.0,<3"] gemini = [ # Version bounds: local types in gemini_types.py are tested for subset compatibility # with the SDK (local can have extra fields for forward compatibility). "google-genai>=1.68.0,<3.0", # `google.auth.credentials` / `google.oauth2.service_account` in gemini_client.py. # Vertex AI is reached through google-genai's own `vertexai=True`, so # google-cloud-aiplatform is not needed. "google-auth", ] # === Voice / TTS providers (ag2.live) === elevenlabs = ["elevenlabs>=2.73.0,<3"] xai = ["xai-sdk>=1.21.0,<2"] zai = ["zai-sdk>=0.2.3,<1"] mistral = ["mistralai>=3.6.0,<4"] ollama = ["ollama>=0.5.2,<1"] bedrock = ["aiobotocore>=3.9.1,<4"] dashscope = ["dashscope>=1.25.13"] typesafe = ["typesafe-sdk>=0.7.5,<0.8"] redis = ["redis>=5.0,<9"] # SDK only: ag2 builds spans and hands them to whatever SpanProcessor the caller # passes in, so the exporter (console, OTLP over http/grpc, vendor SDK) is the # caller's choice to install. tracing = ["opentelemetry-sdk>=1.20"] metrics = ["prometheus-client>=0.20.0,<0.30.0"] # === Search tools === tavily = ["tavily-python>=0.7.4,<0.9"] ddgs = ["ddgs>=9.0.0,<10"] perplexity = ["perplexityai>=0.30.0,<1"] # === MCP (ag2.mcp) === mcp = [ "mcp>=2.3.0,<3", "mcp-types>=2.3.0,<3", # imported directly to validate tool arguments; `mcp` requires it too, so this # only pins what is already installed "jsonschema>=4.20.0,<5", ] # MCP-UI resources (ag2.mcp_ui) — return renderable UI blobs from tool results mcp-ui = [ "mcp-ui-server>=1.0.0", "mcp>=2.3.0,<3", ] [dependency-groups] # Dev dependency groups (PEP 735). These replace the former dev *extras* and are # NOT published with the package — install with `uv pip install --group ` # (or `uv sync`). Feature extras (openai, gemini, redis, …) stay in # [project.optional-dependencies] because they are part of the published API. # test dependencies test-core = [ "pytest==9.1.1", "pytest-asyncio==1.4.0", "pytest-cov==7.1.0", "pytest-timeout>=2.4.0,<3", "dirty-equals>=0.11,<0.12", "freezegun>=1.5,<2", ] test = [ { include-group = "test-core" }, "docker>=6.0.0,<8", "ag2[mcp]", "ag2[mcp-ui]", # ag2.mcp_ui — MCP-UI resource & action builders "pillow", # test/providers/gemini builds an image fixture; no longer implied by ag2[gemini] "python-dotenv", # provider smoke tests load repo-root .env "respx>=0.23.0,<0.24.0", # search tests # starlette >=1.0 drives `starlette.testclient.TestClient` with httpx2 and warns # (StarletteDeprecationWarning) when it has to fall back to httpx 0.x; the 2.10 # floor is what mcp>=2.3 requires "httpx2>=2.10.0,<3", ] # All optional feature deps needed so the full (non-LLM) test suite can be # collected and run. This has no dev-extra equivalent; it is consumed directly # by the test CI workflow alongside the `test` group. optionals = [ "ag2[redis]", "ag2[ag-ui]", "ag2[a2a]", # ag2.a2a — without it `test/a2a` importorskips itself out of the run "ag2[a2ui]", # provides starlette/jsonschema for AG-UI / a2ui serving tests "ag2[mcp]", # ag2.mcp — serve an Agent as an MCP server "ag2[mcp-ui]", # ag2.mcp_ui — MCP-UI resource & action builders "ag2[acp]", "ag2[tracing]", "ag2[metrics]", # search tools "ag2[tavily,ddgs,perplexity]", # extensions are not shipped as extras; install their packages directly for testing "a2a-sdk[signing,sqlite]", # AgentCard JWS signing (test_card_signing.py) and the SQL task store (test_multi_replica.py); not part of ag2[a2a] "agent-client-protocol[http]", # ag2.acp.remote transports — without them `test/acp/test_remote_*` importorskips itself out of the run "watchdog>=4.0,<7", # DiskKnowledgeStore (ag2.knowledge) "websockets>=14.0,<17", # WsLink (ag2.network) "mistralai[realtime]>=3.6.0,<4", # MistralRealtimeTranscriber, MistralRealtimeTTSConfig (ag2.live) "docker>=6.0.0,<8", # code execution (ag2.extensions.docker) "daytona>=0.171.0,<1", "tenki>=1.4.0,<2", "boxd>=0.3.0,<1", "e2b>=2.0.0,<3", "tinyfish>=0.5,<0.6", "exa-py>=2.12.1,<3", "firecrawl-py>=4.53,<5", "torch>=2.5,<3", "transformers>=4.57,<6", "safetensors>=0.4.3,<1", # providers "ag2[openai]", "ag2[anthropic]", "ag2[bedrock]", "ag2[gemini]", "ag2[ollama]", "ag2[dashscope]", "ag2[xai]", "ag2[zai]", "ag2[mistral]", "ag2[typesafe]", "ag2[elevenlabs]", ] docs = [ "ag2[a2a]", # A2A is exposed in the public API by the optional `ag2.a2a` module "ag2[tracing]", # Tracing is exposed in the public API by the optional `ag2.opentelemetry` / `ag2.middleware` module "ag2[metrics]", # Metrics is exposed in the public API by the optional `ag2.middleware` module "ag2[ag-ui]", # AG-UI is exposed in the public API by the optional `ag2.ag_ui` module # Extensions are not shipped as extras; install their packages directly so docs can import the modules "tinyfish>=0.5,<0.6", "daytona>=0.171.0,<1", "tenki>=1.4.0,<2", "boxd>=0.3.0,<1", "e2b>=2.0.0,<3", "exa-py>=2.12.1,<3", "firecrawl-py>=4.53,<5", "docker>=6.0.0,<8", # Code execution tools are exposed in the public API by the optional `ag2.tools` module "nlip-sdk>=0.1.0,<1", # NLIP is exposed in the public API by the optional `autogen.extensions.nlip` module "nlip-server>=0.1.3,<1", "torch>=2.5,<3", # Used by the optional `ag2.extensions.mi4afa` module "transformers>=4.57,<6", "safetensors>=0.4.3,<1", # Optional dependencies "ag2[openai]", # OpenAI is exposed in the public API by the optional `ag2.config` module "ag2[anthropic]", # Anthropic is exposed in the public API by the optional `ag2.config` module "ag2[gemini]", # Gemini is exposed in the public API by the optional `ag2.config` module "ag2[ollama]", # Ollama is exposed in the public API by the optional `ag2.config` module "ag2[dashscope]", # DashScope is exposed in the public API by the optional `ag2.config` module "ag2[xai]", # xAI is exposed in the public API by the optional `ag2.config` module "ag2[mistral]", # Mistral is exposed in the public API by the optional `ag2.config` module "ag2[redis]", # Redis is exposed in the public API by the optional `ag2.streams` module "watchdog>=4.0,<7", # DiskKnowledgeStore (ag2.knowledge) — additional dependency "websockets>=14.0,<17", # WsLink (ag2.network) — additional dependency "ag2[tavily,ddgs,perplexity]", # Search tools are exposed by optional search modules "mkdocs-material==9.7.7", "mkdocs-literate-nav==0.6.3", "mdx-include==1.4.2", # ToDo: currently problematic and cannot be upgraded "mkdocs-git-revision-date-localized-plugin==1.6.0", "typer>=0.22,<0.28", "mkdocs-minify-plugin==0.8.0", "mkdocs-macros-plugin==1.5.0", # includes with variables "mkdocs-glightbox==0.5.2", # img zoom "mkdocs-redirects==1.2.3", # required for handling redirects natively "jinja2==3.1.6", "pyyaml==6.0.3", "mcp>=2.3.0,<3", ] types = [ "mypy==2.4.0", "types-PyYAML", # `ag2/a2ui` registers format checks with jsonschema, which ships no inline types "types-jsonschema", # aiobotocore is data-driven and ships no runtime param classes. The extra pulls # `botocore-stubs` in, which `ag2/config/bedrock/bedrock_client.py` needs under # `disallow_any_unimported`. Type-check only — never a published constraint. "types-aiobotocore[bedrock-runtime]", { include-group = "test" }, "ag2[openai, anthropic, gemini, mistral, xai, zai, ollama, dashscope, typesafe, elevenlabs, a2a, ag-ui, acp]", # search tools in `ag2/tools/search`, checked under `disallow_any_unimported` "ag2[ddgs, perplexity]", # `ag2/streams/redis`, `ag2/knowledge/redis.py` and `ag2/middleware/builtin/metrics.py`: # without these their imports are Any, which hides real errors (redis) or trips # `disallow_any_unimported` (prometheus_client) "ag2[redis, metrics]", # `ag2/textual.py` subclasses textual's `App`; unresolved, the base class is Any # and trips `disallow_any_unimported`. Not an extra: the TUI is an optional demo. "textual", # Extensions are not shipped as extras. Unresolved, their packages are Any, which hides # real errors or trips `disallow_any_unimported` in `ag2/extensions/*`. "daytona>=0.171.0,<1", "tenki>=1.4.0,<2", "boxd>=0.3.0,<1", "exa-py>=2.12.1,<3", "nlip-sdk>=0.1.0,<1", "nlip-server>=0.1.3,<1", "e2b>=2.0.0,<3", ] lint = ["ruff==0.16.10", "codespell==2.4.3", "zizmor==1.30.1"] dev = [ { include-group = "lint" }, { include-group = "test" }, { include-group = "types" }, { include-group = "docs" }, "prek==0.5.4", ] [tool.pytest.ini_options] addopts = '-m "not conda"' timeout = 45 testpaths = ["test"] markers = [ "conda: test related to conda forge distribution", "all", "openai", "openai_realtime", "gemini", "gemini_realtime", "anthropic", "zai", "mistral", "typesafe", "deepseek", "cosmosdb", "ollama", "bedrock", "cerebras", "cohere", "groq", "together", "integration", "aux_neg_flag", # optional deps "redis", "docker", "docs", "rag", "jupyter_executor", "retrievechat", "retrievechat_pgvector", "retrievechat_mongodb", "retrievechat_qdrant", "graph_rag_falkor_db", "neo4j", "mcp", "interop", "browser_use", "crawl4ai", "google_api", "google_search", "websockets", "elevenlabs", "commsagent_discord", "commsagent_slack", "commsagent-telegram", "lmm", "tracing", ] [tool.coverage.run] branch = true source = ["ag2"] # Store source paths relative to the repo root (with normalized separators) so # coverage data recorded on different runners (Linux/macOS/Windows) can be # merged by `coverage combine` into a single report before upload. relative_files = true [tool.ruff] fix = true line-length = 120 target-version = 'py311' # Exclude a variety of commonly ignored directories. exclude = [ ".eggs", ".git", ".mypy_cache", ".ruff_cache", "__pypackages__", "_build", "build", "dist", "docs", # This file needs to be either upgraded or removed and therefore should be # ignore from type checking for now "math_utils\\.py$", "setup_*.py", ] preview = true [tool.ruff.lint] # Enable Pyflakes `E` and `F` codes by default. select = [ "E", # pycodestyle errors https://docs.astral.sh/ruff/rules/#error-e "W", # pycodestyle warnings https://docs.astral.sh/ruff/rules/#warning-w "C90", # mccabe https://docs.astral.sh/ruff/rules/#mccabe-c90 "N", # pep8-naming https://docs.astral.sh/ruff/rules/#pep8-naming-n # "D", # pydocstyle https://docs.astral.sh/ruff/rules/#pydocstyle-d "I", # isort https://docs.astral.sh/ruff/rules/#isort-i "F", # pyflakes https://docs.astral.sh/ruff/rules/#pyflakes-f "ASYNC", # flake8-async https://docs.astral.sh/ruff/rules/#flake8-async-async "C4", # flake8-comprehensions https://docs.astral.sh/ruff/rules/#flake8-comprehensions-c4 # "B", # flake8-bugbear https://docs.astral.sh/ruff/rules/#flake8-bugbear-b "Q", # flake8-quotes https://docs.astral.sh/ruff/rules/#flake8-quotes-q # "T20", # flake8-print https://docs.astral.sh/ruff/rules/#flake8-print-t20 "SIM", # flake8-simplify https://docs.astral.sh/ruff/rules/#flake8-simplify-sim # "PT", # flake8-pytest-style https://docs.astral.sh/ruff/rules/#flake8-pytest-style-pt # "PTH", # flake8-use-pathlib https://docs.astral.sh/ruff/rules/#flake8-use-pathlib-pth # "TCH", # flake8-type-checking https://docs.astral.sh/ruff/rules/#flake8-type-checking-tch # "RUF", # Ruff-specific rules https://docs.astral.sh/ruff/rules/#ruff-specific-rules-ruf # "PERF", # Perflint https://docs.astral.sh/ruff/rules/#perflint-perf "RUF022", # Sort __all__ https://docs.astral.sh/ruff/rules/unsorted-dunder-all/ "UP", # pyupgrade https://docs.astral.sh/ruff/rules/#pydocstyle-d "D417", # undocumented-param https://docs.astral.sh/ruff/rules/undocumented-param/ ] ignore = [ "E501", "F403", "C901", "E402", "E721", "E501", # line too long, handled by formatter later "C901", # too complex "ASYNC109", "ASYNC240", "UP042", # Preserve the str() behavior of existing public str/Enum types. ] [tool.ruff.lint.mccabe] # Unlike Flake8, default to a complexity level of 10. max-complexity = 10 [tool.ruff.lint.isort] case-sensitive = true [tool.ruff.format] docstring-code-format = true [tool.ruff.lint.pydocstyle] convention = "google" [tool.mypy] python_version = "3.11" # The `ag2` tree is not yet strict-clean. Strict mypy coverage is being # (re)introduced module-by-module; until then mypy is scoped to modules # that already pass under `strict = true`. files = [ "ag2/__init__.py", "ag2/_import_utils.py", "ag2/_model_input.py", "ag2/_replay.py", "ag2/_run_lifecycle.py", "ag2/_telemetry_consts.py", "ag2/a2a", "ag2/a2ui", "ag2/acp", "ag2/ag_ui", "ag2/agent.py", "ag2/aggregate.py", "ag2/annotations.py", "ag2/assembly.py", "ag2/compact.py", "ag2/config", "ag2/context.py", "ag2/eval", "ag2/events", "ag2/exceptions.py", "ag2/extensions", "ag2/files", "ag2/history.py", "ag2/hitl.py", "ag2/knowledge", "ag2/live", "ag2/mcp", "ag2/mcp_ui", "ag2/middleware", "ag2/mypy.py", "ag2/network/__init__.py", "ag2/network/adapters/__init__.py", "ag2/network/auth.py", "ag2/network/channel.py", "ag2/network/client/__init__.py", "ag2/network/client/agent_client.py", "ag2/network/client/channel.py", "ag2/network/client/checkpoint.py", "ag2/network/client/human_client.py", "ag2/network/client/inject.py", "ag2/network/client/network_client.py", "ag2/network/client/task.py", "ag2/network/client/tools/__init__.py", "ag2/network/client/tools/say.py", "ag2/network/envelope.py", "ag2/network/errors.py", "ag2/network/handoff.py", "ag2/network/hub/arbiter.py", "ag2/network/hub/expectations.py", "ag2/network/hub/layout.py", "ag2/network/hub/sweepers.py", "ag2/network/identity.py", "ag2/network/ids.py", "ag2/network/policies.py", "ag2/network/remote.py", "ag2/network/rule.py", "ag2/network/task_mirror.py", "ag2/network/testing.py", "ag2/network/transport", "ag2/network/views", "ag2/network/workflow_helpers.py", "ag2/observers", "ag2/plugin.py", "ag2/policies", "ag2/response", "ag2/scopes.py", "ag2/spec.py", "ag2/stream.py", "ag2/streams", "ag2/task.py", "ag2/testing.py", "ag2/textual.py", "ag2/tools", "ag2/types.py", "ag2/usage.py", "ag2/utils.py", "ag2/version.py", "ag2/watch.py", ] strict = true ignore_missing_imports = true install_types = true non_interactive = true plugins = ["pydantic.mypy", "ag2.mypy"] # remove after all files in the repo are fixed follow_imports = "silent" # from https://blog.wolt.com/engineering/2021/09/30/professional-grade-mypy-configuration/ disallow_untyped_defs = true no_implicit_optional = true check_untyped_defs = true warn_return_any = true show_error_codes = true warn_unused_ignores = false disallow_incomplete_defs = true disallow_untyped_decorators = true disallow_any_unimported = true # `perplexity.types` re-exports its result types without an `__all__`, which # strict mode reads as "not exported". The alternative is importing from # `perplexity.generated.*`, which is internal. [[tool.mypy.overrides]] module = "perplexity.*" implicit_reexport = true [[tool.mypy.overrides]] module = "xai_sdk.*" follow_untyped_imports = true # `mcp-ui-server` is fully annotated but ships no `py.typed` marker, so mypy # would hand us `Any` from every name we import. Following it uses its real types. [[tool.mypy.overrides]] module = "mcp_ui_server.*" follow_untyped_imports = true # `nlip-sdk` / `nlip-server` are annotated (pydantic models, typed session hooks) but # ship no `py.typed` marker; `ag2/extensions/nlip` subclasses and returns their types. [[tool.mypy.overrides]] module = ["nlip_sdk.*", "nlip_server.*"] follow_untyped_imports = true # `ag2/extensions/mi4afa` is built on torch / transformers. They are too heavy for the # `types` group, so the type-check job runs without them and their types are Any there. [[tool.mypy.overrides]] module = "ag2.extensions.mi4afa.*" disallow_any_unimported = false # AG-UI's SDK does not ship typing metadata, so values crossing that boundary # are Any even though this module's own functions remain checked strictly. [[tool.mypy.overrides]] module = "ag2.ag_ui.*" disable_error_code = ["no-any-unimported"] # `sounddevice` ships no stubs, so its types arrive as Any at this module's boundary. [[tool.mypy.overrides]] module = "ag2.live.sound_device" disable_error_code = ["no-any-unimported"] # numpy's bundled stubs use 3.12-only `type` syntax, which mypy cannot parse # under python_version = "3.11"; skip analysing numpy entirely (it is not our code). [[tool.mypy.overrides]] module = "numpy.*" follow_imports = "skip" follow_imports_for_stubs = true [tool.codespell] skip = "*.js,*.map,*.pdf,*.po,*.ts,*.json,*.svg" quiet-level = 3 ignore-words-list = "ans,crasher,fulfilment,iif"