Files
hive/core/framework/schemas/agent_config.py
T
2026-04-07 13:42:39 -07:00

193 lines
5.7 KiB
Python

"""Declarative agent configuration schema.
Allows defining agents via JSON/YAML config files instead of Python modules.
The ``AgentConfig`` model is the top-level schema loaded from ``agent.json``.
The runner detects this format by checking for a ``name`` key at the top level.
Template variables
------------------
System prompts and identity_prompt support ``{{variable_name}}`` placeholders.
These are resolved at load time from ``AgentConfig.variables``.
"""
from __future__ import annotations
from pydantic import BaseModel, Field
class ToolAccessConfig(BaseModel):
"""Declarative tool access policy.
Controls which tools a node/agent has access to.
* ``all`` -- every tool from the registry.
* ``explicit`` -- only tools listed in ``allowed`` (default; empty = zero tools).
* ``none`` -- no tools at all.
"""
policy: str = Field(
default="explicit",
description="One of: 'all', 'explicit', 'none'.",
)
allowed: list[str] = Field(
default_factory=list,
description="Tool names when policy='explicit'.",
)
denied: list[str] = Field(
default_factory=list,
description="Tool names to deny (applied after allowed).",
)
class NodeConfig(BaseModel):
"""Declarative node definition."""
id: str
name: str | None = None
description: str | None = None
node_type: str = Field(
default="event_loop",
description="event_loop",
)
system_prompt: str | None = None
tools: ToolAccessConfig = Field(default_factory=ToolAccessConfig)
model: str | None = None
input_keys: list[str] = Field(default_factory=list)
output_keys: list[str] = Field(default_factory=list)
nullable_output_keys: list[str] = Field(default_factory=list)
max_iterations: int = 30
max_node_visits: int = 1
client_facing: bool = False
success_criteria: str | None = None
failure_criteria: str | None = None
skip_judge: bool = False
max_retries: int | None = None
class EdgeConfig(BaseModel):
"""Declarative edge definition."""
from_node: str = Field(description="Source node ID.")
to_node: str = Field(description="Target node ID.")
condition: str = Field(
default="on_success",
description="always | on_success | on_failure | conditional | llm_decide",
)
condition_expr: str | None = None
input_mapping: dict[str, str] = Field(default_factory=dict)
priority: int = 1
class GoalConfig(BaseModel):
"""Simplified goal definition for declarative config."""
description: str
success_criteria: list[str] = Field(default_factory=list)
constraints: list[str] = Field(default_factory=list)
class EntryPointConfig(BaseModel):
"""Entry point configuration."""
id: str = "default"
name: str = "Default"
entry_node: str | None = None # defaults to AgentConfig.entry_node
trigger_type: str = Field(
default="manual",
description="manual | scheduled | timer",
)
trigger_config: dict = Field(default_factory=dict)
isolation_level: str = "shared"
max_concurrent: int | None = None
class MCPServerRef(BaseModel):
"""Reference to an MCP server to connect for this agent."""
name: str
config: dict | None = None
class MetadataConfig(BaseModel):
"""Agent metadata for display / intro messages."""
intro_message: str = ""
class AgentConfig(BaseModel):
"""Top-level declarative agent configuration.
Load from ``agent.json`` and pass to
:func:`framework.runner.runner.load_agent_config` to build the
``GraphSpec`` + ``Goal`` pair.
Example (YAML)::
name: lead-enrichment-agent
version: 1.0.0
variables:
spreadsheet_id: "1ZVx..."
sheet_name: "contacts"
goal:
description: "Enrich leads in Google Sheets"
success_criteria:
- "All unprocessed leads enriched"
constraints:
- "Browser-only research"
identity_prompt: |
You are the Lead Enrichment Agent...
nodes:
- id: start
tools: {policy: explicit, allowed: [google_sheets_get_values]}
system_prompt: |
Spreadsheet ID: {{spreadsheet_id}}
...
"""
name: str
version: str = "1.0.0"
description: str | None = None
metadata: MetadataConfig = Field(default_factory=MetadataConfig)
# Template variables -- substituted into prompts via {{var_name}}
variables: dict[str, str] = Field(default_factory=dict)
# Goal
goal: GoalConfig
# Graph structure
nodes: list[NodeConfig]
edges: list[EdgeConfig]
entry_node: str
terminal_nodes: list[str] = Field(default_factory=list)
pause_nodes: list[str] = Field(default_factory=list)
# Entry points (if omitted, a single "default" manual entry is created)
entry_points: list[EntryPointConfig] = Field(default_factory=list)
# Agent-level tool defaults (nodes inherit unless they override)
tools: ToolAccessConfig = Field(default_factory=ToolAccessConfig)
mcp_servers: list[MCPServerRef] = Field(default_factory=list)
# LLM / execution
model: str | None = None
max_tokens: int = 4096
conversation_mode: str = "continuous"
identity_prompt: str = ""
loop_config: dict = Field(
default_factory=lambda: {
"max_iterations": 100,
"max_tool_calls_per_turn": 30,
"max_context_tokens": 32000,
},
)
# Pipeline overrides (per-agent, merged with global config)
pipeline: dict = Field(
default_factory=dict,
description="Per-agent pipeline stage overrides. Same format as global pipeline config.",
)
# Resource limits
max_cost_per_run: float | None = None