diff --git "a/data/repository_analysis.json" "b/data/repository_analysis.json" new file mode 100644--- /dev/null +++ "b/data/repository_analysis.json" @@ -0,0 +1,13506 @@ +{ + "project_context": { + "project_name": "Laddr", + "description": "
\n\n\"Laddr\n\n**Your framework for building scalable multi-agent systems**\n\n[![License: Apache 2.0](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0)\n[![Python 3.10+](https://img.shields.io/badge/python-3.10+-blue.svg)](https://www.python.org/downloads/)\n[![Docker](https://img.shields.io/badge/docker-optional-blue.svg)](https://www.docker.com/)\n[![FastAPI](https:/", + "main_technologies": [ + "bandit", + "black", + "boto3-stubs", + "build", + "click", + "docker", + "fastapi", + "google-generativeai", + "jinja2", + "laddr" + ], + "architecture_style": "modular", + "key_modules": [], + "dependencies": { + "laddr": ">=0.8.1", + "build": "any", + "twine": "any", + "pydantic": ">=2.11.9", + "pydantic-settings": ">=2.10.1", + "requests": ">=2.31.0", + "python-dotenv": ">=1.1.1", + "fastapi": ">=0.115.0", + "uvicorn": "any", + "redis": ">=5.0.0", + "sqlalchemy": ">=2.0.0", + "psycopg2-binary": ">=2.9.0", + "click": ">=8.1.7", + "rich": ">=13.7.0", + "pyyaml": ">=6.0.1", + "jinja2": ">=3.1.0", + "openai": ">=1.13.3", + "litellm": "any", + "google-generativeai": ">=0.8.0", + "mkdocs": ">=1.6.1", + "mkdocs-material": ">=9.5.0", + "mkdocstrings": ">=0.26.0", + "mkdocstrings-python": ">=1.0.0", + "mkdocs-autorefs": ">=1.2.0", + "mkdocs-click": ">=0.8.0", + "pytest": ">=8.0.0", + "pytest-asyncio": ">=0.23.0", + "pytest-subprocess": ">=1.5.3", + "pytest-recording": ">=0.13.4", + "pytest-randomly": ">=4.0.1", + "pytest-timeout": ">=2.4.0", + "pytest-xdist": ">=3.8.0", + "pytest-split": ">=0.10.0", + "vcrpy": "==7.0.0", + "black": ">=24.0.0", + "ruff": ">=0.5.0", + "mypy": ">=1.11.0", + "pre-commit": ">=4.3.0", + "bandit": ">=1.8.6", + "types-requests": ">=2.31.0", + "types-pyyaml": ">=6.0.0", + "types-regex": ">=2024.11.6", + "types-appdirs": ">=1.4.0", + "types-psycopg2": ">=2.9.21", + "types-pymysql": ">=1.1.0", + "boto3-stubs": "any", + "docker": ">=7.0.0" + } + }, + "project_structure": { + "SECURITY.md": "SECURITY.md", + "CODE_OF_CONDUCT.md": "CODE_OF_CONDUCT.md", + "mkdocs.yml": "mkdocs.yml", + "README.md": "README.md", + "docs": { + "cli.md": "docs/cli.md", + "api-reference.md": "docs/api-reference.md", + "local-runtime.md": "docs/local-runtime.md", + "getting-started.md": "docs/getting-started.md", + "agent-config.md": "docs/agent-config.md", + "migrations.md": "docs/migrations.md", + "tool-config.md": "docs/tool-config.md" + }, + "Assets": {}, + ".github": { + "pull_request_template.md": ".github/pull_request_template.md", + "ISSUE_TEMPLATE": { + "feature_request.md": ".github/ISSUE_TEMPLATE/feature_request.md", + "bug_report.md": ".github/ISSUE_TEMPLATE/bug_report.md" + } + }, + "dashboard": { + "package.json": "dashboard/package.json", + "tsconfig.node.json": "dashboard/tsconfig.node.json", + "tsconfig.json": "dashboard/tsconfig.json", + "package-lock.json": "dashboard/package-lock.json", + "README.md": "dashboard/README.md", + "src": { + "components": {}, + "pages": { + "Agents": {} + }, + "lib": { + "hooks": {}, + "queries": {} + } + }, + "public": {} + }, + "lib": { + "laddr": { + "README.md": "lib/laddr/README.md", + "src": { + "laddr": { + "llms.py": "lib/laddr/src/laddr/llms.py", + "__init__.py": "lib/laddr/src/laddr/__init__.py", + "core": { + "tooling.py": "lib/laddr/src/laddr/core/tooling.py", + "config.py": "lib/laddr/src/laddr/core/config.py", + "runtime_entry.py": "lib/laddr/src/laddr/core/runtime_entry.py", + "cache.py": "lib/laddr/src/laddr/core/cache.py", + "agent_runtime.py": "lib/laddr/src/laddr/core/agent_runtime.py", + "mcp_client.py": "lib/laddr/src/laddr/core/mcp_client.py", + "storage.py": "lib/laddr/src/laddr/core/storage.py", + "database.py": "lib/laddr/src/laddr/core/database.py", + "message_bus.py": "lib/laddr/src/laddr/core/message_bus.py", + "__init__.py": "lib/laddr/src/laddr/core/__init__.py", + "system_tools.py": "lib/laddr/src/laddr/core/system_tools.py", + "llm.py": "lib/laddr/src/laddr/core/llm.py" + }, + "cli": { + "main.py": "lib/laddr/src/laddr/cli/main.py", + "__init__.py": "lib/laddr/src/laddr/cli/__init__.py", + "templates": {}, + "commands": { + "prompt.py": "lib/laddr/src/laddr/cli/commands/prompt.py", + "run.py": "lib/laddr/src/laddr/cli/commands/run.py", + "add.py": "lib/laddr/src/laddr/cli/commands/add.py", + "management.py": "lib/laddr/src/laddr/cli/commands/management.py", + "infra.py": "lib/laddr/src/laddr/cli/commands/infra.py", + "check.py": "lib/laddr/src/laddr/cli/commands/check.py", + "__init__.py": "lib/laddr/src/laddr/cli/commands/__init__.py", + "init.py": "lib/laddr/src/laddr/cli/commands/init.py" + }, + "utils": { + "config.py": "lib/laddr/src/laddr/cli/utils/config.py", + "logger.py": "lib/laddr/src/laddr/cli/utils/logger.py", + "templates.py": "lib/laddr/src/laddr/cli/utils/templates.py", + "docker.py": "lib/laddr/src/laddr/cli/utils/docker.py", + "errors.py": "lib/laddr/src/laddr/cli/utils/errors.py", + "__init__.py": "lib/laddr/src/laddr/cli/utils/__init__.py" + } + }, + "api": { + "main.py": "lib/laddr/src/laddr/api/main.py", + "__init__.py": "lib/laddr/src/laddr/api/__init__.py" + } + } + } + } + } + }, + "code_elements": [ + { + "type": "class", + "name": "_LLMWithDefaults", + "filepath": "lib/laddr/src/laddr/llms.py", + "start_line": 18, + "end_line": 43, + "code": "class _LLMWithDefaults:\n \"\"\"Wrap an LLM backend with default params for generate().\"\"\"\n", + "docstring": "Wrap an LLM backend with default params for generate().", + "dependencies": [], + "complexity": 5, + "business_context": "generate, llm", + "imports": [], + "called_functions": [], + "parent_class": "", + "decorators": [], + "parameters": [], + "return_type": "" + }, + { + "type": "method", + "name": "_LLMWithDefaults.__init__", + "filepath": "lib/laddr/src/laddr/llms.py", + "start_line": 21, + "end_line": 23, + "code": " def __init__(self, backend: Any, default_params: Dict[str, Any] | None = None):\n self._backend = backend\n self.default_params = dict(default_params or {})", + "docstring": "", + "dependencies": [ + "dict" + ], + "complexity": 2, + "business_context": "", + "imports": [], + "called_functions": [ + "dict" + ], + "parent_class": "_LLMWithDefaults", + "decorators": [], + "parameters": [ + { + "name": "self", + "type": "Any" + }, + { + "name": "backend", + "type": "Any" + }, + { + "name": "default_params", + "type": "Dict[str, Any] | None" + } + ], + "return_type": "None" + }, + { + "type": "function", + "name": "openai", + "filepath": "lib/laddr/src/laddr/llms.py", + "start_line": 46, + "end_line": 53, + "code": "def openai(model: str | None = None, temperature: float | None = None, base_url: str | None = None) -> _LLMWithDefaults:\n backend = OpenAILLM(api_key=None, model=model, base_url=base_url)\n defaults: Dict[str, Any] = {}\n if temperature is not None:\n defaults[\"temperature\"] = temperature\n if model is not None:\n defaults[\"model\"] = model\n return _LLMWithDefaults(backend, defaults)", + "docstring": "", + "dependencies": [ + "_LLMWithDefaults", + "OpenAILLM" + ], + "complexity": 3, + "business_context": "", + "imports": [], + "called_functions": [ + "_LLMWithDefaults", + "OpenAILLM" + ], + "parent_class": "", + "decorators": [], + "parameters": [ + { + "name": "model", + "type": "str | None" + }, + { + "name": "temperature", + "type": "float | None" + }, + { + "name": "base_url", + "type": "str | None" + } + ], + "return_type": "_LLMWithDefaults" + }, + { + "type": "function", + "name": "gemini", + "filepath": "lib/laddr/src/laddr/llms.py", + "start_line": 56, + "end_line": 63, + "code": "def gemini(model: str | None = None, temperature: float | None = None) -> _LLMWithDefaults:\n backend = GeminiLLM(api_key=None, model=model)\n defaults: Dict[str, Any] = {}\n if temperature is not None:\n defaults[\"temperature\"] = temperature\n if model is not None:\n defaults[\"model\"] = model\n return _LLMWithDefaults(backend, defaults)", + "docstring": "", + "dependencies": [ + "GeminiLLM", + "_LLMWithDefaults" + ], + "complexity": 3, + "business_context": "", + "imports": [], + "called_functions": [ + "GeminiLLM", + "_LLMWithDefaults" + ], + "parent_class": "", + "decorators": [], + "parameters": [ + { + "name": "model", + "type": "str | None" + }, + { + "name": "temperature", + "type": "float | None" + } + ], + "return_type": "_LLMWithDefaults" + }, + { + "type": "function", + "name": "anthropic", + "filepath": "lib/laddr/src/laddr/llms.py", + "start_line": 66, + "end_line": 73, + "code": "def anthropic(model: str | None = None, temperature: float | None = None) -> _LLMWithDefaults:\n backend = AnthropicLLM(api_key=None, model=model)\n defaults: Dict[str, Any] = {}\n if temperature is not None:\n defaults[\"temperature\"] = temperature\n if model is not None:\n defaults[\"model\"] = model\n return _LLMWithDefaults(backend, defaults)", + "docstring": "", + "dependencies": [ + "_LLMWithDefaults", + "AnthropicLLM" + ], + "complexity": 3, + "business_context": "", + "imports": [], + "called_functions": [ + "_LLMWithDefaults", + "AnthropicLLM" + ], + "parent_class": "", + "decorators": [], + "parameters": [ + { + "name": "model", + "type": "str | None" + }, + { + "name": "temperature", + "type": "float | None" + } + ], + "return_type": "_LLMWithDefaults" + }, + { + "type": "function", + "name": "groq", + "filepath": "lib/laddr/src/laddr/llms.py", + "start_line": 76, + "end_line": 93, + "code": "def groq(model: str | None = None, temperature: float | None = None) -> _LLMWithDefaults:\n \"\"\"\n Create a Groq LLM backend instance.\n \n Args:\n model: Model name (default: llama-3.3-70b-versatile)\n temperature: Generation temperature\n \n Example:\n llm = groq(model=\"llama-3.3-70b-versatile\", temperature=0.5)\n \"\"\"\n backend = GroqLLM(api_key=None, model=model)\n defaults: Dict[str, Any] = {}\n if temperature is not None:\n defaults[\"temperature\"] = temperature\n if model is not None:\n defaults[\"model\"] = model\n return _LLMWithDefaults(backend, defaults)", + "docstring": "Create a Groq LLM backend instance.\n\nArgs:\n model: Model name (default: llama-3.3-70b-versatile)\n temperature: Generation temperature\n\nExample:\n llm = groq(model=\"llama-3.3-70b-versatile\", temperature=0.5)", + "dependencies": [ + "_LLMWithDefaults", + "GroqLLM" + ], + "complexity": 3, + "business_context": "create, llm", + "imports": [], + "called_functions": [ + "_LLMWithDefaults", + "GroqLLM" + ], + "parent_class": "", + "decorators": [], + "parameters": [ + { + "name": "model", + "type": "str | None" + }, + { + "name": "temperature", + "type": "float | None" + } + ], + "return_type": "_LLMWithDefaults" + }, + { + "type": "function", + "name": "grok", + "filepath": "lib/laddr/src/laddr/llms.py", + "start_line": 96, + "end_line": 113, + "code": "def grok(model: str | None = None, temperature: float | None = None) -> _LLMWithDefaults:\n \"\"\"\n Create a xAI Grok LLM backend instance.\n \n Args:\n model: Model name (default: grok-beta)\n temperature: Generation temperature\n \n Example:\n llm = grok(model=\"grok-beta\", temperature=0.7)\n \"\"\"\n backend = GrokLLM(api_key=None, model=model)\n defaults: Dict[str, Any] = {}\n if temperature is not None:\n defaults[\"temperature\"] = temperature\n if model is not None:\n defaults[\"model\"] = model\n return _LLMWithDefaults(backend, defaults)", + "docstring": "Create a xAI Grok LLM backend instance.\n\nArgs:\n model: Model name (default: grok-beta)\n temperature: Generation temperature\n\nExample:\n llm = grok(model=\"grok-beta\", temperature=0.7)", + "dependencies": [ + "_LLMWithDefaults", + "GrokLLM" + ], + "complexity": 3, + "business_context": "create, llm", + "imports": [], + "called_functions": [ + "_LLMWithDefaults", + "GrokLLM" + ], + "parent_class": "", + "decorators": [], + "parameters": [ + { + "name": "model", + "type": "str | None" + }, + { + "name": "temperature", + "type": "float | None" + } + ], + "return_type": "_LLMWithDefaults" + }, + { + "type": "function", + "name": "ollama", + "filepath": "lib/laddr/src/laddr/llms.py", + "start_line": 116, + "end_line": 128, + "code": "def ollama(model: str | None = None, temperature: float | None = None, base_url: str | None = None) -> _LLMWithDefaults:\n \"\"\"Create a local Ollama backend instance.\n\n Example:\n llm = ollama(model=\"gemma2:2b\", base_url=\"http://localhost:11434\")\n \"\"\"\n backend = OllamaLLM(base_url=base_url, model=model)\n defaults: Dict[str, Any] = {}\n if temperature is not None:\n defaults[\"temperature\"] = temperature\n if model is not None:\n defaults[\"model\"] = model\n return _LLMWithDefaults(backend, defaults)", + "docstring": "Create a local Ollama backend instance.\n\nExample:\n llm = ollama(model=\"gemma2:2b\", base_url=\"http://localhost:11434\")", + "dependencies": [ + "OllamaLLM", + "_LLMWithDefaults" + ], + "complexity": 3, + "business_context": "create, llm", + "imports": [], + "called_functions": [ + "OllamaLLM", + "_LLMWithDefaults" + ], + "parent_class": "", + "decorators": [], + "parameters": [ + { + "name": "model", + "type": "str | None" + }, + { + "name": "temperature", + "type": "float | None" + }, + { + "name": "base_url", + "type": "str | None" + } + ], + "return_type": "_LLMWithDefaults" + }, + { + "type": "function", + "name": "Agent", + "filepath": "lib/laddr/src/laddr/__init__.py", + "start_line": 48, + "end_line": 97, + "code": "def Agent(\n *,\n name: str,\n role: str,\n goal: str,\n backstory: str | None = None,\n tools: \"ToolRegistry | list[object] | None\" = None,\n llm: object | None = None,\n queue: object | None = None,\n instructions: str | None = None,\n is_coordinator: bool | None = None,\n available_agents: list[str] | None = None,\n max_iterations: int | None = None, # Maximum autonomous iterations before forced finish\n max_tool_calls: int | None = None, # Maximum successful tool calls before forced finish\n max_retries: int | None = None, # reserved for future behavior\n timeout: int | None = None, # reserved for future behavior\n trace_enabled: bool = True, # Enable/disable tracing for this agent\n trace_mask: list[str] | set[str] | None = None, # Event types to exclude from traces\n):\n \"\"\"Factory for user-friendly Agent(...) syntax.\n\n Creates a CoreAgent instance under the hood using AgentConfig and LaddrConfig.\n Unrecognized parameters like max_retries/timeout are currently accepted for\n forward-compatibility but not used directly.\n \"\"\"\n cfg = LaddrConfig()\n a_cfg = AgentConfig(\n name=name,\n role=role,\n goal=goal,\n backstory=backstory,\n max_iterations=max_iterations if max_iterations is not None else 5,\n )\n agent = CoreAgent(\n a_cfg,\n cfg,\n tools=tools, # CoreAgent accepts ToolRegistry or list of callables\n llm=llm,\n queue=queue,\n instructions=instructions,\n is_coordinator=is_coordinator,\n available_agents=available_agents,\n )\n # Set tracing configuration directly on instance\n agent._trace_enabled = bool(trace_enabled)\n agent._trace_mask = set(trace_mask or [])\n # Set max_tool_calls limit if specified\n if max_tool_calls is not None:\n agent._max_tool_calls = max_tool_calls\n return agent", + "docstring": "Factory for user-friendly Agent(...) syntax.\n\nCreates a CoreAgent instance under the hood using AgentConfig and LaddrConfig.\nUnrecognized parameters like max_retries/timeout are currently accepted for\nforward-compatibility but not used directly.", + "dependencies": [ + "set", + "CoreAgent", + "AgentConfig", + "bool", + "LaddrConfig" + ], + "complexity": 3, + "business_context": "queue, llm, tool, agent", + "imports": [], + "called_functions": [ + "set", + "CoreAgent", + "AgentConfig", + "bool", + "LaddrConfig" + ], + "parent_class": "", + "decorators": [], + "parameters": [], + "return_type": "None" + }, + { + "type": "class", + "name": "Tool", + "filepath": "lib/laddr/src/laddr/core/tooling.py", + "start_line": 27, + "end_line": 68, + "code": "class Tool:\n \"\"\"\n Tool metadata and callable.\n \n Represents a single tool that can be invoked by agents.\n \"\"\"\n\n name: str\n func: Callable\n description: str\n input_model: type | None = None\n # Optional explicit JSON schema for parameters (for LLM function-calling UIs)\n parameters_schema: dict | None = None\n # Tracing controls\n trace_enabled: bool = True\n trace_mask: set[str] = field(default_factory=set)\n", + "docstring": "Tool metadata and callable.\n\nRepresents a single tool that can be invoked by agents.", + "dependencies": [], + "complexity": 10, + "business_context": "llm, tool", + "imports": [], + "called_functions": [], + "parent_class": "", + "decorators": [ + "dataclass" + ], + "parameters": [], + "return_type": "" + }, + { + "type": "method", + "name": "Tool.validate_inputs", + "filepath": "lib/laddr/src/laddr/core/tooling.py", + "start_line": 44, + "end_line": 59, + "code": " def validate_inputs(self, inputs: dict[str, Any]) -> dict[str, Any]:\n \"\"\"\n Validate inputs against input_model if available.\n \n Returns validated inputs or raises ValidationError.\n \"\"\"\n if self.input_model is None:\n return inputs\n\n if BaseModel and issubclass(self.input_model, BaseModel):\n # Pydantic validation\n validated = self.input_model(**inputs)\n return validated.model_dump()\n\n # No validation available\n return inputs", + "docstring": "Validate inputs against input_model if available.\n\nReturns validated inputs or raises ValidationError.", + "dependencies": [ + "issubclass", + "input_model", + "model_dump" + ], + "complexity": 4, + "business_context": "validate", + "imports": [], + "called_functions": [ + "issubclass", + "input_model", + "model_dump" + ], + "parent_class": "Tool", + "decorators": [], + "parameters": [ + { + "name": "self", + "type": "Any" + }, + { + "name": "inputs", + "type": "dict[str, Any]" + } + ], + "return_type": "dict[str, Any]" + }, + { + "type": "method", + "name": "Tool.invoke", + "filepath": "lib/laddr/src/laddr/core/tooling.py", + "start_line": 61, + "end_line": 68, + "code": " def invoke(self, **kwargs) -> Any:\n \"\"\"\n Invoke the tool with validated inputs.\n \n Returns tool execution result.\n \"\"\"\n validated = self.validate_inputs(kwargs)\n return self.func(**validated)", + "docstring": "Invoke the tool with validated inputs.\n\nReturns tool execution result.", + "dependencies": [ + "validate_inputs", + "func" + ], + "complexity": 1, + "business_context": "tool", + "imports": [], + "called_functions": [ + "validate_inputs", + "func" + ], + "parent_class": "Tool", + "decorators": [], + "parameters": [ + { + "name": "self", + "type": "Any" + } + ], + "return_type": "Any" + }, + { + "type": "class", + "name": "ToolRegistry", + "filepath": "lib/laddr/src/laddr/core/tooling.py", + "start_line": 134, + "end_line": 214, + "code": "class ToolRegistry:\n \"\"\"\n Registry for managing available tools.\n \n Supports registration, lookup, and listing of tools.\n \"\"\"\n", + "docstring": "Registry for managing available tools.\n\nSupports registration, lookup, and listing of tools.", + "dependencies": [], + "complexity": 8, + "business_context": "", + "imports": [], + "called_functions": [], + "parent_class": "", + "decorators": [], + "parameters": [], + "return_type": "" + }, + { + "type": "method", + "name": "ToolRegistry.__init__", + "filepath": "lib/laddr/src/laddr/core/tooling.py", + "start_line": 141, + "end_line": 143, + "code": " def __init__(self):\n self._tools: dict[str, Tool] = {}\n self._aliases: dict[str, str] = {} # alias -> canonical name mapping", + "docstring": "", + "dependencies": [], + "complexity": 1, + "business_context": "tool", + "imports": [], + "called_functions": [], + "parent_class": "ToolRegistry", + "decorators": [], + "parameters": [ + { + "name": "self", + "type": "Any" + } + ], + "return_type": "None" + }, + { + "type": "method", + "name": "ToolRegistry.register", + "filepath": "lib/laddr/src/laddr/core/tooling.py", + "start_line": 145, + "end_line": 184, + "code": " def register(self, tool_obj: Tool | Callable, name: str | None = None, aliases: list[str] | None = None) -> None:\n \"\"\"\n Register a tool with optional aliases.\n \n Args:\n tool_obj: Tool instance or callable to register\n name: Optional explicit name (overrides tool_obj.name)\n aliases: Optional list of alias names that resolve to this tool\n \"\"\"\n # Determine the canonical name and tool object\n if isinstance(tool_obj, Tool):\n tool = tool_obj\n canonical_name = name or tool.name\n # Update the tool's name if explicit name provided\n if name and name != tool.name:\n tool = Tool(name=name, func=tool.func, description=tool.description)\n elif hasattr(tool_obj, \"__laddr_tool__\"):\n tool = tool_obj.__laddr_tool__\n canonical_name = name or tool.name\n # Update the tool's name if explicit name provided\n if name and name != tool.name:\n tool = Tool(name=name, func=tool.func, description=tool.description)\n elif callable(tool_obj):\n # Auto-wrap callable without decorator\n canonical_name = name or tool_obj.__name__\n tool = Tool(\n name=canonical_name,\n func=tool_obj,\n description=(tool_obj.__doc__ or \"\").strip().split(\"\\n\")[0]\n )\n else:\n raise ValueError(f\"Cannot register {tool_obj} as a tool\")\n\n # Register the tool under its canonical name\n self._tools[canonical_name] = tool\n\n # Register aliases if provided\n if aliases:\n for alias in aliases:\n self._aliases[alias] = canonical_name", + "docstring": "Register a tool with optional aliases.\n\nArgs:\n tool_obj: Tool instance or callable to register\n name: Optional explicit name (overrides tool_obj.name)\n aliases: Optional list of alias names that resolve to this tool", + "dependencies": [ + "isinstance", + "hasattr", + "ValueError", + "split", + "strip", + "Tool", + "callable" + ], + "complexity": 14, + "business_context": "update, tool", + "imports": [], + "called_functions": [ + "isinstance", + "hasattr", + "ValueError", + "split", + "strip", + "Tool", + "callable" + ], + "parent_class": "ToolRegistry", + "decorators": [], + "parameters": [ + { + "name": "self", + "type": "Any" + }, + { + "name": "tool_obj", + "type": "Tool | Callable" + }, + { + "name": "name", + "type": "str | None" + }, + { + "name": "aliases", + "type": "list[str] | None" + } + ], + "return_type": "None" + }, + { + "type": "method", + "name": "ToolRegistry.get", + "filepath": "lib/laddr/src/laddr/core/tooling.py", + "start_line": 186, + "end_line": 198, + "code": " def get(self, name: str) -> Tool | None:\n \"\"\"\n Get a tool by name or alias.\n \n Args:\n name: Tool name (canonical or alias)\n \n Returns:\n Tool instance or None if not found\n \"\"\"\n # Check if name is an alias first\n canonical_name = self._aliases.get(name, name)\n return self._tools.get(canonical_name)", + "docstring": "Get a tool by name or alias.\n\nArgs:\n name: Tool name (canonical or alias)\n\nReturns:\n Tool instance or None if not found", + "dependencies": [ + "get" + ], + "complexity": 1, + "business_context": "tool", + "imports": [], + "called_functions": [ + "get" + ], + "parent_class": "ToolRegistry", + "decorators": [], + "parameters": [ + { + "name": "self", + "type": "Any" + }, + { + "name": "name", + "type": "str" + } + ], + "return_type": "Tool | None" + }, + { + "type": "method", + "name": "ToolRegistry.list", + "filepath": "lib/laddr/src/laddr/core/tooling.py", + "start_line": 200, + "end_line": 202, + "code": " def list(self) -> list[Tool]:\n \"\"\"List all registered tools (excludes aliases).\"\"\"\n return list(self._tools.values())", + "docstring": "List all registered tools (excludes aliases).", + "dependencies": [ + "values", + "list" + ], + "complexity": 1, + "business_context": "tool", + "imports": [], + "called_functions": [ + "values", + "list" + ], + "parent_class": "ToolRegistry", + "decorators": [], + "parameters": [ + { + "name": "self", + "type": "Any" + } + ], + "return_type": "list[Tool]" + }, + { + "type": "method", + "name": "ToolRegistry.list_names", + "filepath": "lib/laddr/src/laddr/core/tooling.py", + "start_line": 204, + "end_line": 206, + "code": " def list_names(self) -> list[str]:\n \"\"\"List all canonical tool names (excludes aliases).\"\"\"\n return list(self._tools.keys())", + "docstring": "List all canonical tool names (excludes aliases).", + "dependencies": [ + "list", + "keys" + ], + "complexity": 1, + "business_context": "tool", + "imports": [], + "called_functions": [ + "list", + "keys" + ], + "parent_class": "ToolRegistry", + "decorators": [], + "parameters": [ + { + "name": "self", + "type": "Any" + } + ], + "return_type": "list[str]" + }, + { + "type": "method", + "name": "ToolRegistry.list_all_names", + "filepath": "lib/laddr/src/laddr/core/tooling.py", + "start_line": 208, + "end_line": 210, + "code": " def list_all_names(self) -> list[str]:\n \"\"\"List all names including aliases (for debugging).\"\"\"\n return list(self._tools.keys()) + list(self._aliases.keys())", + "docstring": "List all names including aliases (for debugging).", + "dependencies": [ + "list", + "keys" + ], + "complexity": 1, + "business_context": "", + "imports": [], + "called_functions": [ + "list", + "keys" + ], + "parent_class": "ToolRegistry", + "decorators": [], + "parameters": [ + { + "name": "self", + "type": "Any" + } + ], + "return_type": "list[str]" + }, + { + "type": "method", + "name": "ToolRegistry.has", + "filepath": "lib/laddr/src/laddr/core/tooling.py", + "start_line": 212, + "end_line": 214, + "code": " def has(self, name: str) -> bool:\n \"\"\"Check if a tool exists (checks both canonical names and aliases).\"\"\"\n return name in self._tools or name in self._aliases", + "docstring": "Check if a tool exists (checks both canonical names and aliases).", + "dependencies": [], + "complexity": 2, + "business_context": "tool", + "imports": [], + "called_functions": [], + "parent_class": "ToolRegistry", + "decorators": [], + "parameters": [ + { + "name": "self", + "type": "Any" + }, + { + "name": "name", + "type": "str" + } + ], + "return_type": "bool" + }, + { + "type": "function", + "name": "tool", + "filepath": "lib/laddr/src/laddr/core/tooling.py", + "start_line": 71, + "end_line": 131, + "code": "def tool(\n name: str | None = None,\n description: str | None = None,\n *,\n trace: bool = True,\n trace_mask: list[str] | None = None,\n parameters: dict | None = None,\n):\n \"\"\"\n Decorator to mark a function as an agent tool.\n \n Usage:\n @tool(name=\"web_search\", description=\"Search the web\")\n def search(query: str) -> dict:\n return {\"results\": [...]}\n \n Or with Pydantic input model:\n class SearchInput(BaseModel):\n query: str\n limit: int = 10\n \n @tool()\n def search(inputs: SearchInput) -> dict:\n return {\"results\": [...]}\n \n The decorator attaches metadata to the function as __laddr_tool__.\n \"\"\"\n def decorator(func: Callable) -> Callable:\n # Infer name from function if not provided\n tool_name = name or func.__name__\n\n # Infer description from docstring if not provided\n tool_description = description or (func.__doc__ or \"\").strip().split(\"\\n\")[0]\n\n # Try to extract input model from type hints\n input_model = None\n try:\n hints = get_type_hints(func)\n # Check if first parameter has a Pydantic BaseModel hint\n params = list(inspect.signature(func).parameters.values())\n if params and BaseModel:\n first_param_hint = hints.get(params[0].name)\n if first_param_hint and inspect.isclass(first_param_hint) and issubclass(first_param_hint, BaseModel):\n input_model = first_param_hint\n except Exception:\n pass\n\n # Attach metadata\n func.__laddr_tool__ = Tool(\n name=tool_name,\n func=func,\n description=tool_description,\n input_model=input_model,\n parameters_schema=parameters,\n trace_enabled=bool(trace),\n trace_mask=set(trace_mask or []),\n )\n\n return func\n\n return decorator", + "docstring": "Decorator to mark a function as an agent tool.\n\nUsage:\n @tool(name=\"web_search\", description=\"Search the web\")\n def search(query: str) -> dict:\n return {\"results\": [...]}\n\nOr with Pydantic input model:\n class SearchInput(BaseModel):\n query: str\n limit: int = 10\n \n @tool()\n def search(inputs: SearchInput) -> dict:\n return {\"results\": [...]}\n\nThe decorator attaches metadata to the function as __laddr_tool__.", + "dependencies": [ + "values", + "get", + "issubclass", + "set", + "list", + "bool", + "split", + "isclass", + "strip", + "Tool", + "signature", + "get_type_hints" + ], + "complexity": 10, + "business_context": "query, tool, agent", + "imports": [ + "inspect" + ], + "called_functions": [ + "values", + "get", + "issubclass", + "set", + "list", + "bool", + "split", + "isclass", + "strip", + "Tool", + "signature", + "get_type_hints" + ], + "parent_class": "", + "decorators": [], + "parameters": [ + { + "name": "name", + "type": "str | None" + }, + { + "name": "description", + "type": "str | None" + } + ], + "return_type": "None" + }, + { + "type": "function", + "name": "discover_tools", + "filepath": "lib/laddr/src/laddr/core/tooling.py", + "start_line": 217, + "end_line": 260, + "code": "def discover_tools(agent_name: str) -> ToolRegistry:\n \"\"\"\n Auto-discover tools for an agent from agents..tools package.\n \n Scans for:\n 1. Functions decorated with @tool\n 2. Functions named 'run' (fallback)\n \n Args:\n agent_name: Name of the agent (e.g., \"researcher\")\n \n Returns:\n ToolRegistry with discovered tools\n \"\"\"\n registry = ToolRegistry()\n\n try:\n # Import the tools package\n tools_module = importlib.import_module(f\"agents.{agent_name}.tools\")\n\n # First, scan the package module itself (for tools defined in __init__.py)\n for name, obj in inspect.getmembers(tools_module):\n if hasattr(obj, \"__laddr_tool__\"):\n registry.register(obj)\n elif name == \"run\" and callable(obj):\n registry.register(obj)\n\n # Then, scan all submodules in the tools package\n if hasattr(tools_module, \"__path__\"):\n for _, module_name, _ in pkgutil.iter_modules(tools_module.__path__):\n try:\n module = importlib.import_module(f\"agents.{agent_name}.tools.{module_name}\")\n\n for name, obj in inspect.getmembers(module):\n if hasattr(obj, \"__laddr_tool__\"):\n registry.register(obj)\n elif name == \"run\" and callable(obj):\n registry.register(obj)\n except Exception:\n continue\n except ImportError:\n pass\n\n return registry", + "docstring": "Auto-discover tools for an agent from agents..tools package.\n\nScans for:\n1. Functions decorated with @tool\n2. Functions named 'run' (fallback)\n\nArgs:\n agent_name: Name of the agent (e.g., \"researcher\")\n\nReturns:\n ToolRegistry with discovered tools", + "dependencies": [ + "ToolRegistry", + "hasattr", + "import_module", + "getmembers", + "iter_modules", + "callable", + "register" + ], + "complexity": 13, + "business_context": "tool, agent", + "imports": [ + "pkgutil", + "importlib", + "inspect" + ], + "called_functions": [ + "ToolRegistry", + "hasattr", + "import_module", + "getmembers", + "iter_modules", + "callable", + "register" + ], + "parent_class": "", + "decorators": [], + "parameters": [ + { + "name": "agent_name", + "type": "str" + } + ], + "return_type": "ToolRegistry" + }, + { + "type": "function", + "name": "bind_tools", + "filepath": "lib/laddr/src/laddr/core/tooling.py", + "start_line": 263, + "end_line": 309, + "code": "def bind_tools(agent_instance: Any, tools: list[str | Callable]) -> None:\n \"\"\"\n Bind tools to an agent instance for explicit, readable tool registration.\n \n Supports:\n - String names: auto-import from agents..tools.\n - Callables: register directly\n - Decorated functions: extract Tool metadata\n \n Usage:\n from agents.researcher.tools import web_search\n bind_tools(self, [web_search.run, \"summarize\"])\n \n Args:\n agent_instance: Agent instance with .tools registry\n tools: List of tool names (str) or callables\n \"\"\"\n if not hasattr(agent_instance, \"tools\"):\n raise AttributeError(\"Agent must have .tools ToolRegistry\")\n \n agent_name = agent_instance.config.name\n \n for tool_ref in tools:\n if isinstance(tool_ref, str):\n # Auto-import from agents..tools.\n try:\n module = importlib.import_module(f\"agents.{agent_name}.tools.{tool_ref}\")\n # Look for 'run' function or decorated tool\n tool_func = None\n if hasattr(module, \"run\"):\n tool_func = module.run\n else:\n # Find first decorated function\n for name, obj in inspect.getmembers(module):\n if hasattr(obj, \"__laddr_tool__\"):\n tool_func = obj\n break\n \n if tool_func:\n agent_instance.tools.register(tool_func)\n except ImportError as e:\n raise ImportError(f\"Could not import tool '{tool_ref}' for agent '{agent_name}': {e}\")\n elif callable(tool_ref):\n # Register callable directly\n agent_instance.tools.register(tool_ref)\n else:\n raise ValueError(f\"Tool must be string or callable, got {type(tool_ref)}\")", + "docstring": "Bind tools to an agent instance for explicit, readable tool registration.\n\nSupports:\n- String names: auto-import from agents..tools.\n- Callables: register directly\n- Decorated functions: extract Tool metadata\n\nUsage:\n from agents.researcher.tools import web_search\n bind_tools(self, [web_search.run, \"summarize\"])\n\nArgs:\n agent_instance: Agent instance with .tools registry\n tools: List of tool names (str) or callables", + "dependencies": [ + "isinstance", + "ImportError", + "hasattr", + "import_module", + "getmembers", + "ValueError", + "AttributeError", + "callable", + "type", + "register" + ], + "complexity": 10, + "business_context": "config, tool, agent", + "imports": [ + "importlib", + "inspect" + ], + "called_functions": [ + "isinstance", + "ImportError", + "hasattr", + "import_module", + "getmembers", + "ValueError", + "AttributeError", + "callable", + "type", + "register" + ], + "parent_class": "", + "decorators": [], + "parameters": [ + { + "name": "agent_instance", + "type": "Any" + }, + { + "name": "tools", + "type": "list[str | Callable]" + } + ], + "return_type": "None" + }, + { + "type": "function", + "name": "create_tool_schema", + "filepath": "lib/laddr/src/laddr/core/tooling.py", + "start_line": 364, + "end_line": 421, + "code": "def create_tool_schema(tool: Tool) -> dict[str, Any]:\n \"\"\"\n Create a JSON schema for a tool (for LLM function calling).\n \n Returns OpenAI-style function schema.\n \"\"\"\n # If user provided an explicit parameters schema via decorator, honor it directly\n if getattr(tool, \"parameters_schema\", None):\n return {\n \"name\": tool.name,\n \"description\": tool.description,\n \"parameters\": tool.parameters_schema,\n }\n\n schema = {\n \"name\": tool.name,\n \"description\": tool.description,\n \"parameters\": {\n \"type\": \"object\",\n \"properties\": {},\n \"required\": []\n }\n }\n\n # Try to extract parameters from input_model or function signature\n if tool.input_model and BaseModel and issubclass(tool.input_model, BaseModel):\n # Use Pydantic schema\n model_schema = tool.input_model.model_json_schema()\n schema[\"parameters\"][\"properties\"] = model_schema.get(\"properties\", {})\n schema[\"parameters\"][\"required\"] = model_schema.get(\"required\", [])\n else:\n # Fallback: extract from function signature\n sig = inspect.signature(tool.func)\n for param_name, param in sig.parameters.items():\n if param_name in [\"self\", \"cls\"]:\n continue\n\n param_schema = {\"type\": \"string\"} # Default type\n\n # Try to infer type from annotation\n if param.annotation != inspect.Parameter.empty:\n if param.annotation == int:\n param_schema[\"type\"] = \"integer\"\n elif param.annotation == float:\n param_schema[\"type\"] = \"number\"\n elif param.annotation == bool:\n param_schema[\"type\"] = \"boolean\"\n elif param.annotation == list:\n param_schema[\"type\"] = \"array\"\n elif param.annotation == dict:\n param_schema[\"type\"] = \"object\"\n\n schema[\"parameters\"][\"properties\"][param_name] = param_schema\n\n if param.default == inspect.Parameter.empty:\n schema[\"parameters\"][\"required\"].append(param_name)\n\n return schema", + "docstring": "Create a JSON schema for a tool (for LLM function calling).\n\nReturns OpenAI-style function schema.", + "dependencies": [ + "get", + "issubclass", + "append", + "getattr", + "items", + "signature", + "model_json_schema" + ], + "complexity": 13, + "business_context": "create, tool, llm", + "imports": [ + "inspect" + ], + "called_functions": [ + "get", + "issubclass", + "append", + "getattr", + "items", + "signature", + "model_json_schema" + ], + "parent_class": "", + "decorators": [], + "parameters": [ + { + "name": "tool", + "type": "Tool" + } + ], + "return_type": "dict[str, Any]" + }, + { + "type": "function", + "name": "decorator", + "filepath": "lib/laddr/src/laddr/core/tooling.py", + "start_line": 98, + "end_line": 129, + "code": " def decorator(func: Callable) -> Callable:\n # Infer name from function if not provided\n tool_name = name or func.__name__\n\n # Infer description from docstring if not provided\n tool_description = description or (func.__doc__ or \"\").strip().split(\"\\n\")[0]\n\n # Try to extract input model from type hints\n input_model = None\n try:\n hints = get_type_hints(func)\n # Check if first parameter has a Pydantic BaseModel hint\n params = list(inspect.signature(func).parameters.values())\n if params and BaseModel:\n first_param_hint = hints.get(params[0].name)\n if first_param_hint and inspect.isclass(first_param_hint) and issubclass(first_param_hint, BaseModel):\n input_model = first_param_hint\n except Exception:\n pass\n\n # Attach metadata\n func.__laddr_tool__ = Tool(\n name=tool_name,\n func=func,\n description=tool_description,\n input_model=input_model,\n parameters_schema=parameters,\n trace_enabled=bool(trace),\n trace_mask=set(trace_mask or []),\n )\n\n return func", + "docstring": "", + "dependencies": [ + "values", + "get", + "issubclass", + "set", + "list", + "bool", + "split", + "isclass", + "strip", + "Tool", + "signature", + "get_type_hints" + ], + "complexity": 10, + "business_context": "tool", + "imports": [ + "inspect" + ], + "called_functions": [ + "values", + "get", + "issubclass", + "set", + "list", + "bool", + "split", + "isclass", + "strip", + "Tool", + "signature", + "get_type_hints" + ], + "parent_class": "", + "decorators": [], + "parameters": [ + { + "name": "func", + "type": "Callable" + } + ], + "return_type": "Callable" + }, + { + "type": "class", + "name": "LaddrConfig", + "filepath": "lib/laddr/src/laddr/core/config.py", + "start_line": 66, + "end_line": 229, + "code": "class LaddrConfig(BaseSettings):\n \"\"\"\n Environment-based configuration for Laddr runtime.\n \n Supports pluggable backends for all infrastructure:\n - queue_backend: \"redis\" (default) | \"memory\"\n - db_backend: \"postgres\" (default) | \"sqlite\"\n - llm_backend: \"noop\" (default) | \"openai\" | \"anthropic\" | \"gemini\"\n - cache_backend: \"inmemory\" (default) | \"redis\"\n \"\"\"\n\n # Pluggable backend selection\n queue_backend: str = Field(default=\"redis\", description=\"Message queue backend\")\n db_backend: str = Field(default=\"postgres\", description=\"Database backend\")\n llm_backend: str = Field(default=\"noop\", description=\"LLM backend (noop=echo)\")\n llm_model: str | None = Field(default=None, description=\"LLM model name (optional, backend-specific)\")\n openai_base_url: str | None = Field(default=None, description=\"OpenAI-compatible base URL (e.g., vLLM)\")\n cache_backend: str = Field(default=\"inmemory\", description=\"Cache backend\")\n\n # Database connection\n database_url: str = Field(", + "docstring": "Environment-based configuration for Laddr runtime.\n\nSupports pluggable backends for all infrastructure:\n- queue_backend: \"redis\" (default) | \"memory\"\n- db_backend: \"postgres\" (default) | \"sqlite\"\n- llm_backend: \"noop\" (default) | \"openai\" | \"anthropic\" | \"gemini\"\n- cache_backend: \"inmemory\" (default) | \"redis\"", + "dependencies": [ + "BaseSettings" + ], + "complexity": 40, + "business_context": "database, llm, queue, cache, runtime", + "imports": [], + "called_functions": [], + "parent_class": "", + "decorators": [], + "parameters": [], + "return_type": "" + }, + { + "type": "class", + "name": "QueueBackend", + "filepath": "lib/laddr/src/laddr/core/config.py", + "start_line": 235, + "end_line": 245, + "code": "class QueueBackend(Protocol):\n \"\"\"Protocol for message queue backends.\"\"\"\n\n async def register_agent(self, name: str, metadata: dict) -> bool: ...\n async def publish_task(self, agent_name: str, task: dict) -> str: ...\n async def publish_response(self, task_id: str, response: dict) -> bool: ...\n async def consume_tasks(self, agent_name: str, block_ms: int, count: int) -> list[dict]: ...\n async def wait_for_response(self, task_id: str, timeout_sec: int) -> dict | None: ...\n async def get_registered_agents(self) -> dict[str, dict]: ...\n async def get_queue_depth(self, agent_name: str) -> int: ...\n async def health_check(self) -> bool: ...", + "docstring": "Protocol for message queue backends.", + "dependencies": [ + "Protocol" + ], + "complexity": 9, + "business_context": "message, queue", + "imports": [], + "called_functions": [], + "parent_class": "", + "decorators": [], + "parameters": [], + "return_type": "" + }, + { + "type": "class", + "name": "DatabaseBackend", + "filepath": "lib/laddr/src/laddr/core/config.py", + "start_line": 248, + "end_line": 259, + "code": "class DatabaseBackend(Protocol):\n \"\"\"Protocol for database backends.\"\"\"\n", + "docstring": "Protocol for database backends.", + "dependencies": [ + "Protocol" + ], + "complexity": 10, + "business_context": "database", + "imports": [], + "called_functions": [], + "parent_class": "", + "decorators": [], + "parameters": [], + "return_type": "" + }, + { + "type": "method", + "name": "DatabaseBackend.create_job", + "filepath": "lib/laddr/src/laddr/core/config.py", + "start_line": 251, + "end_line": 251, + "code": " def create_job(self, job_id: str, pipeline: str, inputs: dict) -> None: ...", + "docstring": "", + "dependencies": [], + "complexity": 1, + "business_context": "", + "imports": [], + "called_functions": [], + "parent_class": "DatabaseBackend", + "decorators": [], + "parameters": [ + { + "name": "self", + "type": "Any" + }, + { + "name": "job_id", + "type": "str" + }, + { + "name": "pipeline", + "type": "str" + }, + { + "name": "inputs", + "type": "dict" + } + ], + "return_type": "None" + }, + { + "type": "method", + "name": "DatabaseBackend.save_result", + "filepath": "lib/laddr/src/laddr/core/config.py", + "start_line": 252, + "end_line": 252, + "code": " def save_result(self, job_id: str, outputs: dict) -> None: ...", + "docstring": "", + "dependencies": [], + "complexity": 1, + "business_context": "", + "imports": [], + "called_functions": [], + "parent_class": "DatabaseBackend", + "decorators": [], + "parameters": [ + { + "name": "self", + "type": "Any" + }, + { + "name": "job_id", + "type": "str" + }, + { + "name": "outputs", + "type": "dict" + } + ], + "return_type": "None" + }, + { + "type": "method", + "name": "DatabaseBackend.get_result", + "filepath": "lib/laddr/src/laddr/core/config.py", + "start_line": 253, + "end_line": 253, + "code": " def get_result(self, job_id: str) -> dict | None: ...", + "docstring": "", + "dependencies": [], + "complexity": 1, + "business_context": "", + "imports": [], + "called_functions": [], + "parent_class": "DatabaseBackend", + "decorators": [], + "parameters": [ + { + "name": "self", + "type": "Any" + }, + { + "name": "job_id", + "type": "str" + } + ], + "return_type": "dict | None" + }, + { + "type": "method", + "name": "DatabaseBackend.list_jobs", + "filepath": "lib/laddr/src/laddr/core/config.py", + "start_line": 254, + "end_line": 254, + "code": " def list_jobs(self, limit: int) -> list[dict]: ...", + "docstring": "", + "dependencies": [], + "complexity": 1, + "business_context": "", + "imports": [], + "called_functions": [], + "parent_class": "DatabaseBackend", + "decorators": [], + "parameters": [ + { + "name": "self", + "type": "Any" + }, + { + "name": "limit", + "type": "int" + } + ], + "return_type": "list[dict]" + }, + { + "type": "method", + "name": "DatabaseBackend.append_trace", + "filepath": "lib/laddr/src/laddr/core/config.py", + "start_line": 255, + "end_line": 255, + "code": " def append_trace(self, job_id: str, agent_name: str, event_type: str, payload: dict) -> None: ...", + "docstring": "", + "dependencies": [], + "complexity": 1, + "business_context": "", + "imports": [], + "called_functions": [], + "parent_class": "DatabaseBackend", + "decorators": [], + "parameters": [ + { + "name": "self", + "type": "Any" + }, + { + "name": "job_id", + "type": "str" + }, + { + "name": "agent_name", + "type": "str" + }, + { + "name": "event_type", + "type": "str" + }, + { + "name": "payload", + "type": "dict" + } + ], + "return_type": "None" + }, + { + "type": "method", + "name": "DatabaseBackend.memory_put", + "filepath": "lib/laddr/src/laddr/core/config.py", + "start_line": 256, + "end_line": 256, + "code": " def memory_put(self, agent_name: str, key: str, value: Any, job_id: str | None = None) -> None: ...", + "docstring": "", + "dependencies": [], + "complexity": 1, + "business_context": "", + "imports": [], + "called_functions": [], + "parent_class": "DatabaseBackend", + "decorators": [], + "parameters": [ + { + "name": "self", + "type": "Any" + }, + { + "name": "agent_name", + "type": "str" + }, + { + "name": "key", + "type": "str" + }, + { + "name": "value", + "type": "Any" + }, + { + "name": "job_id", + "type": "str | None" + } + ], + "return_type": "None" + }, + { + "type": "method", + "name": "DatabaseBackend.memory_get", + "filepath": "lib/laddr/src/laddr/core/config.py", + "start_line": 257, + "end_line": 257, + "code": " def memory_get(self, agent_name: str, key: str, job_id: str | None = None) -> Any: ...", + "docstring": "", + "dependencies": [], + "complexity": 1, + "business_context": "", + "imports": [], + "called_functions": [], + "parent_class": "DatabaseBackend", + "decorators": [], + "parameters": [ + { + "name": "self", + "type": "Any" + }, + { + "name": "agent_name", + "type": "str" + }, + { + "name": "key", + "type": "str" + }, + { + "name": "job_id", + "type": "str | None" + } + ], + "return_type": "Any" + }, + { + "type": "method", + "name": "DatabaseBackend.register_agent", + "filepath": "lib/laddr/src/laddr/core/config.py", + "start_line": 258, + "end_line": 258, + "code": " def register_agent(self, agent_name: str, metadata: dict) -> None: ...", + "docstring": "", + "dependencies": [], + "complexity": 1, + "business_context": "", + "imports": [], + "called_functions": [], + "parent_class": "DatabaseBackend", + "decorators": [], + "parameters": [ + { + "name": "self", + "type": "Any" + }, + { + "name": "agent_name", + "type": "str" + }, + { + "name": "metadata", + "type": "dict" + } + ], + "return_type": "None" + }, + { + "type": "method", + "name": "DatabaseBackend.list_agents", + "filepath": "lib/laddr/src/laddr/core/config.py", + "start_line": 259, + "end_line": 259, + "code": " def list_agents(self) -> list[dict]: ...", + "docstring": "", + "dependencies": [], + "complexity": 1, + "business_context": "", + "imports": [], + "called_functions": [], + "parent_class": "DatabaseBackend", + "decorators": [], + "parameters": [ + { + "name": "self", + "type": "Any" + } + ], + "return_type": "list[dict]" + }, + { + "type": "class", + "name": "LLMBackend", + "filepath": "lib/laddr/src/laddr/core/config.py", + "start_line": 263, + "end_line": 266, + "code": "class LLMBackend(Protocol):\n \"\"\"Protocol for LLM backends.\"\"\"\n\n async def generate(self, prompt: str, system: str | None = None, **kwargs) -> str: ...", + "docstring": "Protocol for LLM backends.", + "dependencies": [ + "Protocol" + ], + "complexity": 2, + "business_context": "generate, llm", + "imports": [], + "called_functions": [], + "parent_class": "", + "decorators": [], + "parameters": [], + "return_type": "" + }, + { + "type": "class", + "name": "CacheBackend", + "filepath": "lib/laddr/src/laddr/core/config.py", + "start_line": 269, + "end_line": 275, + "code": "class CacheBackend(Protocol):\n \"\"\"Protocol for cache backends.\"\"\"\n", + "docstring": "Protocol for cache backends.", + "dependencies": [ + "Protocol" + ], + "complexity": 5, + "business_context": "cache", + "imports": [], + "called_functions": [], + "parent_class": "", + "decorators": [], + "parameters": [], + "return_type": "" + }, + { + "type": "method", + "name": "CacheBackend.get", + "filepath": "lib/laddr/src/laddr/core/config.py", + "start_line": 272, + "end_line": 272, + "code": " def get(self, key: str) -> Any: ...", + "docstring": "", + "dependencies": [], + "complexity": 1, + "business_context": "", + "imports": [], + "called_functions": [], + "parent_class": "CacheBackend", + "decorators": [], + "parameters": [ + { + "name": "self", + "type": "Any" + }, + { + "name": "key", + "type": "str" + } + ], + "return_type": "Any" + }, + { + "type": "method", + "name": "CacheBackend.set", + "filepath": "lib/laddr/src/laddr/core/config.py", + "start_line": 273, + "end_line": 273, + "code": " def set(self, key: str, value: Any, ttl: int | None = None) -> None: ...", + "docstring": "", + "dependencies": [], + "complexity": 1, + "business_context": "", + "imports": [], + "called_functions": [], + "parent_class": "CacheBackend", + "decorators": [], + "parameters": [ + { + "name": "self", + "type": "Any" + }, + { + "name": "key", + "type": "str" + }, + { + "name": "value", + "type": "Any" + }, + { + "name": "ttl", + "type": "int | None" + } + ], + "return_type": "None" + }, + { + "type": "method", + "name": "CacheBackend.delete", + "filepath": "lib/laddr/src/laddr/core/config.py", + "start_line": 274, + "end_line": 274, + "code": " def delete(self, key: str) -> None: ...", + "docstring": "", + "dependencies": [], + "complexity": 1, + "business_context": "delete", + "imports": [], + "called_functions": [], + "parent_class": "CacheBackend", + "decorators": [], + "parameters": [ + { + "name": "self", + "type": "Any" + }, + { + "name": "key", + "type": "str" + } + ], + "return_type": "None" + }, + { + "type": "method", + "name": "CacheBackend.clear", + "filepath": "lib/laddr/src/laddr/core/config.py", + "start_line": 275, + "end_line": 275, + "code": " def clear(self) -> None: ...", + "docstring": "", + "dependencies": [], + "complexity": 1, + "business_context": "", + "imports": [], + "called_functions": [], + "parent_class": "CacheBackend", + "decorators": [], + "parameters": [ + { + "name": "self", + "type": "Any" + } + ], + "return_type": "None" + }, + { + "type": "class", + "name": "BackendFactory", + "filepath": "lib/laddr/src/laddr/core/config.py", + "start_line": 278, + "end_line": 428, + "code": "class BackendFactory:\n \"\"\"\n Factory for creating backend instances based on configuration.\n \n Provides pluggable implementations for queue, database, LLM, and cache.\n \"\"\"\n", + "docstring": "Factory for creating backend instances based on configuration.\n\nProvides pluggable implementations for queue, database, LLM, and cache.", + "dependencies": [], + "complexity": 8, + "business_context": "database, cache, queue, llm", + "imports": [], + "called_functions": [], + "parent_class": "", + "decorators": [], + "parameters": [], + "return_type": "" + }, + { + "type": "method", + "name": "BackendFactory.__init__", + "filepath": "lib/laddr/src/laddr/core/config.py", + "start_line": 285, + "end_line": 286, + "code": " def __init__(self, config: LaddrConfig):\n self.config = config", + "docstring": "", + "dependencies": [], + "complexity": 1, + "business_context": "config", + "imports": [], + "called_functions": [], + "parent_class": "BackendFactory", + "decorators": [], + "parameters": [ + { + "name": "self", + "type": "Any" + }, + { + "name": "config", + "type": "LaddrConfig" + } + ], + "return_type": "None" + }, + { + "type": "method", + "name": "BackendFactory.create_queue_backend", + "filepath": "lib/laddr/src/laddr/core/config.py", + "start_line": 291, + "end_line": 336, + "code": " def create_queue_backend(self) -> QueueBackend:\n \"\"\"Create message queue backend based on config.\"\"\"\n import os as _os\n # Respect explicit env override first\n backend = _os.environ.get(\"QUEUE_BACKEND\", self.config.queue_backend)\n # Smart default: if user didn't explicitly opt into Redis (no env for\n # QUEUE_BACKEND and no REDIS_URL), then default to in-memory for a\n # frictionless local run.\n if backend == \"redis\" and (\"QUEUE_BACKEND\" not in _os.environ) and (\"REDIS_URL\" not in _os.environ):\n backend = \"memory\"\n\n if backend == \"redis\":\n from .message_bus import RedisBus\n bus = RedisBus(self.config.redis_url)\n elif backend == \"memory\":\n from .message_bus import MemoryBus\n if BackendFactory._memory_bus_singleton is None:\n BackendFactory._memory_bus_singleton = MemoryBus()\n bus = BackendFactory._memory_bus_singleton\n elif backend == \"kafka\":\n from .message_bus import KafkaBus\n if not self.config.kafka_bootstrap:\n raise ValueError(\"kafka_bootstrap is required for kafka queue_backend\")\n bus = KafkaBus(self.config.kafka_bootstrap)\n else:\n raise ValueError(f\"Unknown queue_backend: {backend}\")\n\n # Optionally enable large-response offload to storage\n try:\n if getattr(self.config, \"enable_large_response_storage\", True):\n print(f\"[STORAGE] Enabling large response storage (threshold={self.config.storage_threshold_kb} KB)\")\n storage = self.create_storage_backend()\n bus._storage = storage\n # Use new storage_bucket field, fallback to old minio_bucket for compatibility\n bucket = self.config.storage_bucket or self.config.minio_bucket or \"laddr\"\n bus._storage_bucket = bucket\n bus._storage_threshold_kb = self.config.storage_threshold_kb\n print(f\"[STORAGE] Configured: bucket={bucket}, threshold={self.config.storage_threshold_kb} KB\")\n except Exception as e:\n # Non-fatal: continue without offload\n print(f\"[STORAGE] Failed to enable storage: {e}\")\n import traceback\n traceback.print_exc()\n pass\n\n return bus", + "docstring": "Create message queue backend based on config.", + "dependencies": [ + "print_exc", + "get", + "print", + "getattr", + "create_storage_backend", + "RedisBus", + "KafkaBus", + "ValueError", + "MemoryBus" + ], + "complexity": 11, + "business_context": "config, queue, create, message, storage", + "imports": [ + "message_bus", + "storage", + "traceback", + "os" + ], + "called_functions": [ + "print_exc", + "get", + "print", + "getattr", + "create_storage_backend", + "RedisBus", + "KafkaBus", + "ValueError", + "MemoryBus" + ], + "parent_class": "BackendFactory", + "decorators": [], + "parameters": [ + { + "name": "self", + "type": "Any" + } + ], + "return_type": "QueueBackend" + }, + { + "type": "method", + "name": "BackendFactory.create_database_backend", + "filepath": "lib/laddr/src/laddr/core/config.py", + "start_line": 338, + "end_line": 344, + "code": " def create_database_backend(self) -> DatabaseBackend:\n \"\"\"Create database backend based on config.\"\"\"\n from .database import DatabaseService\n import os as _os\n # Prefer env override if present, then config, else SQLite\n db_url = _os.environ.get(\"DATABASE_URL\") or self.config.database_url or \"sqlite:///laddr.db\"\n return DatabaseService(db_url)", + "docstring": "Create database backend based on config.", + "dependencies": [ + "DatabaseService", + "get" + ], + "complexity": 2, + "business_context": "database, create, config", + "imports": [ + "database", + "os" + ], + "called_functions": [ + "DatabaseService", + "get" + ], + "parent_class": "BackendFactory", + "decorators": [], + "parameters": [ + { + "name": "self", + "type": "Any" + } + ], + "return_type": "DatabaseBackend" + }, + { + "type": "method", + "name": "BackendFactory.create_llm_backend", + "filepath": "lib/laddr/src/laddr/core/config.py", + "start_line": 346, + "end_line": 387, + "code": " def create_llm_backend(self, override: str | None = None, model_override: str | None = None, agent_name: str | None = None) -> LLMBackend:\n \"\"\"Create LLM backend based on config.\n\n If `override` is provided it takes precedence over the global\n `LaddrConfig.llm_backend`. `model_override` can be provided to\n select a different model for an agent.\n \"\"\"\n backend_name = override or self.config.llm_backend\n model = model_override or self.config.llm_model\n\n if backend_name == \"noop\":\n from .llm import NoOpLLM\n return NoOpLLM()\n if backend_name == \"openai\":\n from .llm import OpenAILLM\n return OpenAILLM(self.config.openai_api_key, model, base_url=self.config.openai_base_url)\n if backend_name == \"anthropic\":\n from .llm import AnthropicLLM\n return AnthropicLLM(self.config.anthropic_api_key, model)\n if backend_name == \"gemini\":\n from .llm import GeminiLLM\n return GeminiLLM(self.config.gemini_api_key, model)\n if backend_name == \"groq\":\n from .llm import GroqLLM\n return GroqLLM(self.config.groq_api_key, model)\n if backend_name == \"grok\":\n from .llm import GrokLLM\n return GrokLLM(self.config.xai_api_key or self.config.grok_api_key, model)\n if backend_name == \"ollama\":\n # Local Ollama HTTP backend. Supports per-agent LLM_MODEL_\n from .llm import OllamaLLM\n # Resolve model: explicit override -> per-agent env -> config -> default\n resolved_model = model\n if not resolved_model and agent_name:\n resolved_model = os.environ.get(f\"LLM_MODEL_{agent_name.upper()}\") or os.environ.get(f\"LLM_MODEL_{agent_name.lower()}\")\n resolved_model = resolved_model or self.config.llm_model or \"gemma2:2b\"\n # Resolve base URL: per-agent LLM_BASE_URL_ -> OLLAMA_BASE_URL -> default\n base_url = None\n if agent_name:\n base_url = os.environ.get(f\"LLM_BASE_URL_{agent_name.upper()}\") or os.environ.get(f\"LLM_BASE_URL_{agent_name.lower()}\")\n base_url = base_url or os.environ.get(\"OLLAMA_BASE_URL\") or \"http://localhost:11434\"\n return OllamaLLM(base_url=base_url, model=resolved_model)", + "docstring": "Create LLM backend based on config.\n\nIf `override` is provided it takes precedence over the global\n`LaddrConfig.llm_backend`. `model_override` can be provided to\nselect a different model for an agent.", + "dependencies": [ + "OllamaLLM", + "upper", + "get", + "AnthropicLLM", + "OpenAILLM", + "lower", + "NoOpLLM", + "GeminiLLM", + "GrokLLM", + "GroqLLM" + ], + "complexity": 18, + "business_context": "create, llm, agent, config", + "imports": [ + "llm", + "os" + ], + "called_functions": [ + "OllamaLLM", + "upper", + "get", + "AnthropicLLM", + "OpenAILLM", + "lower", + "NoOpLLM", + "GeminiLLM", + "GrokLLM", + "GroqLLM" + ], + "parent_class": "BackendFactory", + "decorators": [], + "parameters": [ + { + "name": "self", + "type": "Any" + }, + { + "name": "override", + "type": "str | None" + }, + { + "name": "model_override", + "type": "str | None" + }, + { + "name": "agent_name", + "type": "str | None" + } + ], + "return_type": "LLMBackend" + }, + { + "type": "method", + "name": "BackendFactory.create_cache_backend", + "filepath": "lib/laddr/src/laddr/core/config.py", + "start_line": 390, + "end_line": 398, + "code": " def create_cache_backend(self) -> CacheBackend:\n \"\"\"Create cache backend based on config.\"\"\"\n if self.config.cache_backend == \"inmemory\":\n from .cache import InMemoryCache\n return InMemoryCache()\n if self.config.cache_backend == \"redis\":\n from .cache import RedisCache\n return RedisCache(self.config.redis_url)\n raise ValueError(f\"Unknown cache_backend: {self.config.cache_backend}\")", + "docstring": "Create cache backend based on config.", + "dependencies": [ + "InMemoryCache", + "RedisCache", + "ValueError" + ], + "complexity": 3, + "business_context": "cache, create, config", + "imports": [ + "cache" + ], + "called_functions": [ + "InMemoryCache", + "RedisCache", + "ValueError" + ], + "parent_class": "BackendFactory", + "decorators": [], + "parameters": [ + { + "name": "self", + "type": "Any" + } + ], + "return_type": "CacheBackend" + }, + { + "type": "method", + "name": "BackendFactory.create_storage_backend", + "filepath": "lib/laddr/src/laddr/core/config.py", + "start_line": 400, + "end_line": 428, + "code": " def create_storage_backend(self):\n \"\"\"\n Create S3-compatible storage backend (AWS S3, MinIO, or compatible).\n \n Supports backward compatibility with old minio_* field names.\n \"\"\"\n # Use new storage_* fields, fallback to old minio_* fields for backward compatibility\n endpoint = (\n self.config.storage_endpoint\n if self.config.storage_endpoint != \"localhost:9000\" or self.config.minio_endpoint is None\n else self.config.minio_endpoint or self.config.storage_endpoint\n )\n access_key = self.config.storage_access_key\n secret_key = self.config.storage_secret_key\n secure = self.config.storage_secure\n region = self.config.storage_region\n \n if endpoint:\n from .storage import S3Storage\n return S3Storage(\n endpoint=endpoint,\n access_key=access_key,\n secret_key=secret_key,\n secure=secure,\n region=region\n )\n # Fall back to in-memory storage if no storage configured\n from .storage import InMemoryStorage\n return InMemoryStorage()", + "docstring": "Create S3-compatible storage backend (AWS S3, MinIO, or compatible).\n\nSupports backward compatibility with old minio_* field names.", + "dependencies": [ + "InMemoryStorage", + "S3Storage" + ], + "complexity": 4, + "business_context": "create, storage, config", + "imports": [ + "storage", + "os" + ], + "called_functions": [ + "InMemoryStorage", + "S3Storage" + ], + "parent_class": "BackendFactory", + "decorators": [], + "parameters": [ + { + "name": "self", + "type": "Any" + } + ], + "return_type": "None" + }, + { + "type": "class", + "name": "ProjectConfig", + "filepath": "lib/laddr/src/laddr/core/config.py", + "start_line": 432, + "end_line": 448, + "code": "class ProjectConfig(BaseModel):\n \"\"\"\n Project configuration from laddr.yml.\n \n Defines which agents exist and backend preferences.\n \"\"\"\n\n name: str = Field(description=\"Project name\")\n\n # Backend selections (can override env defaults)\n queue_backend: str = Field(default=\"redis\", description=\"Message queue backend\")\n db_backend: str = Field(default=\"postgres\", description=\"Database backend\")\n llm_backend: str = Field(default=\"noop\", description=\"LLM backend\")\n cache_backend: str = Field(default=\"inmemory\", description=\"Cache backend\")\n\n # Agent registry\n agents: list[str] = Field(default_factory=list, description=\"List of agent names\")", + "docstring": "Project configuration from laddr.yml.\n\nDefines which agents exist and backend preferences.", + "dependencies": [ + "BaseModel" + ], + "complexity": 7, + "business_context": "database, llm, queue, agent, cache", + "imports": [], + "called_functions": [], + "parent_class": "", + "decorators": [], + "parameters": [], + "return_type": "" + }, + { + "type": "class", + "name": "AgentConfig", + "filepath": "lib/laddr/src/laddr/core/config.py", + "start_line": 451, + "end_line": 468, + "code": "class AgentConfig(BaseModel):\n \"\"\"\n Agent configuration (minimal for @actor pattern).\n \n Most runtime config is auto-discovered or comes from LaddrConfig.\n \"\"\"\n\n name: str\n role: str\n goal: str\n backstory: str | None = None\n max_iterations: int = Field(default=5) # Reduced from 7 to 5 - forced finish at 4 successful tools\n allow_delegation: bool = Field(default=True)\n verbose: bool = Field(default=False)\n # Optional per-agent LLM overrides. If set, they take precedence over the\n # global `LaddrConfig.llm_backend` and `LaddrConfig.llm_model` values.\n llm_backend: str | None = Field(default=None, description=\"Optional agent-specific LLM backend (e.g., 'openai', 'gemini')\")\n llm_model: str | None = Field(default=None, description=\"Optional agent-specific LLM model name\")", + "docstring": "Agent configuration (minimal for @actor pattern).\n\nMost runtime config is auto-discovered or comes from LaddrConfig.", + "dependencies": [ + "BaseModel" + ], + "complexity": 10, + "business_context": "runtime, config, llm, agent", + "imports": [], + "called_functions": [], + "parent_class": "", + "decorators": [], + "parameters": [], + "return_type": "" + }, + { + "type": "class", + "name": "PipelineStage", + "filepath": "lib/laddr/src/laddr/core/config.py", + "start_line": 471, + "end_line": 475, + "code": "class PipelineStage(BaseModel):\n \"\"\"Stage definition in a pipeline (simplified).\"\"\"\n\n agent: str = Field(description=\"Agent name to execute\")\n inputs: dict[str, Any] = Field(default_factory=dict, description=\"Stage inputs\")", + "docstring": "Stage definition in a pipeline (simplified).", + "dependencies": [ + "BaseModel" + ], + "complexity": 3, + "business_context": "agent", + "imports": [], + "called_functions": [], + "parent_class": "", + "decorators": [], + "parameters": [], + "return_type": "" + }, + { + "type": "class", + "name": "PipelineConfig", + "filepath": "lib/laddr/src/laddr/core/config.py", + "start_line": 478, + "end_line": 483, + "code": "class PipelineConfig(BaseModel):\n \"\"\"Pipeline configuration for sequential agent execution.\"\"\"\n\n name: str\n description: str | None = None\n stages: list[PipelineStage]", + "docstring": "Pipeline configuration for sequential agent execution.", + "dependencies": [ + "BaseModel" + ], + "complexity": 4, + "business_context": "agent", + "imports": [], + "called_functions": [], + "parent_class": "", + "decorators": [], + "parameters": [], + "return_type": "" + }, + { + "type": "class", + "name": "Config", + "filepath": "lib/laddr/src/laddr/core/config.py", + "start_line": 226, + "end_line": 229, + "code": " class Config:\n env_file = \".env\"\n env_file_encoding = \"utf-8\"\n extra = \"ignore\" # Ignore unknown fields from environment", + "docstring": "", + "dependencies": [], + "complexity": 3, + "business_context": "config", + "imports": [], + "called_functions": [], + "parent_class": "", + "decorators": [], + "parameters": [], + "return_type": "" + }, + { + "type": "function", + "name": "load_agents", + "filepath": "lib/laddr/src/laddr/core/config.py", + "start_line": 14, + "end_line": 49, + "code": "def load_agents() -> Dict[str, Any]:\n \"\"\"Load agents dynamically from 'agents..handler'.\n\n Supports two patterns:\n 1) @actor-decorated classes named Agent (instantiated without args)\n 2) Module-level Agent instances (any variable bound to laddr.core.agent_runtime.Agent)\n \"\"\"\n discovered: Dict[str, Any] = {}\n try:\n agents_pkg = importlib.import_module(\"agents\")\n import pkgutil\n\n for m in pkgutil.iter_modules(agents_pkg.__path__):\n name = m.name\n try:\n mod = importlib.import_module(f\"agents.{name}.handler\")\n\n # Prefer class pattern: Agent\n # We intentionally do NOT instantiate here to avoid needing configs.\n # run-local has a fallback path that constructs the class with AgentConfig/LaddrConfig.\n # So we skip adding classes in this discovery step.\n # cls_name = f\"{name.capitalize()}Agent\"\n # AgentCls = getattr(mod, cls_name, None)\n # if inspect.isclass(AgentCls):\n # pass\n\n # Instance pattern: find any Agent instance in module globals\n for var_name, obj in vars(mod).items():\n if not inspect.isclass(obj) and callable(getattr(obj, \"handle\", None)):\n discovered[name] = obj\n break\n except Exception:\n continue\n except Exception:\n pass\n return discovered", + "docstring": "Load agents dynamically from 'agents..handler'.\n\nSupports two patterns:\n1) @actor-decorated classes named Agent (instantiated without args)\n2) Module-level Agent instances (any variable bound to laddr.core.agent_runtime.Agent)", + "dependencies": [ + "getattr", + "import_module", + "isclass", + "items", + "iter_modules", + "callable", + "vars" + ], + "complexity": 7, + "business_context": "load, handle, agent", + "imports": [ + "pkgutil", + "importlib", + "inspect" + ], + "called_functions": [ + "getattr", + "import_module", + "isclass", + "items", + "iter_modules", + "callable", + "vars" + ], + "parent_class": "", + "decorators": [], + "parameters": [], + "return_type": "Dict[str, Any]" + }, + { + "type": "class", + "name": "AgentRunner", + "filepath": "lib/laddr/src/laddr/core/runtime_entry.py", + "start_line": 21, + "end_line": 558, + "code": "class AgentRunner:\n \"\"\"\n High-level agent runner for executing agents and managing jobs.\n \n Provides:\n - run(): Execute agent with job tracking\n - replay(): Replay previous job\n - run_pipeline(): Orchestrate multi-agent workflow\n \"\"\"\n", + "docstring": "High-level agent runner for executing agents and managing jobs.\n\nProvides:\n- run(): Execute agent with job tracking\n- replay(): Replay previous job\n- run_pipeline(): Orchestrate multi-agent workflow", + "dependencies": [], + "complexity": 8, + "business_context": "agent", + "imports": [], + "called_functions": [], + "parent_class": "", + "decorators": [], + "parameters": [], + "return_type": "" + }, + { + "type": "method", + "name": "AgentRunner.__init__", + "filepath": "lib/laddr/src/laddr/core/runtime_entry.py", + "start_line": 31, + "end_line": 51, + "code": " def __init__(\n self,\n agent: Agent | None = None,\n env_config: LaddrConfig | None = None\n ):\n \"\"\"\n Initialize runner.\n \n \n \n Args:\n agent: Agent instance (created if None)\n env_config: Environment configuration\n \"\"\"\n self.agent = agent\n self.env_config = env_config or LaddrConfig()\n self.factory = BackendFactory(self.env_config)\n self.database = self.factory.create_database_backend()\n # Inline worker control\n self._inline_worker_task = None\n self._inline_worker_stop = None", + "docstring": "Initialize runner.\n\n\n\nArgs:\n agent: Agent instance (created if None)\n env_config: Environment configuration", + "dependencies": [ + "BackendFactory", + "create_database_backend", + "LaddrConfig" + ], + "complexity": 2, + "business_context": "initialize, database, agent", + "imports": [ + "config" + ], + "called_functions": [ + "BackendFactory", + "create_database_backend", + "LaddrConfig" + ], + "parent_class": "AgentRunner", + "decorators": [], + "parameters": [ + { + "name": "self", + "type": "Any" + }, + { + "name": "agent", + "type": "Agent | None" + }, + { + "name": "env_config", + "type": "LaddrConfig | None" + } + ], + "return_type": "None" + }, + { + "type": "method", + "name": "AgentRunner._discover_local_agents", + "filepath": "lib/laddr/src/laddr/core/runtime_entry.py", + "start_line": 262, + "end_line": 278, + "code": " def _discover_local_agents(self, exclude: list[str] | None = None) -> list[str]:\n \"\"\"Discover local agents under the 'agents' package for inline execution.\"\"\"\n names: list[str] = []\n exclude = set(exclude or [])\n try:\n agents_pkg = importlib.import_module(\"agents\")\n for m in pkgutil.iter_modules(agents_pkg.__path__):\n name = m.name\n if name in exclude:\n continue\n # Ensure handler exists\n with suppress(Exception):\n importlib.import_module(f\"agents.{name}.handler\")\n names.append(name)\n except Exception:\n pass\n return names", + "docstring": "Discover local agents under the 'agents' package for inline execution.", + "dependencies": [ + "append", + "set", + "suppress", + "import_module", + "iter_modules" + ], + "complexity": 6, + "business_context": "", + "imports": [ + "pkgutil", + "importlib" + ], + "called_functions": [ + "append", + "set", + "suppress", + "import_module", + "iter_modules" + ], + "parent_class": "AgentRunner", + "decorators": [], + "parameters": [ + { + "name": "self", + "type": "Any" + }, + { + "name": "exclude", + "type": "list[str] | None" + } + ], + "return_type": "list[str]" + }, + { + "type": "method", + "name": "AgentRunner._start_inline_workers", + "filepath": "lib/laddr/src/laddr/core/runtime_entry.py", + "start_line": 280, + "end_line": 350, + "code": " def _start_inline_workers(self, agent_names: list[str]) -> None:\n \"\"\"Start a background task that consumes tasks and runs target agents inline.\"\"\"\n import logging\n logger = logging.getLogger(__name__)\n logger.info(f\"Starting inline workers for agents: {agent_names}\")\n \n stop = asyncio.Event()\n self._inline_worker_stop = stop\n\n async def _loop():\n # Cache agent instances per name to avoid re-import overhead\n agent_cache: dict[str, Agent] = {}\n bus = self.factory.create_queue_backend()\n logger.info(f\"Inline worker loop started, listening for tasks...\")\n while not stop.is_set():\n progress = False\n for name in agent_names:\n try:\n tasks = await bus.consume_tasks(name, block_ms=200, count=5)\n except Exception:\n logger.exception(f\"Error consuming tasks for {name}\")\n tasks = []\n if not tasks:\n continue\n logger.info(f\"Inline worker received {len(tasks)} task(s) for {name}\")\n progress = True\n for data in tasks:\n try:\n task_id = data.get(\"task_id\")\n payload = data.get(\"payload\", {})\n logger.info(f\"Processing task {task_id} for {name}\")\n # Get or create agent instance\n if name not in agent_cache:\n try:\n mod = importlib.import_module(f\"agents.{name}.handler\")\n cls = getattr(mod, f\"{name.capitalize()}Agent\")\n except Exception as e:\n # Skip if cannot import\n logger.error(f\"Failed to import agent {name}: {e}\")\n continue\n agent_cfg = AgentConfig(\n name=getattr(cls, 'AGENT_NAME', name),\n role=getattr(cls, 'ROLE', 'Agent'),\n goal=getattr(cls, 'GOAL', 'Execute tasks')\n )\n inst: Agent = cls(agent_cfg, self.env_config)\n await inst.connect_bus()\n logger.info(f\"Created inline worker agent instance for {name}\")\n agent_cache[name] = inst\n inst = agent_cache[name]\n # Execute task\n result = await inst.handle(payload)\n logger.info(f\"Task {task_id} completed: {result.get('status', 'unknown')}\")\n # Publish response for waiter\n if task_id:\n with suppress(Exception):\n await bus.publish_response(task_id, result)\n logger.info(f\"Published response for task {task_id}\")\n except Exception:\n # On processing error, continue loop\n logger.exception(f\"Error processing task {data.get('task_id')} for {name}\")\n continue\n\n # Avoid busy loop if no progress\n if not progress:\n try:\n await asyncio.wait_for(stop.wait(), timeout=0.25)\n except asyncio.TimeoutError:\n pass\n\n self._inline_worker_task = asyncio.create_task(_loop())", + "docstring": "Start a background task that consumes tasks and runs target agents inline.", + "dependencies": [ + "wait_for", + "consume_tasks", + "capitalize", + "create_queue_backend", + "publish_response", + "_loop", + "error", + "is_set", + "suppress", + "import_module", + "get", + "wait", + "getattr", + "create_task", + "AgentConfig", + "connect_bus", + "len", + "exception", + "info", + "Event", + "cls", + "handle", + "getLogger" + ], + "complexity": 13, + "business_context": "cache, create, handle, agent", + "imports": [ + "config", + "logging", + "importlib", + "asyncio", + "time" + ], + "called_functions": [ + "wait_for", + "consume_tasks", + "capitalize", + "create_queue_backend", + "publish_response", + "_loop", + "error", + "is_set", + "suppress", + "import_module", + "get", + "wait", + "getattr", + "create_task", + "AgentConfig", + "connect_bus", + "len", + "exception", + "info", + "Event", + "cls", + "handle", + "getLogger" + ], + "parent_class": "AgentRunner", + "decorators": [], + "parameters": [ + { + "name": "self", + "type": "Any" + }, + { + "name": "agent_names", + "type": "list[str]" + } + ], + "return_type": "None" + }, + { + "type": "method", + "name": "AgentRunner._stop_inline_workers", + "filepath": "lib/laddr/src/laddr/core/runtime_entry.py", + "start_line": 352, + "end_line": 360, + "code": " def _stop_inline_workers(self) -> None:\n if self._inline_worker_stop is not None:\n self._inline_worker_stop.set()\n if self._inline_worker_task is not None:\n self._inline_worker_task.cancel()\n with suppress(Exception):\n asyncio.get_event_loop().run_until_complete(self._inline_worker_task)\n self._inline_worker_task = None\n self._inline_worker_stop = None", + "docstring": "", + "dependencies": [ + "get_event_loop", + "set", + "cancel", + "suppress", + "run_until_complete" + ], + "complexity": 4, + "business_context": "", + "imports": [ + "asyncio" + ], + "called_functions": [ + "get_event_loop", + "set", + "cancel", + "suppress", + "run_until_complete" + ], + "parent_class": "AgentRunner", + "decorators": [], + "parameters": [ + { + "name": "self", + "type": "Any" + } + ], + "return_type": "None" + }, + { + "type": "method", + "name": "AgentRunner.replay", + "filepath": "lib/laddr/src/laddr/core/runtime_entry.py", + "start_line": 362, + "end_line": 402, + "code": " def replay(\n self,\n job_id: str,\n reexecute: bool = False\n ) -> dict:\n \"\"\"\n Replay a previous job.\n \n Args:\n job_id: Job ID to replay\n reexecute: If True, re-execute the job; if False, return stored result\n \n Returns:\n Job result\n \"\"\"\n # Get stored result\n result = self.database.get_result(job_id)\n\n if not result:\n return {\n \"status\": \"error\",\n \"error\": f\"Job not found: {job_id}\"\n }\n\n if reexecute:\n # Re-execute with stored inputs\n inputs = result.get(\"inputs\", {})\n pipeline_name = result.get(\"pipeline_name\")\n\n # Run with same job_id\n return asyncio.run(self.run(inputs, agent_name=pipeline_name, job_id=job_id))\n # Return stored result\n return {\n \"job_id\": job_id,\n \"status\": result.get(\"status\"),\n \"result\": result.get(\"outputs\"),\n \"error\": result.get(\"error\"),\n \"pipeline_name\": result.get(\"pipeline_name\"),\n \"created_at\": result.get(\"created_at\"),\n \"completed_at\": result.get(\"completed_at\")\n }", + "docstring": "Replay a previous job.\n\nArgs:\n job_id: Job ID to replay\n reexecute: If True, re-execute the job; if False, return stored result\n\nReturns:\n Job result", + "dependencies": [ + "get", + "get_result", + "run" + ], + "complexity": 3, + "business_context": "database", + "imports": [ + "asyncio" + ], + "called_functions": [ + "get", + "get_result", + "run" + ], + "parent_class": "AgentRunner", + "decorators": [], + "parameters": [ + { + "name": "self", + "type": "Any" + }, + { + "name": "job_id", + "type": "str" + }, + { + "name": "reexecute", + "type": "bool" + } + ], + "return_type": "dict" + }, + { + "type": "class", + "name": "WorkerRunner", + "filepath": "lib/laddr/src/laddr/core/runtime_entry.py", + "start_line": 582, + "end_line": 668, + "code": "class WorkerRunner:\n \"\"\"\n Lightweight worker runner for a single agent instance.\n\n Consumes tasks from the message bus for the agent's name and publishes results.\n \"\"\"\n", + "docstring": "Lightweight worker runner for a single agent instance.\n\nConsumes tasks from the message bus for the agent's name and publishes results.", + "dependencies": [], + "complexity": 3, + "business_context": "message, agent", + "imports": [], + "called_functions": [], + "parent_class": "", + "decorators": [], + "parameters": [], + "return_type": "" + }, + { + "type": "method", + "name": "WorkerRunner.__init__", + "filepath": "lib/laddr/src/laddr/core/runtime_entry.py", + "start_line": 589, + "end_line": 618, + "code": " def __init__(\n self,\n agent: Agent,\n database_url: str | None = None,\n redis_url: str | None = None,\n storage_endpoint: str | None = None,\n storage_access_key: str | None = None,\n storage_secret_key: str | None = None,\n storage_secure: bool | None = None,\n storage_region: str | None = None,\n ) -> None:\n # Build env config from provided values (fallback to defaults)\n # Support both new storage_* and old minio_* parameter names\n defaults = LaddrConfig()\n self.env_config = LaddrConfig(\n database_url=database_url or defaults.database_url,\n redis_url=redis_url or defaults.redis_url,\n storage_endpoint=storage_endpoint or defaults.storage_endpoint,\n storage_access_key=storage_access_key or defaults.storage_access_key,\n storage_secret_key=storage_secret_key or defaults.storage_secret_key,\n storage_secure=storage_secure if storage_secure is not None else (\n False\n ),\n storage_region=storage_region or defaults.storage_region,\n storage_bucket=defaults.storage_bucket, # Read from env\n enable_large_response_storage=defaults.enable_large_response_storage, # Read from env\n storage_threshold_kb=defaults.storage_threshold_kb, # Read from env\n )\n self.factory = BackendFactory(self.env_config)\n self.agent = agent", + "docstring": "", + "dependencies": [ + "BackendFactory", + "LaddrConfig" + ], + "complexity": 7, + "business_context": "config, agent", + "imports": [ + "config" + ], + "called_functions": [ + "BackendFactory", + "LaddrConfig" + ], + "parent_class": "WorkerRunner", + "decorators": [], + "parameters": [ + { + "name": "self", + "type": "Any" + }, + { + "name": "agent", + "type": "Agent" + }, + { + "name": "database_url", + "type": "str | None" + }, + { + "name": "redis_url", + "type": "str | None" + }, + { + "name": "storage_endpoint", + "type": "str | None" + }, + { + "name": "storage_access_key", + "type": "str | None" + }, + { + "name": "storage_secret_key", + "type": "str | None" + }, + { + "name": "storage_secure", + "type": "bool | None" + }, + { + "name": "storage_region", + "type": "str | None" + } + ], + "return_type": "None" + }, + { + "type": "class", + "name": "InMemoryCache", + "filepath": "lib/laddr/src/laddr/core/cache.py", + "start_line": 19, + "end_line": 58, + "code": "class InMemoryCache:\n \"\"\"\n In-memory cache for local dev and testing.\n \n Simple dict-based cache with optional TTL.\n \"\"\"\n", + "docstring": "In-memory cache for local dev and testing.\n\nSimple dict-based cache with optional TTL.", + "dependencies": [], + "complexity": 6, + "business_context": "cache", + "imports": [], + "called_functions": [], + "parent_class": "", + "decorators": [], + "parameters": [], + "return_type": "" + }, + { + "type": "method", + "name": "InMemoryCache.__init__", + "filepath": "lib/laddr/src/laddr/core/cache.py", + "start_line": 26, + "end_line": 27, + "code": " def __init__(self):\n self._cache: dict[str, tuple[Any, float | None]] = {}", + "docstring": "", + "dependencies": [], + "complexity": 1, + "business_context": "", + "imports": [], + "called_functions": [], + "parent_class": "InMemoryCache", + "decorators": [], + "parameters": [ + { + "name": "self", + "type": "Any" + } + ], + "return_type": "None" + }, + { + "type": "method", + "name": "InMemoryCache.get", + "filepath": "lib/laddr/src/laddr/core/cache.py", + "start_line": 29, + "end_line": 41, + "code": " def get(self, key: str) -> Any:\n \"\"\"Get value from cache.\"\"\"\n if key not in self._cache:\n return None\n\n value, expires_at = self._cache[key]\n\n # Check expiration\n if expires_at is not None and time.time() > expires_at:\n del self._cache[key]\n return None\n\n return value", + "docstring": "Get value from cache.", + "dependencies": [ + "time" + ], + "complexity": 4, + "business_context": "cache", + "imports": [ + "time" + ], + "called_functions": [ + "time" + ], + "parent_class": "InMemoryCache", + "decorators": [], + "parameters": [ + { + "name": "self", + "type": "Any" + }, + { + "name": "key", + "type": "str" + } + ], + "return_type": "Any" + }, + { + "type": "method", + "name": "InMemoryCache.set", + "filepath": "lib/laddr/src/laddr/core/cache.py", + "start_line": 43, + "end_line": 49, + "code": " def set(self, key: str, value: Any, ttl: int | None = None) -> None:\n \"\"\"Set value in cache with optional TTL (seconds).\"\"\"\n expires_at = None\n if ttl is not None:\n expires_at = time.time() + ttl\n\n self._cache[key] = (value, expires_at)", + "docstring": "Set value in cache with optional TTL (seconds).", + "dependencies": [ + "time" + ], + "complexity": 2, + "business_context": "cache", + "imports": [ + "time" + ], + "called_functions": [ + "time" + ], + "parent_class": "InMemoryCache", + "decorators": [], + "parameters": [ + { + "name": "self", + "type": "Any" + }, + { + "name": "key", + "type": "str" + }, + { + "name": "value", + "type": "Any" + }, + { + "name": "ttl", + "type": "int | None" + } + ], + "return_type": "None" + }, + { + "type": "method", + "name": "InMemoryCache.delete", + "filepath": "lib/laddr/src/laddr/core/cache.py", + "start_line": 51, + "end_line": 54, + "code": " def delete(self, key: str) -> None:\n \"\"\"Delete value from cache.\"\"\"\n if key in self._cache:\n del self._cache[key]", + "docstring": "Delete value from cache.", + "dependencies": [], + "complexity": 2, + "business_context": "cache, delete", + "imports": [], + "called_functions": [], + "parent_class": "InMemoryCache", + "decorators": [], + "parameters": [ + { + "name": "self", + "type": "Any" + }, + { + "name": "key", + "type": "str" + } + ], + "return_type": "None" + }, + { + "type": "method", + "name": "InMemoryCache.clear", + "filepath": "lib/laddr/src/laddr/core/cache.py", + "start_line": 56, + "end_line": 58, + "code": " def clear(self) -> None:\n \"\"\"Clear all cache entries.\"\"\"\n self._cache.clear()", + "docstring": "Clear all cache entries.", + "dependencies": [ + "clear" + ], + "complexity": 1, + "business_context": "cache", + "imports": [], + "called_functions": [ + "clear" + ], + "parent_class": "InMemoryCache", + "decorators": [], + "parameters": [ + { + "name": "self", + "type": "Any" + } + ], + "return_type": "None" + }, + { + "type": "class", + "name": "RedisCache", + "filepath": "lib/laddr/src/laddr/core/cache.py", + "start_line": 61, + "end_line": 115, + "code": "class RedisCache:\n \"\"\"\n Redis-based cache for production.\n \n Uses Redis with TTL support.\n \"\"\"\n", + "docstring": "Redis-based cache for production.\n\nUses Redis with TTL support.", + "dependencies": [], + "complexity": 7, + "business_context": "cache", + "imports": [], + "called_functions": [], + "parent_class": "", + "decorators": [], + "parameters": [], + "return_type": "" + }, + { + "type": "method", + "name": "RedisCache.__init__", + "filepath": "lib/laddr/src/laddr/core/cache.py", + "start_line": 68, + "end_line": 70, + "code": " def __init__(self, redis_url: str):\n self.redis_url = redis_url\n self._client: aioredis.Redis | None = None", + "docstring": "", + "dependencies": [], + "complexity": 1, + "business_context": "", + "imports": [ + "redis.asyncio" + ], + "called_functions": [], + "parent_class": "RedisCache", + "decorators": [], + "parameters": [ + { + "name": "self", + "type": "Any" + }, + { + "name": "redis_url", + "type": "str" + } + ], + "return_type": "None" + }, + { + "type": "class", + "name": "AgentMemory", + "filepath": "lib/laddr/src/laddr/core/agent_runtime.py", + "start_line": 28, + "end_line": 45, + "code": "class AgentMemory:\n \"\"\"Memory interface for agents.\"\"\"\n\n agent_name: str\n database: Any # DatabaseService\n job_id: str | None = None\n", + "docstring": "Memory interface for agents.", + "dependencies": [], + "complexity": 7, + "business_context": "database", + "imports": [], + "called_functions": [], + "parent_class": "", + "decorators": [ + "dataclass" + ], + "parameters": [], + "return_type": "" + }, + { + "type": "method", + "name": "AgentMemory.put", + "filepath": "lib/laddr/src/laddr/core/agent_runtime.py", + "start_line": 35, + "end_line": 37, + "code": " def put(self, key: str, value: Any) -> None:\n \"\"\"Store a memory entry.\"\"\"\n self.database.memory_put(self.agent_name, key, value, self.job_id)", + "docstring": "Store a memory entry.", + "dependencies": [ + "memory_put" + ], + "complexity": 1, + "business_context": "database", + "imports": [ + "re" + ], + "called_functions": [ + "memory_put" + ], + "parent_class": "AgentMemory", + "decorators": [], + "parameters": [ + { + "name": "self", + "type": "Any" + }, + { + "name": "key", + "type": "str" + }, + { + "name": "value", + "type": "Any" + } + ], + "return_type": "None" + }, + { + "type": "method", + "name": "AgentMemory.get", + "filepath": "lib/laddr/src/laddr/core/agent_runtime.py", + "start_line": 39, + "end_line": 41, + "code": " def get(self, key: str) -> Any:\n \"\"\"Retrieve a memory entry.\"\"\"\n return self.database.memory_get(self.agent_name, key, self.job_id)", + "docstring": "Retrieve a memory entry.", + "dependencies": [ + "memory_get" + ], + "complexity": 1, + "business_context": "database", + "imports": [ + "re" + ], + "called_functions": [ + "memory_get" + ], + "parent_class": "AgentMemory", + "decorators": [], + "parameters": [ + { + "name": "self", + "type": "Any" + }, + { + "name": "key", + "type": "str" + } + ], + "return_type": "Any" + }, + { + "type": "method", + "name": "AgentMemory.list", + "filepath": "lib/laddr/src/laddr/core/agent_runtime.py", + "start_line": 43, + "end_line": 45, + "code": " def list(self) -> dict[str, Any]:\n \"\"\"List all memory entries.\"\"\"\n return self.database.memory_list(self.agent_name, self.job_id)", + "docstring": "List all memory entries.", + "dependencies": [ + "memory_list" + ], + "complexity": 1, + "business_context": "database", + "imports": [ + "re" + ], + "called_functions": [ + "memory_list" + ], + "parent_class": "AgentMemory", + "decorators": [], + "parameters": [ + { + "name": "self", + "type": "Any" + } + ], + "return_type": "dict[str, Any]" + }, + { + "type": "class", + "name": "Agent", + "filepath": "lib/laddr/src/laddr/core/agent_runtime.py", + "start_line": 48, + "end_line": 1736, + "code": "class Agent:\n \"\"\"\n Base Agent class with full runtime capabilities.\n \n Provides:\n - Planning and execution\n - Tool calling with validation and caching\n - Delegation with blocking wait for responses\n - Memory storage\n - Auto-registration on message bus\n - Trace logging to database\n \n Subclasses should override plan() method for custom planning logic,\n or use the default autonomous_run() for intelligent LLM-driven execution.\n \"\"\"\n\n # Class-level metadata (set by @actor decorator or subclass)\n ROLE: str = \"agent\"\n GOAL: str = \"Complete assigned tasks\"\n", + "docstring": "Base Agent class with full runtime capabilities.\n\nProvides:\n- Planning and execution\n- Tool calling with validation and caching\n- Delegation with blocking wait for responses\n- Memory storage\n- Auto-registration on message bus\n- Trace logging to database\n\nSubclasses should override plan() method for custom planning logic,\nor use the default autonomous_run() for intelligent LLM-driven execution.", + "dependencies": [], + "complexity": 29, + "business_context": "database, llm, agent, runtime, tool", + "imports": [], + "called_functions": [], + "parent_class": "", + "decorators": [], + "parameters": [], + "return_type": "" + }, + { + "type": "method", + "name": "Agent.__init__", + "filepath": "lib/laddr/src/laddr/core/agent_runtime.py", + "start_line": 68, + "end_line": 235, + "code": " def __init__(\n self,\n config: AgentConfig,\n env_config: LaddrConfig | None = None,\n tools: ToolRegistry | list[Any] | None = None,\n *,\n llm: Any | None = None,\n queue: Any | None = None,\n instructions: str | None = None,\n is_coordinator: bool | None = None,\n available_agents: list[str] | None = None,\n ):\n \"\"\"\n Initialize agent.\n \n Args:\n config: Agent-specific configuration\n env_config: Environment configuration (loaded if None)\n tools: Tool registry (auto-discovered if None)\n \"\"\"\n self.config = config\n self.env_config = env_config or LaddrConfig()\n\n # Create backends (allow explicit overrides for llm and queue)\n self.factory = BackendFactory(self.env_config)\n # Queue override\n if queue is not None:\n try:\n # Direct bus instance (RedisBus/MemoryBus)\n if hasattr(queue, \"publish_task\") and hasattr(queue, \"consume_tasks\"):\n self.bus = queue\n # Wrapper with uri attribute (e.g., RedisQueue from laddr.queues)\n elif hasattr(queue, \"uri\"):\n from .message_bus import RedisBus\n self.bus = RedisBus(getattr(queue, \"uri\"))\n # String shorthand\n elif isinstance(queue, str) and queue.startswith(\"redis://\"):\n from .message_bus import RedisBus\n self.bus = RedisBus(queue)\n else:\n self.bus = self.factory.create_queue_backend()\n except Exception:\n self.bus = self.factory.create_queue_backend()\n else:\n self.bus = self.factory.create_queue_backend()\n\n self.database = self.factory.create_database_backend()\n\n # LLM override\n if llm is not None:\n self.llm = llm\n else:\n # Determine per-agent LLM selection in order of precedence:\n # 1) AgentConfig.llm_backend\n # 2) Environment variable LLM_BACKEND_\n # 3) Global LaddrConfig.llm_backend\n import os as _os\n # Build keys for environment overrides. Support multiple naming conventions:\n # 1) LLM_BACKEND_ / LLM_MODEL_\n # 2) _backend / _model (legacy/project-specific, often lowercase like \"researcher_model\")\n agent_name = (self.config.name or \"\")\n agent_name_key = agent_name.upper()\n env_key = f\"LLM_BACKEND_{agent_name_key}\"\n env_model_key = f\"LLM_MODEL_{agent_name_key}\"\n\n legacy_backend_key = f\"{agent_name}_backend\"\n legacy_model_key = f\"{agent_name}_model\"\n\n backend_override = None\n model_override = None\n\n try:\n # AgentConfig override if provided\n backend_override = getattr(self.config, \"llm_backend\", None) or None\n model_override = getattr(self.config, \"llm_model\", None) or None\n except Exception:\n backend_override = None\n model_override = None\n\n # Environment variables take precedence over agent config. Check multiple keys and case variants.\n try:\n # Prefer LLM_* names (upper or lower), then legacy agent-specific names\n env_backend = (\n _os.environ.get(env_key)\n or _os.environ.get(env_key.lower())\n or _os.environ.get(legacy_backend_key)\n or _os.environ.get(legacy_backend_key.upper())\n )\n env_model = (\n _os.environ.get(env_model_key)\n or _os.environ.get(env_model_key.lower())\n or _os.environ.get(legacy_model_key)\n or _os.environ.get(legacy_model_key.upper())\n )\n\n if env_backend:\n backend_override = env_backend\n if env_model:\n model_override = env_model\n except Exception:\n pass\n\n self.llm = self.factory.create_llm_backend(override=backend_override, model_override=model_override, agent_name=self.config.name)\n # Default LLM params (temperature, max_tokens, etc.) optionally provided by llm wrapper\n self._llm_params: dict = {}\n try:\n if hasattr(self.llm, \"default_params\") and isinstance(self.llm.default_params, dict): # type: ignore[attr-defined]\n self._llm_params = dict(self.llm.default_params) # type: ignore[attr-defined]\n except Exception:\n self._llm_params = {}\n self.cache_backend = self.factory.create_cache_backend()\n self.storage = self.factory.create_storage_backend()\n\n # Coordinator support (allow explicit override)\n # Set this BEFORE tool registration so _register_system_tools can check it\n self.is_coordinator = (\n bool(is_coordinator)\n if is_coordinator is not None\n else (getattr(self.env_config, \"has_coordinator\", False) and (\n self.config.name == getattr(self.env_config, \"coordinator_agent\", \"coordinator\")\n ))\n )\n self.coordinator_agent = (\n getattr(self.env_config, \"coordinator_agent\", \"coordinator\")\n if getattr(self.env_config, \"has_coordinator\", False)\n else None\n )\n # Available agents hint for prompts/planning\n self.available_agents_hint: list[str] = list(available_agents or [])\n # Agent-level instruction supplement\n self._extra_instructions: str | None = (instructions or None)\n\n # Tool registry - start with user tools, then add system tools\n if tools is None:\n self.tools = discover_tools(config.name)\n elif isinstance(tools, ToolRegistry):\n self.tools = tools\n else:\n # Accept list of callables and register\n from .tooling import ToolRegistry as _TR\n tr = _TR()\n for t in (tools or []):\n try:\n tr.register(t)\n except Exception:\n continue\n self.tools = tr\n\n # Add system tools for delegation and artifact storage\n # This is called AFTER is_coordinator is set\n self._register_system_tools()\n\n # Memory interface\n self.memory: AgentMemory | None = None\n\n # Current job context\n self.current_job_id: str | None = None\n\n # Tracing controls (configurable via @actor decorator class attributes)\n self._trace_enabled: bool = bool(getattr(self.__class__, \"TRACE_ENABLED\", True))\n mask = getattr(self.__class__, \"TRACE_MASK\", set()) or set()\n try:\n self._trace_mask: set[str] = set(mask)\n except Exception:\n self._trace_mask = set()\n\n # Heartbeat task\n self._heartbeat_task: asyncio.Task | None = None", + "docstring": "Initialize agent.\n\nArgs:\n config: Agent-specific configuration\n env_config: Environment configuration (loaded if None)\n tools: Tool registry (auto-discovered if None)", + "dependencies": [ + "isinstance", + "create_cache_backend", + "BackendFactory", + "hasattr", + "create_queue_backend", + "list", + "RedisBus", + "LaddrConfig", + "upper", + "set", + "_TR", + "register", + "get", + "getattr", + "_register_system_tools", + "create_storage_backend", + "create_llm_backend", + "startswith", + "create_database_backend", + "discover_tools", + "dict", + "bool", + "lower" + ], + "complexity": 32, + "business_context": "config, llm, queue, database, agent", + "imports": [ + "tooling", + "config", + "os", + "message_bus", + "sys", + "asyncio", + "re", + "system_tools" + ], + "called_functions": [ + "isinstance", + "create_cache_backend", + "BackendFactory", + "hasattr", + "create_queue_backend", + "list", + "RedisBus", + "LaddrConfig", + "upper", + "set", + "_TR", + "register", + "get", + "getattr", + "_register_system_tools", + "create_storage_backend", + "create_llm_backend", + "startswith", + "create_database_backend", + "discover_tools", + "dict", + "bool", + "lower" + ], + "parent_class": "Agent", + "decorators": [], + "parameters": [ + { + "name": "self", + "type": "Any" + }, + { + "name": "config", + "type": "AgentConfig" + }, + { + "name": "env_config", + "type": "LaddrConfig | None" + }, + { + "name": "tools", + "type": "ToolRegistry | list[Any] | None" + } + ], + "return_type": "None" + }, + { + "type": "method", + "name": "Agent._discover_tool_overrides", + "filepath": "lib/laddr/src/laddr/core/agent_runtime.py", + "start_line": 314, + "end_line": 360, + "code": " def _discover_tool_overrides(self):\n \"\"\"\n Auto-discover and load tool override modules from the project.\n \n Searches common locations for tool override files:\n - ./tools/overrides/\n - ./custom_tools/\n - ./agent_tools/overrides/\n \n When override modules are imported, the @override_system_tool\n decorators will automatically register the overrides.\n \"\"\"\n import importlib.util\n import os\n from pathlib import Path\n \n # Get workspace root (where laddr.yml is located)\n workspace_root = Path.cwd()\n if hasattr(self, 'config') and hasattr(self.config, 'project_root'):\n workspace_root = Path(self.config.project_root)\n \n # Search paths for override modules\n search_paths = [\n workspace_root / \"tools\" / \"overrides\",\n workspace_root / \"custom_tools\",\n workspace_root / \"agent_tools\" / \"overrides\",\n ]\n \n for search_path in search_paths:\n if not search_path.exists() or not search_path.is_dir():\n continue\n \n # Find all .py files in this directory\n for py_file in search_path.glob(\"*.py\"):\n if py_file.name.startswith(\"_\"):\n continue # Skip __init__.py, etc.\n \n try:\n # Import the module - this triggers @override_system_tool decorators\n module_name = f\"tool_overrides.{py_file.stem}\"\n spec = importlib.util.spec_from_file_location(module_name, py_file)\n if spec and spec.loader:\n module = importlib.util.module_from_spec(spec)\n spec.loader.exec_module(module)\n logger.info(f\"✅ Loaded tool override module: {py_file.name}\")\n except Exception as e:\n logger.warning(f\"⚠️ Failed to load tool override module {py_file.name}: {e}\")", + "docstring": "Auto-discover and load tool override modules from the project.\n\nSearches common locations for tool override files:\n- ./tools/overrides/\n- ./custom_tools/\n- ./agent_tools/overrides/\n\nWhen override modules are imported, the @override_system_tool\ndecorators will automatically register the overrides.", + "dependencies": [ + "module_from_spec", + "info", + "hasattr", + "startswith", + "cwd", + "exec_module", + "Path", + "spec_from_file_location", + "exists", + "warning", + "is_dir", + "glob" + ], + "complexity": 11, + "business_context": "load, config, tool", + "imports": [ + "config", + "os", + "importlib.util", + "sys", + "re", + "pathlib" + ], + "called_functions": [ + "module_from_spec", + "info", + "hasattr", + "startswith", + "cwd", + "exec_module", + "Path", + "spec_from_file_location", + "exists", + "warning", + "is_dir", + "glob" + ], + "parent_class": "Agent", + "decorators": [], + "parameters": [ + { + "name": "self", + "type": "Any" + } + ], + "return_type": "None" + }, + { + "type": "method", + "name": "Agent._register_system_tools", + "filepath": "lib/laddr/src/laddr/core/agent_runtime.py", + "start_line": 362, + "end_line": 410, + "code": " def _register_system_tools(self):\n \"\"\"Register built-in system tools for delegation and artifacts.\"\"\"\n from .system_tools import create_system_tools\n from .tooling import Tool as _Tool\n \n # First, discover any user-provided tool overrides\n self._discover_tool_overrides()\n\n # Pass self (agent instance) so system tools can access current_job_id\n system_tools = create_system_tools(self.bus, self.storage, agent=self)\n\n # Disallow any system tool that could bypass delegation safeguards\n reserved = {\"delegate\", \"publish\", \"publish_task\"}\n \n # Delegation tools - only register for coordinator agents\n delegation_tools = {\n \"system_delegate_task\",\n \"system_delegate_parallel\", \n \"delegate_parallel\",\n \"system_split_document\",\n \"split_document\"\n }\n\n for name, tool_data in system_tools.items():\n # Skip reserved/unsafe tool names\n if name in reserved:\n continue\n \n # Skip delegation tools for non-coordinator agents\n if name in delegation_tools and not self.is_coordinator:\n continue\n \n # Unpack tool data - can be either (func, aliases) or just func\n if isinstance(tool_data, tuple):\n func, aliases = tool_data\n else:\n func = tool_data\n aliases = []\n \n # Register each system tool with the explicit provided name,\n # not the callable's __name__, so both prefixed and alias names exist.\n if not self.tools.has(name):\n try:\n desc = (func.__doc__ or \"\").strip().split(\"\\n\")[0]\n tool = _Tool(name=name, func=func, description=desc)\n self.tools.register(tool, name=name, aliases=aliases)\n except Exception:\n # Fallback to direct registration\n self.tools.register(func, name=name, aliases=aliases)", + "docstring": "Register built-in system tools for delegation and artifacts.", + "dependencies": [ + "isinstance", + "_Tool", + "_discover_tool_overrides", + "create_system_tools", + "split", + "strip", + "items", + "register", + "has" + ], + "complexity": 9, + "business_context": "tool, storage, agent", + "imports": [ + "sys", + "system_tools", + "re", + "tooling" + ], + "called_functions": [ + "isinstance", + "_Tool", + "_discover_tool_overrides", + "create_system_tools", + "split", + "strip", + "items", + "register", + "has" + ], + "parent_class": "Agent", + "decorators": [], + "parameters": [ + { + "name": "self", + "type": "Any" + } + ], + "return_type": "None" + }, + { + "type": "method", + "name": "Agent.plan", + "filepath": "lib/laddr/src/laddr/core/agent_runtime.py", + "start_line": 469, + "end_line": 491, + "code": " def plan(self, task: dict) -> list[dict]:\n \"\"\"\n Create execution plan for a task.\n \n Default implementation: simple single-step plan.\n Subclasses should override for custom planning logic.\n \n Args:\n task: Task inputs\n \n Returns:\n List of steps, where each step is a dict with:\n - action: \"tool\" | \"delegate\" | \"llm\" | \"custom\"\n - Additional fields depending on action type\n \"\"\"\n # Default: single LLM call step\n return [\n {\n \"action\": \"llm\",\n \"prompt\": f\"Task: {json.dumps(task)}\",\n \"system\": f\"You are {self.config.role}. Your goal: {self.config.goal}\"\n }\n ]", + "docstring": "Create execution plan for a task.\n\nDefault implementation: simple single-step plan.\nSubclasses should override for custom planning logic.\n\nArgs:\n task: Task inputs\n\nReturns:\n List of steps, where each step is a dict with:\n - action: \"tool\" | \"delegate\" | \"llm\" | \"custom\"\n - Additional fields depending on action type", + "dependencies": [ + "dumps" + ], + "complexity": 1, + "business_context": "create, tool, config, llm", + "imports": [ + "sys", + "json", + "config", + "re" + ], + "called_functions": [ + "dumps" + ], + "parent_class": "Agent", + "decorators": [], + "parameters": [ + { + "name": "self", + "type": "Any" + }, + { + "name": "task", + "type": "dict" + } + ], + "return_type": "list[dict]" + }, + { + "type": "method", + "name": "Agent._tool_cache_key", + "filepath": "lib/laddr/src/laddr/core/agent_runtime.py", + "start_line": 934, + "end_line": 938, + "code": " def _tool_cache_key(self, name: str, params: dict) -> str:\n \"\"\"Generate cache key for tool call.\"\"\"\n param_str = json.dumps(params, sort_keys=True)\n key_data = f\"{name}:{param_str}\"\n return f\"tool:{hashlib.md5(key_data.encode()).hexdigest()}\"", + "docstring": "Generate cache key for tool call.", + "dependencies": [ + "hexdigest", + "md5", + "encode", + "dumps" + ], + "complexity": 1, + "business_context": "cache, generate, tool", + "imports": [ + "re", + "json", + "hashlib" + ], + "called_functions": [ + "hexdigest", + "md5", + "encode", + "dumps" + ], + "parent_class": "Agent", + "decorators": [], + "parameters": [ + { + "name": "self", + "type": "Any" + }, + { + "name": "name", + "type": "str" + }, + { + "name": "params", + "type": "dict" + } + ], + "return_type": "str" + }, + { + "type": "method", + "name": "Agent._trace", + "filepath": "lib/laddr/src/laddr/core/agent_runtime.py", + "start_line": 1412, + "end_line": 1422, + "code": " def _trace(self, job_id: str, agent_name: str, event_type: str, payload: dict) -> None:\n \"\"\"Append a trace if enabled and not masked.\"\"\"\n if not self._trace_enabled:\n return\n if event_type in self._trace_mask:\n return\n try:\n self.database.append_trace(job_id, agent_name, event_type, payload)\n except Exception:\n # Tracing must never break runtime\n pass", + "docstring": "Append a trace if enabled and not masked.", + "dependencies": [ + "append_trace" + ], + "complexity": 4, + "business_context": "database, runtime", + "imports": [ + "re", + "time" + ], + "called_functions": [ + "append_trace" + ], + "parent_class": "Agent", + "decorators": [], + "parameters": [ + { + "name": "self", + "type": "Any" + }, + { + "name": "job_id", + "type": "str" + }, + { + "name": "agent_name", + "type": "str" + }, + { + "name": "event_type", + "type": "str" + }, + { + "name": "payload", + "type": "dict" + } + ], + "return_type": "None" + }, + { + "type": "method", + "name": "Agent._build_autonomous_system_prompt", + "filepath": "lib/laddr/src/laddr/core/agent_runtime.py", + "start_line": 1424, + "end_line": 1581, + "code": " def _build_autonomous_system_prompt(\n self,\n available_tools: list[str],\n available_agents: list[dict],\n can_delegate: bool,\n ) -> str:\n \"\"\"Build system prompt for autonomous agent.\"\"\"\n # Optional: legacy prompt.md (kept for backward compatibility) or explicit instructions passed at construction\n custom_prompt = self._extra_instructions\n if not custom_prompt:\n try:\n prompt_path = os.path.join(os.getcwd(), \"agents\", self.config.name, \"prompt.md\")\n if os.path.isfile(prompt_path):\n with open(prompt_path, \"r\", encoding=\"utf-8\") as f:\n custom_prompt = f.read().strip()\n except Exception:\n custom_prompt = None\n\n tool_list = \"\\n\".join([f\"- {t}\" for t in available_tools]) if available_tools else \"None\"\n\n agent_list = \"None\"\n if can_delegate and available_agents:\n agent_list = \"\\n\".join(\n [\n f\"- {a['name']}: {a.get('role', 'N/A')} - {a.get('goal', 'N/A')}\"\n for a in available_agents\n ]\n )\n\n header = f\"You are {self.config.role}.\\nYour goal: {self.config.goal}\"\n\n if custom_prompt:\n header += \"\\n\\nAgent Policy (from prompt.md):\\n\" + custom_prompt\n\n system = f\"\"\"{header}\n\nYou act autonomously to complete the task. Always respond with a SINGLE JSON object only.\n\nCRITICAL: \n- Output ONLY the JSON object, no markdown code fences (no ```json), no explanatory text\n- Do NOT add extra fields like \"action\", \"reasoning\", or \"explanation\"\n- Follow the EXACT schema below\n\nSchema:\n{{\n \"type\": \"tool|delegate|finish\",\n \"tool\": \"\",\n \"params\": {{\"param1\": \"value1\"}},\n \"agent\": \"\",\n \"task\": \"\",\n \"timeout\": 60,\n \"answer\": \"\"\n}}\n\n## Available Tools:\n{tool_list}\n\nTo use a tool:\n{{\"type\":\"tool\",\"tool\":\"\",\"params\":{{\"param\":\"value\"}}}}\n\"\"\"\n\n if can_delegate:\n system += f\"\"\"\n## Available Agents (for delegation):\n{agent_list}\n\nTo delegate a task:\n{{\"type\":\"delegate\",\"agent\":\"\",\"task\":\"\",\"timeout\":90}}\n\n⚠️ CRITICAL DELEGATION RULES:\n- ONLY use agent names from the list above - do NOT invent names\n- If no listed agent matches your needs, use tools or finish instead\n- Each agent has specific expertise (role/goal) - choose the most appropriate one\n- Delegation is for complex subtasks that require specialized processing\n\n## Key System Tools:\n\n### Parallel Processing (for independent subtasks):\n{{\"type\":\"tool\",\"tool\":\"system_delegate_parallel\",\"params\":{{\"agent\":\"\",\"tasks\":[\"task1\",\"task2\",\"task3\"]}}}}\n- Use when you have multiple independent subtasks that can run simultaneously\n- All tasks execute in parallel and results are aggregated\n- Ideal for: processing multiple documents, analyzing multiple data points, gathering info from multiple sources\n\n### Document Splitting (for large content):\n{{\"type\":\"tool\",\"tool\":\"system_split_document\",\"params\":{{\"document\":\"\",\"num_chunks\":3}}}}\n- Use before parallel processing to divide large documents into manageable chunks\n- Returns list of chunks that can be passed to system_delegate_parallel\n\n### Artifact Storage (for sharing data):\n{{\"type\":\"tool\",\"tool\":\"system_store_artifact\",\"params\":{{\"data\":\"\",\"artifact_type\":\"result\",\"metadata\":{{\"description\":\"...\"}}}}}}\n- Store results, intermediate data, or large outputs for other agents to retrieve\n- Use when delegating and the subtask needs access to data you've gathered\n- Returns artifact_id which can be used for retrieval\n\n### Artifact Retrieval:\n{{\"type\":\"tool\",\"tool\":\"system_retrieve_artifact\",\"params\":{{\"artifact_id\":\"\",\"artifact_type\":\"result\"}}}}\n- Retrieve previously stored artifacts by artifact_id (returned from store_artifact)\n- Useful when building on work from previous steps or other agents\n - IMPORTANT: Do NOT attempt to retrieve artifacts using job_id or task_id. Only call\n `system_retrieve_artifact` when you have an explicit artifact_id (UUID)\n returned by a previous store_artifact call or delegation response.\n If a delegated agent returned its result inline (i.e., the response field contains the data),\n you do NOT need to call artifact retrieval — synthesize from the inline result instead.\n Calling artifact retrieval with job/task ids often fails and causes unnecessary retries.\n\"\"\"\n else:\n # Explicitly tell non-coordinator agents they CANNOT delegate\n system += \"\"\"\n⚠️ IMPORTANT: You are a specialist agent and CANNOT delegate tasks to other agents.\n- Use your available tools to complete the task yourself\n- DO NOT attempt to use system_delegate_task or any delegation tools\n- Focus on using your specialized tools to complete the task\n- Synthesize findings from your tool calls and finish with a comprehensive answer\n\n## Artifact Storage (ONLY for large results):\n⚠️ CRITICAL: DO NOT use artifact tools unless explicitly storing/retrieving YOUR OWN large data (>100KB).\n\n### When to store artifacts:\n- Your final result/answer exceeds 100KB in size\n- You need to save intermediate data for your own later use\n- Example: {{\"type\":\"tool\",\"tool\":\"system_store_artifact\",\"params\":{{\"data\":\"\",\"artifact_type\":\"result\"}}}}\n- Returns artifact_id for later retrieval\n\n### When to retrieve artifacts:\n- ONLY if you previously stored an artifact and have its artifact_id (UUID)\n- DO NOT try to retrieve artifacts you didn't create\n- DO NOT use random search terms or keys\n- Example: {{\"type\":\"tool\",\"tool\":\"system_retrieve_artifact\",\"params\":{{\"artifact_id\":\"\",\"artifact_type\":\"result\"}}}}\n\n⚠️ DO NOT use system_retrieve_artifact to search for data - use your domain tools instead!\n\"\"\"\n\n system += \"\"\"\nWhen you complete the task:\n{{\"type\":\"finish\",\"answer\":\"\"}}\n\n## Execution Guidelines:\n- Use tools strategically - avoid redundant calls for the same information\"\"\"\n\n if can_delegate:\n system += \"\"\"\n- Delegate complex subtasks to specialized agents when available\n- Use parallel processing for independent subtasks to improve efficiency\n- Store intermediate results as artifacts when they'll be reused\"\"\"\n else:\n system += \"\"\"\n- Make the most of your specialized tools - they are designed for your domain\n- Process information directly rather than attempting delegation\n- Synthesize findings from multiple tool calls when needed\"\"\"\n\n system += \"\"\"\n- Focus on quality synthesis over quantity of actions\n- Finish when you have sufficient information to provide a comprehensive answer\n\nThink step-by-step internally, but output ONLY the JSON action object. No explanatory text.\"\"\"\n\n\n return system", + "docstring": "Build system prompt for autonomous agent.", + "dependencies": [ + "get", + "getcwd", + "join", + "isfile", + "strip", + "read", + "open" + ], + "complexity": 10, + "business_context": "config, agent, save, create, tool", + "imports": [ + "uuid", + "config", + "os", + "sys", + "json", + "re", + "time" + ], + "called_functions": [ + "get", + "getcwd", + "join", + "isfile", + "strip", + "read", + "open" + ], + "parent_class": "Agent", + "decorators": [], + "parameters": [ + { + "name": "self", + "type": "Any" + }, + { + "name": "available_tools", + "type": "list[str]" + }, + { + "name": "available_agents", + "type": "list[dict]" + }, + { + "name": "can_delegate", + "type": "bool" + } + ], + "return_type": "str" + }, + { + "type": "method", + "name": "Agent._build_autonomous_user_prompt", + "filepath": "lib/laddr/src/laddr/core/agent_runtime.py", + "start_line": 1583, + "end_line": 1636, + "code": " def _build_autonomous_user_prompt(\n self,\n task_description: str,\n history: list[dict],\n iteration: int\n ) -> str:\n \"\"\"Build user prompt with task and history.\"\"\"\n \n prompt = f\"TASK: {task_description}\\n\\n\"\n \n if history:\n prompt += \"HISTORY:\\n\"\n for i, entry in enumerate(history):\n if entry[\"action\"] == \"tool\":\n prompt += f\"{i+1}. Used tool '{entry['tool']}' with params {entry.get('params', {})}\\n\"\n if \"error\" in entry:\n prompt += f\" ERROR: {entry['error']}\\n\"\n else:\n result_str = str(entry.get('result', ''))[:500]\n prompt += f\" RESULT: {result_str}\\n\"\n \n elif entry[\"action\"] == \"delegate\":\n prompt += f\"{i+1}. Delegated to agent '{entry['agent']}': {entry.get('task', 'N/A')}\\n\"\n if \"error\" in entry:\n prompt += f\" ERROR: {entry['error']}\\n\"\n else:\n result_str = str(entry.get('result', ''))[:500]\n prompt += f\" RESULT: {result_str}\\n\"\n \n elif entry[\"action\"] == \"think\":\n prompt += f\"{i+1}. THOUGHT: {entry.get('thought', '')[:200]}\\n\"\n \n elif entry[\"action\"] == \"system_hint\":\n prompt += f\"\\n⚠️ SYSTEM: {entry.get('message', '')}\\n\\n\"\n \n prompt += \"\\n\"\n\n # If a delegation result is present in history, add a short SYSTEM hint\n # so the LLM prefers synthesizing and finishing over re-delegating the\n # same task. This reduces redundant delegations and wasted tool calls.\n has_delegate_result = False\n for entry in history:\n if entry.get(\"action\") == \"delegate\" and \"result\" in entry and entry.get(\"status\") in {\"completed\", \"success\", None}:\n has_delegate_result = True\n break\n\n if has_delegate_result:\n prompt += \"\\nSYSTEM_HINT: A delegated agent has returned a result above. \"\n prompt += \"Prefer synthesizing a final answer from that result and other history. \"\n prompt += \"Do NOT re-delegate the same task unless the returned result is clearly insufficient or explicitly requests further delegation.\\n\\n\"\n\n prompt += f\"What should you do next? (Iteration {iteration + 1})\\n\"\n\n return prompt", + "docstring": "Build user prompt with task and history.", + "dependencies": [ + "enumerate", + "str", + "get" + ], + "complexity": 13, + "business_context": "message, tool, llm, agent", + "imports": [ + "sys", + "re" + ], + "called_functions": [ + "enumerate", + "str", + "get" + ], + "parent_class": "Agent", + "decorators": [], + "parameters": [ + { + "name": "self", + "type": "Any" + }, + { + "name": "task_description", + "type": "str" + }, + { + "name": "history", + "type": "list[dict]" + }, + { + "name": "iteration", + "type": "int" + } + ], + "return_type": "str" + }, + { + "type": "method", + "name": "Agent._format_history_for_synthesis", + "filepath": "lib/laddr/src/laddr/core/agent_runtime.py", + "start_line": 1638, + "end_line": 1654, + "code": " def _format_history_for_synthesis(self, history: list[dict]) -> str:\n \"\"\"Format execution history for final answer synthesis.\"\"\"\n formatted = []\n for i, entry in enumerate(history, 1):\n action = entry.get(\"action\", \"unknown\")\n if action == \"tool\":\n tool_name = entry.get(\"tool\", \"unknown\")\n result = entry.get(\"result\", entry.get(\"error\", \"No result\"))\n # Truncate large results\n result_str = str(result)[:500]\n formatted.append(f\"{i}. Tool '{tool_name}' returned: {result_str}\")\n elif action == \"delegate\":\n agent = entry.get(\"agent\", \"unknown\")\n result = entry.get(\"result\", entry.get(\"error\", \"No result\"))\n formatted.append(f\"{i}. Delegated to '{agent}', result: {result}\")\n \n return \"\\n\".join(formatted) if formatted else \"No information gathered yet.\"", + "docstring": "Format execution history for final answer synthesis.", + "dependencies": [ + "append", + "get", + "join", + "str", + "enumerate" + ], + "complexity": 4, + "business_context": "tool, agent", + "imports": [ + "re" + ], + "called_functions": [ + "append", + "get", + "join", + "str", + "enumerate" + ], + "parent_class": "Agent", + "decorators": [], + "parameters": [ + { + "name": "self", + "type": "Any" + }, + { + "name": "history", + "type": "list[dict]" + } + ], + "return_type": "str" + }, + { + "type": "method", + "name": "Agent._parse_autonomous_action", + "filepath": "lib/laddr/src/laddr/core/agent_runtime.py", + "start_line": 1656, + "end_line": 1700, + "code": " def _parse_autonomous_action(self, response: str) -> dict:\n \"\"\"Parse LLM response to extract action, preferring JSON format with text fallback.\"\"\"\n # 1) Try JSON extraction anywhere in the response\n try:\n start = response.find(\"{\")\n end = response.rfind(\"}\")\n if start != -1 and end != -1 and end > start:\n payload = response[start : end + 1]\n data = json.loads(payload)\n # Normalize keys\n norm = {str(k).lower(): v for k, v in data.items()}\n action_type = norm.get(\"type\") or norm.get(\"action\") or \"think\"\n action_type = str(action_type).lower()\n \n # Handle LLM putting tool name in \"action\" field instead of \"type\"\n # e.g., {\"action\": \"web_search\", \"tool\": \"web_search\", ...}\n if action_type not in [\"tool\", \"delegate\", \"finish\", \"think\"]:\n # LLM likely put the tool name in \"action\" field\n # Check if there's a \"tool\" field, or use \"action\" value as tool name\n if norm.get(\"tool\") or norm.get(\"params\"):\n # This looks like a tool call\n tool = norm.get(\"tool\") or action_type\n params = norm.get(\"params\") or {}\n return {\"type\": \"tool\", \"tool\": tool, \"params\": params}\n \n # Map common aliases\n if action_type == \"tool\":\n tool = norm.get(\"tool\") or norm.get(\"tool_name\")\n params = norm.get(\"params\") or {}\n return {\"type\": \"tool\", \"tool\": tool, \"params\": params}\n if action_type == \"delegate\":\n agent = norm.get(\"agent\") or norm.get(\"target\")\n task = norm.get(\"task\") or norm.get(\"query\") or norm.get(\"message\")\n timeout = norm.get(\"timeout\") or 60\n return {\"type\": \"delegate\", \"agent\": agent, \"task\": task, \"timeout\": timeout}\n if action_type == \"finish\":\n answer = norm.get(\"answer\") or norm.get(\"result\") or response\n return {\"type\": \"finish\", \"answer\": answer}\n # Default think\n return {\"type\": \"think\"}\n except Exception:\n pass\n\n # 2) Fallback to text parser for legacy format\n return self._parse_autonomous_action_text(response)", + "docstring": "Parse LLM response to extract action, preferring JSON format with text fallback.", + "dependencies": [ + "get", + "loads", + "find", + "str", + "rfind", + "_parse_autonomous_action_text", + "items", + "lower" + ], + "complexity": 19, + "business_context": "llm, agent, query, parse, tool", + "imports": [ + "re", + "time", + "json" + ], + "called_functions": [ + "get", + "loads", + "find", + "str", + "rfind", + "_parse_autonomous_action_text", + "items", + "lower" + ], + "parent_class": "Agent", + "decorators": [], + "parameters": [ + { + "name": "self", + "type": "Any" + }, + { + "name": "response", + "type": "str" + } + ], + "return_type": "dict" + }, + { + "type": "method", + "name": "Agent._parse_autonomous_action_text", + "filepath": "lib/laddr/src/laddr/core/agent_runtime.py", + "start_line": 1702, + "end_line": 1736, + "code": " def _parse_autonomous_action_text(self, response: str) -> dict:\n \"\"\"Parse legacy text format with ACTION:/AGENT:/TOOL_NAME:/... lines.\"\"\"\n lines = response.strip().split(\"\\n\")\n action_type = None\n data: dict[str, Any] = {}\n for line in lines:\n line = line.strip()\n if line.startswith(\"ACTION:\"):\n action_str = line.replace(\"ACTION:\", \"\").strip().upper()\n if \"TOOL\" in action_str:\n action_type = \"tool\"\n elif \"DELEGATE\" in action_str:\n action_type = \"delegate\"\n elif \"FINISH\" in action_str:\n action_type = \"finish\"\n elif line.startswith(\"TOOL_NAME:\"):\n data[\"tool\"] = line.replace(\"TOOL_NAME:\", \"\").strip()\n elif line.startswith(\"PARAMS:\"):\n params_str = line.replace(\"PARAMS:\", \"\").strip()\n try:\n data[\"params\"] = json.loads(params_str)\n except Exception:\n data[\"params\"] = {}\n elif line.startswith(\"AGENT:\"):\n data[\"agent\"] = line.replace(\"AGENT:\", \"\").strip()\n elif line.startswith(\"TASK:\"):\n data[\"task\"] = line.replace(\"TASK:\", \"\").strip()\n elif line.startswith(\"ANSWER:\"):\n # Capture everything after ANSWER:\n answer_index = response.find(\"ANSWER:\")\n if answer_index != -1:\n data[\"answer\"] = response[answer_index + 7 :].strip()\n else:\n data[\"answer\"] = line.replace(\"ANSWER:\", \"\").strip()\n return {\"type\": action_type or \"think\", **data}", + "docstring": "Parse legacy text format with ACTION:/AGENT:/TOOL_NAME:/... lines.", + "dependencies": [ + "upper", + "loads", + "startswith", + "find", + "split", + "strip", + "replace" + ], + "complexity": 14, + "business_context": "parse, tool, agent", + "imports": [ + "re", + "json" + ], + "called_functions": [ + "upper", + "loads", + "startswith", + "find", + "split", + "strip", + "replace" + ], + "parent_class": "Agent", + "decorators": [], + "parameters": [ + { + "name": "self", + "type": "Any" + }, + { + "name": "response", + "type": "str" + } + ], + "return_type": "dict" + }, + { + "type": "class", + "name": "MCPToolSource", + "filepath": "lib/laddr/src/laddr/core/mcp_client.py", + "start_line": 32, + "end_line": 35, + "code": "class MCPToolSource:\n \"\"\"Placeholder - MCP feature disabled for this release.\"\"\"", + "docstring": "Placeholder - MCP feature disabled for this release.", + "dependencies": [], + "complexity": 2, + "business_context": "", + "imports": [], + "called_functions": [], + "parent_class": "", + "decorators": [], + "parameters": [], + "return_type": "" + }, + { + "type": "method", + "name": "MCPToolSource.__init__", + "filepath": "lib/laddr/src/laddr/core/mcp_client.py", + "start_line": 34, + "end_line": 35, + "code": " def __init__(self, *args, **kwargs):\n raise NotImplementedError(\"MCP feature is not available in this release\")", + "docstring": "", + "dependencies": [ + "NotImplementedError" + ], + "complexity": 1, + "business_context": "", + "imports": [], + "called_functions": [ + "NotImplementedError" + ], + "parent_class": "MCPToolSource", + "decorators": [], + "parameters": [ + { + "name": "self", + "type": "Any" + } + ], + "return_type": "None" + }, + { + "type": "class", + "name": "MCPToolRegistry", + "filepath": "lib/laddr/src/laddr/core/mcp_client.py", + "start_line": 38, + "end_line": 41, + "code": "class MCPToolRegistry:\n \"\"\"Placeholder - MCP feature disabled for this release.\"\"\"", + "docstring": "Placeholder - MCP feature disabled for this release.", + "dependencies": [], + "complexity": 2, + "business_context": "", + "imports": [], + "called_functions": [], + "parent_class": "", + "decorators": [], + "parameters": [], + "return_type": "" + }, + { + "type": "method", + "name": "MCPToolRegistry.__init__", + "filepath": "lib/laddr/src/laddr/core/mcp_client.py", + "start_line": 40, + "end_line": 41, + "code": " def __init__(self, *args, **kwargs):\n raise NotImplementedError(\"MCP feature is not available in this release\")", + "docstring": "", + "dependencies": [ + "NotImplementedError" + ], + "complexity": 1, + "business_context": "", + "imports": [], + "called_functions": [ + "NotImplementedError" + ], + "parent_class": "MCPToolRegistry", + "decorators": [], + "parameters": [ + { + "name": "self", + "type": "Any" + } + ], + "return_type": "None" + }, + { + "type": "class", + "name": "S3Storage", + "filepath": "lib/laddr/src/laddr/core/storage.py", + "start_line": 14, + "end_line": 254, + "code": "class S3Storage:\n \"\"\"\n S3-compatible storage backend for AWS S3 and MinIO.\n \n This class uses the minio Python package which is fully compatible with:\n - AWS S3 (cloud)\n - MinIO (self-hosted)\n - Any S3-compatible storage service\n \n For AWS S3:\n endpoint: \"s3.amazonaws.com\" or \"s3..amazonaws.com\"\n access_key: AWS Access Key ID\n secret_key: AWS Secret Access Key\n secure: True (use HTTPS)\n \n For MinIO:\n endpoint: \"localhost:9000\" or \"minio:9000\"\n access_key: MinIO access key (default: minioadmin)\n secret_key: MinIO secret key\n secure: False (or True for HTTPS)\n \"\"\"", + "docstring": "S3-compatible storage backend for AWS S3 and MinIO.\n\nThis class uses the minio Python package which is fully compatible with:\n- AWS S3 (cloud)\n- MinIO (self-hosted)\n- Any S3-compatible storage service\n\nFor AWS S3:\n endpoint: \"s3.amazonaws.com\" or \"s3..amazonaws.com\"\n access_key: AWS Access Key ID\n secret_key: AWS Secret Access Key\n secure: True (use HTTPS)\n\nFor MinIO:\n endpoint: \"localhost:9000\" or \"minio:9000\"\n access_key: MinIO access key (default: minioadmin)\n secret_key: MinIO secret key\n secure: False (or True for HTTPS)", + "dependencies": [], + "complexity": 9, + "business_context": "storage", + "imports": [], + "called_functions": [], + "parent_class": "", + "decorators": [], + "parameters": [], + "return_type": "" + }, + { + "type": "method", + "name": "S3Storage.__init__", + "filepath": "lib/laddr/src/laddr/core/storage.py", + "start_line": 36, + "end_line": 59, + "code": " def __init__(\n self,\n endpoint: str,\n access_key: str,\n secret_key: str,\n secure: bool = False,\n region: str | None = None\n ):\n \"\"\"\n Initialize S3-compatible storage.\n \n Args:\n endpoint: S3 endpoint (e.g., \"s3.amazonaws.com\" or \"minio:9000\")\n access_key: Access key (AWS Access Key ID or MinIO access key)\n secret_key: Secret key (AWS Secret Access Key or MinIO secret key)\n secure: Use HTTPS (True for AWS S3, False for local MinIO)\n region: AWS region (optional, e.g., \"us-east-1\")\n \"\"\"\n self.endpoint = endpoint\n self.access_key = access_key\n self.secret_key = secret_key\n self.secure = secure\n self.region = region\n self._client = None", + "docstring": "Initialize S3-compatible storage.\n\nArgs:\n endpoint: S3 endpoint (e.g., \"s3.amazonaws.com\" or \"minio:9000\")\n access_key: Access key (AWS Access Key ID or MinIO access key)\n secret_key: Secret key (AWS Secret Access Key or MinIO secret key)\n secure: Use HTTPS (True for AWS S3, False for local MinIO)\n region: AWS region (optional, e.g., \"us-east-1\")", + "dependencies": [], + "complexity": 1, + "business_context": "initialize, storage", + "imports": [ + "io", + "minio" + ], + "called_functions": [], + "parent_class": "S3Storage", + "decorators": [], + "parameters": [ + { + "name": "self", + "type": "Any" + }, + { + "name": "endpoint", + "type": "str" + }, + { + "name": "access_key", + "type": "str" + }, + { + "name": "secret_key", + "type": "str" + }, + { + "name": "secure", + "type": "bool" + }, + { + "name": "region", + "type": "str | None" + } + ], + "return_type": "None" + }, + { + "type": "method", + "name": "S3Storage._get_client", + "filepath": "lib/laddr/src/laddr/core/storage.py", + "start_line": 61, + "end_line": 77, + "code": " def _get_client(self):\n \"\"\"Get synchronous S3-compatible client.\"\"\"\n if self._client is None:\n try:\n from minio import Minio\n self._client = Minio(\n self.endpoint,\n access_key=self.access_key,\n secret_key=self.secret_key,\n secure=self.secure,\n region=self.region\n )\n except ImportError:\n raise RuntimeError(\n \"minio package not installed. Install with: pip install minio\"\n )\n return self._client", + "docstring": "Get synchronous S3-compatible client.", + "dependencies": [ + "Minio", + "RuntimeError" + ], + "complexity": 3, + "business_context": "", + "imports": [ + "io", + "minio" + ], + "called_functions": [ + "Minio", + "RuntimeError" + ], + "parent_class": "S3Storage", + "decorators": [], + "parameters": [ + { + "name": "self", + "type": "Any" + } + ], + "return_type": "None" + }, + { + "type": "class", + "name": "InMemoryStorage", + "filepath": "lib/laddr/src/laddr/core/storage.py", + "start_line": 261, + "end_line": 325, + "code": "class InMemoryStorage:\n \"\"\"In-memory storage backend for testing.\"\"\"\n", + "docstring": "In-memory storage backend for testing.", + "dependencies": [], + "complexity": 8, + "business_context": "storage", + "imports": [], + "called_functions": [], + "parent_class": "", + "decorators": [], + "parameters": [], + "return_type": "" + }, + { + "type": "method", + "name": "InMemoryStorage.__init__", + "filepath": "lib/laddr/src/laddr/core/storage.py", + "start_line": 264, + "end_line": 266, + "code": " def __init__(self):\n \"\"\"Initialize in-memory storage.\"\"\"\n self._storage: dict[str, dict[str, tuple[bytes, dict]]] = {}", + "docstring": "Initialize in-memory storage.", + "dependencies": [], + "complexity": 1, + "business_context": "initialize, storage", + "imports": [], + "called_functions": [], + "parent_class": "InMemoryStorage", + "decorators": [], + "parameters": [ + { + "name": "self", + "type": "Any" + } + ], + "return_type": "None" + }, + { + "type": "function", + "name": "_ensure", + "filepath": "lib/laddr/src/laddr/core/storage.py", + "start_line": 92, + "end_line": 106, + "code": " def _ensure():\n client = self._get_client()\n if not client.bucket_exists(bucket):\n try:\n # Create bucket with region if specified (required for AWS S3)\n if self.region:\n client.make_bucket(bucket, location=self.region)\n else:\n client.make_bucket(bucket)\n print(f\"✓ Created storage bucket: {bucket}\")\n except Exception as e:\n raise RuntimeError(\n f\"Bucket '{bucket}' does not exist and could not be created. \"\n f\"Error: {e}. Check permissions and configuration.\"\n )", + "docstring": "", + "dependencies": [ + "print", + "RuntimeError", + "bucket_exists", + "make_bucket", + "_get_client" + ], + "complexity": 4, + "business_context": "create, storage", + "imports": [ + "io" + ], + "called_functions": [ + "print", + "RuntimeError", + "bucket_exists", + "make_bucket", + "_get_client" + ], + "parent_class": "", + "decorators": [], + "parameters": [], + "return_type": "None" + }, + { + "type": "function", + "name": "_put", + "filepath": "lib/laddr/src/laddr/core/storage.py", + "start_line": 131, + "end_line": 156, + "code": " def _put():\n client = self._get_client()\n # MinIO signing can fail if content-type is passed inside metadata,\n # so map common keys to the dedicated content_type parameter and\n # pass only non-reserved metadata keys.\n content_type = None\n meta = None\n if metadata:\n # Extract content type from common variations\n content_type = (\n metadata.get(\"content-type\")\n or metadata.get(\"Content-Type\")\n or metadata.get(\"content_type\")\n )\n # Filter out content-type keys from metadata dict\n meta = {k: v for k, v in metadata.items() if k.lower() not in (\"content-type\", \"content_type\")}\n\n client.put_object(\n bucket,\n key,\n io.BytesIO(data),\n length=len(data),\n content_type=content_type,\n metadata=meta,\n )\n return True", + "docstring": "", + "dependencies": [ + "get", + "BytesIO", + "len", + "items", + "lower", + "put_object", + "_get_client" + ], + "complexity": 3, + "business_context": "filter", + "imports": [ + "io" + ], + "called_functions": [ + "get", + "BytesIO", + "len", + "items", + "lower", + "put_object", + "_get_client" + ], + "parent_class": "", + "decorators": [], + "parameters": [], + "return_type": "None" + }, + { + "type": "function", + "name": "_get", + "filepath": "lib/laddr/src/laddr/core/storage.py", + "start_line": 171, + "end_line": 177, + "code": " def _get():\n client = self._get_client()\n response = client.get_object(bucket, key)\n data = response.read()\n response.close()\n response.release_conn()\n return data", + "docstring": "", + "dependencies": [ + "read", + "get_object", + "release_conn", + "close", + "_get_client" + ], + "complexity": 1, + "business_context": "", + "imports": [], + "called_functions": [ + "read", + "get_object", + "release_conn", + "close", + "_get_client" + ], + "parent_class": "", + "decorators": [], + "parameters": [], + "return_type": "None" + }, + { + "type": "function", + "name": "_list", + "filepath": "lib/laddr/src/laddr/core/storage.py", + "start_line": 198, + "end_line": 213, + "code": " def _list():\n client = self._get_client()\n objects = []\n\n for obj in client.list_objects(bucket, prefix=prefix):\n objects.append({\n \"key\": obj.object_name,\n \"size\": obj.size,\n \"last_modified\": obj.last_modified.isoformat() if obj.last_modified else None,\n \"etag\": obj.etag\n })\n\n if len(objects) >= max_keys:\n break\n\n return objects", + "docstring": "", + "dependencies": [ + "append", + "isoformat", + "list_objects", + "len", + "_get_client" + ], + "complexity": 3, + "business_context": "", + "imports": [], + "called_functions": [ + "append", + "isoformat", + "list_objects", + "len", + "_get_client" + ], + "parent_class": "", + "decorators": [], + "parameters": [], + "return_type": "None" + }, + { + "type": "function", + "name": "_delete", + "filepath": "lib/laddr/src/laddr/core/storage.py", + "start_line": 228, + "end_line": 231, + "code": " def _delete():\n client = self._get_client()\n client.remove_object(bucket, key)\n return True", + "docstring": "", + "dependencies": [ + "remove_object", + "_get_client" + ], + "complexity": 1, + "business_context": "", + "imports": [], + "called_functions": [ + "remove_object", + "_get_client" + ], + "parent_class": "", + "decorators": [], + "parameters": [], + "return_type": "None" + }, + { + "type": "function", + "name": "_exists", + "filepath": "lib/laddr/src/laddr/core/storage.py", + "start_line": 246, + "end_line": 252, + "code": " def _exists():\n client = self._get_client()\n try:\n client.stat_object(bucket, key)\n return True\n except Exception:\n return False", + "docstring": "", + "dependencies": [ + "stat_object", + "_get_client" + ], + "complexity": 2, + "business_context": "", + "imports": [ + "io" + ], + "called_functions": [ + "stat_object", + "_get_client" + ], + "parent_class": "", + "decorators": [], + "parameters": [], + "return_type": "None" + }, + { + "type": "class", + "name": "Job", + "filepath": "lib/laddr/src/laddr/core/database.py", + "start_line": 26, + "end_line": 37, + "code": "class Job(Base):\n \"\"\"Job execution record (legacy terminology).\"\"\"\n\n __tablename__ = \"jobs\"\n\n job_id = Column(String(36), primary_key=True)\n pipeline_name = Column(String(255), nullable=False)\n status = Column(String(50), default=\"pending\") # pending, running, completed, failed\n inputs = Column(JSON, nullable=False)\n outputs = Column(JSON, nullable=True)\n created_at = Column(DateTime, default=datetime.utcnow)\n completed_at = Column(DateTime, nullable=True)", + "docstring": "Job execution record (legacy terminology).", + "dependencies": [ + "Base" + ], + "complexity": 9, + "business_context": "", + "imports": [], + "called_functions": [], + "parent_class": "", + "decorators": [], + "parameters": [], + "return_type": "" + }, + { + "type": "class", + "name": "PromptExecution", + "filepath": "lib/laddr/src/laddr/core/database.py", + "start_line": 40, + "end_line": 51, + "code": "class PromptExecution(Base):\n \"\"\"Prompt execution record (new terminology for pipeline/job).\"\"\"\n\n __tablename__ = \"prompt_executions\"\n\n prompt_id = Column(String(36), primary_key=True)\n prompt_name = Column(String(255), nullable=False) # Agent/prompt name\n status = Column(String(50), default=\"pending\") # pending, running, completed, failed\n inputs = Column(JSON, nullable=False)\n outputs = Column(JSON, nullable=True)\n created_at = Column(DateTime, default=datetime.utcnow)\n completed_at = Column(DateTime, nullable=True)", + "docstring": "Prompt execution record (new terminology for pipeline/job).", + "dependencies": [ + "Base" + ], + "complexity": 9, + "business_context": "agent", + "imports": [], + "called_functions": [], + "parent_class": "", + "decorators": [], + "parameters": [], + "return_type": "" + }, + { + "type": "class", + "name": "Trace", + "filepath": "lib/laddr/src/laddr/core/database.py", + "start_line": 54, + "end_line": 65, + "code": "class Trace(Base):\n \"\"\"Trace event for observability with hierarchical span support.\"\"\"\n\n __tablename__ = \"traces\"\n\n id = Column(Integer, primary_key=True, autoincrement=True)\n job_id = Column(String(36), nullable=False, index=True)\n agent_name = Column(String(255), nullable=False, index=True)\n event_type = Column(String(100), nullable=False) # task_start, task_complete, tool_call, etc.\n parent_id = Column(Integer, nullable=True, index=True) # For hierarchical traces (span parent)\n payload = Column(JSON, nullable=False)\n timestamp = Column(DateTime, default=datetime.utcnow, index=True)", + "docstring": "Trace event for observability with hierarchical span support.", + "dependencies": [ + "Base" + ], + "complexity": 9, + "business_context": "", + "imports": [], + "called_functions": [], + "parent_class": "", + "decorators": [], + "parameters": [], + "return_type": "" + }, + { + "type": "class", + "name": "Memory", + "filepath": "lib/laddr/src/laddr/core/database.py", + "start_line": 68, + "end_line": 78, + "code": "class Memory(Base):\n \"\"\"Agent memory storage.\"\"\"\n\n __tablename__ = \"memory\"\n\n id = Column(Integer, primary_key=True, autoincrement=True)\n agent_name = Column(String(255), nullable=False, index=True)\n job_id = Column(String(36), nullable=True, index=True) # Optional: job-specific memory\n key = Column(String(255), nullable=False)\n value = Column(JSON, nullable=False)\n updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)", + "docstring": "Agent memory storage.", + "dependencies": [ + "Base" + ], + "complexity": 8, + "business_context": "storage, agent", + "imports": [], + "called_functions": [], + "parent_class": "", + "decorators": [], + "parameters": [], + "return_type": "" + }, + { + "type": "class", + "name": "AgentRegistry", + "filepath": "lib/laddr/src/laddr/core/database.py", + "start_line": 81, + "end_line": 88, + "code": "class AgentRegistry(Base):\n \"\"\"Agent registration and metadata.\"\"\"\n\n __tablename__ = \"agent_registry\"\n\n agent_name = Column(String(255), primary_key=True)\n meta = Column(JSON, nullable=False) # role, goal, tools, status, host_url, etc.\n last_seen = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)", + "docstring": "Agent registration and metadata.", + "dependencies": [ + "Base" + ], + "complexity": 5, + "business_context": "agent", + "imports": [], + "called_functions": [], + "parent_class": "", + "decorators": [], + "parameters": [], + "return_type": "" + }, + { + "type": "class", + "name": "DatabaseService", + "filepath": "lib/laddr/src/laddr/core/database.py", + "start_line": 91, + "end_line": 577, + "code": "class DatabaseService:\n \"\"\"\n High-level database API.\n \n Provides methods for job management, tracing, memory, and agent registry.\n Supports both Postgres and SQLite.\n \"\"\"\n", + "docstring": "High-level database API.\n\nProvides methods for job management, tracing, memory, and agent registry.\nSupports both Postgres and SQLite.", + "dependencies": [], + "complexity": 24, + "business_context": "database, agent", + "imports": [], + "called_functions": [], + "parent_class": "", + "decorators": [], + "parameters": [], + "return_type": "" + }, + { + "type": "method", + "name": "DatabaseService.__init__", + "filepath": "lib/laddr/src/laddr/core/database.py", + "start_line": 99, + "end_line": 129, + "code": " def __init__(self, database_url: str):\n \"\"\"\n Initialize database service.\n \n Args:\n database_url: SQLAlchemy connection string\n e.g., \"postgresql://user:pass@host:5432/db\"\n or \"sqlite:///./laddr.db\" for local dev\n \"\"\"\n # Fallback to SQLite if URL is None or connection fails\n if not database_url:\n logger.warning(\"DATABASE_URL not set, using local SQLite\")\n database_url = \"sqlite:///laddr.db\"\n \n try:\n self.engine = create_engine(database_url, echo=False)\n # Test connection\n with self.engine.connect() as conn:\n conn.execute(text(\"SELECT 1\"))\n logger.info(f\"Connected to database: {database_url.split('@')[-1] if '@' in database_url else database_url}\")\n except Exception as e:\n db_type = database_url.split('://')[0] if '://' in database_url else 'unknown'\n logger.warning(f\"Failed to connect to {db_type} database: {e}\")\n logger.info(\"Falling back to SQLite database\")\n database_url = \"sqlite:///laddr.db\"\n self.engine = create_engine(database_url, echo=False)\n \n self.SessionLocal = sessionmaker(bind=self.engine)\n\n # Create tables if they don't exist\n Base.metadata.create_all(self.engine)", + "docstring": "Initialize database service.\n\nArgs:\n database_url: SQLAlchemy connection string\n e.g., \"postgresql://user:pass@host:5432/db\"\n or \"sqlite:///./laddr.db\" for local dev", + "dependencies": [ + "info", + "create_engine", + "text", + "connect", + "split", + "create_all", + "execute", + "warning", + "sessionmaker" + ], + "complexity": 4, + "business_context": "initialize, database, create", + "imports": [], + "called_functions": [ + "info", + "create_engine", + "text", + "connect", + "split", + "create_all", + "execute", + "warning", + "sessionmaker" + ], + "parent_class": "DatabaseService", + "decorators": [], + "parameters": [ + { + "name": "self", + "type": "Any" + }, + { + "name": "database_url", + "type": "str" + } + ], + "return_type": "None" + }, + { + "type": "method", + "name": "DatabaseService.create_tables", + "filepath": "lib/laddr/src/laddr/core/database.py", + "start_line": 131, + "end_line": 133, + "code": " def create_tables(self):\n \"\"\"Create all database tables if they don't exist.\"\"\"\n Base.metadata.create_all(self.engine)", + "docstring": "Create all database tables if they don't exist.", + "dependencies": [ + "create_all" + ], + "complexity": 1, + "business_context": "database, create", + "imports": [], + "called_functions": [ + "create_all" + ], + "parent_class": "DatabaseService", + "decorators": [], + "parameters": [ + { + "name": "self", + "type": "Any" + } + ], + "return_type": "None" + }, + { + "type": "method", + "name": "DatabaseService.get_session", + "filepath": "lib/laddr/src/laddr/core/database.py", + "start_line": 136, + "end_line": 146, + "code": " def get_session(self) -> Generator[Session, None, None]:\n \"\"\"Context manager for database sessions.\"\"\"\n session = self.SessionLocal()\n try:\n yield session\n session.commit()\n except Exception:\n session.rollback()\n raise\n finally:\n session.close()", + "docstring": "Context manager for database sessions.", + "dependencies": [ + "SessionLocal", + "rollback", + "commit", + "close" + ], + "complexity": 2, + "business_context": "database", + "imports": [], + "called_functions": [ + "SessionLocal", + "rollback", + "commit", + "close" + ], + "parent_class": "DatabaseService", + "decorators": [ + "contextmanager" + ], + "parameters": [ + { + "name": "self", + "type": "Any" + } + ], + "return_type": "Generator[Session, None, None]" + }, + { + "type": "method", + "name": "DatabaseService.create_job", + "filepath": "lib/laddr/src/laddr/core/database.py", + "start_line": 150, + "end_line": 172, + "code": " def create_job(self, job_id: str | None, pipeline: str, inputs: dict) -> str:\n \"\"\"Create a new job record, or reuse existing if job_id already exists (for sequential chains).\"\"\"\n if job_id is None:\n job_id = str(uuid.uuid4())\n\n with self.get_session() as session:\n # Check if job already exists (for sequential mode where agents share job_id)\n existing_job = session.query(Job).filter_by(job_id=job_id).first()\n if existing_job:\n # Job already exists (e.g., from first agent in sequential chain)\n # Just return the job_id without creating a duplicate\n return job_id\n \n # Create new job\n job = Job(\n job_id=job_id,\n pipeline_name=pipeline,\n status=\"pending\",\n inputs=inputs\n )\n session.add(job)\n\n return job_id", + "docstring": "Create a new job record, or reuse existing if job_id already exists (for sequential chains).", + "dependencies": [ + "first", + "Job", + "str", + "uuid4", + "query", + "add", + "get_session", + "filter_by" + ], + "complexity": 4, + "business_context": "query, create, agent", + "imports": [ + "uuid" + ], + "called_functions": [ + "first", + "Job", + "str", + "uuid4", + "query", + "add", + "get_session", + "filter_by" + ], + "parent_class": "DatabaseService", + "decorators": [], + "parameters": [ + { + "name": "self", + "type": "Any" + }, + { + "name": "job_id", + "type": "str | None" + }, + { + "name": "pipeline", + "type": "str" + }, + { + "name": "inputs", + "type": "dict" + } + ], + "return_type": "str" + }, + { + "type": "method", + "name": "DatabaseService.save_result", + "filepath": "lib/laddr/src/laddr/core/database.py", + "start_line": 174, + "end_line": 181, + "code": " def save_result(self, job_id: str, outputs: dict, status: str = \"completed\") -> None:\n \"\"\"Save job result.\"\"\"\n with self.get_session() as session:\n job = session.query(Job).filter_by(job_id=job_id).first()\n if job:\n job.outputs = outputs\n job.status = status\n job.completed_at = datetime.utcnow()", + "docstring": "Save job result.", + "dependencies": [ + "first", + "query", + "utcnow", + "get_session", + "filter_by" + ], + "complexity": 3, + "business_context": "query, save", + "imports": [ + "datetime" + ], + "called_functions": [ + "first", + "query", + "utcnow", + "get_session", + "filter_by" + ], + "parent_class": "DatabaseService", + "decorators": [], + "parameters": [ + { + "name": "self", + "type": "Any" + }, + { + "name": "job_id", + "type": "str" + }, + { + "name": "outputs", + "type": "dict" + }, + { + "name": "status", + "type": "str" + } + ], + "return_type": "None" + }, + { + "type": "method", + "name": "DatabaseService.get_result", + "filepath": "lib/laddr/src/laddr/core/database.py", + "start_line": 183, + "end_line": 204, + "code": " def get_result(self, job_id: str) -> dict | None:\n \"\"\"Get job result.\"\"\"\n with self.get_session() as session:\n job = session.query(Job).filter_by(job_id=job_id).first()\n if not job:\n return None\n\n def _iso_z(dt: datetime | None) -> str | None:\n if not dt:\n return None\n # stored as naive UTC; normalize to Z-suffixed ISO for clients\n return dt.isoformat() + \"Z\"\n\n return {\n \"job_id\": job.job_id,\n \"pipeline_name\": job.pipeline_name,\n \"status\": job.status,\n \"inputs\": job.inputs,\n \"outputs\": job.outputs,\n \"created_at\": _iso_z(job.created_at),\n \"completed_at\": _iso_z(job.completed_at),\n }", + "docstring": "Get job result.", + "dependencies": [ + "isoformat", + "first", + "_iso_z", + "query", + "get_session", + "filter_by" + ], + "complexity": 4, + "business_context": "query", + "imports": [ + "datetime" + ], + "called_functions": [ + "isoformat", + "first", + "_iso_z", + "query", + "get_session", + "filter_by" + ], + "parent_class": "DatabaseService", + "decorators": [], + "parameters": [ + { + "name": "self", + "type": "Any" + }, + { + "name": "job_id", + "type": "str" + } + ], + "return_type": "dict | None" + }, + { + "type": "method", + "name": "DatabaseService.list_jobs", + "filepath": "lib/laddr/src/laddr/core/database.py", + "start_line": 206, + "end_line": 222, + "code": " def list_jobs(self, limit: int = 50) -> list[dict]:\n \"\"\"List recent jobs.\"\"\"\n with self.get_session() as session:\n jobs = session.query(Job).order_by(Job.created_at.desc()).limit(limit).all()\n\n def _iso_z(dt: datetime | None) -> str | None:\n return dt.isoformat() + \"Z\" if dt else None\n\n return [\n {\n \"job_id\": job.job_id,\n \"pipeline_name\": job.pipeline_name,\n \"status\": job.status,\n \"created_at\": _iso_z(job.created_at),\n }\n for job in jobs\n ]", + "docstring": "List recent jobs.", + "dependencies": [ + "limit", + "isoformat", + "_iso_z", + "query", + "all", + "desc", + "get_session", + "order_by" + ], + "complexity": 2, + "business_context": "query", + "imports": [ + "datetime" + ], + "called_functions": [ + "limit", + "isoformat", + "_iso_z", + "query", + "all", + "desc", + "get_session", + "order_by" + ], + "parent_class": "DatabaseService", + "decorators": [], + "parameters": [ + { + "name": "self", + "type": "Any" + }, + { + "name": "limit", + "type": "int" + } + ], + "return_type": "list[dict]" + }, + { + "type": "method", + "name": "DatabaseService.create_prompt", + "filepath": "lib/laddr/src/laddr/core/database.py", + "start_line": 226, + "end_line": 240, + "code": " def create_prompt(self, prompt_id: str | None, prompt_name: str, inputs: dict) -> str:\n \"\"\"Create a new prompt execution record.\"\"\"\n if prompt_id is None:\n prompt_id = str(uuid.uuid4())\n\n with self.get_session() as session:\n prompt = PromptExecution(\n prompt_id=prompt_id,\n prompt_name=prompt_name,\n status=\"pending\",\n inputs=inputs\n )\n session.add(prompt)\n\n return prompt_id", + "docstring": "Create a new prompt execution record.", + "dependencies": [ + "uuid4", + "str", + "add", + "get_session", + "PromptExecution" + ], + "complexity": 3, + "business_context": "create", + "imports": [ + "uuid" + ], + "called_functions": [ + "uuid4", + "str", + "add", + "get_session", + "PromptExecution" + ], + "parent_class": "DatabaseService", + "decorators": [], + "parameters": [ + { + "name": "self", + "type": "Any" + }, + { + "name": "prompt_id", + "type": "str | None" + }, + { + "name": "prompt_name", + "type": "str" + }, + { + "name": "inputs", + "type": "dict" + } + ], + "return_type": "str" + }, + { + "type": "method", + "name": "DatabaseService.save_prompt_result", + "filepath": "lib/laddr/src/laddr/core/database.py", + "start_line": 242, + "end_line": 249, + "code": " def save_prompt_result(self, prompt_id: str, outputs: dict, status: str = \"completed\") -> None:\n \"\"\"Save prompt execution result.\"\"\"\n with self.get_session() as session:\n prompt = session.query(PromptExecution).filter_by(prompt_id=prompt_id).first()\n if prompt:\n prompt.outputs = outputs\n prompt.status = status\n prompt.completed_at = datetime.utcnow()", + "docstring": "Save prompt execution result.", + "dependencies": [ + "first", + "query", + "utcnow", + "get_session", + "filter_by" + ], + "complexity": 3, + "business_context": "query, save", + "imports": [ + "datetime" + ], + "called_functions": [ + "first", + "query", + "utcnow", + "get_session", + "filter_by" + ], + "parent_class": "DatabaseService", + "decorators": [], + "parameters": [ + { + "name": "self", + "type": "Any" + }, + { + "name": "prompt_id", + "type": "str" + }, + { + "name": "outputs", + "type": "dict" + }, + { + "name": "status", + "type": "str" + } + ], + "return_type": "None" + }, + { + "type": "method", + "name": "DatabaseService.update_prompt_status", + "filepath": "lib/laddr/src/laddr/core/database.py", + "start_line": 251, + "end_line": 259, + "code": " def update_prompt_status(self, prompt_id: str, status: str) -> None:\n \"\"\"Update prompt execution status only; set completed_at for terminal states.\"\"\"\n terminal = {\"completed\", \"failed\", \"error\", \"canceled\"}\n with self.get_session() as session:\n prompt = session.query(PromptExecution).filter_by(prompt_id=prompt_id).first()\n if prompt:\n prompt.status = status\n if status in terminal and prompt.completed_at is None:\n prompt.completed_at = datetime.utcnow()", + "docstring": "Update prompt execution status only; set completed_at for terminal states.", + "dependencies": [ + "first", + "query", + "utcnow", + "get_session", + "filter_by" + ], + "complexity": 5, + "business_context": "query, update", + "imports": [ + "datetime" + ], + "called_functions": [ + "first", + "query", + "utcnow", + "get_session", + "filter_by" + ], + "parent_class": "DatabaseService", + "decorators": [], + "parameters": [ + { + "name": "self", + "type": "Any" + }, + { + "name": "prompt_id", + "type": "str" + }, + { + "name": "status", + "type": "str" + } + ], + "return_type": "None" + }, + { + "type": "method", + "name": "DatabaseService.get_prompt_result", + "filepath": "lib/laddr/src/laddr/core/database.py", + "start_line": 261, + "end_line": 279, + "code": " def get_prompt_result(self, prompt_id: str) -> dict | None:\n \"\"\"Get prompt execution result.\"\"\"\n with self.get_session() as session:\n prompt = session.query(PromptExecution).filter_by(prompt_id=prompt_id).first()\n if not prompt:\n return None\n\n def _iso_z(dt: datetime | None) -> str | None:\n return dt.isoformat() + \"Z\" if dt else None\n\n return {\n \"prompt_id\": prompt.prompt_id,\n \"prompt_name\": prompt.prompt_name,\n \"status\": prompt.status,\n \"inputs\": prompt.inputs,\n \"outputs\": prompt.outputs,\n \"created_at\": _iso_z(prompt.created_at),\n \"completed_at\": _iso_z(prompt.completed_at),\n }", + "docstring": "Get prompt execution result.", + "dependencies": [ + "isoformat", + "first", + "_iso_z", + "query", + "get_session", + "filter_by" + ], + "complexity": 3, + "business_context": "query", + "imports": [ + "datetime" + ], + "called_functions": [ + "isoformat", + "first", + "_iso_z", + "query", + "get_session", + "filter_by" + ], + "parent_class": "DatabaseService", + "decorators": [], + "parameters": [ + { + "name": "self", + "type": "Any" + }, + { + "name": "prompt_id", + "type": "str" + } + ], + "return_type": "dict | None" + }, + { + "type": "method", + "name": "DatabaseService.list_prompts", + "filepath": "lib/laddr/src/laddr/core/database.py", + "start_line": 281, + "end_line": 297, + "code": " def list_prompts(self, limit: int = 50) -> list[dict]:\n \"\"\"List recent prompt executions.\"\"\"\n with self.get_session() as session:\n prompts = session.query(PromptExecution).order_by(PromptExecution.created_at.desc()).limit(limit).all()\n\n def _iso_z(dt: datetime | None) -> str | None:\n return dt.isoformat() + \"Z\" if dt else None\n\n return [\n {\n \"prompt_id\": prompt.prompt_id,\n \"prompt_name\": prompt.prompt_name,\n \"status\": prompt.status,\n \"created_at\": _iso_z(prompt.created_at),\n }\n for prompt in prompts\n ]", + "docstring": "List recent prompt executions.", + "dependencies": [ + "limit", + "isoformat", + "_iso_z", + "query", + "all", + "desc", + "get_session", + "order_by" + ], + "complexity": 2, + "business_context": "query", + "imports": [ + "datetime" + ], + "called_functions": [ + "limit", + "isoformat", + "_iso_z", + "query", + "all", + "desc", + "get_session", + "order_by" + ], + "parent_class": "DatabaseService", + "decorators": [], + "parameters": [ + { + "name": "self", + "type": "Any" + }, + { + "name": "limit", + "type": "int" + } + ], + "return_type": "list[dict]" + }, + { + "type": "method", + "name": "DatabaseService.get_job_traces", + "filepath": "lib/laddr/src/laddr/core/database.py", + "start_line": 299, + "end_line": 317, + "code": " def get_job_traces(self, job_id: str) -> list[dict]:\n \"\"\"Get all traces for a job.\"\"\"\n with self.get_session() as session:\n traces = session.query(Trace).filter_by(job_id=job_id).order_by(Trace.timestamp).all()\n\n def _iso_z(dt: datetime | None) -> str | None:\n return dt.isoformat() + \"Z\" if dt else None\n\n return [\n {\n \"id\": trace.id,\n \"job_id\": trace.job_id,\n \"agent_name\": trace.agent_name,\n \"event_type\": trace.event_type,\n \"payload\": trace.payload,\n \"timestamp\": _iso_z(trace.timestamp),\n }\n for trace in traces\n ]", + "docstring": "Get all traces for a job.", + "dependencies": [ + "isoformat", + "_iso_z", + "query", + "all", + "get_session", + "filter_by", + "order_by" + ], + "complexity": 2, + "business_context": "query", + "imports": [ + "datetime" + ], + "called_functions": [ + "isoformat", + "_iso_z", + "query", + "all", + "get_session", + "filter_by", + "order_by" + ], + "parent_class": "DatabaseService", + "decorators": [], + "parameters": [ + { + "name": "self", + "type": "Any" + }, + { + "name": "job_id", + "type": "str" + } + ], + "return_type": "list[dict]" + }, + { + "type": "method", + "name": "DatabaseService.append_trace", + "filepath": "lib/laddr/src/laddr/core/database.py", + "start_line": 321, + "end_line": 330, + "code": " def append_trace(self, job_id: str, agent_name: str, event_type: str, payload: dict) -> None:\n \"\"\"Append a trace event.\"\"\"\n with self.get_session() as session:\n trace = Trace(\n job_id=job_id,\n agent_name=agent_name,\n event_type=event_type,\n payload=payload\n )\n session.add(trace)", + "docstring": "Append a trace event.", + "dependencies": [ + "add", + "Trace", + "get_session" + ], + "complexity": 2, + "business_context": "", + "imports": [], + "called_functions": [ + "add", + "Trace", + "get_session" + ], + "parent_class": "DatabaseService", + "decorators": [], + "parameters": [ + { + "name": "self", + "type": "Any" + }, + { + "name": "job_id", + "type": "str" + }, + { + "name": "agent_name", + "type": "str" + }, + { + "name": "event_type", + "type": "str" + }, + { + "name": "payload", + "type": "dict" + } + ], + "return_type": "None" + }, + { + "type": "method", + "name": "DatabaseService.list_traces", + "filepath": "lib/laddr/src/laddr/core/database.py", + "start_line": 332, + "end_line": 355, + "code": " def list_traces(self, agent: str | None = None, limit: int = 100) -> list[dict]:\n \"\"\"List recent traces, optionally filtered by agent.\"\"\"\n with self.get_session() as session:\n query = session.query(Trace)\n\n if agent:\n query = query.filter_by(agent_name=agent)\n\n traces = query.order_by(Trace.timestamp.desc()).limit(limit).all()\n\n def _iso_z(dt: datetime | None) -> str | None:\n return dt.isoformat() + \"Z\" if dt else None\n\n return [\n {\n \"id\": trace.id,\n \"job_id\": trace.job_id,\n \"agent_name\": trace.agent_name,\n \"event_type\": trace.event_type,\n \"payload\": trace.payload,\n \"timestamp\": _iso_z(trace.timestamp),\n }\n for trace in traces\n ]", + "docstring": "List recent traces, optionally filtered by agent.", + "dependencies": [ + "limit", + "isoformat", + "_iso_z", + "query", + "all", + "desc", + "get_session", + "filter_by", + "order_by" + ], + "complexity": 3, + "business_context": "query, agent", + "imports": [ + "datetime" + ], + "called_functions": [ + "limit", + "isoformat", + "_iso_z", + "query", + "all", + "desc", + "get_session", + "filter_by", + "order_by" + ], + "parent_class": "DatabaseService", + "decorators": [], + "parameters": [ + { + "name": "self", + "type": "Any" + }, + { + "name": "agent", + "type": "str | None" + }, + { + "name": "limit", + "type": "int" + } + ], + "return_type": "list[dict]" + }, + { + "type": "method", + "name": "DatabaseService.get_trace", + "filepath": "lib/laddr/src/laddr/core/database.py", + "start_line": 357, + "end_line": 372, + "code": " def get_trace(self, trace_id: str) -> dict | None:\n \"\"\"Get a single trace event by id with full payload.\"\"\"\n with self.get_session() as session:\n trace = session.query(Trace).filter_by(id=trace_id).first()\n if not trace:\n return None\n def _iso_z(dt: datetime | None) -> str | None:\n return dt.isoformat() + \"Z\" if dt else None\n return {\n \"id\": trace.id,\n \"job_id\": trace.job_id,\n \"agent_name\": trace.agent_name,\n \"event_type\": trace.event_type,\n \"payload\": trace.payload,\n \"timestamp\": _iso_z(trace.timestamp),\n }", + "docstring": "Get a single trace event by id with full payload.", + "dependencies": [ + "isoformat", + "first", + "_iso_z", + "query", + "get_session", + "filter_by" + ], + "complexity": 3, + "business_context": "query", + "imports": [ + "datetime" + ], + "called_functions": [ + "isoformat", + "first", + "_iso_z", + "query", + "get_session", + "filter_by" + ], + "parent_class": "DatabaseService", + "decorators": [], + "parameters": [ + { + "name": "self", + "type": "Any" + }, + { + "name": "trace_id", + "type": "str" + } + ], + "return_type": "dict | None" + }, + { + "type": "method", + "name": "DatabaseService.memory_put", + "filepath": "lib/laddr/src/laddr/core/database.py", + "start_line": 376, + "end_line": 396, + "code": " def memory_put(self, agent_name: str, key: str, value: Any, job_id: str | None = None) -> None:\n \"\"\"Store a memory entry.\"\"\"\n with self.get_session() as session:\n # Check if exists\n existing = session.query(Memory).filter_by(\n agent_name=agent_name,\n key=key,\n job_id=job_id\n ).first()\n\n if existing:\n existing.value = value\n existing.updated_at = datetime.utcnow()\n else:\n memory = Memory(\n agent_name=agent_name,\n job_id=job_id,\n key=key,\n value=value\n )\n session.add(memory)", + "docstring": "Store a memory entry.", + "dependencies": [ + "Memory", + "first", + "query", + "add", + "utcnow", + "get_session", + "filter_by" + ], + "complexity": 3, + "business_context": "query", + "imports": [ + "datetime" + ], + "called_functions": [ + "Memory", + "first", + "query", + "add", + "utcnow", + "get_session", + "filter_by" + ], + "parent_class": "DatabaseService", + "decorators": [], + "parameters": [ + { + "name": "self", + "type": "Any" + }, + { + "name": "agent_name", + "type": "str" + }, + { + "name": "key", + "type": "str" + }, + { + "name": "value", + "type": "Any" + }, + { + "name": "job_id", + "type": "str | None" + } + ], + "return_type": "None" + }, + { + "type": "method", + "name": "DatabaseService.memory_get", + "filepath": "lib/laddr/src/laddr/core/database.py", + "start_line": 398, + "end_line": 407, + "code": " def memory_get(self, agent_name: str, key: str, job_id: str | None = None) -> Any:\n \"\"\"Retrieve a memory entry.\"\"\"\n with self.get_session() as session:\n memory = session.query(Memory).filter_by(\n agent_name=agent_name,\n key=key,\n job_id=job_id\n ).first()\n\n return memory.value if memory else None", + "docstring": "Retrieve a memory entry.", + "dependencies": [ + "query", + "filter_by", + "first", + "get_session" + ], + "complexity": 2, + "business_context": "query", + "imports": [], + "called_functions": [ + "query", + "filter_by", + "first", + "get_session" + ], + "parent_class": "DatabaseService", + "decorators": [], + "parameters": [ + { + "name": "self", + "type": "Any" + }, + { + "name": "agent_name", + "type": "str" + }, + { + "name": "key", + "type": "str" + }, + { + "name": "job_id", + "type": "str | None" + } + ], + "return_type": "Any" + }, + { + "type": "method", + "name": "DatabaseService.memory_list", + "filepath": "lib/laddr/src/laddr/core/database.py", + "start_line": 409, + "end_line": 419, + "code": " def memory_list(self, agent_name: str, job_id: str | None = None) -> dict[str, Any]:\n \"\"\"List all memory entries for an agent.\"\"\"\n with self.get_session() as session:\n query = session.query(Memory).filter_by(agent_name=agent_name)\n\n if job_id:\n query = query.filter_by(job_id=job_id)\n\n memories = query.all()\n\n return {mem.key: mem.value for mem in memories}", + "docstring": "List all memory entries for an agent.", + "dependencies": [ + "query", + "filter_by", + "all", + "get_session" + ], + "complexity": 3, + "business_context": "query, agent", + "imports": [], + "called_functions": [ + "query", + "filter_by", + "all", + "get_session" + ], + "parent_class": "DatabaseService", + "decorators": [], + "parameters": [ + { + "name": "self", + "type": "Any" + }, + { + "name": "agent_name", + "type": "str" + }, + { + "name": "job_id", + "type": "str | None" + } + ], + "return_type": "dict[str, Any]" + }, + { + "type": "method", + "name": "DatabaseService.register_agent", + "filepath": "lib/laddr/src/laddr/core/database.py", + "start_line": 423, + "end_line": 436, + "code": " def register_agent(self, agent_name: str, metadata: dict) -> None:\n \"\"\"Register or update an agent in the registry.\"\"\"\n with self.get_session() as session:\n existing = session.query(AgentRegistry).filter_by(agent_name=agent_name).first()\n\n if existing:\n existing.meta = metadata\n existing.last_seen = datetime.utcnow()\n else:\n registry = AgentRegistry(\n agent_name=agent_name,\n meta=metadata\n )\n session.add(registry)", + "docstring": "Register or update an agent in the registry.", + "dependencies": [ + "first", + "query", + "add", + "AgentRegistry", + "utcnow", + "get_session", + "filter_by" + ], + "complexity": 3, + "business_context": "query, update, agent", + "imports": [ + "datetime" + ], + "called_functions": [ + "first", + "query", + "add", + "AgentRegistry", + "utcnow", + "get_session", + "filter_by" + ], + "parent_class": "DatabaseService", + "decorators": [], + "parameters": [ + { + "name": "self", + "type": "Any" + }, + { + "name": "agent_name", + "type": "str" + }, + { + "name": "metadata", + "type": "dict" + } + ], + "return_type": "None" + }, + { + "type": "method", + "name": "DatabaseService.list_agents", + "filepath": "lib/laddr/src/laddr/core/database.py", + "start_line": 438, + "end_line": 468, + "code": " def list_agents(self) -> list[dict]:\n \"\"\"List all registered agents with trace counts and last execution time.\"\"\"\n with self.get_session() as session:\n agents = session.query(AgentRegistry).all()\n\n def _iso_z(dt: datetime | None) -> str | None:\n return dt.isoformat() + \"Z\" if dt else None\n\n result = []\n for agent in agents:\n # Get trace count for this agent\n trace_count = session.query(Trace).filter_by(agent_name=agent.agent_name).count()\n \n # Get last execution time (most recent trace)\n last_trace = (\n session.query(Trace)\n .filter_by(agent_name=agent.agent_name)\n .order_by(Trace.timestamp.desc())\n .first()\n )\n last_executed = _iso_z(last_trace.timestamp) if last_trace else None\n \n result.append({\n \"agent_name\": agent.agent_name,\n \"metadata\": agent.meta,\n \"last_seen\": _iso_z(agent.last_seen),\n \"trace_count\": trace_count,\n \"last_executed\": last_executed,\n })\n \n return result", + "docstring": "List all registered agents with trace counts and last execution time.", + "dependencies": [ + "append", + "count", + "isoformat", + "first", + "_iso_z", + "query", + "all", + "desc", + "get_session", + "filter_by", + "order_by" + ], + "complexity": 3, + "business_context": "query, agent", + "imports": [ + "datetime" + ], + "called_functions": [ + "append", + "count", + "isoformat", + "first", + "_iso_z", + "query", + "all", + "desc", + "get_session", + "filter_by", + "order_by" + ], + "parent_class": "DatabaseService", + "decorators": [], + "parameters": [ + { + "name": "self", + "type": "Any" + } + ], + "return_type": "list[dict]" + }, + { + "type": "method", + "name": "DatabaseService.get_metrics", + "filepath": "lib/laddr/src/laddr/core/database.py", + "start_line": 472, + "end_line": 532, + "code": " def get_metrics(self) -> dict[str, Any]:\n \"\"\"Get aggregated metrics.\"\"\"\n with self.get_session() as session:\n # Count both legacy Jobs and new PromptExecutions\n total_jobs = session.query(Job).count()\n total_prompts = session.query(PromptExecution).count()\n total_executions = total_jobs + total_prompts\n \n completed_jobs = session.query(Job).filter_by(status=\"completed\").count()\n completed_prompts = session.query(PromptExecution).filter_by(status=\"completed\").count()\n total_completed = completed_jobs + completed_prompts\n \n failed_jobs = session.query(Job).filter_by(status=\"failed\").count()\n failed_prompts = session.query(PromptExecution).filter_by(status=\"failed\").count()\n total_failed = failed_jobs + failed_prompts\n\n # Calculate average latency from both Jobs and PromptExecutions\n latencies = []\n \n # Get latencies from completed jobs\n completed = session.query(Job).filter_by(status=\"completed\").all()\n for job in completed:\n if job.created_at and job.completed_at:\n delta = (job.completed_at - job.created_at).total_seconds()\n latencies.append(delta)\n \n # Get latencies from completed prompts\n completed_prompts_list = session.query(PromptExecution).filter_by(status=\"completed\").all()\n for prompt in completed_prompts_list:\n if prompt.created_at and prompt.completed_at:\n delta = (prompt.completed_at - prompt.created_at).total_seconds()\n latencies.append(delta)\n\n avg_latency_sec = sum(latencies) / len(latencies) if latencies else 0\n\n # Active agents (from registry)\n active_agents = session.query(AgentRegistry).count()\n\n # Tool calls (from traces)\n tool_calls = session.query(Trace).filter_by(event_type=\"tool_call\").count()\n\n # Cache hits (from traces)\n cache_hits = session.query(Trace).filter_by(event_type=\"cache_hit\").count()\n\n # Total tokens from llm_usage traces\n llm_usage_traces = session.query(Trace).filter_by(event_type=\"llm_usage\").all()\n total_tokens = 0\n for trace in llm_usage_traces:\n payload = trace.payload or {}\n total_tokens += int(payload.get(\"total_tokens\") or 0)\n\n return {\n \"total_jobs\": total_executions,\n \"completed_jobs\": total_completed,\n \"failed_jobs\": total_failed,\n \"avg_latency_ms\": int(avg_latency_sec * 1000),\n \"active_agents_count\": active_agents,\n \"tool_calls\": tool_calls,\n \"cache_hits\": cache_hits,\n \"total_tokens\": total_tokens,\n }", + "docstring": "Get aggregated metrics.", + "dependencies": [ + "count", + "append", + "get", + "sum", + "query", + "total_seconds", + "len", + "int", + "all", + "get_session", + "filter_by" + ], + "complexity": 11, + "business_context": "query, cache, tool, calculate", + "imports": [], + "called_functions": [ + "count", + "append", + "get", + "sum", + "query", + "total_seconds", + "len", + "int", + "all", + "get_session", + "filter_by" + ], + "parent_class": "DatabaseService", + "decorators": [], + "parameters": [ + { + "name": "self", + "type": "Any" + } + ], + "return_type": "dict[str, Any]" + }, + { + "type": "method", + "name": "DatabaseService.get_token_usage", + "filepath": "lib/laddr/src/laddr/core/database.py", + "start_line": 535, + "end_line": 577, + "code": " def get_token_usage(self, job_id: str) -> dict:\n \"\"\"Aggregate token usage for a job from llm_usage traces.\"\"\"\n with self.get_session() as session:\n traces = (\n session.query(Trace)\n .filter_by(job_id=job_id, event_type=\"llm_usage\")\n .all()\n )\n total_prompt = 0\n total_completion = 0\n total = 0\n breakdown: dict[tuple[str | None, str | None], dict] = {}\n for tr in traces:\n payload = tr.payload or {}\n pt = int(payload.get(\"prompt_tokens\") or 0)\n ct = int(payload.get(\"completion_tokens\") or 0)\n tt = int(payload.get(\"total_tokens\") or (pt + ct))\n prov = payload.get(\"provider\")\n model = payload.get(\"model\")\n total_prompt += pt\n total_completion += ct\n total += tt\n key = (prov, model)\n if key not in breakdown:\n breakdown[key] = {\n \"provider\": prov,\n \"model\": model,\n \"prompt_tokens\": 0,\n \"completion_tokens\": 0,\n \"total_tokens\": 0,\n \"calls\": 0,\n }\n breakdown[key][\"prompt_tokens\"] += pt\n breakdown[key][\"completion_tokens\"] += ct\n breakdown[key][\"total_tokens\"] += tt\n breakdown[key][\"calls\"] += 1\n\n return {\n \"prompt_tokens\": total_prompt,\n \"completion_tokens\": total_completion,\n \"total_tokens\": total,\n \"by_model\": list(breakdown.values()),\n }", + "docstring": "Aggregate token usage for a job from llm_usage traces.", + "dependencies": [ + "values", + "get", + "list", + "query", + "int", + "all", + "get_session", + "filter_by" + ], + "complexity": 8, + "business_context": "query", + "imports": [], + "called_functions": [ + "values", + "get", + "list", + "query", + "int", + "all", + "get_session", + "filter_by" + ], + "parent_class": "DatabaseService", + "decorators": [], + "parameters": [ + { + "name": "self", + "type": "Any" + }, + { + "name": "job_id", + "type": "str" + } + ], + "return_type": "dict" + }, + { + "type": "function", + "name": "_iso_z", + "filepath": "lib/laddr/src/laddr/core/database.py", + "start_line": 443, + "end_line": 444, + "code": " def _iso_z(dt: datetime | None) -> str | None:\n return dt.isoformat() + \"Z\" if dt else None", + "docstring": "", + "dependencies": [ + "isoformat" + ], + "complexity": 1, + "business_context": "", + "imports": [ + "datetime" + ], + "called_functions": [ + "isoformat" + ], + "parent_class": "", + "decorators": [], + "parameters": [ + { + "name": "dt", + "type": "datetime | None" + } + ], + "return_type": "str | None" + }, + { + "type": "class", + "name": "TaskMessage", + "filepath": "lib/laddr/src/laddr/core/message_bus.py", + "start_line": 32, + "end_line": 55, + "code": "class TaskMessage:\n \"\"\"Message sent to an agent's task queue.\"\"\"\n\n task_id: str\n agent_name: str\n payload: dict[str, Any]\n created_at: float = field(default_factory=time.time)\n", + "docstring": "Message sent to an agent's task queue.", + "dependencies": [], + "complexity": 7, + "business_context": "message, queue, agent", + "imports": [], + "called_functions": [], + "parent_class": "", + "decorators": [ + "dataclass" + ], + "parameters": [], + "return_type": "" + }, + { + "type": "method", + "name": "TaskMessage.to_dict", + "filepath": "lib/laddr/src/laddr/core/message_bus.py", + "start_line": 40, + "end_line": 46, + "code": " def to_dict(self) -> dict:\n return {\n \"task_id\": self.task_id,\n \"agent_name\": self.agent_name,\n \"payload\": self.payload,\n \"created_at\": self.created_at,\n }", + "docstring": "", + "dependencies": [], + "complexity": 1, + "business_context": "", + "imports": [], + "called_functions": [], + "parent_class": "TaskMessage", + "decorators": [], + "parameters": [ + { + "name": "self", + "type": "Any" + } + ], + "return_type": "dict" + }, + { + "type": "method", + "name": "TaskMessage.from_dict", + "filepath": "lib/laddr/src/laddr/core/message_bus.py", + "start_line": 49, + "end_line": 55, + "code": " def from_dict(cls, data: dict) -> TaskMessage:\n return cls(\n task_id=data[\"task_id\"],\n agent_name=data[\"agent_name\"],\n payload=data[\"payload\"],\n created_at=data.get(\"created_at\", time.time()),\n )", + "docstring": "", + "dependencies": [ + "time", + "get", + "cls" + ], + "complexity": 1, + "business_context": "", + "imports": [ + "time" + ], + "called_functions": [ + "time", + "get", + "cls" + ], + "parent_class": "TaskMessage", + "decorators": [ + "classmethod" + ], + "parameters": [ + { + "name": "cls", + "type": "Any" + }, + { + "name": "data", + "type": "dict" + } + ], + "return_type": "TaskMessage" + }, + { + "type": "class", + "name": "ResponseMessage", + "filepath": "lib/laddr/src/laddr/core/message_bus.py", + "start_line": 59, + "end_line": 85, + "code": "class ResponseMessage:\n \"\"\"Response from an agent after processing a task.\"\"\"\n\n task_id: str\n status: str # \"success\" | \"error\" | \"pending\"\n result: dict[str, Any] | None = None\n error: str | None = None\n created_at: float = field(default_factory=time.time)\n", + "docstring": "Response from an agent after processing a task.", + "dependencies": [], + "complexity": 8, + "business_context": "agent", + "imports": [], + "called_functions": [], + "parent_class": "", + "decorators": [ + "dataclass" + ], + "parameters": [], + "return_type": "" + }, + { + "type": "method", + "name": "ResponseMessage.to_dict", + "filepath": "lib/laddr/src/laddr/core/message_bus.py", + "start_line": 68, + "end_line": 75, + "code": " def to_dict(self) -> dict:\n return {\n \"task_id\": self.task_id,\n \"status\": self.status,\n \"result\": self.result,\n \"error\": self.error,\n \"created_at\": self.created_at,\n }", + "docstring": "", + "dependencies": [], + "complexity": 1, + "business_context": "", + "imports": [], + "called_functions": [], + "parent_class": "ResponseMessage", + "decorators": [], + "parameters": [ + { + "name": "self", + "type": "Any" + } + ], + "return_type": "dict" + }, + { + "type": "method", + "name": "ResponseMessage.from_dict", + "filepath": "lib/laddr/src/laddr/core/message_bus.py", + "start_line": 78, + "end_line": 85, + "code": " def from_dict(cls, data: dict) -> ResponseMessage:\n return cls(\n task_id=data[\"task_id\"],\n status=data[\"status\"],\n result=data.get(\"result\"),\n error=data.get(\"error\"),\n created_at=data.get(\"created_at\", time.time()),\n )", + "docstring": "", + "dependencies": [ + "time", + "get", + "cls" + ], + "complexity": 1, + "business_context": "", + "imports": [ + "time" + ], + "called_functions": [ + "time", + "get", + "cls" + ], + "parent_class": "ResponseMessage", + "decorators": [ + "classmethod" + ], + "parameters": [ + { + "name": "cls", + "type": "Any" + }, + { + "name": "data", + "type": "dict" + } + ], + "return_type": "ResponseMessage" + }, + { + "type": "class", + "name": "RedisBus", + "filepath": "lib/laddr/src/laddr/core/message_bus.py", + "start_line": 88, + "end_line": 364, + "code": "class RedisBus:\n \"\"\"\n Redis-based message bus for production use.\n \n Uses Redis Streams for task queues and pub/sub for responses.\n Agents are registered in a hash with metadata and heartbeats.\n \"\"\"\n", + "docstring": "Redis-based message bus for production use.\n\nUses Redis Streams for task queues and pub/sub for responses.\nAgents are registered in a hash with metadata and heartbeats.", + "dependencies": [], + "complexity": 14, + "business_context": "message", + "imports": [], + "called_functions": [], + "parent_class": "", + "decorators": [], + "parameters": [], + "return_type": "" + }, + { + "type": "method", + "name": "RedisBus.__init__", + "filepath": "lib/laddr/src/laddr/core/message_bus.py", + "start_line": 96, + "end_line": 101, + "code": " def __init__(self, redis_url: str):\n self.redis_url = redis_url\n self._client = None\n self._cancel_prefix = \"laddr:cancel:\"\n # Persistent consumer ID for this worker instance\n self._consumer_id = f\"worker_{uuid.uuid4().hex[:8]}\"", + "docstring": "", + "dependencies": [ + "uuid4" + ], + "complexity": 1, + "business_context": "", + "imports": [ + "uuid" + ], + "called_functions": [ + "uuid4" + ], + "parent_class": "RedisBus", + "decorators": [], + "parameters": [ + { + "name": "self", + "type": "Any" + }, + { + "name": "redis_url", + "type": "str" + } + ], + "return_type": "None" + }, + { + "type": "class", + "name": "MemoryBus", + "filepath": "lib/laddr/src/laddr/core/message_bus.py", + "start_line": 367, + "end_line": 493, + "code": "class MemoryBus:\n \"\"\"\n In-memory message bus for testing and quick local dev.\n \n Not suitable for production or distributed systems.\n \"\"\"\n", + "docstring": "In-memory message bus for testing and quick local dev.\n\nNot suitable for production or distributed systems.", + "dependencies": [], + "complexity": 13, + "business_context": "message", + "imports": [], + "called_functions": [], + "parent_class": "", + "decorators": [], + "parameters": [], + "return_type": "" + }, + { + "type": "method", + "name": "MemoryBus.__init__", + "filepath": "lib/laddr/src/laddr/core/message_bus.py", + "start_line": 374, + "end_line": 379, + "code": " def __init__(self):\n self._agents: dict[str, dict] = {}\n self._tasks: dict[str, list[dict]] = {}\n self._responses: dict[str, dict] = {}\n self._waiters: dict[str, list[asyncio.Future]] = {}\n self._canceled: set[str] = set()", + "docstring": "", + "dependencies": [ + "set" + ], + "complexity": 1, + "business_context": "", + "imports": [ + "asyncio" + ], + "called_functions": [ + "set" + ], + "parent_class": "MemoryBus", + "decorators": [], + "parameters": [ + { + "name": "self", + "type": "Any" + } + ], + "return_type": "None" + }, + { + "type": "class", + "name": "KafkaBus", + "filepath": "lib/laddr/src/laddr/core/message_bus.py", + "start_line": 496, + "end_line": 786, + "code": "class KafkaBus:\n \"\"\"\n Kafka-based message bus (experimental / enterprise option).\n\n Uses topics per agent: laddr.tasks.\n Responses published to: laddr.responses\n Agent registry is memory-only in this minimal implementation.\n \"\"\"\n", + "docstring": "Kafka-based message bus (experimental / enterprise option).\n\nUses topics per agent: laddr.tasks.\nResponses published to: laddr.responses\nAgent registry is memory-only in this minimal implementation.", + "dependencies": [], + "complexity": 15, + "business_context": "message, agent", + "imports": [], + "called_functions": [], + "parent_class": "", + "decorators": [], + "parameters": [], + "return_type": "" + }, + { + "type": "method", + "name": "KafkaBus.__init__", + "filepath": "lib/laddr/src/laddr/core/message_bus.py", + "start_line": 505, + "end_line": 513, + "code": " def __init__(self, bootstrap_servers: str):\n if AIOKafkaProducer is None or AIOKafkaConsumer is None:\n raise RuntimeError(\"aiokafka is not installed. Install with: pip install aiokafka\")\n self.bootstrap_servers = bootstrap_servers\n self._producer = None\n self._consumers: dict[str, Any] = {} # Cache consumers per agent\n self._agents: dict[str, dict] = {}\n self._waiters: dict[str, list[asyncio.Future]] = {}\n self._canceled: set[str] = set()", + "docstring": "", + "dependencies": [ + "RuntimeError", + "set" + ], + "complexity": 3, + "business_context": "cache, agent", + "imports": [ + "time", + "aiokafka", + "asyncio" + ], + "called_functions": [ + "RuntimeError", + "set" + ], + "parent_class": "KafkaBus", + "decorators": [], + "parameters": [ + { + "name": "self", + "type": "Any" + }, + { + "name": "bootstrap_servers", + "type": "str" + } + ], + "return_type": "None" + }, + { + "type": "class", + "name": "TaskDelegationTool", + "filepath": "lib/laddr/src/laddr/core/system_tools.py", + "start_line": 120, + "end_line": 308, + "code": "class TaskDelegationTool:\n \"\"\"Tool for delegating tasks to other agents.\"\"\"\n", + "docstring": "Tool for delegating tasks to other agents.", + "dependencies": [], + "complexity": 5, + "business_context": "tool", + "imports": [], + "called_functions": [], + "parent_class": "", + "decorators": [], + "parameters": [], + "return_type": "" + }, + { + "type": "method", + "name": "TaskDelegationTool.__init__", + "filepath": "lib/laddr/src/laddr/core/system_tools.py", + "start_line": 123, + "end_line": 141, + "code": " def __init__(self, message_bus, artifact_storage=None, agent=None):\n \"\"\"\n Initialize task delegation tool.\n \n Args:\n message_bus: Message queue backend for inter-agent communication\n artifact_storage: Optional storage backend for large payloads\n agent: Optional agent instance for accessing current_job_id\n \"\"\"\n self.message_bus = message_bus\n self.artifact_storage = artifact_storage\n self.agent = agent\n self._size_threshold_kb = 100 # Store payloads > 100KB in artifact registry\n \n # Get storage bucket from agent config, fallback to default\n self._storage_bucket = None\n if agent and hasattr(agent, 'env_config'):\n self._storage_bucket = getattr(agent.env_config, 'storage_bucket', None)\n self._storage_bucket = self._storage_bucket or \"laddr\"", + "docstring": "Initialize task delegation tool.\n\nArgs:\n message_bus: Message queue backend for inter-agent communication\n artifact_storage: Optional storage backend for large payloads\n agent: Optional agent instance for accessing current_job_id", + "dependencies": [ + "hasattr", + "getattr" + ], + "complexity": 4, + "business_context": "queue, config, agent, tool, initialize", + "imports": [], + "called_functions": [ + "hasattr", + "getattr" + ], + "parent_class": "TaskDelegationTool", + "decorators": [], + "parameters": [ + { + "name": "self", + "type": "Any" + }, + { + "name": "message_bus", + "type": "Any" + }, + { + "name": "artifact_storage", + "type": "Any" + }, + { + "name": "agent", + "type": "Any" + } + ], + "return_type": "None" + }, + { + "type": "class", + "name": "ArtifactStorageTool", + "filepath": "lib/laddr/src/laddr/core/system_tools.py", + "start_line": 311, + "end_line": 562, + "code": "class ArtifactStorageTool:\n \"\"\"Tool for storing and retrieving large data artifacts.\n\n Backward-compatible retrieval:\n - Old style: by artifact_id and artifact_type (stored under default artifact bucket)\n - New style: by bucket+key (e.g., offloaded response pointers)\n \"\"\"\n", + "docstring": "Tool for storing and retrieving large data artifacts.\n\nBackward-compatible retrieval:\n- Old style: by artifact_id and artifact_type (stored under default artifact bucket)\n- New style: by bucket+key (e.g., offloaded response pointers)", + "dependencies": [], + "complexity": 5, + "business_context": "tool", + "imports": [], + "called_functions": [], + "parent_class": "", + "decorators": [], + "parameters": [], + "return_type": "" + }, + { + "type": "method", + "name": "ArtifactStorageTool.__init__", + "filepath": "lib/laddr/src/laddr/core/system_tools.py", + "start_line": 319, + "end_line": 330, + "code": " def __init__(self, storage_backend, default_bucket: str | None = None):\n \"\"\"\n Initialize artifact storage tool.\n \n Args:\n storage_backend: Storage backend (MinIO/S3)\n \"\"\"\n self.storage = storage_backend\n # Allow caller to provide a project-wide default bucket name\n # (e.g. from LaddrConfig.storage_bucket). If not provided we\n # keep backward-compatible defaults in retrieval logic.\n self.default_bucket = default_bucket", + "docstring": "Initialize artifact storage tool.\n\nArgs:\n storage_backend: Storage backend (MinIO/S3)", + "dependencies": [], + "complexity": 1, + "business_context": "initialize, tool, storage", + "imports": [], + "called_functions": [], + "parent_class": "ArtifactStorageTool", + "decorators": [], + "parameters": [ + { + "name": "self", + "type": "Any" + }, + { + "name": "storage_backend", + "type": "Any" + }, + { + "name": "default_bucket", + "type": "str | None" + } + ], + "return_type": "None" + }, + { + "type": "class", + "name": "ParallelDelegationTool", + "filepath": "lib/laddr/src/laddr/core/system_tools.py", + "start_line": 565, + "end_line": 784, + "code": "class ParallelDelegationTool:\n \"\"\"Tool for delegating multiple tasks in parallel (sharding/fan-out pattern).\"\"\"\n", + "docstring": "Tool for delegating multiple tasks in parallel (sharding/fan-out pattern).", + "dependencies": [], + "complexity": 4, + "business_context": "tool", + "imports": [], + "called_functions": [], + "parent_class": "", + "decorators": [], + "parameters": [], + "return_type": "" + }, + { + "type": "method", + "name": "ParallelDelegationTool.__init__", + "filepath": "lib/laddr/src/laddr/core/system_tools.py", + "start_line": 568, + "end_line": 580, + "code": " def __init__(self, message_bus, artifact_storage=None, agent=None):\n \"\"\"\n Initialize parallel delegation tool.\n \n Args:\n message_bus: Message queue backend for inter-agent communication\n artifact_storage: Optional storage backend for large payloads\n agent: Optional agent instance for job context propagation\n \"\"\"\n self.message_bus = message_bus\n self.artifact_storage = artifact_storage\n self.agent = agent\n self.delegation_tool = TaskDelegationTool(message_bus, artifact_storage, agent)", + "docstring": "Initialize parallel delegation tool.\n\nArgs:\n message_bus: Message queue backend for inter-agent communication\n artifact_storage: Optional storage backend for large payloads\n agent: Optional agent instance for job context propagation", + "dependencies": [ + "TaskDelegationTool" + ], + "complexity": 1, + "business_context": "queue, agent, tool, initialize, message", + "imports": [], + "called_functions": [ + "TaskDelegationTool" + ], + "parent_class": "ParallelDelegationTool", + "decorators": [], + "parameters": [ + { + "name": "self", + "type": "Any" + }, + { + "name": "message_bus", + "type": "Any" + }, + { + "name": "artifact_storage", + "type": "Any" + }, + { + "name": "agent", + "type": "Any" + } + ], + "return_type": "None" + }, + { + "type": "method", + "name": "ParallelDelegationTool.split_document", + "filepath": "lib/laddr/src/laddr/core/system_tools.py", + "start_line": 696, + "end_line": 784, + "code": " def split_document(\n self,\n document: str,\n num_chunks: int = 3,\n overlap: int = 0\n ) -> dict:\n \"\"\"\n Split a large document into smaller chunks for parallel processing.\n \n Use this before calling delegate_parallel to divide large documents\n into manageable sections that can be processed by multiple workers.\n\n Parameters:\n - document: The document text to split\n - num_chunks: Number of chunks to split into (e.g., 3 for 3 workers)\n - overlap: Number of characters to overlap between chunks (for context continuity)\n\n Returns:\n - chunks: List of document chunks\n - metadata: Information about the split (lengths, chunk count, etc.)\n \"\"\"\n doc_len = len(document)\n \n if num_chunks <= 0:\n num_chunks = 3\n \n if num_chunks == 1:\n return {\n \"chunks\": [document],\n \"metadata\": {\n \"original_length\": doc_len,\n \"chunk_sizes\": [doc_len],\n \"num_chunks\": 1,\n \"overlap\": 0\n }\n }\n\n # Helper: choose a split point near target on whitespace to avoid mid-word cuts\n def nearest_whitespace(target: int, low: int, high: int) -> int:\n target = max(low, min(high, target))\n window = 200 # search window on either side\n left = max(low, target - window)\n right = min(high, target + window)\n best = None\n best_dist = None\n for idx in range(target, left - 1, -1):\n if document[idx:idx+1].isspace():\n best = idx\n best_dist = abs(idx - target)\n break\n if best is None:\n for idx in range(target, right + 1):\n if document[idx:idx+1].isspace():\n best = idx\n best_dist = abs(idx - target)\n break\n return best if best is not None else target\n\n # Calculate approximate equal segments\n base = doc_len // num_chunks\n indices = [0]\n for i in range(1, num_chunks):\n target = i * base\n # ensure monotonic increase\n low = indices[-1] + 1\n high = doc_len - 1\n split_at = nearest_whitespace(target, low, high)\n indices.append(split_at)\n indices.append(doc_len)\n\n # Build chunks with optional overlap (character-based)\n chunks = []\n for i in range(num_chunks):\n start = indices[i]\n end = indices[i+1]\n # apply overlap: extend end by overlap for all but last, and start backward for all but first\n start_o = max(0, start - (overlap if i > 0 else 0))\n end_o = min(doc_len, end + (overlap if i < num_chunks - 1 else 0))\n chunks.append(document[start_o:end_o])\n \n return {\n \"chunks\": chunks,\n \"metadata\": {\n \"original_length\": doc_len,\n \"chunk_sizes\": [len(c) for c in chunks],\n \"num_chunks\": len(chunks),\n \"overlap\": overlap\n }\n }", + "docstring": "Split a large document into smaller chunks for parallel processing.\n\nUse this before calling delegate_parallel to divide large documents\ninto manageable sections that can be processed by multiple workers.\n\nParameters:\n- document: The document text to split\n- num_chunks: Number of chunks to split into (e.g., 3 for 3 workers)\n- overlap: Number of characters to overlap between chunks (for context continuity)\n\nReturns:\n- chunks: List of document chunks\n- metadata: Information about the split (lengths, chunk count, etc.)", + "dependencies": [ + "range", + "append", + "abs", + "nearest_whitespace", + "isspace", + "min", + "len", + "max" + ], + "complexity": 10, + "business_context": "calculate", + "imports": [], + "called_functions": [ + "range", + "append", + "abs", + "nearest_whitespace", + "isspace", + "min", + "len", + "max" + ], + "parent_class": "ParallelDelegationTool", + "decorators": [], + "parameters": [ + { + "name": "self", + "type": "Any" + }, + { + "name": "document", + "type": "str" + }, + { + "name": "num_chunks", + "type": "int" + }, + { + "name": "overlap", + "type": "int" + } + ], + "return_type": "dict" + }, + { + "type": "function", + "name": "override_system_tool", + "filepath": "lib/laddr/src/laddr/core/system_tools.py", + "start_line": 48, + "end_line": 84, + "code": "def override_system_tool(tool_name: str):\n \"\"\"\n Decorator to override a system tool with a custom implementation.\n \n Use this decorator to replace built-in system tools (like system_delegate_parallel,\n system_split_document, etc.) with your own implementations.\n \n Example:\n from laddr.core.system_tools import override_system_tool\n \n @override_system_tool(\"system_delegate_parallel\")\n async def my_custom_parallel_delegation(agent_name: str, tasks: list[str], **kwargs):\n # Your custom implementation\n print(f\"Custom parallel delegation: {len(tasks)} tasks to {agent_name}\")\n return {\"status\": \"success\", \"results\": [...]}\n \n Args:\n tool_name: Name of the system tool to override (e.g., \"system_delegate_parallel\")\n \n Returns:\n Decorator function that registers the override\n \"\"\"\n def decorator(func: Callable) -> Callable:\n _TOOL_OVERRIDES[tool_name] = func\n \n # Also register common aliases\n if tool_name == \"system_delegate_parallel\":\n _TOOL_OVERRIDES[\"delegate_parallel\"] = func\n elif tool_name == \"system_split_document\":\n _TOOL_OVERRIDES[\"split_document\"] = func\n elif tool_name == \"system_delegate_task\":\n _TOOL_OVERRIDES[\"delegate_task\"] = func\n \n logger.info(f\"✅ Registered tool override: {tool_name} -> {func.__name__}\")\n return func\n \n return decorator", + "docstring": "Decorator to override a system tool with a custom implementation.\n\nUse this decorator to replace built-in system tools (like system_delegate_parallel,\nsystem_split_document, etc.) with your own implementations.\n\nExample:\n from laddr.core.system_tools import override_system_tool\n \n @override_system_tool(\"system_delegate_parallel\")\n async def my_custom_parallel_delegation(agent_name: str, tasks: list[str], **kwargs):\n # Your custom implementation\n print(f\"Custom parallel delegation: {len(tasks)} tasks to {agent_name}\")\n return {\"status\": \"success\", \"results\": [...]}\n\nArgs:\n tool_name: Name of the system tool to override (e.g., \"system_delegate_parallel\")\n\nReturns:\n Decorator function that registers the override", + "dependencies": [ + "info" + ], + "complexity": 4, + "business_context": "tool", + "imports": [ + "sys" + ], + "called_functions": [ + "info" + ], + "parent_class": "", + "decorators": [], + "parameters": [ + { + "name": "tool_name", + "type": "str" + } + ], + "return_type": "None" + }, + { + "type": "function", + "name": "get_tool_override", + "filepath": "lib/laddr/src/laddr/core/system_tools.py", + "start_line": 87, + "end_line": 97, + "code": "def get_tool_override(tool_name: str) -> Callable | None:\n \"\"\"\n Get a tool override if it exists.\n \n Args:\n tool_name: Name of the tool to check\n \n Returns:\n The override function if registered, None otherwise\n \"\"\"\n return _TOOL_OVERRIDES.get(tool_name)", + "docstring": "Get a tool override if it exists.\n\nArgs:\n tool_name: Name of the tool to check\n\nReturns:\n The override function if registered, None otherwise", + "dependencies": [ + "get" + ], + "complexity": 1, + "business_context": "tool", + "imports": [], + "called_functions": [ + "get" + ], + "parent_class": "", + "decorators": [], + "parameters": [ + { + "name": "tool_name", + "type": "str" + } + ], + "return_type": "Callable | None" + }, + { + "type": "function", + "name": "clear_tool_overrides", + "filepath": "lib/laddr/src/laddr/core/system_tools.py", + "start_line": 100, + "end_line": 104, + "code": "def clear_tool_overrides():\n \"\"\"Clear all tool overrides. Useful for testing.\"\"\"\n global _TOOL_OVERRIDES\n _TOOL_OVERRIDES = {}\n logger.info(\"Cleared all tool overrides\")", + "docstring": "Clear all tool overrides. Useful for testing.", + "dependencies": [ + "info" + ], + "complexity": 1, + "business_context": "tool", + "imports": [], + "called_functions": [ + "info" + ], + "parent_class": "", + "decorators": [], + "parameters": [], + "return_type": "None" + }, + { + "type": "function", + "name": "list_tool_overrides", + "filepath": "lib/laddr/src/laddr/core/system_tools.py", + "start_line": 107, + "end_line": 117, + "code": "def list_tool_overrides() -> dict[str, str]:\n \"\"\"\n List all registered tool overrides.\n \n Returns:\n Dict mapping tool names to their override function names\n \"\"\"\n return {\n name: func.__name__ \n for name, func in _TOOL_OVERRIDES.items()\n }", + "docstring": "List all registered tool overrides.\n\nReturns:\n Dict mapping tool names to their override function names", + "dependencies": [ + "items" + ], + "complexity": 1, + "business_context": "tool", + "imports": [], + "called_functions": [ + "items" + ], + "parent_class": "", + "decorators": [], + "parameters": [], + "return_type": "dict[str, str]" + }, + { + "type": "function", + "name": "create_system_tools", + "filepath": "lib/laddr/src/laddr/core/system_tools.py", + "start_line": 787, + "end_line": 921, + "code": "def create_system_tools(message_bus, storage_backend=None, agent=None) -> dict[str, tuple[Any, list[str]]]:\n \"\"\"\n Create system tools for task delegation and artifact management.\n \n This function checks for user-provided overrides before registering\n the default implementations. Users can use @override_system_tool \n decorator to replace any system tool OR create entirely new system tools.\n \n Args:\n message_bus: Message queue backend\n storage_backend: Optional storage backend (MinIO/S3)\n agent: Optional agent instance for accessing current_job_id context\n \n Returns:\n Dict mapping tool names to (tool_function, aliases) tuples\n \"\"\"\n import functools\n import inspect as _inspect\n \n def wrap_with_runtime_injection(tool_func):\n \"\"\"\n Wrap a custom system tool function to inject runtime parameters.\n \n Custom override functions can declare _message_bus, _artifact_storage, _agent\n parameters which will be automatically injected at call time.\n \"\"\"\n sig = _inspect.signature(tool_func)\n needs_injection = any(p in sig.parameters for p in ['_message_bus', '_artifact_storage', '_agent'])\n \n if not needs_injection:\n # No injection needed, return as-is\n return tool_func\n \n # Create wrapper that injects runtime parameters\n @functools.wraps(tool_func)\n async def wrapped_tool(*args, **kwargs):\n # Inject runtime parameters if not already provided\n if '_message_bus' in sig.parameters and '_message_bus' not in kwargs:\n kwargs['_message_bus'] = message_bus\n if '_artifact_storage' in sig.parameters and '_artifact_storage' not in kwargs:\n kwargs['_artifact_storage'] = storage_backend\n if '_agent' in sig.parameters and '_agent' not in kwargs:\n kwargs['_agent'] = agent\n \n # Call the original function\n result = tool_func(*args, **kwargs)\n \n # Await if async\n if _inspect.iscoroutine(result) or _inspect.isawaitable(result):\n return await result\n return result\n \n return wrapped_tool\n \n tools = {}\n\n # Task delegation tool (with agent context for job_id propagation)\n override = get_tool_override(\"system_delegate_task\")\n if override:\n logger.info(\"✅ Using custom override for system_delegate_task\")\n tools[\"system_delegate_task\"] = (wrap_with_runtime_injection(override), [])\n else:\n delegation_tool = TaskDelegationTool(message_bus, storage_backend, agent=agent)\n tools[\"system_delegate_task\"] = (delegation_tool.delegate_task, [])\n\n # Parallel delegation and document splitting tools WITH ALIASES\n parallel_tool = ParallelDelegationTool(message_bus, storage_backend, agent=agent)\n \n # Register with aliases instead of duplicate entries\n override = get_tool_override(\"system_delegate_parallel\")\n if override:\n logger.info(\"✅ Using custom override for system_delegate_parallel\")\n tools[\"system_delegate_parallel\"] = (wrap_with_runtime_injection(override), [\"delegate_parallel\"])\n else:\n tools[\"system_delegate_parallel\"] = (\n parallel_tool.delegate_parallel,\n [\"delegate_parallel\"] # Friendly alias\n )\n \n override = get_tool_override(\"system_split_document\")\n if override:\n logger.info(\"✅ Using custom override for system_split_document\")\n tools[\"system_split_document\"] = (wrap_with_runtime_injection(override), [\"split_document\"])\n else:\n tools[\"system_split_document\"] = (\n parallel_tool.split_document,\n [\"split_document\"] # Friendly alias\n )\n\n # Artifact storage tools (if storage backend available)\n if storage_backend:\n # Pass configured default bucket when available (agent may be None)\n default_bucket = None\n try:\n default_bucket = agent.env_config.storage_bucket if agent and getattr(agent, 'env_config', None) else None\n except Exception:\n default_bucket = None\n artifact_tool = ArtifactStorageTool(storage_backend, default_bucket=default_bucket)\n \n override = get_tool_override(\"system_store_artifact\")\n if override:\n logger.info(\"✅ Using custom override for system_store_artifact\")\n tools[\"system_store_artifact\"] = (wrap_with_runtime_injection(override), [])\n else:\n tools[\"system_store_artifact\"] = (artifact_tool.store_artifact, [])\n \n override = get_tool_override(\"system_retrieve_artifact\")\n if override:\n logger.info(\"✅ Using custom override for system_retrieve_artifact\")\n tools[\"system_retrieve_artifact\"] = (wrap_with_runtime_injection(override), [])\n else:\n tools[\"system_retrieve_artifact\"] = (artifact_tool.retrieve_artifact, [])\n \n override = get_tool_override(\"system_list_artifacts\")\n if override:\n logger.info(\"✅ Using custom override for system_list_artifacts\")\n tools[\"system_list_artifacts\"] = (wrap_with_runtime_injection(override), [])\n else:\n tools[\"system_list_artifacts\"] = (artifact_tool.list_artifacts, [])\n\n # NEW FEATURE: Register any additional custom system tools that weren't overrides\n # This allows users to create entirely new system tools, not just override existing ones\n known_tool_names = set(tools.keys())\n for tool_name, tool_func in _TOOL_OVERRIDES.items():\n # Skip aliases (like \"delegate_parallel\") - they're registered with the main tool\n if tool_name in [\"delegate_parallel\", \"split_document\", \"delegate_task\"]:\n continue\n \n # Add new custom system tools that don't override built-in ones\n if tool_name not in known_tool_names:\n logger.info(f\"✅ Registering NEW system tool: {tool_name}\")\n # Wrap with runtime injection\n tools[tool_name] = (wrap_with_runtime_injection(tool_func), [])\n\n return tools", + "docstring": "Create system tools for task delegation and artifact management.\n\nThis function checks for user-provided overrides before registering\nthe default implementations. Users can use @override_system_tool \ndecorator to replace any system tool OR create entirely new system tools.\n\nArgs:\n message_bus: Message queue backend\n storage_backend: Optional storage backend (MinIO/S3)\n agent: Optional agent instance for accessing current_job_id context\n\nReturns:\n Dict mapping tool names to (tool_function, aliases) tuples", + "dependencies": [ + "wraps", + "tool_func", + "any", + "info", + "TaskDelegationTool", + "iscoroutine", + "set", + "keys", + "getattr", + "isawaitable", + "wrap_with_runtime_injection", + "ArtifactStorageTool", + "get_tool_override", + "items", + "ParallelDelegationTool", + "signature" + ], + "complexity": 22, + "business_context": "queue, agent, runtime, create, tool", + "imports": [ + "sys", + "time", + "functools", + "inspect" + ], + "called_functions": [ + "wraps", + "tool_func", + "any", + "info", + "TaskDelegationTool", + "iscoroutine", + "set", + "keys", + "getattr", + "isawaitable", + "wrap_with_runtime_injection", + "ArtifactStorageTool", + "get_tool_override", + "items", + "ParallelDelegationTool", + "signature" + ], + "parent_class": "", + "decorators": [], + "parameters": [ + { + "name": "message_bus", + "type": "Any" + }, + { + "name": "storage_backend", + "type": "Any" + }, + { + "name": "agent", + "type": "Any" + } + ], + "return_type": "dict[str, tuple[Any, list[str]]]" + }, + { + "type": "function", + "name": "decorator", + "filepath": "lib/laddr/src/laddr/core/system_tools.py", + "start_line": 70, + "end_line": 82, + "code": " def decorator(func: Callable) -> Callable:\n _TOOL_OVERRIDES[tool_name] = func\n \n # Also register common aliases\n if tool_name == \"system_delegate_parallel\":\n _TOOL_OVERRIDES[\"delegate_parallel\"] = func\n elif tool_name == \"system_split_document\":\n _TOOL_OVERRIDES[\"split_document\"] = func\n elif tool_name == \"system_delegate_task\":\n _TOOL_OVERRIDES[\"delegate_task\"] = func\n \n logger.info(f\"✅ Registered tool override: {tool_name} -> {func.__name__}\")\n return func", + "docstring": "", + "dependencies": [ + "info" + ], + "complexity": 4, + "business_context": "tool", + "imports": [ + "sys" + ], + "called_functions": [ + "info" + ], + "parent_class": "", + "decorators": [], + "parameters": [ + { + "name": "func", + "type": "Callable" + } + ], + "return_type": "Callable" + }, + { + "type": "function", + "name": "wrap_with_runtime_injection", + "filepath": "lib/laddr/src/laddr/core/system_tools.py", + "start_line": 806, + "end_line": 839, + "code": " def wrap_with_runtime_injection(tool_func):\n \"\"\"\n Wrap a custom system tool function to inject runtime parameters.\n \n Custom override functions can declare _message_bus, _artifact_storage, _agent\n parameters which will be automatically injected at call time.\n \"\"\"\n sig = _inspect.signature(tool_func)\n needs_injection = any(p in sig.parameters for p in ['_message_bus', '_artifact_storage', '_agent'])\n \n if not needs_injection:\n # No injection needed, return as-is\n return tool_func\n \n # Create wrapper that injects runtime parameters\n @functools.wraps(tool_func)\n async def wrapped_tool(*args, **kwargs):\n # Inject runtime parameters if not already provided\n if '_message_bus' in sig.parameters and '_message_bus' not in kwargs:\n kwargs['_message_bus'] = message_bus\n if '_artifact_storage' in sig.parameters and '_artifact_storage' not in kwargs:\n kwargs['_artifact_storage'] = storage_backend\n if '_agent' in sig.parameters and '_agent' not in kwargs:\n kwargs['_agent'] = agent\n \n # Call the original function\n result = tool_func(*args, **kwargs)\n \n # Await if async\n if _inspect.iscoroutine(result) or _inspect.isawaitable(result):\n return await result\n return result\n \n return wrapped_tool", + "docstring": "Wrap a custom system tool function to inject runtime parameters.\n\nCustom override functions can declare _message_bus, _artifact_storage, _agent\nparameters which will be automatically injected at call time.", + "dependencies": [ + "wraps", + "tool_func", + "iscoroutine", + "any", + "isawaitable", + "signature" + ], + "complexity": 10, + "business_context": "create, tool, agent, runtime", + "imports": [ + "sys", + "time", + "functools", + "inspect" + ], + "called_functions": [ + "wraps", + "tool_func", + "iscoroutine", + "any", + "isawaitable", + "signature" + ], + "parent_class": "", + "decorators": [], + "parameters": [ + { + "name": "tool_func", + "type": "Any" + } + ], + "return_type": "None" + }, + { + "type": "function", + "name": "nearest_whitespace", + "filepath": "lib/laddr/src/laddr/core/system_tools.py", + "start_line": 734, + "end_line": 752, + "code": " def nearest_whitespace(target: int, low: int, high: int) -> int:\n target = max(low, min(high, target))\n window = 200 # search window on either side\n left = max(low, target - window)\n right = min(high, target + window)\n best = None\n best_dist = None\n for idx in range(target, left - 1, -1):\n if document[idx:idx+1].isspace():\n best = idx\n best_dist = abs(idx - target)\n break\n if best is None:\n for idx in range(target, right + 1):\n if document[idx:idx+1].isspace():\n best = idx\n best_dist = abs(idx - target)\n break\n return best if best is not None else target", + "docstring": "", + "dependencies": [ + "range", + "abs", + "isspace", + "min", + "max" + ], + "complexity": 6, + "business_context": "", + "imports": [], + "called_functions": [ + "range", + "abs", + "isspace", + "min", + "max" + ], + "parent_class": "", + "decorators": [], + "parameters": [ + { + "name": "target", + "type": "int" + }, + { + "name": "low", + "type": "int" + }, + { + "name": "high", + "type": "int" + } + ], + "return_type": "int" + }, + { + "type": "class", + "name": "NoOpLLM", + "filepath": "lib/laddr/src/laddr/core/llm.py", + "start_line": 10, + "end_line": 19, + "code": "class NoOpLLM:\n \"\"\"\n No-op LLM backend that echoes prompts.\n \n Useful for testing and local dev without API keys.\n \"\"\"\n\n async def generate(self, prompt: str, system: str | None = None, **kwargs) -> str:\n \"\"\"Echo the prompt as response.\"\"\"\n return f\"[NoOpLLM Echo] Prompt: {prompt[:100]}...\"", + "docstring": "No-op LLM backend that echoes prompts.\n\nUseful for testing and local dev without API keys.", + "dependencies": [], + "complexity": 2, + "business_context": "generate, llm", + "imports": [], + "called_functions": [], + "parent_class": "", + "decorators": [], + "parameters": [], + "return_type": "" + }, + { + "type": "class", + "name": "OpenAILLM", + "filepath": "lib/laddr/src/laddr/core/llm.py", + "start_line": 22, + "end_line": 108, + "code": "class OpenAILLM:\n \"\"\"OpenAI LLM backend.\"\"\"\n", + "docstring": "OpenAI LLM backend.", + "dependencies": [], + "complexity": 5, + "business_context": "llm", + "imports": [], + "called_functions": [], + "parent_class": "", + "decorators": [], + "parameters": [], + "return_type": "" + }, + { + "type": "method", + "name": "OpenAILLM.__init__", + "filepath": "lib/laddr/src/laddr/core/llm.py", + "start_line": 25, + "end_line": 33, + "code": " def __init__(self, api_key: str | None, model: str | None = None, base_url: str | None = None):\n import os\n self.api_key = api_key or os.getenv(\"OPENAI_API_KEY\")\n # Support per-agent model config via env\n self.default_model = (\n model or os.getenv(\"AGENT_MODEL\") or os.getenv(\"OPENAI_MODEL\") or \"gpt-4\"\n )\n self.base_url = base_url\n self._client = None", + "docstring": "", + "dependencies": [ + "getenv" + ], + "complexity": 3, + "business_context": "config, agent", + "imports": [ + "os" + ], + "called_functions": [ + "getenv" + ], + "parent_class": "OpenAILLM", + "decorators": [], + "parameters": [ + { + "name": "self", + "type": "Any" + }, + { + "name": "api_key", + "type": "str | None" + }, + { + "name": "model", + "type": "str | None" + }, + { + "name": "base_url", + "type": "str | None" + } + ], + "return_type": "None" + }, + { + "type": "class", + "name": "AnthropicLLM", + "filepath": "lib/laddr/src/laddr/core/llm.py", + "start_line": 111, + "end_line": 170, + "code": "class AnthropicLLM:\n \"\"\"Anthropic LLM backend.\"\"\"\n", + "docstring": "Anthropic LLM backend.", + "dependencies": [], + "complexity": 4, + "business_context": "llm", + "imports": [], + "called_functions": [], + "parent_class": "", + "decorators": [], + "parameters": [], + "return_type": "" + }, + { + "type": "method", + "name": "AnthropicLLM.__init__", + "filepath": "lib/laddr/src/laddr/core/llm.py", + "start_line": 114, + "end_line": 118, + "code": " def __init__(self, api_key: str | None, model: str | None = None):\n import os\n self.api_key = api_key or os.getenv(\"ANTHROPIC_API_KEY\")\n self.default_model = model or \"claude-3-sonnet-20240229\"\n self._client = None", + "docstring": "", + "dependencies": [ + "getenv" + ], + "complexity": 3, + "business_context": "", + "imports": [ + "os" + ], + "called_functions": [ + "getenv" + ], + "parent_class": "AnthropicLLM", + "decorators": [], + "parameters": [ + { + "name": "self", + "type": "Any" + }, + { + "name": "api_key", + "type": "str | None" + }, + { + "name": "model", + "type": "str | None" + } + ], + "return_type": "None" + }, + { + "type": "class", + "name": "GeminiLLM", + "filepath": "lib/laddr/src/laddr/core/llm.py", + "start_line": 173, + "end_line": 237, + "code": "class GeminiLLM:\n \"\"\"Google Gemini LLM backend.\"\"\"\n", + "docstring": "Google Gemini LLM backend.", + "dependencies": [], + "complexity": 5, + "business_context": "llm", + "imports": [], + "called_functions": [], + "parent_class": "", + "decorators": [], + "parameters": [], + "return_type": "" + }, + { + "type": "method", + "name": "GeminiLLM.__init__", + "filepath": "lib/laddr/src/laddr/core/llm.py", + "start_line": 176, + "end_line": 180, + "code": " def __init__(self, api_key: str | None, model: str | None = None):\n import os\n self.api_key = api_key or os.getenv(\"GEMINI_API_KEY\")\n self.default_model = model or \"gemini-2.5-flash\"\n self._model = None", + "docstring": "", + "dependencies": [ + "getenv" + ], + "complexity": 3, + "business_context": "", + "imports": [ + "os" + ], + "called_functions": [ + "getenv" + ], + "parent_class": "GeminiLLM", + "decorators": [], + "parameters": [ + { + "name": "self", + "type": "Any" + }, + { + "name": "api_key", + "type": "str | None" + }, + { + "name": "model", + "type": "str | None" + } + ], + "return_type": "None" + }, + { + "type": "class", + "name": "GroqLLM", + "filepath": "lib/laddr/src/laddr/core/llm.py", + "start_line": 240, + "end_line": 321, + "code": "class GroqLLM:\n \"\"\"Groq LLM backend (ultra-fast inference).\"\"\"\n", + "docstring": "Groq LLM backend (ultra-fast inference).", + "dependencies": [], + "complexity": 5, + "business_context": "llm", + "imports": [], + "called_functions": [], + "parent_class": "", + "decorators": [], + "parameters": [], + "return_type": "" + }, + { + "type": "method", + "name": "GroqLLM.__init__", + "filepath": "lib/laddr/src/laddr/core/llm.py", + "start_line": 243, + "end_line": 247, + "code": " def __init__(self, api_key: str | None, model: str | None = None):\n import os\n self.api_key = api_key or os.getenv(\"GROQ_API_KEY\")\n self.default_model = model or \"llama-3.3-70b-versatile\"\n self._client = None", + "docstring": "", + "dependencies": [ + "getenv" + ], + "complexity": 3, + "business_context": "", + "imports": [ + "os" + ], + "called_functions": [ + "getenv" + ], + "parent_class": "GroqLLM", + "decorators": [], + "parameters": [ + { + "name": "self", + "type": "Any" + }, + { + "name": "api_key", + "type": "str | None" + }, + { + "name": "model", + "type": "str | None" + } + ], + "return_type": "None" + }, + { + "type": "class", + "name": "GrokLLM", + "filepath": "lib/laddr/src/laddr/core/llm.py", + "start_line": 324, + "end_line": 438, + "code": "class GrokLLM:\n \"\"\"xAI Grok LLM backend - uses native HTTP API.\"\"\"\n", + "docstring": "xAI Grok LLM backend - uses native HTTP API.", + "dependencies": [], + "complexity": 5, + "business_context": "llm", + "imports": [], + "called_functions": [], + "parent_class": "", + "decorators": [], + "parameters": [], + "return_type": "" + }, + { + "type": "method", + "name": "GrokLLM.__init__", + "filepath": "lib/laddr/src/laddr/core/llm.py", + "start_line": 327, + "end_line": 331, + "code": " def __init__(self, api_key: str | None, model: str | None = None):\n import os\n self.api_key = api_key or os.getenv(\"XAI_API_KEY\") or os.getenv(\"GROK_API_KEY\")\n self.default_model = model or \"grok-4\"\n self.base_url = \"https://api.x.ai/v1\"", + "docstring": "", + "dependencies": [ + "getenv" + ], + "complexity": 3, + "business_context": "", + "imports": [ + "os" + ], + "called_functions": [ + "getenv" + ], + "parent_class": "GrokLLM", + "decorators": [], + "parameters": [ + { + "name": "self", + "type": "Any" + }, + { + "name": "api_key", + "type": "str | None" + }, + { + "name": "model", + "type": "str | None" + } + ], + "return_type": "None" + }, + { + "type": "class", + "name": "HTTPLLM", + "filepath": "lib/laddr/src/laddr/core/llm.py", + "start_line": 441, + "end_line": 479, + "code": "class HTTPLLM:\n \"\"\"Generic HTTP LLM adapter expecting a simple JSON API.\n\n It POSTs to endpoint with body: {\"prompt\": str, \"system\": str | null, \"params\": {...}}\n and expects a JSON response with {\"text\": str}.\n \"\"\"\n", + "docstring": "Generic HTTP LLM adapter expecting a simple JSON API.\n\nIt POSTs to endpoint with body: {\"prompt\": str, \"system\": str | null, \"params\": {...}}\nand expects a JSON response with {\"text\": str}.", + "dependencies": [], + "complexity": 5, + "business_context": "llm", + "imports": [], + "called_functions": [], + "parent_class": "", + "decorators": [], + "parameters": [], + "return_type": "" + }, + { + "type": "method", + "name": "HTTPLLM.__init__", + "filepath": "lib/laddr/src/laddr/core/llm.py", + "start_line": 448, + "end_line": 449, + "code": " def __init__(self, endpoint: str):\n self.endpoint = endpoint", + "docstring": "", + "dependencies": [], + "complexity": 1, + "business_context": "", + "imports": [], + "called_functions": [], + "parent_class": "HTTPLLM", + "decorators": [], + "parameters": [ + { + "name": "self", + "type": "Any" + }, + { + "name": "endpoint", + "type": "str" + } + ], + "return_type": "None" + }, + { + "type": "class", + "name": "OllamaLLM", + "filepath": "lib/laddr/src/laddr/core/llm.py", + "start_line": 482, + "end_line": 606, + "code": "class OllamaLLM:\n \"\"\"Local Ollama LLM backend (talks to a local Ollama HTTP server).\n\n Default base URL: http://localhost:11434\n Expected model names: e.g. \"gemma2:2b\" (use LLM_MODEL or per-agent LLM_MODEL_)\n \"\"\"\n", + "docstring": "Local Ollama LLM backend (talks to a local Ollama HTTP server).\n\nDefault base URL: http://localhost:11434\nExpected model names: e.g. \"gemma2:2b\" (use LLM_MODEL or per-agent LLM_MODEL_)", + "dependencies": [], + "complexity": 5, + "business_context": "llm, agent", + "imports": [], + "called_functions": [], + "parent_class": "", + "decorators": [], + "parameters": [], + "return_type": "" + }, + { + "type": "method", + "name": "OllamaLLM.__init__", + "filepath": "lib/laddr/src/laddr/core/llm.py", + "start_line": 489, + "end_line": 492, + "code": " def __init__(self, base_url: str | None = None, model: str | None = None):\n import os\n self.base_url = base_url or os.getenv(\"OLLAMA_BASE_URL\") or \"http://localhost:11434\"\n self.default_model = model or os.getenv(\"LLM_MODEL\") or os.getenv(\"OLLAMA_MODEL\") or \"gemma2:2b\"", + "docstring": "", + "dependencies": [ + "getenv" + ], + "complexity": 3, + "business_context": "", + "imports": [ + "os" + ], + "called_functions": [ + "getenv" + ], + "parent_class": "OllamaLLM", + "decorators": [], + "parameters": [ + { + "name": "self", + "type": "Any" + }, + { + "name": "base_url", + "type": "str | None" + }, + { + "name": "model", + "type": "str | None" + } + ], + "return_type": "None" + }, + { + "type": "function", + "name": "_do_request", + "filepath": "lib/laddr/src/laddr/core/llm.py", + "start_line": 457, + "end_line": 460, + "code": " def _do_request():\n req = urllib.request.Request(self.endpoint, data=body, headers={\"Content-Type\": \"application/json\"})\n with urllib.request.urlopen(req, timeout=30) as resp:\n return resp.read().decode(\"utf-8\")", + "docstring": "", + "dependencies": [ + "decode", + "read", + "Request", + "urlopen" + ], + "complexity": 2, + "business_context": "", + "imports": [ + "json", + "urllib.request" + ], + "called_functions": [ + "decode", + "read", + "Request", + "urlopen" + ], + "parent_class": "", + "decorators": [], + "parameters": [], + "return_type": "None" + }, + { + "type": "function", + "name": "cli", + "filepath": "lib/laddr/src/laddr/cli/main.py", + "start_line": 38, + "end_line": 39, + "code": "def cli():\n \"\"\"Laddr - Transparent, Docker-native agent framework.\"\"\"", + "docstring": "Laddr - Transparent, Docker-native agent framework.", + "dependencies": [ + "group", + "version_option" + ], + "complexity": 1, + "business_context": "agent", + "imports": [], + "called_functions": [ + "group", + "version_option" + ], + "parent_class": "", + "decorators": [ + "group", + "version_option" + ], + "parameters": [], + "return_type": "None" + }, + { + "type": "function", + "name": "run_local", + "filepath": "lib/laddr/src/laddr/cli/main.py", + "start_line": 59, + "end_line": 107, + "code": "def run_local(agent: str, input_json: str):\n \"\"\"Run an agent once locally using in-memory components (no Redis/DB).\"\"\"\n import json\n import asyncio\n import os\n import sys\n # Ensure current working directory is importable (for 'agents' package)\n try:\n cwd = os.getcwd()\n if cwd not in sys.path:\n sys.path.insert(0, cwd)\n except Exception:\n pass\n # Sensible local defaults to avoid external deps\n try:\n if not os.environ.get(\"DATABASE_URL\"):\n os.environ[\"DATABASE_URL\"] = \"sqlite:///laddr.db\"\n if not os.environ.get(\"QUEUE_BACKEND\") and not os.environ.get(\"REDIS_URL\"):\n os.environ[\"QUEUE_BACKEND\"] = \"memory\"\n except Exception:\n pass\n from laddr.core.config import load_agents\n\n try:\n payload = json.loads(input_json)\n except Exception as e:\n raise click.ClickException(f\"Invalid JSON for --input: {e}\")\n\n agents = load_agents()\n a = agents.get(agent)\n if a is None:\n # Fallback: try instantiating legacy-style agent (requires config/env)\n try:\n mod = __import__(f\"agents.{agent}.handler\", fromlist=[\"*\"])\n cls_name = f\"{agent.capitalize()}Agent\"\n AgentCls = getattr(mod, cls_name)\n from laddr.core.config import LaddrConfig, AgentConfig # type: ignore\n cfg = AgentConfig(name=agent, role=getattr(AgentCls, \"ROLE\", agent), goal=getattr(AgentCls, \"GOAL\", \"\"))\n env = LaddrConfig()\n a = AgentCls(cfg, env)\n except Exception as e:\n hint = (\n \"Make sure you're running this command from your project root where the 'agents/' folder exists, \"\n \"and that 'agents/__init__.py' is present.\"\n )\n raise click.ClickException(f\"Agent not found or cannot be constructed: {e}\\n{hint}\")\n\n result = asyncio.run(a.handle(payload))\n click.echo(json.dumps(result, ensure_ascii=False))", + "docstring": "Run an agent once locally using in-memory components (no Redis/DB).", + "dependencies": [ + "capitalize", + "option", + "LaddrConfig", + "load_agents", + "insert", + "AgentCls", + "argument", + "get", + "loads", + "getattr", + "AgentConfig", + "run", + "getcwd", + "command", + "echo", + "dumps", + "handle", + "ClickException", + "__import__" + ], + "complexity": 10, + "business_context": "config, handle, agent", + "imports": [ + "os", + "laddr.core.config", + "click", + "sys", + "json", + "asyncio", + "laddr" + ], + "called_functions": [ + "capitalize", + "option", + "LaddrConfig", + "load_agents", + "insert", + "AgentCls", + "argument", + "get", + "loads", + "getattr", + "AgentConfig", + "run", + "getcwd", + "command", + "echo", + "dumps", + "handle", + "ClickException", + "__import__" + ], + "parent_class": "", + "decorators": [ + "command", + "argument", + "option" + ], + "parameters": [ + { + "name": "agent", + "type": "str" + }, + { + "name": "input_json", + "type": "str" + } + ], + "return_type": "None" + }, + { + "type": "function", + "name": "main", + "filepath": "lib/laddr/src/laddr/cli/main.py", + "start_line": 110, + "end_line": 127, + "code": "def main():\n \"\"\"Main entry point with error handling.\"\"\"\n try:\n cli()\n except LaddrError as e:\n print_error(e.message, hint=e.hint)\n sys.exit(1)\n except click.ClickException as e:\n e.show()\n sys.exit(e.exit_code)\n except KeyboardInterrupt:\n console.print(\"\\n[yellow]Operation cancelled[/yellow]\")\n sys.exit(130)\n except Exception as e:\n console.print(f\"\\n[red bold]Unexpected error:[/red bold] {e}\")\n if \"--debug\" in sys.argv:\n traceback.print_exc()\n sys.exit(1)", + "docstring": "Main entry point with error handling.", + "dependencies": [ + "print_error", + "print_exc", + "print", + "show", + "exit", + "cli" + ], + "complexity": 6, + "business_context": "message", + "imports": [ + "sys", + "click", + "traceback" + ], + "called_functions": [ + "print_error", + "print_exc", + "print", + "show", + "exit", + "cli" + ], + "parent_class": "", + "decorators": [], + "parameters": [], + "return_type": "None" + }, + { + "type": "function", + "name": "prompt", + "filepath": "lib/laddr/src/laddr/cli/commands/prompt.py", + "start_line": 20, + "end_line": 21, + "code": "def prompt():\n \"\"\"Run and manage prompt executions.\"\"\"", + "docstring": "Run and manage prompt executions.", + "dependencies": [ + "group" + ], + "complexity": 1, + "business_context": "", + "imports": [], + "called_functions": [ + "group" + ], + "parent_class": "", + "decorators": [ + "group" + ], + "parameters": [], + "return_type": "None" + }, + { + "type": "function", + "name": "run_prompt", + "filepath": "lib/laddr/src/laddr/cli/commands/prompt.py", + "start_line": 49, + "end_line": 112, + "code": "def run_prompt(prompt_name: str, inputs: tuple, json_input: str | None, wait: bool, timeout: int):\n \"\"\"\n Run a prompt execution.\n \n Examples:\n laddr prompt run researcher --input query=\"AI trends\"\n laddr prompt run researcher --json '{\"query\": \"AI trends\"}'\n laddr prompt run researcher --json @inputs.json\n \"\"\"\n try:\n # Parse inputs\n parsed_inputs = {}\n\n if json_input:\n if json_input.startswith(\"@\"):\n # Load from file\n file_path = Path(json_input[1:])\n if not file_path.exists():\n print_error(f\"Input file not found: {file_path}\")\n return\n with open(file_path) as f:\n parsed_inputs = json.load(f)\n else:\n # Parse JSON string\n parsed_inputs = json.loads(json_input)\n\n # Add KEY=VALUE inputs\n for inp in inputs:\n if \"=\" not in inp:\n print_error(f\"Invalid input format: {inp}. Expected KEY=VALUE\")\n return\n key, value = inp.split(\"=\", 1)\n parsed_inputs[key] = value\n\n if not parsed_inputs:\n print_error(\"No inputs provided. Use --input KEY=VALUE or --json\")\n return\n\n console.print(f\"[cyan]Running prompt:[/cyan] {prompt_name}\")\n console.print(f\"[dim]Inputs:[/dim] {json.dumps(parsed_inputs, indent=2)}\")\n\n # Load config and run\n config = LaddrConfig()\n\n result = asyncio.run(run_agent(\n agent_name=prompt_name,\n inputs=parsed_inputs,\n env_config=config\n ))\n\n prompt_id = result.get(\"prompt_id\") or result.get(\"job_id\")\n\n if result.get(\"status\") == \"success\":\n print_success(f\"Prompt completed: {prompt_id}\")\n console.print(\"\\n[green bold]Result:[/green bold]\")\n console.print(json.dumps(result.get(\"result\"), indent=2))\n else:\n print_error(f\"Prompt failed: {result.get('error')}\")\n console.print(f\"[dim]Prompt ID:[/dim] {prompt_id}\")\n\n except Exception as e:\n print_error(f\"Failed to run prompt: {e}\")\n if \"--debug\" in click.get_current_context().params:\n raise", + "docstring": "Run a prompt execution.\n\nExamples:\n laddr prompt run researcher --input query=\"AI trends\"\n laddr prompt run researcher --json '{\"query\": \"AI trends\"}'\n laddr prompt run researcher --json @inputs.json", + "dependencies": [ + "print", + "get_current_context", + "print_success", + "split", + "option", + "LaddrConfig", + "exists", + "run_agent", + "Path", + "argument", + "print_error", + "get", + "loads", + "startswith", + "run", + "load", + "command", + "dumps", + "open" + ], + "complexity": 12, + "business_context": "load, query, parse, config", + "imports": [ + "json", + "click", + "asyncio" + ], + "called_functions": [ + "print", + "get_current_context", + "print_success", + "split", + "option", + "LaddrConfig", + "exists", + "run_agent", + "Path", + "argument", + "print_error", + "get", + "loads", + "startswith", + "run", + "load", + "command", + "dumps", + "open" + ], + "parent_class": "", + "decorators": [ + "command", + "argument", + "option", + "option", + "option", + "option" + ], + "parameters": [ + { + "name": "prompt_name", + "type": "str" + }, + { + "name": "inputs", + "type": "tuple" + }, + { + "name": "json_input", + "type": "str | None" + }, + { + "name": "wait", + "type": "bool" + }, + { + "name": "timeout", + "type": "int" + } + ], + "return_type": "None" + }, + { + "type": "function", + "name": "list_prompts", + "filepath": "lib/laddr/src/laddr/cli/commands/prompt.py", + "start_line": 121, + "end_line": 152, + "code": "def list_prompts(limit: int):\n \"\"\"List recent prompt executions.\"\"\"\n try:\n from laddr.core import DatabaseService, LaddrConfig\n\n config = LaddrConfig()\n db = DatabaseService(config.database_url)\n\n prompts = db.list_prompts(limit=limit)\n\n if not prompts:\n console.print(\"[yellow]No prompt executions found[/yellow]\")\n return\n\n console.print(f\"\\n[cyan bold]Recent Prompts[/cyan bold] (limit: {limit})\\n\")\n\n for prompt in prompts:\n status = prompt.get(\"status\", \"unknown\")\n status_color = {\n \"completed\": \"green\",\n \"pending\": \"yellow\",\n \"running\": \"blue\",\n \"failed\": \"red\"\n }.get(status, \"dim\")\n\n console.print(f\"[{status_color}]●[/{status_color}] {prompt.get('prompt_id')} - {prompt.get('prompt_name')}\")\n console.print(f\" Status: [{status_color}]{status}[/{status_color}]\")\n console.print(f\" Created: {prompt.get('created_at')}\")\n console.print()\n\n except Exception as e:\n print_error(f\"Failed to list prompts: {e}\")", + "docstring": "List recent prompt executions.", + "dependencies": [ + "print_error", + "get", + "print", + "command", + "option", + "LaddrConfig", + "DatabaseService", + "list_prompts" + ], + "complexity": 4, + "business_context": "config", + "imports": [ + "laddr.core" + ], + "called_functions": [ + "print_error", + "get", + "print", + "command", + "option", + "LaddrConfig", + "DatabaseService", + "list_prompts" + ], + "parent_class": "", + "decorators": [ + "command", + "option" + ], + "parameters": [ + { + "name": "limit", + "type": "int" + } + ], + "return_type": "None" + }, + { + "type": "function", + "name": "run", + "filepath": "lib/laddr/src/laddr/cli/commands/run.py", + "start_line": 34, + "end_line": 41, + "code": "def run(ctx: click.Context):\n \"\"\"Run environments, agents, or pipelines.\n\n Usage:\n laddr run dev [--build] [--no-detach]\n laddr run agent [--inputs '{...}']\n laddr run pipeline \n \"\"\"", + "docstring": "Run environments, agents, or pipelines.\n\nUsage:\n laddr run dev [--build] [--no-detach]\n laddr run agent [--inputs '{...}']\n laddr run pipeline ", + "dependencies": [ + "group" + ], + "complexity": 1, + "business_context": "agent", + "imports": [ + "click" + ], + "called_functions": [ + "group" + ], + "parent_class": "", + "decorators": [ + "group", + "" + ], + "parameters": [ + { + "name": "ctx", + "type": "click.Context" + } + ], + "return_type": "None" + }, + { + "type": "function", + "name": "run_dev", + "filepath": "lib/laddr/src/laddr/cli/commands/run.py", + "start_line": 47, + "end_line": 119, + "code": "def run_dev(build: bool, detach: bool):\n \"\"\"Run the Laddr development environment.\n \n Starts all infrastructure services and agent workers:\n - PostgreSQL (internal observability database)\n - Redis (message bus)\n - API server\n - Agent workers\n - Dashboard\n \n By default, shows live logs (like docker compose up).\n Use --detach/-d to run in background.\n \n Examples:\n laddr run dev # Show logs\n laddr run dev --build # Rebuild and show logs\n laddr run dev --detach # Run in background\n \"\"\"\n # Ensure we're in a project\n if not validate_project_directory(Path.cwd()):\n raise ProjectNotFoundError()\n\n # Check Docker availability\n check_docker()\n check_docker_compose()\n\n print_header(\"Starting Laddr Development Environment (This may take a few minutes)\")\n\n # Build command for docker compose up\n cmd = [\"docker\", \"compose\", \"up\"]\n if build:\n cmd.append(\"--build\")\n \n # In detached mode, start services in background first\n if detach:\n cmd.append(\"-d\")\n \n # Start services and show logs\n try:\n if not detach:\n # Non-detached mode: stream logs live (Ctrl+C to stop)\n print_info(\"Starting services and streaming logs (Ctrl+C to stop)...\")\n subprocess.run(cmd, check=False)\n else:\n # Detached mode: start in background, then show all logs\n print_info(\"Starting services in detached mode...\")\n result = subprocess.run(cmd, capture_output=True, text=True, check=False)\n if result.returncode != 0:\n print_info(f\"Error starting services: {result.stderr}\")\n raise click.ClickException(\"Failed to start services\")\n \n # Wait a moment for services to initialize\n print_info(\"Waiting for services to initialize...\")\n time.sleep(3)\n \n # Show all logs from all services\n print_info(\"Showing service logs...\\n\")\n subprocess.run([\"docker\", \"compose\", \"logs\"], check=False)\n \n # Wait for critical services to be ready\n print_info(\"\\nWaiting for services to be ready...\")\n critical_services = [\"postgres\", \"redis\", \"api\"]\n for service in critical_services:\n wait_for_service(service, timeout=30)\n \n print_success(\"\\nLaddr is running!\")\n _print_service_info()\n _print_management_commands()\n \n except KeyboardInterrupt:\n if not detach:\n print_info(\"\\nStopping services...\")\n subprocess.run([\"docker\", \"compose\", \"down\"], check=False)", + "docstring": "Run the Laddr development environment.\n\nStarts all infrastructure services and agent workers:\n- PostgreSQL (internal observability database)\n- Redis (message bus)\n- API server\n- Agent workers\n- Dashboard\n\nBy default, shows live logs (like docker compose up).\nUse --detach/-d to run in background.\n\nExamples:\n laddr run dev # Show logs\n laddr run dev --build # Rebuild and show logs\n laddr run dev --detach # Run in background", + "dependencies": [ + "ProjectNotFoundError", + "check_docker_compose", + "append", + "command", + "print_info", + "sleep", + "_print_management_commands", + "print_success", + "print_header", + "cwd", + "option", + "ClickException", + "wait_for_service", + "validate_project_directory", + "check_docker", + "_print_service_info", + "run" + ], + "complexity": 9, + "business_context": "message, initialize, database, agent", + "imports": [ + "time", + "click", + "os", + "subprocess" + ], + "called_functions": [ + "ProjectNotFoundError", + "check_docker_compose", + "append", + "command", + "print_info", + "sleep", + "_print_management_commands", + "print_success", + "print_header", + "cwd", + "option", + "ClickException", + "wait_for_service", + "validate_project_directory", + "check_docker", + "_print_service_info", + "run" + ], + "parent_class": "", + "decorators": [ + "command", + "option", + "option" + ], + "parameters": [ + { + "name": "build", + "type": "bool" + }, + { + "name": "detach", + "type": "bool" + } + ], + "return_type": "None" + }, + { + "type": "function", + "name": "run_pipeline", + "filepath": "lib/laddr/src/laddr/cli/commands/run.py", + "start_line": 124, + "end_line": 161, + "code": "def run_pipeline(pipeline_file: str):\n \"\"\"Run a pipeline defined in a YAML file.\"\"\"\n # Lazy import to avoid hard deps when not running pipeline\n import yaml # type: ignore\n\n if not validate_project_directory(Path.cwd()):\n raise ProjectNotFoundError()\n\n with open(pipeline_file, \"r\", encoding=\"utf-8\") as f:\n data = yaml.safe_load(f)\n\n stages = data.get(\"pipeline\") or data.get(\"stages\") or data.get(\"tasks\")\n if not isinstance(stages, list):\n raise click.BadParameter(\"Pipeline YAML must define a list under 'pipeline' or 'stages'\")\n\n # Use Laddr runtime (shared env for all agents)\n import asyncio\n\n from laddr.core import AgentRunner, LaddrConfig # type: ignore\n\n print_header(\"Running Pipeline\")\n\n results: dict[str, Any] = {}\n runner = AgentRunner(env_config=LaddrConfig())\n for stage in stages:\n agent_name = stage.get(\"agent\")\n # Support both {inputs: {...}} and arbitrary keys; if tasks format, pass remaining keys except 'agent'\n inputs = stage.get(\"inputs\", {k: v for k, v in stage.items() if k != \"agent\"})\n if not agent_name:\n raise click.BadParameter(\"Each stage must include 'agent'\")\n console.print(f\" [dim]→[/dim] Running stage: [cyan]{agent_name}[/cyan]\")\n res = asyncio.run(runner.run(inputs, agent_name=agent_name))\n results[agent_name] = res\n\n print_success(\"Pipeline complete\")\n console.print(\"\\n[bold cyan]Results[/bold cyan]\")\n console.print(json.dumps(results, indent=2, ensure_ascii=False))\n console.print()", + "docstring": "Run a pipeline defined in a YAML file.", + "dependencies": [ + "ProjectNotFoundError", + "get", + "print", + "command", + "isinstance", + "dumps", + "print_success", + "print_header", + "cwd", + "BadParameter", + "items", + "argument", + "safe_load", + "LaddrConfig", + "open", + "validate_project_directory", + "AgentRunner", + "run" + ], + "complexity": 7, + "business_context": "agent, runtime", + "imports": [ + "click", + "json", + "yaml", + "asyncio", + "time", + "laddr.core" + ], + "called_functions": [ + "ProjectNotFoundError", + "get", + "print", + "command", + "isinstance", + "dumps", + "print_success", + "print_header", + "cwd", + "BadParameter", + "items", + "argument", + "safe_load", + "LaddrConfig", + "open", + "validate_project_directory", + "AgentRunner", + "run" + ], + "parent_class": "", + "decorators": [ + "command", + "argument" + ], + "parameters": [ + { + "name": "pipeline_file", + "type": "str" + } + ], + "return_type": "None" + }, + { + "type": "function", + "name": "run_agent_cmd", + "filepath": "lib/laddr/src/laddr/cli/commands/run.py", + "start_line": 167, + "end_line": 218, + "code": "def run_agent_cmd(agent_name: str, inputs_json: str):\n \"\"\"Run a single agent locally using AgentRunner.\"\"\"\n import asyncio\n import os\n import sys\n\n # Ensure local project imports work (agents package)\n try:\n cwd = str(Path.cwd())\n if cwd not in sys.path:\n sys.path.insert(0, cwd)\n except Exception:\n pass\n\n # Favor local-friendly defaults to avoid external deps\n try:\n if not os.environ.get(\"DATABASE_URL\"):\n os.environ[\"DATABASE_URL\"] = \"sqlite:///laddr.db\"\n if not os.environ.get(\"QUEUE_BACKEND\") and not os.environ.get(\"REDIS_URL\"):\n os.environ[\"QUEUE_BACKEND\"] = \"memory\"\n except Exception:\n pass\n\n if not validate_project_directory(Path.cwd()):\n raise ProjectNotFoundError()\n\n try:\n inputs = json.loads(inputs_json) if inputs_json else {}\n except json.JSONDecodeError:\n raise click.BadParameter(\"--inputs must be a valid JSON object\")\n\n # Use new runtime\n try:\n from laddr.core import LaddrConfig, run_agent\n\n print_header(f\"Running Agent → {agent_name}\")\n\n # Load environment config\n config = LaddrConfig()\n\n # Run agent\n result = asyncio.run(run_agent(agent_name, inputs, config))\n\n # Print result\n print_success(f\"Job completed: {result['job_id']}\")\n console.print(\"\\n[bold cyan]Result[/bold cyan]\")\n console.print(json.dumps(result, indent=2, ensure_ascii=False))\n console.print()\n\n except Exception as e:\n print_info(f\"Error: {e}\")\n raise click.ClickException(str(e))", + "docstring": "Run a single agent locally using AgentRunner.", + "dependencies": [ + "print_info", + "print", + "print_success", + "option", + "LaddrConfig", + "insert", + "run_agent", + "cwd", + "argument", + "ProjectNotFoundError", + "get", + "loads", + "BadParameter", + "run", + "command", + "dumps", + "str", + "print_header", + "ClickException", + "validate_project_directory" + ], + "complexity": 10, + "business_context": "load, runtime, config, agent", + "imports": [ + "os", + "click", + "sys", + "json", + "asyncio", + "time", + "laddr.core" + ], + "called_functions": [ + "print_info", + "print", + "print_success", + "option", + "LaddrConfig", + "insert", + "run_agent", + "cwd", + "argument", + "ProjectNotFoundError", + "get", + "loads", + "BadParameter", + "run", + "command", + "dumps", + "str", + "print_header", + "ClickException", + "validate_project_directory" + ], + "parent_class": "", + "decorators": [ + "command", + "argument", + "option" + ], + "parameters": [ + { + "name": "agent_name", + "type": "str" + }, + { + "name": "inputs_json", + "type": "str" + } + ], + "return_type": "None" + }, + { + "type": "function", + "name": "replay_job", + "filepath": "lib/laddr/src/laddr/cli/commands/run.py", + "start_line": 224, + "end_line": 254, + "code": "def replay_job(job_id: str, reexecute: bool):\n \"\"\"Replay a previous job by job ID.\n \n Examples:\n laddr run replay abc123-456-def\n laddr run replay abc123-456-def --reexecute\n \"\"\"\n if not validate_project_directory(Path.cwd()):\n raise ProjectNotFoundError()\n\n try:\n from laddr.core import AgentRunner, LaddrConfig\n\n print_header(f\"Replaying Job → {job_id}\")\n\n # Load environment config\n config = LaddrConfig()\n runner = AgentRunner(env_config=config)\n\n # Replay\n result = runner.replay(job_id, reexecute=reexecute)\n\n # Print result\n print_success(\"Job replay complete\")\n console.print(\"\\n[bold cyan]Result[/bold cyan]\")\n console.print(json.dumps(result, indent=2, ensure_ascii=False))\n console.print()\n\n except Exception as e:\n print_info(f\"Error: {e}\")\n raise click.ClickException(str(e))", + "docstring": "Replay a previous job by job ID.\n\nExamples:\n laddr run replay abc123-456-def\n laddr run replay abc123-456-def --reexecute", + "dependencies": [ + "ProjectNotFoundError", + "print_info", + "print", + "command", + "replay", + "dumps", + "str", + "print_success", + "print_header", + "cwd", + "option", + "argument", + "LaddrConfig", + "ClickException", + "validate_project_directory", + "AgentRunner" + ], + "complexity": 3, + "business_context": "load, config", + "imports": [ + "json", + "click", + "laddr.core" + ], + "called_functions": [ + "ProjectNotFoundError", + "print_info", + "print", + "command", + "replay", + "dumps", + "str", + "print_success", + "print_header", + "cwd", + "option", + "argument", + "LaddrConfig", + "ClickException", + "validate_project_directory", + "AgentRunner" + ], + "parent_class": "", + "decorators": [ + "command", + "argument", + "option" + ], + "parameters": [ + { + "name": "job_id", + "type": "str" + }, + { + "name": "reexecute", + "type": "bool" + } + ], + "return_type": "None" + }, + { + "type": "function", + "name": "_print_service_info", + "filepath": "lib/laddr/src/laddr/cli/commands/run.py", + "start_line": 257, + "end_line": 264, + "code": "def _print_service_info() -> None:\n \"\"\"Print service URLs in minimalistic style.\"\"\"\n console.print(\"\\n[bold cyan]Services[/bold cyan]\")\n console.print(\" [dim]Dashboard[/dim] http://localhost:5173\")\n console.print(\" [dim]API[/dim] http://localhost:8000\")\n console.print(\" [dim]API Docs[/dim] http://localhost:8000/docs\")\n console.print(\" [dim]Postgres[/dim] localhost:5432\")\n console.print(\" [dim]Redis[/dim] localhost:6379\")", + "docstring": "Print service URLs in minimalistic style.", + "dependencies": [ + "print" + ], + "complexity": 1, + "business_context": "", + "imports": [ + "os" + ], + "called_functions": [ + "print" + ], + "parent_class": "", + "decorators": [], + "parameters": [], + "return_type": "None" + }, + { + "type": "function", + "name": "_print_management_commands", + "filepath": "lib/laddr/src/laddr/cli/commands/run.py", + "start_line": 267, + "end_line": 274, + "code": "def _print_management_commands() -> None:\n \"\"\"Print management command help in minimalistic style.\"\"\"\n console.print(\"\\n[bold cyan]Commands[/bold cyan]\")\n console.print(\" [cyan]laddr logs [/cyan] View agent logs\")\n console.print(\" [cyan]laddr ps[/cyan] Show container status\")\n console.print(\" [cyan]laddr scale [/cyan] Scale agent workers\")\n console.print(\" [cyan]laddr stop[/cyan] Stop all services\")\n console.print()", + "docstring": "Print management command help in minimalistic style.", + "dependencies": [ + "print" + ], + "complexity": 1, + "business_context": "agent", + "imports": [], + "called_functions": [ + "print" + ], + "parent_class": "", + "decorators": [], + "parameters": [], + "return_type": "None" + }, + { + "type": "function", + "name": "run_dev_alias", + "filepath": "lib/laddr/src/laddr/cli/commands/run.py", + "start_line": 279, + "end_line": 284, + "code": "def run_dev_alias():\n \"\"\"Alias for 'laddr run dev'.\"\"\"\n from click import Context\n\n ctx = Context(run_dev)\n ctx.invoke(run_dev)", + "docstring": "Alias for 'laddr run dev'.", + "dependencies": [ + "Context", + "invoke", + "command" + ], + "complexity": 1, + "business_context": "", + "imports": [ + "click" + ], + "called_functions": [ + "Context", + "invoke", + "command" + ], + "parent_class": "", + "decorators": [ + "command" + ], + "parameters": [], + "return_type": "None" + }, + { + "type": "function", + "name": "add", + "filepath": "lib/laddr/src/laddr/cli/commands/add.py", + "start_line": 29, + "end_line": 30, + "code": "def add():\n \"\"\"Add resources to your project (agents, tools).\"\"\"", + "docstring": "Add resources to your project (agents, tools).", + "dependencies": [ + "group" + ], + "complexity": 1, + "business_context": "", + "imports": [], + "called_functions": [ + "group" + ], + "parent_class": "", + "decorators": [ + "group" + ], + "parameters": [], + "return_type": "None" + }, + { + "type": "function", + "name": "add_agent", + "filepath": "lib/laddr/src/laddr/cli/commands/add.py", + "start_line": 40, + "end_line": 94, + "code": "def add_agent(\n agent_name: str,\n role: str | None,\n goal: str | None,\n backstory: str | None,\n llm_provider: str,\n llm_model: str,\n):\n \"\"\"Add a new agent to the project.\n \n Examples:\n laddr add agent summarizer\n laddr add agent analyst --role \"Data Analyst\" --goal \"Analyze data\"\n \"\"\"\n # Validate agent name\n if \"-\" in agent_name:\n raise click.BadParameter(\n \"Agent name cannot contain hyphens (-). Use underscores (_) instead.\",\n param_hint=\"agent_name\"\n )\n \n if not agent_name.replace(\"_\", \"\").isalnum():\n raise click.BadParameter(\n \"Agent name must contain only letters, numbers, and underscores.\",\n param_hint=\"agent_name\"\n )\n \n # Ensure we're in a project\n if not validate_project_directory(Path.cwd()):\n raise ProjectNotFoundError()\n\n print_header(f\"Adding Agent → {agent_name}\")\n\n # Prompt for missing details\n if not role:\n role = click.prompt(\"Agent role\")\n if not goal:\n goal = click.prompt(\"Agent goal\")\n if not backstory:\n backstory = click.prompt(\"Agent backstory\", default=\"\", show_default=False)\n\n # Create agent files\n print_step(\"Generating agent file\", f\"agents/{agent_name}.py\")\n _create_agent_files(\n agent_name=agent_name,\n role=role,\n goal=goal,\n backstory=backstory,\n llm_provider=llm_provider,\n llm_model=llm_model,\n )\n\n print_completion(f\"Agent '{agent_name}' created\")\n console.print(\"[dim]Next:[/dim]\")\n console.print(f\" [cyan]laddr run agent {agent_name} --inputs '{{}}'[/cyan]\\n\")", + "docstring": "Add a new agent to the project.\n\nExamples:\n laddr add agent summarizer\n laddr add agent analyst --role \"Data Analyst\" --goal \"Analyze data\"", + "dependencies": [ + "_create_agent_files", + "ProjectNotFoundError", + "print", + "command", + "prompt", + "print_header", + "cwd", + "option", + "BadParameter", + "validate_project_directory", + "argument", + "print_completion", + "isalnum", + "print_step", + "replace" + ], + "complexity": 7, + "business_context": "validate, create, agent", + "imports": [ + "click" + ], + "called_functions": [ + "_create_agent_files", + "ProjectNotFoundError", + "print", + "command", + "prompt", + "print_header", + "cwd", + "option", + "BadParameter", + "validate_project_directory", + "argument", + "print_completion", + "isalnum", + "print_step", + "replace" + ], + "parent_class": "", + "decorators": [ + "command", + "argument", + "option", + "option", + "option", + "option", + "option" + ], + "parameters": [ + { + "name": "agent_name", + "type": "str" + }, + { + "name": "role", + "type": "str | None" + }, + { + "name": "goal", + "type": "str | None" + }, + { + "name": "backstory", + "type": "str | None" + }, + { + "name": "llm_provider", + "type": "str" + }, + { + "name": "llm_model", + "type": "str" + } + ], + "return_type": "None" + }, + { + "type": "function", + "name": "add_tool", + "filepath": "lib/laddr/src/laddr/cli/commands/add.py", + "start_line": 101, + "end_line": 151, + "code": "def add_tool(\n tool_name: str,\n agent: str | None,\n description: str | None,\n):\n \"\"\"Add a new tool to an agent.\n \n Examples:\n laddr add tool calculator\n laddr add tool scraper --agent researcher\n \"\"\"\n # Validate tool name\n if \"-\" in tool_name:\n raise click.BadParameter(\n \"Tool name cannot contain hyphens (-). Use underscores (_) instead.\",\n param_hint=\"tool_name\"\n )\n \n if not tool_name.replace(\"_\", \"\").isalnum():\n raise click.BadParameter(\n \"Tool name must contain only letters, numbers, and underscores.\",\n param_hint=\"tool_name\"\n )\n \n # Ensure we're in a project\n if not validate_project_directory(Path.cwd()):\n raise ProjectNotFoundError()\n\n print_header(f\"Adding Tool → {tool_name}\")\n\n # Determine target agent\n if not agent:\n with open(\"laddr.yml\", \"r\", encoding=\"utf-8\") as f:\n config = yaml.safe_load(f)\n agents = config.get(\"project\", {}).get(\"agents\", [])\n if not agents:\n raise AgentNotFoundError(\"No agents found in project\")\n agent = agents[0]\n print_step(\"Attaching to agent\", agent)\n\n # Prompt for description if not provided\n if not description:\n description = click.prompt(\"Tool description\", default=f\"{tool_name} tool\")\n\n # Create tool file\n print_step(\"Generating tool file\", f\"tools/{tool_name}.py\")\n _create_tool_file(tool_name, agent, description)\n\n print_completion(f\"Tool '{tool_name}' created\")\n console.print(f\"[dim]Add to agents/{agent}.py:[/dim]\")\n console.print(f\" [dim]from tools.{tool_name} import {tool_name}[/dim]\\n\")", + "docstring": "Add a new tool to an agent.\n\nExamples:\n laddr add tool calculator\n laddr add tool scraper --agent researcher", + "dependencies": [ + "print", + "option", + "safe_load", + "print_step", + "cwd", + "argument", + "print_completion", + "ProjectNotFoundError", + "get", + "_create_tool_file", + "isalnum", + "BadParameter", + "replace", + "command", + "prompt", + "print_header", + "AgentNotFoundError", + "open", + "validate_project_directory" + ], + "complexity": 8, + "business_context": "config, agent, create, tool, validate", + "imports": [ + "click", + "yaml" + ], + "called_functions": [ + "print", + "option", + "safe_load", + "print_step", + "cwd", + "argument", + "print_completion", + "ProjectNotFoundError", + "get", + "_create_tool_file", + "isalnum", + "BadParameter", + "replace", + "command", + "prompt", + "print_header", + "AgentNotFoundError", + "open", + "validate_project_directory" + ], + "parent_class": "", + "decorators": [ + "command", + "argument", + "option", + "option" + ], + "parameters": [ + { + "name": "tool_name", + "type": "str" + }, + { + "name": "agent", + "type": "str | None" + }, + { + "name": "description", + "type": "str | None" + } + ], + "return_type": "None" + }, + { + "type": "function", + "name": "_create_agent_files", + "filepath": "lib/laddr/src/laddr/cli/commands/add.py", + "start_line": 154, + "end_line": 235, + "code": "def _create_agent_files(\n agent_name: str,\n role: str,\n goal: str,\n backstory: str,\n llm_provider: str,\n llm_model: str,\n) -> None:\n \"\"\"Create agent files (flat layout) and update configuration.\"\"\"\n renderer = get_template_renderer()\n\n # Ensure agents package exists\n agents_dir = Path(\"agents\")\n agents_dir.mkdir(parents=True, exist_ok=True)\n write_file(agents_dir / \"__init__.py\", \"\")\n\n # Render flat agent module\n agent_context = {\n \"name\": agent_name,\n \"role\": role,\n \"goal\": goal,\n \"backstory\": backstory or \"\",\n \"is_coordinator\": agent_name.lower() == \"coordinator\",\n \"available_agents\": [],\n \"model_env\": f\"{agent_name.upper()}_MODEL\",\n \"model\": llm_model,\n \"temperature\": 0.3 if agent_name.lower() == \"coordinator\" else 0.2,\n \"max_retries\": 3,\n \"max_iterations\": 5 if agent_name.lower() == \"coordinator\" else 15,\n \"timeout\": 600,\n \"instructions\": (\n \"When you receive a task: 1) Analyze 2) Plan 3) Execute using tools 4) Synthesize results.\"\n if agent_name.lower() == \"coordinator\"\n else \"Be concise and cite sources when researching.\"\n ),\n }\n renderer.render_to_file(\"agent_flat.py.j2\", agents_dir / f\"{agent_name}.py\", agent_context)\n\n # Worker entry using WorkerRunner template\n worker_context = {\"agent_name\": agent_name}\n\n # Update laddr.yml\n with open(\"laddr.yml\", \"r\", encoding=\"utf-8\") as f:\n config_data = yaml.safe_load(f)\n\n if agent_name not in config_data[\"project\"].get(\"agents\", []):\n config_data[\"project\"].setdefault(\"agents\", []).append(agent_name)\n\n with open(\"laddr.yml\", \"w\", encoding=\"utf-8\") as f:\n yaml.dump(config_data, f, default_flow_style=False, sort_keys=False)\n\n # Add worker service to docker-compose.yml\n add_worker_to_compose(Path.cwd(), agent_name)\n\n # Update project .env with canonical per-agent LLM settings if present\n # Do not overwrite existing values. Use canonical names: LLM_MODEL_, LLM_BACKEND_\n env_path = Path(\".env\")\n try:\n if env_path.exists():\n existing = env_path.read_text(encoding=\"utf-8\")\n else:\n existing = \"\"\n\n env_lines: list[str] = []\n upper = agent_name.upper()\n model_key = f\"LLM_MODEL_{upper}\"\n backend_key = f\"LLM_BACKEND_{upper}\"\n\n if model_key not in existing:\n env_lines.append(f\"{model_key}={llm_model}\")\n if backend_key not in existing:\n env_lines.append(f\"{backend_key}={llm_provider}\")\n\n if env_lines:\n # Append with a blank line separator if file not empty\n sep = \"\\n\" if existing and not existing.endswith(\"\\n\") else \"\"\n content = existing + sep + \"\\n\".join(env_lines) + \"\\n\"\n write_file(env_path, content)\n print_step(\"Updating .env\", \", \".join(env_lines))\n except Exception:\n # Non-fatal: skip env update if anything goes wrong\n pass", + "docstring": "Create agent files (flat layout) and update configuration.", + "dependencies": [ + "safe_load", + "exists", + "print_step", + "read_text", + "upper", + "endswith", + "add_worker_to_compose", + "render_to_file", + "setdefault", + "cwd", + "Path", + "get", + "append", + "dump", + "mkdir", + "join", + "get_template_renderer", + "lower", + "write_file", + "open" + ], + "complexity": 11, + "business_context": "update, create, llm, agent", + "imports": [ + "yaml" + ], + "called_functions": [ + "safe_load", + "exists", + "print_step", + "read_text", + "upper", + "endswith", + "add_worker_to_compose", + "render_to_file", + "setdefault", + "cwd", + "Path", + "get", + "append", + "dump", + "mkdir", + "join", + "get_template_renderer", + "lower", + "write_file", + "open" + ], + "parent_class": "", + "decorators": [], + "parameters": [ + { + "name": "agent_name", + "type": "str" + }, + { + "name": "role", + "type": "str" + }, + { + "name": "goal", + "type": "str" + }, + { + "name": "backstory", + "type": "str" + }, + { + "name": "llm_provider", + "type": "str" + }, + { + "name": "llm_model", + "type": "str" + } + ], + "return_type": "None" + }, + { + "type": "function", + "name": "_create_tool_file", + "filepath": "lib/laddr/src/laddr/cli/commands/add.py", + "start_line": 238, + "end_line": 253, + "code": "def _create_tool_file(tool_name: str, agent_name: str, description: str) -> None:\n \"\"\"Create tool file (project-level) and update agent configuration.\"\"\"\n renderer = get_template_renderer()\n\n # Create tool file in top-level tools directory\n tools_dir = Path(\"tools\")\n tools_dir.mkdir(parents=True, exist_ok=True)\n\n tool_context = {\n \"tool_name\": tool_name,\n \"tool_function_name\": tool_name.lower().replace(\"-\", \"_\"),\n \"tool_description\": description,\n \"tool_params\": \"query: str\", # Default params\n \"params\": [{\"name\": \"query\", \"description\": \"Input query\"}]\n }\n renderer.render_to_file(\"tool.py.j2\", tools_dir / f\"{tool_name}.py\", tool_context)", + "docstring": "Create tool file (project-level) and update agent configuration.", + "dependencies": [ + "mkdir", + "render_to_file", + "Path", + "get_template_renderer", + "lower", + "replace" + ], + "complexity": 1, + "business_context": "agent, query, create, tool, update", + "imports": [], + "called_functions": [ + "mkdir", + "render_to_file", + "Path", + "get_template_renderer", + "lower", + "replace" + ], + "parent_class": "", + "decorators": [], + "parameters": [ + { + "name": "tool_name", + "type": "str" + }, + { + "name": "agent_name", + "type": "str" + }, + { + "name": "description", + "type": "str" + } + ], + "return_type": "None" + }, + { + "type": "function", + "name": "logs", + "filepath": "lib/laddr/src/laddr/cli/commands/management.py", + "start_line": 30, + "end_line": 48, + "code": "def logs(agent_name: str, follow: bool, tail: int | None):\n \"\"\"View logs for an agent worker.\n \n Examples:\n laddr logs researcher\n laddr logs researcher --follow\n laddr logs researcher --tail 100\n \"\"\"\n # Ensure we're in a project\n if not validate_project_directory(Path.cwd()):\n raise ProjectNotFoundError()\n\n service_name = f\"{agent_name}_worker\"\n print_info(f\"Fetching logs for {service_name}...\")\n\n try:\n compose_logs(service_name, follow=follow, tail=tail)\n except KeyboardInterrupt:\n print_info(\"Stopped following logs\")", + "docstring": "View logs for an agent worker.\n\nExamples:\n laddr logs researcher\n laddr logs researcher --follow\n laddr logs researcher --tail 100", + "dependencies": [ + "ProjectNotFoundError", + "print_info", + "command", + "cwd", + "option", + "compose_logs", + "argument", + "validate_project_directory" + ], + "complexity": 3, + "business_context": "agent", + "imports": [], + "called_functions": [ + "ProjectNotFoundError", + "print_info", + "command", + "cwd", + "option", + "compose_logs", + "argument", + "validate_project_directory" + ], + "parent_class": "", + "decorators": [ + "command", + "argument", + "option", + "option" + ], + "parameters": [ + { + "name": "agent_name", + "type": "str" + }, + { + "name": "follow", + "type": "bool" + }, + { + "name": "tail", + "type": "int | None" + } + ], + "return_type": "None" + }, + { + "type": "function", + "name": "ps", + "filepath": "lib/laddr/src/laddr/cli/commands/management.py", + "start_line": 53, + "end_line": 72, + "code": "def ps(format: str):\n \"\"\"Show status of all services and workers.\n \n Examples:\n laddr ps\n laddr ps --format json\n \"\"\"\n # Ensure we're in a project\n if not validate_project_directory(Path.cwd()):\n raise ProjectNotFoundError()\n\n print_info(\"Fetching service status...\")\n\n output = compose_ps()\n\n if format == \"json\":\n # TODO: Parse and format as JSON\n console.print(output)\n else:\n console.print(output)", + "docstring": "Show status of all services and workers.\n\nExamples:\n laddr ps\n laddr ps --format json", + "dependencies": [ + "ProjectNotFoundError", + "print_info", + "print", + "command", + "Choice", + "cwd", + "option", + "compose_ps", + "validate_project_directory" + ], + "complexity": 3, + "business_context": "parse", + "imports": [], + "called_functions": [ + "ProjectNotFoundError", + "print_info", + "print", + "command", + "Choice", + "cwd", + "option", + "compose_ps", + "validate_project_directory" + ], + "parent_class": "", + "decorators": [ + "command", + "option" + ], + "parameters": [ + { + "name": "format", + "type": "str" + } + ], + "return_type": "None" + }, + { + "type": "function", + "name": "scale", + "filepath": "lib/laddr/src/laddr/cli/commands/management.py", + "start_line": 78, + "end_line": 101, + "code": "def scale(agent_name: str, replicas: int):\n \"\"\"Scale an agent worker to N replicas.\n \n Examples:\n laddr scale researcher 3\n laddr scale summarizer 0 # Stop all replicas\n \"\"\"\n # Ensure we're in a project\n if not validate_project_directory(Path.cwd()):\n raise ProjectNotFoundError()\n\n if replicas < 0:\n raise click.BadParameter(\"Replicas must be >= 0\")\n\n service_name = f\"{agent_name}_worker\"\n print_info(f\"Scaling {service_name} to {replicas} replicas...\")\n\n compose_scale(service_name, replicas)\n\n print_success(f\"{agent_name} scaled to {replicas} instances\")\n\n # Show updated status\n output = compose_ps([service_name])\n console.print(\"\\n\" + output)", + "docstring": "Scale an agent worker to N replicas.\n\nExamples:\n laddr scale researcher 3\n laddr scale summarizer 0 # Stop all replicas", + "dependencies": [ + "ProjectNotFoundError", + "print_info", + "print", + "command", + "compose_scale", + "print_success", + "cwd", + "BadParameter", + "compose_ps", + "argument", + "validate_project_directory" + ], + "complexity": 3, + "business_context": "agent", + "imports": [ + "click" + ], + "called_functions": [ + "ProjectNotFoundError", + "print_info", + "print", + "command", + "compose_scale", + "print_success", + "cwd", + "BadParameter", + "compose_ps", + "argument", + "validate_project_directory" + ], + "parent_class": "", + "decorators": [ + "command", + "argument", + "argument" + ], + "parameters": [ + { + "name": "agent_name", + "type": "str" + }, + { + "name": "replicas", + "type": "int" + } + ], + "return_type": "None" + }, + { + "type": "function", + "name": "stop", + "filepath": "lib/laddr/src/laddr/cli/commands/management.py", + "start_line": 106, + "end_line": 124, + "code": "def stop(volumes: bool):\n \"\"\"Stop all Laddr services.\n \n Examples:\n laddr stop\n laddr stop --volumes # Also remove data volumes\n \"\"\"\n # Ensure we're in a project\n if not validate_project_directory(Path.cwd()):\n raise ProjectNotFoundError()\n\n print_info(\"Stopping Laddr services...\")\n\n compose_down(remove_volumes=volumes)\n\n print_success(\"Services stopped\")\n\n if volumes:\n print_info(\"Named volumes removed\")", + "docstring": "Stop all Laddr services.\n\nExamples:\n laddr stop\n laddr stop --volumes # Also remove data volumes", + "dependencies": [ + "ProjectNotFoundError", + "print_info", + "command", + "print_success", + "compose_down", + "cwd", + "option", + "validate_project_directory" + ], + "complexity": 3, + "business_context": "", + "imports": [], + "called_functions": [ + "ProjectNotFoundError", + "print_info", + "command", + "print_success", + "compose_down", + "cwd", + "option", + "validate_project_directory" + ], + "parent_class": "", + "decorators": [ + "command", + "option" + ], + "parameters": [ + { + "name": "volumes", + "type": "bool" + } + ], + "return_type": "None" + }, + { + "type": "function", + "name": "infra", + "filepath": "lib/laddr/src/laddr/cli/commands/infra.py", + "start_line": 18, + "end_line": 64, + "code": "def infra(show_config: bool):\n \"\"\"Show infrastructure configuration and status.\"\"\"\n try:\n from laddr.core import LaddrConfig\n\n config = LaddrConfig()\n\n console.print(\"\\n[cyan bold]Laddr Infrastructure[/cyan bold]\\n\")\n\n # Queue backend\n console.print(f\"[green]Queue Backend:[/green] {config.queue_backend}\")\n if config.queue_backend == \"redis\":\n console.print(f\" └─ URL: [dim]{config.redis_url}[/dim]\")\n\n # Database backend\n console.print(f\"\\n[green]Database:[/green] {config.db_backend}\")\n if show_config:\n console.print(f\" └─ URL: [dim]{config.database_url}[/dim]\")\n\n # LLM backend\n console.print(f\"\\n[green]LLM Backend:[/green] {config.llm_backend}\")\n if config.llm_model:\n console.print(f\" └─ Model: [dim]{config.llm_model}[/dim]\")\n\n # Storage\n console.print(\"\\n[green]Storage:[/green] MinIO\")\n if show_config:\n console.print(f\" ├─ Endpoint: [dim]{config.minio_endpoint}[/dim]\")\n console.print(f\" ├─ Bucket: [dim]{config.minio_bucket}[/dim]\")\n console.print(f\" └─ Large response offload: [dim]{'enabled' if config.enable_large_response_storage else 'disabled'}[/dim]\")\n if config.enable_large_response_storage:\n console.print(f\" └─ Threshold: [dim]{config.storage_threshold_kb} KB[/dim]\")\n\n # Cache backend\n console.print(f\"\\n[green]Cache:[/green] {config.cache_backend}\")\n if config.cache_backend == \"redis\" and show_config:\n console.print(f\" └─ URL: [dim]{config.redis_url}[/dim]\")\n\n # Observability\n console.print(\"\\n[green]Observability:[/green]\")\n console.print(f\" ├─ Tracing: [dim]{'enabled (DB)' if config.enable_tracing else 'disabled'}[/dim]\")\n console.print(f\" └─ Metrics: [dim]{'enabled (DB)' if config.enable_metrics else 'disabled'}[/dim]\")\n\n console.print()\n\n except Exception as e:\n print_error(f\"Failed to show infrastructure: {e}\")", + "docstring": "Show infrastructure configuration and status.", + "dependencies": [ + "print_error", + "print", + "command", + "option", + "LaddrConfig" + ], + "complexity": 9, + "business_context": "database, config, llm, queue, cache", + "imports": [ + "laddr.core" + ], + "called_functions": [ + "print_error", + "print", + "command", + "option", + "LaddrConfig" + ], + "parent_class": "", + "decorators": [ + "command", + "option" + ], + "parameters": [ + { + "name": "show_config", + "type": "bool" + } + ], + "return_type": "None" + }, + { + "type": "function", + "name": "check", + "filepath": "lib/laddr/src/laddr/cli/commands/check.py", + "start_line": 27, + "end_line": 76, + "code": "def check(output_path: str):\n \"\"\"Run diagnostic checks for the Laddr runtime using the DiagnosticAgent.\"\"\"\n if not validate_project_directory(Path.cwd()):\n raise ProjectNotFoundError()\n\n try:\n mod = importlib.import_module(\"agents.diagnostic.handler\")\n except Exception:\n print_error(\n \"Diagnostic agent not found. Run: laddr add agent diagnostic\",\n hint=\"This creates agents/diagnostic/handler.py\"\n )\n raise click.Abort()\n\n AgentClass = getattr(mod, \"DiagnosticAgent\", None)\n if AgentClass is None:\n print_error(\"Invalid diagnostic agent module\")\n raise click.Abort()\n\n import asyncio\n\n from laddr.core import AgentRunner, LaddrConfig # type: ignore\n\n print_info(\"Running diagnostic checks...\")\n runner = AgentRunner(env_config=LaddrConfig())\n report = asyncio.run(runner.run({\"payload\": {}}, agent_name=\"diagnostic\"))\n\n results = report.get(\"results\", [])\n ok_count = 0\n for r in results:\n status = r.get(\"status\")\n subsystem = r.get(\"subsystem\")\n message = r.get(\"message\")\n prefix = \"[green]✓[/green]\" if status == \"ok\" else \"[red]✗[/red]\"\n console.print(f\"{prefix} {subsystem}: {message}\")\n if status == \"ok\":\n ok_count += 1\n\n overall_ok = ok_count == len(results)\n if overall_ok:\n console.print(\"\\n[bold green]✅ Diagnostic complete: all subsystems operational[/bold green]\")\n else:\n console.print(\"\\n[bold red]❌ Diagnostic complete: issues detected[/bold red]\")\n\n try:\n with open(output_path, \"w\", encoding=\"utf-8\") as f:\n json.dump(report, f, indent=2, ensure_ascii=False)\n print_success(f\"Report saved to {output_path}\")\n except Exception as e:\n print_error(f\"Failed to write report: {e}\")", + "docstring": "Run diagnostic checks for the Laddr runtime using the DiagnosticAgent.", + "dependencies": [ + "print_info", + "print", + "Abort", + "print_success", + "option", + "LaddrConfig", + "import_module", + "cwd", + "print_error", + "ProjectNotFoundError", + "get", + "getattr", + "dump", + "len", + "AgentRunner", + "run", + "command", + "open", + "validate_project_directory" + ], + "complexity": 9, + "business_context": "message, agent, runtime", + "imports": [ + "click", + "importlib", + "json", + "asyncio", + "laddr.core" + ], + "called_functions": [ + "print_info", + "print", + "Abort", + "print_success", + "option", + "LaddrConfig", + "import_module", + "cwd", + "print_error", + "ProjectNotFoundError", + "get", + "getattr", + "dump", + "len", + "AgentRunner", + "run", + "command", + "open", + "validate_project_directory" + ], + "parent_class": "", + "decorators": [ + "command", + "option" + ], + "parameters": [ + { + "name": "output_path", + "type": "str" + } + ], + "return_type": "None" + }, + { + "type": "function", + "name": "init", + "filepath": "lib/laddr/src/laddr/cli/commands/init.py", + "start_line": 32, + "end_line": 125, + "code": "def init(project_name: str | None, path: str):\n \"\"\"Initialize a new Laddr project.\n \n Creates project structure with:\n - Configuration files (laddr.yml, .env)\n - Docker setup (docker-compose.yml, Dockerfile)\n - Default researcher agent\n - Example tools and pipeline\n \n Users import from laddr package:\n from laddr import Agent, AgentRunner, run_agent, actor, tool\n \n Examples:\n laddr init myproject\n laddr init myproject --path /path/to/dir\n laddr init # Interactive mode\n \"\"\"\n # Prompt for project name if not provided\n if not project_name:\n from ..utils import console\n console.print()\n # Use Rich console input to render markup (colors) correctly instead of\n # passing markup through Click which prints raw markup tags.\n # Console.input supports markup=True so the prompt will be colored.\n project_name = console.input(\"[cyan]Project name: [/cyan]\", markup=True).strip()\n\n # Resolve project path\n project_path = Path(path)\n if project_name and project_name != \".\":\n project_path = project_path / project_name\n project_path = project_path.resolve()\n\n # Check if project already exists\n if project_path.exists() and validate_project_directory(project_path):\n raise ProjectExistsError(str(project_path))\n\n # Check for nested project\n for parent in project_path.parents:\n if validate_project_directory(parent):\n raise NestedProjectError(str(parent))\n\n # Handle in-place creation\n create_in_place = False\n if project_name in (\".\", \"\"):\n create_in_place = True\n else:\n cwd_name = Path.cwd().name\n if path in (\".\", \"\") and project_name == cwd_name:\n create_in_place = True\n\n if create_in_place:\n project_path = Path.cwd().resolve()\n\n # Check if directory is empty (except for existing laddr.yml)\n if project_path.exists() and any(project_path.iterdir()):\n if not validate_project_directory(project_path):\n raise ProjectExistsError(str(project_path))\n\n # Print header\n print_header(f\"Initializing Laddr Project → {project_path.name}\")\n\n # Create directory structure\n print_step(\"Creating project structure\")\n _create_directories(project_path)\n\n # Generate core modules (no-op in V3.0)\n _generate_core_modules(project_path)\n\n # Generate configuration files\n print_step(\"Creating configuration\", \"laddr.yml, .env\")\n _generate_config_files(project_path, project_path.name)\n\n # Generate Docker setup\n print_step(\"Setting up Docker\", \"compose, Dockerfile, requirements\")\n _generate_docker_setup(project_path)\n\n # Generate default agent\n print_step(\"Creating agents\", \"coordinator, researcher\")\n _generate_default_agent(project_path)\n\n # Generate README\n print_step(\"Generating documentation\", \"README.md\")\n _generate_readme(project_path, project_path.name)\n\n # Success message\n print_completion(f\"Project created at {project_path}\")\n\n # Next steps\n from ..utils import console\n \n console.print(\"[dim]Get started:[/dim]\")\n if project_path != Path.cwd():\n console.print(f\" [cyan]cd {project_path}[/cyan]\")\n console.print(f\" [cyan]laddr run dev[/cyan]\\n\")", + "docstring": "Initialize a new Laddr project.\n\nCreates project structure with:\n- Configuration files (laddr.yml, .env)\n- Docker setup (docker-compose.yml, Dockerfile)\n- Default researcher agent\n- Example tools and pipeline\n\nUsers import from laddr package:\n from laddr import Agent, AgentRunner, run_agent, actor, tool\n\nExamples:\n laddr init myproject\n laddr init myproject --path /path/to/dir\n laddr init # Interactive mode", + "dependencies": [ + "print", + "iterdir", + "option", + "_generate_default_agent", + "strip", + "exists", + "input", + "print_step", + "_create_directories", + "cwd", + "NestedProjectError", + "resolve", + "Path", + "_generate_readme", + "argument", + "print_completion", + "any", + "_generate_config_files", + "_generate_docker_setup", + "command", + "_generate_core_modules", + "ProjectExistsError", + "str", + "print_header", + "validate_project_directory" + ], + "complexity": 16, + "business_context": "setup, init, agent, create, tool", + "imports": [ + "utils" + ], + "called_functions": [ + "print", + "iterdir", + "option", + "_generate_default_agent", + "strip", + "exists", + "input", + "print_step", + "_create_directories", + "cwd", + "NestedProjectError", + "resolve", + "Path", + "_generate_readme", + "argument", + "print_completion", + "any", + "_generate_config_files", + "_generate_docker_setup", + "command", + "_generate_core_modules", + "ProjectExistsError", + "str", + "print_header", + "validate_project_directory" + ], + "parent_class": "", + "decorators": [ + "command", + "argument", + "option" + ], + "parameters": [ + { + "name": "project_name", + "type": "str | None" + }, + { + "name": "path", + "type": "str" + } + ], + "return_type": "None" + }, + { + "type": "function", + "name": "_create_directories", + "filepath": "lib/laddr/src/laddr/cli/commands/init.py", + "start_line": 128, + "end_line": 137, + "code": "def _create_directories(project_path: Path) -> None:\n \"\"\"Create project directory structure.\"\"\"\n directories = [\n project_path,\n project_path / \"agents\",\n project_path / \"tools\",\n ]\n\n for directory in directories:\n directory.mkdir(parents=True, exist_ok=True)", + "docstring": "Create project directory structure.", + "dependencies": [ + "mkdir" + ], + "complexity": 2, + "business_context": "create", + "imports": [], + "called_functions": [ + "mkdir" + ], + "parent_class": "", + "decorators": [], + "parameters": [ + { + "name": "project_path", + "type": "Path" + } + ], + "return_type": "None" + }, + { + "type": "function", + "name": "_generate_core_modules", + "filepath": "lib/laddr/src/laddr/cli/commands/init.py", + "start_line": 140, + "end_line": 146, + "code": "def _generate_core_modules(project_path: Path) -> None:\n \"\"\"\n V3.0: Users import from laddr package, no need to generate core files.\n \n This function is kept for backward compatibility but does nothing.\n Users should use: from laddr import Agent, AgentRunner, run_agent\n \"\"\"", + "docstring": "V3.0: Users import from laddr package, no need to generate core files.\n\nThis function is kept for backward compatibility but does nothing.\nUsers should use: from laddr import Agent, AgentRunner, run_agent", + "dependencies": [], + "complexity": 1, + "business_context": "generate, agent", + "imports": [], + "called_functions": [], + "parent_class": "", + "decorators": [], + "parameters": [ + { + "name": "project_path", + "type": "Path" + } + ], + "return_type": "None" + }, + { + "type": "function", + "name": "_generate_config_files", + "filepath": "lib/laddr/src/laddr/cli/commands/init.py", + "start_line": 149, + "end_line": 168, + "code": "def _generate_config_files(project_path: Path, project_name: str) -> None:\n \"\"\"Generate configuration files.\"\"\"\n renderer = get_template_renderer()\n\n # laddr.yml\n config = ProjectConfigSchema(\n project=ProjectDetails(\n name=project_name,\n broker=\"redis\",\n database=\"postgres\",\n storage=\"minio\",\n tracing=True,\n metrics=True,\n agents=[],\n )\n )\n config.save_to_file(project_path / \"laddr.yml\")\n\n # .env\n renderer.render_to_file(\"dotenv.j2\", project_path / \".env\", {})", + "docstring": "Generate configuration files.", + "dependencies": [ + "render_to_file", + "ProjectConfigSchema", + "ProjectDetails", + "get_template_renderer", + "save_to_file" + ], + "complexity": 1, + "business_context": "database, generate, storage, config", + "imports": [], + "called_functions": [ + "render_to_file", + "ProjectConfigSchema", + "ProjectDetails", + "get_template_renderer", + "save_to_file" + ], + "parent_class": "", + "decorators": [], + "parameters": [ + { + "name": "project_path", + "type": "Path" + }, + { + "name": "project_name", + "type": "str" + } + ], + "return_type": "None" + }, + { + "type": "function", + "name": "_generate_docker_setup", + "filepath": "lib/laddr/src/laddr/cli/commands/init.py", + "start_line": 171, + "end_line": 177, + "code": "def _generate_docker_setup(project_path: Path) -> None:\n \"\"\"Generate Docker configuration.\"\"\"\n renderer = get_template_renderer()\n\n renderer.render_to_file(\"docker-compose.yml.j2\", project_path / \"docker-compose.yml\", {})\n renderer.render_to_file(\"Dockerfile.j2\", project_path / \"Dockerfile\", {})\n renderer.render_to_file(\"requirements.txt.j2\", project_path / \"requirements.txt\", {})", + "docstring": "Generate Docker configuration.", + "dependencies": [ + "render_to_file", + "get_template_renderer" + ], + "complexity": 1, + "business_context": "generate", + "imports": [], + "called_functions": [ + "render_to_file", + "get_template_renderer" + ], + "parent_class": "", + "decorators": [], + "parameters": [ + { + "name": "project_path", + "type": "Path" + } + ], + "return_type": "None" + }, + { + "type": "function", + "name": "_generate_default_agent", + "filepath": "lib/laddr/src/laddr/cli/commands/init.py", + "start_line": 180, + "end_line": 259, + "code": "def _generate_default_agent(project_path: Path) -> None:\n \"\"\"Generate default coordinator and researcher agents.\"\"\"\n renderer = get_template_renderer()\n\n # Create agents directory __init__\n write_file(project_path / \"agents\" / \"__init__.py\", \"\")\n\n # Define both default agents (flat files in agents/)\n default_agents = [\n {\n \"name\": \"coordinator\",\n \"role\": \"Orchestration Coordinator & Task Manager\",\n \"goal\": \"Analyze complex user requests, break them into specific subtasks, delegate each subtask to the appropriate specialist agent, and synthesize their responses into a comprehensive final answer\",\n \"backstory\": \"An experienced orchestrator who excels at task decomposition and delegation. You never perform research yourself - instead, you coordinate a team of specialists. You understand that effective delegation requires clear, specific instructions and that your role is to combine specialist outputs into coherent, complete responses for users.\",\n \"is_coordinator\": True,\n \"available_agents\": [\"researcher\"],\n },\n {\n \"name\": \"researcher\",\n \"role\": \"Web Research Specialist & Information Analyst\",\n \"goal\": \"Conduct thorough web research using available tools (web_search, scrape_url, extract_links) to find accurate, relevant information and deliver comprehensive, well-sourced answers\",\n \"backstory\": \"A meticulous research specialist with expertise in web searching, content extraction, and information synthesis. You have direct access to powerful web tools and know how to use them effectively. Your strength is finding high-quality sources, extracting key information, and presenting findings clearly with proper citations. You prioritize accuracy and always verify information across multiple sources when possible.\",\n \"is_coordinator\": False,\n \"available_agents\": [],\n }\n ]\n\n agent_names = []\n\n for agent_config in default_agents:\n agent_name = agent_config[\"name\"]\n agent_names.append(agent_name)\n\n # agents/__init__.py once\n write_file(project_path / \"agents\" / \"__init__.py\", \"\")\n\n # Render flat agent file\n agent_context = {\n \"name\": agent_name,\n \"role\": agent_config[\"role\"],\n \"goal\": agent_config[\"goal\"],\n \"backstory\": agent_config[\"backstory\"],\n \"is_coordinator\": agent_config.get(\"is_coordinator\", False),\n \"available_agents\": agent_config.get(\"available_agents\", []),\n \"model_env\": f\"{agent_name.upper()}_MODEL\",\n \"model\": \"gemini-2.5-flash\",\n \"fallback_model_env\": f\"{agent_name.upper()}_FALLBACK_MODEL\",\n \"fallback_model\": \"gpt-3.5-turbo\",\n \"temperature\": 0.3 if agent_config.get(\"is_coordinator\") else 0.2,\n \"max_retries\": 3,\n \"max_iterations\": 5 if agent_config.get(\"is_coordinator\") else 15,\n \"timeout\": 600,\n \"instructions\": (\n \"When you receive a task: 1) Analyze 2) Plan 3) Execute using tools 4) Synthesize results.\"\n if agent_config.get(\"is_coordinator\")\n else \"Be concise and cite sources when researching.\"\n ),\n }\n renderer.render_to_file(\"agent_flat.py.j2\", project_path / \"agents\" / f\"{agent_name}.py\", agent_context)\n\n # Add worker to docker-compose\n add_worker_to_compose(project_path, agent_name)\n\n # Tools package and defaults\n write_file(project_path / \"tools\" / \"__init__.py\", \"\")\n renderer.render_to_file(\"tools_web.py.j2\", project_path / \"tools\" / \"web_tools.py\", {})\n renderer.render_to_file(\"tools_communication.py.j2\", project_path / \"tools\" / \"communication_tools.py\", {})\n\n # Main entry - simple runner selecting agent by env\n renderer.render_to_file(\"main_flat.py.j2\", project_path / \"main.py\", {})\n\n # Update laddr.yml to add both agents\n config_path = project_path / \"laddr.yml\"\n with open(config_path, \"r\", encoding=\"utf-8\") as f:\n config_data = yaml.safe_load(f)\n\n config_data[\"project\"][\"agents\"] = agent_names\n\n with open(config_path, \"w\", encoding=\"utf-8\") as f:\n yaml.dump(config_data, f, default_flow_style=False, sort_keys=False)", + "docstring": "Generate default coordinator and researcher agents.", + "dependencies": [ + "upper", + "append", + "get", + "add_worker_to_compose", + "render_to_file", + "dump", + "get_template_renderer", + "safe_load", + "write_file", + "open" + ], + "complexity": 4, + "business_context": "update, generate, create, agent", + "imports": [ + "yaml" + ], + "called_functions": [ + "upper", + "append", + "get", + "add_worker_to_compose", + "render_to_file", + "dump", + "get_template_renderer", + "safe_load", + "write_file", + "open" + ], + "parent_class": "", + "decorators": [], + "parameters": [ + { + "name": "project_path", + "type": "Path" + } + ], + "return_type": "None" + }, + { + "type": "function", + "name": "_generate_readme", + "filepath": "lib/laddr/src/laddr/cli/commands/init.py", + "start_line": 262, + "end_line": 269, + "code": "def _generate_readme(project_path: Path, project_name: str) -> None:\n \"\"\"Generate project README.\"\"\"\n renderer = get_template_renderer()\n renderer.render_to_file(\n \"README.md.j2\",\n project_path / \"README.md\",\n {\"project_name\": project_name},\n )", + "docstring": "Generate project README.", + "dependencies": [ + "render_to_file", + "get_template_renderer" + ], + "complexity": 1, + "business_context": "generate", + "imports": [], + "called_functions": [ + "render_to_file", + "get_template_renderer" + ], + "parent_class": "", + "decorators": [], + "parameters": [ + { + "name": "project_path", + "type": "Path" + }, + { + "name": "project_name", + "type": "str" + } + ], + "return_type": "None" + }, + { + "type": "class", + "name": "ProjectConfigSchema", + "filepath": "lib/laddr/src/laddr/cli/utils/config.py", + "start_line": 16, + "end_line": 63, + "code": "class ProjectConfigSchema(BaseModel):\n \"\"\"Project configuration schema (laddr.yml).\"\"\"\n\n project: ProjectDetails = Field(..., description=\"Project details\")\n\n @classmethod", + "docstring": "Project configuration schema (laddr.yml).", + "dependencies": [ + "BaseModel" + ], + "complexity": 4, + "business_context": "", + "imports": [], + "called_functions": [], + "parent_class": "", + "decorators": [], + "parameters": [], + "return_type": "" + }, + { + "type": "method", + "name": "ProjectConfigSchema.load_from_file", + "filepath": "lib/laddr/src/laddr/cli/utils/config.py", + "start_line": 22, + "end_line": 43, + "code": " def load_from_file(cls, path: Path) -> ProjectConfigSchema:\n \"\"\"Load project config from YAML file.\n\n Args:\n path: Path to laddr.yml\n\n Returns:\n Parsed project configuration\n\n Raises:\n InvalidConfigError: If config is invalid\n \"\"\"\n try:\n with open(path, \"r\", encoding=\"utf-8\") as f:\n data = yaml.safe_load(f)\n return cls(**data)\n except FileNotFoundError:\n raise InvalidConfigError(str(path), \"File not found\")\n except yaml.YAMLError as e:\n raise InvalidConfigError(str(path), f\"Invalid YAML: {e}\")\n except Exception as e:\n raise InvalidConfigError(str(path), str(e))", + "docstring": "Load project config from YAML file.\n\nArgs:\n path: Path to laddr.yml\n\nReturns:\n Parsed project configuration\n\nRaises:\n InvalidConfigError: If config is invalid", + "dependencies": [ + "InvalidConfigError", + "str", + "cls", + "safe_load", + "open" + ], + "complexity": 5, + "business_context": "load, config", + "imports": [ + "yaml" + ], + "called_functions": [ + "InvalidConfigError", + "str", + "cls", + "safe_load", + "open" + ], + "parent_class": "ProjectConfigSchema", + "decorators": [ + "classmethod" + ], + "parameters": [ + { + "name": "cls", + "type": "Any" + }, + { + "name": "path", + "type": "Path" + } + ], + "return_type": "ProjectConfigSchema" + }, + { + "type": "method", + "name": "ProjectConfigSchema.save_to_file", + "filepath": "lib/laddr/src/laddr/cli/utils/config.py", + "start_line": 45, + "end_line": 63, + "code": " def save_to_file(self, path: Path) -> None:\n \"\"\"Save project config to YAML file.\n\n Args:\n path: Path to laddr.yml\n\n Raises:\n InvalidConfigError: If save fails\n \"\"\"\n try:\n with open(path, \"w\", encoding=\"utf-8\") as f:\n yaml.dump(\n self.model_dump(exclude_none=True),\n f,\n default_flow_style=False,\n sort_keys=False,\n )\n except Exception as e:\n raise InvalidConfigError(str(path), f\"Failed to save: {e}\")", + "docstring": "Save project config to YAML file.\n\nArgs:\n path: Path to laddr.yml\n\nRaises:\n InvalidConfigError: If save fails", + "dependencies": [ + "InvalidConfigError", + "dump", + "str", + "open", + "model_dump" + ], + "complexity": 3, + "business_context": "save, config", + "imports": [ + "yaml" + ], + "called_functions": [ + "InvalidConfigError", + "dump", + "str", + "open", + "model_dump" + ], + "parent_class": "ProjectConfigSchema", + "decorators": [], + "parameters": [ + { + "name": "self", + "type": "Any" + }, + { + "name": "path", + "type": "Path" + } + ], + "return_type": "None" + }, + { + "type": "class", + "name": "ProjectDetails", + "filepath": "lib/laddr/src/laddr/cli/utils/config.py", + "start_line": 66, + "end_line": 83, + "code": "class ProjectDetails(BaseModel):\n \"\"\"Project details section.\"\"\"\n\n name: str = Field(..., description=\"Project name\")\n broker: str = Field(default=\"redis\", description=\"Message broker type\")\n database: str = Field(default=\"postgres\", description=\"Database type\")\n storage: str = Field(default=\"minio\", description=\"Storage backend\")\n tracing: bool = Field(default=True, description=\"Enable tracing\")\n metrics: bool = Field(default=True, description=\"Enable metrics\")\n agents: list[str] = Field(default_factory=list, description=\"List of agent names\")\n\n @field_validator(\"name\")\n @classmethod", + "docstring": "Project details section.", + "dependencies": [ + "BaseModel" + ], + "complexity": 9, + "business_context": "database, message, storage, agent", + "imports": [], + "called_functions": [], + "parent_class": "", + "decorators": [], + "parameters": [], + "return_type": "" + }, + { + "type": "method", + "name": "ProjectDetails.validate_name", + "filepath": "lib/laddr/src/laddr/cli/utils/config.py", + "start_line": 79, + "end_line": 83, + "code": " def validate_name(cls, v: str) -> str:\n \"\"\"Validate project name format.\"\"\"\n if not v.replace(\"_\", \"\").replace(\"-\", \"\").isalnum():\n raise ValueError(\"Project name must be alphanumeric (with underscores/hyphens)\")\n return v", + "docstring": "Validate project name format.", + "dependencies": [ + "ValueError", + "field_validator", + "isalnum", + "replace" + ], + "complexity": 2, + "business_context": "validate", + "imports": [], + "called_functions": [ + "ValueError", + "field_validator", + "isalnum", + "replace" + ], + "parent_class": "ProjectDetails", + "decorators": [ + "field_validator", + "classmethod" + ], + "parameters": [ + { + "name": "cls", + "type": "Any" + }, + { + "name": "v", + "type": "str" + } + ], + "return_type": "str" + }, + { + "type": "function", + "name": "validate_project_directory", + "filepath": "lib/laddr/src/laddr/cli/utils/config.py", + "start_line": 86, + "end_line": 95, + "code": "def validate_project_directory(path: Path) -> bool:\n \"\"\"Check if a directory contains a valid Laddr project.\n\n Args:\n path: Directory path to check\n\n Returns:\n True if valid project directory\n \"\"\"\n return (path / \"laddr.yml\").exists()", + "docstring": "Check if a directory contains a valid Laddr project.\n\nArgs:\n path: Directory path to check\n\nReturns:\n True if valid project directory", + "dependencies": [ + "exists" + ], + "complexity": 1, + "business_context": "", + "imports": [], + "called_functions": [ + "exists" + ], + "parent_class": "", + "decorators": [], + "parameters": [ + { + "name": "path", + "type": "Path" + } + ], + "return_type": "bool" + }, + { + "type": "function", + "name": "get_logger", + "filepath": "lib/laddr/src/laddr/cli/utils/logger.py", + "start_line": 32, + "end_line": 59, + "code": "def get_logger(name: str, level: int = logging.INFO) -> logging.Logger:\n \"\"\"Get a configured logger with rich handler.\n\n Args:\n name: Logger name (usually __name__)\n level: Log level (default: INFO)\n\n Returns:\n Configured logger instance\n \"\"\"\n logger = logging.getLogger(name)\n logger.setLevel(level)\n\n # Remove existing handlers to avoid duplicates\n logger.handlers.clear()\n\n # Add rich handler\n handler = RichHandler(\n console=console,\n show_time=False,\n show_path=False,\n markup=True,\n rich_tracebacks=True,\n )\n handler.setFormatter(logging.Formatter(\"%(message)s\"))\n logger.addHandler(handler)\n\n return logger", + "docstring": "Get a configured logger with rich handler.\n\nArgs:\n name: Logger name (usually __name__)\n level: Log level (default: INFO)\n\nReturns:\n Configured logger instance", + "dependencies": [ + "RichHandler", + "clear", + "addHandler", + "Formatter", + "getLogger", + "setFormatter", + "setLevel" + ], + "complexity": 1, + "business_context": "message", + "imports": [ + "logging" + ], + "called_functions": [ + "RichHandler", + "clear", + "addHandler", + "Formatter", + "getLogger", + "setFormatter", + "setLevel" + ], + "parent_class": "", + "decorators": [], + "parameters": [ + { + "name": "name", + "type": "str" + }, + { + "name": "level", + "type": "int" + } + ], + "return_type": "logging.Logger" + }, + { + "type": "function", + "name": "print_success", + "filepath": "lib/laddr/src/laddr/cli/utils/logger.py", + "start_line": 62, + "end_line": 71, + "code": "def print_success(message: str, details: str | None = None) -> None:\n \"\"\"Print a success message.\n\n Args:\n message: Main success message\n details: Optional additional details\n \"\"\"\n console.print(f\"[success]✓[/success] {message}\")\n if details:\n console.print(f\" {details}\", style=\"dim\")", + "docstring": "Print a success message.\n\nArgs:\n message: Main success message\n details: Optional additional details", + "dependencies": [ + "print" + ], + "complexity": 2, + "business_context": "message", + "imports": [], + "called_functions": [ + "print" + ], + "parent_class": "", + "decorators": [], + "parameters": [ + { + "name": "message", + "type": "str" + }, + { + "name": "details", + "type": "str | None" + } + ], + "return_type": "None" + }, + { + "type": "function", + "name": "print_error", + "filepath": "lib/laddr/src/laddr/cli/utils/logger.py", + "start_line": 74, + "end_line": 83, + "code": "def print_error(message: str, hint: str | None = None) -> None:\n \"\"\"Print an error message.\n\n Args:\n message: Error message\n hint: Optional hint for resolution\n \"\"\"\n console.print(f\"[error]✗[/error] {message}\")\n if hint:\n console.print(f\" [dim]Hint:[/dim] {hint}\", style=\"yellow\")", + "docstring": "Print an error message.\n\nArgs:\n message: Error message\n hint: Optional hint for resolution", + "dependencies": [ + "print" + ], + "complexity": 2, + "business_context": "message", + "imports": [], + "called_functions": [ + "print" + ], + "parent_class": "", + "decorators": [], + "parameters": [ + { + "name": "message", + "type": "str" + }, + { + "name": "hint", + "type": "str | None" + } + ], + "return_type": "None" + }, + { + "type": "function", + "name": "print_warning", + "filepath": "lib/laddr/src/laddr/cli/utils/logger.py", + "start_line": 86, + "end_line": 92, + "code": "def print_warning(message: str) -> None:\n \"\"\"Print a warning message.\n\n Args:\n message: Warning message\n \"\"\"\n console.print(f\"[warning]![/warning] {message}\")", + "docstring": "Print a warning message.\n\nArgs:\n message: Warning message", + "dependencies": [ + "print" + ], + "complexity": 1, + "business_context": "message", + "imports": [], + "called_functions": [ + "print" + ], + "parent_class": "", + "decorators": [], + "parameters": [ + { + "name": "message", + "type": "str" + } + ], + "return_type": "None" + }, + { + "type": "function", + "name": "print_info", + "filepath": "lib/laddr/src/laddr/cli/utils/logger.py", + "start_line": 95, + "end_line": 101, + "code": "def print_info(message: str) -> None:\n \"\"\"Print an info message.\n\n Args:\n message: Info message\n \"\"\"\n console.print(f\"[info]ℹ[/info] {message}\")", + "docstring": "Print an info message.\n\nArgs:\n message: Info message", + "dependencies": [ + "print" + ], + "complexity": 1, + "business_context": "message", + "imports": [], + "called_functions": [ + "print" + ], + "parent_class": "", + "decorators": [], + "parameters": [ + { + "name": "message", + "type": "str" + } + ], + "return_type": "None" + }, + { + "type": "function", + "name": "print_panel", + "filepath": "lib/laddr/src/laddr/cli/utils/logger.py", + "start_line": 104, + "end_line": 112, + "code": "def print_panel(title: str, content: str, style: str = \"cyan\") -> None:\n \"\"\"Print a styled panel.\n\n Args:\n title: Panel title\n content: Panel content\n style: Panel border style (default: cyan)\n \"\"\"\n console.print(Panel(content, title=f\"[bold]{title}[/bold]\", border_style=style))", + "docstring": "Print a styled panel.\n\nArgs:\n title: Panel title\n content: Panel content\n style: Panel border style (default: cyan)", + "dependencies": [ + "print", + "Panel" + ], + "complexity": 1, + "business_context": "", + "imports": [], + "called_functions": [ + "print", + "Panel" + ], + "parent_class": "", + "decorators": [], + "parameters": [ + { + "name": "title", + "type": "str" + }, + { + "name": "content", + "type": "str" + }, + { + "name": "style", + "type": "str" + } + ], + "return_type": "None" + }, + { + "type": "function", + "name": "print_header", + "filepath": "lib/laddr/src/laddr/cli/utils/logger.py", + "start_line": 115, + "end_line": 122, + "code": "def print_header(text: str, style: str = \"bold cyan\") -> None:\n \"\"\"Print a minimalistic header.\n\n Args:\n text: Header text\n style: Text style (default: bold cyan)\n \"\"\"\n console.print(f\"\\n[{style}]{text}[/{style}]\")", + "docstring": "Print a minimalistic header.\n\nArgs:\n text: Header text\n style: Text style (default: bold cyan)", + "dependencies": [ + "print" + ], + "complexity": 1, + "business_context": "", + "imports": [], + "called_functions": [ + "print" + ], + "parent_class": "", + "decorators": [], + "parameters": [ + { + "name": "text", + "type": "str" + }, + { + "name": "style", + "type": "str" + } + ], + "return_type": "None" + }, + { + "type": "function", + "name": "print_step", + "filepath": "lib/laddr/src/laddr/cli/utils/logger.py", + "start_line": 125, + "end_line": 135, + "code": "def print_step(step: str, description: str = \"\") -> None:\n \"\"\"Print a step in a process with minimalistic style.\n\n Args:\n step: Step name/action\n description: Optional description\n \"\"\"\n if description:\n console.print(f\" [dim]→[/dim] {step} [dim]{description}[/dim]\")\n else:\n console.print(f\" [dim]→[/dim] {step}\")", + "docstring": "Print a step in a process with minimalistic style.\n\nArgs:\n step: Step name/action\n description: Optional description", + "dependencies": [ + "print" + ], + "complexity": 2, + "business_context": "process", + "imports": [], + "called_functions": [ + "print" + ], + "parent_class": "", + "decorators": [], + "parameters": [ + { + "name": "step", + "type": "str" + }, + { + "name": "description", + "type": "str" + } + ], + "return_type": "None" + }, + { + "type": "function", + "name": "print_completion", + "filepath": "lib/laddr/src/laddr/cli/utils/logger.py", + "start_line": 138, + "end_line": 144, + "code": "def print_completion(message: str) -> None:\n \"\"\"Print a completion message with checkmark.\n\n Args:\n message: Completion message\n \"\"\"\n console.print(f\"\\n[success]✓[/success] {message}\\n\")", + "docstring": "Print a completion message with checkmark.\n\nArgs:\n message: Completion message", + "dependencies": [ + "print" + ], + "complexity": 1, + "business_context": "message", + "imports": [], + "called_functions": [ + "print" + ], + "parent_class": "", + "decorators": [], + "parameters": [ + { + "name": "message", + "type": "str" + } + ], + "return_type": "None" + }, + { + "type": "function", + "name": "print_table", + "filepath": "lib/laddr/src/laddr/cli/utils/logger.py", + "start_line": 147, + "end_line": 170, + "code": "def print_table(data: list[dict], title: str | None = None) -> None:\n \"\"\"Print data as a rich table.\n\n Args:\n data: List of dictionaries (each dict is a row)\n title: Optional table title\n \"\"\"\n from rich.table import Table\n\n if not data:\n console.print(\"[dim]No data to display[/dim]\")\n return\n\n table = Table(title=title, show_header=True, header_style=\"bold cyan\")\n\n # Add columns from first row keys\n for key in data[0].keys():\n table.add_column(key, style=\"white\")\n\n # Add rows\n for row in data:\n table.add_row(*[str(v) for v in row.values()])\n\n console.print(table)", + "docstring": "Print data as a rich table.\n\nArgs:\n data: List of dictionaries (each dict is a row)\n title: Optional table title", + "dependencies": [ + "values", + "add_row", + "print", + "keys", + "add_column", + "Table", + "str" + ], + "complexity": 4, + "business_context": "", + "imports": [ + "rich.table" + ], + "called_functions": [ + "values", + "add_row", + "print", + "keys", + "add_column", + "Table", + "str" + ], + "parent_class": "", + "decorators": [], + "parameters": [ + { + "name": "data", + "type": "list[dict]" + }, + { + "name": "title", + "type": "str | None" + } + ], + "return_type": "None" + }, + { + "type": "class", + "name": "TemplateRenderer", + "filepath": "lib/laddr/src/laddr/cli/utils/templates.py", + "start_line": 17, + "end_line": 118, + "code": "class TemplateRenderer:\n \"\"\"Jinja2 template renderer for Laddr CLI.\"\"\"\n", + "docstring": "Jinja2 template renderer for Laddr CLI.", + "dependencies": [], + "complexity": 8, + "business_context": "", + "imports": [], + "called_functions": [], + "parent_class": "", + "decorators": [], + "parameters": [], + "return_type": "" + }, + { + "type": "method", + "name": "TemplateRenderer.__init__", + "filepath": "lib/laddr/src/laddr/cli/utils/templates.py", + "start_line": 20, + "end_line": 37, + "code": " def __init__(self, templates_dir: Path):\n \"\"\"Initialize template renderer.\n\n Args:\n templates_dir: Directory containing Jinja2 templates\n \"\"\"\n self.templates_dir = templates_dir\n self.env = Environment(\n loader=FileSystemLoader(str(templates_dir)),\n trim_blocks=True,\n lstrip_blocks=True,\n keep_trailing_newline=True,\n )\n\n # Add custom filters\n self.env.filters[\"snake_case\"] = self._snake_case\n self.env.filters[\"pascal_case\"] = self._pascal_case\n self.env.filters[\"kebab_case\"] = self._kebab_case", + "docstring": "Initialize template renderer.\n\nArgs:\n templates_dir: Directory containing Jinja2 templates", + "dependencies": [ + "str", + "Environment", + "FileSystemLoader" + ], + "complexity": 1, + "business_context": "initialize", + "imports": [], + "called_functions": [ + "str", + "Environment", + "FileSystemLoader" + ], + "parent_class": "TemplateRenderer", + "decorators": [], + "parameters": [ + { + "name": "self", + "type": "Any" + }, + { + "name": "templates_dir", + "type": "Path" + } + ], + "return_type": "None" + }, + { + "type": "method", + "name": "TemplateRenderer._snake_case", + "filepath": "lib/laddr/src/laddr/cli/utils/templates.py", + "start_line": 40, + "end_line": 42, + "code": " def _snake_case(value: str) -> str:\n \"\"\"Convert string to snake_case.\"\"\"\n return value.replace(\"-\", \"_\").replace(\" \", \"_\").lower()", + "docstring": "Convert string to snake_case.", + "dependencies": [ + "replace", + "lower" + ], + "complexity": 1, + "business_context": "", + "imports": [], + "called_functions": [ + "replace", + "lower" + ], + "parent_class": "TemplateRenderer", + "decorators": [ + "staticmethod" + ], + "parameters": [ + { + "name": "value", + "type": "str" + } + ], + "return_type": "str" + }, + { + "type": "method", + "name": "TemplateRenderer._pascal_case", + "filepath": "lib/laddr/src/laddr/cli/utils/templates.py", + "start_line": 45, + "end_line": 47, + "code": " def _pascal_case(value: str) -> str:\n \"\"\"Convert string to PascalCase.\"\"\"\n return \"\".join(word.capitalize() for word in value.replace(\"-\", \"_\").split(\"_\"))", + "docstring": "Convert string to PascalCase.", + "dependencies": [ + "split", + "capitalize", + "join", + "replace" + ], + "complexity": 1, + "business_context": "", + "imports": [], + "called_functions": [ + "split", + "capitalize", + "join", + "replace" + ], + "parent_class": "TemplateRenderer", + "decorators": [ + "staticmethod" + ], + "parameters": [ + { + "name": "value", + "type": "str" + } + ], + "return_type": "str" + }, + { + "type": "method", + "name": "TemplateRenderer._kebab_case", + "filepath": "lib/laddr/src/laddr/cli/utils/templates.py", + "start_line": 50, + "end_line": 52, + "code": " def _kebab_case(value: str) -> str:\n \"\"\"Convert string to kebab-case.\"\"\"\n return value.replace(\"_\", \"-\").replace(\" \", \"-\").lower()", + "docstring": "Convert string to kebab-case.", + "dependencies": [ + "replace", + "lower" + ], + "complexity": 1, + "business_context": "", + "imports": [], + "called_functions": [ + "replace", + "lower" + ], + "parent_class": "TemplateRenderer", + "decorators": [ + "staticmethod" + ], + "parameters": [ + { + "name": "value", + "type": "str" + } + ], + "return_type": "str" + }, + { + "type": "method", + "name": "TemplateRenderer.render_template", + "filepath": "lib/laddr/src/laddr/cli/utils/templates.py", + "start_line": 54, + "end_line": 71, + "code": " def render_template(self, template_name: str, context: dict[str, Any]) -> str:\n \"\"\"Render a template with context.\n\n Args:\n template_name: Template filename\n context: Template context variables\n\n Returns:\n Rendered template content\n\n Raises:\n FileGenerationError: If template rendering fails\n \"\"\"\n try:\n template = self.env.get_template(template_name)\n return template.render(**context)\n except Exception as e:\n raise FileGenerationError(template_name, str(e))", + "docstring": "Render a template with context.\n\nArgs:\n template_name: Template filename\n context: Template context variables\n\nReturns:\n Rendered template content\n\nRaises:\n FileGenerationError: If template rendering fails", + "dependencies": [ + "str", + "get_template", + "render", + "FileGenerationError" + ], + "complexity": 2, + "business_context": "", + "imports": [], + "called_functions": [ + "str", + "get_template", + "render", + "FileGenerationError" + ], + "parent_class": "TemplateRenderer", + "decorators": [], + "parameters": [ + { + "name": "self", + "type": "Any" + }, + { + "name": "template_name", + "type": "str" + }, + { + "name": "context", + "type": "dict[str, Any]" + } + ], + "return_type": "str" + }, + { + "type": "method", + "name": "TemplateRenderer.render_to_file", + "filepath": "lib/laddr/src/laddr/cli/utils/templates.py", + "start_line": 73, + "end_line": 99, + "code": " def render_to_file(\n self,\n template_name: str,\n output_path: Path,\n context: dict[str, Any],\n create_dirs: bool = True,\n ) -> None:\n \"\"\"Render a template and write to file.\n\n Args:\n template_name: Template filename\n output_path: Output file path\n context: Template context variables\n create_dirs: Create parent directories if needed (default: True)\n\n Raises:\n FileGenerationError: If rendering or writing fails\n \"\"\"\n content = self.render_template(template_name, context)\n\n try:\n if create_dirs:\n output_path.parent.mkdir(parents=True, exist_ok=True)\n\n output_path.write_text(content, encoding=\"utf-8\")\n except Exception as e:\n raise FileGenerationError(str(output_path), str(e))", + "docstring": "Render a template and write to file.\n\nArgs:\n template_name: Template filename\n output_path: Output file path\n context: Template context variables\n create_dirs: Create parent directories if needed (default: True)\n\nRaises:\n FileGenerationError: If rendering or writing fails", + "dependencies": [ + "mkdir", + "write_text", + "str", + "render_template", + "FileGenerationError" + ], + "complexity": 3, + "business_context": "create", + "imports": [], + "called_functions": [ + "mkdir", + "write_text", + "str", + "render_template", + "FileGenerationError" + ], + "parent_class": "TemplateRenderer", + "decorators": [], + "parameters": [ + { + "name": "self", + "type": "Any" + }, + { + "name": "template_name", + "type": "str" + }, + { + "name": "output_path", + "type": "Path" + }, + { + "name": "context", + "type": "dict[str, Any]" + }, + { + "name": "create_dirs", + "type": "bool" + } + ], + "return_type": "None" + }, + { + "type": "method", + "name": "TemplateRenderer.render_string", + "filepath": "lib/laddr/src/laddr/cli/utils/templates.py", + "start_line": 101, + "end_line": 118, + "code": " def render_string(self, template_str: str, context: dict[str, Any]) -> str:\n \"\"\"Render a template string with context.\n\n Args:\n template_str: Template string\n context: Template context variables\n\n Returns:\n Rendered content\n\n Raises:\n FileGenerationError: If rendering fails\n \"\"\"\n try:\n template = Template(template_str)\n return template.render(**context)\n except Exception as e:\n raise FileGenerationError(\"inline template\", str(e))", + "docstring": "Render a template string with context.\n\nArgs:\n template_str: Template string\n context: Template context variables\n\nReturns:\n Rendered content\n\nRaises:\n FileGenerationError: If rendering fails", + "dependencies": [ + "str", + "Template", + "render", + "FileGenerationError" + ], + "complexity": 2, + "business_context": "", + "imports": [], + "called_functions": [ + "str", + "Template", + "render", + "FileGenerationError" + ], + "parent_class": "TemplateRenderer", + "decorators": [], + "parameters": [ + { + "name": "self", + "type": "Any" + }, + { + "name": "template_str", + "type": "str" + }, + { + "name": "context", + "type": "dict[str, Any]" + } + ], + "return_type": "str" + }, + { + "type": "function", + "name": "get_template_renderer", + "filepath": "lib/laddr/src/laddr/cli/utils/templates.py", + "start_line": 121, + "end_line": 137, + "code": "def get_template_renderer() -> TemplateRenderer:\n \"\"\"Get a configured template renderer.\n\n Returns:\n TemplateRenderer instance with CLI templates directory\n \"\"\"\n # Find templates directory relative to this file\n cli_dir = Path(__file__).parent.parent\n templates_dir = cli_dir / \"templates\"\n\n if not templates_dir.exists():\n raise FileGenerationError(\n \"templates directory\",\n f\"Templates directory not found at {templates_dir}\",\n )\n\n return TemplateRenderer(templates_dir)", + "docstring": "Get a configured template renderer.\n\nReturns:\n TemplateRenderer instance with CLI templates directory", + "dependencies": [ + "exists", + "Path", + "TemplateRenderer", + "FileGenerationError" + ], + "complexity": 2, + "business_context": "", + "imports": [], + "called_functions": [ + "exists", + "Path", + "TemplateRenderer", + "FileGenerationError" + ], + "parent_class": "", + "decorators": [], + "parameters": [], + "return_type": "TemplateRenderer" + }, + { + "type": "function", + "name": "write_file", + "filepath": "lib/laddr/src/laddr/cli/utils/templates.py", + "start_line": 140, + "end_line": 157, + "code": "def write_file(path: Path, content: str, create_dirs: bool = True) -> None:\n \"\"\"Write content to a file.\n\n Args:\n path: File path\n content: File content\n create_dirs: Create parent directories if needed (default: True)\n\n Raises:\n FileGenerationError: If write fails\n \"\"\"\n try:\n if create_dirs:\n path.parent.mkdir(parents=True, exist_ok=True)\n\n path.write_text(content, encoding=\"utf-8\")\n except Exception as e:\n raise FileGenerationError(str(path), str(e))", + "docstring": "Write content to a file.\n\nArgs:\n path: File path\n content: File content\n create_dirs: Create parent directories if needed (default: True)\n\nRaises:\n FileGenerationError: If write fails", + "dependencies": [ + "write_text", + "FileGenerationError", + "str", + "mkdir" + ], + "complexity": 3, + "business_context": "create", + "imports": [], + "called_functions": [ + "write_text", + "FileGenerationError", + "str", + "mkdir" + ], + "parent_class": "", + "decorators": [], + "parameters": [ + { + "name": "path", + "type": "Path" + }, + { + "name": "content", + "type": "str" + }, + { + "name": "create_dirs", + "type": "bool" + } + ], + "return_type": "None" + }, + { + "type": "function", + "name": "check_docker", + "filepath": "lib/laddr/src/laddr/cli/utils/docker.py", + "start_line": 19, + "end_line": 40, + "code": "def check_docker() -> bool:\n \"\"\"Check if Docker is installed and running.\n\n Returns:\n True if Docker is available\n\n Raises:\n DockerNotFoundError: If Docker is not found or not running\n \"\"\"\n try:\n result = subprocess.run(\n [\"docker\", \"version\"],\n capture_output=True,\n text=True,\n check=False,\n timeout=5,\n )\n if result.returncode != 0:\n raise DockerNotFoundError()\n return True\n except (FileNotFoundError, subprocess.TimeoutExpired):\n raise DockerNotFoundError()", + "docstring": "Check if Docker is installed and running.\n\nReturns:\n True if Docker is available\n\nRaises:\n DockerNotFoundError: If Docker is not found or not running", + "dependencies": [ + "DockerNotFoundError", + "run" + ], + "complexity": 3, + "business_context": "", + "imports": [ + "time", + "subprocess" + ], + "called_functions": [ + "DockerNotFoundError", + "run" + ], + "parent_class": "", + "decorators": [], + "parameters": [], + "return_type": "bool" + }, + { + "type": "function", + "name": "check_docker_compose", + "filepath": "lib/laddr/src/laddr/cli/utils/docker.py", + "start_line": 43, + "end_line": 64, + "code": "def check_docker_compose() -> bool:\n \"\"\"Check if Docker Compose is available.\n\n Returns:\n True if Docker Compose is available\n\n Raises:\n DockerNotFoundError: If Docker Compose is not found\n \"\"\"\n try:\n result = subprocess.run(\n [\"docker\", \"compose\", \"version\"],\n capture_output=True,\n text=True,\n check=False,\n timeout=5,\n )\n if result.returncode != 0:\n raise DockerNotFoundError()\n return True\n except (FileNotFoundError, subprocess.TimeoutExpired):\n raise DockerNotFoundError()", + "docstring": "Check if Docker Compose is available.\n\nReturns:\n True if Docker Compose is available\n\nRaises:\n DockerNotFoundError: If Docker Compose is not found", + "dependencies": [ + "DockerNotFoundError", + "run" + ], + "complexity": 3, + "business_context": "", + "imports": [ + "time", + "subprocess" + ], + "called_functions": [ + "DockerNotFoundError", + "run" + ], + "parent_class": "", + "decorators": [], + "parameters": [], + "return_type": "bool" + }, + { + "type": "function", + "name": "compose_up", + "filepath": "lib/laddr/src/laddr/cli/utils/docker.py", + "start_line": 67, + "end_line": 112, + "code": "def compose_up(\n services: list[str] | None = None,\n detach: bool = True,\n build: bool = False,\n scale: dict[str, int] | None = None,\n) -> None:\n \"\"\"Start Docker Compose services.\n\n Args:\n services: Optional list of specific services to start\n detach: Run in detached mode (default: True)\n build: Force rebuild images (default: False)\n scale: Optional dict of service: replicas for scaling\n\n Raises:\n DockerComposeError: If compose up fails\n \"\"\"\n cmd = [\"docker\", \"compose\", \"up\"]\n\n if detach:\n cmd.append(\"-d\")\n\n if build:\n cmd.append(\"--build\")\n\n if scale:\n for service, replicas in scale.items():\n cmd.extend([\"--scale\", f\"{service}={replicas}\"])\n\n if services:\n cmd.extend(services)\n\n try:\n result = subprocess.run(\n cmd,\n capture_output=True,\n text=True,\n check=False,\n timeout=300, # 5 minutes timeout\n )\n if result.returncode != 0:\n raise DockerComposeError(\"up\", result.stderr or result.stdout)\n except subprocess.TimeoutExpired:\n raise DockerComposeError(\"up\", \"Operation timed out after 5 minutes\")\n except FileNotFoundError:\n raise DockerNotFoundError()", + "docstring": "Start Docker Compose services.\n\nArgs:\n services: Optional list of specific services to start\n detach: Run in detached mode (default: True)\n build: Force rebuild images (default: False)\n scale: Optional dict of service: replicas for scaling\n\nRaises:\n DockerComposeError: If compose up fails", + "dependencies": [ + "append", + "extend", + "items", + "DockerComposeError", + "DockerNotFoundError", + "run" + ], + "complexity": 10, + "business_context": "", + "imports": [ + "time", + "subprocess" + ], + "called_functions": [ + "append", + "extend", + "items", + "DockerComposeError", + "DockerNotFoundError", + "run" + ], + "parent_class": "", + "decorators": [], + "parameters": [ + { + "name": "services", + "type": "list[str] | None" + }, + { + "name": "detach", + "type": "bool" + }, + { + "name": "build", + "type": "bool" + }, + { + "name": "scale", + "type": "dict[str, int] | None" + } + ], + "return_type": "None" + }, + { + "type": "function", + "name": "compose_down", + "filepath": "lib/laddr/src/laddr/cli/utils/docker.py", + "start_line": 115, + "end_line": 142, + "code": "def compose_down(remove_volumes: bool = False) -> None:\n \"\"\"Stop Docker Compose services.\n\n Args:\n remove_volumes: Remove named volumes (default: False)\n\n Raises:\n DockerComposeError: If compose down fails\n \"\"\"\n cmd = [\"docker\", \"compose\", \"down\"]\n\n if remove_volumes:\n cmd.append(\"-v\")\n\n try:\n result = subprocess.run(\n cmd,\n capture_output=True,\n text=True,\n check=False,\n timeout=60,\n )\n if result.returncode != 0:\n raise DockerComposeError(\"down\", result.stderr or result.stdout)\n except subprocess.TimeoutExpired:\n raise DockerComposeError(\"down\", \"Operation timed out\")\n except FileNotFoundError:\n raise DockerNotFoundError()", + "docstring": "Stop Docker Compose services.\n\nArgs:\n remove_volumes: Remove named volumes (default: False)\n\nRaises:\n DockerComposeError: If compose down fails", + "dependencies": [ + "DockerNotFoundError", + "append", + "DockerComposeError", + "run" + ], + "complexity": 6, + "business_context": "", + "imports": [ + "time", + "subprocess" + ], + "called_functions": [ + "DockerNotFoundError", + "append", + "DockerComposeError", + "run" + ], + "parent_class": "", + "decorators": [], + "parameters": [ + { + "name": "remove_volumes", + "type": "bool" + } + ], + "return_type": "None" + }, + { + "type": "function", + "name": "compose_ps", + "filepath": "lib/laddr/src/laddr/cli/utils/docker.py", + "start_line": 145, + "end_line": 176, + "code": "def compose_ps(services: list[str] | None = None) -> str:\n \"\"\"Get status of Docker Compose services.\n\n Args:\n services: Optional list of specific services to check\n\n Returns:\n Status output as string\n\n Raises:\n DockerComposeError: If compose ps fails\n \"\"\"\n cmd = [\"docker\", \"compose\", \"ps\"]\n\n if services:\n cmd.extend(services)\n\n try:\n result = subprocess.run(\n cmd,\n capture_output=True,\n text=True,\n check=False,\n timeout=30,\n )\n if result.returncode != 0:\n raise DockerComposeError(\"ps\", result.stderr or result.stdout)\n return result.stdout\n except subprocess.TimeoutExpired:\n raise DockerComposeError(\"ps\", \"Operation timed out\")\n except FileNotFoundError:\n raise DockerNotFoundError()", + "docstring": "Get status of Docker Compose services.\n\nArgs:\n services: Optional list of specific services to check\n\nReturns:\n Status output as string\n\nRaises:\n DockerComposeError: If compose ps fails", + "dependencies": [ + "DockerNotFoundError", + "extend", + "DockerComposeError", + "run" + ], + "complexity": 6, + "business_context": "", + "imports": [ + "time", + "subprocess" + ], + "called_functions": [ + "DockerNotFoundError", + "extend", + "DockerComposeError", + "run" + ], + "parent_class": "", + "decorators": [], + "parameters": [ + { + "name": "services", + "type": "list[str] | None" + } + ], + "return_type": "str" + }, + { + "type": "function", + "name": "compose_logs", + "filepath": "lib/laddr/src/laddr/cli/utils/docker.py", + "start_line": 179, + "end_line": 210, + "code": "def compose_logs(\n service: str,\n follow: bool = False,\n tail: int | None = None,\n) -> None:\n \"\"\"View logs for a Docker Compose service.\n\n Args:\n service: Service name\n follow: Follow log output (default: False)\n tail: Number of lines to show from end (default: all)\n\n Raises:\n DockerComposeError: If compose logs fails\n \"\"\"\n cmd = [\"docker\", \"compose\", \"logs\"]\n\n if follow:\n cmd.append(\"-f\")\n\n if tail is not None:\n cmd.extend([\"--tail\", str(tail)])\n\n cmd.append(service)\n\n try:\n subprocess.run(cmd, check=False)\n except FileNotFoundError:\n raise DockerNotFoundError()\n except KeyboardInterrupt:\n # User stopped following logs\n pass", + "docstring": "View logs for a Docker Compose service.\n\nArgs:\n service: Service name\n follow: Follow log output (default: False)\n tail: Number of lines to show from end (default: all)\n\nRaises:\n DockerComposeError: If compose logs fails", + "dependencies": [ + "append", + "extend", + "str", + "DockerNotFoundError", + "run" + ], + "complexity": 5, + "business_context": "", + "imports": [ + "subprocess" + ], + "called_functions": [ + "append", + "extend", + "str", + "DockerNotFoundError", + "run" + ], + "parent_class": "", + "decorators": [], + "parameters": [ + { + "name": "service", + "type": "str" + }, + { + "name": "follow", + "type": "bool" + }, + { + "name": "tail", + "type": "int | None" + } + ], + "return_type": "None" + }, + { + "type": "function", + "name": "compose_scale", + "filepath": "lib/laddr/src/laddr/cli/utils/docker.py", + "start_line": 213, + "end_line": 239, + "code": "def compose_scale(service: str, replicas: int) -> None:\n \"\"\"Scale a Docker Compose service.\n\n Args:\n service: Service name\n replicas: Number of replicas\n\n Raises:\n DockerComposeError: If scaling fails\n \"\"\"\n if replicas < 0:\n raise ValueError(\"Replicas must be >= 0\")\n\n try:\n result = subprocess.run(\n [\"docker\", \"compose\", \"up\", \"--scale\", f\"{service}={replicas}\", \"-d\"],\n capture_output=True,\n text=True,\n check=False,\n timeout=120,\n )\n if result.returncode != 0:\n raise DockerComposeError(\"scale\", result.stderr or result.stdout)\n except subprocess.TimeoutExpired:\n raise DockerComposeError(\"scale\", \"Operation timed out\")\n except FileNotFoundError:\n raise DockerNotFoundError()", + "docstring": "Scale a Docker Compose service.\n\nArgs:\n service: Service name\n replicas: Number of replicas\n\nRaises:\n DockerComposeError: If scaling fails", + "dependencies": [ + "DockerNotFoundError", + "ValueError", + "DockerComposeError", + "run" + ], + "complexity": 6, + "business_context": "", + "imports": [ + "time", + "subprocess" + ], + "called_functions": [ + "DockerNotFoundError", + "ValueError", + "DockerComposeError", + "run" + ], + "parent_class": "", + "decorators": [], + "parameters": [ + { + "name": "service", + "type": "str" + }, + { + "name": "replicas", + "type": "int" + } + ], + "return_type": "None" + }, + { + "type": "function", + "name": "wait_for_service", + "filepath": "lib/laddr/src/laddr/cli/utils/docker.py", + "start_line": 242, + "end_line": 278, + "code": "def wait_for_service(\n service: str,\n timeout: int = 30,\n check_interval: int = 2,\n) -> bool:\n \"\"\"Wait for a service to be healthy.\n\n Args:\n service: Service name\n timeout: Maximum seconds to wait\n check_interval: Seconds between checks\n\n Returns:\n True if service is healthy, False otherwise\n \"\"\"\n print_info(f\"Waiting for {service} to be ready...\")\n\n elapsed = 0\n while elapsed < timeout:\n try:\n result = subprocess.run(\n [\"docker\", \"compose\", \"ps\", \"--format\", \"json\", service],\n capture_output=True,\n text=True,\n check=False,\n timeout=10,\n )\n if result.returncode == 0 and \"running\" in result.stdout.lower():\n print_success(f\"{service} is ready\")\n return True\n except (subprocess.TimeoutExpired, FileNotFoundError):\n pass\n\n time.sleep(check_interval)\n elapsed += check_interval\n\n return False", + "docstring": "Wait for a service to be healthy.\n\nArgs:\n service: Service name\n timeout: Maximum seconds to wait\n check_interval: Seconds between checks\n\nReturns:\n True if service is healthy, False otherwise", + "dependencies": [ + "print_info", + "sleep", + "print_success", + "lower", + "run" + ], + "complexity": 5, + "business_context": "", + "imports": [ + "time", + "subprocess" + ], + "called_functions": [ + "print_info", + "sleep", + "print_success", + "lower", + "run" + ], + "parent_class": "", + "decorators": [], + "parameters": [ + { + "name": "service", + "type": "str" + }, + { + "name": "timeout", + "type": "int" + }, + { + "name": "check_interval", + "type": "int" + } + ], + "return_type": "bool" + }, + { + "type": "function", + "name": "build_image", + "filepath": "lib/laddr/src/laddr/cli/utils/docker.py", + "start_line": 281, + "end_line": 305, + "code": "def build_image(dockerfile: Path, tag: str, context: Path) -> None:\n \"\"\"Build a Docker image.\n\n Args:\n dockerfile: Path to Dockerfile\n tag: Image tag\n context: Build context directory\n\n Raises:\n DockerComposeError: If build fails\n \"\"\"\n try:\n result = subprocess.run(\n [\"docker\", \"build\", \"-f\", str(dockerfile), \"-t\", tag, str(context)],\n capture_output=True,\n text=True,\n check=False,\n timeout=600, # 10 minutes\n )\n if result.returncode != 0:\n raise DockerComposeError(\"build\", result.stderr or result.stdout)\n except subprocess.TimeoutExpired:\n raise DockerComposeError(\"build\", \"Build timed out after 10 minutes\")\n except FileNotFoundError:\n raise DockerNotFoundError()", + "docstring": "Build a Docker image.\n\nArgs:\n dockerfile: Path to Dockerfile\n tag: Image tag\n context: Build context directory\n\nRaises:\n DockerComposeError: If build fails", + "dependencies": [ + "DockerNotFoundError", + "str", + "DockerComposeError", + "run" + ], + "complexity": 5, + "business_context": "", + "imports": [ + "time", + "subprocess" + ], + "called_functions": [ + "DockerNotFoundError", + "str", + "DockerComposeError", + "run" + ], + "parent_class": "", + "decorators": [], + "parameters": [ + { + "name": "dockerfile", + "type": "Path" + }, + { + "name": "tag", + "type": "str" + }, + { + "name": "context", + "type": "Path" + } + ], + "return_type": "None" + }, + { + "type": "function", + "name": "add_worker_to_compose", + "filepath": "lib/laddr/src/laddr/cli/utils/docker.py", + "start_line": 308, + "end_line": 351, + "code": "def add_worker_to_compose(project_path: Path, agent_name: str) -> None:\n \"\"\"Add worker service to docker-compose.yml.\n \n Args:\n project_path: Path to project root\n agent_name: Name of the agent\n \"\"\"\n compose_path = project_path / \"docker-compose.yml\"\n\n with open(compose_path, \"r\", encoding=\"utf-8\") as f:\n compose_data = yaml.safe_load(f)\n\n # Check if service already exists\n if f\"{agent_name}\" in compose_data.get(\"services\", {}):\n return\n\n # Add worker service\n worker_service = {\n \"build\": \".\",\n \"command\": f\"python -m agents.{agent_name}\",\n \"env_file\": \".env\",\n \"environment\": {\n \"AGENT_NAME\": agent_name,\n },\n \"depends_on\": [\"redis\", \"postgres\"],\n \"volumes\": [\".:/app\"],\n \"deploy\": {\n \"replicas\": f\"${{{agent_name.upper()}_SCALE:-1}}\",\n \"restart_policy\": {\n \"condition\": \"on-failure\",\n \"delay\": \"5s\",\n \"max_attempts\": 3,\n },\n \"resources\": {\n \"limits\": {\"cpus\": \"1.0\", \"memory\": \"1G\"},\n \"reservations\": {\"cpus\": \"0.5\", \"memory\": \"512M\"},\n },\n },\n }\n\n compose_data.setdefault(\"services\", {})[f\"{agent_name}_worker\"] = worker_service\n\n with open(compose_path, \"w\", encoding=\"utf-8\") as f:\n yaml.dump(compose_data, f, default_flow_style=False, sort_keys=False)", + "docstring": "Add worker service to docker-compose.yml.\n\nArgs:\n project_path: Path to project root\n agent_name: Name of the agent", + "dependencies": [ + "upper", + "get", + "setdefault", + "dump", + "safe_load", + "open" + ], + "complexity": 4, + "business_context": "agent", + "imports": [ + "yaml" + ], + "called_functions": [ + "upper", + "get", + "setdefault", + "dump", + "safe_load", + "open" + ], + "parent_class": "", + "decorators": [], + "parameters": [ + { + "name": "project_path", + "type": "Path" + }, + { + "name": "agent_name", + "type": "str" + } + ], + "return_type": "None" + }, + { + "type": "class", + "name": "LaddrError", + "filepath": "lib/laddr/src/laddr/cli/utils/errors.py", + "start_line": 10, + "end_line": 16, + "code": "class LaddrError(Exception):\n \"\"\"Base exception for all Laddr CLI errors.\"\"\"\n", + "docstring": "Base exception for all Laddr CLI errors.", + "dependencies": [ + "Exception" + ], + "complexity": 2, + "business_context": "", + "imports": [], + "called_functions": [], + "parent_class": "", + "decorators": [], + "parameters": [], + "return_type": "" + }, + { + "type": "method", + "name": "LaddrError.__init__", + "filepath": "lib/laddr/src/laddr/cli/utils/errors.py", + "start_line": 13, + "end_line": 16, + "code": " def __init__(self, message: str, hint: str | None = None):\n self.message = message\n self.hint = hint\n super().__init__(message)", + "docstring": "", + "dependencies": [ + "__init__", + "super" + ], + "complexity": 1, + "business_context": "message", + "imports": [], + "called_functions": [ + "__init__", + "super" + ], + "parent_class": "LaddrError", + "decorators": [], + "parameters": [ + { + "name": "self", + "type": "Any" + }, + { + "name": "message", + "type": "str" + }, + { + "name": "hint", + "type": "str | None" + } + ], + "return_type": "None" + }, + { + "type": "class", + "name": "ProjectNotFoundError", + "filepath": "lib/laddr/src/laddr/cli/utils/errors.py", + "start_line": 19, + "end_line": 26, + "code": "class ProjectNotFoundError(LaddrError):\n \"\"\"Raised when not in an Laddr project directory.\"\"\"\n", + "docstring": "Raised when not in an Laddr project directory.", + "dependencies": [ + "LaddrError" + ], + "complexity": 2, + "business_context": "", + "imports": [], + "called_functions": [], + "parent_class": "", + "decorators": [], + "parameters": [], + "return_type": "" + }, + { + "type": "method", + "name": "ProjectNotFoundError.__init__", + "filepath": "lib/laddr/src/laddr/cli/utils/errors.py", + "start_line": 22, + "end_line": 26, + "code": " def __init__(self, message: str = \"Not in an Laddr project directory\"):\n super().__init__(\n message,\n hint=\"Run 'laddr init ' to create a new project\",\n )", + "docstring": "", + "dependencies": [ + "__init__", + "super" + ], + "complexity": 1, + "business_context": "create, message, init", + "imports": [], + "called_functions": [ + "__init__", + "super" + ], + "parent_class": "ProjectNotFoundError", + "decorators": [], + "parameters": [ + { + "name": "self", + "type": "Any" + }, + { + "name": "message", + "type": "str" + } + ], + "return_type": "None" + }, + { + "type": "class", + "name": "ProjectExistsError", + "filepath": "lib/laddr/src/laddr/cli/utils/errors.py", + "start_line": 29, + "end_line": 36, + "code": "class ProjectExistsError(LaddrError):\n \"\"\"Raised when trying to create a project that already exists.\"\"\"\n", + "docstring": "Raised when trying to create a project that already exists.", + "dependencies": [ + "LaddrError" + ], + "complexity": 2, + "business_context": "create", + "imports": [], + "called_functions": [], + "parent_class": "", + "decorators": [], + "parameters": [], + "return_type": "" + }, + { + "type": "method", + "name": "ProjectExistsError.__init__", + "filepath": "lib/laddr/src/laddr/cli/utils/errors.py", + "start_line": 32, + "end_line": 36, + "code": " def __init__(self, path: str):\n super().__init__(\n f\"Directory {path} already contains an Laddr project\",\n hint=\"Use a different directory or delete the existing laddr.yml\",\n )", + "docstring": "", + "dependencies": [ + "__init__", + "super" + ], + "complexity": 1, + "business_context": "delete", + "imports": [], + "called_functions": [ + "__init__", + "super" + ], + "parent_class": "ProjectExistsError", + "decorators": [], + "parameters": [ + { + "name": "self", + "type": "Any" + }, + { + "name": "path", + "type": "str" + } + ], + "return_type": "None" + }, + { + "type": "class", + "name": "NestedProjectError", + "filepath": "lib/laddr/src/laddr/cli/utils/errors.py", + "start_line": 39, + "end_line": 46, + "code": "class NestedProjectError(LaddrError):\n \"\"\"Raised when trying to create a nested project.\"\"\"\n", + "docstring": "Raised when trying to create a nested project.", + "dependencies": [ + "LaddrError" + ], + "complexity": 2, + "business_context": "create", + "imports": [], + "called_functions": [], + "parent_class": "", + "decorators": [], + "parameters": [], + "return_type": "" + }, + { + "type": "method", + "name": "NestedProjectError.__init__", + "filepath": "lib/laddr/src/laddr/cli/utils/errors.py", + "start_line": 42, + "end_line": 46, + "code": " def __init__(self, parent_path: str):\n super().__init__(\n f\"Detected existing Laddr project at {parent_path}\",\n hint=\"Cannot create a nested project inside another Laddr project\",\n )", + "docstring": "", + "dependencies": [ + "__init__", + "super" + ], + "complexity": 1, + "business_context": "create", + "imports": [], + "called_functions": [ + "__init__", + "super" + ], + "parent_class": "NestedProjectError", + "decorators": [], + "parameters": [ + { + "name": "self", + "type": "Any" + }, + { + "name": "parent_path", + "type": "str" + } + ], + "return_type": "None" + }, + { + "type": "class", + "name": "InvalidConfigError", + "filepath": "lib/laddr/src/laddr/cli/utils/errors.py", + "start_line": 49, + "end_line": 56, + "code": "class InvalidConfigError(LaddrError):\n \"\"\"Raised when configuration files are invalid.\"\"\"\n", + "docstring": "Raised when configuration files are invalid.", + "dependencies": [ + "LaddrError" + ], + "complexity": 2, + "business_context": "", + "imports": [], + "called_functions": [], + "parent_class": "", + "decorators": [], + "parameters": [], + "return_type": "" + }, + { + "type": "method", + "name": "InvalidConfigError.__init__", + "filepath": "lib/laddr/src/laddr/cli/utils/errors.py", + "start_line": 52, + "end_line": 56, + "code": " def __init__(self, filename: str, details: str):\n super().__init__(\n f\"Invalid configuration in {filename}: {details}\",\n hint=\"Check the file syntax and ensure all required fields are present\",\n )", + "docstring": "", + "dependencies": [ + "__init__", + "super" + ], + "complexity": 1, + "business_context": "", + "imports": [], + "called_functions": [ + "__init__", + "super" + ], + "parent_class": "InvalidConfigError", + "decorators": [], + "parameters": [ + { + "name": "self", + "type": "Any" + }, + { + "name": "filename", + "type": "str" + }, + { + "name": "details", + "type": "str" + } + ], + "return_type": "None" + }, + { + "type": "class", + "name": "DockerNotFoundError", + "filepath": "lib/laddr/src/laddr/cli/utils/errors.py", + "start_line": 59, + "end_line": 66, + "code": "class DockerNotFoundError(LaddrError):\n \"\"\"Raised when Docker is not installed or not running.\"\"\"\n", + "docstring": "Raised when Docker is not installed or not running.", + "dependencies": [ + "LaddrError" + ], + "complexity": 2, + "business_context": "", + "imports": [], + "called_functions": [], + "parent_class": "", + "decorators": [], + "parameters": [], + "return_type": "" + }, + { + "type": "method", + "name": "DockerNotFoundError.__init__", + "filepath": "lib/laddr/src/laddr/cli/utils/errors.py", + "start_line": 62, + "end_line": 66, + "code": " def __init__(self):\n super().__init__(\n \"Docker not found or not running\",\n hint=\"Install Docker (https://docs.docker.com/get-docker/) and ensure it's running\",\n )", + "docstring": "", + "dependencies": [ + "__init__", + "super" + ], + "complexity": 1, + "business_context": "", + "imports": [], + "called_functions": [ + "__init__", + "super" + ], + "parent_class": "DockerNotFoundError", + "decorators": [], + "parameters": [ + { + "name": "self", + "type": "Any" + } + ], + "return_type": "None" + }, + { + "type": "class", + "name": "DockerComposeError", + "filepath": "lib/laddr/src/laddr/cli/utils/errors.py", + "start_line": 69, + "end_line": 76, + "code": "class DockerComposeError(LaddrError):\n \"\"\"Raised when Docker Compose operations fail.\"\"\"\n", + "docstring": "Raised when Docker Compose operations fail.", + "dependencies": [ + "LaddrError" + ], + "complexity": 2, + "business_context": "", + "imports": [], + "called_functions": [], + "parent_class": "", + "decorators": [], + "parameters": [], + "return_type": "" + }, + { + "type": "method", + "name": "DockerComposeError.__init__", + "filepath": "lib/laddr/src/laddr/cli/utils/errors.py", + "start_line": 72, + "end_line": 76, + "code": " def __init__(self, operation: str, details: str):\n super().__init__(\n f\"Docker Compose {operation} failed: {details}\",\n hint=\"Check docker-compose.yml and ensure all services are properly configured\",\n )", + "docstring": "", + "dependencies": [ + "__init__", + "super" + ], + "complexity": 1, + "business_context": "", + "imports": [], + "called_functions": [ + "__init__", + "super" + ], + "parent_class": "DockerComposeError", + "decorators": [], + "parameters": [ + { + "name": "self", + "type": "Any" + }, + { + "name": "operation", + "type": "str" + }, + { + "name": "details", + "type": "str" + } + ], + "return_type": "None" + }, + { + "type": "class", + "name": "AgentNotFoundError", + "filepath": "lib/laddr/src/laddr/cli/utils/errors.py", + "start_line": 79, + "end_line": 86, + "code": "class AgentNotFoundError(LaddrError):\n \"\"\"Raised when an agent doesn't exist.\"\"\"\n", + "docstring": "Raised when an agent doesn't exist.", + "dependencies": [ + "LaddrError" + ], + "complexity": 2, + "business_context": "agent", + "imports": [], + "called_functions": [], + "parent_class": "", + "decorators": [], + "parameters": [], + "return_type": "" + }, + { + "type": "method", + "name": "AgentNotFoundError.__init__", + "filepath": "lib/laddr/src/laddr/cli/utils/errors.py", + "start_line": 82, + "end_line": 86, + "code": " def __init__(self, agent_name: str):\n super().__init__(\n f\"Agent '{agent_name}' not found\",\n hint=f\"Run 'laddr add agent {agent_name}' to create it\",\n )", + "docstring": "", + "dependencies": [ + "__init__", + "super" + ], + "complexity": 1, + "business_context": "create, agent", + "imports": [], + "called_functions": [ + "__init__", + "super" + ], + "parent_class": "AgentNotFoundError", + "decorators": [], + "parameters": [ + { + "name": "self", + "type": "Any" + }, + { + "name": "agent_name", + "type": "str" + } + ], + "return_type": "None" + }, + { + "type": "class", + "name": "InvalidInputError", + "filepath": "lib/laddr/src/laddr/cli/utils/errors.py", + "start_line": 89, + "end_line": 96, + "code": "class InvalidInputError(LaddrError):\n \"\"\"Raised when user input is invalid.\"\"\"\n", + "docstring": "Raised when user input is invalid.", + "dependencies": [ + "LaddrError" + ], + "complexity": 2, + "business_context": "", + "imports": [], + "called_functions": [], + "parent_class": "", + "decorators": [], + "parameters": [], + "return_type": "" + }, + { + "type": "method", + "name": "InvalidInputError.__init__", + "filepath": "lib/laddr/src/laddr/cli/utils/errors.py", + "start_line": 92, + "end_line": 96, + "code": " def __init__(self, field: str, details: str):\n super().__init__(\n f\"Invalid {field}: {details}\",\n hint=\"Check the command usage with --help\",\n )", + "docstring": "", + "dependencies": [ + "__init__", + "super" + ], + "complexity": 1, + "business_context": "", + "imports": [], + "called_functions": [ + "__init__", + "super" + ], + "parent_class": "InvalidInputError", + "decorators": [], + "parameters": [ + { + "name": "self", + "type": "Any" + }, + { + "name": "field", + "type": "str" + }, + { + "name": "details", + "type": "str" + } + ], + "return_type": "None" + }, + { + "type": "class", + "name": "FileGenerationError", + "filepath": "lib/laddr/src/laddr/cli/utils/errors.py", + "start_line": 99, + "end_line": 106, + "code": "class FileGenerationError(LaddrError):\n \"\"\"Raised when file generation fails.\"\"\"\n", + "docstring": "Raised when file generation fails.", + "dependencies": [ + "LaddrError" + ], + "complexity": 2, + "business_context": "", + "imports": [], + "called_functions": [], + "parent_class": "", + "decorators": [], + "parameters": [], + "return_type": "" + }, + { + "type": "method", + "name": "FileGenerationError.__init__", + "filepath": "lib/laddr/src/laddr/cli/utils/errors.py", + "start_line": 102, + "end_line": 106, + "code": " def __init__(self, filename: str, details: str):\n super().__init__(\n f\"Failed to generate {filename}: {details}\",\n hint=\"Check file permissions and available disk space\",\n )", + "docstring": "", + "dependencies": [ + "__init__", + "super" + ], + "complexity": 1, + "business_context": "generate", + "imports": [], + "called_functions": [ + "__init__", + "super" + ], + "parent_class": "FileGenerationError", + "decorators": [], + "parameters": [ + { + "name": "self", + "type": "Any" + }, + { + "name": "filename", + "type": "str" + }, + { + "name": "details", + "type": "str" + } + ], + "return_type": "None" + }, + { + "type": "class", + "name": "ServiceNotReadyError", + "filepath": "lib/laddr/src/laddr/cli/utils/errors.py", + "start_line": 109, + "end_line": 116, + "code": "class ServiceNotReadyError(LaddrError):\n \"\"\"Raised when a service is not ready after startup.\"\"\"\n", + "docstring": "Raised when a service is not ready after startup.", + "dependencies": [ + "LaddrError" + ], + "complexity": 2, + "business_context": "", + "imports": [], + "called_functions": [], + "parent_class": "", + "decorators": [], + "parameters": [], + "return_type": "" + }, + { + "type": "method", + "name": "ServiceNotReadyError.__init__", + "filepath": "lib/laddr/src/laddr/cli/utils/errors.py", + "start_line": 112, + "end_line": 116, + "code": " def __init__(self, service: str, timeout: int):\n super().__init__(\n f\"Service '{service}' did not become ready within {timeout} seconds\",\n hint=f\"Check logs with 'laddr logs {service}' or 'docker compose logs {service}'\",\n )", + "docstring": "", + "dependencies": [ + "__init__", + "super" + ], + "complexity": 1, + "business_context": "", + "imports": [], + "called_functions": [ + "__init__", + "super" + ], + "parent_class": "ServiceNotReadyError", + "decorators": [], + "parameters": [ + { + "name": "self", + "type": "Any" + }, + { + "name": "service", + "type": "str" + }, + { + "name": "timeout", + "type": "int" + } + ], + "return_type": "None" + }, + { + "type": "class", + "name": "SubmitJobRequest", + "filepath": "lib/laddr/src/laddr/api/main.py", + "start_line": 46, + "end_line": 49, + "code": "class SubmitJobRequest(BaseModel):\n \"\"\"Request to submit a new job (legacy).\"\"\"\n pipeline_name: str\n inputs: dict[str, Any]", + "docstring": "Request to submit a new job (legacy).", + "dependencies": [ + "BaseModel" + ], + "complexity": 3, + "business_context": "", + "imports": [], + "called_functions": [], + "parent_class": "", + "decorators": [], + "parameters": [], + "return_type": "" + }, + { + "type": "class", + "name": "SubmitPromptRequest", + "filepath": "lib/laddr/src/laddr/api/main.py", + "start_line": 52, + "end_line": 57, + "code": "class SubmitPromptRequest(BaseModel):\n \"\"\"Request to submit a new prompt execution.\"\"\"\n prompt_name: str\n inputs: dict[str, Any]\n mode: str = \"single\" # \"single\" or \"sequential\"\n agents: list[str] | None = None # For sequential mode: ordered list of agents", + "docstring": "Request to submit a new prompt execution.", + "dependencies": [ + "BaseModel" + ], + "complexity": 5, + "business_context": "", + "imports": [], + "called_functions": [], + "parent_class": "", + "decorators": [], + "parameters": [], + "return_type": "" + }, + { + "type": "class", + "name": "CancelPromptResponse", + "filepath": "lib/laddr/src/laddr/api/main.py", + "start_line": 60, + "end_line": 63, + "code": "class CancelPromptResponse(BaseModel):\n ok: bool\n prompt_id: str\n status: str", + "docstring": "", + "dependencies": [ + "BaseModel" + ], + "complexity": 3, + "business_context": "", + "imports": [], + "called_functions": [], + "parent_class": "", + "decorators": [], + "parameters": [], + "return_type": "" + }, + { + "type": "class", + "name": "ReplayJobRequest", + "filepath": "lib/laddr/src/laddr/api/main.py", + "start_line": 66, + "end_line": 68, + "code": "class ReplayJobRequest(BaseModel):\n \"\"\"Request to replay a job.\"\"\"\n reexecute: bool = False", + "docstring": "Request to replay a job.", + "dependencies": [ + "BaseModel" + ], + "complexity": 2, + "business_context": "", + "imports": [], + "called_functions": [], + "parent_class": "", + "decorators": [], + "parameters": [], + "return_type": "" + }, + { + "type": "class", + "name": "AgentChatRequest", + "filepath": "lib/laddr/src/laddr/api/main.py", + "start_line": 71, + "end_line": 75, + "code": "class AgentChatRequest(BaseModel):\n \"\"\"Request to chat with an agent.\"\"\"\n message: str\n wait: bool = True\n timeout: int = 30", + "docstring": "Request to chat with an agent.", + "dependencies": [ + "BaseModel" + ], + "complexity": 4, + "business_context": "message, agent", + "imports": [], + "called_functions": [], + "parent_class": "", + "decorators": [], + "parameters": [], + "return_type": "" + }, + { + "type": "class", + "name": "AgentInfo", + "filepath": "lib/laddr/src/laddr/api/main.py", + "start_line": 78, + "end_line": 87, + "code": "class AgentInfo(BaseModel):\n \"\"\"Agent information.\"\"\"\n name: str\n role: str\n goal: str\n status: str\n tools: list[str]\n last_seen: str | None = None\n trace_count: int = 0\n last_executed: str | None = None", + "docstring": "Agent information.", + "dependencies": [ + "BaseModel" + ], + "complexity": 9, + "business_context": "agent", + "imports": [], + "called_functions": [], + "parent_class": "", + "decorators": [], + "parameters": [], + "return_type": "" + }, + { + "type": "class", + "name": "EventThrottler", + "filepath": "lib/laddr/src/laddr/api/main.py", + "start_line": 1155, + "end_line": 1179, + "code": "class EventThrottler:\n \"\"\"Throttle WebSocket events to avoid overwhelming clients.\"\"\"\n", + "docstring": "Throttle WebSocket events to avoid overwhelming clients.", + "dependencies": [], + "complexity": 3, + "business_context": "", + "imports": [], + "called_functions": [], + "parent_class": "", + "decorators": [], + "parameters": [], + "return_type": "" + }, + { + "type": "method", + "name": "EventThrottler.__init__", + "filepath": "lib/laddr/src/laddr/api/main.py", + "start_line": 1158, + "end_line": 1161, + "code": " def __init__(self, max_per_second: int = 10):\n self.max_per_second = max_per_second\n self.last_sent = 0.0\n self.buffer: list[dict] = []", + "docstring": "", + "dependencies": [], + "complexity": 1, + "business_context": "", + "imports": [], + "called_functions": [], + "parent_class": "EventThrottler", + "decorators": [], + "parameters": [ + { + "name": "self", + "type": "Any" + }, + { + "name": "max_per_second", + "type": "int" + } + ], + "return_type": "None" + }, + { + "type": "method", + "name": "EventThrottler.add", + "filepath": "lib/laddr/src/laddr/api/main.py", + "start_line": 1163, + "end_line": 1179, + "code": " def add(self, event: dict) -> dict | None:\n \"\"\"Add event, return it if should be sent immediately.\"\"\"\n now = time.time()\n elapsed = now - self.last_sent\n\n if elapsed >= (1.0 / self.max_per_second):\n self.last_sent = now\n return event\n # Buffer for batching\n self.buffer.append(event)\n if len(self.buffer) >= 5: # Batch size\n batch = self.buffer[:]\n self.buffer.clear()\n self.last_sent = now\n return {\"type\": \"batch\", \"events\": batch}\n\n return None", + "docstring": "Add event, return it if should be sent immediately.", + "dependencies": [ + "clear", + "time", + "len", + "append" + ], + "complexity": 3, + "business_context": "", + "imports": [ + "time" + ], + "called_functions": [ + "clear", + "time", + "len", + "append" + ], + "parent_class": "EventThrottler", + "decorators": [], + "parameters": [ + { + "name": "self", + "type": "Any" + }, + { + "name": "event", + "type": "dict" + } + ], + "return_type": "dict | None" + }, + { + "type": "function", + "name": "_parse_ts", + "filepath": "lib/laddr/src/laddr/api/main.py", + "start_line": 1246, + "end_line": 1252, + "code": " def _parse_ts(ts: str | None) -> datetime | None:\n if not ts:\n return None\n try:\n return datetime.fromisoformat(ts.replace(\"Z\", \"+00:00\")).replace(tzinfo=None)\n except Exception:\n return None", + "docstring": "", + "dependencies": [ + "fromisoformat", + "replace" + ], + "complexity": 3, + "business_context": "", + "imports": [ + "datetime", + "time" + ], + "called_functions": [ + "fromisoformat", + "replace" + ], + "parent_class": "", + "decorators": [], + "parameters": [ + { + "name": "ts", + "type": "str | None" + } + ], + "return_type": "datetime | None" + }, + { + "type": "function", + "name": "_build_trace_tree", + "filepath": "lib/laddr/src/laddr/api/main.py", + "start_line": 1254, + "end_line": 1330, + "code": " def _build_trace_tree(traces: list[dict]) -> list[dict]:\n \"\"\"\n Build hierarchical trace tree from flat traces.\n Groups by agent runs and nests tool calls/LLM calls within them.\n \"\"\"\n # Index traces by ID for quick lookup\n trace_map = {t['id']: t for t in traces}\n roots = []\n \n # First pass: identify parent-child relationships\n for trace in traces:\n parent_id = trace.get('parent_id')\n if parent_id and parent_id in trace_map:\n parent = trace_map[parent_id]\n if 'children' not in parent:\n parent['children'] = []\n parent['children'].append(trace)\n else:\n # Root level trace\n roots.append(trace)\n \n # Build span structure\n def build_span(trace: dict) -> dict:\n event_type = trace.get('event_type', '')\n payload = trace.get('payload', {})\n agent_name = trace.get('agent_name', '')\n timestamp = trace.get('timestamp', '')\n \n # Extract metrics\n duration_ms = None\n tokens = None\n cost = None\n \n # Calculate duration if we have start/end events\n if event_type == 'task_complete':\n # Look for matching task_start to calculate duration\n start_trace = next((t for t in traces if t.get('agent_name') == agent_name \n and t.get('event_type') == 'task_start' \n and t.get('id') < trace['id']), None)\n if start_trace:\n start_ts = _parse_ts(start_trace.get('timestamp'))\n end_ts = _parse_ts(timestamp)\n if start_ts and end_ts:\n duration_ms = int((end_ts - start_ts).total_seconds() * 1000)\n \n # Extract token usage\n if event_type == 'llm_usage' or 'usage' in payload:\n usage = payload.get('usage', payload)\n tokens = usage.get('total_tokens', 0)\n cost = usage.get('cost')\n \n # Build span object\n span = {\n 'id': trace['id'],\n 'name': agent_name if event_type.startswith('task_') else payload.get('tool', event_type),\n 'type': _get_span_type(event_type, payload),\n 'start_time': timestamp,\n 'agent': agent_name,\n 'event_type': event_type,\n 'input': payload.get('inputs', payload.get('params', {})),\n 'output': payload.get('result', payload.get('outputs')),\n 'metadata': {\n 'duration_ms': duration_ms,\n 'tokens': tokens,\n 'cost': cost,\n **payload\n },\n 'children': []\n }\n \n # Add children recursively\n if 'children' in trace:\n span['children'] = [build_span(child) for child in trace['children']]\n \n return span\n \n return [build_span(root) for root in roots]", + "docstring": "Build hierarchical trace tree from flat traces.\nGroups by agent runs and nests tool calls/LLM calls within them.", + "dependencies": [ + "append", + "get", + "_get_span_type", + "startswith", + "_parse_ts", + "build_span", + "total_seconds", + "int", + "next" + ], + "complexity": 13, + "business_context": "llm, tool, calculate, agent", + "imports": [ + "time" + ], + "called_functions": [ + "append", + "get", + "_get_span_type", + "startswith", + "_parse_ts", + "build_span", + "total_seconds", + "int", + "next" + ], + "parent_class": "", + "decorators": [], + "parameters": [ + { + "name": "traces", + "type": "list[dict]" + } + ], + "return_type": "list[dict]" + }, + { + "type": "function", + "name": "_get_span_type", + "filepath": "lib/laddr/src/laddr/api/main.py", + "start_line": 1332, + "end_line": 1343, + "code": " def _get_span_type(event_type: str, payload: dict) -> str:\n \"\"\"Determine span type for UI rendering.\"\"\"\n if event_type.startswith('task_'):\n return 'agent'\n elif event_type in ('tool_call', 'tool_result'):\n return 'tool'\n elif event_type in ('llm_call', 'llm_usage'):\n return 'llm'\n elif 'think' in event_type:\n return 'reasoning'\n else:\n return 'event'", + "docstring": "Determine span type for UI rendering.", + "dependencies": [ + "startswith" + ], + "complexity": 5, + "business_context": "llm, tool, agent", + "imports": [], + "called_functions": [ + "startswith" + ], + "parent_class": "", + "decorators": [], + "parameters": [ + { + "name": "event_type", + "type": "str" + }, + { + "name": "payload", + "type": "dict" + } + ], + "return_type": "str" + }, + { + "type": "function", + "name": "build_span", + "filepath": "lib/laddr/src/laddr/api/main.py", + "start_line": 1276, + "end_line": 1328, + "code": " def build_span(trace: dict) -> dict:\n event_type = trace.get('event_type', '')\n payload = trace.get('payload', {})\n agent_name = trace.get('agent_name', '')\n timestamp = trace.get('timestamp', '')\n \n # Extract metrics\n duration_ms = None\n tokens = None\n cost = None\n \n # Calculate duration if we have start/end events\n if event_type == 'task_complete':\n # Look for matching task_start to calculate duration\n start_trace = next((t for t in traces if t.get('agent_name') == agent_name \n and t.get('event_type') == 'task_start' \n and t.get('id') < trace['id']), None)\n if start_trace:\n start_ts = _parse_ts(start_trace.get('timestamp'))\n end_ts = _parse_ts(timestamp)\n if start_ts and end_ts:\n duration_ms = int((end_ts - start_ts).total_seconds() * 1000)\n \n # Extract token usage\n if event_type == 'llm_usage' or 'usage' in payload:\n usage = payload.get('usage', payload)\n tokens = usage.get('total_tokens', 0)\n cost = usage.get('cost')\n \n # Build span object\n span = {\n 'id': trace['id'],\n 'name': agent_name if event_type.startswith('task_') else payload.get('tool', event_type),\n 'type': _get_span_type(event_type, payload),\n 'start_time': timestamp,\n 'agent': agent_name,\n 'event_type': event_type,\n 'input': payload.get('inputs', payload.get('params', {})),\n 'output': payload.get('result', payload.get('outputs')),\n 'metadata': {\n 'duration_ms': duration_ms,\n 'tokens': tokens,\n 'cost': cost,\n **payload\n },\n 'children': []\n }\n \n # Add children recursively\n if 'children' in trace:\n span['children'] = [build_span(child) for child in trace['children']]\n \n return span", + "docstring": "", + "dependencies": [ + "get", + "_get_span_type", + "startswith", + "build_span", + "total_seconds", + "int", + "next", + "_parse_ts" + ], + "complexity": 9, + "business_context": "tool, calculate, agent", + "imports": [ + "time" + ], + "called_functions": [ + "get", + "_get_span_type", + "startswith", + "build_span", + "total_seconds", + "int", + "next", + "_parse_ts" + ], + "parent_class": "", + "decorators": [], + "parameters": [ + { + "name": "trace", + "type": "dict" + } + ], + "return_type": "dict" + } + ], + "code_patterns": [ + { + "pattern_type": "class_implementation", + "description": "类 _LLMWithDefaults 的实现", + "code_snippet": "class _LLMWithDefaults:\n \"\"\"Wrap an LLM backend with default params for generate().\"\"\"\n", + "context": "文件: lib/laddr/src/laddr/llms.py\n文档: Wrap an LLM backend with default params for generate().", + "related_elements": [ + "_LLMWithDefaults" + ] + }, + { + "pattern_type": "class_implementation", + "description": "类 Tool 的实现", + "code_snippet": "class Tool:\n \"\"\"\n Tool metadata and callable.\n \n Represents a single tool that can be invoked by agents.\n \"\"\"\n\n name: str\n func: Callable\n description: str\n input_model: type | None = None\n # Optional explicit JSON schema for parameters (for LLM function-calling UIs)\n parameters_schema: dict | None = None\n # Tracing controls\n trace_enabled: bool = True\n trace_mask: set[str] = field(default_factory=set)\n", + "context": "文件: lib/laddr/src/laddr/core/tooling.py\n文档: Tool metadata and callable.\n\nRepresents a single tool that can be invoked by agents.", + "related_elements": [ + "Tool" + ] + }, + { + "pattern_type": "class_implementation", + "description": "类 ToolRegistry 的实现", + "code_snippet": "class ToolRegistry:\n \"\"\"\n Registry for managing available tools.\n \n Supports registration, lookup, and listing of tools.\n \"\"\"\n", + "context": "文件: lib/laddr/src/laddr/core/tooling.py\n文档: Registry for managing available tools.\n\nSupports registration, lookup, and listing of tools.", + "related_elements": [ + "ToolRegistry" + ] + }, + { + "pattern_type": "class_implementation", + "description": "类 LaddrConfig 的实现", + "code_snippet": "class LaddrConfig(BaseSettings):\n \"\"\"\n Environment-based configuration for Laddr runtime.\n \n Supports pluggable backends for all infrastructure:\n - queue_backend: \"redis\" (default) | \"memory\"\n - db_backend: \"postgres\" (default) | \"sqlite\"\n - llm_backend: \"noop\" (default) | \"openai\" | \"anthropic\" | \"gemini\"\n - cache_backend: \"inmemory\" (default) | \"redis\"\n \"\"\"\n\n # Pluggable backend selection\n queue_backend: str = Field(default=\"redis\", description=\"Message queue backend\")\n db_backend: str = Field(default=\"postgres\", description=\"Database backend\")\n llm_backend: str = Field(default=\"noop\", description=\"LLM backend (noop=echo)\")\n llm_model: str | None = Field(default=None, description=\"LLM model name (optional, backend-specific)\")\n openai_base_url: str | None = Field(default=None, description=\"OpenAI-compatible base URL (e.g., vLLM)\")\n cache_backend: str = Field(default=\"inmemory\", description=\"Cache backend\")\n\n # Database connection\n database_url: str = Field(", + "context": "文件: lib/laddr/src/laddr/core/config.py\n文档: Environment-based configuration for Laddr runtime.\n\nSupports pluggable backends for all infrastructure:\n- queue_backend: \"redis\" (default) | \"memory\"\n- db_backend: \"postgres\" (default) | \"sqlite\"\n- ll", + "related_elements": [ + "LaddrConfig" + ] + }, + { + "pattern_type": "class_implementation", + "description": "类 QueueBackend 的实现", + "code_snippet": "class QueueBackend(Protocol):\n \"\"\"Protocol for message queue backends.\"\"\"\n\n async def register_agent(self, name: str, metadata: dict) -> bool: ...\n async def publish_task(self, agent_name: str, task: dict) -> str: ...\n async def publish_response(self, task_id: str, response: dict) -> bool: ...\n async def consume_tasks(self, agent_name: str, block_ms: int, count: int) -> list[dict]: ...\n async def wait_for_response(self, task_id: str, timeout_sec: int) -> dict | None: ...\n async def get_registered_agents(self) -> dict[str, dict]: ...\n async def get_queue_depth(self, agent_name: str) -> int: ...\n async def health_check(self) -> bool: ...", + "context": "文件: lib/laddr/src/laddr/core/config.py\n文档: Protocol for message queue backends.", + "related_elements": [ + "QueueBackend" + ] + }, + { + "pattern_type": "class_implementation", + "description": "类 DatabaseBackend 的实现", + "code_snippet": "class DatabaseBackend(Protocol):\n \"\"\"Protocol for database backends.\"\"\"\n", + "context": "文件: lib/laddr/src/laddr/core/config.py\n文档: Protocol for database backends.", + "related_elements": [ + "DatabaseBackend" + ] + }, + { + "pattern_type": "class_implementation", + "description": "类 LLMBackend 的实现", + "code_snippet": "class LLMBackend(Protocol):\n \"\"\"Protocol for LLM backends.\"\"\"\n\n async def generate(self, prompt: str, system: str | None = None, **kwargs) -> str: ...", + "context": "文件: lib/laddr/src/laddr/core/config.py\n文档: Protocol for LLM backends.", + "related_elements": [ + "LLMBackend" + ] + }, + { + "pattern_type": "class_implementation", + "description": "类 CacheBackend 的实现", + "code_snippet": "class CacheBackend(Protocol):\n \"\"\"Protocol for cache backends.\"\"\"\n", + "context": "文件: lib/laddr/src/laddr/core/config.py\n文档: Protocol for cache backends.", + "related_elements": [ + "CacheBackend" + ] + }, + { + "pattern_type": "class_implementation", + "description": "类 BackendFactory 的实现", + "code_snippet": "class BackendFactory:\n \"\"\"\n Factory for creating backend instances based on configuration.\n \n Provides pluggable implementations for queue, database, LLM, and cache.\n \"\"\"\n", + "context": "文件: lib/laddr/src/laddr/core/config.py\n文档: Factory for creating backend instances based on configuration.\n\nProvides pluggable implementations for queue, database, LLM, and cache.", + "related_elements": [ + "BackendFactory" + ] + }, + { + "pattern_type": "class_implementation", + "description": "类 ProjectConfig 的实现", + "code_snippet": "class ProjectConfig(BaseModel):\n \"\"\"\n Project configuration from laddr.yml.\n \n Defines which agents exist and backend preferences.\n \"\"\"\n\n name: str = Field(description=\"Project name\")\n\n # Backend selections (can override env defaults)\n queue_backend: str = Field(default=\"redis\", description=\"Message queue backend\")\n db_backend: str = Field(default=\"postgres\", description=\"Database backend\")\n llm_backend: str = Field(default=\"noop\", description=\"LLM backend\")\n cache_backend: str = Field(default=\"inmemory\", description=\"Cache backend\")\n\n # Agent registry\n agents: list[str] = Field(default_factory=list, description=\"List of agent names\")", + "context": "文件: lib/laddr/src/laddr/core/config.py\n文档: Project configuration from laddr.yml.\n\nDefines which agents exist and backend preferences.", + "related_elements": [ + "ProjectConfig" + ] + }, + { + "pattern_type": "class_implementation", + "description": "类 AgentConfig 的实现", + "code_snippet": "class AgentConfig(BaseModel):\n \"\"\"\n Agent configuration (minimal for @actor pattern).\n \n Most runtime config is auto-discovered or comes from LaddrConfig.\n \"\"\"\n\n name: str\n role: str\n goal: str\n backstory: str | None = None\n max_iterations: int = Field(default=5) # Reduced from 7 to 5 - forced finish at 4 successful tools\n allow_delegation: bool = Field(default=True)\n verbose: bool = Field(default=False)\n # Optional per-agent LLM overrides. If set, they take precedence over the\n # global `LaddrConfig.llm_backend` and `LaddrConfig.llm_model` values.\n llm_backend: str | None = Field(default=None, description=\"Optional agent-specific LLM backend (e.g., 'openai', 'gemini')\")\n llm_model: str | None = Field(default=None, description=\"Optional agent-specific LLM model name\")", + "context": "文件: lib/laddr/src/laddr/core/config.py\n文档: Agent configuration (minimal for @actor pattern).\n\nMost runtime config is auto-discovered or comes from LaddrConfig.", + "related_elements": [ + "AgentConfig" + ] + }, + { + "pattern_type": "class_implementation", + "description": "类 PipelineStage 的实现", + "code_snippet": "class PipelineStage(BaseModel):\n \"\"\"Stage definition in a pipeline (simplified).\"\"\"\n\n agent: str = Field(description=\"Agent name to execute\")\n inputs: dict[str, Any] = Field(default_factory=dict, description=\"Stage inputs\")", + "context": "文件: lib/laddr/src/laddr/core/config.py\n文档: Stage definition in a pipeline (simplified).", + "related_elements": [ + "PipelineStage" + ] + }, + { + "pattern_type": "class_implementation", + "description": "类 PipelineConfig 的实现", + "code_snippet": "class PipelineConfig(BaseModel):\n \"\"\"Pipeline configuration for sequential agent execution.\"\"\"\n\n name: str\n description: str | None = None\n stages: list[PipelineStage]", + "context": "文件: lib/laddr/src/laddr/core/config.py\n文档: Pipeline configuration for sequential agent execution.", + "related_elements": [ + "PipelineConfig" + ] + }, + { + "pattern_type": "class_implementation", + "description": "类 AgentRunner 的实现", + "code_snippet": "class AgentRunner:\n \"\"\"\n High-level agent runner for executing agents and managing jobs.\n \n Provides:\n - run(): Execute agent with job tracking\n - replay(): Replay previous job\n - run_pipeline(): Orchestrate multi-agent workflow\n \"\"\"\n", + "context": "文件: lib/laddr/src/laddr/core/runtime_entry.py\n文档: High-level agent runner for executing agents and managing jobs.\n\nProvides:\n- run(): Execute agent with job tracking\n- replay(): Replay previous job\n- run_pipeline(): Orchestrate multi-agent workflow", + "related_elements": [ + "AgentRunner" + ] + }, + { + "pattern_type": "class_implementation", + "description": "类 WorkerRunner 的实现", + "code_snippet": "class WorkerRunner:\n \"\"\"\n Lightweight worker runner for a single agent instance.\n\n Consumes tasks from the message bus for the agent's name and publishes results.\n \"\"\"\n", + "context": "文件: lib/laddr/src/laddr/core/runtime_entry.py\n文档: Lightweight worker runner for a single agent instance.\n\nConsumes tasks from the message bus for the agent's name and publishes results.", + "related_elements": [ + "WorkerRunner" + ] + }, + { + "pattern_type": "class_implementation", + "description": "类 InMemoryCache 的实现", + "code_snippet": "class InMemoryCache:\n \"\"\"\n In-memory cache for local dev and testing.\n \n Simple dict-based cache with optional TTL.\n \"\"\"\n", + "context": "文件: lib/laddr/src/laddr/core/cache.py\n文档: In-memory cache for local dev and testing.\n\nSimple dict-based cache with optional TTL.", + "related_elements": [ + "InMemoryCache" + ] + }, + { + "pattern_type": "class_implementation", + "description": "类 RedisCache 的实现", + "code_snippet": "class RedisCache:\n \"\"\"\n Redis-based cache for production.\n \n Uses Redis with TTL support.\n \"\"\"\n", + "context": "文件: lib/laddr/src/laddr/core/cache.py\n文档: Redis-based cache for production.\n\nUses Redis with TTL support.", + "related_elements": [ + "RedisCache" + ] + }, + { + "pattern_type": "class_implementation", + "description": "类 AgentMemory 的实现", + "code_snippet": "class AgentMemory:\n \"\"\"Memory interface for agents.\"\"\"\n\n agent_name: str\n database: Any # DatabaseService\n job_id: str | None = None\n", + "context": "文件: lib/laddr/src/laddr/core/agent_runtime.py\n文档: Memory interface for agents.", + "related_elements": [ + "AgentMemory" + ] + }, + { + "pattern_type": "class_implementation", + "description": "类 Agent 的实现", + "code_snippet": "class Agent:\n \"\"\"\n Base Agent class with full runtime capabilities.\n \n Provides:\n - Planning and execution\n - Tool calling with validation and caching\n - Delegation with blocking wait for responses\n - Memory storage\n - Auto-registration on message bus\n - Trace logging to database\n \n Subclasses should override plan() method for custom planning logic,\n or use the default autonomous_run() for intelligent LLM-driven execution.\n \"\"\"\n\n # Class-level metadata (set by @actor decorator or subclass)\n ROLE: str = \"agent\"\n GOAL: str = \"Complete assigned tasks\"\n", + "context": "文件: lib/laddr/src/laddr/core/agent_runtime.py\n文档: Base Agent class with full runtime capabilities.\n\nProvides:\n- Planning and execution\n- Tool calling with validation and caching\n- Delegation with blocking wait for responses\n- Memory storage\n- Auto-re", + "related_elements": [ + "Agent" + ] + }, + { + "pattern_type": "class_implementation", + "description": "类 MCPToolSource 的实现", + "code_snippet": "class MCPToolSource:\n \"\"\"Placeholder - MCP feature disabled for this release.\"\"\"", + "context": "文件: lib/laddr/src/laddr/core/mcp_client.py\n文档: Placeholder - MCP feature disabled for this release.", + "related_elements": [ + "MCPToolSource" + ] + }, + { + "pattern_type": "class_implementation", + "description": "类 MCPToolRegistry 的实现", + "code_snippet": "class MCPToolRegistry:\n \"\"\"Placeholder - MCP feature disabled for this release.\"\"\"", + "context": "文件: lib/laddr/src/laddr/core/mcp_client.py\n文档: Placeholder - MCP feature disabled for this release.", + "related_elements": [ + "MCPToolRegistry" + ] + }, + { + "pattern_type": "class_implementation", + "description": "类 S3Storage 的实现", + "code_snippet": "class S3Storage:\n \"\"\"\n S3-compatible storage backend for AWS S3 and MinIO.\n \n This class uses the minio Python package which is fully compatible with:\n - AWS S3 (cloud)\n - MinIO (self-hosted)\n - Any S3-compatible storage service\n \n For AWS S3:\n endpoint: \"s3.amazonaws.com\" or \"s3..amazonaws.com\"\n access_key: AWS Access Key ID\n secret_key: AWS Secret Access Key\n secure: True (use HTTPS)\n \n For MinIO:\n endpoint: \"localhost:9000\" or \"minio:9000\"\n access_key: MinIO access key (default: minioadmin)\n secret_key: MinIO secret key\n secure: False (or True for HTTPS)\n \"\"\"", + "context": "文件: lib/laddr/src/laddr/core/storage.py\n文档: S3-compatible storage backend for AWS S3 and MinIO.\n\nThis class uses the minio Python package which is fully compatible with:\n- AWS S3 (cloud)\n- MinIO (self-hosted)\n- Any S3-compatible storage service", + "related_elements": [ + "S3Storage" + ] + }, + { + "pattern_type": "class_implementation", + "description": "类 InMemoryStorage 的实现", + "code_snippet": "class InMemoryStorage:\n \"\"\"In-memory storage backend for testing.\"\"\"\n", + "context": "文件: lib/laddr/src/laddr/core/storage.py\n文档: In-memory storage backend for testing.", + "related_elements": [ + "InMemoryStorage" + ] + }, + { + "pattern_type": "class_implementation", + "description": "类 Job 的实现", + "code_snippet": "class Job(Base):\n \"\"\"Job execution record (legacy terminology).\"\"\"\n\n __tablename__ = \"jobs\"\n\n job_id = Column(String(36), primary_key=True)\n pipeline_name = Column(String(255), nullable=False)\n status = Column(String(50), default=\"pending\") # pending, running, completed, failed\n inputs = Column(JSON, nullable=False)\n outputs = Column(JSON, nullable=True)\n created_at = Column(DateTime, default=datetime.utcnow)\n completed_at = Column(DateTime, nullable=True)", + "context": "文件: lib/laddr/src/laddr/core/database.py\n文档: Job execution record (legacy terminology).", + "related_elements": [ + "Job" + ] + }, + { + "pattern_type": "class_implementation", + "description": "�� PromptExecution 的实现", + "code_snippet": "class PromptExecution(Base):\n \"\"\"Prompt execution record (new terminology for pipeline/job).\"\"\"\n\n __tablename__ = \"prompt_executions\"\n\n prompt_id = Column(String(36), primary_key=True)\n prompt_name = Column(String(255), nullable=False) # Agent/prompt name\n status = Column(String(50), default=\"pending\") # pending, running, completed, failed\n inputs = Column(JSON, nullable=False)\n outputs = Column(JSON, nullable=True)\n created_at = Column(DateTime, default=datetime.utcnow)\n completed_at = Column(DateTime, nullable=True)", + "context": "文件: lib/laddr/src/laddr/core/database.py\n文档: Prompt execution record (new terminology for pipeline/job).", + "related_elements": [ + "PromptExecution" + ] + }, + { + "pattern_type": "class_implementation", + "description": "类 Trace 的实现", + "code_snippet": "class Trace(Base):\n \"\"\"Trace event for observability with hierarchical span support.\"\"\"\n\n __tablename__ = \"traces\"\n\n id = Column(Integer, primary_key=True, autoincrement=True)\n job_id = Column(String(36), nullable=False, index=True)\n agent_name = Column(String(255), nullable=False, index=True)\n event_type = Column(String(100), nullable=False) # task_start, task_complete, tool_call, etc.\n parent_id = Column(Integer, nullable=True, index=True) # For hierarchical traces (span parent)\n payload = Column(JSON, nullable=False)\n timestamp = Column(DateTime, default=datetime.utcnow, index=True)", + "context": "文件: lib/laddr/src/laddr/core/database.py\n文档: Trace event for observability with hierarchical span support.", + "related_elements": [ + "Trace" + ] + }, + { + "pattern_type": "class_implementation", + "description": "类 Memory 的实现", + "code_snippet": "class Memory(Base):\n \"\"\"Agent memory storage.\"\"\"\n\n __tablename__ = \"memory\"\n\n id = Column(Integer, primary_key=True, autoincrement=True)\n agent_name = Column(String(255), nullable=False, index=True)\n job_id = Column(String(36), nullable=True, index=True) # Optional: job-specific memory\n key = Column(String(255), nullable=False)\n value = Column(JSON, nullable=False)\n updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)", + "context": "文件: lib/laddr/src/laddr/core/database.py\n文档: Agent memory storage.", + "related_elements": [ + "Memory" + ] + }, + { + "pattern_type": "class_implementation", + "description": "类 AgentRegistry 的实现", + "code_snippet": "class AgentRegistry(Base):\n \"\"\"Agent registration and metadata.\"\"\"\n\n __tablename__ = \"agent_registry\"\n\n agent_name = Column(String(255), primary_key=True)\n meta = Column(JSON, nullable=False) # role, goal, tools, status, host_url, etc.\n last_seen = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)", + "context": "文件: lib/laddr/src/laddr/core/database.py\n文档: Agent registration and metadata.", + "related_elements": [ + "AgentRegistry" + ] + }, + { + "pattern_type": "class_implementation", + "description": "类 DatabaseService 的实现", + "code_snippet": "class DatabaseService:\n \"\"\"\n High-level database API.\n \n Provides methods for job management, tracing, memory, and agent registry.\n Supports both Postgres and SQLite.\n \"\"\"\n", + "context": "文件: lib/laddr/src/laddr/core/database.py\n文档: High-level database API.\n\nProvides methods for job management, tracing, memory, and agent registry.\nSupports both Postgres and SQLite.", + "related_elements": [ + "DatabaseService" + ] + }, + { + "pattern_type": "class_implementation", + "description": "类 TaskMessage 的实现", + "code_snippet": "class TaskMessage:\n \"\"\"Message sent to an agent's task queue.\"\"\"\n\n task_id: str\n agent_name: str\n payload: dict[str, Any]\n created_at: float = field(default_factory=time.time)\n", + "context": "文件: lib/laddr/src/laddr/core/message_bus.py\n文档: Message sent to an agent's task queue.", + "related_elements": [ + "TaskMessage" + ] + }, + { + "pattern_type": "class_implementation", + "description": "类 ResponseMessage 的实现", + "code_snippet": "class ResponseMessage:\n \"\"\"Response from an agent after processing a task.\"\"\"\n\n task_id: str\n status: str # \"success\" | \"error\" | \"pending\"\n result: dict[str, Any] | None = None\n error: str | None = None\n created_at: float = field(default_factory=time.time)\n", + "context": "文件: lib/laddr/src/laddr/core/message_bus.py\n文档: Response from an agent after processing a task.", + "related_elements": [ + "ResponseMessage" + ] + }, + { + "pattern_type": "class_implementation", + "description": "类 RedisBus 的实���", + "code_snippet": "class RedisBus:\n \"\"\"\n Redis-based message bus for production use.\n \n Uses Redis Streams for task queues and pub/sub for responses.\n Agents are registered in a hash with metadata and heartbeats.\n \"\"\"\n", + "context": "文件: lib/laddr/src/laddr/core/message_bus.py\n文档: Redis-based message bus for production use.\n\nUses Redis Streams for task queues and pub/sub for responses.\nAgents are registered in a hash with metadata and heartbeats.", + "related_elements": [ + "RedisBus" + ] + }, + { + "pattern_type": "class_implementation", + "description": "类 MemoryBus 的实现", + "code_snippet": "class MemoryBus:\n \"\"\"\n In-memory message bus for testing and quick local dev.\n \n Not suitable for production or distributed systems.\n \"\"\"\n", + "context": "文件: lib/laddr/src/laddr/core/message_bus.py\n文档: In-memory message bus for testing and quick local dev.\n\nNot suitable for production or distributed systems.", + "related_elements": [ + "MemoryBus" + ] + }, + { + "pattern_type": "class_implementation", + "description": "类 KafkaBus 的实现", + "code_snippet": "class KafkaBus:\n \"\"\"\n Kafka-based message bus (experimental / enterprise option).\n\n Uses topics per agent: laddr.tasks.\n Responses published to: laddr.responses\n Agent registry is memory-only in this minimal implementation.\n \"\"\"\n", + "context": "文件: lib/laddr/src/laddr/core/message_bus.py\n文档: Kafka-based message bus (experimental / enterprise option).\n\nUses topics per agent: laddr.tasks.\nResponses published to: laddr.responses\nAgent registry is memory-only in this minimal imple", + "related_elements": [ + "KafkaBus" + ] + }, + { + "pattern_type": "class_implementation", + "description": "类 TaskDelegationTool 的实现", + "code_snippet": "class TaskDelegationTool:\n \"\"\"Tool for delegating tasks to other agents.\"\"\"\n", + "context": "文件: lib/laddr/src/laddr/core/system_tools.py\n文档: Tool for delegating tasks to other agents.", + "related_elements": [ + "TaskDelegationTool" + ] + }, + { + "pattern_type": "class_implementation", + "description": "类 ArtifactStorageTool 的实现", + "code_snippet": "class ArtifactStorageTool:\n \"\"\"Tool for storing and retrieving large data artifacts.\n\n Backward-compatible retrieval:\n - Old style: by artifact_id and artifact_type (stored under default artifact bucket)\n - New style: by bucket+key (e.g., offloaded response pointers)\n \"\"\"\n", + "context": "文件: lib/laddr/src/laddr/core/system_tools.py\n文档: Tool for storing and retrieving large data artifacts.\n\nBackward-compatible retrieval:\n- Old style: by artifact_id and artifact_type (stored under default artifact bucket)\n- New style: by bucket+key (e", + "related_elements": [ + "ArtifactStorageTool" + ] + }, + { + "pattern_type": "class_implementation", + "description": "类 ParallelDelegationTool 的实现", + "code_snippet": "class ParallelDelegationTool:\n \"\"\"Tool for delegating multiple tasks in parallel (sharding/fan-out pattern).\"\"\"\n", + "context": "文件: lib/laddr/src/laddr/core/system_tools.py\n文档: Tool for delegating multiple tasks in parallel (sharding/fan-out pattern).", + "related_elements": [ + "ParallelDelegationTool" + ] + }, + { + "pattern_type": "class_implementation", + "description": "类 NoOpLLM 的实现", + "code_snippet": "class NoOpLLM:\n \"\"\"\n No-op LLM backend that echoes prompts.\n \n Useful for testing and local dev without API keys.\n \"\"\"\n\n async def generate(self, prompt: str, system: str | None = None, **kwargs) -> str:\n \"\"\"Echo the prompt as response.\"\"\"\n return f\"[NoOpLLM Echo] Prompt: {prompt[:100]}...\"", + "context": "文件: lib/laddr/src/laddr/core/llm.py\n文档: No-op LLM backend that echoes prompts.\n\nUseful for testing and local dev without API keys.", + "related_elements": [ + "NoOpLLM" + ] + }, + { + "pattern_type": "class_implementation", + "description": "类 OpenAILLM 的实现", + "code_snippet": "class OpenAILLM:\n \"\"\"OpenAI LLM backend.\"\"\"\n", + "context": "文件: lib/laddr/src/laddr/core/llm.py\n文档: OpenAI LLM backend.", + "related_elements": [ + "OpenAILLM" + ] + }, + { + "pattern_type": "class_implementation", + "description": "类 AnthropicLLM 的实现", + "code_snippet": "class AnthropicLLM:\n \"\"\"Anthropic LLM backend.\"\"\"\n", + "context": "文件: lib/laddr/src/laddr/core/llm.py\n文档: Anthropic LLM backend.", + "related_elements": [ + "AnthropicLLM" + ] + }, + { + "pattern_type": "class_implementation", + "description": "类 GeminiLLM 的实现", + "code_snippet": "class GeminiLLM:\n \"\"\"Google Gemini LLM backend.\"\"\"\n", + "context": "文件: lib/laddr/src/laddr/core/llm.py\n文档: Google Gemini LLM backend.", + "related_elements": [ + "GeminiLLM" + ] + }, + { + "pattern_type": "class_implementation", + "description": "类 GroqLLM 的实现", + "code_snippet": "class GroqLLM:\n \"\"\"Groq LLM backend (ultra-fast inference).\"\"\"\n", + "context": "文件: lib/laddr/src/laddr/core/llm.py\n文档: Groq LLM backend (ultra-fast inference).", + "related_elements": [ + "GroqLLM" + ] + }, + { + "pattern_type": "class_implementation", + "description": "类 GrokLLM 的实现", + "code_snippet": "class GrokLLM:\n \"\"\"xAI Grok LLM backend - uses native HTTP API.\"\"\"\n", + "context": "文件: lib/laddr/src/laddr/core/llm.py\n文档: xAI Grok LLM backend - uses native HTTP API.", + "related_elements": [ + "GrokLLM" + ] + }, + { + "pattern_type": "class_implementation", + "description": "类 HTTPLLM 的实现", + "code_snippet": "class HTTPLLM:\n \"\"\"Generic HTTP LLM adapter expecting a simple JSON API.\n\n It POSTs to endpoint with body: {\"prompt\": str, \"system\": str | null, \"params\": {...}}\n and expects a JSON response with {\"text\": str}.\n \"\"\"\n", + "context": "文件: lib/laddr/src/laddr/core/llm.py\n文档: Generic HTTP LLM adapter expecting a simple JSON API.\n\nIt POSTs to endpoint with body: {\"prompt\": str, \"system\": str | null, \"params\": {...}}\nand expects a JSON response with {\"text\": str}.", + "related_elements": [ + "HTTPLLM" + ] + }, + { + "pattern_type": "class_implementation", + "description": "类 OllamaLLM 的实现", + "code_snippet": "class OllamaLLM:\n \"\"\"Local Ollama LLM backend (talks to a local Ollama HTTP server).\n\n Default base URL: http://localhost:11434\n Expected model names: e.g. \"gemma2:2b\" (use LLM_MODEL or per-agent LLM_MODEL_)\n \"\"\"\n", + "context": "文件: lib/laddr/src/laddr/core/llm.py\n文档: Local Ollama LLM backend (talks to a local Ollama HTTP server).\n\nDefault base URL: http://localhost:11434\nExpected model names: e.g. \"gemma2:2b\" (use LLM_MODEL or per-agent LLM_MODEL_)", + "related_elements": [ + "OllamaLLM" + ] + }, + { + "pattern_type": "class_implementation", + "description": "类 ProjectConfigSchema 的实现", + "code_snippet": "class ProjectConfigSchema(BaseModel):\n \"\"\"Project configuration schema (laddr.yml).\"\"\"\n\n project: ProjectDetails = Field(..., description=\"Project details\")\n\n @classmethod", + "context": "文件: lib/laddr/src/laddr/cli/utils/config.py\n文档: Project configuration schema (laddr.yml).", + "related_elements": [ + "ProjectConfigSchema" + ] + }, + { + "pattern_type": "class_implementation", + "description": "类 ProjectDetails 的实现", + "code_snippet": "class ProjectDetails(BaseModel):\n \"\"\"Project details section.\"\"\"\n\n name: str = Field(..., description=\"Project name\")\n broker: str = Field(default=\"redis\", description=\"Message broker type\")\n database: str = Field(default=\"postgres\", description=\"Database type\")\n storage: str = Field(default=\"minio\", description=\"Storage backend\")\n tracing: bool = Field(default=True, description=\"Enable tracing\")\n metrics: bool = Field(default=True, description=\"Enable metrics\")\n agents: list[str] = Field(default_factory=list, description=\"List of agent names\")\n\n @field_validator(\"name\")\n @classmethod", + "context": "文件: lib/laddr/src/laddr/cli/utils/config.py\n文档: Project details section.", + "related_elements": [ + "ProjectDetails" + ] + }, + { + "pattern_type": "class_implementation", + "description": "类 TemplateRenderer 的实现", + "code_snippet": "class TemplateRenderer:\n \"\"\"Jinja2 template renderer for Laddr CLI.\"\"\"\n", + "context": "文件: lib/laddr/src/laddr/cli/utils/templates.py\n文档: Jinja2 template renderer for Laddr CLI.", + "related_elements": [ + "TemplateRenderer" + ] + }, + { + "pattern_type": "class_implementation", + "description": "类 LaddrError 的实现", + "code_snippet": "class LaddrError(Exception):\n \"\"\"Base exception for all Laddr CLI errors.\"\"\"\n", + "context": "文件: lib/laddr/src/laddr/cli/utils/errors.py\n文档: Base exception for all Laddr CLI errors.", + "related_elements": [ + "LaddrError" + ] + }, + { + "pattern_type": "class_implementation", + "description": "类 ProjectNotFoundError 的实现", + "code_snippet": "class ProjectNotFoundError(LaddrError):\n \"\"\"Raised when not in an Laddr project directory.\"\"\"\n", + "context": "文件: lib/laddr/src/laddr/cli/utils/errors.py\n文档: Raised when not in an Laddr project directory.", + "related_elements": [ + "ProjectNotFoundError" + ] + }, + { + "pattern_type": "class_implementation", + "description": "类 ProjectExistsError 的实现", + "code_snippet": "class ProjectExistsError(LaddrError):\n \"\"\"Raised when trying to create a project that already exists.\"\"\"\n", + "context": "文件: lib/laddr/src/laddr/cli/utils/errors.py\n文档: Raised when trying to create a project that already exists.", + "related_elements": [ + "ProjectExistsError" + ] + }, + { + "pattern_type": "class_implementation", + "description": "类 NestedProjectError 的实现", + "code_snippet": "class NestedProjectError(LaddrError):\n \"\"\"Raised when trying to create a nested project.\"\"\"\n", + "context": "文件: lib/laddr/src/laddr/cli/utils/errors.py\n文档: Raised when trying to create a nested project.", + "related_elements": [ + "NestedProjectError" + ] + }, + { + "pattern_type": "class_implementation", + "description": "类 InvalidConfigError 的实现", + "code_snippet": "class InvalidConfigError(LaddrError):\n \"\"\"Raised when configuration files are invalid.\"\"\"\n", + "context": "文件: lib/laddr/src/laddr/cli/utils/errors.py\n文档: Raised when configuration files are invalid.", + "related_elements": [ + "InvalidConfigError" + ] + }, + { + "pattern_type": "class_implementation", + "description": "类 DockerNotFoundError 的实现", + "code_snippet": "class DockerNotFoundError(LaddrError):\n \"\"\"Raised when Docker is not installed or not running.\"\"\"\n", + "context": "文件: lib/laddr/src/laddr/cli/utils/errors.py\n文档: Raised when Docker is not installed or not running.", + "related_elements": [ + "DockerNotFoundError" + ] + }, + { + "pattern_type": "class_implementation", + "description": "类 DockerComposeError 的实现", + "code_snippet": "class DockerComposeError(LaddrError):\n \"\"\"Raised when Docker Compose operations fail.\"\"\"\n", + "context": "文件: lib/laddr/src/laddr/cli/utils/errors.py\n文档: Raised when Docker Compose operations fail.", + "related_elements": [ + "DockerComposeError" + ] + }, + { + "pattern_type": "class_implementation", + "description": "类 AgentNotFoundError 的实现", + "code_snippet": "class AgentNotFoundError(LaddrError):\n \"\"\"Raised when an agent doesn't exist.\"\"\"\n", + "context": "文件: lib/laddr/src/laddr/cli/utils/errors.py\n文档: Raised when an agent doesn't exist.", + "related_elements": [ + "AgentNotFoundError" + ] + }, + { + "pattern_type": "class_implementation", + "description": "类 InvalidInputError 的实现", + "code_snippet": "class InvalidInputError(LaddrError):\n \"\"\"Raised when user input is invalid.\"\"\"\n", + "context": "文件: lib/laddr/src/laddr/cli/utils/errors.py\n文档: Raised when user input is invalid.", + "related_elements": [ + "InvalidInputError" + ] + }, + { + "pattern_type": "class_implementation", + "description": "类 FileGenerationError 的实现", + "code_snippet": "class FileGenerationError(LaddrError):\n \"\"\"Raised when file generation fails.\"\"\"\n", + "context": "文件: lib/laddr/src/laddr/cli/utils/errors.py\n文档: Raised when file generation fails.", + "related_elements": [ + "FileGenerationError" + ] + }, + { + "pattern_type": "class_implementation", + "description": "类 ServiceNotReadyError 的实现", + "code_snippet": "class ServiceNotReadyError(LaddrError):\n \"\"\"Raised when a service is not ready after startup.\"\"\"\n", + "context": "文件: lib/laddr/src/laddr/cli/utils/errors.py\n文档: Raised when a service is not ready after startup.", + "related_elements": [ + "ServiceNotReadyError" + ] + }, + { + "pattern_type": "class_implementation", + "description": "类 SubmitJobRequest 的实现", + "code_snippet": "class SubmitJobRequest(BaseModel):\n \"\"\"Request to submit a new job (legacy).\"\"\"\n pipeline_name: str\n inputs: dict[str, Any]", + "context": "文件: lib/laddr/src/laddr/api/main.py\n文档: Request to submit a new job (legacy).", + "related_elements": [ + "SubmitJobRequest" + ] + }, + { + "pattern_type": "class_implementation", + "description": "类 SubmitPromptRequest 的实现", + "code_snippet": "class SubmitPromptRequest(BaseModel):\n \"\"\"Request to submit a new prompt execution.\"\"\"\n prompt_name: str\n inputs: dict[str, Any]\n mode: str = \"single\" # \"single\" or \"sequential\"\n agents: list[str] | None = None # For sequential mode: ordered list of agents", + "context": "文件: lib/laddr/src/laddr/api/main.py\n文档: Request to submit a new prompt execution.", + "related_elements": [ + "SubmitPromptRequest" + ] + }, + { + "pattern_type": "class_implementation", + "description": "类 ReplayJobRequest 的实现", + "code_snippet": "class ReplayJobRequest(BaseModel):\n \"\"\"Request to replay a job.\"\"\"\n reexecute: bool = False", + "context": "文件: lib/laddr/src/laddr/api/main.py\n文档: Request to replay a job.", + "related_elements": [ + "ReplayJobRequest" + ] + }, + { + "pattern_type": "class_implementation", + "description": "类 AgentChatRequest 的实现", + "code_snippet": "class AgentChatRequest(BaseModel):\n \"\"\"Request to chat with an agent.\"\"\"\n message: str\n wait: bool = True\n timeout: int = 30", + "context": "文件: lib/laddr/src/laddr/api/main.py\n文档: Request to chat with an agent.", + "related_elements": [ + "AgentChatRequest" + ] + }, + { + "pattern_type": "class_implementation", + "description": "类 AgentInfo 的实现", + "code_snippet": "class AgentInfo(BaseModel):\n \"\"\"Agent information.\"\"\"\n name: str\n role: str\n goal: str\n status: str\n tools: list[str]\n last_seen: str | None = None\n trace_count: int = 0\n last_executed: str | None = None", + "context": "文件: lib/laddr/src/laddr/api/main.py\n文档: Agent information.", + "related_elements": [ + "AgentInfo" + ] + }, + { + "pattern_type": "class_implementation", + "description": "类 EventThrottler 的实现", + "code_snippet": "class EventThrottler:\n \"\"\"Throttle WebSocket events to avoid overwhelming clients.\"\"\"\n", + "context": "文件: lib/laddr/src/laddr/api/main.py\n文档: Throttle WebSocket events to avoid overwhelming clients.", + "related_elements": [ + "EventThrottler" + ] + }, + { + "pattern_type": "function_implementation", + "description": "function groq 的实现和用法", + "code_snippet": "def groq(model: str | None = None, temperature: float | None = None) -> _LLMWithDefaults:\n \"\"\"\n Create a Groq LLM backend instance.\n \n Args:\n model: Model name (default: llama-3.3-70b-versatile)\n temperature: Generation temperature\n \n Example:\n llm = groq(model=\"llama-3.3-70b-versatile\", temperature=0.5)\n \"\"\"\n backend = GroqLLM(api_key=None, model=model)\n defaults: Dict[str, Any] = {}\n if temperature is not None:\n defaults[\"temperature\"] = temperature\n if model is not None:\n defaults[\"model\"] = model\n return _LLMWithDefaults(backend, defaults)", + "context": "文件: lib/laddr/src/laddr/llms.py\n参数: model, temperature\n调用者: 无", + "related_elements": [ + "groq" + ] + }, + { + "pattern_type": "function_implementation", + "description": "function grok 的实现和用法", + "code_snippet": "def grok(model: str | None = None, temperature: float | None = None) -> _LLMWithDefaults:\n \"\"\"\n Create a xAI Grok LLM backend instance.\n \n Args:\n model: Model name (default: grok-beta)\n temperature: Generation temperature\n \n Example:\n llm = grok(model=\"grok-beta\", temperature=0.7)\n \"\"\"\n backend = GrokLLM(api_key=None, model=model)\n defaults: Dict[str, Any] = {}\n if temperature is not None:\n defaults[\"temperature\"] = temperature\n if model is not None:\n defaults[\"model\"] = model\n return _LLMWithDefaults(backend, defaults)", + "context": "文件: lib/laddr/src/laddr/llms.py\n参数: model, temperature\n调用者: 无", + "related_elements": [ + "grok" + ] + }, + { + "pattern_type": "function_implementation", + "description": "function ollama 的实现和用法", + "code_snippet": "def ollama(model: str | None = None, temperature: float | None = None, base_url: str | None = None) -> _LLMWithDefaults:\n \"\"\"Create a local Ollama backend instance.\n\n Example:\n llm = ollama(model=\"gemma2:2b\", base_url=\"http://localhost:11434\")\n \"\"\"\n backend = OllamaLLM(base_url=base_url, model=model)\n defaults: Dict[str, Any] = {}\n if temperature is not None:\n defaults[\"temperature\"] = temperature\n if model is not None:\n defaults[\"model\"] = model\n return _LLMWithDefaults(backend, defaults)", + "context": "文件: lib/laddr/src/laddr/llms.py\n参数: model, temperature, base_url\n调用者: 无", + "related_elements": [ + "ollama" + ] + }, + { + "pattern_type": "function_implementation", + "description": "function Agent 的实现和用法", + "code_snippet": "def Agent(\n *,\n name: str,\n role: str,\n goal: str,\n backstory: str | None = None,\n tools: \"ToolRegistry | list[object] | None\" = None,\n llm: object | None = None,\n queue: object | None = None,\n instructions: str | None = None,\n is_coordinator: bool | None = None,\n available_agents: list[str] | None = None,\n max_iterations: int | None = None, # Maximum autonomous iterations before forced finish\n max_tool_calls: int | None = None, # Maximum successful tool calls before forced finish\n max_retries: int | None = None, # reserved for future behavior\n timeout: int | None = None, # reserved for future behavior\n trace_enabled: bool = True, # Enable/disable tracing for this agent\n trace_mask: list[str] | set[str] | None = None, # Event types to exclude from traces\n):\n \"\"\"Factory for user-friendly Agent(...) syntax.\n\n Creates a CoreAgent instance under the hood using AgentConfig and LaddrConfig.\n Unrecognized parameters like max_retries/timeout are currently accepted for\n forward-compatibility but not used directly.\n \"\"\"\n cfg = LaddrConfig()\n a_cfg = AgentConfig(\n name=name,\n role=role,\n goal=goal,\n backstory=backstory,\n max_iterations=max_iterations if max_iterations is not None else 5,\n )\n agent = CoreAgent(\n a_cfg,\n cfg,\n tools=tools, # CoreAgent accepts ToolRegistry or list of callables\n llm=llm,\n queue=queue,\n instructions=instructions,\n is_coordinator=is_coordinator,\n available_agents=available_agents,\n )\n # Set tracing configuration directly on instance\n agent._trace_enabled = bool(trace_enabled)\n agent._trace_mask = set(trace_mask or [])\n # Set max_tool_calls limit if specified\n if max_tool_calls is not None:\n agent._max_tool_calls = max_tool_calls\n return agent", + "context": "文件: lib/laddr/src/laddr/__init__.py\n参数: \n调用者: 无", + "related_elements": [ + "Agent" + ] + }, + { + "pattern_type": "function_implementation", + "description": "method Tool.validate_inputs 的实现和用法", + "code_snippet": " def validate_inputs(self, inputs: dict[str, Any]) -> dict[str, Any]:\n \"\"\"\n Validate inputs against input_model if available.\n \n Returns validated inputs or raises ValidationError.\n \"\"\"\n if self.input_model is None:\n return inputs\n\n if BaseModel and issubclass(self.input_model, BaseModel):\n # Pydantic validation\n validated = self.input_model(**inputs)\n return validated.model_dump()\n\n # No validation available\n return inputs", + "context": "文件: lib/laddr/src/laddr/core/tooling.py\n参数: self, inputs\n调用者: Tool.invoke", + "related_elements": [ + "Tool.validate_inputs", + "Tool.invoke" + ] + }, + { + "pattern_type": "function_implementation", + "description": "method Tool.invoke 的实现和用法", + "code_snippet": " def invoke(self, **kwargs) -> Any:\n \"\"\"\n Invoke the tool with validated inputs.\n \n Returns tool execution result.\n \"\"\"\n validated = self.validate_inputs(kwargs)\n return self.func(**validated)", + "context": "文件: lib/laddr/src/laddr/core/tooling.py\n参数: self\n调用者: run_dev_alias", + "related_elements": [ + "Tool.invoke", + "run_dev_alias" + ] + }, + { + "pattern_type": "function_implementation", + "description": "method ToolRegistry.register 的实现和用法", + "code_snippet": " def register(self, tool_obj: Tool | Callable, name: str | None = None, aliases: list[str] | None = None) -> None:\n \"\"\"\n Register a tool with optional aliases.\n \n Args:\n tool_obj: Tool instance or callable to register\n name: Optional explicit name (overrides tool_obj.name)\n aliases: Optional list of alias names that resolve to this tool\n \"\"\"\n # Determine the canonical name and tool object\n if isinstance(tool_obj, Tool):\n tool = tool_obj\n canonical_name = name or tool.name\n # Update the tool's name if explicit name provided\n if name and name != tool.name:\n tool = Tool(name=name, func=tool.func, description=tool.description)\n elif hasattr(tool_obj, \"__laddr_tool__\"):\n tool = tool_obj.__laddr_tool__\n canonical_name = name or tool.name\n # Update the tool's name if explicit name provided\n if name and name != tool.name:\n tool = Tool(name=name, func=tool.func, description=tool.description)\n elif callable(tool_obj):\n # Auto-wrap callable without decorator\n canonical_name = name or tool_obj.__name__\n tool = Tool(\n name=canonical_name,\n func=tool_obj,\n description=(tool_obj.__doc__ or \"\").strip().split(\"\\n\")[0]\n )\n else:\n raise ValueError(f\"Cannot register {tool_obj} as a tool\")\n\n # Register the tool under its canonical name\n self._tools[canonical_name] = tool\n\n # Register aliases if provided\n if aliases:\n for alias in aliases:\n self._aliases[alias] = canonical_name", + "context": "文件: lib/laddr/src/laddr/core/tooling.py\n参数: self, tool_obj, name, aliases\n调用者: discover_tools, bind_tools, Agent.__init__", + "related_elements": [ + "ToolRegistry.register", + "discover_tools", + "bind_tools", + "Agent.__init__" + ] + }, + { + "pattern_type": "function_implementation", + "description": "method ToolRegistry.get 的实现和用法", + "code_snippet": " def get(self, name: str) -> Tool | None:\n \"\"\"\n Get a tool by name or alias.\n \n Args:\n name: Tool name (canonical or alias)\n \n Returns:\n Tool instance or None if not found\n \"\"\"\n # Check if name is an alias first\n canonical_name = self._aliases.get(name, name)\n return self._tools.get(canonical_name)", + "context": "文件: lib/laddr/src/laddr/core/tooling.py\n参数: self, name\n调用者: ToolRegistry.get, tool, create_tool_schema", + "related_elements": [ + "ToolRegistry.get", + "ToolRegistry.get", + "tool", + "create_tool_schema" + ] + }, + { + "pattern_type": "function_implementation", + "description": "method ToolRegistry.list 的实现和用法", + "code_snippet": " def list(self) -> list[Tool]:\n \"\"\"List all registered tools (excludes aliases).\"\"\"\n return list(self._tools.values())", + "context": "文件: lib/laddr/src/laddr/core/tooling.py\n参数: self\n调用者: ToolRegistry.list, ToolRegistry.list_names, ToolRegistry.list_all_names", + "related_elements": [ + "ToolRegistry.list", + "ToolRegistry.list", + "ToolRegistry.list_names", + "ToolRegistry.list_all_names" + ] + }, + { + "pattern_type": "function_implementation", + "description": "method ToolRegistry.list_names 的实现和用法", + "code_snippet": " def list_names(self) -> list[str]:\n \"\"\"List all canonical tool names (excludes aliases).\"\"\"\n return list(self._tools.keys())", + "context": "文件: lib/laddr/src/laddr/core/tooling.py\n参数: self\n调用者: 无", + "related_elements": [ + "ToolRegistry.list_names" + ] + }, + { + "pattern_type": "function_implementation", + "description": "method ToolRegistry.list_all_names 的实现和用法", + "code_snippet": " def list_all_names(self) -> list[str]:\n \"\"\"List all names including aliases (for debugging).\"\"\"\n return list(self._tools.keys()) + list(self._aliases.keys())", + "context": "文件: lib/laddr/src/laddr/core/tooling.py\n参数: self\n调用者: 无", + "related_elements": [ + "ToolRegistry.list_all_names" + ] + }, + { + "pattern_type": "function_implementation", + "description": "method ToolRegistry.has 的实现和用法", + "code_snippet": " def has(self, name: str) -> bool:\n \"\"\"Check if a tool exists (checks both canonical names and aliases).\"\"\"\n return name in self._tools or name in self._aliases", + "context": "文件: lib/laddr/src/laddr/core/tooling.py\n参数: self, name\n调用者: Agent._register_system_tools", + "related_elements": [ + "ToolRegistry.has", + "Agent._register_system_tools" + ] + }, + { + "pattern_type": "function_implementation", + "description": "function tool 的实现和用法", + "code_snippet": "def tool(\n name: str | None = None,\n description: str | None = None,\n *,\n trace: bool = True,\n trace_mask: list[str] | None = None,\n parameters: dict | None = None,\n):\n \"\"\"\n Decorator to mark a function as an agent tool.\n \n Usage:\n @tool(name=\"web_search\", description=\"Search the web\")\n def search(query: str) -> dict:\n return {\"results\": [...]}\n \n Or with Pydantic input model:\n class SearchInput(BaseModel):\n query: str\n limit: int = 10\n \n @tool()\n def search(inputs: SearchInput) -> dict:\n return {\"results\": [...]}\n \n The decorator attaches metadata to the function as __laddr_tool__.\n \"\"\"\n def decorator(func: Callable) -> Callable:\n # Infer name from function if not provided\n tool_name = name or func.__name__\n\n # Infer description from docstring if not provided\n tool_description = description or (func.__doc__ or \"\").strip().split(\"\\n\")[0]\n\n # Try to extract input model from type hints\n input_model = None\n try:\n hints = get_type_hints(func)\n # Check if first parameter has a Pydantic BaseModel hint\n params = list(inspect.signature(func).parameters.values())\n if params and BaseModel:\n first_param_hint = hints.get(params[0].name)\n if first_param_hint and inspect.isclass(first_param_hint) and issubclass(first_param_hint, BaseModel):\n input_model = first_param_hint\n except Exception:\n pass\n\n # Attach metadata\n func.__laddr_tool__ = Tool(\n name=tool_name,\n func=func,\n description=tool_description,\n input_model=input_model,\n parameters_schema=parameters,\n trace_enabled=bool(trace),\n trace_mask=set(trace_mask or []),\n )\n\n return func\n\n return decorator", + "context": "文件: lib/laddr/src/laddr/core/tooling.py\n参数: name, description\n调用者: 无", + "related_elements": [ + "tool" + ] + }, + { + "pattern_type": "function_implementation", + "description": "function discover_tools 的实现和用法", + "code_snippet": "def discover_tools(agent_name: str) -> ToolRegistry:\n \"\"\"\n Auto-discover tools for an agent from agents..tools package.\n \n Scans for:\n 1. Functions decorated with @tool\n 2. Functions named 'run' (fallback)\n \n Args:\n agent_name: Name of the agent (e.g., \"researcher\")\n \n Returns:\n ToolRegistry with discovered tools\n \"\"\"\n registry = ToolRegistry()\n\n try:\n # Import the tools package\n tools_module = importlib.import_module(f\"agents.{agent_name}.tools\")\n\n # First, scan the package module itself (for tools defined in __init__.py)\n for name, obj in inspect.getmembers(tools_module):\n if hasattr(obj, \"__laddr_tool__\"):\n registry.register(obj)\n elif name == \"run\" and callable(obj):\n registry.register(obj)\n\n # Then, scan all submodules in the tools package\n if hasattr(tools_module, \"__path__\"):\n for _, module_name, _ in pkgutil.iter_modules(tools_module.__path__):\n try:\n module = importlib.import_module(f\"agents.{agent_name}.tools.{module_name}\")\n\n for name, obj in inspect.getmembers(module):\n if hasattr(obj, \"__laddr_tool__\"):\n registry.register(obj)\n elif name == \"run\" and callable(obj):\n registry.register(obj)\n except Exception:\n continue\n except ImportError:\n pass\n\n return registry", + "context": "文件: lib/laddr/src/laddr/core/tooling.py\n参数: agent_name\n调用者: Agent.__init__", + "related_elements": [ + "discover_tools", + "Agent.__init__" + ] + }, + { + "pattern_type": "function_implementation", + "description": "function bind_tools 的实现和用法", + "code_snippet": "def bind_tools(agent_instance: Any, tools: list[str | Callable]) -> None:\n \"\"\"\n Bind tools to an agent instance for explicit, readable tool registration.\n \n Supports:\n - String names: auto-import from agents..tools.\n - Callables: register directly\n - Decorated functions: extract Tool metadata\n \n Usage:\n from agents.researcher.tools import web_search\n bind_tools(self, [web_search.run, \"summarize\"])\n \n Args:\n agent_instance: Agent instance with .tools registry\n tools: List of tool names (str) or callables\n \"\"\"\n if not hasattr(agent_instance, \"tools\"):\n raise AttributeError(\"Agent must have .tools ToolRegistry\")\n \n agent_name = agent_instance.config.name\n \n for tool_ref in tools:\n if isinstance(tool_ref, str):\n # Auto-import from agents..tools.\n try:\n module = importlib.import_module(f\"agents.{agent_name}.tools.{tool_ref}\")\n # Look for 'run' function or decorated tool\n tool_func = None\n if hasattr(module, \"run\"):\n tool_func = module.run\n else:\n # Find first decorated function\n for name, obj in inspect.getmembers(module):\n if hasattr(obj, \"__laddr_tool__\"):\n tool_func = obj\n break\n \n if tool_func:\n agent_instance.tools.register(tool_func)\n except ImportError as e:\n raise ImportError(f\"Could not import tool '{tool_ref}' for agent '{agent_name}': {e}\")\n elif callable(tool_ref):\n # Register callable directly\n agent_instance.tools.register(tool_ref)\n else:\n raise ValueError(f\"Tool must be string or callable, got {type(tool_ref)}\")", + "context": "文件: lib/laddr/src/laddr/core/tooling.py\n参数: agent_instance, tools\n调用者: 无", + "related_elements": [ + "bind_tools" + ] + }, + { + "pattern_type": "function_implementation", + "description": "function create_tool_schema 的实现和用法", + "code_snippet": "def create_tool_schema(tool: Tool) -> dict[str, Any]:\n \"\"\"\n Create a JSON schema for a tool (for LLM function calling).\n \n Returns OpenAI-style function schema.\n \"\"\"\n # If user provided an explicit parameters schema via decorator, honor it directly\n if getattr(tool, \"parameters_schema\", None):\n return {\n \"name\": tool.name,\n \"description\": tool.description,\n \"parameters\": tool.parameters_schema,\n }\n\n schema = {\n \"name\": tool.name,\n \"description\": tool.description,\n \"parameters\": {\n \"type\": \"object\",\n \"properties\": {},\n \"required\": []\n }\n }\n\n # Try to extract parameters from input_model or function signature\n if tool.input_model and BaseModel and issubclass(tool.input_model, BaseModel):\n # Use Pydantic schema\n model_schema = tool.input_model.model_json_schema()\n schema[\"parameters\"][\"properties\"] = model_schema.get(\"properties\", {})\n schema[\"parameters\"][\"required\"] = model_schema.get(\"required\", [])\n else:\n # Fallback: extract from function signature\n sig = inspect.signature(tool.func)\n for param_name, param in sig.parameters.items():\n if param_name in [\"self\", \"cls\"]:\n continue\n\n param_schema = {\"type\": \"string\"} # Default type\n\n # Try to infer type from annotation\n if param.annotation != inspect.Parameter.empty:\n if param.annotation == int:\n param_schema[\"type\"] = \"integer\"\n elif param.annotation == float:\n param_schema[\"type\"] = \"number\"\n elif param.annotation == bool:\n param_schema[\"type\"] = \"boolean\"\n elif param.annotation == list:\n param_schema[\"type\"] = \"array\"\n elif param.annotation == dict:\n param_schema[\"type\"] = \"object\"\n\n schema[\"parameters\"][\"properties\"][param_name] = param_schema\n\n if param.default == inspect.Parameter.empty:\n schema[\"parameters\"][\"required\"].append(param_name)\n\n return schema", + "context": "文件: lib/laddr/src/laddr/core/tooling.py\n参数: tool\n调用者: 无", + "related_elements": [ + "create_tool_schema" + ] + }, + { + "pattern_type": "function_implementation", + "description": "method BackendFactory.create_queue_backend 的实现和用法", + "code_snippet": " def create_queue_backend(self) -> QueueBackend:\n \"\"\"Create message queue backend based on config.\"\"\"\n import os as _os\n # Respect explicit env override first\n backend = _os.environ.get(\"QUEUE_BACKEND\", self.config.queue_backend)\n # Smart default: if user didn't explicitly opt into Redis (no env for\n # QUEUE_BACKEND and no REDIS_URL), then default to in-memory for a\n # frictionless local run.\n if backend == \"redis\" and (\"QUEUE_BACKEND\" not in _os.environ) and (\"REDIS_URL\" not in _os.environ):\n backend = \"memory\"\n\n if backend == \"redis\":\n from .message_bus import RedisBus\n bus = RedisBus(self.config.redis_url)\n elif backend == \"memory\":\n from .message_bus import MemoryBus\n if BackendFactory._memory_bus_singleton is None:\n BackendFactory._memory_bus_singleton = MemoryBus()\n bus = BackendFactory._memory_bus_singleton\n elif backend == \"kafka\":\n from .message_bus import KafkaBus\n if not self.config.kafka_bootstrap:\n raise ValueError(\"kafka_bootstrap is required for kafka queue_backend\")\n bus = KafkaBus(self.config.kafka_bootstrap)\n else:\n raise ValueError(f\"Unknown queue_backend: {backend}\")\n\n # Optionally enable large-response offload to storage\n try:\n if getattr(self.config, \"enable_large_response_storage\", True):\n print(f\"[STORAGE] Enabling large response storage (threshold={self.config.storage_threshold_kb} KB)\")\n storage = self.create_storage_backend()\n bus._storage = storage\n # Use new storage_bucket field, fallback to old minio_bucket for compatibility\n bucket = self.config.storage_bucket or self.config.minio_bucket or \"laddr\"\n bus._storage_bucket = bucket\n bus._storage_threshold_kb = self.config.storage_threshold_kb\n print(f\"[STORAGE] Configured: bucket={bucket}, threshold={self.config.storage_threshold_kb} KB\")\n except Exception as e:\n # Non-fatal: continue without offload\n print(f\"[STORAGE] Failed to enable storage: {e}\")\n import traceback\n traceback.print_exc()\n pass\n\n return bus", + "context": "文件: lib/laddr/src/laddr/core/config.py\n参数: self\n调用者: AgentRunner._start_inline_workers, Agent.__init__", + "related_elements": [ + "BackendFactory.create_queue_backend", + "AgentRunner._start_inline_workers", + "Agent.__init__" + ] + }, + { + "pattern_type": "function_implementation", + "description": "method BackendFactory.create_database_backend 的实现和用法", + "code_snippet": " def create_database_backend(self) -> DatabaseBackend:\n \"\"\"Create database backend based on config.\"\"\"\n from .database import DatabaseService\n import os as _os\n # Prefer env override if present, then config, else SQLite\n db_url = _os.environ.get(\"DATABASE_URL\") or self.config.database_url or \"sqlite:///laddr.db\"\n return DatabaseService(db_url)", + "context": "文件: lib/laddr/src/laddr/core/config.py\n参数: self\n调用者: AgentRunner.__init__, Agent.__init__", + "related_elements": [ + "BackendFactory.create_database_backend", + "AgentRunner.__init__", + "Agent.__init__" + ] + }, + { + "pattern_type": "function_implementation", + "description": "method BackendFactory.create_llm_backend 的实现和用法", + "code_snippet": " def create_llm_backend(self, override: str | None = None, model_override: str | None = None, agent_name: str | None = None) -> LLMBackend:\n \"\"\"Create LLM backend based on config.\n\n If `override` is provided it takes precedence over the global\n `LaddrConfig.llm_backend`. `model_override` can be provided to\n select a different model for an agent.\n \"\"\"\n backend_name = override or self.config.llm_backend\n model = model_override or self.config.llm_model\n\n if backend_name == \"noop\":\n from .llm import NoOpLLM\n return NoOpLLM()\n if backend_name == \"openai\":\n from .llm import OpenAILLM\n return OpenAILLM(self.config.openai_api_key, model, base_url=self.config.openai_base_url)\n if backend_name == \"anthropic\":\n from .llm import AnthropicLLM\n return AnthropicLLM(self.config.anthropic_api_key, model)\n if backend_name == \"gemini\":\n from .llm import GeminiLLM\n return GeminiLLM(self.config.gemini_api_key, model)\n if backend_name == \"groq\":\n from .llm import GroqLLM\n return GroqLLM(self.config.groq_api_key, model)\n if backend_name == \"grok\":\n from .llm import GrokLLM\n return GrokLLM(self.config.xai_api_key or self.config.grok_api_key, model)\n if backend_name == \"ollama\":\n # Local Ollama HTTP backend. Supports per-agent LLM_MODEL_\n from .llm import OllamaLLM\n # Resolve model: explicit override -> per-agent env -> config -> default\n resolved_model = model\n if not resolved_model and agent_name:\n resolved_model = os.environ.get(f\"LLM_MODEL_{agent_name.upper()}\") or os.environ.get(f\"LLM_MODEL_{agent_name.lower()}\")\n resolved_model = resolved_model or self.config.llm_model or \"gemma2:2b\"\n # Resolve base URL: per-agent LLM_BASE_URL_ -> OLLAMA_BASE_URL -> default\n base_url = None\n if agent_name:\n base_url = os.environ.get(f\"LLM_BASE_URL_{agent_name.upper()}\") or os.environ.get(f\"LLM_BASE_URL_{agent_name.lower()}\")\n base_url = base_url or os.environ.get(\"OLLAMA_BASE_URL\") or \"http://localhost:11434\"\n return OllamaLLM(base_url=base_url, model=resolved_model)", + "context": "文件: lib/laddr/src/laddr/core/config.py\n参数: self, override, model_override, agent_name\n调用者: Agent.__init__", + "related_elements": [ + "BackendFactory.create_llm_backend", + "Agent.__init__" + ] + }, + { + "pattern_type": "function_implementation", + "description": "method BackendFactory.create_cache_backend 的实现和用法", + "code_snippet": " def create_cache_backend(self) -> CacheBackend:\n \"\"\"Create cache backend based on config.\"\"\"\n if self.config.cache_backend == \"inmemory\":\n from .cache import InMemoryCache\n return InMemoryCache()\n if self.config.cache_backend == \"redis\":\n from .cache import RedisCache\n return RedisCache(self.config.redis_url)\n raise ValueError(f\"Unknown cache_backend: {self.config.cache_backend}\")", + "context": "文件: lib/laddr/src/laddr/core/config.py\n参数: self\n调用者: Agent.__init__", + "related_elements": [ + "BackendFactory.create_cache_backend", + "Agent.__init__" + ] + }, + { + "pattern_type": "function_implementation", + "description": "method BackendFactory.create_storage_backend 的实现和用法", + "code_snippet": " def create_storage_backend(self):\n \"\"\"\n Create S3-compatible storage backend (AWS S3, MinIO, or compatible).\n \n Supports backward compatibility with old minio_* field names.\n \"\"\"\n # Use new storage_* fields, fallback to old minio_* fields for backward compatibility\n endpoint = (\n self.config.storage_endpoint\n if self.config.storage_endpoint != \"localhost:9000\" or self.config.minio_endpoint is None\n else self.config.minio_endpoint or self.config.storage_endpoint\n )\n access_key = self.config.storage_access_key\n secret_key = self.config.storage_secret_key\n secure = self.config.storage_secure\n region = self.config.storage_region\n \n if endpoint:\n from .storage import S3Storage\n return S3Storage(\n endpoint=endpoint,\n access_key=access_key,\n secret_key=secret_key,\n secure=secure,\n region=region\n )\n # Fall back to in-memory storage if no storage configured\n from .storage import InMemoryStorage\n return InMemoryStorage()", + "context": "文件: lib/laddr/src/laddr/core/config.py\n参数: self\n调用者: BackendFactory.create_queue_backend, Agent.__init__", + "related_elements": [ + "BackendFactory.create_storage_backend", + "BackendFactory.create_queue_backend", + "Agent.__init__" + ] + }, + { + "pattern_type": "function_implementation", + "description": "function load_agents 的实现和用法", + "code_snippet": "def load_agents() -> Dict[str, Any]:\n \"\"\"Load agents dynamically from 'agents..handler'.\n\n Supports two patterns:\n 1) @actor-decorated classes named Agent (instantiated without args)\n 2) Module-level Agent instances (any variable bound to laddr.core.agent_runtime.Agent)\n \"\"\"\n discovered: Dict[str, Any] = {}\n try:\n agents_pkg = importlib.import_module(\"agents\")\n import pkgutil\n\n for m in pkgutil.iter_modules(agents_pkg.__path__):\n name = m.name\n try:\n mod = importlib.import_module(f\"agents.{name}.handler\")\n\n # Prefer class pattern: Agent\n # We intentionally do NOT instantiate here to avoid needing configs.\n # run-local has a fallback path that constructs the class with AgentConfig/LaddrConfig.\n # So we skip adding classes in this discovery step.\n # cls_name = f\"{name.capitalize()}Agent\"\n # AgentCls = getattr(mod, cls_name, None)\n # if inspect.isclass(AgentCls):\n # pass\n\n # Instance pattern: find any Agent instance in module globals\n for var_name, obj in vars(mod).items():\n if not inspect.isclass(obj) and callable(getattr(obj, \"handle\", None)):\n discovered[name] = obj\n break\n except Exception:\n continue\n except Exception:\n pass\n return discovered", + "context": "文件: lib/laddr/src/laddr/core/config.py\n参数: \n调用者: run_local", + "related_elements": [ + "load_agents", + "run_local" + ] + }, + { + "pattern_type": "function_implementation", + "description": "method AgentRunner.__init__ 的实现和用法", + "code_snippet": " def __init__(\n self,\n agent: Agent | None = None,\n env_config: LaddrConfig | None = None\n ):\n \"\"\"\n Initialize runner.\n \n \n \n Args:\n agent: Agent instance (created if None)\n env_config: Environment configuration\n \"\"\"\n self.agent = agent\n self.env_config = env_config or LaddrConfig()\n self.factory = BackendFactory(self.env_config)\n self.database = self.factory.create_database_backend()\n # Inline worker control\n self._inline_worker_task = None\n self._inline_worker_stop = None", + "context": "文件: lib/laddr/src/laddr/core/runtime_entry.py\n参数: self, agent, env_config\n调用者: LaddrError.__init__, ProjectNotFoundError.__init__, ProjectExistsError.__init__", + "related_elements": [ + "AgentRunner.__init__", + "LaddrError.__init__", + "ProjectNotFoundError.__init__", + "ProjectExistsError.__init__" + ] + }, + { + "pattern_type": "function_implementation", + "description": "method AgentRunner._discover_local_agents 的实现和用法", + "code_snippet": " def _discover_local_agents(self, exclude: list[str] | None = None) -> list[str]:\n \"\"\"Discover local agents under the 'agents' package for inline execution.\"\"\"\n names: list[str] = []\n exclude = set(exclude or [])\n try:\n agents_pkg = importlib.import_module(\"agents\")\n for m in pkgutil.iter_modules(agents_pkg.__path__):\n name = m.name\n if name in exclude:\n continue\n # Ensure handler exists\n with suppress(Exception):\n importlib.import_module(f\"agents.{name}.handler\")\n names.append(name)\n except Exception:\n pass\n return names", + "context": "文件: lib/laddr/src/laddr/core/runtime_entry.py\n参数: self, exclude\n调用者: 无", + "related_elements": [ + "AgentRunner._discover_local_agents" + ] + }, + { + "pattern_type": "function_implementation", + "description": "method AgentRunner._start_inline_workers 的实现和用法", + "code_snippet": " def _start_inline_workers(self, agent_names: list[str]) -> None:\n \"\"\"Start a background task that consumes tasks and runs target agents inline.\"\"\"\n import logging\n logger = logging.getLogger(__name__)\n logger.info(f\"Starting inline workers for agents: {agent_names}\")\n \n stop = asyncio.Event()\n self._inline_worker_stop = stop\n\n async def _loop():\n # Cache agent instances per name to avoid re-import overhead\n agent_cache: dict[str, Agent] = {}\n bus = self.factory.create_queue_backend()\n logger.info(f\"Inline worker loop started, listening for tasks...\")\n while not stop.is_set():\n progress = False\n for name in agent_names:\n try:\n tasks = await bus.consume_tasks(name, block_ms=200, count=5)\n except Exception:\n logger.exception(f\"Error consuming tasks for {name}\")\n tasks = []\n if not tasks:\n continue\n logger.info(f\"Inline worker received {len(tasks)} task(s) for {name}\")\n progress = True\n for data in tasks:\n try:\n task_id = data.get(\"task_id\")\n payload = data.get(\"payload\", {})\n logger.info(f\"Processing task {task_id} for {name}\")\n # Get or create agent instance\n if name not in agent_cache:\n try:\n mod = importlib.import_module(f\"agents.{name}.handler\")\n cls = getattr(mod, f\"{name.capitalize()}Agent\")\n except Exception as e:\n # Skip if cannot import\n logger.error(f\"Failed to import agent {name}: {e}\")\n continue\n agent_cfg = AgentConfig(\n name=getattr(cls, 'AGENT_NAME', name),\n role=getattr(cls, 'ROLE', 'Agent'),\n goal=getattr(cls, 'GOAL', 'Execute tasks')\n )\n inst: Agent = cls(agent_cfg, self.env_config)\n await inst.connect_bus()\n logger.info(f\"Created inline worker agent instance for {name}\")\n agent_cache[name] = inst\n inst = agent_cache[name]\n # Execute task\n result = await inst.handle(payload)\n logger.info(f\"Task {task_id} completed: {result.get('status', 'unknown')}\")\n # Publish response for waiter\n if task_id:\n with suppress(Exception):\n await bus.publish_response(task_id, result)\n logger.info(f\"Published response for task {task_id}\")\n except Exception:\n # On processing error, continue loop\n logger.exception(f\"Error processing task {data.get('task_id')} for {name}\")\n continue\n\n # Avoid busy loop if no progress\n if not progress:\n try:\n await asyncio.wait_for(stop.wait(), timeout=0.25)\n except asyncio.TimeoutError:\n pass\n\n self._inline_worker_task = asyncio.create_task(_loop())", + "context": "文件: lib/laddr/src/laddr/core/runtime_entry.py\n参数: self, agent_names\n调用者: 无", + "related_elements": [ + "AgentRunner._start_inline_workers" + ] + }, + { + "pattern_type": "function_implementation", + "description": "method AgentRunner.replay 的实现和用法", + "code_snippet": " def replay(\n self,\n job_id: str,\n reexecute: bool = False\n ) -> dict:\n \"\"\"\n Replay a previous job.\n \n Args:\n job_id: Job ID to replay\n reexecute: If True, re-execute the job; if False, return stored result\n \n Returns:\n Job result\n \"\"\"\n # Get stored result\n result = self.database.get_result(job_id)\n\n if not result:\n return {\n \"status\": \"error\",\n \"error\": f\"Job not found: {job_id}\"\n }\n\n if reexecute:\n # Re-execute with stored inputs\n inputs = result.get(\"inputs\", {})\n pipeline_name = result.get(\"pipeline_name\")\n\n # Run with same job_id\n return asyncio.run(self.run(inputs, agent_name=pipeline_name, job_id=job_id))\n # Return stored result\n return {\n \"job_id\": job_id,\n \"status\": result.get(\"status\"),\n \"result\": result.get(\"outputs\"),\n \"error\": result.get(\"error\"),\n \"pipeline_name\": result.get(\"pipeline_name\"),\n \"created_at\": result.get(\"created_at\"),\n \"completed_at\": result.get(\"completed_at\")\n }", + "context": "文件: lib/laddr/src/laddr/core/runtime_entry.py\n参数: self, job_id, reexecute\n调用者: replay_job", + "related_elements": [ + "AgentRunner.replay", + "replay_job" + ] + }, + { + "pattern_type": "function_implementation", + "description": "method InMemoryCache.get 的实现和用法", + "code_snippet": " def get(self, key: str) -> Any:\n \"\"\"Get value from cache.\"\"\"\n if key not in self._cache:\n return None\n\n value, expires_at = self._cache[key]\n\n # Check expiration\n if expires_at is not None and time.time() > expires_at:\n del self._cache[key]\n return None\n\n return value", + "context": "文件: lib/laddr/src/laddr/core/cache.py\n参数: self, key\n调用者: ToolRegistry.get, tool, create_tool_schema", + "related_elements": [ + "InMemoryCache.get", + "ToolRegistry.get", + "tool", + "create_tool_schema" + ] + }, + { + "pattern_type": "function_implementation", + "description": "method InMemoryCache.set 的实现和用法", + "code_snippet": " def set(self, key: str, value: Any, ttl: int | None = None) -> None:\n \"\"\"Set value in cache with optional TTL (seconds).\"\"\"\n expires_at = None\n if ttl is not None:\n expires_at = time.time() + ttl\n\n self._cache[key] = (value, expires_at)", + "context": "文件: lib/laddr/src/laddr/core/cache.py\n参数: self, key, value, ttl\n调用者: Agent, tool, decorator", + "related_elements": [ + "InMemoryCache.set", + "Agent", + "tool", + "decorator" + ] + }, + { + "pattern_type": "function_implementation", + "description": "method InMemoryCache.delete 的实现和用法", + "code_snippet": " def delete(self, key: str) -> None:\n \"\"\"Delete value from cache.\"\"\"\n if key in self._cache:\n del self._cache[key]", + "context": "文件: lib/laddr/src/laddr/core/cache.py\n参数: self, key\n调用者: 无", + "related_elements": [ + "InMemoryCache.delete" + ] + }, + { + "pattern_type": "function_implementation", + "description": "method InMemoryCache.clear 的实现和用法", + "code_snippet": " def clear(self) -> None:\n \"\"\"Clear all cache entries.\"\"\"\n self._cache.clear()", + "context": "文件: lib/laddr/src/laddr/core/cache.py\n参数: self\n调用者: InMemoryCache.clear, get_logger, EventThrottler.add", + "related_elements": [ + "InMemoryCache.clear", + "InMemoryCache.clear", + "get_logger", + "EventThrottler.add" + ] + }, + { + "pattern_type": "function_implementation", + "description": "method AgentMemory.put 的实现和用法", + "code_snippet": " def put(self, key: str, value: Any) -> None:\n \"\"\"Store a memory entry.\"\"\"\n self.database.memory_put(self.agent_name, key, value, self.job_id)", + "context": "文件: lib/laddr/src/laddr/core/agent_runtime.py\n参数: self, key, value\n调用者: 无", + "related_elements": [ + "AgentMemory.put" + ] + }, + { + "pattern_type": "function_implementation", + "description": "method AgentMemory.get 的实现和用法", + "code_snippet": " def get(self, key: str) -> Any:\n \"\"\"Retrieve a memory entry.\"\"\"\n return self.database.memory_get(self.agent_name, key, self.job_id)", + "context": "文件: lib/laddr/src/laddr/core/agent_runtime.py\n参数: self, key\n调用者: ToolRegistry.get, tool, create_tool_schema", + "related_elements": [ + "AgentMemory.get", + "ToolRegistry.get", + "tool", + "create_tool_schema" + ] + }, + { + "pattern_type": "function_implementation", + "description": "method AgentMemory.list 的实现和用法", + "code_snippet": " def list(self) -> dict[str, Any]:\n \"\"\"List all memory entries.\"\"\"\n return self.database.memory_list(self.agent_name, self.job_id)", + "context": "文件: lib/laddr/src/laddr/core/agent_runtime.py\n参数: self\n调用者: ToolRegistry.list, ToolRegistry.list_names, ToolRegistry.list_all_names", + "related_elements": [ + "AgentMemory.list", + "ToolRegistry.list", + "ToolRegistry.list_names", + "ToolRegistry.list_all_names" + ] + }, + { + "pattern_type": "function_implementation", + "description": "method Agent.__init__ 的实现和用法", + "code_snippet": " def __init__(\n self,\n config: AgentConfig,\n env_config: LaddrConfig | None = None,\n tools: ToolRegistry | list[Any] | None = None,\n *,\n llm: Any | None = None,\n queue: Any | None = None,\n instructions: str | None = None,\n is_coordinator: bool | None = None,\n available_agents: list[str] | None = None,\n ):\n \"\"\"\n Initialize agent.\n \n Args:\n config: Agent-specific configuration\n env_config: Environment configuration (loaded if None)\n tools: Tool registry (auto-discovered if None)\n \"\"\"\n self.config = config\n self.env_config = env_config or LaddrConfig()\n\n # Create backends (allow explicit overrides for llm and queue)\n self.factory = BackendFactory(self.env_config)\n # Queue override\n if queue is not None:\n try:\n # Direct bus instance (RedisBus/MemoryBus)\n if hasattr(queue, \"publish_task\") and hasattr(queue, \"consume_tasks\"):\n self.bus = queue\n # Wrapper with uri attribute (e.g., RedisQueue from laddr.queues)\n elif hasattr(queue, \"uri\"):\n from .message_bus import RedisBus\n self.bus = RedisBus(getattr(queue, \"uri\"))\n # String shorthand\n elif isinstance(queue, str) and queue.startswith(\"redis://\"):\n from .message_bus import RedisBus\n self.bus = RedisBus(queue)\n else:\n self.bus = self.factory.create_queue_backend()\n except Exception:\n self.bus = self.factory.create_queue_backend()\n else:\n self.bus = self.factory.create_queue_backend()\n\n self.database = self.factory.create_database_backend()\n\n # LLM override\n if llm is not None:\n self.llm = llm\n else:\n # Determine per-agent LLM selection in order of precedence:\n # 1) AgentConfig.llm_backend\n # 2) Environment variable LLM_BACKEND_\n # 3) Global LaddrConfig.llm_backend\n import os as _os\n # Build keys for environment overrides. Support multiple naming conventions:\n # 1) LLM_BACKEND_ / LLM_MODEL_\n # 2) _backend / _model (legacy/project-specific, often lowercase like \"researcher_model\")\n agent_name = (self.config.name or \"\")\n agent_name_key = agent_name.upper()\n env_key = f\"LLM_BACKEND_{agent_name_key}\"\n env_model_key = f\"LLM_MODEL_{agent_name_key}\"\n\n legacy_backend_key = f\"{agent_name}_backend\"\n legacy_model_key = f\"{agent_name}_model\"\n\n backend_override = None\n model_override = None\n\n try:\n # AgentConfig override if provided\n backend_override = getattr(self.config, \"llm_backend\", None) or None\n model_override = getattr(self.config, \"llm_model\", None) or None\n except Exception:\n backend_override = None\n model_override = None\n\n # Environment variables take precedence over agent config. Check multiple keys and case variants.\n try:\n # Prefer LLM_* names (upper or lower), then legacy agent-specific names\n env_backend = (\n _os.environ.get(env_key)\n or _os.environ.get(env_key.lower())\n or _os.environ.get(legacy_backend_key)\n or _os.environ.get(legacy_backend_key.upper())\n )\n env_model = (\n _os.environ.get(env_model_key)\n or _os.environ.get(env_model_key.lower())\n or _os.environ.get(legacy_model_key)\n or _os.environ.get(legacy_model_key.upper())\n )\n\n if env_backend:\n backend_override = env_backend\n if env_model:\n model_override = env_model\n except Exception:\n pass\n\n self.llm = self.factory.create_llm_backend(override=backend_override, model_override=model_override, agent_name=self.config.name)\n # Default LLM params (temperature, max_tokens, etc.) optionally provided by llm wrapper\n self._llm_params: dict = {}\n try:\n if hasattr(self.llm, \"default_params\") and isinstance(self.llm.default_params, dict): # type: ignore[attr-defined]\n self._llm_params = dict(self.llm.default_params) # type: ignore[attr-defined]\n except Exception:\n self._llm_params = {}\n self.cache_backend = self.factory.create_cache_backend()\n self.storage = self.factory.create_storage_backend()\n\n # Coordinator support (allow explicit override)\n # Set this BEFORE tool registration so _register_system_tools can check it\n self.is_coordinator = (\n bool(is_coordinator)\n if is_coordinator is not None\n else (getattr(self.env_config, \"has_coordinator\", False) and (\n self.config.name == getattr(self.env_config, \"coordinator_agent\", \"coordinator\")\n ))\n )\n self.coordinator_agent = (\n getattr(self.env_config, \"coordinator_agent\", \"coordinator\")\n if getattr(self.env_config, \"has_coordinator\", False)\n else None\n )\n # Available agents hint for prompts/planning\n self.available_agents_hint: list[str] = list(available_agents or [])\n # Agent-level instruction supplement\n self._extra_instructions: str | None = (instructions or None)\n\n # Tool registry - start with user tools, then add system tools\n if tools is None:\n self.tools = discover_tools(config.name)\n elif isinstance(tools, ToolRegistry):\n self.tools = tools\n else:\n # Accept list of callables and register\n from .tooling import ToolRegistry as _TR\n tr = _TR()\n for t in (tools or []):\n try:\n tr.register(t)\n except Exception:\n continue\n self.tools = tr\n\n # Add system tools for delegation and artifact storage\n # This is called AFTER is_coordinator is set\n self._register_system_tools()\n\n # Memory interface\n self.memory: AgentMemory | None = None\n\n # Current job context\n self.current_job_id: str | None = None\n\n # Tracing controls (configurable via @actor decorator class attributes)\n self._trace_enabled: bool = bool(getattr(self.__class__, \"TRACE_ENABLED\", True))\n mask = getattr(self.__class__, \"TRACE_MASK\", set()) or set()\n try:\n self._trace_mask: set[str] = set(mask)\n except Exception:\n self._trace_mask = set()\n\n # Heartbeat task\n self._heartbeat_task: asyncio.Task | None = None", + "context": "文件: lib/laddr/src/laddr/core/agent_runtime.py\n参数: self, config, env_config, tools\n调用者: LaddrError.__init__, ProjectNotFoundError.__init__, ProjectExistsError.__init__", + "related_elements": [ + "Agent.__init__", + "LaddrError.__init__", + "ProjectNotFoundError.__init__", + "ProjectExistsError.__init__" + ] + }, + { + "pattern_type": "function_implementation", + "description": "method Agent._discover_tool_overrides 的实现和用法", + "code_snippet": " def _discover_tool_overrides(self):\n \"\"\"\n Auto-discover and load tool override modules from the project.\n \n Searches common locations for tool override files:\n - ./tools/overrides/\n - ./custom_tools/\n - ./agent_tools/overrides/\n \n When override modules are imported, the @override_system_tool\n decorators will automatically register the overrides.\n \"\"\"\n import importlib.util\n import os\n from pathlib import Path\n \n # Get workspace root (where laddr.yml is located)\n workspace_root = Path.cwd()\n if hasattr(self, 'config') and hasattr(self.config, 'project_root'):\n workspace_root = Path(self.config.project_root)\n \n # Search paths for override modules\n search_paths = [\n workspace_root / \"tools\" / \"overrides\",\n workspace_root / \"custom_tools\",\n workspace_root / \"agent_tools\" / \"overrides\",\n ]\n \n for search_path in search_paths:\n if not search_path.exists() or not search_path.is_dir():\n continue\n \n # Find all .py files in this directory\n for py_file in search_path.glob(\"*.py\"):\n if py_file.name.startswith(\"_\"):\n continue # Skip __init__.py, etc.\n \n try:\n # Import the module - this triggers @override_system_tool decorators\n module_name = f\"tool_overrides.{py_file.stem}\"\n spec = importlib.util.spec_from_file_location(module_name, py_file)\n if spec and spec.loader:\n module = importlib.util.module_from_spec(spec)\n spec.loader.exec_module(module)\n logger.info(f\"✅ Loaded tool override module: {py_file.name}\")\n except Exception as e:\n logger.warning(f\"⚠️ Failed to load tool override module {py_file.name}: {e}\")", + "context": "文件: lib/laddr/src/laddr/core/agent_runtime.py\n参数: self\n调用者: Agent._register_system_tools", + "related_elements": [ + "Agent._discover_tool_overrides", + "Agent._register_system_tools" + ] + }, + { + "pattern_type": "function_implementation", + "description": "method Agent._register_system_tools 的实现和用法", + "code_snippet": " def _register_system_tools(self):\n \"\"\"Register built-in system tools for delegation and artifacts.\"\"\"\n from .system_tools import create_system_tools\n from .tooling import Tool as _Tool\n \n # First, discover any user-provided tool overrides\n self._discover_tool_overrides()\n\n # Pass self (agent instance) so system tools can access current_job_id\n system_tools = create_system_tools(self.bus, self.storage, agent=self)\n\n # Disallow any system tool that could bypass delegation safeguards\n reserved = {\"delegate\", \"publish\", \"publish_task\"}\n \n # Delegation tools - only register for coordinator agents\n delegation_tools = {\n \"system_delegate_task\",\n \"system_delegate_parallel\", \n \"delegate_parallel\",\n \"system_split_document\",\n \"split_document\"\n }\n\n for name, tool_data in system_tools.items():\n # Skip reserved/unsafe tool names\n if name in reserved:\n continue\n \n # Skip delegation tools for non-coordinator agents\n if name in delegation_tools and not self.is_coordinator:\n continue\n \n # Unpack tool data - can be either (func, aliases) or just func\n if isinstance(tool_data, tuple):\n func, aliases = tool_data\n else:\n func = tool_data\n aliases = []\n \n # Register each system tool with the explicit provided name,\n # not the callable's __name__, so both prefixed and alias names exist.\n if not self.tools.has(name):\n try:\n desc = (func.__doc__ or \"\").strip().split(\"\\n\")[0]\n tool = _Tool(name=name, func=func, description=desc)\n self.tools.register(tool, name=name, aliases=aliases)\n except Exception:\n # Fallback to direct registration\n self.tools.register(func, name=name, aliases=aliases)", + "context": "文件: lib/laddr/src/laddr/core/agent_runtime.py\n参数: self\n调用者: Agent.__init__", + "related_elements": [ + "Agent._register_system_tools", + "Agent.__init__" + ] + }, + { + "pattern_type": "function_implementation", + "description": "method Agent.plan 的实现和用法", + "code_snippet": " def plan(self, task: dict) -> list[dict]:\n \"\"\"\n Create execution plan for a task.\n \n Default implementation: simple single-step plan.\n Subclasses should override for custom planning logic.\n \n Args:\n task: Task inputs\n \n Returns:\n List of steps, where each step is a dict with:\n - action: \"tool\" | \"delegate\" | \"llm\" | \"custom\"\n - Additional fields depending on action type\n \"\"\"\n # Default: single LLM call step\n return [\n {\n \"action\": \"llm\",\n \"prompt\": f\"Task: {json.dumps(task)}\",\n \"system\": f\"You are {self.config.role}. Your goal: {self.config.goal}\"\n }\n ]", + "context": "文件: lib/laddr/src/laddr/core/agent_runtime.py\n参数: self, task\n调用者: 无", + "related_elements": [ + "Agent.plan" + ] + }, + { + "pattern_type": "function_implementation", + "description": "method Agent._tool_cache_key 的实现和用法", + "code_snippet": " def _tool_cache_key(self, name: str, params: dict) -> str:\n \"\"\"Generate cache key for tool call.\"\"\"\n param_str = json.dumps(params, sort_keys=True)\n key_data = f\"{name}:{param_str}\"\n return f\"tool:{hashlib.md5(key_data.encode()).hexdigest()}\"", + "context": "文件: lib/laddr/src/laddr/core/agent_runtime.py\n参数: self, name, params\n调用者: 无", + "related_elements": [ + "Agent._tool_cache_key" + ] + }, + { + "pattern_type": "function_implementation", + "description": "method Agent._trace 的实现和用法", + "code_snippet": " def _trace(self, job_id: str, agent_name: str, event_type: str, payload: dict) -> None:\n \"\"\"Append a trace if enabled and not masked.\"\"\"\n if not self._trace_enabled:\n return\n if event_type in self._trace_mask:\n return\n try:\n self.database.append_trace(job_id, agent_name, event_type, payload)\n except Exception:\n # Tracing must never break runtime\n pass", + "context": "文件: lib/laddr/src/laddr/core/agent_runtime.py\n参数: self, job_id, agent_name, event_type, payload\n调用者: 无", + "related_elements": [ + "Agent._trace" + ] + }, + { + "pattern_type": "function_implementation", + "description": "method Agent._build_autonomous_system_prompt 的实现和用法", + "code_snippet": " def _build_autonomous_system_prompt(\n self,\n available_tools: list[str],\n available_agents: list[dict],\n can_delegate: bool,\n ) -> str:\n \"\"\"Build system prompt for autonomous agent.\"\"\"\n # Optional: legacy prompt.md (kept for backward compatibility) or explicit instructions passed at construction\n custom_prompt = self._extra_instructions\n if not custom_prompt:\n try:\n prompt_path = os.path.join(os.getcwd(), \"agents\", self.config.name, \"prompt.md\")\n if os.path.isfile(prompt_path):\n with open(prompt_path, \"r\", encoding=\"utf-8\") as f:\n custom_prompt = f.read().strip()\n except Exception:\n custom_prompt = None\n\n tool_list = \"\\n\".join([f\"- {t}\" for t in available_tools]) if available_tools else \"None\"\n\n agent_list = \"None\"\n if can_delegate and available_agents:\n agent_list = \"\\n\".join(\n [\n f\"- {a['name']}: {a.get('role', 'N/A')} - {a.get('goal', 'N/A')}\"\n for a in available_agents\n ]\n )\n\n header = f\"You are {self.config.role}.\\nYour goal: {self.config.goal}\"\n\n if custom_prompt:\n header += \"\\n\\nAgent Policy (from prompt.md):\\n\" + custom_prompt\n\n system = f\"\"\"{header}\n\nYou act autonomously to complete the task. Always respond with a SINGLE JSON object only.\n\nCRITICAL: \n- Output ONLY the JSON object, no markdown code fences (no ```json), no explanatory text\n- Do NOT add extra fields like \"action\", \"reasoning\", or \"explanation\"\n- Follow the EXACT schema below\n\nSchema:\n{{\n \"type\": \"tool|delegate|finish\",\n \"tool\": \"\",\n \"params\": {{\"param1\": \"value1\"}},\n \"agent\": \"\",\n \"task\": \"\",\n \"timeout\": 60,\n \"answer\": \"\"\n}}\n\n## Available Tools:\n{tool_list}\n\nTo use a tool:\n{{\"type\":\"tool\",\"tool\":\"\",\"params\":{{\"param\":\"value\"}}}}\n\"\"\"\n\n if can_delegate:\n system += f\"\"\"\n## Available Agents (for delegation):\n{agent_list}\n\nTo delegate a task:\n{{\"type\":\"delegate\",\"agent\":\"\",\"task\":\"\",\"timeout\":90}}\n\n⚠️ CRITICAL DELEGATION RULES:\n- ONLY use agent names from the list above - do NOT invent names\n- If no listed agent matches your needs, use tools or finish instead\n- Each agent has specific expertise (role/goal) - choose the most appropriate one\n- Delegation is for complex subtasks that require specialized processing\n\n## Key System Tools:\n\n### Parallel Processing (for independent subtasks):\n{{\"type\":\"tool\",\"tool\":\"system_delegate_parallel\",\"params\":{{\"agent\":\"\",\"tasks\":[\"task1\",\"task2\",\"task3\"]}}}}\n- Use when you have multiple independent subtasks that can run simultaneously\n- All tasks execute in parallel and results are aggregated\n- Ideal for: processing multiple documents, analyzing multiple data points, gathering info from multiple sources\n\n### Document Splitting (for large content):\n{{\"type\":\"tool\",\"tool\":\"system_split_document\",\"params\":{{\"document\":\"\",\"num_chunks\":3}}}}\n- Use before parallel processing to divide large documents into manageable chunks\n- Returns list of chunks that can be passed to system_delegate_parallel\n\n### Artifact Storage (for sharing data):\n{{\"type\":\"tool\",\"tool\":\"system_store_artifact\",\"params\":{{\"data\":\"\",\"artifact_type\":\"result\",\"metadata\":{{\"description\":\"...\"}}}}}}\n- Store results, intermediate data, or large outputs for other agents to retrieve\n- Use when delegating and the subtask needs access to data you've gathered\n- Returns artifact_id which can be used for retrieval\n\n### Artifact Retrieval:\n{{\"type\":\"tool\",\"tool\":\"system_retrieve_artifact\",\"params\":{{\"artifact_id\":\"\",\"artifact_type\":\"result\"}}}}\n- Retrieve previously stored artifacts by artifact_id (returned from store_artifact)\n- Useful when building on work from previous steps or other agents\n - IMPORTANT: Do NOT attempt to retrieve artifacts using job_id or task_id. Only call\n `system_retrieve_artifact` when you have an explicit artifact_id (UUID)\n returned by a previous store_artifact call or delegation response.\n If a delegated agent returned its result inline (i.e., the response field contains the data),\n you do NOT need to call artifact retrieval — synthesize from the inline result instead.\n Calling artifact retrieval with job/task ids often fails and causes unnecessary retries.\n\"\"\"\n else:\n # Explicitly tell non-coordinator agents they CANNOT delegate\n system += \"\"\"\n⚠️ IMPORTANT: You are a specialist agent and CANNOT delegate tasks to other agents.\n- Use your available tools to complete the task yourself\n- DO NOT attempt to use system_delegate_task or any delegation tools\n- Focus on using your specialized tools to complete the task\n- Synthesize findings from your tool calls and finish with a comprehensive answer\n\n## Artifact Storage (ONLY for large results):\n⚠️ CRITICAL: DO NOT use artifact tools unless explicitly storing/retrieving YOUR OWN large data (>100KB).\n\n### When to store artifacts:\n- Your final result/answer exceeds 100KB in size\n- You need to save intermediate data for your own later use\n- Example: {{\"type\":\"tool\",\"tool\":\"system_store_artifact\",\"params\":{{\"data\":\"\",\"artifact_type\":\"result\"}}}}\n- Returns artifact_id for later retrieval\n\n### When to retrieve artifacts:\n- ONLY if you previously stored an artifact and have its artifact_id (UUID)\n- DO NOT try to retrieve artifacts you didn't create\n- DO NOT use random search terms or keys\n- Example: {{\"type\":\"tool\",\"tool\":\"system_retrieve_artifact\",\"params\":{{\"artifact_id\":\"\",\"artifact_type\":\"result\"}}}}\n\n⚠️ DO NOT use system_retrieve_artifact to search for data - use your domain tools instead!\n\"\"\"\n\n system += \"\"\"\nWhen you complete the task:\n{{\"type\":\"finish\",\"answer\":\"\"}}\n\n## Execution Guidelines:\n- Use tools strategically - avoid redundant calls for the same information\"\"\"\n\n if can_delegate:\n system += \"\"\"\n- Delegate complex subtasks to specialized agents when available\n- Use parallel processing for independent subtasks to improve efficiency\n- Store intermediate results as artifacts when they'll be reused\"\"\"\n else:\n system += \"\"\"\n- Make the most of your specialized tools - they are designed for your domain\n- Process information directly rather than attempting delegation\n- Synthesize findings from multiple tool calls when needed\"\"\"\n\n system += \"\"\"\n- Focus on quality synthesis over quantity of actions\n- Finish when you have sufficient information to provide a comprehensive answer\n\nThink step-by-step internally, but output ONLY the JSON action object. No explanatory text.\"\"\"\n\n\n return system", + "context": "文件: lib/laddr/src/laddr/core/agent_runtime.py\n参数: self, available_tools, available_agents, can_delegate\n调用者: 无", + "related_elements": [ + "Agent._build_autonomous_system_prompt" + ] + }, + { + "pattern_type": "function_implementation", + "description": "method Agent._build_autonomous_user_prompt 的实现和用法", + "code_snippet": " def _build_autonomous_user_prompt(\n self,\n task_description: str,\n history: list[dict],\n iteration: int\n ) -> str:\n \"\"\"Build user prompt with task and history.\"\"\"\n \n prompt = f\"TASK: {task_description}\\n\\n\"\n \n if history:\n prompt += \"HISTORY:\\n\"\n for i, entry in enumerate(history):\n if entry[\"action\"] == \"tool\":\n prompt += f\"{i+1}. Used tool '{entry['tool']}' with params {entry.get('params', {})}\\n\"\n if \"error\" in entry:\n prompt += f\" ERROR: {entry['error']}\\n\"\n else:\n result_str = str(entry.get('result', ''))[:500]\n prompt += f\" RESULT: {result_str}\\n\"\n \n elif entry[\"action\"] == \"delegate\":\n prompt += f\"{i+1}. Delegated to agent '{entry['agent']}': {entry.get('task', 'N/A')}\\n\"\n if \"error\" in entry:\n prompt += f\" ERROR: {entry['error']}\\n\"\n else:\n result_str = str(entry.get('result', ''))[:500]\n prompt += f\" RESULT: {result_str}\\n\"\n \n elif entry[\"action\"] == \"think\":\n prompt += f\"{i+1}. THOUGHT: {entry.get('thought', '')[:200]}\\n\"\n \n elif entry[\"action\"] == \"system_hint\":\n prompt += f\"\\n⚠️ SYSTEM: {entry.get('message', '')}\\n\\n\"\n \n prompt += \"\\n\"\n\n # If a delegation result is present in history, add a short SYSTEM hint\n # so the LLM prefers synthesizing and finishing over re-delegating the\n # same task. This reduces redundant delegations and wasted tool calls.\n has_delegate_result = False\n for entry in history:\n if entry.get(\"action\") == \"delegate\" and \"result\" in entry and entry.get(\"status\") in {\"completed\", \"success\", None}:\n has_delegate_result = True\n break\n\n if has_delegate_result:\n prompt += \"\\nSYSTEM_HINT: A delegated agent has returned a result above. \"\n prompt += \"Prefer synthesizing a final answer from that result and other history. \"\n prompt += \"Do NOT re-delegate the same task unless the returned result is clearly insufficient or explicitly requests further delegation.\\n\\n\"\n\n prompt += f\"What should you do next? (Iteration {iteration + 1})\\n\"\n\n return prompt", + "context": "文件: lib/laddr/src/laddr/core/agent_runtime.py\n参数: self, task_description, history, iteration\n调用者: 无", + "related_elements": [ + "Agent._build_autonomous_user_prompt" + ] + }, + { + "pattern_type": "function_implementation", + "description": "method Agent._format_history_for_synthesis 的实现和用法", + "code_snippet": " def _format_history_for_synthesis(self, history: list[dict]) -> str:\n \"\"\"Format execution history for final answer synthesis.\"\"\"\n formatted = []\n for i, entry in enumerate(history, 1):\n action = entry.get(\"action\", \"unknown\")\n if action == \"tool\":\n tool_name = entry.get(\"tool\", \"unknown\")\n result = entry.get(\"result\", entry.get(\"error\", \"No result\"))\n # Truncate large results\n result_str = str(result)[:500]\n formatted.append(f\"{i}. Tool '{tool_name}' returned: {result_str}\")\n elif action == \"delegate\":\n agent = entry.get(\"agent\", \"unknown\")\n result = entry.get(\"result\", entry.get(\"error\", \"No result\"))\n formatted.append(f\"{i}. Delegated to '{agent}', result: {result}\")\n \n return \"\\n\".join(formatted) if formatted else \"No information gathered yet.\"", + "context": "文件: lib/laddr/src/laddr/core/agent_runtime.py\n参数: self, history\n调用者: 无", + "related_elements": [ + "Agent._format_history_for_synthesis" + ] + }, + { + "pattern_type": "function_implementation", + "description": "method Agent._parse_autonomous_action 的实现和用法", + "code_snippet": " def _parse_autonomous_action(self, response: str) -> dict:\n \"\"\"Parse LLM response to extract action, preferring JSON format with text fallback.\"\"\"\n # 1) Try JSON extraction anywhere in the response\n try:\n start = response.find(\"{\")\n end = response.rfind(\"}\")\n if start != -1 and end != -1 and end > start:\n payload = response[start : end + 1]\n data = json.loads(payload)\n # Normalize keys\n norm = {str(k).lower(): v for k, v in data.items()}\n action_type = norm.get(\"type\") or norm.get(\"action\") or \"think\"\n action_type = str(action_type).lower()\n \n # Handle LLM putting tool name in \"action\" field instead of \"type\"\n # e.g., {\"action\": \"web_search\", \"tool\": \"web_search\", ...}\n if action_type not in [\"tool\", \"delegate\", \"finish\", \"think\"]:\n # LLM likely put the tool name in \"action\" field\n # Check if there's a \"tool\" field, or use \"action\" value as tool name\n if norm.get(\"tool\") or norm.get(\"params\"):\n # This looks like a tool call\n tool = norm.get(\"tool\") or action_type\n params = norm.get(\"params\") or {}\n return {\"type\": \"tool\", \"tool\": tool, \"params\": params}\n \n # Map common aliases\n if action_type == \"tool\":\n tool = norm.get(\"tool\") or norm.get(\"tool_name\")\n params = norm.get(\"params\") or {}\n return {\"type\": \"tool\", \"tool\": tool, \"params\": params}\n if action_type == \"delegate\":\n agent = norm.get(\"agent\") or norm.get(\"target\")\n task = norm.get(\"task\") or norm.get(\"query\") or norm.get(\"message\")\n timeout = norm.get(\"timeout\") or 60\n return {\"type\": \"delegate\", \"agent\": agent, \"task\": task, \"timeout\": timeout}\n if action_type == \"finish\":\n answer = norm.get(\"answer\") or norm.get(\"result\") or response\n return {\"type\": \"finish\", \"answer\": answer}\n # Default think\n return {\"type\": \"think\"}\n except Exception:\n pass\n\n # 2) Fallback to text parser for legacy format\n return self._parse_autonomous_action_text(response)", + "context": "文件: lib/laddr/src/laddr/core/agent_runtime.py\n参数: self, response\n调用者: 无", + "related_elements": [ + "Agent._parse_autonomous_action" + ] + }, + { + "pattern_type": "function_implementation", + "description": "method Agent._parse_autonomous_action_text 的实现和用法", + "code_snippet": " def _parse_autonomous_action_text(self, response: str) -> dict:\n \"\"\"Parse legacy text format with ACTION:/AGENT:/TOOL_NAME:/... lines.\"\"\"\n lines = response.strip().split(\"\\n\")\n action_type = None\n data: dict[str, Any] = {}\n for line in lines:\n line = line.strip()\n if line.startswith(\"ACTION:\"):\n action_str = line.replace(\"ACTION:\", \"\").strip().upper()\n if \"TOOL\" in action_str:\n action_type = \"tool\"\n elif \"DELEGATE\" in action_str:\n action_type = \"delegate\"\n elif \"FINISH\" in action_str:\n action_type = \"finish\"\n elif line.startswith(\"TOOL_NAME:\"):\n data[\"tool\"] = line.replace(\"TOOL_NAME:\", \"\").strip()\n elif line.startswith(\"PARAMS:\"):\n params_str = line.replace(\"PARAMS:\", \"\").strip()\n try:\n data[\"params\"] = json.loads(params_str)\n except Exception:\n data[\"params\"] = {}\n elif line.startswith(\"AGENT:\"):\n data[\"agent\"] = line.replace(\"AGENT:\", \"\").strip()\n elif line.startswith(\"TASK:\"):\n data[\"task\"] = line.replace(\"TASK:\", \"\").strip()\n elif line.startswith(\"ANSWER:\"):\n # Capture everything after ANSWER:\n answer_index = response.find(\"ANSWER:\")\n if answer_index != -1:\n data[\"answer\"] = response[answer_index + 7 :].strip()\n else:\n data[\"answer\"] = line.replace(\"ANSWER:\", \"\").strip()\n return {\"type\": action_type or \"think\", **data}", + "context": "文件: lib/laddr/src/laddr/core/agent_runtime.py\n参数: self, response\n调用者: Agent._parse_autonomous_action", + "related_elements": [ + "Agent._parse_autonomous_action_text", + "Agent._parse_autonomous_action" + ] + }, + { + "pattern_type": "function_implementation", + "description": "method S3Storage.__init__ 的实现和用法", + "code_snippet": " def __init__(\n self,\n endpoint: str,\n access_key: str,\n secret_key: str,\n secure: bool = False,\n region: str | None = None\n ):\n \"\"\"\n Initialize S3-compatible storage.\n \n Args:\n endpoint: S3 endpoint (e.g., \"s3.amazonaws.com\" or \"minio:9000\")\n access_key: Access key (AWS Access Key ID or MinIO access key)\n secret_key: Secret key (AWS Secret Access Key or MinIO secret key)\n secure: Use HTTPS (True for AWS S3, False for local MinIO)\n region: AWS region (optional, e.g., \"us-east-1\")\n \"\"\"\n self.endpoint = endpoint\n self.access_key = access_key\n self.secret_key = secret_key\n self.secure = secure\n self.region = region\n self._client = None", + "context": "文件: lib/laddr/src/laddr/core/storage.py\n参数: self, endpoint, access_key, secret_key, secure, region\n调用者: LaddrError.__init__, ProjectNotFoundError.__init__, ProjectExistsError.__init__", + "related_elements": [ + "S3Storage.__init__", + "LaddrError.__init__", + "ProjectNotFoundError.__init__", + "ProjectExistsError.__init__" + ] + }, + { + "pattern_type": "function_implementation", + "description": "method S3Storage._get_client 的实现和用法", + "code_snippet": " def _get_client(self):\n \"\"\"Get synchronous S3-compatible client.\"\"\"\n if self._client is None:\n try:\n from minio import Minio\n self._client = Minio(\n self.endpoint,\n access_key=self.access_key,\n secret_key=self.secret_key,\n secure=self.secure,\n region=self.region\n )\n except ImportError:\n raise RuntimeError(\n \"minio package not installed. Install with: pip install minio\"\n )\n return self._client", + "context": "文件: lib/laddr/src/laddr/core/storage.py\n参数: self\n调用者: _ensure, _put, _get", + "related_elements": [ + "S3Storage._get_client", + "_ensure", + "_put", + "_get" + ] + }, + { + "pattern_type": "function_implementation", + "description": "method InMemoryStorage.__init__ 的实现和用法", + "code_snippet": " def __init__(self):\n \"\"\"Initialize in-memory storage.\"\"\"\n self._storage: dict[str, dict[str, tuple[bytes, dict]]] = {}", + "context": "文件: lib/laddr/src/laddr/core/storage.py\n参数: self\n调用者: LaddrError.__init__, ProjectNotFoundError.__init__, ProjectExistsError.__init__", + "related_elements": [ + "InMemoryStorage.__init__", + "LaddrError.__init__", + "ProjectNotFoundError.__init__", + "ProjectExistsError.__init__" + ] + }, + { + "pattern_type": "function_implementation", + "description": "method DatabaseService.__init__ 的实现和用法", + "code_snippet": " def __init__(self, database_url: str):\n \"\"\"\n Initialize database service.\n \n Args:\n database_url: SQLAlchemy connection string\n e.g., \"postgresql://user:pass@host:5432/db\"\n or \"sqlite:///./laddr.db\" for local dev\n \"\"\"\n # Fallback to SQLite if URL is None or connection fails\n if not database_url:\n logger.warning(\"DATABASE_URL not set, using local SQLite\")\n database_url = \"sqlite:///laddr.db\"\n \n try:\n self.engine = create_engine(database_url, echo=False)\n # Test connection\n with self.engine.connect() as conn:\n conn.execute(text(\"SELECT 1\"))\n logger.info(f\"Connected to database: {database_url.split('@')[-1] if '@' in database_url else database_url}\")\n except Exception as e:\n db_type = database_url.split('://')[0] if '://' in database_url else 'unknown'\n logger.warning(f\"Failed to connect to {db_type} database: {e}\")\n logger.info(\"Falling back to SQLite database\")\n database_url = \"sqlite:///laddr.db\"\n self.engine = create_engine(database_url, echo=False)\n \n self.SessionLocal = sessionmaker(bind=self.engine)\n\n # Create tables if they don't exist\n Base.metadata.create_all(self.engine)", + "context": "文件: lib/laddr/src/laddr/core/database.py\n参数: self, database_url\n调用者: LaddrError.__init__, ProjectNotFoundError.__init__, ProjectExistsError.__init__", + "related_elements": [ + "DatabaseService.__init__", + "LaddrError.__init__", + "ProjectNotFoundError.__init__", + "ProjectExistsError.__init__" + ] + }, + { + "pattern_type": "function_implementation", + "description": "method DatabaseService.create_tables 的实现和用法", + "code_snippet": " def create_tables(self):\n \"\"\"Create all database tables if they don't exist.\"\"\"\n Base.metadata.create_all(self.engine)", + "context": "文件: lib/laddr/src/laddr/core/database.py\n参数: self\n调用者: 无", + "related_elements": [ + "DatabaseService.create_tables" + ] + }, + { + "pattern_type": "function_implementation", + "description": "method DatabaseService.get_session 的实现和用法", + "code_snippet": " def get_session(self) -> Generator[Session, None, None]:\n \"\"\"Context manager for database sessions.\"\"\"\n session = self.SessionLocal()\n try:\n yield session\n session.commit()\n except Exception:\n session.rollback()\n raise\n finally:\n session.close()", + "context": "文件: lib/laddr/src/laddr/core/database.py\n参数: self\n调用者: DatabaseService.create_job, DatabaseService.save_result, DatabaseService.get_result", + "related_elements": [ + "DatabaseService.get_session", + "DatabaseService.create_job", + "DatabaseService.save_result", + "DatabaseService.get_result" + ] + }, + { + "pattern_type": "function_implementation", + "description": "method DatabaseService.create_job 的实现和用法", + "code_snippet": " def create_job(self, job_id: str | None, pipeline: str, inputs: dict) -> str:\n \"\"\"Create a new job record, or reuse existing if job_id already exists (for sequential chains).\"\"\"\n if job_id is None:\n job_id = str(uuid.uuid4())\n\n with self.get_session() as session:\n # Check if job already exists (for sequential mode where agents share job_id)\n existing_job = session.query(Job).filter_by(job_id=job_id).first()\n if existing_job:\n # Job already exists (e.g., from first agent in sequential chain)\n # Just return the job_id without creating a duplicate\n return job_id\n \n # Create new job\n job = Job(\n job_id=job_id,\n pipeline_name=pipeline,\n status=\"pending\",\n inputs=inputs\n )\n session.add(job)\n\n return job_id", + "context": "文件: lib/laddr/src/laddr/core/database.py\n参数: self, job_id, pipeline, inputs\n调用者: 无", + "related_elements": [ + "DatabaseService.create_job" + ] + }, + { + "pattern_type": "function_implementation", + "description": "method DatabaseService.save_result 的实现和用法", + "code_snippet": " def save_result(self, job_id: str, outputs: dict, status: str = \"completed\") -> None:\n \"\"\"Save job result.\"\"\"\n with self.get_session() as session:\n job = session.query(Job).filter_by(job_id=job_id).first()\n if job:\n job.outputs = outputs\n job.status = status\n job.completed_at = datetime.utcnow()", + "context": "文件: lib/laddr/src/laddr/core/database.py\n参数: self, job_id, outputs, status\n调用者: 无", + "related_elements": [ + "DatabaseService.save_result" + ] + }, + { + "pattern_type": "function_implementation", + "description": "method DatabaseService.get_result 的实现和用法", + "code_snippet": " def get_result(self, job_id: str) -> dict | None:\n \"\"\"Get job result.\"\"\"\n with self.get_session() as session:\n job = session.query(Job).filter_by(job_id=job_id).first()\n if not job:\n return None\n\n def _iso_z(dt: datetime | None) -> str | None:\n if not dt:\n return None\n # stored as naive UTC; normalize to Z-suffixed ISO for clients\n return dt.isoformat() + \"Z\"\n\n return {\n \"job_id\": job.job_id,\n \"pipeline_name\": job.pipeline_name,\n \"status\": job.status,\n \"inputs\": job.inputs,\n \"outputs\": job.outputs,\n \"created_at\": _iso_z(job.created_at),\n \"completed_at\": _iso_z(job.completed_at),\n }", + "context": "文件: lib/laddr/src/laddr/core/database.py\n参数: self, job_id\n调用者: AgentRunner.replay", + "related_elements": [ + "DatabaseService.get_result", + "AgentRunner.replay" + ] + }, + { + "pattern_type": "function_implementation", + "description": "method DatabaseService.list_jobs 的实现和用法", + "code_snippet": " def list_jobs(self, limit: int = 50) -> list[dict]:\n \"\"\"List recent jobs.\"\"\"\n with self.get_session() as session:\n jobs = session.query(Job).order_by(Job.created_at.desc()).limit(limit).all()\n\n def _iso_z(dt: datetime | None) -> str | None:\n return dt.isoformat() + \"Z\" if dt else None\n\n return [\n {\n \"job_id\": job.job_id,\n \"pipeline_name\": job.pipeline_name,\n \"status\": job.status,\n \"created_at\": _iso_z(job.created_at),\n }\n for job in jobs\n ]", + "context": "文件: lib/laddr/src/laddr/core/database.py\n参数: self, limit\n调用者: 无", + "related_elements": [ + "DatabaseService.list_jobs" + ] + }, + { + "pattern_type": "function_implementation", + "description": "method DatabaseService.create_prompt 的实现和用法", + "code_snippet": " def create_prompt(self, prompt_id: str | None, prompt_name: str, inputs: dict) -> str:\n \"\"\"Create a new prompt execution record.\"\"\"\n if prompt_id is None:\n prompt_id = str(uuid.uuid4())\n\n with self.get_session() as session:\n prompt = PromptExecution(\n prompt_id=prompt_id,\n prompt_name=prompt_name,\n status=\"pending\",\n inputs=inputs\n )\n session.add(prompt)\n\n return prompt_id", + "context": "文件: lib/laddr/src/laddr/core/database.py\n参数: self, prompt_id, prompt_name, inputs\n调用者: 无", + "related_elements": [ + "DatabaseService.create_prompt" + ] + }, + { + "pattern_type": "function_implementation", + "description": "method DatabaseService.save_prompt_result 的实现和用法", + "code_snippet": " def save_prompt_result(self, prompt_id: str, outputs: dict, status: str = \"completed\") -> None:\n \"\"\"Save prompt execution result.\"\"\"\n with self.get_session() as session:\n prompt = session.query(PromptExecution).filter_by(prompt_id=prompt_id).first()\n if prompt:\n prompt.outputs = outputs\n prompt.status = status\n prompt.completed_at = datetime.utcnow()", + "context": "文件: lib/laddr/src/laddr/core/database.py\n参数: self, prompt_id, outputs, status\n调用者: 无", + "related_elements": [ + "DatabaseService.save_prompt_result" + ] + }, + { + "pattern_type": "function_implementation", + "description": "method DatabaseService.update_prompt_status 的实现和用法", + "code_snippet": " def update_prompt_status(self, prompt_id: str, status: str) -> None:\n \"\"\"Update prompt execution status only; set completed_at for terminal states.\"\"\"\n terminal = {\"completed\", \"failed\", \"error\", \"canceled\"}\n with self.get_session() as session:\n prompt = session.query(PromptExecution).filter_by(prompt_id=prompt_id).first()\n if prompt:\n prompt.status = status\n if status in terminal and prompt.completed_at is None:\n prompt.completed_at = datetime.utcnow()", + "context": "文件: lib/laddr/src/laddr/core/database.py\n参数: self, prompt_id, status\n调用者: 无", + "related_elements": [ + "DatabaseService.update_prompt_status" + ] + }, + { + "pattern_type": "function_implementation", + "description": "method DatabaseService.get_prompt_result 的实现和用法", + "code_snippet": " def get_prompt_result(self, prompt_id: str) -> dict | None:\n \"\"\"Get prompt execution result.\"\"\"\n with self.get_session() as session:\n prompt = session.query(PromptExecution).filter_by(prompt_id=prompt_id).first()\n if not prompt:\n return None\n\n def _iso_z(dt: datetime | None) -> str | None:\n return dt.isoformat() + \"Z\" if dt else None\n\n return {\n \"prompt_id\": prompt.prompt_id,\n \"prompt_name\": prompt.prompt_name,\n \"status\": prompt.status,\n \"inputs\": prompt.inputs,\n \"outputs\": prompt.outputs,\n \"created_at\": _iso_z(prompt.created_at),\n \"completed_at\": _iso_z(prompt.completed_at),\n }", + "context": "文件: lib/laddr/src/laddr/core/database.py\n参数: self, prompt_id\n调用者: 无", + "related_elements": [ + "DatabaseService.get_prompt_result" + ] + }, + { + "pattern_type": "function_implementation", + "description": "method DatabaseService.list_prompts 的实现和用法", + "code_snippet": " def list_prompts(self, limit: int = 50) -> list[dict]:\n \"\"\"List recent prompt executions.\"\"\"\n with self.get_session() as session:\n prompts = session.query(PromptExecution).order_by(PromptExecution.created_at.desc()).limit(limit).all()\n\n def _iso_z(dt: datetime | None) -> str | None:\n return dt.isoformat() + \"Z\" if dt else None\n\n return [\n {\n \"prompt_id\": prompt.prompt_id,\n \"prompt_name\": prompt.prompt_name,\n \"status\": prompt.status,\n \"created_at\": _iso_z(prompt.created_at),\n }\n for prompt in prompts\n ]", + "context": "文件: lib/laddr/src/laddr/core/database.py\n参数: self, limit\n调用者: list_prompts", + "related_elements": [ + "DatabaseService.list_prompts", + "list_prompts" + ] + }, + { + "pattern_type": "function_implementation", + "description": "method DatabaseService.get_job_traces 的实现和用法", + "code_snippet": " def get_job_traces(self, job_id: str) -> list[dict]:\n \"\"\"Get all traces for a job.\"\"\"\n with self.get_session() as session:\n traces = session.query(Trace).filter_by(job_id=job_id).order_by(Trace.timestamp).all()\n\n def _iso_z(dt: datetime | None) -> str | None:\n return dt.isoformat() + \"Z\" if dt else None\n\n return [\n {\n \"id\": trace.id,\n \"job_id\": trace.job_id,\n \"agent_name\": trace.agent_name,\n \"event_type\": trace.event_type,\n \"payload\": trace.payload,\n \"timestamp\": _iso_z(trace.timestamp),\n }\n for trace in traces\n ]", + "context": "文件: lib/laddr/src/laddr/core/database.py\n参数: self, job_id\n调用者: 无", + "related_elements": [ + "DatabaseService.get_job_traces" + ] + }, + { + "pattern_type": "function_implementation", + "description": "method DatabaseService.append_trace 的实现和用法", + "code_snippet": " def append_trace(self, job_id: str, agent_name: str, event_type: str, payload: dict) -> None:\n \"\"\"Append a trace event.\"\"\"\n with self.get_session() as session:\n trace = Trace(\n job_id=job_id,\n agent_name=agent_name,\n event_type=event_type,\n payload=payload\n )\n session.add(trace)", + "context": "文件: lib/laddr/src/laddr/core/database.py\n参数: self, job_id, agent_name, event_type, payload\n调用者: Agent._trace", + "related_elements": [ + "DatabaseService.append_trace", + "Agent._trace" + ] + }, + { + "pattern_type": "function_implementation", + "description": "method DatabaseService.list_traces 的实现和用法", + "code_snippet": " def list_traces(self, agent: str | None = None, limit: int = 100) -> list[dict]:\n \"\"\"List recent traces, optionally filtered by agent.\"\"\"\n with self.get_session() as session:\n query = session.query(Trace)\n\n if agent:\n query = query.filter_by(agent_name=agent)\n\n traces = query.order_by(Trace.timestamp.desc()).limit(limit).all()\n\n def _iso_z(dt: datetime | None) -> str | None:\n return dt.isoformat() + \"Z\" if dt else None\n\n return [\n {\n \"id\": trace.id,\n \"job_id\": trace.job_id,\n \"agent_name\": trace.agent_name,\n \"event_type\": trace.event_type,\n \"payload\": trace.payload,\n \"timestamp\": _iso_z(trace.timestamp),\n }\n for trace in traces\n ]", + "context": "文件: lib/laddr/src/laddr/core/database.py\n参数: self, agent, limit\n调用者: 无", + "related_elements": [ + "DatabaseService.list_traces" + ] + }, + { + "pattern_type": "function_implementation", + "description": "method DatabaseService.get_trace 的实现和用法", + "code_snippet": " def get_trace(self, trace_id: str) -> dict | None:\n \"\"\"Get a single trace event by id with full payload.\"\"\"\n with self.get_session() as session:\n trace = session.query(Trace).filter_by(id=trace_id).first()\n if not trace:\n return None\n def _iso_z(dt: datetime | None) -> str | None:\n return dt.isoformat() + \"Z\" if dt else None\n return {\n \"id\": trace.id,\n \"job_id\": trace.job_id,\n \"agent_name\": trace.agent_name,\n \"event_type\": trace.event_type,\n \"payload\": trace.payload,\n \"timestamp\": _iso_z(trace.timestamp),\n }", + "context": "文件: lib/laddr/src/laddr/core/database.py\n参数: self, trace_id\n调用者: 无", + "related_elements": [ + "DatabaseService.get_trace" + ] + }, + { + "pattern_type": "function_implementation", + "description": "method DatabaseService.memory_put 的实现和用法", + "code_snippet": " def memory_put(self, agent_name: str, key: str, value: Any, job_id: str | None = None) -> None:\n \"\"\"Store a memory entry.\"\"\"\n with self.get_session() as session:\n # Check if exists\n existing = session.query(Memory).filter_by(\n agent_name=agent_name,\n key=key,\n job_id=job_id\n ).first()\n\n if existing:\n existing.value = value\n existing.updated_at = datetime.utcnow()\n else:\n memory = Memory(\n agent_name=agent_name,\n job_id=job_id,\n key=key,\n value=value\n )\n session.add(memory)", + "context": "文件: lib/laddr/src/laddr/core/database.py\n参数: self, agent_name, key, value, job_id\n调用者: AgentMemory.put", + "related_elements": [ + "DatabaseService.memory_put", + "AgentMemory.put" + ] + }, + { + "pattern_type": "function_implementation", + "description": "method DatabaseService.memory_get 的实现和用法", + "code_snippet": " def memory_get(self, agent_name: str, key: str, job_id: str | None = None) -> Any:\n \"\"\"Retrieve a memory entry.\"\"\"\n with self.get_session() as session:\n memory = session.query(Memory).filter_by(\n agent_name=agent_name,\n key=key,\n job_id=job_id\n ).first()\n\n return memory.value if memory else None", + "context": "文件: lib/laddr/src/laddr/core/database.py\n参数: self, agent_name, key, job_id\n调用者: AgentMemory.get", + "related_elements": [ + "DatabaseService.memory_get", + "AgentMemory.get" + ] + }, + { + "pattern_type": "function_implementation", + "description": "method DatabaseService.memory_list 的实现和用法", + "code_snippet": " def memory_list(self, agent_name: str, job_id: str | None = None) -> dict[str, Any]:\n \"\"\"List all memory entries for an agent.\"\"\"\n with self.get_session() as session:\n query = session.query(Memory).filter_by(agent_name=agent_name)\n\n if job_id:\n query = query.filter_by(job_id=job_id)\n\n memories = query.all()\n\n return {mem.key: mem.value for mem in memories}", + "context": "文件: lib/laddr/src/laddr/core/database.py\n参数: self, agent_name, job_id\n调用者: AgentMemory.list", + "related_elements": [ + "DatabaseService.memory_list", + "AgentMemory.list" + ] + }, + { + "pattern_type": "function_implementation", + "description": "method DatabaseService.register_agent 的实现和用法", + "code_snippet": " def register_agent(self, agent_name: str, metadata: dict) -> None:\n \"\"\"Register or update an agent in the registry.\"\"\"\n with self.get_session() as session:\n existing = session.query(AgentRegistry).filter_by(agent_name=agent_name).first()\n\n if existing:\n existing.meta = metadata\n existing.last_seen = datetime.utcnow()\n else:\n registry = AgentRegistry(\n agent_name=agent_name,\n meta=metadata\n )\n session.add(registry)", + "context": "文件: lib/laddr/src/laddr/core/database.py\n参数: self, agent_name, metadata\n调用者: 无", + "related_elements": [ + "DatabaseService.register_agent" + ] + }, + { + "pattern_type": "function_implementation", + "description": "method DatabaseService.list_agents 的实现和用法", + "code_snippet": " def list_agents(self) -> list[dict]:\n \"\"\"List all registered agents with trace counts and last execution time.\"\"\"\n with self.get_session() as session:\n agents = session.query(AgentRegistry).all()\n\n def _iso_z(dt: datetime | None) -> str | None:\n return dt.isoformat() + \"Z\" if dt else None\n\n result = []\n for agent in agents:\n # Get trace count for this agent\n trace_count = session.query(Trace).filter_by(agent_name=agent.agent_name).count()\n \n # Get last execution time (most recent trace)\n last_trace = (\n session.query(Trace)\n .filter_by(agent_name=agent.agent_name)\n .order_by(Trace.timestamp.desc())\n .first()\n )\n last_executed = _iso_z(last_trace.timestamp) if last_trace else None\n \n result.append({\n \"agent_name\": agent.agent_name,\n \"metadata\": agent.meta,\n \"last_seen\": _iso_z(agent.last_seen),\n \"trace_count\": trace_count,\n \"last_executed\": last_executed,\n })\n \n return result", + "context": "文件: lib/laddr/src/laddr/core/database.py\n参数: self\n调用者: 无", + "related_elements": [ + "DatabaseService.list_agents" + ] + }, + { + "pattern_type": "function_implementation", + "description": "method DatabaseService.get_metrics 的实现和用法", + "code_snippet": " def get_metrics(self) -> dict[str, Any]:\n \"\"\"Get aggregated metrics.\"\"\"\n with self.get_session() as session:\n # Count both legacy Jobs and new PromptExecutions\n total_jobs = session.query(Job).count()\n total_prompts = session.query(PromptExecution).count()\n total_executions = total_jobs + total_prompts\n \n completed_jobs = session.query(Job).filter_by(status=\"completed\").count()\n completed_prompts = session.query(PromptExecution).filter_by(status=\"completed\").count()\n total_completed = completed_jobs + completed_prompts\n \n failed_jobs = session.query(Job).filter_by(status=\"failed\").count()\n failed_prompts = session.query(PromptExecution).filter_by(status=\"failed\").count()\n total_failed = failed_jobs + failed_prompts\n\n # Calculate average latency from both Jobs and PromptExecutions\n latencies = []\n \n # Get latencies from completed jobs\n completed = session.query(Job).filter_by(status=\"completed\").all()\n for job in completed:\n if job.created_at and job.completed_at:\n delta = (job.completed_at - job.created_at).total_seconds()\n latencies.append(delta)\n \n # Get latencies from completed prompts\n completed_prompts_list = session.query(PromptExecution).filter_by(status=\"completed\").all()\n for prompt in completed_prompts_list:\n if prompt.created_at and prompt.completed_at:\n delta = (prompt.completed_at - prompt.created_at).total_seconds()\n latencies.append(delta)\n\n avg_latency_sec = sum(latencies) / len(latencies) if latencies else 0\n\n # Active agents (from registry)\n active_agents = session.query(AgentRegistry).count()\n\n # Tool calls (from traces)\n tool_calls = session.query(Trace).filter_by(event_type=\"tool_call\").count()\n\n # Cache hits (from traces)\n cache_hits = session.query(Trace).filter_by(event_type=\"cache_hit\").count()\n\n # Total tokens from llm_usage traces\n llm_usage_traces = session.query(Trace).filter_by(event_type=\"llm_usage\").all()\n total_tokens = 0\n for trace in llm_usage_traces:\n payload = trace.payload or {}\n total_tokens += int(payload.get(\"total_tokens\") or 0)\n\n return {\n \"total_jobs\": total_executions,\n \"completed_jobs\": total_completed,\n \"failed_jobs\": total_failed,\n \"avg_latency_ms\": int(avg_latency_sec * 1000),\n \"active_agents_count\": active_agents,\n \"tool_calls\": tool_calls,\n \"cache_hits\": cache_hits,\n \"total_tokens\": total_tokens,\n }", + "context": "文件: lib/laddr/src/laddr/core/database.py\n参数: self\n调用者: 无", + "related_elements": [ + "DatabaseService.get_metrics" + ] + }, + { + "pattern_type": "function_implementation", + "description": "method DatabaseService.get_token_usage 的实现和用法", + "code_snippet": " def get_token_usage(self, job_id: str) -> dict:\n \"\"\"Aggregate token usage for a job from llm_usage traces.\"\"\"\n with self.get_session() as session:\n traces = (\n session.query(Trace)\n .filter_by(job_id=job_id, event_type=\"llm_usage\")\n .all()\n )\n total_prompt = 0\n total_completion = 0\n total = 0\n breakdown: dict[tuple[str | None, str | None], dict] = {}\n for tr in traces:\n payload = tr.payload or {}\n pt = int(payload.get(\"prompt_tokens\") or 0)\n ct = int(payload.get(\"completion_tokens\") or 0)\n tt = int(payload.get(\"total_tokens\") or (pt + ct))\n prov = payload.get(\"provider\")\n model = payload.get(\"model\")\n total_prompt += pt\n total_completion += ct\n total += tt\n key = (prov, model)\n if key not in breakdown:\n breakdown[key] = {\n \"provider\": prov,\n \"model\": model,\n \"prompt_tokens\": 0,\n \"completion_tokens\": 0,\n \"total_tokens\": 0,\n \"calls\": 0,\n }\n breakdown[key][\"prompt_tokens\"] += pt\n breakdown[key][\"completion_tokens\"] += ct\n breakdown[key][\"total_tokens\"] += tt\n breakdown[key][\"calls\"] += 1\n\n return {\n \"prompt_tokens\": total_prompt,\n \"completion_tokens\": total_completion,\n \"total_tokens\": total,\n \"by_model\": list(breakdown.values()),\n }", + "context": "文件: lib/laddr/src/laddr/core/database.py\n参数: self, job_id\n调用者: 无", + "related_elements": [ + "DatabaseService.get_token_usage" + ] + }, + { + "pattern_type": "function_implementation", + "description": "method TaskDelegationTool.__init__ 的实现和用法", + "code_snippet": " def __init__(self, message_bus, artifact_storage=None, agent=None):\n \"\"\"\n Initialize task delegation tool.\n \n Args:\n message_bus: Message queue backend for inter-agent communication\n artifact_storage: Optional storage backend for large payloads\n agent: Optional agent instance for accessing current_job_id\n \"\"\"\n self.message_bus = message_bus\n self.artifact_storage = artifact_storage\n self.agent = agent\n self._size_threshold_kb = 100 # Store payloads > 100KB in artifact registry\n \n # Get storage bucket from agent config, fallback to default\n self._storage_bucket = None\n if agent and hasattr(agent, 'env_config'):\n self._storage_bucket = getattr(agent.env_config, 'storage_bucket', None)\n self._storage_bucket = self._storage_bucket or \"laddr\"", + "context": "文件: lib/laddr/src/laddr/core/system_tools.py\n参数: self, message_bus, artifact_storage, agent\n调用者: LaddrError.__init__, ProjectNotFoundError.__init__, ProjectExistsError.__init__", + "related_elements": [ + "TaskDelegationTool.__init__", + "LaddrError.__init__", + "ProjectNotFoundError.__init__", + "ProjectExistsError.__init__" + ] + }, + { + "pattern_type": "function_implementation", + "description": "method ArtifactStorageTool.__init__ 的实现和用法", + "code_snippet": " def __init__(self, storage_backend, default_bucket: str | None = None):\n \"\"\"\n Initialize artifact storage tool.\n \n Args:\n storage_backend: Storage backend (MinIO/S3)\n \"\"\"\n self.storage = storage_backend\n # Allow caller to provide a project-wide default bucket name\n # (e.g. from LaddrConfig.storage_bucket). If not provided we\n # keep backward-compatible defaults in retrieval logic.\n self.default_bucket = default_bucket", + "context": "文件: lib/laddr/src/laddr/core/system_tools.py\n参数: self, storage_backend, default_bucket\n调用者: LaddrError.__init__, ProjectNotFoundError.__init__, ProjectExistsError.__init__", + "related_elements": [ + "ArtifactStorageTool.__init__", + "LaddrError.__init__", + "ProjectNotFoundError.__init__", + "ProjectExistsError.__init__" + ] + }, + { + "pattern_type": "function_implementation", + "description": "method ParallelDelegationTool.__init__ 的实现和用法", + "code_snippet": " def __init__(self, message_bus, artifact_storage=None, agent=None):\n \"\"\"\n Initialize parallel delegation tool.\n \n Args:\n message_bus: Message queue backend for inter-agent communication\n artifact_storage: Optional storage backend for large payloads\n agent: Optional agent instance for job context propagation\n \"\"\"\n self.message_bus = message_bus\n self.artifact_storage = artifact_storage\n self.agent = agent\n self.delegation_tool = TaskDelegationTool(message_bus, artifact_storage, agent)", + "context": "文件: lib/laddr/src/laddr/core/system_tools.py\n参数: self, message_bus, artifact_storage, agent\n调用者: LaddrError.__init__, ProjectNotFoundError.__init__, ProjectExistsError.__init__", + "related_elements": [ + "ParallelDelegationTool.__init__", + "LaddrError.__init__", + "ProjectNotFoundError.__init__", + "ProjectExistsError.__init__" + ] + }, + { + "pattern_type": "function_implementation", + "description": "method ParallelDelegationTool.split_document 的实现和用法", + "code_snippet": " def split_document(\n self,\n document: str,\n num_chunks: int = 3,\n overlap: int = 0\n ) -> dict:\n \"\"\"\n Split a large document into smaller chunks for parallel processing.\n \n Use this before calling delegate_parallel to divide large documents\n into manageable sections that can be processed by multiple workers.\n\n Parameters:\n - document: The document text to split\n - num_chunks: Number of chunks to split into (e.g., 3 for 3 workers)\n - overlap: Number of characters to overlap between chunks (for context continuity)\n\n Returns:\n - chunks: List of document chunks\n - metadata: Information about the split (lengths, chunk count, etc.)\n \"\"\"\n doc_len = len(document)\n \n if num_chunks <= 0:\n num_chunks = 3\n \n if num_chunks == 1:\n return {\n \"chunks\": [document],\n \"metadata\": {\n \"original_length\": doc_len,\n \"chunk_sizes\": [doc_len],\n \"num_chunks\": 1,\n \"overlap\": 0\n }\n }\n\n # Helper: choose a split point near target on whitespace to avoid mid-word cuts\n def nearest_whitespace(target: int, low: int, high: int) -> int:\n target = max(low, min(high, target))\n window = 200 # search window on either side\n left = max(low, target - window)\n right = min(high, target + window)\n best = None\n best_dist = None\n for idx in range(target, left - 1, -1):\n if document[idx:idx+1].isspace():\n best = idx\n best_dist = abs(idx - target)\n break\n if best is None:\n for idx in range(target, right + 1):\n if document[idx:idx+1].isspace():\n best = idx\n best_dist = abs(idx - target)\n break\n return best if best is not None else target\n\n # Calculate approximate equal segments\n base = doc_len // num_chunks\n indices = [0]\n for i in range(1, num_chunks):\n target = i * base\n # ensure monotonic increase\n low = indices[-1] + 1\n high = doc_len - 1\n split_at = nearest_whitespace(target, low, high)\n indices.append(split_at)\n indices.append(doc_len)\n\n # Build chunks with optional overlap (character-based)\n chunks = []\n for i in range(num_chunks):\n start = indices[i]\n end = indices[i+1]\n # apply overlap: extend end by overlap for all but last, and start backward for all but first\n start_o = max(0, start - (overlap if i > 0 else 0))\n end_o = min(doc_len, end + (overlap if i < num_chunks - 1 else 0))\n chunks.append(document[start_o:end_o])\n \n return {\n \"chunks\": chunks,\n \"metadata\": {\n \"original_length\": doc_len,\n \"chunk_sizes\": [len(c) for c in chunks],\n \"num_chunks\": len(chunks),\n \"overlap\": overlap\n }\n }", + "context": "文件: lib/laddr/src/laddr/core/system_tools.py\n参数: self, document, num_chunks, overlap\n调用者: 无", + "related_elements": [ + "ParallelDelegationTool.split_document" + ] + }, + { + "pattern_type": "function_implementation", + "description": "function override_system_tool 的实现和用法", + "code_snippet": "def override_system_tool(tool_name: str):\n \"\"\"\n Decorator to override a system tool with a custom implementation.\n \n Use this decorator to replace built-in system tools (like system_delegate_parallel,\n system_split_document, etc.) with your own implementations.\n \n Example:\n from laddr.core.system_tools import override_system_tool\n \n @override_system_tool(\"system_delegate_parallel\")\n async def my_custom_parallel_delegation(agent_name: str, tasks: list[str], **kwargs):\n # Your custom implementation\n print(f\"Custom parallel delegation: {len(tasks)} tasks to {agent_name}\")\n return {\"status\": \"success\", \"results\": [...]}\n \n Args:\n tool_name: Name of the system tool to override (e.g., \"system_delegate_parallel\")\n \n Returns:\n Decorator function that registers the override\n \"\"\"\n def decorator(func: Callable) -> Callable:\n _TOOL_OVERRIDES[tool_name] = func\n \n # Also register common aliases\n if tool_name == \"system_delegate_parallel\":\n _TOOL_OVERRIDES[\"delegate_parallel\"] = func\n elif tool_name == \"system_split_document\":\n _TOOL_OVERRIDES[\"split_document\"] = func\n elif tool_name == \"system_delegate_task\":\n _TOOL_OVERRIDES[\"delegate_task\"] = func\n \n logger.info(f\"✅ Registered tool override: {tool_name} -> {func.__name__}\")\n return func\n \n return decorator", + "context": "文件: lib/laddr/src/laddr/core/system_tools.py\n参数: tool_name\n调用者: 无", + "related_elements": [ + "override_system_tool" + ] + }, + { + "pattern_type": "function_implementation", + "description": "function get_tool_override 的实现和用法", + "code_snippet": "def get_tool_override(tool_name: str) -> Callable | None:\n \"\"\"\n Get a tool override if it exists.\n \n Args:\n tool_name: Name of the tool to check\n \n Returns:\n The override function if registered, None otherwise\n \"\"\"\n return _TOOL_OVERRIDES.get(tool_name)", + "context": "文件: lib/laddr/src/laddr/core/system_tools.py\n参数: tool_name\n调用者: create_system_tools", + "related_elements": [ + "get_tool_override", + "create_system_tools" + ] + }, + { + "pattern_type": "function_implementation", + "description": "function clear_tool_overrides 的实现和用法", + "code_snippet": "def clear_tool_overrides():\n \"\"\"Clear all tool overrides. Useful for testing.\"\"\"\n global _TOOL_OVERRIDES\n _TOOL_OVERRIDES = {}\n logger.info(\"Cleared all tool overrides\")", + "context": "文件: lib/laddr/src/laddr/core/system_tools.py\n参数: \n调用者: 无", + "related_elements": [ + "clear_tool_overrides" + ] + }, + { + "pattern_type": "function_implementation", + "description": "function list_tool_overrides 的实现和用法", + "code_snippet": "def list_tool_overrides() -> dict[str, str]:\n \"\"\"\n List all registered tool overrides.\n \n Returns:\n Dict mapping tool names to their override function names\n \"\"\"\n return {\n name: func.__name__ \n for name, func in _TOOL_OVERRIDES.items()\n }", + "context": "文件: lib/laddr/src/laddr/core/system_tools.py\n参数: \n调用者: 无", + "related_elements": [ + "list_tool_overrides" + ] + }, + { + "pattern_type": "function_implementation", + "description": "function create_system_tools 的实现和用法", + "code_snippet": "def create_system_tools(message_bus, storage_backend=None, agent=None) -> dict[str, tuple[Any, list[str]]]:\n \"\"\"\n Create system tools for task delegation and artifact management.\n \n This function checks for user-provided overrides before registering\n the default implementations. Users can use @override_system_tool \n decorator to replace any system tool OR create entirely new system tools.\n \n Args:\n message_bus: Message queue backend\n storage_backend: Optional storage backend (MinIO/S3)\n agent: Optional agent instance for accessing current_job_id context\n \n Returns:\n Dict mapping tool names to (tool_function, aliases) tuples\n \"\"\"\n import functools\n import inspect as _inspect\n \n def wrap_with_runtime_injection(tool_func):\n \"\"\"\n Wrap a custom system tool function to inject runtime parameters.\n \n Custom override functions can declare _message_bus, _artifact_storage, _agent\n parameters which will be automatically injected at call time.\n \"\"\"\n sig = _inspect.signature(tool_func)\n needs_injection = any(p in sig.parameters for p in ['_message_bus', '_artifact_storage', '_agent'])\n \n if not needs_injection:\n # No injection needed, return as-is\n return tool_func\n \n # Create wrapper that injects runtime parameters\n @functools.wraps(tool_func)\n async def wrapped_tool(*args, **kwargs):\n # Inject runtime parameters if not already provided\n if '_message_bus' in sig.parameters and '_message_bus' not in kwargs:\n kwargs['_message_bus'] = message_bus\n if '_artifact_storage' in sig.parameters and '_artifact_storage' not in kwargs:\n kwargs['_artifact_storage'] = storage_backend\n if '_agent' in sig.parameters and '_agent' not in kwargs:\n kwargs['_agent'] = agent\n \n # Call the original function\n result = tool_func(*args, **kwargs)\n \n # Await if async\n if _inspect.iscoroutine(result) or _inspect.isawaitable(result):\n return await result\n return result\n \n return wrapped_tool\n \n tools = {}\n\n # Task delegation tool (with agent context for job_id propagation)\n override = get_tool_override(\"system_delegate_task\")\n if override:\n logger.info(\"✅ Using custom override for system_delegate_task\")\n tools[\"system_delegate_task\"] = (wrap_with_runtime_injection(override), [])\n else:\n delegation_tool = TaskDelegationTool(message_bus, storage_backend, agent=agent)\n tools[\"system_delegate_task\"] = (delegation_tool.delegate_task, [])\n\n # Parallel delegation and document splitting tools WITH ALIASES\n parallel_tool = ParallelDelegationTool(message_bus, storage_backend, agent=agent)\n \n # Register with aliases instead of duplicate entries\n override = get_tool_override(\"system_delegate_parallel\")\n if override:\n logger.info(\"✅ Using custom override for system_delegate_parallel\")\n tools[\"system_delegate_parallel\"] = (wrap_with_runtime_injection(override), [\"delegate_parallel\"])\n else:\n tools[\"system_delegate_parallel\"] = (\n parallel_tool.delegate_parallel,\n [\"delegate_parallel\"] # Friendly alias\n )\n \n override = get_tool_override(\"system_split_document\")\n if override:\n logger.info(\"✅ Using custom override for system_split_document\")\n tools[\"system_split_document\"] = (wrap_with_runtime_injection(override), [\"split_document\"])\n else:\n tools[\"system_split_document\"] = (\n parallel_tool.split_document,\n [\"split_document\"] # Friendly alias\n )\n\n # Artifact storage tools (if storage backend available)\n if storage_backend:\n # Pass configured default bucket when available (agent may be None)\n default_bucket = None\n try:\n default_bucket = agent.env_config.storage_bucket if agent and getattr(agent, 'env_config', None) else None\n except Exception:\n default_bucket = None\n artifact_tool = ArtifactStorageTool(storage_backend, default_bucket=default_bucket)\n \n override = get_tool_override(\"system_store_artifact\")\n if override:\n logger.info(\"✅ Using custom override for system_store_artifact\")\n tools[\"system_store_artifact\"] = (wrap_with_runtime_injection(override), [])\n else:\n tools[\"system_store_artifact\"] = (artifact_tool.store_artifact, [])\n \n override = get_tool_override(\"system_retrieve_artifact\")\n if override:\n logger.info(\"✅ Using custom override for system_retrieve_artifact\")\n tools[\"system_retrieve_artifact\"] = (wrap_with_runtime_injection(override), [])\n else:\n tools[\"system_retrieve_artifact\"] = (artifact_tool.retrieve_artifact, [])\n \n override = get_tool_override(\"system_list_artifacts\")\n if override:\n logger.info(\"✅ Using custom override for system_list_artifacts\")\n tools[\"system_list_artifacts\"] = (wrap_with_runtime_injection(override), [])\n else:\n tools[\"system_list_artifacts\"] = (artifact_tool.list_artifacts, [])\n\n # NEW FEATURE: Register any additional custom system tools that weren't overrides\n # This allows users to create entirely new system tools, not just override existing ones\n known_tool_names = set(tools.keys())\n for tool_name, tool_func in _TOOL_OVERRIDES.items():\n # Skip aliases (like \"delegate_parallel\") - they're registered with the main tool\n if tool_name in [\"delegate_parallel\", \"split_document\", \"delegate_task\"]:\n continue\n \n # Add new custom system tools that don't override built-in ones\n if tool_name not in known_tool_names:\n logger.info(f\"✅ Registering NEW system tool: {tool_name}\")\n # Wrap with runtime injection\n tools[tool_name] = (wrap_with_runtime_injection(tool_func), [])\n\n return tools", + "context": "文件: lib/laddr/src/laddr/core/system_tools.py\n参数: message_bus, storage_backend, agent\n调用者: Agent._register_system_tools", + "related_elements": [ + "create_system_tools", + "Agent._register_system_tools" + ] + }, + { + "pattern_type": "function_implementation", + "description": "function wrap_with_runtime_injection 的实现和用法", + "code_snippet": " def wrap_with_runtime_injection(tool_func):\n \"\"\"\n Wrap a custom system tool function to inject runtime parameters.\n \n Custom override functions can declare _message_bus, _artifact_storage, _agent\n parameters which will be automatically injected at call time.\n \"\"\"\n sig = _inspect.signature(tool_func)\n needs_injection = any(p in sig.parameters for p in ['_message_bus', '_artifact_storage', '_agent'])\n \n if not needs_injection:\n # No injection needed, return as-is\n return tool_func\n \n # Create wrapper that injects runtime parameters\n @functools.wraps(tool_func)\n async def wrapped_tool(*args, **kwargs):\n # Inject runtime parameters if not already provided\n if '_message_bus' in sig.parameters and '_message_bus' not in kwargs:\n kwargs['_message_bus'] = message_bus\n if '_artifact_storage' in sig.parameters and '_artifact_storage' not in kwargs:\n kwargs['_artifact_storage'] = storage_backend\n if '_agent' in sig.parameters and '_agent' not in kwargs:\n kwargs['_agent'] = agent\n \n # Call the original function\n result = tool_func(*args, **kwargs)\n \n # Await if async\n if _inspect.iscoroutine(result) or _inspect.isawaitable(result):\n return await result\n return result\n \n return wrapped_tool", + "context": "文件: lib/laddr/src/laddr/core/system_tools.py\n参数: tool_func\n调用者: create_system_tools", + "related_elements": [ + "wrap_with_runtime_injection", + "create_system_tools" + ] + }, + { + "pattern_type": "function_implementation", + "description": "function cli 的实现和用法", + "code_snippet": "def cli():\n \"\"\"Laddr - Transparent, Docker-native agent framework.\"\"\"", + "context": "文件: lib/laddr/src/laddr/cli/main.py\n参数: \n调用者: main", + "related_elements": [ + "cli", + "main" + ] + }, + { + "pattern_type": "function_implementation", + "description": "function run_local 的实现和用法", + "code_snippet": "def run_local(agent: str, input_json: str):\n \"\"\"Run an agent once locally using in-memory components (no Redis/DB).\"\"\"\n import json\n import asyncio\n import os\n import sys\n # Ensure current working directory is importable (for 'agents' package)\n try:\n cwd = os.getcwd()\n if cwd not in sys.path:\n sys.path.insert(0, cwd)\n except Exception:\n pass\n # Sensible local defaults to avoid external deps\n try:\n if not os.environ.get(\"DATABASE_URL\"):\n os.environ[\"DATABASE_URL\"] = \"sqlite:///laddr.db\"\n if not os.environ.get(\"QUEUE_BACKEND\") and not os.environ.get(\"REDIS_URL\"):\n os.environ[\"QUEUE_BACKEND\"] = \"memory\"\n except Exception:\n pass\n from laddr.core.config import load_agents\n\n try:\n payload = json.loads(input_json)\n except Exception as e:\n raise click.ClickException(f\"Invalid JSON for --input: {e}\")\n\n agents = load_agents()\n a = agents.get(agent)\n if a is None:\n # Fallback: try instantiating legacy-style agent (requires config/env)\n try:\n mod = __import__(f\"agents.{agent}.handler\", fromlist=[\"*\"])\n cls_name = f\"{agent.capitalize()}Agent\"\n AgentCls = getattr(mod, cls_name)\n from laddr.core.config import LaddrConfig, AgentConfig # type: ignore\n cfg = AgentConfig(name=agent, role=getattr(AgentCls, \"ROLE\", agent), goal=getattr(AgentCls, \"GOAL\", \"\"))\n env = LaddrConfig()\n a = AgentCls(cfg, env)\n except Exception as e:\n hint = (\n \"Make sure you're running this command from your project root where the 'agents/' folder exists, \"\n \"and that 'agents/__init__.py' is present.\"\n )\n raise click.ClickException(f\"Agent not found or cannot be constructed: {e}\\n{hint}\")\n\n result = asyncio.run(a.handle(payload))\n click.echo(json.dumps(result, ensure_ascii=False))", + "context": "文件: lib/laddr/src/laddr/cli/main.py\n参数: agent, input_json\n调用者: 无", + "related_elements": [ + "run_local" + ] + }, + { + "pattern_type": "function_implementation", + "description": "function main 的实现和用法", + "code_snippet": "def main():\n \"\"\"Main entry point with error handling.\"\"\"\n try:\n cli()\n except LaddrError as e:\n print_error(e.message, hint=e.hint)\n sys.exit(1)\n except click.ClickException as e:\n e.show()\n sys.exit(e.exit_code)\n except KeyboardInterrupt:\n console.print(\"\\n[yellow]Operation cancelled[/yellow]\")\n sys.exit(130)\n except Exception as e:\n console.print(f\"\\n[red bold]Unexpected error:[/red bold] {e}\")\n if \"--debug\" in sys.argv:\n traceback.print_exc()\n sys.exit(1)", + "context": "文件: lib/laddr/src/laddr/cli/main.py\n参数: \n调用者: 无", + "related_elements": [ + "main" + ] + }, + { + "pattern_type": "function_implementation", + "description": "function prompt 的实现和用法", + "code_snippet": "def prompt():\n \"\"\"Run and manage prompt executions.\"\"\"", + "context": "文件: lib/laddr/src/laddr/cli/commands/prompt.py\n参数: \n调用者: add_agent, add_tool", + "related_elements": [ + "prompt", + "add_agent", + "add_tool" + ] + }, + { + "pattern_type": "function_implementation", + "description": "function run_prompt 的实现和用法", + "code_snippet": "def run_prompt(prompt_name: str, inputs: tuple, json_input: str | None, wait: bool, timeout: int):\n \"\"\"\n Run a prompt execution.\n \n Examples:\n laddr prompt run researcher --input query=\"AI trends\"\n laddr prompt run researcher --json '{\"query\": \"AI trends\"}'\n laddr prompt run researcher --json @inputs.json\n \"\"\"\n try:\n # Parse inputs\n parsed_inputs = {}\n\n if json_input:\n if json_input.startswith(\"@\"):\n # Load from file\n file_path = Path(json_input[1:])\n if not file_path.exists():\n print_error(f\"Input file not found: {file_path}\")\n return\n with open(file_path) as f:\n parsed_inputs = json.load(f)\n else:\n # Parse JSON string\n parsed_inputs = json.loads(json_input)\n\n # Add KEY=VALUE inputs\n for inp in inputs:\n if \"=\" not in inp:\n print_error(f\"Invalid input format: {inp}. Expected KEY=VALUE\")\n return\n key, value = inp.split(\"=\", 1)\n parsed_inputs[key] = value\n\n if not parsed_inputs:\n print_error(\"No inputs provided. Use --input KEY=VALUE or --json\")\n return\n\n console.print(f\"[cyan]Running prompt:[/cyan] {prompt_name}\")\n console.print(f\"[dim]Inputs:[/dim] {json.dumps(parsed_inputs, indent=2)}\")\n\n # Load config and run\n config = LaddrConfig()\n\n result = asyncio.run(run_agent(\n agent_name=prompt_name,\n inputs=parsed_inputs,\n env_config=config\n ))\n\n prompt_id = result.get(\"prompt_id\") or result.get(\"job_id\")\n\n if result.get(\"status\") == \"success\":\n print_success(f\"Prompt completed: {prompt_id}\")\n console.print(\"\\n[green bold]Result:[/green bold]\")\n console.print(json.dumps(result.get(\"result\"), indent=2))\n else:\n print_error(f\"Prompt failed: {result.get('error')}\")\n console.print(f\"[dim]Prompt ID:[/dim] {prompt_id}\")\n\n except Exception as e:\n print_error(f\"Failed to run prompt: {e}\")\n if \"--debug\" in click.get_current_context().params:\n raise", + "context": "文件: lib/laddr/src/laddr/cli/commands/prompt.py\n参数: prompt_name, inputs, json_input, wait, timeout\n调用者: 无", + "related_elements": [ + "run_prompt" + ] + }, + { + "pattern_type": "function_implementation", + "description": "function list_prompts 的实现和用法", + "code_snippet": "def list_prompts(limit: int):\n \"\"\"List recent prompt executions.\"\"\"\n try:\n from laddr.core import DatabaseService, LaddrConfig\n\n config = LaddrConfig()\n db = DatabaseService(config.database_url)\n\n prompts = db.list_prompts(limit=limit)\n\n if not prompts:\n console.print(\"[yellow]No prompt executions found[/yellow]\")\n return\n\n console.print(f\"\\n[cyan bold]Recent Prompts[/cyan bold] (limit: {limit})\\n\")\n\n for prompt in prompts:\n status = prompt.get(\"status\", \"unknown\")\n status_color = {\n \"completed\": \"green\",\n \"pending\": \"yellow\",\n \"running\": \"blue\",\n \"failed\": \"red\"\n }.get(status, \"dim\")\n\n console.print(f\"[{status_color}]●[/{status_color}] {prompt.get('prompt_id')} - {prompt.get('prompt_name')}\")\n console.print(f\" Status: [{status_color}]{status}[/{status_color}]\")\n console.print(f\" Created: {prompt.get('created_at')}\")\n console.print()\n\n except Exception as e:\n print_error(f\"Failed to list prompts: {e}\")", + "context": "文件: lib/laddr/src/laddr/cli/commands/prompt.py\n参数: limit\n调用者: list_prompts", + "related_elements": [ + "list_prompts", + "list_prompts" + ] + }, + { + "pattern_type": "function_implementation", + "description": "function run 的实现和用法", + "code_snippet": "def run(ctx: click.Context):\n \"\"\"Run environments, agents, or pipelines.\n\n Usage:\n laddr run dev [--build] [--no-detach]\n laddr run agent [--inputs '{...}']\n laddr run pipeline \n \"\"\"", + "context": "文件: lib/laddr/src/laddr/cli/commands/run.py\n参数: ctx\n调用者: AgentRunner.replay, run_local, run_prompt", + "related_elements": [ + "run", + "AgentRunner.replay", + "run_local", + "run_prompt" + ] + }, + { + "pattern_type": "function_implementation", + "description": "function run_dev 的实现和用法", + "code_snippet": "def run_dev(build: bool, detach: bool):\n \"\"\"Run the Laddr development environment.\n \n Starts all infrastructure services and agent workers:\n - PostgreSQL (internal observability database)\n - Redis (message bus)\n - API server\n - Agent workers\n - Dashboard\n \n By default, shows live logs (like docker compose up).\n Use --detach/-d to run in background.\n \n Examples:\n laddr run dev # Show logs\n laddr run dev --build # Rebuild and show logs\n laddr run dev --detach # Run in background\n \"\"\"\n # Ensure we're in a project\n if not validate_project_directory(Path.cwd()):\n raise ProjectNotFoundError()\n\n # Check Docker availability\n check_docker()\n check_docker_compose()\n\n print_header(\"Starting Laddr Development Environment (This may take a few minutes)\")\n\n # Build command for docker compose up\n cmd = [\"docker\", \"compose\", \"up\"]\n if build:\n cmd.append(\"--build\")\n \n # In detached mode, start services in background first\n if detach:\n cmd.append(\"-d\")\n \n # Start services and show logs\n try:\n if not detach:\n # Non-detached mode: stream logs live (Ctrl+C to stop)\n print_info(\"Starting services and streaming logs (Ctrl+C to stop)...\")\n subprocess.run(cmd, check=False)\n else:\n # Detached mode: start in background, then show all logs\n print_info(\"Starting services in detached mode...\")\n result = subprocess.run(cmd, capture_output=True, text=True, check=False)\n if result.returncode != 0:\n print_info(f\"Error starting services: {result.stderr}\")\n raise click.ClickException(\"Failed to start services\")\n \n # Wait a moment for services to initialize\n print_info(\"Waiting for services to initialize...\")\n time.sleep(3)\n \n # Show all logs from all services\n print_info(\"Showing service logs...\\n\")\n subprocess.run([\"docker\", \"compose\", \"logs\"], check=False)\n \n # Wait for critical services to be ready\n print_info(\"\\nWaiting for services to be ready...\")\n critical_services = [\"postgres\", \"redis\", \"api\"]\n for service in critical_services:\n wait_for_service(service, timeout=30)\n \n print_success(\"\\nLaddr is running!\")\n _print_service_info()\n _print_management_commands()\n \n except KeyboardInterrupt:\n if not detach:\n print_info(\"\\nStopping services...\")\n subprocess.run([\"docker\", \"compose\", \"down\"], check=False)", + "context": "文件: lib/laddr/src/laddr/cli/commands/run.py\n参数: build, detach\n调用者: 无", + "related_elements": [ + "run_dev" + ] + }, + { + "pattern_type": "function_implementation", + "description": "function run_pipeline 的实现和用法", + "code_snippet": "def run_pipeline(pipeline_file: str):\n \"\"\"Run a pipeline defined in a YAML file.\"\"\"\n # Lazy import to avoid hard deps when not running pipeline\n import yaml # type: ignore\n\n if not validate_project_directory(Path.cwd()):\n raise ProjectNotFoundError()\n\n with open(pipeline_file, \"r\", encoding=\"utf-8\") as f:\n data = yaml.safe_load(f)\n\n stages = data.get(\"pipeline\") or data.get(\"stages\") or data.get(\"tasks\")\n if not isinstance(stages, list):\n raise click.BadParameter(\"Pipeline YAML must define a list under 'pipeline' or 'stages'\")\n\n # Use Laddr runtime (shared env for all agents)\n import asyncio\n\n from laddr.core import AgentRunner, LaddrConfig # type: ignore\n\n print_header(\"Running Pipeline\")\n\n results: dict[str, Any] = {}\n runner = AgentRunner(env_config=LaddrConfig())\n for stage in stages:\n agent_name = stage.get(\"agent\")\n # Support both {inputs: {...}} and arbitrary keys; if tasks format, pass remaining keys except 'agent'\n inputs = stage.get(\"inputs\", {k: v for k, v in stage.items() if k != \"agent\"})\n if not agent_name:\n raise click.BadParameter(\"Each stage must include 'agent'\")\n console.print(f\" [dim]→[/dim] Running stage: [cyan]{agent_name}[/cyan]\")\n res = asyncio.run(runner.run(inputs, agent_name=agent_name))\n results[agent_name] = res\n\n print_success(\"Pipeline complete\")\n console.print(\"\\n[bold cyan]Results[/bold cyan]\")\n console.print(json.dumps(results, indent=2, ensure_ascii=False))\n console.print()", + "context": "文件: lib/laddr/src/laddr/cli/commands/run.py\n参数: pipeline_file\n调用者: 无", + "related_elements": [ + "run_pipeline" + ] + }, + { + "pattern_type": "function_implementation", + "description": "function run_agent_cmd 的实现和用法", + "code_snippet": "def run_agent_cmd(agent_name: str, inputs_json: str):\n \"\"\"Run a single agent locally using AgentRunner.\"\"\"\n import asyncio\n import os\n import sys\n\n # Ensure local project imports work (agents package)\n try:\n cwd = str(Path.cwd())\n if cwd not in sys.path:\n sys.path.insert(0, cwd)\n except Exception:\n pass\n\n # Favor local-friendly defaults to avoid external deps\n try:\n if not os.environ.get(\"DATABASE_URL\"):\n os.environ[\"DATABASE_URL\"] = \"sqlite:///laddr.db\"\n if not os.environ.get(\"QUEUE_BACKEND\") and not os.environ.get(\"REDIS_URL\"):\n os.environ[\"QUEUE_BACKEND\"] = \"memory\"\n except Exception:\n pass\n\n if not validate_project_directory(Path.cwd()):\n raise ProjectNotFoundError()\n\n try:\n inputs = json.loads(inputs_json) if inputs_json else {}\n except json.JSONDecodeError:\n raise click.BadParameter(\"--inputs must be a valid JSON object\")\n\n # Use new runtime\n try:\n from laddr.core import LaddrConfig, run_agent\n\n print_header(f\"Running Agent → {agent_name}\")\n\n # Load environment config\n config = LaddrConfig()\n\n # Run agent\n result = asyncio.run(run_agent(agent_name, inputs, config))\n\n # Print result\n print_success(f\"Job completed: {result['job_id']}\")\n console.print(\"\\n[bold cyan]Result[/bold cyan]\")\n console.print(json.dumps(result, indent=2, ensure_ascii=False))\n console.print()\n\n except Exception as e:\n print_info(f\"Error: {e}\")\n raise click.ClickException(str(e))", + "context": "文件: lib/laddr/src/laddr/cli/commands/run.py\n参数: agent_name, inputs_json\n调用者: 无", + "related_elements": [ + "run_agent_cmd" + ] + }, + { + "pattern_type": "function_implementation", + "description": "function replay_job 的实现和用法", + "code_snippet": "def replay_job(job_id: str, reexecute: bool):\n \"\"\"Replay a previous job by job ID.\n \n Examples:\n laddr run replay abc123-456-def\n laddr run replay abc123-456-def --reexecute\n \"\"\"\n if not validate_project_directory(Path.cwd()):\n raise ProjectNotFoundError()\n\n try:\n from laddr.core import AgentRunner, LaddrConfig\n\n print_header(f\"Replaying Job → {job_id}\")\n\n # Load environment config\n config = LaddrConfig()\n runner = AgentRunner(env_config=config)\n\n # Replay\n result = runner.replay(job_id, reexecute=reexecute)\n\n # Print result\n print_success(\"Job replay complete\")\n console.print(\"\\n[bold cyan]Result[/bold cyan]\")\n console.print(json.dumps(result, indent=2, ensure_ascii=False))\n console.print()\n\n except Exception as e:\n print_info(f\"Error: {e}\")\n raise click.ClickException(str(e))", + "context": "文件: lib/laddr/src/laddr/cli/commands/run.py\n参数: job_id, reexecute\n调用者: 无", + "related_elements": [ + "replay_job" + ] + }, + { + "pattern_type": "function_implementation", + "description": "function _print_service_info 的实现和用法", + "code_snippet": "def _print_service_info() -> None:\n \"\"\"Print service URLs in minimalistic style.\"\"\"\n console.print(\"\\n[bold cyan]Services[/bold cyan]\")\n console.print(\" [dim]Dashboard[/dim] http://localhost:5173\")\n console.print(\" [dim]API[/dim] http://localhost:8000\")\n console.print(\" [dim]API Docs[/dim] http://localhost:8000/docs\")\n console.print(\" [dim]Postgres[/dim] localhost:5432\")\n console.print(\" [dim]Redis[/dim] localhost:6379\")", + "context": "文件: lib/laddr/src/laddr/cli/commands/run.py\n参数: \n调用者: run_dev", + "related_elements": [ + "_print_service_info", + "run_dev" + ] + }, + { + "pattern_type": "function_implementation", + "description": "function _print_management_commands 的实现和用法", + "code_snippet": "def _print_management_commands() -> None:\n \"\"\"Print management command help in minimalistic style.\"\"\"\n console.print(\"\\n[bold cyan]Commands[/bold cyan]\")\n console.print(\" [cyan]laddr logs [/cyan] View agent logs\")\n console.print(\" [cyan]laddr ps[/cyan] Show container status\")\n console.print(\" [cyan]laddr scale [/cyan] Scale agent workers\")\n console.print(\" [cyan]laddr stop[/cyan] Stop all services\")\n console.print()", + "context": "文件: lib/laddr/src/laddr/cli/commands/run.py\n参数: \n调用者: run_dev", + "related_elements": [ + "_print_management_commands", + "run_dev" + ] + }, + { + "pattern_type": "function_implementation", + "description": "function run_dev_alias 的实现和用法", + "code_snippet": "def run_dev_alias():\n \"\"\"Alias for 'laddr run dev'.\"\"\"\n from click import Context\n\n ctx = Context(run_dev)\n ctx.invoke(run_dev)", + "context": "文件: lib/laddr/src/laddr/cli/commands/run.py\n参数: \n调用者: 无", + "related_elements": [ + "run_dev_alias" + ] + }, + { + "pattern_type": "function_implementation", + "description": "function add 的实现和用法", + "code_snippet": "def add():\n \"\"\"Add resources to your project (agents, tools).\"\"\"", + "context": "文件: lib/laddr/src/laddr/cli/commands/add.py\n参数: \n调用者: DatabaseService.create_job, DatabaseService.create_prompt, DatabaseService.append_trace", + "related_elements": [ + "add", + "DatabaseService.create_job", + "DatabaseService.create_prompt", + "DatabaseService.append_trace" + ] + }, + { + "pattern_type": "function_implementation", + "description": "function add_agent 的实现和用法", + "code_snippet": "def add_agent(\n agent_name: str,\n role: str | None,\n goal: str | None,\n backstory: str | None,\n llm_provider: str,\n llm_model: str,\n):\n \"\"\"Add a new agent to the project.\n \n Examples:\n laddr add agent summarizer\n laddr add agent analyst --role \"Data Analyst\" --goal \"Analyze data\"\n \"\"\"\n # Validate agent name\n if \"-\" in agent_name:\n raise click.BadParameter(\n \"Agent name cannot contain hyphens (-). Use underscores (_) instead.\",\n param_hint=\"agent_name\"\n )\n \n if not agent_name.replace(\"_\", \"\").isalnum():\n raise click.BadParameter(\n \"Agent name must contain only letters, numbers, and underscores.\",\n param_hint=\"agent_name\"\n )\n \n # Ensure we're in a project\n if not validate_project_directory(Path.cwd()):\n raise ProjectNotFoundError()\n\n print_header(f\"Adding Agent → {agent_name}\")\n\n # Prompt for missing details\n if not role:\n role = click.prompt(\"Agent role\")\n if not goal:\n goal = click.prompt(\"Agent goal\")\n if not backstory:\n backstory = click.prompt(\"Agent backstory\", default=\"\", show_default=False)\n\n # Create agent files\n print_step(\"Generating agent file\", f\"agents/{agent_name}.py\")\n _create_agent_files(\n agent_name=agent_name,\n role=role,\n goal=goal,\n backstory=backstory,\n llm_provider=llm_provider,\n llm_model=llm_model,\n )\n\n print_completion(f\"Agent '{agent_name}' created\")\n console.print(\"[dim]Next:[/dim]\")\n console.print(f\" [cyan]laddr run agent {agent_name} --inputs '{{}}'[/cyan]\\n\")", + "context": "文件: lib/laddr/src/laddr/cli/commands/add.py\n参数: agent_name, role, goal, backstory, llm_provider, llm_model\n调用者: 无", + "related_elements": [ + "add_agent" + ] + }, + { + "pattern_type": "function_implementation", + "description": "function add_tool 的实现和用法", + "code_snippet": "def add_tool(\n tool_name: str,\n agent: str | None,\n description: str | None,\n):\n \"\"\"Add a new tool to an agent.\n \n Examples:\n laddr add tool calculator\n laddr add tool scraper --agent researcher\n \"\"\"\n # Validate tool name\n if \"-\" in tool_name:\n raise click.BadParameter(\n \"Tool name cannot contain hyphens (-). Use underscores (_) instead.\",\n param_hint=\"tool_name\"\n )\n \n if not tool_name.replace(\"_\", \"\").isalnum():\n raise click.BadParameter(\n \"Tool name must contain only letters, numbers, and underscores.\",\n param_hint=\"tool_name\"\n )\n \n # Ensure we're in a project\n if not validate_project_directory(Path.cwd()):\n raise ProjectNotFoundError()\n\n print_header(f\"Adding Tool → {tool_name}\")\n\n # Determine target agent\n if not agent:\n with open(\"laddr.yml\", \"r\", encoding=\"utf-8\") as f:\n config = yaml.safe_load(f)\n agents = config.get(\"project\", {}).get(\"agents\", [])\n if not agents:\n raise AgentNotFoundError(\"No agents found in project\")\n agent = agents[0]\n print_step(\"Attaching to agent\", agent)\n\n # Prompt for description if not provided\n if not description:\n description = click.prompt(\"Tool description\", default=f\"{tool_name} tool\")\n\n # Create tool file\n print_step(\"Generating tool file\", f\"tools/{tool_name}.py\")\n _create_tool_file(tool_name, agent, description)\n\n print_completion(f\"Tool '{tool_name}' created\")\n console.print(f\"[dim]Add to agents/{agent}.py:[/dim]\")\n console.print(f\" [dim]from tools.{tool_name} import {tool_name}[/dim]\\n\")", + "context": "文件: lib/laddr/src/laddr/cli/commands/add.py\n参数: tool_name, agent, description\n调用者: 无", + "related_elements": [ + "add_tool" + ] + }, + { + "pattern_type": "function_implementation", + "description": "function _create_agent_files 的实现和用法", + "code_snippet": "def _create_agent_files(\n agent_name: str,\n role: str,\n goal: str,\n backstory: str,\n llm_provider: str,\n llm_model: str,\n) -> None:\n \"\"\"Create agent files (flat layout) and update configuration.\"\"\"\n renderer = get_template_renderer()\n\n # Ensure agents package exists\n agents_dir = Path(\"agents\")\n agents_dir.mkdir(parents=True, exist_ok=True)\n write_file(agents_dir / \"__init__.py\", \"\")\n\n # Render flat agent module\n agent_context = {\n \"name\": agent_name,\n \"role\": role,\n \"goal\": goal,\n \"backstory\": backstory or \"\",\n \"is_coordinator\": agent_name.lower() == \"coordinator\",\n \"available_agents\": [],\n \"model_env\": f\"{agent_name.upper()}_MODEL\",\n \"model\": llm_model,\n \"temperature\": 0.3 if agent_name.lower() == \"coordinator\" else 0.2,\n \"max_retries\": 3,\n \"max_iterations\": 5 if agent_name.lower() == \"coordinator\" else 15,\n \"timeout\": 600,\n \"instructions\": (\n \"When you receive a task: 1) Analyze 2) Plan 3) Execute using tools 4) Synthesize results.\"\n if agent_name.lower() == \"coordinator\"\n else \"Be concise and cite sources when researching.\"\n ),\n }\n renderer.render_to_file(\"agent_flat.py.j2\", agents_dir / f\"{agent_name}.py\", agent_context)\n\n # Worker entry using WorkerRunner template\n worker_context = {\"agent_name\": agent_name}\n\n # Update laddr.yml\n with open(\"laddr.yml\", \"r\", encoding=\"utf-8\") as f:\n config_data = yaml.safe_load(f)\n\n if agent_name not in config_data[\"project\"].get(\"agents\", []):\n config_data[\"project\"].setdefault(\"agents\", []).append(agent_name)\n\n with open(\"laddr.yml\", \"w\", encoding=\"utf-8\") as f:\n yaml.dump(config_data, f, default_flow_style=False, sort_keys=False)\n\n # Add worker service to docker-compose.yml\n add_worker_to_compose(Path.cwd(), agent_name)\n\n # Update project .env with canonical per-agent LLM settings if present\n # Do not overwrite existing values. Use canonical names: LLM_MODEL_, LLM_BACKEND_\n env_path = Path(\".env\")\n try:\n if env_path.exists():\n existing = env_path.read_text(encoding=\"utf-8\")\n else:\n existing = \"\"\n\n env_lines: list[str] = []\n upper = agent_name.upper()\n model_key = f\"LLM_MODEL_{upper}\"\n backend_key = f\"LLM_BACKEND_{upper}\"\n\n if model_key not in existing:\n env_lines.append(f\"{model_key}={llm_model}\")\n if backend_key not in existing:\n env_lines.append(f\"{backend_key}={llm_provider}\")\n\n if env_lines:\n # Append with a blank line separator if file not empty\n sep = \"\\n\" if existing and not existing.endswith(\"\\n\") else \"\"\n content = existing + sep + \"\\n\".join(env_lines) + \"\\n\"\n write_file(env_path, content)\n print_step(\"Updating .env\", \", \".join(env_lines))\n except Exception:\n # Non-fatal: skip env update if anything goes wrong\n pass", + "context": "文件: lib/laddr/src/laddr/cli/commands/add.py\n参数: agent_name, role, goal, backstory, llm_provider, llm_model\n调用者: add_agent", + "related_elements": [ + "_create_agent_files", + "add_agent" + ] + }, + { + "pattern_type": "function_implementation", + "description": "function _create_tool_file 的实现和用法", + "code_snippet": "def _create_tool_file(tool_name: str, agent_name: str, description: str) -> None:\n \"\"\"Create tool file (project-level) and update agent configuration.\"\"\"\n renderer = get_template_renderer()\n\n # Create tool file in top-level tools directory\n tools_dir = Path(\"tools\")\n tools_dir.mkdir(parents=True, exist_ok=True)\n\n tool_context = {\n \"tool_name\": tool_name,\n \"tool_function_name\": tool_name.lower().replace(\"-\", \"_\"),\n \"tool_description\": description,\n \"tool_params\": \"query: str\", # Default params\n \"params\": [{\"name\": \"query\", \"description\": \"Input query\"}]\n }\n renderer.render_to_file(\"tool.py.j2\", tools_dir / f\"{tool_name}.py\", tool_context)", + "context": "文件: lib/laddr/src/laddr/cli/commands/add.py\n参数: tool_name, agent_name, description\n调用者: add_tool", + "related_elements": [ + "_create_tool_file", + "add_tool" + ] + }, + { + "pattern_type": "function_implementation", + "description": "function logs 的实现和用法", + "code_snippet": "def logs(agent_name: str, follow: bool, tail: int | None):\n \"\"\"View logs for an agent worker.\n \n Examples:\n laddr logs researcher\n laddr logs researcher --follow\n laddr logs researcher --tail 100\n \"\"\"\n # Ensure we're in a project\n if not validate_project_directory(Path.cwd()):\n raise ProjectNotFoundError()\n\n service_name = f\"{agent_name}_worker\"\n print_info(f\"Fetching logs for {service_name}...\")\n\n try:\n compose_logs(service_name, follow=follow, tail=tail)\n except KeyboardInterrupt:\n print_info(\"Stopped following logs\")", + "context": "文件: lib/laddr/src/laddr/cli/commands/management.py\n参数: agent_name, follow, tail\n调用者: 无", + "related_elements": [ + "logs" + ] + }, + { + "pattern_type": "function_implementation", + "description": "function ps 的实现和用法", + "code_snippet": "def ps(format: str):\n \"\"\"Show status of all services and workers.\n \n Examples:\n laddr ps\n laddr ps --format json\n \"\"\"\n # Ensure we're in a project\n if not validate_project_directory(Path.cwd()):\n raise ProjectNotFoundError()\n\n print_info(\"Fetching service status...\")\n\n output = compose_ps()\n\n if format == \"json\":\n # TODO: Parse and format as JSON\n console.print(output)\n else:\n console.print(output)", + "context": "文件: lib/laddr/src/laddr/cli/commands/management.py\n参数: format\n调用者: 无", + "related_elements": [ + "ps" + ] + }, + { + "pattern_type": "function_implementation", + "description": "function scale 的实现和用法", + "code_snippet": "def scale(agent_name: str, replicas: int):\n \"\"\"Scale an agent worker to N replicas.\n \n Examples:\n laddr scale researcher 3\n laddr scale summarizer 0 # Stop all replicas\n \"\"\"\n # Ensure we're in a project\n if not validate_project_directory(Path.cwd()):\n raise ProjectNotFoundError()\n\n if replicas < 0:\n raise click.BadParameter(\"Replicas must be >= 0\")\n\n service_name = f\"{agent_name}_worker\"\n print_info(f\"Scaling {service_name} to {replicas} replicas...\")\n\n compose_scale(service_name, replicas)\n\n print_success(f\"{agent_name} scaled to {replicas} instances\")\n\n # Show updated status\n output = compose_ps([service_name])\n console.print(\"\\n\" + output)", + "context": "文件: lib/laddr/src/laddr/cli/commands/management.py\n参数: agent_name, replicas\n调用者: 无", + "related_elements": [ + "scale" + ] + }, + { + "pattern_type": "function_implementation", + "description": "function stop 的实现和用法", + "code_snippet": "def stop(volumes: bool):\n \"\"\"Stop all Laddr services.\n \n Examples:\n laddr stop\n laddr stop --volumes # Also remove data volumes\n \"\"\"\n # Ensure we're in a project\n if not validate_project_directory(Path.cwd()):\n raise ProjectNotFoundError()\n\n print_info(\"Stopping Laddr services...\")\n\n compose_down(remove_volumes=volumes)\n\n print_success(\"Services stopped\")\n\n if volumes:\n print_info(\"Named volumes removed\")", + "context": "文件: lib/laddr/src/laddr/cli/commands/management.py\n参数: volumes\n调用者: 无", + "related_elements": [ + "stop" + ] + }, + { + "pattern_type": "function_implementation", + "description": "function infra 的实现和用法", + "code_snippet": "def infra(show_config: bool):\n \"\"\"Show infrastructure configuration and status.\"\"\"\n try:\n from laddr.core import LaddrConfig\n\n config = LaddrConfig()\n\n console.print(\"\\n[cyan bold]Laddr Infrastructure[/cyan bold]\\n\")\n\n # Queue backend\n console.print(f\"[green]Queue Backend:[/green] {config.queue_backend}\")\n if config.queue_backend == \"redis\":\n console.print(f\" └─ URL: [dim]{config.redis_url}[/dim]\")\n\n # Database backend\n console.print(f\"\\n[green]Database:[/green] {config.db_backend}\")\n if show_config:\n console.print(f\" └─ URL: [dim]{config.database_url}[/dim]\")\n\n # LLM backend\n console.print(f\"\\n[green]LLM Backend:[/green] {config.llm_backend}\")\n if config.llm_model:\n console.print(f\" └─ Model: [dim]{config.llm_model}[/dim]\")\n\n # Storage\n console.print(\"\\n[green]Storage:[/green] MinIO\")\n if show_config:\n console.print(f\" ├─ Endpoint: [dim]{config.minio_endpoint}[/dim]\")\n console.print(f\" ├─ Bucket: [dim]{config.minio_bucket}[/dim]\")\n console.print(f\" └─ Large response offload: [dim]{'enabled' if config.enable_large_response_storage else 'disabled'}[/dim]\")\n if config.enable_large_response_storage:\n console.print(f\" └─ Threshold: [dim]{config.storage_threshold_kb} KB[/dim]\")\n\n # Cache backend\n console.print(f\"\\n[green]Cache:[/green] {config.cache_backend}\")\n if config.cache_backend == \"redis\" and show_config:\n console.print(f\" └─ URL: [dim]{config.redis_url}[/dim]\")\n\n # Observability\n console.print(\"\\n[green]Observability:[/green]\")\n console.print(f\" ├─ Tracing: [dim]{'enabled (DB)' if config.enable_tracing else 'disabled'}[/dim]\")\n console.print(f\" └─ Metrics: [dim]{'enabled (DB)' if config.enable_metrics else 'disabled'}[/dim]\")\n\n console.print()\n\n except Exception as e:\n print_error(f\"Failed to show infrastructure: {e}\")", + "context": "文件: lib/laddr/src/laddr/cli/commands/infra.py\n参数: show_config\n调用者: 无", + "related_elements": [ + "infra" + ] + }, + { + "pattern_type": "function_implementation", + "description": "function check 的实现和用法", + "code_snippet": "def check(output_path: str):\n \"\"\"Run diagnostic checks for the Laddr runtime using the DiagnosticAgent.\"\"\"\n if not validate_project_directory(Path.cwd()):\n raise ProjectNotFoundError()\n\n try:\n mod = importlib.import_module(\"agents.diagnostic.handler\")\n except Exception:\n print_error(\n \"Diagnostic agent not found. Run: laddr add agent diagnostic\",\n hint=\"This creates agents/diagnostic/handler.py\"\n )\n raise click.Abort()\n\n AgentClass = getattr(mod, \"DiagnosticAgent\", None)\n if AgentClass is None:\n print_error(\"Invalid diagnostic agent module\")\n raise click.Abort()\n\n import asyncio\n\n from laddr.core import AgentRunner, LaddrConfig # type: ignore\n\n print_info(\"Running diagnostic checks...\")\n runner = AgentRunner(env_config=LaddrConfig())\n report = asyncio.run(runner.run({\"payload\": {}}, agent_name=\"diagnostic\"))\n\n results = report.get(\"results\", [])\n ok_count = 0\n for r in results:\n status = r.get(\"status\")\n subsystem = r.get(\"subsystem\")\n message = r.get(\"message\")\n prefix = \"[green]✓[/green]\" if status == \"ok\" else \"[red]✗[/red]\"\n console.print(f\"{prefix} {subsystem}: {message}\")\n if status == \"ok\":\n ok_count += 1\n\n overall_ok = ok_count == len(results)\n if overall_ok:\n console.print(\"\\n[bold green]✅ Diagnostic complete: all subsystems operational[/bold green]\")\n else:\n console.print(\"\\n[bold red]❌ Diagnostic complete: issues detected[/bold red]\")\n\n try:\n with open(output_path, \"w\", encoding=\"utf-8\") as f:\n json.dump(report, f, indent=2, ensure_ascii=False)\n print_success(f\"Report saved to {output_path}\")\n except Exception as e:\n print_error(f\"Failed to write report: {e}\")", + "context": "文件: lib/laddr/src/laddr/cli/commands/check.py\n参数: output_path\n调用者: 无", + "related_elements": [ + "check" + ] + }, + { + "pattern_type": "function_implementation", + "description": "function init 的实现和用法", + "code_snippet": "def init(project_name: str | None, path: str):\n \"\"\"Initialize a new Laddr project.\n \n Creates project structure with:\n - Configuration files (laddr.yml, .env)\n - Docker setup (docker-compose.yml, Dockerfile)\n - Default researcher agent\n - Example tools and pipeline\n \n Users import from laddr package:\n from laddr import Agent, AgentRunner, run_agent, actor, tool\n \n Examples:\n laddr init myproject\n laddr init myproject --path /path/to/dir\n laddr init # Interactive mode\n \"\"\"\n # Prompt for project name if not provided\n if not project_name:\n from ..utils import console\n console.print()\n # Use Rich console input to render markup (colors) correctly instead of\n # passing markup through Click which prints raw markup tags.\n # Console.input supports markup=True so the prompt will be colored.\n project_name = console.input(\"[cyan]Project name: [/cyan]\", markup=True).strip()\n\n # Resolve project path\n project_path = Path(path)\n if project_name and project_name != \".\":\n project_path = project_path / project_name\n project_path = project_path.resolve()\n\n # Check if project already exists\n if project_path.exists() and validate_project_directory(project_path):\n raise ProjectExistsError(str(project_path))\n\n # Check for nested project\n for parent in project_path.parents:\n if validate_project_directory(parent):\n raise NestedProjectError(str(parent))\n\n # Handle in-place creation\n create_in_place = False\n if project_name in (\".\", \"\"):\n create_in_place = True\n else:\n cwd_name = Path.cwd().name\n if path in (\".\", \"\") and project_name == cwd_name:\n create_in_place = True\n\n if create_in_place:\n project_path = Path.cwd().resolve()\n\n # Check if directory is empty (except for existing laddr.yml)\n if project_path.exists() and any(project_path.iterdir()):\n if not validate_project_directory(project_path):\n raise ProjectExistsError(str(project_path))\n\n # Print header\n print_header(f\"Initializing Laddr Project → {project_path.name}\")\n\n # Create directory structure\n print_step(\"Creating project structure\")\n _create_directories(project_path)\n\n # Generate core modules (no-op in V3.0)\n _generate_core_modules(project_path)\n\n # Generate configuration files\n print_step(\"Creating configuration\", \"laddr.yml, .env\")\n _generate_config_files(project_path, project_path.name)\n\n # Generate Docker setup\n print_step(\"Setting up Docker\", \"compose, Dockerfile, requirements\")\n _generate_docker_setup(project_path)\n\n # Generate default agent\n print_step(\"Creating agents\", \"coordinator, researcher\")\n _generate_default_agent(project_path)\n\n # Generate README\n print_step(\"Generating documentation\", \"README.md\")\n _generate_readme(project_path, project_path.name)\n\n # Success message\n print_completion(f\"Project created at {project_path}\")\n\n # Next steps\n from ..utils import console\n \n console.print(\"[dim]Get started:[/dim]\")\n if project_path != Path.cwd():\n console.print(f\" [cyan]cd {project_path}[/cyan]\")\n console.print(f\" [cyan]laddr run dev[/cyan]\\n\")", + "context": "文件: lib/laddr/src/laddr/cli/commands/init.py\n参数: project_name, path\n调用者: 无", + "related_elements": [ + "init" + ] + }, + { + "pattern_type": "function_implementation", + "description": "function _create_directories 的实现和用法", + "code_snippet": "def _create_directories(project_path: Path) -> None:\n \"\"\"Create project directory structure.\"\"\"\n directories = [\n project_path,\n project_path / \"agents\",\n project_path / \"tools\",\n ]\n\n for directory in directories:\n directory.mkdir(parents=True, exist_ok=True)", + "context": "文件: lib/laddr/src/laddr/cli/commands/init.py\n参数: project_path\n调用者: init", + "related_elements": [ + "_create_directories", + "init" + ] + }, + { + "pattern_type": "function_implementation", + "description": "function _generate_core_modules 的实现和用法", + "code_snippet": "def _generate_core_modules(project_path: Path) -> None:\n \"\"\"\n V3.0: Users import from laddr package, no need to generate core files.\n \n This function is kept for backward compatibility but does nothing.\n Users should use: from laddr import Agent, AgentRunner, run_agent\n \"\"\"", + "context": "文件: lib/laddr/src/laddr/cli/commands/init.py\n参数: project_path\n调用者: init", + "related_elements": [ + "_generate_core_modules", + "init" + ] + }, + { + "pattern_type": "function_implementation", + "description": "function _generate_config_files 的实现和用法", + "code_snippet": "def _generate_config_files(project_path: Path, project_name: str) -> None:\n \"\"\"Generate configuration files.\"\"\"\n renderer = get_template_renderer()\n\n # laddr.yml\n config = ProjectConfigSchema(\n project=ProjectDetails(\n name=project_name,\n broker=\"redis\",\n database=\"postgres\",\n storage=\"minio\",\n tracing=True,\n metrics=True,\n agents=[],\n )\n )\n config.save_to_file(project_path / \"laddr.yml\")\n\n # .env\n renderer.render_to_file(\"dotenv.j2\", project_path / \".env\", {})", + "context": "文件: lib/laddr/src/laddr/cli/commands/init.py\n参数: project_path, project_name\n调用者: init", + "related_elements": [ + "_generate_config_files", + "init" + ] + }, + { + "pattern_type": "function_implementation", + "description": "function _generate_docker_setup 的实现和用法", + "code_snippet": "def _generate_docker_setup(project_path: Path) -> None:\n \"\"\"Generate Docker configuration.\"\"\"\n renderer = get_template_renderer()\n\n renderer.render_to_file(\"docker-compose.yml.j2\", project_path / \"docker-compose.yml\", {})\n renderer.render_to_file(\"Dockerfile.j2\", project_path / \"Dockerfile\", {})\n renderer.render_to_file(\"requirements.txt.j2\", project_path / \"requirements.txt\", {})", + "context": "文件: lib/laddr/src/laddr/cli/commands/init.py\n参数: project_path\n调用者: init", + "related_elements": [ + "_generate_docker_setup", + "init" + ] + }, + { + "pattern_type": "function_implementation", + "description": "function _generate_default_agent 的实现和用法", + "code_snippet": "def _generate_default_agent(project_path: Path) -> None:\n \"\"\"Generate default coordinator and researcher agents.\"\"\"\n renderer = get_template_renderer()\n\n # Create agents directory __init__\n write_file(project_path / \"agents\" / \"__init__.py\", \"\")\n\n # Define both default agents (flat files in agents/)\n default_agents = [\n {\n \"name\": \"coordinator\",\n \"role\": \"Orchestration Coordinator & Task Manager\",\n \"goal\": \"Analyze complex user requests, break them into specific subtasks, delegate each subtask to the appropriate specialist agent, and synthesize their responses into a comprehensive final answer\",\n \"backstory\": \"An experienced orchestrator who excels at task decomposition and delegation. You never perform research yourself - instead, you coordinate a team of specialists. You understand that effective delegation requires clear, specific instructions and that your role is to combine specialist outputs into coherent, complete responses for users.\",\n \"is_coordinator\": True,\n \"available_agents\": [\"researcher\"],\n },\n {\n \"name\": \"researcher\",\n \"role\": \"Web Research Specialist & Information Analyst\",\n \"goal\": \"Conduct thorough web research using available tools (web_search, scrape_url, extract_links) to find accurate, relevant information and deliver comprehensive, well-sourced answers\",\n \"backstory\": \"A meticulous research specialist with expertise in web searching, content extraction, and information synthesis. You have direct access to powerful web tools and know how to use them effectively. Your strength is finding high-quality sources, extracting key information, and presenting findings clearly with proper citations. You prioritize accuracy and always verify information across multiple sources when possible.\",\n \"is_coordinator\": False,\n \"available_agents\": [],\n }\n ]\n\n agent_names = []\n\n for agent_config in default_agents:\n agent_name = agent_config[\"name\"]\n agent_names.append(agent_name)\n\n # agents/__init__.py once\n write_file(project_path / \"agents\" / \"__init__.py\", \"\")\n\n # Render flat agent file\n agent_context = {\n \"name\": agent_name,\n \"role\": agent_config[\"role\"],\n \"goal\": agent_config[\"goal\"],\n \"backstory\": agent_config[\"backstory\"],\n \"is_coordinator\": agent_config.get(\"is_coordinator\", False),\n \"available_agents\": agent_config.get(\"available_agents\", []),\n \"model_env\": f\"{agent_name.upper()}_MODEL\",\n \"model\": \"gemini-2.5-flash\",\n \"fallback_model_env\": f\"{agent_name.upper()}_FALLBACK_MODEL\",\n \"fallback_model\": \"gpt-3.5-turbo\",\n \"temperature\": 0.3 if agent_config.get(\"is_coordinator\") else 0.2,\n \"max_retries\": 3,\n \"max_iterations\": 5 if agent_config.get(\"is_coordinator\") else 15,\n \"timeout\": 600,\n \"instructions\": (\n \"When you receive a task: 1) Analyze 2) Plan 3) Execute using tools 4) Synthesize results.\"\n if agent_config.get(\"is_coordinator\")\n else \"Be concise and cite sources when researching.\"\n ),\n }\n renderer.render_to_file(\"agent_flat.py.j2\", project_path / \"agents\" / f\"{agent_name}.py\", agent_context)\n\n # Add worker to docker-compose\n add_worker_to_compose(project_path, agent_name)\n\n # Tools package and defaults\n write_file(project_path / \"tools\" / \"__init__.py\", \"\")\n renderer.render_to_file(\"tools_web.py.j2\", project_path / \"tools\" / \"web_tools.py\", {})\n renderer.render_to_file(\"tools_communication.py.j2\", project_path / \"tools\" / \"communication_tools.py\", {})\n\n # Main entry - simple runner selecting agent by env\n renderer.render_to_file(\"main_flat.py.j2\", project_path / \"main.py\", {})\n\n # Update laddr.yml to add both agents\n config_path = project_path / \"laddr.yml\"\n with open(config_path, \"r\", encoding=\"utf-8\") as f:\n config_data = yaml.safe_load(f)\n\n config_data[\"project\"][\"agents\"] = agent_names\n\n with open(config_path, \"w\", encoding=\"utf-8\") as f:\n yaml.dump(config_data, f, default_flow_style=False, sort_keys=False)", + "context": "文件: lib/laddr/src/laddr/cli/commands/init.py\n参数: project_path\n调用者: init", + "related_elements": [ + "_generate_default_agent", + "init" + ] + }, + { + "pattern_type": "function_implementation", + "description": "function _generate_readme 的实现和用法", + "code_snippet": "def _generate_readme(project_path: Path, project_name: str) -> None:\n \"\"\"Generate project README.\"\"\"\n renderer = get_template_renderer()\n renderer.render_to_file(\n \"README.md.j2\",\n project_path / \"README.md\",\n {\"project_name\": project_name},\n )", + "context": "文件: lib/laddr/src/laddr/cli/commands/init.py\n参数: project_path, project_name\n调用者: init", + "related_elements": [ + "_generate_readme", + "init" + ] + }, + { + "pattern_type": "function_implementation", + "description": "method ProjectConfigSchema.load_from_file 的实现和用法", + "code_snippet": " def load_from_file(cls, path: Path) -> ProjectConfigSchema:\n \"\"\"Load project config from YAML file.\n\n Args:\n path: Path to laddr.yml\n\n Returns:\n Parsed project configuration\n\n Raises:\n InvalidConfigError: If config is invalid\n \"\"\"\n try:\n with open(path, \"r\", encoding=\"utf-8\") as f:\n data = yaml.safe_load(f)\n return cls(**data)\n except FileNotFoundError:\n raise InvalidConfigError(str(path), \"File not found\")\n except yaml.YAMLError as e:\n raise InvalidConfigError(str(path), f\"Invalid YAML: {e}\")\n except Exception as e:\n raise InvalidConfigError(str(path), str(e))", + "context": "文件: lib/laddr/src/laddr/cli/utils/config.py\n参数: cls, path\n调用者: 无", + "related_elements": [ + "ProjectConfigSchema.load_from_file" + ] + }, + { + "pattern_type": "function_implementation", + "description": "method ProjectConfigSchema.save_to_file 的实现和用法", + "code_snippet": " def save_to_file(self, path: Path) -> None:\n \"\"\"Save project config to YAML file.\n\n Args:\n path: Path to laddr.yml\n\n Raises:\n InvalidConfigError: If save fails\n \"\"\"\n try:\n with open(path, \"w\", encoding=\"utf-8\") as f:\n yaml.dump(\n self.model_dump(exclude_none=True),\n f,\n default_flow_style=False,\n sort_keys=False,\n )\n except Exception as e:\n raise InvalidConfigError(str(path), f\"Failed to save: {e}\")", + "context": "文件: lib/laddr/src/laddr/cli/utils/config.py\n参数: self, path\n调用者: _generate_config_files", + "related_elements": [ + "ProjectConfigSchema.save_to_file", + "_generate_config_files" + ] + }, + { + "pattern_type": "function_implementation", + "description": "method ProjectDetails.validate_name 的实现和用法", + "code_snippet": " def validate_name(cls, v: str) -> str:\n \"\"\"Validate project name format.\"\"\"\n if not v.replace(\"_\", \"\").replace(\"-\", \"\").isalnum():\n raise ValueError(\"Project name must be alphanumeric (with underscores/hyphens)\")\n return v", + "context": "文件: lib/laddr/src/laddr/cli/utils/config.py\n参数: cls, v\n调用者: 无", + "related_elements": [ + "ProjectDetails.validate_name" + ] + }, + { + "pattern_type": "function_implementation", + "description": "function validate_project_directory 的实现和用法", + "code_snippet": "def validate_project_directory(path: Path) -> bool:\n \"\"\"Check if a directory contains a valid Laddr project.\n\n Args:\n path: Directory path to check\n\n Returns:\n True if valid project directory\n \"\"\"\n return (path / \"laddr.yml\").exists()", + "context": "文件: lib/laddr/src/laddr/cli/utils/config.py\n参数: path\n调用者: run_dev, run_pipeline, run_agent_cmd", + "related_elements": [ + "validate_project_directory", + "run_dev", + "run_pipeline", + "run_agent_cmd" + ] + }, + { + "pattern_type": "function_implementation", + "description": "function get_logger 的实现和用法", + "code_snippet": "def get_logger(name: str, level: int = logging.INFO) -> logging.Logger:\n \"\"\"Get a configured logger with rich handler.\n\n Args:\n name: Logger name (usually __name__)\n level: Log level (default: INFO)\n\n Returns:\n Configured logger instance\n \"\"\"\n logger = logging.getLogger(name)\n logger.setLevel(level)\n\n # Remove existing handlers to avoid duplicates\n logger.handlers.clear()\n\n # Add rich handler\n handler = RichHandler(\n console=console,\n show_time=False,\n show_path=False,\n markup=True,\n rich_tracebacks=True,\n )\n handler.setFormatter(logging.Formatter(\"%(message)s\"))\n logger.addHandler(handler)\n\n return logger", + "context": "文件: lib/laddr/src/laddr/cli/utils/logger.py\n参数: name, level\n调用者: 无", + "related_elements": [ + "get_logger" + ] + }, + { + "pattern_type": "function_implementation", + "description": "function print_success 的实现和用法", + "code_snippet": "def print_success(message: str, details: str | None = None) -> None:\n \"\"\"Print a success message.\n\n Args:\n message: Main success message\n details: Optional additional details\n \"\"\"\n console.print(f\"[success]✓[/success] {message}\")\n if details:\n console.print(f\" {details}\", style=\"dim\")", + "context": "文件: lib/laddr/src/laddr/cli/utils/logger.py\n参数: message, details\n调用者: run_prompt, run_dev, run_pipeline", + "related_elements": [ + "print_success", + "run_prompt", + "run_dev", + "run_pipeline" + ] + }, + { + "pattern_type": "function_implementation", + "description": "function print_error 的实现和用法", + "code_snippet": "def print_error(message: str, hint: str | None = None) -> None:\n \"\"\"Print an error message.\n\n Args:\n message: Error message\n hint: Optional hint for resolution\n \"\"\"\n console.print(f\"[error]✗[/error] {message}\")\n if hint:\n console.print(f\" [dim]Hint:[/dim] {hint}\", style=\"yellow\")", + "context": "文件: lib/laddr/src/laddr/cli/utils/logger.py\n参数: message, hint\n调用者: main, run_prompt, list_prompts", + "related_elements": [ + "print_error", + "main", + "run_prompt", + "list_prompts" + ] + }, + { + "pattern_type": "function_implementation", + "description": "function print_warning 的实现和用法", + "code_snippet": "def print_warning(message: str) -> None:\n \"\"\"Print a warning message.\n\n Args:\n message: Warning message\n \"\"\"\n console.print(f\"[warning]![/warning] {message}\")", + "context": "文件: lib/laddr/src/laddr/cli/utils/logger.py\n参数: message\n调用者: 无", + "related_elements": [ + "print_warning" + ] + }, + { + "pattern_type": "function_implementation", + "description": "function print_info 的实现和用法", + "code_snippet": "def print_info(message: str) -> None:\n \"\"\"Print an info message.\n\n Args:\n message: Info message\n \"\"\"\n console.print(f\"[info]ℹ[/info] {message}\")", + "context": "文件: lib/laddr/src/laddr/cli/utils/logger.py\n参数: message\n调用者: run_dev, run_agent_cmd, replay_job", + "related_elements": [ + "print_info", + "run_dev", + "run_agent_cmd", + "replay_job" + ] + }, + { + "pattern_type": "function_implementation", + "description": "function print_panel 的实现和用法", + "code_snippet": "def print_panel(title: str, content: str, style: str = \"cyan\") -> None:\n \"\"\"Print a styled panel.\n\n Args:\n title: Panel title\n content: Panel content\n style: Panel border style (default: cyan)\n \"\"\"\n console.print(Panel(content, title=f\"[bold]{title}[/bold]\", border_style=style))", + "context": "文件: lib/laddr/src/laddr/cli/utils/logger.py\n参数: title, content, style\n调用者: 无", + "related_elements": [ + "print_panel" + ] + }, + { + "pattern_type": "function_implementation", + "description": "function print_header 的实现和用法", + "code_snippet": "def print_header(text: str, style: str = \"bold cyan\") -> None:\n \"\"\"Print a minimalistic header.\n\n Args:\n text: Header text\n style: Text style (default: bold cyan)\n \"\"\"\n console.print(f\"\\n[{style}]{text}[/{style}]\")", + "context": "文件: lib/laddr/src/laddr/cli/utils/logger.py\n参数: text, style\n调用者: run_dev, run_pipeline, run_agent_cmd", + "related_elements": [ + "print_header", + "run_dev", + "run_pipeline", + "run_agent_cmd" + ] + }, + { + "pattern_type": "function_implementation", + "description": "function print_step 的实现和用法", + "code_snippet": "def print_step(step: str, description: str = \"\") -> None:\n \"\"\"Print a step in a process with minimalistic style.\n\n Args:\n step: Step name/action\n description: Optional description\n \"\"\"\n if description:\n console.print(f\" [dim]→[/dim] {step} [dim]{description}[/dim]\")\n else:\n console.print(f\" [dim]→[/dim] {step}\")", + "context": "文件: lib/laddr/src/laddr/cli/utils/logger.py\n参数: step, description\n调用者: add_agent, add_tool, _create_agent_files", + "related_elements": [ + "print_step", + "add_agent", + "add_tool", + "_create_agent_files" + ] + }, + { + "pattern_type": "function_implementation", + "description": "function print_completion 的实现和用法", + "code_snippet": "def print_completion(message: str) -> None:\n \"\"\"Print a completion message with checkmark.\n\n Args:\n message: Completion message\n \"\"\"\n console.print(f\"\\n[success]✓[/success] {message}\\n\")", + "context": "文件: lib/laddr/src/laddr/cli/utils/logger.py\n参数: message\n调用者: add_agent, add_tool, init", + "related_elements": [ + "print_completion", + "add_agent", + "add_tool", + "init" + ] + }, + { + "pattern_type": "function_implementation", + "description": "function print_table 的实现和用法", + "code_snippet": "def print_table(data: list[dict], title: str | None = None) -> None:\n \"\"\"Print data as a rich table.\n\n Args:\n data: List of dictionaries (each dict is a row)\n title: Optional table title\n \"\"\"\n from rich.table import Table\n\n if not data:\n console.print(\"[dim]No data to display[/dim]\")\n return\n\n table = Table(title=title, show_header=True, header_style=\"bold cyan\")\n\n # Add columns from first row keys\n for key in data[0].keys():\n table.add_column(key, style=\"white\")\n\n # Add rows\n for row in data:\n table.add_row(*[str(v) for v in row.values()])\n\n console.print(table)", + "context": "文件: lib/laddr/src/laddr/cli/utils/logger.py\n参数: data, title\n调用者: 无", + "related_elements": [ + "print_table" + ] + }, + { + "pattern_type": "function_implementation", + "description": "method TemplateRenderer.__init__ 的实现和用法", + "code_snippet": " def __init__(self, templates_dir: Path):\n \"\"\"Initialize template renderer.\n\n Args:\n templates_dir: Directory containing Jinja2 templates\n \"\"\"\n self.templates_dir = templates_dir\n self.env = Environment(\n loader=FileSystemLoader(str(templates_dir)),\n trim_blocks=True,\n lstrip_blocks=True,\n keep_trailing_newline=True,\n )\n\n # Add custom filters\n self.env.filters[\"snake_case\"] = self._snake_case\n self.env.filters[\"pascal_case\"] = self._pascal_case\n self.env.filters[\"kebab_case\"] = self._kebab_case", + "context": "文件: lib/laddr/src/laddr/cli/utils/templates.py\n参数: self, templates_dir\n调用者: LaddrError.__init__, ProjectNotFoundError.__init__, ProjectExistsError.__init__", + "related_elements": [ + "TemplateRenderer.__init__", + "LaddrError.__init__", + "ProjectNotFoundError.__init__", + "ProjectExistsError.__init__" + ] + }, + { + "pattern_type": "function_implementation", + "description": "method TemplateRenderer._snake_case 的实现和用法", + "code_snippet": " def _snake_case(value: str) -> str:\n \"\"\"Convert string to snake_case.\"\"\"\n return value.replace(\"-\", \"_\").replace(\" \", \"_\").lower()", + "context": "文件: lib/laddr/src/laddr/cli/utils/templates.py\n参数: value\n调用者: 无", + "related_elements": [ + "TemplateRenderer._snake_case" + ] + }, + { + "pattern_type": "function_implementation", + "description": "method TemplateRenderer._pascal_case 的实现和用法", + "code_snippet": " def _pascal_case(value: str) -> str:\n \"\"\"Convert string to PascalCase.\"\"\"\n return \"\".join(word.capitalize() for word in value.replace(\"-\", \"_\").split(\"_\"))", + "context": "文件: lib/laddr/src/laddr/cli/utils/templates.py\n参数: value\n调用者: 无", + "related_elements": [ + "TemplateRenderer._pascal_case" + ] + }, + { + "pattern_type": "function_implementation", + "description": "method TemplateRenderer._kebab_case 的实现和用法", + "code_snippet": " def _kebab_case(value: str) -> str:\n \"\"\"Convert string to kebab-case.\"\"\"\n return value.replace(\"_\", \"-\").replace(\" \", \"-\").lower()", + "context": "文件: lib/laddr/src/laddr/cli/utils/templates.py\n参数: value\n调用者: 无", + "related_elements": [ + "TemplateRenderer._kebab_case" + ] + }, + { + "pattern_type": "function_implementation", + "description": "method TemplateRenderer.render_template 的实现和用法", + "code_snippet": " def render_template(self, template_name: str, context: dict[str, Any]) -> str:\n \"\"\"Render a template with context.\n\n Args:\n template_name: Template filename\n context: Template context variables\n\n Returns:\n Rendered template content\n\n Raises:\n FileGenerationError: If template rendering fails\n \"\"\"\n try:\n template = self.env.get_template(template_name)\n return template.render(**context)\n except Exception as e:\n raise FileGenerationError(template_name, str(e))", + "context": "文件: lib/laddr/src/laddr/cli/utils/templates.py\n参数: self, template_name, context\n调用者: TemplateRenderer.render_to_file", + "related_elements": [ + "TemplateRenderer.render_template", + "TemplateRenderer.render_to_file" + ] + }, + { + "pattern_type": "function_implementation", + "description": "method TemplateRenderer.render_to_file 的实现和用法", + "code_snippet": " def render_to_file(\n self,\n template_name: str,\n output_path: Path,\n context: dict[str, Any],\n create_dirs: bool = True,\n ) -> None:\n \"\"\"Render a template and write to file.\n\n Args:\n template_name: Template filename\n output_path: Output file path\n context: Template context variables\n create_dirs: Create parent directories if needed (default: True)\n\n Raises:\n FileGenerationError: If rendering or writing fails\n \"\"\"\n content = self.render_template(template_name, context)\n\n try:\n if create_dirs:\n output_path.parent.mkdir(parents=True, exist_ok=True)\n\n output_path.write_text(content, encoding=\"utf-8\")\n except Exception as e:\n raise FileGenerationError(str(output_path), str(e))", + "context": "文件: lib/laddr/src/laddr/cli/utils/templates.py\n参数: self, template_name, output_path, context, create_dirs\n调用者: _create_agent_files, _create_tool_file, _generate_config_files", + "related_elements": [ + "TemplateRenderer.render_to_file", + "_create_agent_files", + "_create_tool_file", + "_generate_config_files" + ] + }, + { + "pattern_type": "function_implementation", + "description": "method TemplateRenderer.render_string 的实现和用法", + "code_snippet": " def render_string(self, template_str: str, context: dict[str, Any]) -> str:\n \"\"\"Render a template string with context.\n\n Args:\n template_str: Template string\n context: Template context variables\n\n Returns:\n Rendered content\n\n Raises:\n FileGenerationError: If rendering fails\n \"\"\"\n try:\n template = Template(template_str)\n return template.render(**context)\n except Exception as e:\n raise FileGenerationError(\"inline template\", str(e))", + "context": "文件: lib/laddr/src/laddr/cli/utils/templates.py\n参数: self, template_str, context\n调用者: 无", + "related_elements": [ + "TemplateRenderer.render_string" + ] + }, + { + "pattern_type": "function_implementation", + "description": "function get_template_renderer 的实现和用法", + "code_snippet": "def get_template_renderer() -> TemplateRenderer:\n \"\"\"Get a configured template renderer.\n\n Returns:\n TemplateRenderer instance with CLI templates directory\n \"\"\"\n # Find templates directory relative to this file\n cli_dir = Path(__file__).parent.parent\n templates_dir = cli_dir / \"templates\"\n\n if not templates_dir.exists():\n raise FileGenerationError(\n \"templates directory\",\n f\"Templates directory not found at {templates_dir}\",\n )\n\n return TemplateRenderer(templates_dir)", + "context": "文件: lib/laddr/src/laddr/cli/utils/templates.py\n参数: \n调用者: _create_agent_files, _create_tool_file, _generate_config_files", + "related_elements": [ + "get_template_renderer", + "_create_agent_files", + "_create_tool_file", + "_generate_config_files" + ] + }, + { + "pattern_type": "function_implementation", + "description": "function write_file 的实现和用法", + "code_snippet": "def write_file(path: Path, content: str, create_dirs: bool = True) -> None:\n \"\"\"Write content to a file.\n\n Args:\n path: File path\n content: File content\n create_dirs: Create parent directories if needed (default: True)\n\n Raises:\n FileGenerationError: If write fails\n \"\"\"\n try:\n if create_dirs:\n path.parent.mkdir(parents=True, exist_ok=True)\n\n path.write_text(content, encoding=\"utf-8\")\n except Exception as e:\n raise FileGenerationError(str(path), str(e))", + "context": "文件: lib/laddr/src/laddr/cli/utils/templates.py\n参数: path, content, create_dirs\n调用者: _create_agent_files, _generate_default_agent", + "related_elements": [ + "write_file", + "_create_agent_files", + "_generate_default_agent" + ] + }, + { + "pattern_type": "function_implementation", + "description": "function check_docker 的实现和用法", + "code_snippet": "def check_docker() -> bool:\n \"\"\"Check if Docker is installed and running.\n\n Returns:\n True if Docker is available\n\n Raises:\n DockerNotFoundError: If Docker is not found or not running\n \"\"\"\n try:\n result = subprocess.run(\n [\"docker\", \"version\"],\n capture_output=True,\n text=True,\n check=False,\n timeout=5,\n )\n if result.returncode != 0:\n raise DockerNotFoundError()\n return True\n except (FileNotFoundError, subprocess.TimeoutExpired):\n raise DockerNotFoundError()", + "context": "文件: lib/laddr/src/laddr/cli/utils/docker.py\n参数: \n调用者: run_dev", + "related_elements": [ + "check_docker", + "run_dev" + ] + }, + { + "pattern_type": "function_implementation", + "description": "function check_docker_compose 的实现和用法", + "code_snippet": "def check_docker_compose() -> bool:\n \"\"\"Check if Docker Compose is available.\n\n Returns:\n True if Docker Compose is available\n\n Raises:\n DockerNotFoundError: If Docker Compose is not found\n \"\"\"\n try:\n result = subprocess.run(\n [\"docker\", \"compose\", \"version\"],\n capture_output=True,\n text=True,\n check=False,\n timeout=5,\n )\n if result.returncode != 0:\n raise DockerNotFoundError()\n return True\n except (FileNotFoundError, subprocess.TimeoutExpired):\n raise DockerNotFoundError()", + "context": "文件: lib/laddr/src/laddr/cli/utils/docker.py\n参数: \n调用者: run_dev", + "related_elements": [ + "check_docker_compose", + "run_dev" + ] + }, + { + "pattern_type": "function_implementation", + "description": "function compose_up 的实现和用法", + "code_snippet": "def compose_up(\n services: list[str] | None = None,\n detach: bool = True,\n build: bool = False,\n scale: dict[str, int] | None = None,\n) -> None:\n \"\"\"Start Docker Compose services.\n\n Args:\n services: Optional list of specific services to start\n detach: Run in detached mode (default: True)\n build: Force rebuild images (default: False)\n scale: Optional dict of service: replicas for scaling\n\n Raises:\n DockerComposeError: If compose up fails\n \"\"\"\n cmd = [\"docker\", \"compose\", \"up\"]\n\n if detach:\n cmd.append(\"-d\")\n\n if build:\n cmd.append(\"--build\")\n\n if scale:\n for service, replicas in scale.items():\n cmd.extend([\"--scale\", f\"{service}={replicas}\"])\n\n if services:\n cmd.extend(services)\n\n try:\n result = subprocess.run(\n cmd,\n capture_output=True,\n text=True,\n check=False,\n timeout=300, # 5 minutes timeout\n )\n if result.returncode != 0:\n raise DockerComposeError(\"up\", result.stderr or result.stdout)\n except subprocess.TimeoutExpired:\n raise DockerComposeError(\"up\", \"Operation timed out after 5 minutes\")\n except FileNotFoundError:\n raise DockerNotFoundError()", + "context": "文件: lib/laddr/src/laddr/cli/utils/docker.py\n参数: services, detach, build, scale\n调用者: 无", + "related_elements": [ + "compose_up" + ] + }, + { + "pattern_type": "function_implementation", + "description": "function compose_down 的实现和用法", + "code_snippet": "def compose_down(remove_volumes: bool = False) -> None:\n \"\"\"Stop Docker Compose services.\n\n Args:\n remove_volumes: Remove named volumes (default: False)\n\n Raises:\n DockerComposeError: If compose down fails\n \"\"\"\n cmd = [\"docker\", \"compose\", \"down\"]\n\n if remove_volumes:\n cmd.append(\"-v\")\n\n try:\n result = subprocess.run(\n cmd,\n capture_output=True,\n text=True,\n check=False,\n timeout=60,\n )\n if result.returncode != 0:\n raise DockerComposeError(\"down\", result.stderr or result.stdout)\n except subprocess.TimeoutExpired:\n raise DockerComposeError(\"down\", \"Operation timed out\")\n except FileNotFoundError:\n raise DockerNotFoundError()", + "context": "文件: lib/laddr/src/laddr/cli/utils/docker.py\n参数: remove_volumes\n调用者: stop", + "related_elements": [ + "compose_down", + "stop" + ] + }, + { + "pattern_type": "function_implementation", + "description": "function compose_ps 的实现和用法", + "code_snippet": "def compose_ps(services: list[str] | None = None) -> str:\n \"\"\"Get status of Docker Compose services.\n\n Args:\n services: Optional list of specific services to check\n\n Returns:\n Status output as string\n\n Raises:\n DockerComposeError: If compose ps fails\n \"\"\"\n cmd = [\"docker\", \"compose\", \"ps\"]\n\n if services:\n cmd.extend(services)\n\n try:\n result = subprocess.run(\n cmd,\n capture_output=True,\n text=True,\n check=False,\n timeout=30,\n )\n if result.returncode != 0:\n raise DockerComposeError(\"ps\", result.stderr or result.stdout)\n return result.stdout\n except subprocess.TimeoutExpired:\n raise DockerComposeError(\"ps\", \"Operation timed out\")\n except FileNotFoundError:\n raise DockerNotFoundError()", + "context": "文件: lib/laddr/src/laddr/cli/utils/docker.py\n参数: services\n调用者: ps, scale", + "related_elements": [ + "compose_ps", + "ps", + "scale" + ] + }, + { + "pattern_type": "function_implementation", + "description": "function compose_logs 的实现和用法", + "code_snippet": "def compose_logs(\n service: str,\n follow: bool = False,\n tail: int | None = None,\n) -> None:\n \"\"\"View logs for a Docker Compose service.\n\n Args:\n service: Service name\n follow: Follow log output (default: False)\n tail: Number of lines to show from end (default: all)\n\n Raises:\n DockerComposeError: If compose logs fails\n \"\"\"\n cmd = [\"docker\", \"compose\", \"logs\"]\n\n if follow:\n cmd.append(\"-f\")\n\n if tail is not None:\n cmd.extend([\"--tail\", str(tail)])\n\n cmd.append(service)\n\n try:\n subprocess.run(cmd, check=False)\n except FileNotFoundError:\n raise DockerNotFoundError()\n except KeyboardInterrupt:\n # User stopped following logs\n pass", + "context": "文件: lib/laddr/src/laddr/cli/utils/docker.py\n参数: service, follow, tail\n调用者: logs", + "related_elements": [ + "compose_logs", + "logs" + ] + }, + { + "pattern_type": "function_implementation", + "description": "function compose_scale 的实现和用法", + "code_snippet": "def compose_scale(service: str, replicas: int) -> None:\n \"\"\"Scale a Docker Compose service.\n\n Args:\n service: Service name\n replicas: Number of replicas\n\n Raises:\n DockerComposeError: If scaling fails\n \"\"\"\n if replicas < 0:\n raise ValueError(\"Replicas must be >= 0\")\n\n try:\n result = subprocess.run(\n [\"docker\", \"compose\", \"up\", \"--scale\", f\"{service}={replicas}\", \"-d\"],\n capture_output=True,\n text=True,\n check=False,\n timeout=120,\n )\n if result.returncode != 0:\n raise DockerComposeError(\"scale\", result.stderr or result.stdout)\n except subprocess.TimeoutExpired:\n raise DockerComposeError(\"scale\", \"Operation timed out\")\n except FileNotFoundError:\n raise DockerNotFoundError()", + "context": "文件: lib/laddr/src/laddr/cli/utils/docker.py\n参数: service, replicas\n调用者: scale", + "related_elements": [ + "compose_scale", + "scale" + ] + }, + { + "pattern_type": "function_implementation", + "description": "function wait_for_service 的实现和用法", + "code_snippet": "def wait_for_service(\n service: str,\n timeout: int = 30,\n check_interval: int = 2,\n) -> bool:\n \"\"\"Wait for a service to be healthy.\n\n Args:\n service: Service name\n timeout: Maximum seconds to wait\n check_interval: Seconds between checks\n\n Returns:\n True if service is healthy, False otherwise\n \"\"\"\n print_info(f\"Waiting for {service} to be ready...\")\n\n elapsed = 0\n while elapsed < timeout:\n try:\n result = subprocess.run(\n [\"docker\", \"compose\", \"ps\", \"--format\", \"json\", service],\n capture_output=True,\n text=True,\n check=False,\n timeout=10,\n )\n if result.returncode == 0 and \"running\" in result.stdout.lower():\n print_success(f\"{service} is ready\")\n return True\n except (subprocess.TimeoutExpired, FileNotFoundError):\n pass\n\n time.sleep(check_interval)\n elapsed += check_interval\n\n return False", + "context": "文件: lib/laddr/src/laddr/cli/utils/docker.py\n参数: service, timeout, check_interval\n调用者: run_dev", + "related_elements": [ + "wait_for_service", + "run_dev" + ] + }, + { + "pattern_type": "function_implementation", + "description": "function build_image 的实现和用法", + "code_snippet": "def build_image(dockerfile: Path, tag: str, context: Path) -> None:\n \"\"\"Build a Docker image.\n\n Args:\n dockerfile: Path to Dockerfile\n tag: Image tag\n context: Build context directory\n\n Raises:\n DockerComposeError: If build fails\n \"\"\"\n try:\n result = subprocess.run(\n [\"docker\", \"build\", \"-f\", str(dockerfile), \"-t\", tag, str(context)],\n capture_output=True,\n text=True,\n check=False,\n timeout=600, # 10 minutes\n )\n if result.returncode != 0:\n raise DockerComposeError(\"build\", result.stderr or result.stdout)\n except subprocess.TimeoutExpired:\n raise DockerComposeError(\"build\", \"Build timed out after 10 minutes\")\n except FileNotFoundError:\n raise DockerNotFoundError()", + "context": "文件: lib/laddr/src/laddr/cli/utils/docker.py\n参数: dockerfile, tag, context\n调用者: 无", + "related_elements": [ + "build_image" + ] + }, + { + "pattern_type": "function_implementation", + "description": "function add_worker_to_compose 的实现和用法", + "code_snippet": "def add_worker_to_compose(project_path: Path, agent_name: str) -> None:\n \"\"\"Add worker service to docker-compose.yml.\n \n Args:\n project_path: Path to project root\n agent_name: Name of the agent\n \"\"\"\n compose_path = project_path / \"docker-compose.yml\"\n\n with open(compose_path, \"r\", encoding=\"utf-8\") as f:\n compose_data = yaml.safe_load(f)\n\n # Check if service already exists\n if f\"{agent_name}\" in compose_data.get(\"services\", {}):\n return\n\n # Add worker service\n worker_service = {\n \"build\": \".\",\n \"command\": f\"python -m agents.{agent_name}\",\n \"env_file\": \".env\",\n \"environment\": {\n \"AGENT_NAME\": agent_name,\n },\n \"depends_on\": [\"redis\", \"postgres\"],\n \"volumes\": [\".:/app\"],\n \"deploy\": {\n \"replicas\": f\"${{{agent_name.upper()}_SCALE:-1}}\",\n \"restart_policy\": {\n \"condition\": \"on-failure\",\n \"delay\": \"5s\",\n \"max_attempts\": 3,\n },\n \"resources\": {\n \"limits\": {\"cpus\": \"1.0\", \"memory\": \"1G\"},\n \"reservations\": {\"cpus\": \"0.5\", \"memory\": \"512M\"},\n },\n },\n }\n\n compose_data.setdefault(\"services\", {})[f\"{agent_name}_worker\"] = worker_service\n\n with open(compose_path, \"w\", encoding=\"utf-8\") as f:\n yaml.dump(compose_data, f, default_flow_style=False, sort_keys=False)", + "context": "文件: lib/laddr/src/laddr/cli/utils/docker.py\n参数: project_path, agent_name\n调用者: _create_agent_files, _generate_default_agent", + "related_elements": [ + "add_worker_to_compose", + "_create_agent_files", + "_generate_default_agent" + ] + }, + { + "pattern_type": "function_implementation", + "description": "method EventThrottler.add 的实现和用法", + "code_snippet": " def add(self, event: dict) -> dict | None:\n \"\"\"Add event, return it if should be sent immediately.\"\"\"\n now = time.time()\n elapsed = now - self.last_sent\n\n if elapsed >= (1.0 / self.max_per_second):\n self.last_sent = now\n return event\n # Buffer for batching\n self.buffer.append(event)\n if len(self.buffer) >= 5: # Batch size\n batch = self.buffer[:]\n self.buffer.clear()\n self.last_sent = now\n return {\"type\": \"batch\", \"events\": batch}\n\n return None", + "context": "文件: lib/laddr/src/laddr/api/main.py\n参数: self, event\n调用者: DatabaseService.create_job, DatabaseService.create_prompt, DatabaseService.append_trace", + "related_elements": [ + "EventThrottler.add", + "DatabaseService.create_job", + "DatabaseService.create_prompt", + "DatabaseService.append_trace" + ] + }, + { + "pattern_type": "function_implementation", + "description": "function _build_trace_tree 的实现和用法", + "code_snippet": " def _build_trace_tree(traces: list[dict]) -> list[dict]:\n \"\"\"\n Build hierarchical trace tree from flat traces.\n Groups by agent runs and nests tool calls/LLM calls within them.\n \"\"\"\n # Index traces by ID for quick lookup\n trace_map = {t['id']: t for t in traces}\n roots = []\n \n # First pass: identify parent-child relationships\n for trace in traces:\n parent_id = trace.get('parent_id')\n if parent_id and parent_id in trace_map:\n parent = trace_map[parent_id]\n if 'children' not in parent:\n parent['children'] = []\n parent['children'].append(trace)\n else:\n # Root level trace\n roots.append(trace)\n \n # Build span structure\n def build_span(trace: dict) -> dict:\n event_type = trace.get('event_type', '')\n payload = trace.get('payload', {})\n agent_name = trace.get('agent_name', '')\n timestamp = trace.get('timestamp', '')\n \n # Extract metrics\n duration_ms = None\n tokens = None\n cost = None\n \n # Calculate duration if we have start/end events\n if event_type == 'task_complete':\n # Look for matching task_start to calculate duration\n start_trace = next((t for t in traces if t.get('agent_name') == agent_name \n and t.get('event_type') == 'task_start' \n and t.get('id') < trace['id']), None)\n if start_trace:\n start_ts = _parse_ts(start_trace.get('timestamp'))\n end_ts = _parse_ts(timestamp)\n if start_ts and end_ts:\n duration_ms = int((end_ts - start_ts).total_seconds() * 1000)\n \n # Extract token usage\n if event_type == 'llm_usage' or 'usage' in payload:\n usage = payload.get('usage', payload)\n tokens = usage.get('total_tokens', 0)\n cost = usage.get('cost')\n \n # Build span object\n span = {\n 'id': trace['id'],\n 'name': agent_name if event_type.startswith('task_') else payload.get('tool', event_type),\n 'type': _get_span_type(event_type, payload),\n 'start_time': timestamp,\n 'agent': agent_name,\n 'event_type': event_type,\n 'input': payload.get('inputs', payload.get('params', {})),\n 'output': payload.get('result', payload.get('outputs')),\n 'metadata': {\n 'duration_ms': duration_ms,\n 'tokens': tokens,\n 'cost': cost,\n **payload\n },\n 'children': []\n }\n \n # Add children recursively\n if 'children' in trace:\n span['children'] = [build_span(child) for child in trace['children']]\n \n return span\n \n return [build_span(root) for root in roots]", + "context": "文件: lib/laddr/src/laddr/api/main.py\n参数: traces\n调用者: 无", + "related_elements": [ + "_build_trace_tree" + ] + }, + { + "pattern_type": "function_implementation", + "description": "function _get_span_type 的实现和用法", + "code_snippet": " def _get_span_type(event_type: str, payload: dict) -> str:\n \"\"\"Determine span type for UI rendering.\"\"\"\n if event_type.startswith('task_'):\n return 'agent'\n elif event_type in ('tool_call', 'tool_result'):\n return 'tool'\n elif event_type in ('llm_call', 'llm_usage'):\n return 'llm'\n elif 'think' in event_type:\n return 'reasoning'\n else:\n return 'event'", + "context": "文件: lib/laddr/src/laddr/api/main.py\n参数: event_type, payload\n调用者: _build_trace_tree, build_span", + "related_elements": [ + "_get_span_type", + "_build_trace_tree", + "build_span" + ] + }, + { + "pattern_type": "module_interaction", + "description": "核心模块 inspect 的使用方式", + "code_snippet": "def tool(\n name: str | None = None,\n description: str | None = None,\n *,\n trace: bool = True,\n trace_mask: list[str] | None = None,\n parameters: dict | None = None,\n):\n \"\"\"\n De\n\ndef discover_tools(agent_name: str) -> ToolRegistry:\n \"\"\"\n Auto-discover tools for an agent from agents..tools package.\n \n Scans for:\n 1. Functions decorated with @tool\n \n\ndef bind_tools(agent_instance: Any, tools: list[str | Callable]) -> None:\n \"\"\"\n Bind tools to an agent instance for explicit, readable tool registration.\n \n Supports:\n - String names: a", + "context": "被 8 个组件使用", + "related_elements": [ + "tool", + "discover_tools", + "bind_tools", + "create_tool_schema", + "decorator" + ] + }, + { + "pattern_type": "module_interaction", + "description": "核心模块 pkgutil 的使用方式", + "code_snippet": "def discover_tools(agent_name: str) -> ToolRegistry:\n \"\"\"\n Auto-discover tools for an agent from agents..tools package.\n \n Scans for:\n 1. Functions decorated with @tool\n \n\ndef load_agents() -> Dict[str, Any]:\n \"\"\"Load agents dynamically from 'agents..handler'.\n\n Supports two patterns:\n 1) @actor-decorated classes named Agent (instantiated without ar\n\n def _discover_local_agents(self, exclude: list[str] | None = None) -> list[str]:\n \"\"\"Discover local agents under the 'agents' package for inline execution.\"\"\"\n names: list[str] = []\n", + "context": "被 3 个组件使用", + "related_elements": [ + "discover_tools", + "load_agents", + "AgentRunner._discover_local_agents" + ] + }, + { + "pattern_type": "module_interaction", + "description": "核心模块 importlib 的使用方式", + "code_snippet": "def discover_tools(agent_name: str) -> ToolRegistry:\n \"\"\"\n Auto-discover tools for an agent from agents..tools package.\n \n Scans for:\n 1. Functions decorated with @tool\n \n\ndef bind_tools(agent_instance: Any, tools: list[str | Callable]) -> None:\n \"\"\"\n Bind tools to an agent instance for explicit, readable tool registration.\n \n Supports:\n - String names: a\n\ndef load_agents() -> Dict[str, Any]:\n \"\"\"Load agents dynamically from 'agents..handler'.\n\n Supports two patterns:\n 1) @actor-decorated classes named Agent (instantiated without ar", + "context": "被 6 个组件使用", + "related_elements": [ + "discover_tools", + "bind_tools", + "load_agents", + "AgentRunner._discover_local_agents", + "AgentRunner._start_inline_workers" + ] + }, + { + "pattern_type": "module_interaction", + "description": "核心模块 message_bus 的使用方式", + "code_snippet": " def create_queue_backend(self) -> QueueBackend:\n \"\"\"Create message queue backend based on config.\"\"\"\n import os as _os\n # Respect explicit env override first\n backend =\n\n def __init__(\n self,\n config: AgentConfig,\n env_config: LaddrConfig | None = None,\n tools: ToolRegistry | list[Any] | None = None,\n *,\n llm: Any | None = ", + "context": "被 2 个组件使用", + "related_elements": [ + "BackendFactory.create_queue_backend", + "Agent.__init__" + ] + }, + { + "pattern_type": "module_interaction", + "description": "核心模块 storage 的使用方式", + "code_snippet": " def create_queue_backend(self) -> QueueBackend:\n \"\"\"Create message queue backend based on config.\"\"\"\n import os as _os\n # Respect explicit env override first\n backend =\n\n def create_storage_backend(self):\n \"\"\"\n Create S3-compatible storage backend (AWS S3, MinIO, or compatible).\n \n Supports backward compatibility with old minio_* field n", + "context": "被 2 个组件使用", + "related_elements": [ + "BackendFactory.create_queue_backend", + "BackendFactory.create_storage_backend" + ] + }, + { + "pattern_type": "module_interaction", + "description": "核心模块 traceback 的使用方式", + "code_snippet": " def create_queue_backend(self) -> QueueBackend:\n \"\"\"Create message queue backend based on config.\"\"\"\n import os as _os\n # Respect explicit env override first\n backend =\n\ndef main():\n \"\"\"Main entry point with error handling.\"\"\"\n try:\n cli()\n except LaddrError as e:\n print_error(e.message, hint=e.hint)\n sys.exit(1)\n except click.ClickExc", + "context": "被 2 个组件使用", + "related_elements": [ + "BackendFactory.create_queue_backend", + "main" + ] + }, + { + "pattern_type": "module_interaction", + "description": "核心模块 config 的使用方式", + "code_snippet": " def __init__(\n self,\n agent: Agent | None = None,\n env_config: LaddrConfig | None = None\n ):\n \"\"\"\n Initialize runner.\n \n \n \n Args:\n\n def _start_inline_workers(self, agent_names: list[str]) -> None:\n \"\"\"Start a background task that consumes tasks and runs target agents inline.\"\"\"\n import logging\n logger = lo\n\n def __init__(\n self,\n agent: Agent,\n database_url: str | None = None,\n redis_url: str | None = None,\n storage_endpoint: str | None = None,\n storage_access", + "context": "被 7 个组件使用", + "related_elements": [ + "AgentRunner.__init__", + "AgentRunner._start_inline_workers", + "WorkerRunner.__init__", + "Agent.__init__", + "Agent._discover_tool_overrides" + ] + }, + { + "pattern_type": "module_interaction", + "description": "核心模块 logging 的使用方式", + "code_snippet": " def _start_inline_workers(self, agent_names: list[str]) -> None:\n \"\"\"Start a background task that consumes tasks and runs target agents inline.\"\"\"\n import logging\n logger = lo\n\ndef get_logger(name: str, level: int = logging.INFO) -> logging.Logger:\n \"\"\"Get a configured logger with rich handler.\n\n Args:\n name: Logger name (usually __name__)\n level: Log lev", + "context": "被 2 个组件使用", + "related_elements": [ + "AgentRunner._start_inline_workers", + "get_logger" + ] + }, + { + "pattern_type": "module_interaction", + "description": "核心模块 asyncio 的使用方式", + "code_snippet": " def _start_inline_workers(self, agent_names: list[str]) -> None:\n \"\"\"Start a background task that consumes tasks and runs target agents inline.\"\"\"\n import logging\n logger = lo\n\n def _stop_inline_workers(self) -> None:\n if self._inline_worker_stop is not None:\n self._inline_worker_stop.set()\n if self._inline_worker_task is not None:\n sel\n\n def replay(\n self,\n job_id: str,\n reexecute: bool = False\n ) -> dict:\n \"\"\"\n Replay a previous job.\n \n Args:\n job_id: Job ID to replay", + "context": "被 11 个组件使用", + "related_elements": [ + "AgentRunner._start_inline_workers", + "AgentRunner._stop_inline_workers", + "AgentRunner.replay", + "Agent.__init__", + "MemoryBus.__init__" + ] + }, + { + "pattern_type": "module_interaction", + "description": "核心模块 time 的使用方式", + "code_snippet": " def _start_inline_workers(self, agent_names: list[str]) -> None:\n \"\"\"Start a background task that consumes tasks and runs target agents inline.\"\"\"\n import logging\n logger = lo\n\n def get(self, key: str) -> Any:\n \"\"\"Get value from cache.\"\"\"\n if key not in self._cache:\n return None\n\n value, expires_at = self._cache[key]\n\n # Check expira\n\n def set(self, key: str, value: Any, ttl: int | None = None) -> None:\n \"\"\"Set value in cache with optional TTL (seconds).\"\"\"\n expires_at = None\n if ttl is not None:\n ", + "context": "被 26 个组件使用", + "related_elements": [ + "AgentRunner._start_inline_workers", + "InMemoryCache.get", + "InMemoryCache.set", + "Agent._trace", + "Agent._build_autonomous_system_prompt" + ] + }, + { + "pattern_type": "module_interaction", + "description": "核心模块 tooling 的使用方式", + "code_snippet": " def __init__(\n self,\n config: AgentConfig,\n env_config: LaddrConfig | None = None,\n tools: ToolRegistry | list[Any] | None = None,\n *,\n llm: Any | None = \n\n def _register_system_tools(self):\n \"\"\"Register built-in system tools for delegation and artifacts.\"\"\"\n from .system_tools import create_system_tools\n from .tooling import Tool", + "context": "被 2 个组件使用", + "related_elements": [ + "Agent.__init__", + "Agent._register_system_tools" + ] + }, + { + "pattern_type": "module_interaction", + "description": "核心模块 system_tools 的使用方式", + "code_snippet": " def __init__(\n self,\n config: AgentConfig,\n env_config: LaddrConfig | None = None,\n tools: ToolRegistry | list[Any] | None = None,\n *,\n llm: Any | None = \n\n def _register_system_tools(self):\n \"\"\"Register built-in system tools for delegation and artifacts.\"\"\"\n from .system_tools import create_system_tools\n from .tooling import Tool", + "context": "被 2 个组件使用", + "related_elements": [ + "Agent.__init__", + "Agent._register_system_tools" + ] + }, + { + "pattern_type": "module_interaction", + "description": "核心模块 uuid 的使用方式", + "code_snippet": " def _build_autonomous_system_prompt(\n self,\n available_tools: list[str],\n available_agents: list[dict],\n can_delegate: bool,\n ) -> str:\n \"\"\"Build system promp\n\n def create_job(self, job_id: str | None, pipeline: str, inputs: dict) -> str:\n \"\"\"Create a new job record, or reuse existing if job_id already exists (for sequential chains).\"\"\"\n if \n\n def create_prompt(self, prompt_id: str | None, prompt_name: str, inputs: dict) -> str:\n \"\"\"Create a new prompt execution record.\"\"\"\n if prompt_id is None:\n prompt_id = str", + "context": "被 4 个组件使用", + "related_elements": [ + "Agent._build_autonomous_system_prompt", + "DatabaseService.create_job", + "DatabaseService.create_prompt", + "RedisBus.__init__" + ] + }, + { + "pattern_type": "module_interaction", + "description": "核心模块 io 的使用方式", + "code_snippet": " def __init__(\n self,\n endpoint: str,\n access_key: str,\n secret_key: str,\n secure: bool = False,\n region: str | None = None\n ):\n \"\"\"\n Init\n\n def _get_client(self):\n \"\"\"Get synchronous S3-compatible client.\"\"\"\n if self._client is None:\n try:\n from minio import Minio\n self._client = \n\n def _ensure():\n client = self._get_client()\n if not client.bucket_exists(bucket):\n try:\n # Create bucket with region if specified (requi", + "context": "被 5 个组件使用", + "related_elements": [ + "S3Storage.__init__", + "S3Storage._get_client", + "_ensure", + "_put", + "_exists" + ] + }, + { + "pattern_type": "module_interaction", + "description": "核心模块 minio 的使用方式", + "code_snippet": " def __init__(\n self,\n endpoint: str,\n access_key: str,\n secret_key: str,\n secure: bool = False,\n region: str | None = None\n ):\n \"\"\"\n Init\n\n def _get_client(self):\n \"\"\"Get synchronous S3-compatible client.\"\"\"\n if self._client is None:\n try:\n from minio import Minio\n self._client = ", + "context": "被 2 个组件使用", + "related_elements": [ + "S3Storage.__init__", + "S3Storage._get_client" + ] + }, + { + "pattern_type": "module_interaction", + "description": "核心模块 datetime 的使用方式", + "code_snippet": " def save_result(self, job_id: str, outputs: dict, status: str = \"completed\") -> None:\n \"\"\"Save job result.\"\"\"\n with self.get_session() as session:\n job = session.query(Job\n\n def get_result(self, job_id: str) -> dict | None:\n \"\"\"Get job result.\"\"\"\n with self.get_session() as session:\n job = session.query(Job).filter_by(job_id=job_id).first()\n \n\n def list_jobs(self, limit: int = 50) -> list[dict]:\n \"\"\"List recent jobs.\"\"\"\n with self.get_session() as session:\n jobs = session.query(Job).order_by(Job.created_at.desc()", + "context": "被 15 个组件使用", + "related_elements": [ + "DatabaseService.save_result", + "DatabaseService.get_result", + "DatabaseService.list_jobs", + "DatabaseService.save_prompt_result", + "DatabaseService.update_prompt_status" + ] + }, + { + "pattern_type": "module_interaction", + "description": "核心模块 functools 的使用方式", + "code_snippet": "def create_system_tools(message_bus, storage_backend=None, agent=None) -> dict[str, tuple[Any, list[str]]]:\n \"\"\"\n Create system tools for task delegation and artifact management.\n \n This f\n\n def wrap_with_runtime_injection(tool_func):\n \"\"\"\n Wrap a custom system tool function to inject runtime parameters.\n \n Custom override functions can declare _message_bus", + "context": "被 2 个组件使用", + "related_elements": [ + "create_system_tools", + "wrap_with_runtime_injection" + ] + }, + { + "pattern_type": "module_interaction", + "description": "核心模块 click 的使用方式", + "code_snippet": "def run_local(agent: str, input_json: str):\n \"\"\"Run an agent once locally using in-memory components (no Redis/DB).\"\"\"\n import json\n import asyncio\n import os\n import sys\n # Ensure c\n\ndef main():\n \"\"\"Main entry point with error handling.\"\"\"\n try:\n cli()\n except LaddrError as e:\n print_error(e.message, hint=e.hint)\n sys.exit(1)\n except click.ClickExc\n\ndef run_prompt(prompt_name: str, inputs: tuple, json_input: str | None, wait: bool, timeout: int):\n \"\"\"\n Run a prompt execution.\n \n Examples:\n laddr prompt run researcher --input qu", + "context": "被 13 个组件使用", + "related_elements": [ + "run_local", + "main", + "run_prompt", + "run", + "run_dev" + ] + }, + { + "pattern_type": "module_interaction", + "description": "核心模块 laddr.core 的使用方式", + "code_snippet": "def list_prompts(limit: int):\n \"\"\"List recent prompt executions.\"\"\"\n try:\n from laddr.core import DatabaseService, LaddrConfig\n\n config = LaddrConfig()\n db = DatabaseService\n\ndef run_pipeline(pipeline_file: str):\n \"\"\"Run a pipeline defined in a YAML file.\"\"\"\n # Lazy import to avoid hard deps when not running pipeline\n import yaml # type: ignore\n\n if not valida\n\ndef run_agent_cmd(agent_name: str, inputs_json: str):\n \"\"\"Run a single agent locally using AgentRunner.\"\"\"\n import asyncio\n import os\n import sys\n\n # Ensure local project imports work (", + "context": "被 6 个组件使用", + "related_elements": [ + "list_prompts", + "run_pipeline", + "run_agent_cmd", + "replay_job", + "infra" + ] + }, + { + "pattern_type": "module_interaction", + "description": "核心模块 subprocess 的使用方式", + "code_snippet": "def run_dev(build: bool, detach: bool):\n \"\"\"Run the Laddr development environment.\n \n Starts all infrastructure services and agent workers:\n - PostgreSQL (internal observability database)\n\n\ndef check_docker() -> bool:\n \"\"\"Check if Docker is installed and running.\n\n Returns:\n True if Docker is available\n\n Raises:\n DockerNotFoundError: If Docker is not found or not r\n\ndef check_docker_compose() -> bool:\n \"\"\"Check if Docker Compose is available.\n\n Returns:\n True if Docker Compose is available\n\n Raises:\n DockerNotFoundError: If Docker Compose i", + "context": "被 10 个组件使用", + "related_elements": [ + "run_dev", + "check_docker", + "check_docker_compose", + "compose_up", + "compose_down" + ] + } + ], + "statistics": { + "total_elements": 279, + "functions": 88, + "classes": 67, + "methods": 124, + "code_patterns": 233, + "file_type_counts": { + ".md": 15, + ".toml": 2, + ".other": 2, + ".yml": 1, + ".txt": 1, + ".sh": 3, + ".lock": 1, + ".png": 1, + ".svg": 4, + ".example": 1, + ".html": 1, + ".conf": 1, + ".json": 4, + ".js": 2, + ".ts": 15, + ".prod": 1, + ".template": 1, + ".tsx": 15, + ".css": 2, + ".py": 32, + ".j2": 10 + } + }, + "call_graph": { + "_LLMWithDefaults.__init__": [ + "dict" + ], + "openai": [ + "_LLMWithDefaults", + "OpenAILLM" + ], + "gemini": [ + "GeminiLLM", + "_LLMWithDefaults" + ], + "anthropic": [ + "_LLMWithDefaults", + "AnthropicLLM" + ], + "groq": [ + "_LLMWithDefaults", + "GroqLLM" + ], + "grok": [ + "_LLMWithDefaults", + "GrokLLM" + ], + "ollama": [ + "OllamaLLM", + "_LLMWithDefaults" + ], + "Agent": [ + "set", + "CoreAgent", + "AgentConfig", + "bool", + "LaddrConfig" + ], + "Tool.validate_inputs": [ + "issubclass", + "input_model", + "model_dump" + ], + "Tool.invoke": [ + "validate_inputs", + "func" + ], + "ToolRegistry.register": [ + "isinstance", + "callable", + "hasattr", + "split", + "strip", + "Tool", + "ValueError" + ], + "ToolRegistry.get": [ + "get" + ], + "ToolRegistry.list": [ + "values", + "list" + ], + "ToolRegistry.list_names": [ + "list", + "keys" + ], + "ToolRegistry.list_all_names": [ + "list", + "keys" + ], + "tool": [ + "values", + "issubclass", + "get", + "set", + "list", + "bool", + "split", + "isclass", + "strip", + "Tool", + "signature", + "get_type_hints" + ], + "discover_tools": [ + "ToolRegistry", + "hasattr", + "import_module", + "getmembers", + "iter_modules", + "callable", + "register" + ], + "bind_tools": [ + "isinstance", + "ImportError", + "register", + "callable", + "hasattr", + "import_module", + "getmembers", + "AttributeError", + "ValueError", + "type" + ], + "create_tool_schema": [ + "issubclass", + "append", + "get", + "getattr", + "items", + "signature", + "model_json_schema" + ], + "decorator": [ + "values", + "issubclass", + "get", + "info", + "set", + "list", + "bool", + "split", + "isclass", + "strip", + "Tool", + "signature", + "get_type_hints" + ], + "BackendFactory.create_queue_backend": [ + "print_exc", + "get", + "print", + "getattr", + "create_storage_backend", + "RedisBus", + "KafkaBus", + "ValueError", + "MemoryBus" + ], + "BackendFactory.create_database_backend": [ + "DatabaseService", + "get" + ], + "BackendFactory.create_llm_backend": [ + "upper", + "get", + "AnthropicLLM", + "OpenAILLM", + "GroqLLM", + "lower", + "OllamaLLM", + "GeminiLLM", + "GrokLLM", + "NoOpLLM" + ], + "BackendFactory.create_cache_backend": [ + "InMemoryCache", + "ValueError", + "RedisCache" + ], + "BackendFactory.create_storage_backend": [ + "InMemoryStorage", + "S3Storage" + ], + "load_agents": [ + "getattr", + "import_module", + "isclass", + "items", + "iter_modules", + "callable", + "vars" + ], + "AgentRunner.__init__": [ + "BackendFactory", + "create_database_backend", + "LaddrConfig" + ], + "AgentRunner._discover_local_agents": [ + "append", + "set", + "suppress", + "import_module", + "iter_modules" + ], + "AgentRunner._start_inline_workers": [ + "wait_for", + "capitalize", + "consume_tasks", + "create_queue_backend", + "publish_response", + "_loop", + "error", + "is_set", + "suppress", + "import_module", + "get", + "wait", + "getattr", + "create_task", + "AgentConfig", + "connect_bus", + "len", + "exception", + "info", + "Event", + "cls", + "handle", + "getLogger" + ], + "AgentRunner._stop_inline_workers": [ + "get_event_loop", + "set", + "cancel", + "suppress", + "run_until_complete" + ], + "AgentRunner.replay": [ + "get", + "get_result", + "run" + ], + "WorkerRunner.__init__": [ + "BackendFactory", + "LaddrConfig" + ], + "InMemoryCache.get": [ + "time" + ], + "InMemoryCache.set": [ + "time" + ], + "InMemoryCache.clear": [ + "clear" + ], + "AgentMemory.put": [ + "memory_put" + ], + "AgentMemory.get": [ + "memory_get" + ], + "AgentMemory.list": [ + "memory_list" + ], + "Agent.__init__": [ + "isinstance", + "create_cache_backend", + "BackendFactory", + "hasattr", + "create_queue_backend", + "list", + "RedisBus", + "LaddrConfig", + "upper", + "set", + "_TR", + "register", + "get", + "getattr", + "_register_system_tools", + "create_storage_backend", + "create_llm_backend", + "startswith", + "create_database_backend", + "discover_tools", + "dict", + "bool", + "lower" + ], + "Agent._discover_tool_overrides": [ + "module_from_spec", + "info", + "hasattr", + "startswith", + "cwd", + "exec_module", + "Path", + "spec_from_file_location", + "exists", + "warning", + "is_dir", + "glob" + ], + "Agent._register_system_tools": [ + "isinstance", + "_Tool", + "_discover_tool_overrides", + "create_system_tools", + "split", + "strip", + "items", + "register", + "has" + ], + "Agent.plan": [ + "dumps" + ], + "Agent._tool_cache_key": [ + "md5", + "encode", + "hexdigest", + "dumps" + ], + "Agent._trace": [ + "append_trace" + ], + "Agent._build_autonomous_system_prompt": [ + "get", + "getcwd", + "join", + "isfile", + "strip", + "read", + "open" + ], + "Agent._build_autonomous_user_prompt": [ + "get", + "enumerate", + "str" + ], + "Agent._format_history_for_synthesis": [ + "append", + "get", + "join", + "str", + "enumerate" + ], + "Agent._parse_autonomous_action": [ + "get", + "loads", + "find", + "str", + "rfind", + "_parse_autonomous_action_text", + "items", + "lower" + ], + "Agent._parse_autonomous_action_text": [ + "upper", + "loads", + "startswith", + "find", + "split", + "strip", + "replace" + ], + "MCPToolSource.__init__": [ + "NotImplementedError" + ], + "MCPToolRegistry.__init__": [ + "NotImplementedError" + ], + "S3Storage._get_client": [ + "Minio", + "RuntimeError" + ], + "_ensure": [ + "print", + "RuntimeError", + "bucket_exists", + "make_bucket", + "_get_client" + ], + "_put": [ + "get", + "put_object", + "BytesIO", + "items", + "lower", + "len", + "_get_client" + ], + "_get": [ + "read", + "get_object", + "release_conn", + "close", + "_get_client" + ], + "_list": [ + "append", + "isoformat", + "list_objects", + "len", + "_get_client" + ], + "_delete": [ + "remove_object", + "_get_client" + ], + "_exists": [ + "stat_object", + "_get_client" + ], + "DatabaseService.__init__": [ + "info", + "create_engine", + "text", + "connect", + "split", + "create_all", + "execute", + "warning", + "sessionmaker" + ], + "DatabaseService.create_tables": [ + "create_all" + ], + "DatabaseService.get_session": [ + "SessionLocal", + "commit", + "rollback", + "close" + ], + "DatabaseService.create_job": [ + "first", + "Job", + "uuid4", + "str", + "query", + "add", + "get_session", + "filter_by" + ], + "DatabaseService.save_result": [ + "first", + "query", + "utcnow", + "get_session", + "filter_by" + ], + "DatabaseService.get_result": [ + "isoformat", + "first", + "_iso_z", + "query", + "get_session", + "filter_by" + ], + "DatabaseService.list_jobs": [ + "limit", + "isoformat", + "_iso_z", + "query", + "all", + "desc", + "get_session", + "order_by" + ], + "DatabaseService.create_prompt": [ + "str", + "uuid4", + "add", + "get_session", + "PromptExecution" + ], + "DatabaseService.save_prompt_result": [ + "first", + "query", + "utcnow", + "get_session", + "filter_by" + ], + "DatabaseService.update_prompt_status": [ + "first", + "query", + "utcnow", + "get_session", + "filter_by" + ], + "DatabaseService.get_prompt_result": [ + "isoformat", + "first", + "_iso_z", + "query", + "get_session", + "filter_by" + ], + "DatabaseService.list_prompts": [ + "limit", + "isoformat", + "_iso_z", + "query", + "all", + "desc", + "get_session", + "order_by" + ], + "DatabaseService.get_job_traces": [ + "isoformat", + "_iso_z", + "query", + "all", + "get_session", + "filter_by", + "order_by" + ], + "DatabaseService.append_trace": [ + "Trace", + "add", + "get_session" + ], + "DatabaseService.list_traces": [ + "limit", + "isoformat", + "_iso_z", + "query", + "all", + "desc", + "get_session", + "filter_by", + "order_by" + ], + "DatabaseService.get_trace": [ + "isoformat", + "first", + "_iso_z", + "query", + "get_session", + "filter_by" + ], + "DatabaseService.memory_put": [ + "Memory", + "first", + "query", + "add", + "utcnow", + "get_session", + "filter_by" + ], + "DatabaseService.memory_get": [ + "query", + "filter_by", + "first", + "get_session" + ], + "DatabaseService.memory_list": [ + "query", + "filter_by", + "all", + "get_session" + ], + "DatabaseService.register_agent": [ + "first", + "query", + "add", + "AgentRegistry", + "utcnow", + "get_session", + "filter_by" + ], + "DatabaseService.list_agents": [ + "append", + "count", + "isoformat", + "first", + "_iso_z", + "query", + "all", + "desc", + "get_session", + "filter_by", + "order_by" + ], + "DatabaseService.get_metrics": [ + "append", + "count", + "get", + "filter_by", + "sum", + "query", + "total_seconds", + "int", + "all", + "get_session", + "len" + ], + "DatabaseService.get_token_usage": [ + "values", + "get", + "list", + "query", + "int", + "all", + "get_session", + "filter_by" + ], + "_iso_z": [ + "isoformat" + ], + "TaskMessage.from_dict": [ + "time", + "get", + "cls" + ], + "ResponseMessage.from_dict": [ + "time", + "get", + "cls" + ], + "RedisBus.__init__": [ + "uuid4" + ], + "MemoryBus.__init__": [ + "set" + ], + "KafkaBus.__init__": [ + "RuntimeError", + "set" + ], + "TaskDelegationTool.__init__": [ + "hasattr", + "getattr" + ], + "ParallelDelegationTool.__init__": [ + "TaskDelegationTool" + ], + "ParallelDelegationTool.split_document": [ + "range", + "append", + "abs", + "nearest_whitespace", + "isspace", + "min", + "len", + "max" + ], + "override_system_tool": [ + "info" + ], + "get_tool_override": [ + "get" + ], + "clear_tool_overrides": [ + "info" + ], + "list_tool_overrides": [ + "items" + ], + "create_system_tools": [ + "wraps", + "tool_func", + "any", + "TaskDelegationTool", + "info", + "iscoroutine", + "set", + "keys", + "getattr", + "isawaitable", + "wrap_with_runtime_injection", + "ArtifactStorageTool", + "get_tool_override", + "items", + "ParallelDelegationTool", + "signature" + ], + "wrap_with_runtime_injection": [ + "wraps", + "tool_func", + "iscoroutine", + "any", + "isawaitable", + "signature" + ], + "nearest_whitespace": [ + "range", + "abs", + "isspace", + "min", + "max" + ], + "OpenAILLM.__init__": [ + "getenv" + ], + "AnthropicLLM.__init__": [ + "getenv" + ], + "GeminiLLM.__init__": [ + "getenv" + ], + "GroqLLM.__init__": [ + "getenv" + ], + "GrokLLM.__init__": [ + "getenv" + ], + "OllamaLLM.__init__": [ + "getenv" + ], + "_do_request": [ + "urlopen", + "read", + "Request", + "decode" + ], + "cli": [ + "group", + "version_option" + ], + "run_local": [ + "capitalize", + "option", + "LaddrConfig", + "load_agents", + "insert", + "AgentCls", + "argument", + "get", + "loads", + "getattr", + "AgentConfig", + "run", + "getcwd", + "command", + "echo", + "dumps", + "handle", + "ClickException", + "__import__" + ], + "main": [ + "print_error", + "print_exc", + "print", + "show", + "exit", + "cli" + ], + "prompt": [ + "group" + ], + "run_prompt": [ + "print", + "get_current_context", + "print_success", + "split", + "option", + "LaddrConfig", + "exists", + "run_agent", + "Path", + "argument", + "print_error", + "get", + "loads", + "startswith", + "run", + "load", + "command", + "dumps", + "open" + ], + "list_prompts": [ + "print_error", + "get", + "print", + "command", + "option", + "LaddrConfig", + "DatabaseService", + "list_prompts" + ], + "run": [ + "group" + ], + "run_dev": [ + "ProjectNotFoundError", + "print_info", + "append", + "check_docker_compose", + "command", + "sleep", + "_print_management_commands", + "print_success", + "print_header", + "cwd", + "option", + "ClickException", + "wait_for_service", + "validate_project_directory", + "check_docker", + "_print_service_info", + "run" + ], + "run_pipeline": [ + "ProjectNotFoundError", + "isinstance", + "get", + "print", + "command", + "dumps", + "print_success", + "print_header", + "cwd", + "validate_project_directory", + "items", + "argument", + "safe_load", + "LaddrConfig", + "open", + "BadParameter", + "AgentRunner", + "run" + ], + "run_agent_cmd": [ + "print_info", + "print", + "print_success", + "option", + "LaddrConfig", + "insert", + "run_agent", + "cwd", + "argument", + "ProjectNotFoundError", + "get", + "loads", + "BadParameter", + "run", + "command", + "dumps", + "str", + "print_header", + "ClickException", + "validate_project_directory" + ], + "replay_job": [ + "ProjectNotFoundError", + "print_info", + "print", + "command", + "replay", + "dumps", + "str", + "print_success", + "print_header", + "cwd", + "option", + "argument", + "LaddrConfig", + "ClickException", + "validate_project_directory", + "AgentRunner" + ], + "_print_service_info": [ + "print" + ], + "_print_management_commands": [ + "print" + ], + "run_dev_alias": [ + "Context", + "invoke", + "command" + ], + "add": [ + "group" + ], + "add_agent": [ + "_create_agent_files", + "ProjectNotFoundError", + "print", + "command", + "prompt", + "print_header", + "cwd", + "option", + "validate_project_directory", + "print_step", + "argument", + "print_completion", + "isalnum", + "BadParameter", + "replace" + ], + "add_tool": [ + "print", + "option", + "safe_load", + "print_step", + "cwd", + "argument", + "print_completion", + "ProjectNotFoundError", + "get", + "_create_tool_file", + "isalnum", + "BadParameter", + "replace", + "command", + "prompt", + "print_header", + "AgentNotFoundError", + "open", + "validate_project_directory" + ], + "_create_agent_files": [ + "safe_load", + "exists", + "print_step", + "read_text", + "upper", + "endswith", + "add_worker_to_compose", + "render_to_file", + "setdefault", + "cwd", + "Path", + "get", + "append", + "dump", + "mkdir", + "join", + "get_template_renderer", + "lower", + "write_file", + "open" + ], + "_create_tool_file": [ + "mkdir", + "render_to_file", + "Path", + "get_template_renderer", + "lower", + "replace" + ], + "logs": [ + "ProjectNotFoundError", + "print_info", + "command", + "cwd", + "option", + "compose_logs", + "argument", + "validate_project_directory" + ], + "ps": [ + "ProjectNotFoundError", + "print_info", + "print", + "command", + "Choice", + "cwd", + "option", + "compose_ps", + "validate_project_directory" + ], + "scale": [ + "ProjectNotFoundError", + "print_info", + "print", + "command", + "compose_scale", + "print_success", + "cwd", + "validate_project_directory", + "compose_ps", + "argument", + "BadParameter" + ], + "stop": [ + "ProjectNotFoundError", + "print_info", + "command", + "compose_down", + "print_success", + "cwd", + "option", + "validate_project_directory" + ], + "infra": [ + "print_error", + "print", + "command", + "option", + "LaddrConfig" + ], + "check": [ + "print_info", + "print", + "Abort", + "print_success", + "option", + "LaddrConfig", + "import_module", + "cwd", + "print_error", + "ProjectNotFoundError", + "get", + "getattr", + "dump", + "len", + "AgentRunner", + "run", + "command", + "open", + "validate_project_directory" + ], + "init": [ + "print", + "iterdir", + "strip", + "_generate_default_agent", + "option", + "exists", + "input", + "print_step", + "_create_directories", + "cwd", + "NestedProjectError", + "resolve", + "Path", + "_generate_readme", + "argument", + "print_completion", + "any", + "_generate_config_files", + "_generate_docker_setup", + "command", + "_generate_core_modules", + "ProjectExistsError", + "str", + "print_header", + "validate_project_directory" + ], + "_create_directories": [ + "mkdir" + ], + "_generate_config_files": [ + "render_to_file", + "ProjectConfigSchema", + "ProjectDetails", + "get_template_renderer", + "save_to_file" + ], + "_generate_docker_setup": [ + "render_to_file", + "get_template_renderer" + ], + "_generate_default_agent": [ + "upper", + "append", + "get", + "add_worker_to_compose", + "render_to_file", + "dump", + "get_template_renderer", + "safe_load", + "write_file", + "open" + ], + "_generate_readme": [ + "render_to_file", + "get_template_renderer" + ], + "ProjectConfigSchema.load_from_file": [ + "InvalidConfigError", + "cls", + "str", + "safe_load", + "open" + ], + "ProjectConfigSchema.save_to_file": [ + "InvalidConfigError", + "dump", + "str", + "open", + "model_dump" + ], + "ProjectDetails.validate_name": [ + "field_validator", + "ValueError", + "isalnum", + "replace" + ], + "validate_project_directory": [ + "exists" + ], + "get_logger": [ + "RichHandler", + "clear", + "addHandler", + "Formatter", + "getLogger", + "setFormatter", + "setLevel" + ], + "print_success": [ + "print" + ], + "print_error": [ + "print" + ], + "print_warning": [ + "print" + ], + "print_info": [ + "print" + ], + "print_panel": [ + "print", + "Panel" + ], + "print_header": [ + "print" + ], + "print_step": [ + "print" + ], + "print_completion": [ + "print" + ], + "print_table": [ + "values", + "add_row", + "print", + "keys", + "add_column", + "Table", + "str" + ], + "TemplateRenderer.__init__": [ + "Environment", + "str", + "FileSystemLoader" + ], + "TemplateRenderer._snake_case": [ + "lower", + "replace" + ], + "TemplateRenderer._pascal_case": [ + "split", + "capitalize", + "join", + "replace" + ], + "TemplateRenderer._kebab_case": [ + "lower", + "replace" + ], + "TemplateRenderer.render_template": [ + "render", + "get_template", + "str", + "FileGenerationError" + ], + "TemplateRenderer.render_to_file": [ + "mkdir", + "write_text", + "str", + "render_template", + "FileGenerationError" + ], + "TemplateRenderer.render_string": [ + "render", + "Template", + "str", + "FileGenerationError" + ], + "get_template_renderer": [ + "exists", + "Path", + "TemplateRenderer", + "FileGenerationError" + ], + "write_file": [ + "mkdir", + "write_text", + "str", + "FileGenerationError" + ], + "check_docker": [ + "DockerNotFoundError", + "run" + ], + "check_docker_compose": [ + "DockerNotFoundError", + "run" + ], + "compose_up": [ + "append", + "extend", + "items", + "DockerComposeError", + "DockerNotFoundError", + "run" + ], + "compose_down": [ + "DockerNotFoundError", + "append", + "DockerComposeError", + "run" + ], + "compose_ps": [ + "DockerNotFoundError", + "extend", + "DockerComposeError", + "run" + ], + "compose_logs": [ + "append", + "extend", + "str", + "DockerNotFoundError", + "run" + ], + "compose_scale": [ + "DockerNotFoundError", + "ValueError", + "DockerComposeError", + "run" + ], + "wait_for_service": [ + "print_info", + "sleep", + "print_success", + "lower", + "run" + ], + "build_image": [ + "DockerNotFoundError", + "run", + "str", + "DockerComposeError" + ], + "add_worker_to_compose": [ + "upper", + "get", + "setdefault", + "dump", + "safe_load", + "open" + ], + "LaddrError.__init__": [ + "__init__", + "super" + ], + "ProjectNotFoundError.__init__": [ + "__init__", + "super" + ], + "ProjectExistsError.__init__": [ + "__init__", + "super" + ], + "NestedProjectError.__init__": [ + "__init__", + "super" + ], + "InvalidConfigError.__init__": [ + "__init__", + "super" + ], + "DockerNotFoundError.__init__": [ + "__init__", + "super" + ], + "DockerComposeError.__init__": [ + "__init__", + "super" + ], + "AgentNotFoundError.__init__": [ + "__init__", + "super" + ], + "InvalidInputError.__init__": [ + "__init__", + "super" + ], + "FileGenerationError.__init__": [ + "__init__", + "super" + ], + "ServiceNotReadyError.__init__": [ + "__init__", + "super" + ], + "EventThrottler.add": [ + "clear", + "time", + "len", + "append" + ], + "_parse_ts": [ + "fromisoformat", + "replace" + ], + "_build_trace_tree": [ + "append", + "get", + "_get_span_type", + "startswith", + "build_span", + "total_seconds", + "int", + "next", + "_parse_ts" + ], + "_get_span_type": [ + "startswith" + ], + "build_span": [ + "get", + "_get_span_type", + "startswith", + "_parse_ts", + "total_seconds", + "build_span", + "int", + "next" + ] + } +} \ No newline at end of file