<h1 align="center">
<a href="https://prompts.chat">
Before opening a PR, read and follow `.github/AI_POLICY.md`.
Loading actions...
<a href="https://prompts.chat">
TypeScript and ESLint rules that MUST be followed when creating, modifying, or reviewing any file under apps/frontend/, including .ts, .tsx, .js, and .jsx files. Also apply when discussing frontend linting, type safety, or ESLint configuration.
risks
Before opening a PR, read and follow .github/AI_POLICY.md.
from __future__ import annotations.pathlib.Path internally. Public signatures should accept str | os.PathLike[str].autogen.beta.* are for common APIs that are broadly reusable across scenarios and core agent flows.
Good: autogen.beta.[Input] — common structures usable in await agent.ask(Input()) and as tool results.
Bad: autogen.beta.middleware.BaseMiddleware — this is advanced/specialized and should be imported only when implementing custom middleware.# === BAD - import inside function ===
def execute_tool():
from .tool import Tool
...
# === GOOD - top-level import ===
from .tool import Tool
def execute_tool():
...
# === BAD - function will be created each call ===
def execute_tool():
def _inner_function():
pass
_inner_function()
# === GOOD - function created once, executed each call ===
def execute_tool():
_inner_function()
def _inner_function():
pass
# === GOOD - decorator executed import time, so we can use closure functions here ===
def decorator(func):
def wrapper():
return func()
return wrapper
# === BAD - create directory in initial method ===
class KnowledgeStore:
def __init__(self, path: str | os.PathLike[str]) -> None:
self.path = Path(path)
# side effect - directory creation
self.path.parent.mkdir(parents=True, exist_ok=True)
def run(self) -> None:
...
# === GOOD - create directory in runtime method ===
class KnowledgeStore:
def __init__(self, path: str | os.PathLike[str]) -> None:
self.path = Path(path)
def run(self) -> None:
self.path.parent.mkdir(parents=True, exist_ok=True)
...
autogen/beta/ is a protocol-driven async agent framework. Key modules:
| Module | Purpose | Key Exports |
|---|---|---|
agent.py | Core agent loop and reply handling | Agent, AgentReply |
annotations.py | Type annotations for dependency injection | Context, Inject, Variable |
context.py | Runtime context (stream, dependencies, variables, prompt) | Context dataclass, Stream protocol |
stream.py | In-memory event pub/sub | MemoryStream, SubStream |
events/ | Event types for the agent loop | BaseEvent, ModelRequest, ModelResponse, ToolCallEvent, ToolResultEvent, Usage, … |
config/ | LLM provider clients (see below) | ModelConfig, LLMClient, AnthropicConfig, OpenAIConfig, GeminiConfig, … |
tools/ | Tool system — builtin + user-defined | tool, Toolkit, ToolResult, CodeExecutionTool, ShellTool, WebSearchTool, … |
tools/subagents/ | Agent-to-agent delegation | subagent_tool, run_task, persistent_stream, StreamFactory |
eval/ | Offline evaluation framework | run, scorer, EvalTarget, Suite, Task, Trace, RunResult, Feedback, BudgetThresholds, plus prebuilts under eval.scorers |
middleware/ | Request/response interception | BaseMiddleware, Middleware, LoggingMiddleware, RetryMiddleware, TokenLimiter, HistoryLimiter, … |
response/ | Structured output validation | ResponseSchema, PromptedSchema, ResponseProto, response_schema |
history.py | Conversation history storage | History, Storage, MemoryStorage |
hitl.py | Human-in-the-loop hooks | — |
streams/ | Persistent stream backends (e.g. Redis) | — |
autogen.beta)Top-level modules:
autogen.beta - top-level module with most basic functionalityautogen.beta.types - Type aliases and constantsautogen.beta.config - LLM provider clients (see below)autogen.beta.tools - Tool system — builtin + user-defined (see below)autogen.beta.tools.subagents - Agent-to-agent delegation (see below)autogen.beta.testing - Testing utilitiesautogen.beta.middleware - Request/response interception (see below)autogen.beta.observer - Reusable observer implementationsautogen.beta.eval - Offline evaluation framework (datasets, scorers, runner, persistence)Advanced modules:
autogen.beta.events - Event types for the agent loopautogen.beta.streams - Persistent stream backends (e.g. Redis)autogen.beta.watch - Watch system for triggering observersautogen.beta.knowledge - Knowledge managementautogen.beta.plugin - Plugin systemAll implementations must be re-exported from their public module's __init__.py and listed in __all__. If an implementation requires third-party dependencies, wrap the import in a try/except ImportError block and register a missing-dependency fallback so users get a clear install hint instead of an unexplained ImportError (see autogen/beta/config/__init__.py, autogen/beta/middleware/builtin/__init__.py as the reference pattern). Two fallbacks exist:
missing_optional_dependency, which hints pip install "ag2[<extra>]".autogen/beta/extensions/) are not shipped as extras — declare their third-party packages as additional dependencies and fall back via missing_additional_dependency, which hints the upstream package directly (e.g. pip install "daytona>=0.171.0,<1").LLMClient, ModelConfig, Stream, Storage, Tool are all Protocol classes — implementations satisfy them structurally.ask, tool execution, LLM calls) are async. Sync tool functions run via sync_to_thread.Stream as typed events.Context, Inject, and Variable annotations; resolution is handled by fast_depends.Builtin tools live in autogen/beta/tools/builtin/. Each tool has:
ToolSchema dataclass (provider-neutral capability flag)Tool class (constructs the schema, resolves Variables)version as the public parameter name on Tool constructors for provider-versioned tools (e.g., WebSearchTool(version="web_search_20260209")). The schema field may use a more specific name internally (e.g., web_search_version) — the Tool maps between them.Variable for deferred context resolution (e.g., max_uses: int | Variable | None).MemoryTool, CodeExecutionTool) should still accept a version keyword argument to allow version pinning.autogen/beta/config/{provider}/mappers.py convert ToolSchema instances to provider-specific API dicts. Use t.version instead of hardcoding version strings.autogen/beta/tools/builtin/{tool_name}.py with a ToolSchema dataclass and Tool class.elif isinstance(t, YourToolSchema) branch returning the provider-specific dict.raise UnsupportedToolError(t.type, "provider") handles it.Variable parameters, add 2 tests to test/beta/tools/test_resolve.py: one resolving from context, one raising KeyError on missing.Subagent tools live in autogen/beta/tools/subagents/ and are imported from autogen.beta.tools.subagents (not re-exported from autogen.beta.tools).
| File | Purpose |
|---|---|
run_task.py | run_task(), TaskResult — execute an agent as a sub-task |
subagent_tool.py | subagent_tool(), StreamFactory — wrap an agent as a callable tool |
persistent_stream.py | persistent_stream() — StreamFactory that reuses a stream across calls |
Agent.as_tool(description, name?, stream?, middleware?) is a convenience method that delegates to subagent_tool(). It creates a tool named task_{agent.name} with parameters objective (required) and context (optional).
run_subtask / run_subtasksSub-task delegation is off by default (tasks=False). Pass tasks=TaskConfig(...) to opt in, and the Agent gains a run_subtask(task) and a run_subtasks(tasks=[...], parallel=True) tool. Each call spawns a subtask Agent that:
TaskConfig.include_tools / exclude_tools, extendable via extra_tools).tasks=False (the default), so the subtask has no run_subtask tools — recursive delegation is structurally impossible. No depth limiting required.MemoryStream; child events do not leak into the parent's stream beyond TaskStarted / TaskCompleted / TaskFailed lifecycle events.The LLM is told (via the tool description) that run_subtask may be invoked multiple times in parallel within a single response, encouraging the parallel-tool-use pattern Anthropic recommends.
persistent_stream() returns a StreamFactory that gives the same agent a consistent stream across multiple invocations within a context. It stores the stream ID in context.dependencies keyed by ag:{agent.name}:stream, and reuses the parent stream's storage backend.
Use it when sub-task history should accumulate across calls rather than starting fresh each time:
agent.as_tool(description="...", stream=persistent_stream())
| What | Behavior | Why |
|---|---|---|
| Dependencies | Shallow-copied (dict.copy()) | Isolated at the top level; mutable values are still shared by reference. Treat dependencies as read-only inside subtasks. |
| Variables | Copied (new dict); not synced back | Concurrent siblings via asyncio.gather would race-clobber a shared dict — last-writer-wins is silent data loss. Each subtask's mutations stay scoped to it. |
| History | Fresh stream per call | Clean context; LLM passes relevant info via context parameter |
| Tool inheritance | Parent's user-supplied tools (filtered by TaskConfig) | Subtasks need real capabilities to do work; child has no run_subtask tools so recursion is impossible |
Provider clients live in autogen/beta/config/{provider}/. Each provider has at least three files:
config.py — a @dataclass(slots=True) implementing the ModelConfig protocol{provider}_client.py — a concrete class satisfying the LLMClient protocol (async __call__)mappers.py — pure functions for converting messages, tools, response schemas, and usage between internal and provider-specific formatsCreateOptions TypedDict for generation params. It wraps the provider's async SDK client.__call__ converts messages/tools via mappers, calls the provider API, normalises the response into ModelResponse with Usage.ModelMessageChunk / ModelReasoning events via context.send() while accumulating the full response.convert_messages(messages) -> provider format — converts Sequence[BaseEvent] to the provider's message list.tool_to_api(tool) -> dict — converts a ToolSchema to the provider's tool definition. Use isinstance() checks; unsupported tools fall through to raise UnsupportedToolError(t.type, "provider").response_proto_to_*(schema) — converts ResponseProto to the provider's structured-output format. Use _ensure_additional_properties_false() where the provider requires it.normalize_usage(usage) -> Usage — maps provider-specific usage keys to the normalised Usage dataclass.autogen/beta/config/{provider}/ with config.py, {provider}_client.py, and mappers.py.autogen/beta/config/__init__.py: import inside a try/except ImportError block and add a _missing_optional_dependency_config fallback.__all__.test/beta/config/{provider}/