diff --git a/.nvmrc b/.nvmrc new file mode 100644 index 0000000..2bd5a0a --- /dev/null +++ b/.nvmrc @@ -0,0 +1 @@ +22 diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..a663b4b --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,116 @@ +# AFK Repository Guide for Coding Agents + +This file is the first stop for LLM agents working in this repository. It summarizes the repo shape, stable commands, and documentation workflow so agentic edits stay grounded in the current codebase. + +## Project Snapshot + +AFK is a Python 3.13 SDK for building reliable AI agents. The public package imports from `afk.*`; source lives under `src/afk/`. + +Core boundaries: + +- `afk.agents`: agent definitions, policy, prompts, skills, lifecycle, workflow, A2A. +- `afk.core`: `Runner`, interaction providers, streaming handles, telemetry contracts. +- `afk.llms`: provider-portable LLM runtime, adapters, retry/timeout/cache/routing policies. +- `afk.tools`: typed tools, decorators, registry, sandbox and output limiting. +- `afk.memory`: memory stores, checkpoints, retention, compaction, vector helpers. +- `afk.queues`: async task queues, execution contracts, workers, retry/DLQ behavior. +- `afk.observability`: telemetry collectors, projectors, exporters. +- `afk.mcp`, `afk.messaging`, `afk.debugger`, `afk.evals`: optional integration and quality layers. + +## Ground Rules + +- Use public imports in docs and examples, such as `from afk.agents import Agent` and `from afk.core import Runner`. +- Do not import from `src.afk` or internal module paths in user-facing examples unless the document is explicitly an internals reference. +- Keep Agent/Runner/Runtime boundaries intact: agents are configuration, runners execute, adapters/tools/memory provide runtime capabilities. +- Prefer typed contracts: Pydantic models, dataclasses, protocols, and explicit error classes. +- Add or update docs when changing user-visible behavior, especially runner lifecycle, tools, policy, memory, queues, LLM routing, or env vars. +- The worktree may contain user changes. Do not revert unrelated edits. + +## Common Commands + +Install for local development: + +```bash +python -m pip install --upgrade pip +python -m pip install -e . pytest +``` + +Run tests: + +```bash +PYTHONPATH=src pytest -q +``` + +Run targeted tests: + +```bash +PYTHONPATH=src pytest -q tests/llms/test_llm_settings.py +PYTHONPATH=src pytest -q tests/queues/test_queue_factory.py +PYTHONPATH=src pytest -q tests/agents/test_agent_runtime.py +``` + +Lint and format when available: + +```bash +ruff check src tests +ruff format src tests +``` + +Preview docs: + +```bash +./scripts/docs_dev.sh +``` + +Regenerate agent-friendly docs and skill indexes: + +```bash +./scripts/build_agentic_ai_assets.sh +``` + +Install AFK skills with Vercel's Skills CLI: + +```bash +npx skills add https://github.com/arpan404/afk --skill afk-coder +npx skills add https://github.com/arpan404/afk --skill afk-maintainer +``` + +## Documentation Map + +- `README.md`: repository landing page and quickest human orientation. +- `CONTRIBUTING.md`: local setup, tests, PR and docs workflow. +- `ENV_VARS.md`: environment variable reference grounded in runtime factories/settings. +- `docs/docs.json`: Mintlify navigation. Add new docs pages here or they are hard to discover. +- `docs/index.mdx`: public docs landing page. +- `docs/library/developer-guide.mdx`: framework contributor workflow. +- `docs/library/building-with-ai.mdx`: application builder playbook. +- `docs/library/api-reference.mdx`: public import contract. +- `docs/library/full-module-reference.mdx`: generated/source-level symbol map. +- `docs/library/examples/index.mdx` and `docs/library/snippets/*.mdx`: runnable examples. +- `ai-index/`: generated searchable docs records for agents. +- `skills/afk-coder/`: skill for developers building with AFK. +- `skills/afk-maintainer/`: skill for maintainers reviewing or changing AFK itself. + +## Documentation Quality Checklist + +Before finishing docs work: + +- New pages are included in `docs/docs.json`. +- Code examples import `Runner` from `afk.core`, not `afk.agents`. +- Package install guidance distinguishes distribution install from local editable install. +- Environment variable names match `src/afk/llms/settings.py`, `src/afk/memory/factory.py`, and `src/afk/queues/factory.py`. +- Agent-facing docs mention where to search: `python skills/afk-coder/scripts/search_afk_docs.py "query"`. +- Generated assets are refreshed when navigation, snippets, or skill metadata changes. + +## High-Risk Areas + +Use extra care and targeted tests when touching: + +- `src/afk/core/runner/`: execution loop, checkpoints, resume, policy/failure routing. +- `src/afk/core/streaming.py`: stream events and handle lifecycle. +- `src/afk/tools/core/base.py` and `src/afk/tools/registry.py`: tool invocation semantics. +- `src/afk/tools/security.py`: sandbox, secret scope, output limiting. +- `src/afk/llms/runtime/`: retries, circuit breakers, rate limits, caching, routing. +- `src/afk/memory/`: persistence, checkpoint keys, compaction, vector search. +- `src/afk/queues/`: execution contracts, retry/DLQ, worker lifecycle. +- `src/afk/agents/a2a/`: auth, delivery guarantees, protocol compatibility. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 0943774..3360974 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -40,21 +40,40 @@ PYTHONPATH=src pytest -q tests/agents/test_agent_runtime.py - Mintlify config is `docs/docs.json` - Main landing page is `docs/index.mdx` - AI docs index output is generated under `ai-index/` -- Coding-agent skills are stored in `agent-skill/` +- Coding-agent skills are stored in `skills/` +- Repository instructions for coding agents live in `AGENTS.md` Local docs preview: ```bash -cd docs -bunx mintlify dev +./scripts/docs_dev.sh ``` +Mintlify currently requires an LTS Node runtime and fails on Node 25+. The +script above runs the preview with Node 22 even if your global `node` points to a +newer development release. + Build AI-searchable docs index + skill metadata: ```bash ./scripts/build_agentic_ai_assets.sh ``` +Search the bundled agent docs index: + +```bash +python skills/afk-coder/scripts/search_afk_docs.py "runner resume" +``` + +Install the repository skills with Vercel's Skills CLI: + +```bash +npx skills add https://github.com/arpan404/afk --skill afk-coder +npx skills add https://github.com/arpan404/afk --skill afk-maintainer +``` + +Use `afk-coder` when building with AFK. Use `afk-maintainer` when reviewing or changing AFK itself. + ## Contribution Guidelines - Use public imports (`afk.*`) in examples and docs. diff --git a/ENV_VARS.md b/ENV_VARS.md index 8626099..7988f42 100644 --- a/ENV_VARS.md +++ b/ENV_VARS.md @@ -1,12 +1,13 @@ -# AFK Python SDK Environment Variables (v1.0.0) +# AFK Python SDK Environment Variables -This reference documents environment defaults. Runtime configuration APIs remain primary. +This reference documents environment defaults from the current source. Runtime configuration APIs remain primary. ## LLM Defaults | Variable | Default | Description | | --- | --- | --- | | `AFK_LLM_PROVIDER` | `litellm` | Default provider id (`openai`, `litellm`, `anthropic_agent`) | +| `AFK_LLM_PROVIDER_ORDER` | _(none)_ | Comma-separated provider preference order | | `AFK_LLM_MODEL` | `gpt-4.1-mini` | Default model | | `AFK_EMBED_MODEL` | _(none)_ | Embedding model | | `AFK_LLM_API_BASE_URL` | _(none)_ | Provider API base | @@ -23,16 +24,36 @@ This reference documents environment defaults. Runtime configuration APIs remain | Variable | Default | Description | | --- | --- | --- | -| `AFK_MEMORY_BACKEND` | `sqlite` | `inmemory`, `sqlite`, `redis`, `postgres` | +| `AFK_MEMORY_BACKEND` | `sqlite` | `memory`, `inmemory`, `sqlite`, `redis`, `postgres` | | `AFK_SQLITE_PATH` | `afk_memory.sqlite3` | SQLite file path | | `AFK_REDIS_URL` | _(none)_ | Redis URL | +| `AFK_REDIS_HOST` | `localhost` | Redis host when URL is not set | +| `AFK_REDIS_PORT` | `6379` | Redis port when URL is not set | +| `AFK_REDIS_DB` | `0` | Redis DB when URL is not set | +| `AFK_REDIS_PASSWORD` | _(none)_ | Redis password when URL is not set | +| `AFK_REDIS_EVENTS_MAX` | `2000` | Max Redis memory events per thread | | `AFK_PG_DSN` | _(none)_ | PostgreSQL DSN | +| `AFK_PG_HOST` | `localhost` | PostgreSQL host when DSN is not set | +| `AFK_PG_PORT` | `5432` | PostgreSQL port when DSN is not set | +| `AFK_PG_USER` | `postgres` | PostgreSQL user when DSN is not set | +| `AFK_PG_PASSWORD` | _(none)_ | PostgreSQL password when DSN is not set | +| `AFK_PG_DB` | `afk` | PostgreSQL database when DSN is not set | +| `AFK_PG_SSL` | `false` | Enable PostgreSQL SSL | +| `AFK_PG_POOL_MIN` | `1` | PostgreSQL pool minimum size | +| `AFK_PG_POOL_MAX` | `10` | PostgreSQL pool maximum size | +| `AFK_VECTOR_DIM` | _(required for Postgres)_ | Vector dimension for Postgres memory search | ## Queue | Variable | Default | Description | | --- | --- | --- | | `AFK_QUEUE_BACKEND` | `inmemory` | `inmemory`, `redis` | +| `AFK_QUEUE_REDIS_URL` | falls back to `AFK_REDIS_URL` | Redis URL for queue backend | +| `AFK_QUEUE_REDIS_HOST` | falls back to `AFK_REDIS_HOST`, then `localhost` | Redis queue host | +| `AFK_QUEUE_REDIS_PORT` | falls back to `AFK_REDIS_PORT`, then `6379` | Redis queue port | +| `AFK_QUEUE_REDIS_DB` | falls back to `AFK_REDIS_DB`, then `0` | Redis queue DB | +| `AFK_QUEUE_REDIS_PASSWORD` | falls back to `AFK_REDIS_PASSWORD` | Redis queue password | +| `AFK_QUEUE_REDIS_PREFIX` | `afk:queue` | Redis key prefix | | `AFK_QUEUE_RETRY_BACKOFF_BASE_S` | `0.5` | Retry base delay | | `AFK_QUEUE_RETRY_BACKOFF_MAX_S` | `30` | Retry max delay | | `AFK_QUEUE_RETRY_BACKOFF_JITTER_S` | `0.2` | Retry jitter | @@ -45,6 +66,29 @@ Execution contracts are configured in code via `TaskWorker(..., execution_contra | --- | --- | --- | | `AFK_AGENT_PROMPTS_DIR` | `.agents/prompt` | Prompt root directory | +## Runner and Command Tools + +| Variable | Default | Description | +| --- | --- | --- | +| `AFK_ALLOWED_COMMANDS` | _(none)_ | Comma-separated default allowlist for runtime command tools | + +## MCP Server + +| Variable | Default | Description | +| --- | --- | --- | +| `AFK_CORS_ORIGINS` | _(none)_ | Comma-separated CORS origins | +| `AFK_MCP_NAME` | `afk-mcp-server` | Server name | +| `AFK_MCP_VERSION` | `1.0.0` | Server version | +| `AFK_MCP_HOST` | `0.0.0.0` | Bind host | +| `AFK_MCP_PORT` | `8000` | Bind port | +| `AFK_MCP_INSTRUCTIONS` | _(none)_ | Optional server instructions | +| `AFK_MCP_PATH` | `/mcp` | HTTP MCP endpoint path | +| `AFK_MCP_SSE_PATH` | `/mcp/sse` | SSE endpoint path | +| `AFK_MCP_HEALTH_PATH` | `/health` | Health endpoint path | +| `AFK_MCP_ENABLE_SSE` | `true` | Enable SSE endpoint | +| `AFK_MCP_ENABLE_HEALTH` | `true` | Enable health endpoint | +| `AFK_MCP_ALLOW_BATCH` | `true` | Allow batched MCP requests | + ## A2A No default environment variables are required. Configure A2A host/auth in code for explicit security posture. diff --git a/README.md b/README.md index 75661e4..80db41d 100644 --- a/README.md +++ b/README.md @@ -1,291 +1,117 @@ -# Agent Forge Kit (AFK) Python SDK (v1.0.0) +# Agent Forge Kit (AFK) Python SDK -**A production-grade framework for building robust, deterministic agent systems.** +AFK is a Python 3.13+ SDK for building reliable AI agents with typed tools, runtime controls, memory, streaming, evals, and observability. -**Documentation:** [afk.arpan.sh](https://afk.arpan.sh) +Documentation: [afk.arpan.sh](https://afk.arpan.sh) -AFK is built for engineers who need more than just a "chat loop." It provides a typed, observable, and fail-safe runtime for orchestrating complex agent behaviors, managing long-running threads, and integrating with your existing infrastructure. +## Install -## Architecture +The distribution package is `afk-py`; the import package is `afk`. -The framework is built on three core pillars: - -1. **Agent**: Stateless definition of identity, instructions, and tools. -2. **Runner**: Stateful execution engine managing the event loop and memory. -3. **Runtime**: Underlying capabilities (LLM I/O, Tool Registry). - -```mermaid -flowchart LR - Agent[Agent Config] --> Runner[Runner Engine] - Runner --> EventLoop - EventLoop <--> Memory[Memory Store] - EventLoop --> LLM[LLM Client] - EventLoop --> Tools[Tool Registry] +```bash +python -m pip install afk-py ``` -## Key Capabilities - -- **Deterministic Orchestration**: Type-safe event loop with guaranteed lifecycle events. -- **Fail-Safe Runtime**: Configurable circuit breakers, cost limits, and retry policies. -- **Observability First**: Built-in OpenTelemetry tracing and structured metrics. -- **Deep Tooling**: Secure tool execution with policy hooks and sandbox profiles. -- **Scalable Memory**: Pluggable backends (SQLite, Redis, Postgres) with auto-compaction. -- **Workflow State Machine**: Build complex multi-step workflows with DAG-like state machines. -- **Policy Audit Logging**: SOC2/GDPR compliant audit trails for all policy decisions. -- **Checkpoint Replay API**: First-class human-in-loop review and rollback support. - -## Why AFK - -AFK is for teams moving from demos to production agents. - -- Use AFK when you need **predictable runs**, **typed tool contracts**, and **policy-gated actions**. -- Use AFK when reliability matters: retries, limits, circuit breakers, observability, and evals are built in. -- Use AFK when you want provider flexibility without rewriting agent logic for each model vendor. - -Choose AFK over raw SDK calls when your workflow includes tools, multi-step execution, approvals, or release gating. -Choose a raw SDK when you only need simple chat/completions and minimal runtime behavior. - -## Installation +For repository development: ```bash -pip install the-afk==1.0.0 +python -m pip install --upgrade pip +python -m pip install -e . pytest ``` ## Quick Start -The `Runner` supports both synchronous (script) and asynchronous (server) execution modes. - ```python -import asyncio from afk.agents import Agent from afk.core import Runner -# 1. Define your agent (stateless) agent = Agent( name="ops-bot", model="gpt-4.1-mini", instructions="You are a helpful SRE assistant.", ) -# 2. Run it (stateful) -async def main(): - runner = Runner() - result = await runner.run(agent, user_message="Check system health") - - print(f"Status: {result.state}") - print(f"Output: {result.final_text}") - -if __name__ == "__main__": - asyncio.run(main()) -``` - -> **Note**: For scripts and CLI tools, you can use `runner.run_sync(...)`. - -## Power User Features - -AFK is designed for complexity. Here are some of the advanced features available out of the box: - -### Fail-Safe Controls - -Prevent runaway costs and infinite loops with `FailSafeConfig`. - -```python -from afk.agents import FailSafeConfig - -agent = Agent( - ..., - fail_safe=FailSafeConfig( - max_steps=20, - max_total_cost_usd=1.00, # Hard stop at $1 - subagent_failure_policy="continue_with_error", - ) +result = Runner().run_sync( + agent, + user_message="What is an error budget?", ) -``` -[Read the Configuration Reference →](https://afk.arpan.sh/library/configuration-reference) - -### Streaming - -Build real-time UIs with the event stream API. - -```python -handle = await runner.run_stream(agent, user_message="...") -async for event in handle: - if event.type == "text_delta": - print(event.text_delta, end="") +print(result.state) +print(result.final_text) ``` -[Read the Streaming Guide →](https://afk.arpan.sh/library/streaming) +## Core Model -### Debug Mode +AFK separates agent behavior from runtime execution: -Use debugger facade or runner config: +- `Agent` describes identity, model, instructions, tools, subagents, skills, MCP servers, and fail-safe limits. +- `Runner` executes agents synchronously, asynchronously, or as a stream. +- Runtime subsystems provide LLM adapters, tool execution, memory, queues, policy, and telemetry. +- `AgentResult` records final text, state, run/thread ids, tool/subagent executions, usage, and cost. -```python -from afk.debugger import Debugger, DebuggerConfig - -debugger = Debugger(DebuggerConfig(redact_secrets=True, verbosity="detailed")) -runner = debugger.runner() -``` +## Add a Tool ```python -from afk.core import Runner, RunnerConfig - -runner = Runner(config=RunnerConfig(debug=True)) -``` - -### Reasoning Controls - -Set agent defaults and optionally override per run: - -```python -agent = Agent( - ..., - reasoning_enabled=True, - reasoning_effort="low", - reasoning_max_tokens=256, -) - -result = await runner.run( - agent, - context={"_afk": {"reasoning": {"enabled": True, "effort": "high", "max_tokens": 512}}}, -) -``` +from pydantic import BaseModel -### Background Tools +from afk.agents import Agent, FailSafeConfig +from afk.core import Runner +from afk.tools import tool -Tools can defer long-running work and resolve later: -```python -from afk.tools import ToolResult, ToolDeferredHandle - -return ToolResult( - success=True, - deferred=ToolDeferredHandle( - ticket_id="build-1", - tool_name="build_project", - status="running", - resume_hint="continue docs while build runs", - ), - metadata={"background_task": build_future}, -) -``` +class LookupArgs(BaseModel): + order_id: str -### Evals -Test your agents with the built-in eval suite. +@tool(args_model=LookupArgs, name="lookup_order", description="Look up an order.") +def lookup_order(args: LookupArgs) -> dict: + return {"order_id": args.order_id, "status": "shipped"} -```python -from afk.evals import run_suite, EvalCase -await run_suite( - cases=[ - EvalCase(input="Hello", assertions=[...]) - ] +agent = Agent( + name="support-agent", + model="gpt-4.1-mini", + instructions="Use lookup_order when users ask about orders.", + tools=[lookup_order], + fail_safe=FailSafeConfig(max_steps=8, max_tool_calls=4, max_total_cost_usd=0.10), ) -``` - -[Read the Evals Guide →](https://afk.arpan.sh/library/evals) - -### Workflow State Machine -Build complex multi-step workflows with a fluent state machine API: - -```python -from afk.agents import WorkflowBuilder, WorkflowExecutor, WorkflowExecutionContext - -workflow = WorkflowBuilder("deploy", "Deploy Service") -workflow.add_node("build", "Build image", timeout_s=300) -workflow.add_node("test", "Run tests", timeout_s=120) -workflow.add_node("deploy", "Deploy to K8s", timeout_s=60) -workflow.add_edge("build", "test").add_edge("test", "deploy") -workflow.set_initial("build") - -spec = workflow.build() -executor = WorkflowExecutor() -context = WorkflowExecutionContext( - workflow_id="deploy", - run_id="run-1", - thread_id="thread-1", -) -result = await executor.execute(spec, context) +result = Runner().run_sync(agent, user_message="Where is order A123?") +print(result.final_text) ``` -### Policy Audit Logging - -Compliance-ready audit logging for all policy decisions: +## Common Commands -```python -from afk.agents import create_policy_audit_logger, AuditConfig - -audit = create_policy_audit_logger( - file_path="./audit.log", - min_level="info", -) - -# Log policy decisions -await audit.log_policy_decision(event, decision, run_id=run_id) - -# Log tool executions -await audit.log_tool_execution("webfetch", allowed=True, run_id=run_id) - -# Log approvals -await audit.log_approval(approved=True, run_id=run_id) +```bash +PYTHONPATH=src pytest -q +PYTHONPATH=src pytest -q tests/agents/test_agent_runtime.py +ruff check src tests +ruff format src tests +./scripts/docs_dev.sh +./scripts/build_agentic_ai_assets.sh ``` -### Checkpoint Replay - -Human-in-loop review with rollback support: - -```python -from afk.agents import create_replay_api +## Install AFK Codex Skills -api = create_replay_api(memory_store) +Install AFK skills with Vercel's Skills CLI: -# Open review session -session = await api.open_session(run_id, thread_id) - -# Get timeline -timeline = await api.get_timeline(run_id, thread_id) -for event in timeline.events: - print(f"Step {event.step}: {event.summary}") - -# Get snapshot at step -snapshot = await api.get_step_snapshot(run_id, thread_id, step=5) - -# Rollback to earlier step -session = await api.rollback(session, target_step=3) +```bash +npx skills add https://github.com/arpan404/afk --skill afk-coder +npx skills add https://github.com/arpan404/afk --skill afk-maintainer ``` -### Memory Auto-Compaction - -Automatic memory management based on importance scoring: +The `skills` CLI installs the selected skill into the configured agent environment, including Codex. -```python -from afk.memory import MemoryCompactor, CompactionConfig - -compactor = MemoryCompactor( - CompactionConfig( - enabled=True, - trigger_threshold_bytes=5_000_000, # 5MB - target_size_bytes=2_000_000, # 2MB - pressure_threshold=0.7, # 70% - compaction_interval_s=60.0, # Check every minute - ) -) - -# Run compaction manually -result = await compactor.compact(events, thread_id) -print(f"Compacted {result.events_compacted} events, " - f"saved {result.memory_saved_bytes} bytes") -``` +## Docs Paths -## Documentation +- Start building: [Quickstart](https://afk.arpan.sh/library/quickstart) +- Guided tutorial: [Learn AFK in 15 Minutes](https://afk.arpan.sh/library/learn-in-15-minutes) +- Public imports: [API Reference](https://afk.arpan.sh/library/api-reference) +- Contributor workflow: [Developer Guide](https://afk.arpan.sh/library/developer-guide) +- Environment variables: [Environment Variables](https://afk.arpan.sh/library/environment-variables) -- **[Configuration Reference](https://afk.arpan.sh/library/configuration-reference)**: Full list of options. -- **[API Reference](https://afk.arpan.sh/library/api-reference)**: Classes and methods. -- **[Architecture & Modules](https://afk.arpan.sh/library/full-module-reference)**: Inner workings. +## When to Use AFK -## License +Use AFK when your agent needs tools, multi-step execution, streaming, memory, approvals, cost limits, telemetry, evals, queues, or provider portability. -MIT. See `LICENSE`. +A direct provider SDK may be simpler for one-off single-turn text generation. diff --git a/docs/docs.json b/docs/docs.json index 97793b2..6577e5e 100644 --- a/docs/docs.json +++ b/docs/docs.json @@ -16,6 +16,32 @@ "type": "github", "url": "https://github.com/arpan404/afk" }, + "topbarLinks": [ + { + "type": "link", + "name": "Quickstart", + "url": "/library/quickstart", + "arrow": false + }, + { + "type": "link", + "name": "Examples", + "url": "/library/examples/", + "arrow": false + }, + { + "type": "link", + "name": "API", + "url": "/library/api-reference", + "arrow": false + }, + { + "type": "link", + "name": "Skills", + "url": "/library/agent-skills", + "arrow": false + } + ], "sidebar": { "items": "undecorated" }, @@ -30,31 +56,24 @@ "github": "https://github.com/arpan404/afk" } }, - "tabs": [ - { - "name": "Examples", - "url": "library/examples" - } - ], "navigation": { "tabs": [ { - "tab": "Guides", + "tab": "Build with AFK", "groups": [ { - "group": "Welcome", - "pages": ["index", "library/overview", "library/quickstart"] - }, - { - "group": "Learn", + "group": "Start Here", "pages": [ - "library/learn-in-15-minutes", + "index", + "library/overview", "library/mental-model", + "library/quickstart", + "library/learn-in-15-minutes", "library/how-to-use-afk" ] }, { - "group": "Core Concepts", + "group": "Core Building Blocks", "pages": [ "library/agents", "library/core-runner", @@ -66,79 +85,107 @@ ] }, { - "group": "Scaling Up", + "group": "LLM Runtime", "pages": [ - "library/agentic-behavior", - "library/agentic-levels", - "library/architecture", - "library/building-with-ai", - "library/developer-guide" + "llms/index", + "llms/contracts", + "llms/adapters", + "llms/control-and-session", + "llms/agent-integration" ] }, { - "group": "Communication", + "group": "Production", "pages": [ - "library/messaging", - "library/a2a", + "library/building-with-ai", + "library/evals", + "library/observability", + "library/security-model", "library/task-queues", - "library/mcp-server" + "library/deployment", + "library/performance", + "library/troubleshooting" ] }, { - "group": "LLM Layer", + "group": "Integrations", "pages": [ - "llms/index", - "llms/contracts", - "llms/adapters", - "llms/control-and-session", - "llms/agent-integration" + "library/mcp-server", + "library/a2a", + "library/messaging" + ] + } + ] + }, + { + "tab": "Maintain AFK", + "groups": [ + { + "group": "Contributor Guide", + "pages": [ + "library/developer-guide", + "library/architecture", + "library/public-imports-and-function-improvement", + "library/tested-behaviors" ] }, { - "group": "Reliability", + "group": "Internal Contracts", "pages": [ - "library/observability", - "library/evals", - "library/security-model", - "library/failure-policy-matrix" + "library/agentic-behavior", + "library/agentic-levels", + "library/failure-policy-matrix", + "library/tool-call-lifecycle", + "library/tools-system-walkthrough", + "library/run-event-contract", + "library/checkpoint-schema", + "library/llm-interaction", + "library/debugger" ] + }, + { + "group": "Migration", + "pages": ["library/migration"] } ] }, { - "tab": "References", - "pages": [ - "library/api-reference", - "library/configuration-reference", - "library/environment-variables", - "library/full-module-reference", - "library/debugger", - "library/public-imports-and-function-improvement", - "library/run-event-contract", - "library/llm-interaction", - "library/tested-behaviors", - "library/tools-system-walkthrough", - "library/tool-call-lifecycle", - "library/checkpoint-schema" + "tab": "Reference", + "groups": [ + { + "group": "API and Configuration", + "pages": [ + "library/api-reference", + "library/configuration-reference", + "library/environment-variables", + "library/full-module-reference" + ] + } ] }, { "tab": "Examples", - "pages": [ - "library/examples/index", - "library/snippets/01_minimal_chat_agent", - "library/snippets/02_policy_with_hitl", - "library/snippets/03_subagents_with_router", - "library/snippets/04_resume_and_compact", - "library/snippets/05_direct_llm_structured_output", - "library/snippets/06_tool_registry_security", - "library/snippets/07_tool_hooks_and_middleware", - "library/snippets/08_prebuilt_runtime_tools", - "library/snippets/09_system_prompt_loader", - "library/snippets/10_streaming_chat_with_memory", - "library/snippets/11_cost_monitoring", - "library/snippets/12_mcp_client_integration", - "library/snippets/13_multi_model_fallback" + "groups": [ + { + "group": "Runnable Snippets", + "pages": [ + "library/examples/index", + "library/snippets/01_minimal_chat_agent", + "library/snippets/02_policy_with_hitl", + "library/snippets/03_subagents_with_router", + "library/snippets/04_resume_and_compact", + "library/snippets/05_direct_llm_structured_output", + "library/snippets/06_tool_registry_security", + "library/snippets/07_tool_hooks_and_middleware", + "library/snippets/08_prebuilt_runtime_tools", + "library/snippets/09_system_prompt_loader", + "library/snippets/10_streaming_chat_with_memory", + "library/snippets/11_cost_monitoring", + "library/snippets/12_mcp_client_integration", + "library/snippets/13_multi_model_fallback", + "library/snippets/14_production_client" + ] + } ] } ] diff --git a/docs/index.mdx b/docs/index.mdx index 54b1436..d5348b8 100644 --- a/docs/index.mdx +++ b/docs/index.mdx @@ -1,167 +1,145 @@ --- -title: AFK — Agent Forge Kit -description: Build reliable AI agents in Python with typed tools, multi-agent orchestration, and production-grade controls. +title: AFK - Agent Forge Kit +description: Build reliable Python agents with typed tools, runtime controls, and production observability. --- -
-

- AFK is the Python SDK for building AI agents that are{" "} - reliable by default. Define agents with typed contracts, - wire in tools and policies, and ship to production with observability, - evals, and safety limits built in. -

-
+AFK is a Python 3.13+ SDK for building AI agents that need to run reliably outside a demo. You define agents, tools, policies, and memory as typed Python contracts. The runner handles the agent loop, LLM calls, tool execution, streaming, checkpoints, telemetry, and safety limits. + +## Choose your path + + + + Start here if you are building an application, workflow, chatbot, coding tool, + or production agent on top of AFK. + + + Start here if you are changing AFK itself, reviewing internals, or updating + public contracts. + + ## Install +The distribution package is `afk-py`; the import package is `afk`. + ```bash - pip install afk + python -m pip install afk-py ``` - - ```bash - poetry add afk + + ```bash + uv add afk-py ``` - + ```bash - uv add afk + python -m pip install --upgrade pip + python -m pip install -e . pytest ``` -> **NOTE:** You need an LLM provider API key. Set `OPENAI_API_KEY`, or configure any provider supported by [LiteLLM](https://docs.litellm.ai/docs/providers). - -## Your first agent - -```python -from afk.agents import Agent -from afk.core import Runner - -agent = Agent( - name="my-agent", - model="gpt-5.2-mini", - instructions="You are a helpful assistant. Be concise and accurate.", -) +Set an LLM provider key before running examples: -runner = Runner() -result = runner.run_sync(agent, user_message="What is an error budget in SRE?") -print(result.final_text) # ← The agent's response -print(result.state) # ← "completed" +```bash +export OPENAI_API_KEY="..." ``` - - 1. `Agent(...)` defines the agent — its name, model, and system prompt - instructions. 2. `Runner()` creates the execution engine with safe defaults - (headless mode, in-memory state). 3. `run_sync()` sends the user message to - the LLM, waits for the response, and returns an `AgentResult`. 4. `final_text` - holds the model's response. `state` tells you how the run ended (`completed`, - `failed`, `degraded`, or `cancelled`). - - -## Add a tool in 30 seconds +## First agent ```python -from pydantic import BaseModel from afk.agents import Agent -from afk.tools import tool from afk.core import Runner -class WeatherArgs(BaseModel): - city: str - -@tool(args_model=WeatherArgs, name="get_weather", description="Get current weather for a city.") -def get_weather(args: WeatherArgs) -> dict: - return {"city": args.city, "temp_f": 72, "condition": "sunny"} - agent = Agent( - name="weather-bot", - model="gpt-5.2-mini", - instructions="Answer weather questions using your tools.", - tools=[get_weather], # ← Attach tools here + name="assistant", + model="gpt-4.1-mini", + instructions="Answer directly with concrete detail.", +) + +result = Runner().run_sync( + agent, + user_message="What is an error budget in SRE?", ) -runner = Runner() -result = runner.run_sync(agent, user_message="What's the weather in Austin?") -print(result.final_text) # ← "It's 72°F and sunny in Austin." +print(result.final_text) +print(result.state) ``` -## What AFK gives you +What matters: + +- `Agent` is configuration: model, instructions, tools, policies, subagents, and defaults. +- `Runner` is execution: LLM calls, tool calls, memory, streaming, checkpoints, and telemetry. +- `AgentResult` is the run record: final text, terminal state, tool/subagent records, usage, and cost. + +## Core capabilities - Define agents with typed inputs, instructions, tools, and subagents. - Single-agent or multi-agent — you choose. + Declarative agent definitions with instructions, tools, subagents, skills, + MCP servers, and safety limits. - Execute agents synchronously, asynchronously, or with real-time streaming. - Pause, resume, cancel at any point. + Sync, async, and streaming execution with lifecycle control and thread + continuity. - Give agents capabilities through typed Python functions. Schema validation, - policy gates, and output sanitization built in. - - - Persist conversation state across runs. Resume interrupted runs from - checkpoints. Compact long threads automatically. + Typed Python tools with Pydantic validation, hooks, middleware, sandboxing, + and bounded output. - - Orchestrate subagent DAGs with fan-out/fan-in, join policies, and - backpressure controls. + + Provider-portable LLM clients with retries, timeouts, rate limits, caching, + circuit breakers, and fallback chains. - - Provider-portable LLM runtime with retry, circuit breaking, caching, rate - limiting, and fallback chains. - - - Built-in telemetry pipeline with spans, metrics, and exporters for console, - JSON, and OpenTelemetry. - - - Behavioral testing framework with assertions, budgets, golden traces, and - CI-ready reporting. + + Thread state, checkpoints, retention, compaction, and persistent stores. - - Policy engine, sandbox profiles, tool allowlists, A2A auth, and secret scope - isolation out of the box. + + Evals, observability, queues, security controls, deployment guidance, and + troubleshooting. -## How it all fits together +## AFK skills + +Install the AFK skills with Vercel's Skills CLI when you want Codex or another supported agent to use AFK-specific guidance: + +```bash +npx skills add https://github.com/arpan404/afk --skill afk-coder +npx skills add https://github.com/arpan404/afk --skill afk-maintainer +``` + +Use `afk-coder` when building applications with AFK. Use `afk-maintainer` when reviewing or changing AFK itself. See [Agent Skills](/library/agent-skills) for details. + +## How AFK fits together ```mermaid -flowchart TB - User([User / API]) --> Runner +flowchart LR + User["User or API"] --> Runner Runner --> Agent - Agent --> LLM["LLM Runtime"] - Agent --> Tools["Tool Registry"] + Agent --> LLM["LLM runtime"] + Agent --> Tools["Tool registry"] Agent --> Subagents["Subagents"] - Runner --> Memory["Memory Store"] - Runner --> Policy["Policy Engine"] + Runner --> Memory["Memory store"] + Runner --> Policy["Policy and HITL"] Runner --> Telemetry["Observability"] - LLM --> Providers["OpenAI · Anthropic · LiteLLM"] ``` -## Where to go next +## Next steps - Build your first agent with tools and streaming in 5 minutes. + Build one runnable agent with one typed tool. - - Hands-on tutorial covering agents, tools, streaming, multi-agent, and - memory. + + Walk through agents, tools, streaming, memory, and safety. - Runnable code examples for every major AFK feature. + Find complete snippets by scenario. - Complete import reference organized by task. + Check canonical imports, signatures, result fields, and stability rules. diff --git a/docs/library/a2a.mdx b/docs/library/a2a.mdx index 1dbb412..fc4a9e3 100644 --- a/docs/library/a2a.mdx +++ b/docs/library/a2a.mdx @@ -40,12 +40,17 @@ flowchart LR ```python - from afk.agents.contracts import AgentInvocationRequest + from afk.messaging import AgentInvocationRequest request = AgentInvocationRequest( + run_id="run-42", + thread_id="thread-42", + conversation_id="conversation-42", + correlation_id="analysis-42", + source_agent="system-a", target_agent="analyzer", - user_message="Analyze this dataset", - context={"source": "system-a"}, + payload={"user_message": "Analyze this dataset"}, + metadata={"source": "system-a"}, idempotency_key="analysis-42", ) ``` @@ -60,8 +65,8 @@ flowchart LR ```python response = await client.invoke(request) - print(response.final_text) # Agent's response - print(response.state) # "completed" + print(response.output) # Agent output payload + print(response.success) # True when handled successfully print(response.run_id) # For tracing ``` @@ -73,22 +78,31 @@ flowchart LR ```python class AgentInvocationRequest(BaseModel): - target_agent: str # Which agent to invoke - user_message: str # The input message - context: dict = {} # Additional context - idempotency_key: str # Deduplication key - timeout_s: float = 60.0 # Max wait time - thread_id: str | None = None # For multi-turn + run_id: str + thread_id: str + conversation_id: str + correlation_id: str + idempotency_key: str + source_agent: str + target_agent: str + payload: dict = {} + metadata: dict = {} + timeout_s: float | None = None ``` ```python class AgentInvocationResponse(BaseModel): - final_text: str # Agent's response - state: str # completed, failed, degraded - run_id: str # Unique run identifier - error: str | None = None # Error details (if failed) - usage: UsageAggregate | None # Token usage + run_id: str + thread_id: str + conversation_id: str + correlation_id: str + idempotency_key: str + source_agent: str + target_agent: str + success: bool + output: dict | str | None = None + error: str | None = None ``` @@ -98,13 +112,11 @@ flowchart LR Expose your agents as an A2A-accessible service: ```python -from afk.agents.a2a import A2AServiceHost -from afk.agents.a2a.auth import APIKeyA2AAuthProvider -from afk.agents import Agent +from afk.agents import Agent, A2AServiceHost, APIKeyA2AAuthProvider from afk.core import Runner # Define the agent -analyzer = Agent(name="analyzer", model="gpt-5.2-mini", instructions="Analyze data.") +analyzer = Agent(name="analyzer", model="gpt-4.1-mini", instructions="Analyze data.") # Create auth provider auth = APIKeyA2AAuthProvider( @@ -131,39 +143,39 @@ AFK ships with three auth providers: Permits all requests without authentication. **Never use in production.** - ```python - from afk.agents.a2a.auth import AllowAllA2AAuthProvider +```python +from afk.agents import AllowAllA2AAuthProvider - auth = AllowAllA2AAuthProvider() - ``` +auth = AllowAllA2AAuthProvider() +``` Validates requests against pre-shared API keys with HMAC-SHA256 hashing. - ```python - from afk.agents.a2a.auth import APIKeyA2AAuthProvider +```python +from afk.agents import APIKeyA2AAuthProvider - auth = APIKeyA2AAuthProvider( - keys={"caller-id": "secret-key"}, - server_secret="hmac-server-secret", - ) - ``` +auth = APIKeyA2AAuthProvider( + keys={"caller-id": "secret-key"}, + server_secret="hmac-server-secret", +) +``` Validates JWT tokens with configurable issuer and audience claims. - ```python - from afk.agents.a2a.auth import JWTA2AAuthProvider +```python +from afk.agents import JWTA2AAuthProvider - auth = JWTA2AAuthProvider( - secret="jwt-signing-secret", - algorithm="HS256", - issuer="https://auth.example.com", - audience="agent-service", - ) - ``` +auth = JWTA2AAuthProvider( + secret="jwt-signing-secret", + algorithm="HS256", + issuer="https://auth.example.com", + audience="agent-service", +) +``` @@ -173,15 +185,12 @@ AFK ships with three auth providers: For interoperability with Google's A2A protocol, use the Google adapter: ```python -from afk.agents.a2a.google_adapter import GoogleA2AAdapter +from afk.agents import GoogleA2AProtocolAdapter -adapter = GoogleA2AAdapter( - protocol=internal_protocol, - auth_provider=auth, -) +adapter = GoogleA2AProtocolAdapter(client=google_a2a_client) ``` -The adapter normalizes Google A2A envelope formats to AFK's internal protocol types. +The adapter wraps a configured Google A2A SDK client behind AFK's `AgentCommunicationProtocol`. ## Security considerations diff --git a/docs/library/agent-skills.mdx b/docs/library/agent-skills.mdx index f487ddb..cf4e576 100644 --- a/docs/library/agent-skills.mdx +++ b/docs/library/agent-skills.mdx @@ -68,6 +68,34 @@ Check the dashboard at https://monitoring.internal/deployments The YAML frontmatter (`name`, `description`) helps the agent decide which skill is relevant. The markdown body is the knowledge content. +## Installing repository skills + +This repository includes two skills for agentic development: + +- `afk-coder` for building applications with AFK. +- `afk-maintainer` for reviewing or changing AFK itself. + +Install them with Vercel's Skills CLI: + +```bash +npx skills add https://github.com/arpan404/afk --skill afk-coder +npx skills add https://github.com/arpan404/afk --skill afk-maintainer +``` + +Install one skill at a time: + +```bash +npx skills add https://github.com/arpan404/afk --skill afk-coder +``` + +For another repository, use the same shape: + +```bash +npx skills add https://github.com/vercel-labs/skills --skill find-skills +``` + +The Skills CLI installs the selected skill into the configured agent environment. See [skills.sh](https://www.skills.sh/) for CLI details and supported agents. + ## Registering skills on an agent ```python @@ -75,7 +103,7 @@ from afk.agents import Agent agent = Agent( name="devops-agent", - model="gpt-5.2-mini", + model="gpt-4.1-mini", instructions=""" You are a DevOps assistant. Use your skills to help with deployment, monitoring, and infrastructure tasks. @@ -153,7 +181,7 @@ The agent autonomously decides which skills to read based on the user's question ```python agent = Agent( name="reader", - model="gpt-5.2-mini", + model="gpt-4.1-mini", skills_dir="skills/", skill_tools=["list_skills", "read_skill_md", "read_skill_file"], # ← No run_skill_command diff --git a/docs/library/agentic-behavior.mdx b/docs/library/agentic-behavior.mdx index 4e04691..fdc8f2c 100644 --- a/docs/library/agentic-behavior.mdx +++ b/docs/library/agentic-behavior.mdx @@ -13,14 +13,14 @@ from afk.agents import Agent from afk.core import Runner # Specialist agents -researcher = Agent(name="researcher", model="gpt-5.2-mini", instructions="Find facts.") -writer = Agent(name="writer", model="gpt-5.2-mini", instructions="Write summaries.") -reviewer = Agent(name="reviewer", model="gpt-5.2-mini", instructions="Review for accuracy.") +researcher = Agent(name="researcher", model="gpt-4.1-mini", instructions="Find facts.") +writer = Agent(name="writer", model="gpt-4.1-mini", instructions="Write summaries.") +reviewer = Agent(name="reviewer", model="gpt-4.1-mini", instructions="Review for accuracy.") # Coordinator coordinator = Agent( name="coordinator", - model="gpt-5.2-mini", + model="gpt-4.1-mini", instructions=""" 1. Ask 'researcher' for facts 2. Ask 'writer' to summarize diff --git a/docs/library/agentic-levels.mdx b/docs/library/agentic-levels.mdx index 3373981..8432b94 100644 --- a/docs/library/agentic-levels.mdx +++ b/docs/library/agentic-levels.mdx @@ -12,7 +12,7 @@ AFK applications progress through five levels of capability. Each level adds fea One agent, one model, no tools. The simplest possible setup. ```python - agent = Agent(name="classifier", model="gpt-5.2-mini", instructions="Classify as positive/negative/neutral.") + agent = Agent(name="classifier", model="gpt-4.1-mini", instructions="Classify as positive/negative/neutral.") result = runner.run_sync(agent, user_message="Great product!") ``` @@ -31,7 +31,7 @@ AFK applications progress through five levels of capability. Each level adds fea @tool(args_model=SearchArgs, name="search", description="Search knowledge base.") def search(args: SearchArgs) -> dict: ... - agent = Agent(name="helper", model="gpt-5.2-mini", tools=[search]) + agent = Agent(name="helper", model="gpt-4.1-mini", tools=[search]) ``` **AFK features added:** `@tool`, Pydantic models, `FailSafeConfig` @@ -48,7 +48,7 @@ AFK applications progress through five levels of capability. Each level adds fea ```python coordinator = Agent( name="coordinator", - model="gpt-5.2-mini", + model="gpt-4.1-mini", subagents=[researcher, writer, reviewer], ) ``` @@ -81,11 +81,12 @@ AFK applications progress through five levels of capability. Each level adds fea Agents communicate across systems using the A2A protocol with authenticated endpoints. ```python - from afk.a2a import A2AClient - response = await a2a_client.invoke("external-agent", request=invocation_request) + from afk.messaging import AgentInvocationRequest + + response = await protocol.invoke(AgentInvocationRequest(...)) ``` - **AFK features added:** `A2AClient`, `A2AServer`, auth providers, external adapters + **AFK features added:** `InternalA2AProtocol`, `A2AServiceHost`, auth providers, external adapters **Good for:** Microservice agent meshes, third-party integrations, federated AI systems diff --git a/docs/library/agents.mdx b/docs/library/agents.mdx index 8df57f2..e6baea8 100644 --- a/docs/library/agents.mdx +++ b/docs/library/agents.mdx @@ -12,12 +12,12 @@ from afk.agents import Agent agent = Agent( name="assistant", # ← Identity (used in logs, telemetry) - model="gpt-5.2-mini", # ← Which LLM to use + model="gpt-4.1-mini", # ← Which LLM to use instructions="Be helpful and concise.", # ← System prompt ) ``` -That's it. Three fields define a working agent. Everything else is optional. +Those are the fields most examples should set. `model` is the only required constructor argument, but `name` and `instructions` make traces and behavior easier to understand. ## Agent fields reference @@ -44,7 +44,7 @@ That's it. Three fields define a working agent. Everything else is optional. | `max_steps` | `int` | `20` | Maximum agent loop iterations | | `tool_parallelism` | `int` | `None` | Max concurrent tool executions per step | | `subagent_parallelism_mode` | `str` | `"configurable"` | How subagent concurrency is managed | -| `reasoning_enabled`| `bool` | `None` | Enable extended thinking / chain-of-thought | +| `reasoning_enabled`| `bool` | `None` | Enable provider-supported reasoning controls | | `reasoning_effort` | `str` | `None` | Thinking effort level (e.g. `"low"`, `"medium"`, `"high"`) | | `reasoning_max_tokens` | `int` | `None` | Token budget for extended thinking | | `skill_tool_policy`| `SkillToolPolicy` | `None` | Security policy for skill-provided tool execution | @@ -61,7 +61,7 @@ That's it. Three fields define a working agent. Everything else is optional. ```python agent = Agent( name="classifier", - model="gpt-5.2-mini", + model="gpt-4.1-mini", instructions=""" Classify the input into one of: positive, negative, neutral. Output only the label. @@ -81,19 +81,19 @@ That's it. Three fields define a working agent. Everything else is optional. ```python researcher = Agent( name="researcher", - model="gpt-5.2-mini", + model="gpt-4.1-mini", instructions="Find relevant facts. Be thorough.", ) writer = Agent( name="writer", - model="gpt-5.2-mini", + model="gpt-4.1-mini", instructions="Write clear, concise summaries.", ) coordinator = Agent( name="coordinator", - model="gpt-5.2-mini", + model="gpt-4.1-mini", instructions=""" You manage a team: - Delegate research to 'researcher' @@ -139,7 +139,7 @@ from afk.agents import Agent, FailSafeConfig agent = Agent( name="safe-agent", - model="gpt-5.2-mini", + model="gpt-4.1-mini", instructions="...", tools=[...], fail_safe=FailSafeConfig( @@ -155,7 +155,7 @@ agent = Agent( subagent_failure_policy="continue", # "continue" | "retry_then_fail" | "skip_action" # Fallback model chain for LLM resilience - fallback_model_chain=["gpt-5.2-mini", "gpt-5.2-nano"], + fallback_model_chain=["gpt-4.1-mini", "gpt-4.1-nano"], ), ) ``` @@ -171,6 +171,7 @@ Attach a [PolicyEngine](/library/security-model) to control what the agent can d ```python from afk.agents import Agent, PolicyEngine, PolicyRule +from afk.core import Runner policy = PolicyEngine(rules=[ PolicyRule( diff --git a/docs/library/api-reference.mdx b/docs/library/api-reference.mdx index 89ecd1c..b7a1fa4 100644 --- a/docs/library/api-reference.mdx +++ b/docs/library/api-reference.mdx @@ -1,332 +1,201 @@ --- title: API Reference -description: Quick import reference and constructor signatures for the Agent Forge Kit (AFK) Python SDK. +description: Canonical public imports and core signatures for AFK. --- -This page provides a quick reference for commonly used AFK imports and constructor signatures. For the full field-by-field reference, see the [Configuration Reference](/library/configuration-reference). For module-level documentation, see the [Module Reference](/library/full-module-reference). - -## Quick import map - -| Task | Import | From | -| ----------------------- | ----------------------------------------------------- | ------------------ | -| Define an agent | `from afk.agents import Agent` | `afk.agents` | -| Configure safety limits | `from afk.agents import FailSafeConfig` | `afk.agents` | -| Set up policy engine | `from afk.agents import PolicyEngine, PolicyRule` | `afk.agents` | -| Run an agent | `from afk.core import Runner` | `afk.core` | -| Configure the runner | `from afk.core import RunnerConfig` | `afk.core` | -| Debug a run | `from afk.debugger import Debugger, DebuggerConfig` | `afk.debugger` | -| Define a tool | `from afk.tools import tool` | `afk.tools` | -| Tool context access | `from afk.tools import ToolContext` | `afk.tools` | -| Tool hooks | `from afk.tools import prehook, posthook, middleware` | `afk.tools` | -| Build an LLM client | `from afk.llms import LLMBuilder` | `afk.llms` | -| Run evals | `from afk.evals import run_suite` | `afk.evals` | -| Define eval cases | `from afk.evals.models import EvalCase, EvalBudget` | `afk.evals.models` | -| A2A client | `from afk.a2a import A2AClient` | `afk.a2a` | -| MCP server | `from afk.mcp import MCPServer` | `afk.mcp` | -| Task queue | `from afk.queues import TaskQueue, TaskItem` | `afk.queues` | - -## Architecture overview - -The framework is built on three core pillars that interact at runtime: - -1. **Agent** (`afk.agents.Agent`) — The **definition**. - It holds the static configuration: "Who am I?" (instructions, name), "What can I do?" (tools, subagents), and "How safe am I?" (fail-safe limits). Agents are stateless definitions that can be reused across many runs. - -2. **Runner** (`afk.core.Runner`) — The **engine**. - It orchestrates the dynamic execution: managing the event loop, maintaining conversation state in memory, executing tools, handling failures, and dispatching events to telemetry. Runners are stateful (holding connections to memory/telemetry) but re-entrant. - -3. **Runtime** (`afk.llms`, `afk.tools`) — The **capabilities**. - The underlying machinery that powers the agent: LLM clients handling provider I/O, and the tool registry managing safe execution of Python functions. - -```mermaid -flowchart LR - subgraph Definition - Agent[Agent Config] - end - subgraph Execution - Runner[Runner Engine] - Memory[Memory Store] - EventLoop[Event Loop] - end - subgraph Runtime - LLM[LLM Client] - Tools[Tool Registry] - end - - Agent --> Runner - Runner --> EventLoop - EventLoop <--> Memory - EventLoop --> LLM - EventLoop --> Tools -``` - -## Execution model - -The `Runner` supports three execution modes for different use cases. All modes use the same underlying event loop and guarantee the same consistency models. - -### 1. Synchronous (`run_sync`) - -Blocks the calling thread until the run is complete. Best for scripts, cron jobs, and simple CLI tools. - -- **Concurrency**: Internal tasks (like tool execution) still run on the asyncio event loop, but the entry point is blocking. -- **Return**: Returns the final `AgentResult` only after the run reaches a terminal state (`completed`, `failed`). +This page is the public import contract for application developers. Use these imports in docs, examples, tests that exercise public behavior, and downstream applications. -### 2. Asynchronous (`await run`) +For field-by-field configuration, see [Configuration Reference](/library/configuration-reference). For generated module detail, see [Full Module Reference](/library/full-module-reference). -Returns an awaitable coroutine. Best for backend API handlers (FastAPI, Django Async) and high-concurrency workloads. +## Import map -- **Concurrency**: Non-blocking. Allows thousands of concurrent agent runs in a single process. -- **Return**: Returns `AgentResult` upon completion. +| Task | Public import | +| --- | --- | +| Define an agent | `from afk.agents import Agent` | +| Configure fail-safe limits | `from afk.agents import FailSafeConfig` | +| Configure policy rules | `from afk.agents import PolicyEngine, PolicyRule` | +| Implement dynamic policy hooks | `from afk.agents import PolicyRole` | +| Run an agent | `from afk.core import Runner` | +| Configure the runner | `from afk.core import RunnerConfig` | +| Consume streaming events | `from afk.core import AgentStreamEvent, AgentStreamHandle` | +| Define a tool | `from afk.tools import tool` | +| Access tool context | `from afk.tools import ToolContext` | +| Build an LLM client | `from afk.llms import LLMBuilder` | +| Run eval suites | `from afk.evals import run_suite` | +| Define eval cases | `from afk.evals import EvalCase, EvalBudget` | +| Create memory stores | `from afk.memory import InMemoryMemoryStore, SQLiteMemoryStore` | +| Create task queues | `from afk.queues import InMemoryTaskQueue, RedisTaskQueue, TaskWorker` | +| Expose MCP tools | `from afk.mcp import MCPServer` | +| Use A2A messaging | `from afk.messaging import InternalA2AProtocol` | -### 3. Streaming (`await run_stream`) +Do not use `src.afk.*` imports in user-facing docs. -Returns an async iterator of `AgentStreamEvent`s. Best for real-time UIs (chatbots, IDE integrations). +## Agent -- **Feedback**: Emits events _immediately_ as they happen (token delta, tool start, tool end). -- **Control**: Allows the consumer to abort the run by cancelling the stream consumption. - ---- - -## Most common pattern - -```python -from afk.agents import Agent, FailSafeConfig -from afk.tools import tool -from afk.core import Runner, RunnerConfig -from pydantic import BaseModel -``` - ---- - -## Constructor signatures - -### Agent - -```python +```text from afk.agents import Agent agent = Agent( *, - model: str | LLM, # Required — model name or client - name: str | None = None, # Agent identity - instructions: str | callable | None = None, # System prompt - instruction_file: str | Path | None = None, # Prompt file path - tools: list | None = None, # Tool list - subagents: list[Agent] | None = None, # Specialist subagents - context_defaults: dict | None = None, # Default run context - fail_safe: FailSafeConfig | None = None, # Limits and failure policies - max_steps: int = 20, # Max loop iterations - policy_engine: PolicyEngine | None = None, # Deterministic policy rules - model_resolver: callable | None = None, # Custom model resolver - skills: list[str] | None = None, # Skill names to load - mcp_servers: list | None = None, # External MCP server refs + model: str | LLM, + name: str | None = None, + tools: list | None = None, + subagents: list[Agent] | None = None, + instructions: str | callable | None = None, + instruction_file: str | Path | None = None, + prompts_dir: str | Path | None = None, + context_defaults: dict | None = None, + inherit_context_keys: list[str] | None = None, + model_resolver: callable | None = None, + skills: list[str] | None = None, + mcp_servers: list | None = None, + skills_dir: str | Path = ".agents/skills", + instruction_roles: list | None = None, + policy_roles: list | None = None, + policy_engine: PolicyEngine | None = None, + subagent_router: callable | None = None, + max_steps: int = 20, + tool_parallelism: int | None = None, + subagent_parallelism_mode: str = "configurable", + fail_safe: FailSafeConfig | None = None, + reasoning_enabled: bool | None = None, + reasoning_effort: str | None = None, + reasoning_max_tokens: int | None = None, + skill_tool_policy: SkillToolPolicy | None = None, + enable_mcp_tools: bool = True, + enable_skill_tools: bool = True, + runner: Runner | None = None, ) ``` - - Only `model` is required. See the [Configuration - Reference](/library/configuration-reference#agent) for the full list of 25+ - fields with defaults. - +Only `model` is required. Most examples should also set `name` and `instructions` for clear traces and predictable behavior. -### Runner +## Runner ```python -from afk.core import Runner +from afk.core import Runner, RunnerConfig runner = Runner( - *, - memory_store: MemoryStore | None = None, # Memory backend - interaction_provider: InteractionProvider | None = None, # HITL provider - policy_engine: PolicyEngine | None = None, # Shared policy engine - telemetry: str | TelemetrySink | None = None, # "console" | "otel" | "json" - telemetry_config: dict | None = None, # Backend config - config: RunnerConfig | None = None, # Runner configuration + memory_store=None, + interaction_provider=None, + policy_engine=None, + telemetry=None, + telemetry_config=None, + config=RunnerConfig(), ) ``` -#### Key Runner methods - -- **`run_sync(agent, ...)`** -> `AgentResult` - Blocking execution. In memory-backend modes, this persists state to the DB at every step. In-memory mode is transient. - _Use for:_ Scripts, CLI tools, tests. - -- **`await run(agent, ...)`** -> `AgentResult` - Async execution. This is the standard entry point for scalable applications. It handles the full agent loop: LLM calls -> Tool execution -> Policy checks -> Recursion. - _Use for:_ Web servers, queue workers, scalable backend services. - -- **`await run_stream(agent, ...)`** -> `AgentStreamHandle` - Returns a handle exposing an `async iterator` of events. The stream yields events as they happen. You MUST consume the stream to drive execution forward. - _Use for:_ Chat interfaces, real-time feedback. - -- **`await list_background_tools(*, thread_id, run_id, include_resolved=False)`** -> `list[dict]` - Lists persisted deferred tool tickets for a run. - _Use for:_ Long-running tool monitoring and external worker reconciliation. +Common methods: -- **`await resolve_background_tool(*, thread_id, run_id, ticket_id, output=...)`** -> `None` - Marks a deferred ticket as completed and stores output for runner polling. - _Use for:_ External worker completion callbacks. +| Method | Use when | Returns | +| --- | --- | --- | +| `runner.run_sync(agent, user_message=..., thread_id=...)` | Scripts, CLIs, tests without an existing event loop | `AgentResult` | +| `await runner.run(agent, user_message=..., thread_id=...)` | Async services, workers, APIs | `AgentResult` | +| `await runner.run_stream(agent, user_message=..., thread_id=...)` | Chat UIs and progress streams | `AgentStreamHandle` | +| `await runner.resume(agent, run_id=..., thread_id=..., context=...)` | Continue from persisted state | `AgentResult` | +| `await runner.compact_thread(thread_id=...)` | Apply memory retention/compaction | `MemoryCompactionResult` | -- **`await fail_background_tool(*, thread_id, run_id, ticket_id, error=...)`** -> `None` - Marks a deferred ticket as failed with error details. - _Use for:_ External worker failure propagation. +`run_sync()` is a convenience wrapper for synchronous code. Use `await runner.run(...)` inside async applications. -- **`await resume(agent, *, run_id, thread_id)`** -> `AgentResult` - Re-hydrates a run from the database. It loads the full execution snapshot (messages, unexecuted tool calls) and continues exactly where it left off. - _Use for:_ Human-in-the-loop workflows (resume after approval), recovering from process crashes. - -- **`await compact_thread(*, thread_id)`** -> `None` - Triggers memory compaction for a specific thread. This applies the configured `RetentionPolicy` to reduce token usage by summarizing history or discarding old messages. - _Use for:_ Long-running conversations where context window limits are a concern. - -### RunnerConfig +## RunnerConfig ```python from afk.core import RunnerConfig config = RunnerConfig( - interaction_mode: str = "headless", # "headless" | "interactive" | "external" - sanitize_tool_output: bool = True, # Strip injection vectors - tool_output_max_chars: int = 12_000, # Output truncation limit - approval_timeout_s: float = 300.0, # Approval timeout - approval_fallback: str = "deny", # "allow" | "deny" | "defer" + interaction_mode="headless", + approval_timeout_s=300.0, + input_timeout_s=300.0, + approval_fallback="deny", + input_fallback="deny", + sanitize_tool_output=True, + untrusted_tool_preamble=True, + tool_output_max_chars=12_000, + max_parallel_subagents_global=64, + max_parallel_subagents_per_parent=8, + max_parallel_subagents_per_target_agent=4, + checkpoint_async_writes=True, + debug=False, + background_tools_enabled=True, ) ``` -### FailSafeConfig +Use [Configuration Reference](/library/configuration-reference) for the complete set of runner fields and defaults. + +## FailSafeConfig ```python from afk.agents import FailSafeConfig fail_safe = FailSafeConfig( - max_steps: int = 20, # Max run loop iterations - max_llm_calls: int = 50, # Max LLM invocations - max_tool_calls: int = 200, # Max tool invocations - max_wall_time_s: float = 300.0, # Wall-clock timeout - max_total_cost_usd: float | None = None, # Cost ceiling - llm_failure_policy: str = "retry_then_fail", # LLM error strategy - tool_failure_policy: str = "continue_with_error", # Tool error strategy - subagent_failure_policy: str = "continue", # Subagent error strategy - fallback_model_chain: list[str] = [], # Fallback models + llm_failure_policy="retry_then_fail", + tool_failure_policy="continue_with_error", + subagent_failure_policy="continue", + approval_denial_policy="skip_action", + max_steps=20, + max_wall_time_s=300.0, + max_llm_calls=50, + max_tool_calls=200, + max_parallel_tools=16, + max_subagent_depth=3, + max_total_cost_usd=None, + fallback_model_chain=[], ) ``` - - **Always set `max_total_cost_usd`** in production. Without it, a runaway agent - loop can spend significant API credits in minutes. - +Set `max_total_cost_usd` for production agents. The default is intentionally unset because acceptable budgets vary by application. -### @tool decorator +## Tool decorator ```python +from pydantic import BaseModel + from afk.tools import tool -@tool( - *, - args_model: Type[BaseModel], # Required — Pydantic args model - name: str | None = None, # Tool name (default: function name) - description: str | None = None, # LLM-visible description - timeout: float | None = None, # Execution timeout in seconds - prehooks: list[PreHook] | None = None, # Argument transform hooks - posthooks: list[PostHook] | None = None, # Output transform hooks - middlewares: list[Middleware] | None = None, # Execution wrappers - raise_on_error: bool = False, # Raise vs return ToolResult -) + +class SearchArgs(BaseModel): + query: str + + +@tool(args_model=SearchArgs, name="search", description="Search by query.") +def search(args: SearchArgs) -> dict: + return {"query": args.query, "results": []} ``` ---- +Tool functions may be sync or async. They may accept `(args)`, `(args, ctx)`, or `(ctx, args)`. -## Core types - -### AgentResult - -| Field | Type | Description | -| --------------------- | ------------------------------- | ----------------------------------------------------------------------------- | -| `final_text` | `str` | The agent's final response | -| `state` | `str` | Terminal state: `completed`, `failed`, `degraded`, `cancelled`, `interrupted` | -| `run_id` | `str` | Unique run identifier | -| `thread_id` | `str` | Thread identifier for memory continuity | -| `tool_executions` | `list[ToolExecutionRecord]` | All tool calls with name, success, output, latency, and agent provenance | -| `subagent_executions` | `list[SubagentExecutionRecord]` | All subagent invocations | -| `usage_aggregate` | `UsageAggregate` | Token counts and cost estimates | -| `state_snapshot` | `dict[str, JSONValue]` | Final runtime counters/snapshot metadata | - -### AgentStreamEvent - -| Field | Type | Description | -| -------------- | -------------- | -------------------------------------------------------------------- | -| `type` | `str` | Event category (see [Streaming](/library/streaming#event-reference)) | -| `text_delta` | `str \| None` | Incremental text chunk | -| `tool_name` | `str \| None` | Tool name for tool events | -| `tool_call_id` | `str \| None` | Tool call identifier | -| `tool_success` | `bool \| None` | Whether tool succeeded | -| `tool_output` | `JSONValue \| None` | Tool output payload | -| `tool_error` | `str \| None` | Tool error message | -| `step` | `int \| None` | Current step index | -| `state` | `str \| None` | Current agent state | -| `result` | `AgentResult` | Terminal result (for `completed` events) | -| `error` | `str \| None` | Error message (for `error` events) | +## AgentResult ---- +```python +result = Runner().run_sync(agent, user_message="Hello") + +print(result.final_text) +print(result.state) +print(result.run_id) +print(result.thread_id) +print(result.tool_executions) +print(result.subagent_executions) +print(result.usage_aggregate.total_tokens) +print(result.total_cost_usd) +``` -## Agents module - - - - Core agent configuration — model, instructions, tools, subagents. - - - Step limits, cost budgets, timeout, failure policies. - - - Policy rules for gating tool calls and actions. - - - Individual policy rule definition. - - - -## Core module - - - - Agent execution engine — sync, async, stream. - - - Safety defaults, sanitization, interaction mode. - - - Run output — final_text, state, tool/subagent executions, usage. - - - -## Tools module - - - - Define typed tool functions with Pydantic models. - - - Pre/post hooks and middleware for tool execution. - - - -## LLM module - - - - Builder pattern for LLM client configuration. - - - Normalized contracts for LLM interaction. - - +Important fields: + +| Field | Meaning | +| --- | --- | +| `final_text` | Final assistant text | +| `state` | Terminal run state | +| `requested_model` | Model requested by the caller or agent | +| `normalized_model` | Effective normalized model when available | +| `provider_adapter` | Provider/adapter used when available | +| `tool_executions` | Ordered tool execution records | +| `subagent_executions` | Ordered subagent execution records | +| `usage_aggregate` | Aggregated input/output/total tokens | +| `total_cost_usd` | Estimated total cost when available | +| `state_snapshot` | Terminal runtime snapshot payload | ## API stability -AFK follows semantic versioning. Public exports from `afk.agents`, `afk.core`, `afk.tools`, `afk.llms`, and `afk.evals` are considered stable and will not change without a major version bump. - -Internal modules (prefixed with `_` or under `afk.core.runner._internal`) are not part of the public API and may change at any time. +- Public docs and examples should use imports from `afk.agents`, `afk.core`, `afk.tools`, `afk.llms`, `afk.memory`, `afk.queues`, `afk.mcp`, `afk.messaging`, `afk.observability`, and `afk.evals`. +- Internal modules under package subdirectories may change faster than public exports. +- When changing public exports, update this page, [Public API Rules](/library/public-imports-and-function-improvement), examples, and generated agent-facing docs. diff --git a/docs/library/architecture.mdx b/docs/library/architecture.mdx index b956d9b..cc43baa 100644 --- a/docs/library/architecture.mdx +++ b/docs/library/architecture.mdx @@ -20,7 +20,7 @@ flowchart TB LLM["LLM Runtime\n(afk.llms)"] Tools["Tool Registry\n(afk.tools)"] Memory["Memory Backend\n(afk.memory)"] - Telemetry["Telemetry Pipeline\n(afk.telemetry)"] + Telemetry["Observability Pipeline\n(afk.observability)"] end subgraph External["External Systems"] @@ -50,8 +50,8 @@ flowchart TB | `afk.llms` | LLM runtime, provider adapters, retry/circuit breaking | Provider SDKs | | `afk.tools` | Tool registry, execution, validation, hooks | `pydantic` | | `afk.memory` | State persistence, checkpoints, compaction | Backend drivers | -| `afk.telemetry` | Event pipeline, metrics, exporters | OTEL SDK (optional) | -| `afk.a2a` | Agent-to-agent protocol, auth, external adapters | `afk.core` | +| `afk.observability` | Event pipeline, metrics, exporters | OTEL SDK (optional) | +| `afk.messaging` | Public A2A protocol, auth, host, and delivery exports | `afk.agents` | | `afk.evals` | Eval runner, assertions, reporting | `afk.core` | diff --git a/docs/library/building-with-ai.mdx b/docs/library/building-with-ai.mdx index 74161cd..fd1a3de 100644 --- a/docs/library/building-with-ai.mdx +++ b/docs/library/building-with-ai.mdx @@ -32,7 +32,7 @@ flowchart LR ```python agent = Agent( name="classifier", - model="gpt-5.2-mini", + model="gpt-4.1-mini", instructions=""" Classify the support ticket into exactly one category: billing, technical, account, feature-request, other. @@ -58,7 +58,7 @@ flowchart LR agent = Agent( name="rag-agent", - model="gpt-5.2-mini", + model="gpt-4.1-mini", instructions=""" Always search the knowledge base before answering. Cite sources. If no relevant docs found, say so. @@ -84,7 +84,7 @@ flowchart LR agent = Agent( name="coder", - model="gpt-5.2", + model="gpt-4.1", instructions=""" Write code, then run the tests to verify. Fix any failures before presenting the final version. @@ -110,7 +110,7 @@ flowchart LR ```python coordinator = Agent( name="coordinator", - model="gpt-5.2-mini", + model="gpt-4.1-mini", instructions=""" Route each request to the appropriate specialist: - Technical questions → 'engineer' diff --git a/docs/library/checkpoint-schema.mdx b/docs/library/checkpoint-schema.mdx index 91acaef..24b18ec 100644 --- a/docs/library/checkpoint-schema.mdx +++ b/docs/library/checkpoint-schema.mdx @@ -78,7 +78,7 @@ flowchart TB from afk.agents import Agent from afk.core import Runner -agent = Agent(name="analyst", model="gpt-5.2-mini", instructions="Analyze data.") +agent = Agent(name="analyst", model="gpt-4.1-mini", instructions="Analyze data.") runner = Runner() # Start a run that might be interrupted diff --git a/docs/library/configuration-reference.mdx b/docs/library/configuration-reference.mdx index 2dff75c..a420b22 100644 --- a/docs/library/configuration-reference.mdx +++ b/docs/library/configuration-reference.mdx @@ -18,7 +18,7 @@ from afk.agents import Agent | `model` | `str \| LLM` | **required** | LLM model name or pre-built client instance | | `name` | `str` | class name | Agent identity for logs, telemetry, and subagent routing | | `instructions` | `str \| callable` | `None` | System prompt — static string or callable provider | -| `instruction_file` | `str \| Path` | `None` | Path to a prompt file (resolved under `prompts_dir`) | +| `instruction_file` | `str \| Path` | `None` | Path to a prompt file that must resolve under `prompts_dir` | | `prompts_dir` | `str \| Path` | `None` | Root directory for prompt files (env: `AFK_AGENT_PROMPTS_DIR`) | | `tools` | `list` | `[]` | Typed functions the agent can call | | `subagents` | `list[Agent]` | `[]` | Specialist agents this agent can delegate to | @@ -184,8 +184,8 @@ from afk.tools import SandboxProfile | -------------------------- | --------------- | --------- | ---------------------------------------------- | | `profile_id` | `str` | `default` | Profile identifier for logs and policy | | `allow_network` | `bool` | `False` | Whether tools can make network requests | -| `allow_command_execution` | `bool` | `True` | Whether tools can execute shell commands | -| `allowed_command_prefixes` | `list[str]` | `[]` | Allowed command prefixes (empty = all allowed) | +| `allow_command_execution` | `bool` | `False` | Whether tools can execute shell commands | +| `allowed_command_prefixes` | `list[str]` | `[]` | Allowed command prefixes (empty = none) | | `deny_shell_operators` | `bool` | `True` | Block pipes, redirects, semicolons | | `allowed_paths` | `list[str]` | `[]` | Restrict file access to these paths | | `denied_paths` | `list[str]` | `[]` | Explicitly deny access to these paths | diff --git a/docs/library/core-runner.mdx b/docs/library/core-runner.mdx index 1f19496..da4f807 100644 --- a/docs/library/core-runner.mdx +++ b/docs/library/core-runner.mdx @@ -11,7 +11,7 @@ The **Runner** is the execution engine that runs agents. It manages the step loo from afk.agents import Agent from afk.core import Runner -agent = Agent(name="demo", model="gpt-5.2-mini", instructions="Be helpful.") +agent = Agent(name="demo", model="gpt-4.1-mini", instructions="Be helpful.") runner = Runner() # Synchronous (simplest) diff --git a/docs/library/deployment.mdx b/docs/library/deployment.mdx new file mode 100644 index 0000000..381f5b3 --- /dev/null +++ b/docs/library/deployment.mdx @@ -0,0 +1,381 @@ +--- +title: Deployment +description: Deploy AFK agents to production with Docker, Kubernetes, and scaling patterns. +--- + +This guide covers deploying AFK agents to production environments, from single-container setups to distributed, multi-worker deployments. + +## Docker deployment + +### Basic Dockerfile + +```dockerfile +FROM python:3.13-slim + +WORKDIR /app + +# Install dependencies +COPY requirements.txt . +RUN pip install --no-cache-dir -r requirements.txt + +# Copy application code +COPY . . + +# Run your application entrypoint that creates AFK agents/runners +CMD ["python", "-m", "your_app.server"] +``` + +### Production Dockerfile with multi-stage build + +```dockerfile +FROM python:3.13-slim AS builder + +WORKDIR /app +RUN pip install --upgrade pip +COPY requirements.txt . +RUN pip install --prefix=/install -r requirements.txt + +# Production image +FROM python:3.13-slim + +WORKDIR /app +COPY --from=builder /install /usr/local +COPY . . + +# Run as non-root user +RUN useradd -m appuser && chown -R appuser:appuser /app +USER appuser + +CMD ["python", "-m", "your_app.server"] +``` + +### docker-compose.yml + +```yaml +version: '3.8' + +services: + agent: + build: . + ports: + - "8000:8000" + environment: + - OPENAI_API_KEY=${OPENAI_API_KEY} + - AFK_MEMORY_BACKEND=redis + - AFK_REDIS_URL=redis://redis:6379 + - AFK_QUEUE_BACKEND=redis + - AFK_QUEUE_REDIS_URL=redis://redis:6379 + depends_on: + - redis + restart: unless-stopped + healthcheck: + test: ["CMD", "curl", "-f", "http://localhost:8000/health"] + interval: 30s + timeout: 10s + retries: 3 + + redis: + image: redis:7-alpine + volumes: + - redis_data:/data + restart: unless-stopped + +volumes: + redis_data: +``` + +## Environment configuration + +### Required environment variables + +```bash +# LLM Provider +OPENAI_API_KEY=sk-... # Required for OpenAI +# or +ANTHROPIC_API_KEY=sk-ant-... # For Anthropic + +# Memory backend +AFK_MEMORY_BACKEND=postgres # Options: memory, sqlite, redis, postgres +AFK_SQLITE_PATH=./data/memory.sqlite3 +AFK_REDIS_URL=redis://localhost:6379 +AFK_PG_DSN=postgresql://user:pass@host/db +AFK_VECTOR_DIM=1536 # Required for Postgres vector search + +# Queue backend +AFK_QUEUE_BACKEND=redis +AFK_QUEUE_REDIS_URL=redis://localhost:6379 + +# Observability +AFK_TELEMETRY=otel # Options: console, json, otel, none +OTEL_EXPORTER_OTLP_ENDPOINT=http://telemetry:4317 + +# Server mode +AFK_SERVER_PORT=8000 +AFK_SERVER_WORKERS=4 +``` + +### Production configuration file + +Create `config/production.yaml`: + +```yaml +agent: + default_model: gpt-4.1-mini + default_fail_safe: + max_steps: 20 + max_tool_calls: 10 + max_total_cost_usd: 1.00 + max_wall_time_s: 120 + +llm: + provider: openai + profile: production + +memory: + backend: postgres + postgres_dsn: ${AFK_PG_DSN} + +queue: + backend: redis + redis_url: ${AFK_QUEUE_REDIS_URL} + max_concurrency: 10 + +telemetry: + exporter: otel + service_name: afk-agent + export_interval_ms: 5000 +``` + +## Scaling patterns + +### Horizontal scaling with workers + +```python +from afk.queues import RUNNER_CHAT_CONTRACT, InMemoryTaskQueue, TaskWorker +from afk.core import Runner + +queue = InMemoryTaskQueue() +worker = TaskWorker( + queue=queue, + agents={"analyzer": agent}, + runner_factory=lambda: Runner(), + execution_contracts=[RUNNER_CHAT_CONTRACT], +) + +await worker.start() +``` + +### Kubernetes deployment + +```yaml +apiVersion: apps/v1 +kind: Deployment +metadata: + name: afk-agent +spec: + replicas: 3 + selector: + matchLabels: + app: afk-agent + template: + metadata: + labels: + app: afk-agent + spec: + containers: + - name: agent + image: your-registry/afk-agent:latest + ports: + - containerPort: 8000 + env: + - name: OPENAI_API_KEY + valueFrom: + secretKeyRef: + name: llm-secrets + key: api-key + - name: AFK_MEMORY_BACKEND + value: "redis" + - name: AFK_REDIS_URL + value: "redis://redis:6379" + resources: + requests: + memory: "256Mi" + cpu: "250m" + limits: + memory: "512Mi" + cpu: "500m" + livenessProbe: + httpGet: + path: /health + port: 8000 + initialDelaySeconds: 10 + periodSeconds: 30 + readinessProbe: + httpGet: + path: /ready + port: 8000 + initialDelaySeconds: 5 + periodSeconds: 10 +``` + +### Kubernetes HPA for auto-scaling + +```yaml +apiVersion: autoscaling/v2 +kind: HorizontalPodAutoscaler +metadata: + name: afk-agent-hpa +spec: + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: afk-agent + minReplicas: 2 + maxReplicas: 10 + metrics: + - type: Resource + resource: + name: cpu + target: + type: Utilization + averageUtilization: 70 + - type: Pods + pods: + metric: + name: queue_depth + target: + type: AverageValue + averageValue: "10" +``` + +## Health checks + +Implement health endpoints in your server: + +```python +from afk.core import Runner +from afk.memory import InMemoryMemoryStore + +app = FastAPI() + +runner = Runner() +memory_store = InMemoryMemoryStore() + +@app.get("/health") +async def health(): + return {"status": "healthy"} + +@app.get("/ready") +async def ready(): + try: + await memory_store.health_check() + return {"status": "ready", "memory": "ok"} + except Exception as e: + raise HTTPException(status_code=503, detail=str(e)) +``` + +## Database schema + +### SQLite (development) + +SQLite requires no schema setup — tables are created automatically on first use. + +### PostgreSQL + +```sql +-- Run these for production PostgreSQL deployments +CREATE EXTENSION IF NOT EXISTS vector; + +CREATE TABLE afk_events ( + id TEXT PRIMARY KEY, + thread_id TEXT NOT NULL, + run_id TEXT NOT NULL, + event_type TEXT NOT NULL, + role TEXT, + content TEXT, + metadata JSONB, + created_at TIMESTAMPTZ NOT NULL, + INDEX idx_thread_id (thread_id), + INDEX idx_run_id (run_id), + INDEX idx_created_at (created_at) +); + +CREATE TABLE afk_checkpoints ( + id TEXT PRIMARY KEY, + run_id TEXT NOT NULL, + thread_id TEXT NOT NULL, + step INTEGER NOT NULL, + state JSONB NOT NULL, + created_at TIMESTAMPTZ NOT NULL, + UNIQUE(run_id, step) +); + +CREATE TABLE afk_long_term_memory ( + id TEXT PRIMARY KEY, + user_id TEXT, + scope TEXT, + data JSONB, + text TEXT, + embedding VECTOR(1536), + tags TEXT[], + metadata JSONB, + created_at TIMESTAMPTZ NOT NULL, + updated_at TIMESTAMPTZ NOT NULL +); + +-- Vector similarity search +CREATE INDEX ON afk_long_term_memory + USING ivfflat (embedding vector_cosine_ops) + WITH (lists = 100); +``` + +## Security checklist + + + + Store API keys in secrets managers (AWS Secrets Manager, HashiCorp Vault, Kubernetes Secrets). Never commit keys to version control. + + + Restrict traffic between services. Agents should only reach LLM providers and necessary databases. + + + Configure rate limits on public endpoints to prevent abuse. + + + Always set `max_total_cost_usd` in `FailSafeConfig` for production agents. + + + Enable telemetry export to your logging infrastructure for compliance. + + + +## Monitoring + +Key metrics to track: + +| Metric | What it indicates | Alert threshold | +|--------|------------------|-----------------| +| `agent.run.duration` | How long runs take | > 60s p95 | +| `agent.run.cost` | Token spend per run | > $0.50 per run | +| `agent.run.failures` | Failed runs | > 5% error rate | +| `llm.latency` | LLM response time | > 10s p95 | +| `llm.errors` | LLM API errors | > 1% error rate | +| `queue.depth` | Pending tasks | > 100 items | +| `queue.dead_letters` | Failed tasks | > 0 | + +## Next steps + + + + Set up telemetry and alerting for production monitoring. + + + Security hardening checklist and best practices. + + + CI-gated quality checks for agent releases. + + + Production patterns and anti-patterns. + + diff --git a/docs/library/developer-guide.mdx b/docs/library/developer-guide.mdx index 03b5a16..f852a7d 100644 --- a/docs/library/developer-guide.mdx +++ b/docs/library/developer-guide.mdx @@ -1,197 +1,130 @@ --- title: Developer Guide -description: Contribute to AFK — conventions, patterns, and quality tools. +description: Maintainer workflow for changing AFK itself. --- -This guide is for developers contributing to the AFK framework itself. It covers the implementation workflow, code conventions, testing expectations, and quality tools. - - - **Building an app with AFK?** See [Building with - AI](/library/building-with-ai) instead. This page is for AFK framework - contributors. - - -## Implementation workflow - - - - Start with the Pydantic model or Python protocol that defines the interface. This is the most important step — everything else flows from the contract. - - ```python - from pydantic import BaseModel - - class ToolExecutionRecord(BaseModel): - tool_name: str - success: bool - output: str | None - latency_ms: float - error: str | None = None - ``` - - - - Write the implementation that fulfills the contract. Keep the implementation focused — one module, one responsibility. - - ```python - async def execute_tool(call: ToolCall) -> ToolExecutionRecord: - start = time.time() - try: - result = await call.handler(call.validated_args) - return ToolExecutionRecord( - tool_name=call.name, - success=True, - output=str(result), - latency_ms=(time.time() - start) * 1000, - ) - except Exception as e: - return ToolExecutionRecord( - tool_name=call.name, - success=False, - output=None, - latency_ms=(time.time() - start) * 1000, - error=str(e), - ) - ``` - - - - Test the **failure paths** first — they're more important than the happy path. Every error should be classified (retryable, terminal, non-fatal). - - ```python - def test_tool_timeout_produces_record(): - """Timeout should produce a record with success=False, not crash.""" - record = await execute_tool(slow_tool_call) - assert record.success is False - assert "timeout" in record.error.lower() - - def test_invalid_args_returns_validation_error(): - """Bad arguments should return a clear error, not raise.""" - record = await execute_tool(bad_args_call) - assert record.success is False - assert "validation" in record.error.lower() - ``` - - - - Add docstrings to public functions and classes. Include parameter descriptions and examples. - - - -## Project structure +This page is for contributors changing the AFK framework. If you are building an application with AFK, start with [Quickstart](/library/quickstart) or [Building with AI](/library/building-with-ai). +## Local setup + +AFK targets Python 3.13+. + +```bash +python -m pip install --upgrade pip +python -m pip install -e . pytest +``` + +Common commands: + +```bash +PYTHONPATH=src pytest -q +PYTHONPATH=src pytest -q tests/agents/test_agent_runtime.py +PYTHONPATH=src pytest -q tests/llms/test_llm_settings.py +PYTHONPATH=src pytest -q tests/queues/test_queue_factory.py +ruff check src tests +ruff format src tests +``` + +Preview docs: + +```bash +./scripts/docs_dev.sh ``` -src/afk/ -├── agents/ # Agent definition, FailSafeConfig, PolicyEngine -├── core/ # Runner, step loop, state management -│ └── runner/ # Runner implementation -├── llms/ # LLM runtime, provider adapters -├── tools/ # Tool registry, execution, hooks -├── memory/ # State persistence, checkpoints -├── telemetry/ # Event pipeline, exporters -├── a2a/ # Agent-to-agent protocol -├── evals/ # Eval runner, assertions -└── skills/ # Agent skills system + +Regenerate agent-facing docs and skill indexes: + +```bash +./scripts/build_agentic_ai_assets.sh +``` + +Install the repository skills with Vercel's Skills CLI: + +```bash +npx skills add https://github.com/arpan404/afk --skill afk-coder +npx skills add https://github.com/arpan404/afk --skill afk-maintainer ``` -## Code conventions - -| Convention | Rule | -| ------------------ | ------------------------------------------------------------------------------------------- | -| **Contracts** | All public interfaces are Pydantic models or Python protocols | -| **Imports** | No cross-adapter imports (`afk.llms` ↛ `afk.tools`). Only `afk.core` wires modules. | -| **Error handling** | Classify errors: retryable, terminal, or non-fatal. Never raise unclassified exceptions. | -| **Async** | Public APIs support both sync and async. Use `run_sync()` as a sync wrapper around `run()`. | -| **Naming** | Snake_case for functions/variables, PascalCase for classes, UPPER_CASE for constants | -| **Type hints** | Required on all public functions and parameters | - -## Common mistakes - - - - **Wrong:** Runner code that contains OpenAI-specific types. - - ```python - # [BAD] Bad: OpenAI types in the runner - from openai.types.chat import ChatCompletion - result = await openai_client.chat.completions.create(...) - ``` - - **Right:** Runner sends `LLMRequest`, receives `LLMResponse`. - - ```python - # [OK] Good: Provider-agnostic contracts - response: LLMResponse = await llm_client.generate(request) - ``` - - - - **Wrong:** All errors treated the same way. - - ```python - # [BAD] Bad: Generic exception, no classification - except Exception as e: - raise RuntimeError(f"Tool failed: {e}") - ``` - - **Right:** Errors classified for the runner to handle appropriately. - - ```python - # [OK] Good: Classified failure - except httpx.TimeoutException: - return ToolResult(success=False, error_type="retryable", error="Timeout") - except ValueError as e: - return ToolResult(success=False, error_type="terminal", error=str(e)) - ``` - - - - **Wrong:** Passing raw dicts between modules. - - ```python - # [BAD] Bad: Unvalidated dict - return {"tool_name": name, "result": output} - ``` - - **Right:** Using the Pydantic model. - - ```python - # [OK] Good: Validated contract - return ToolExecutionRecord(tool_name=name, success=True, output=output) - ``` - - - - -## Quality tools - -| Tool | Command | Purpose | -| -------------------- | ----------------------------- | ------------------------------------------ | -| **Ruff** | `ruff check src/ tests/` | Linting (replaces flake8, isort, pyflakes) | -| **Ruff format** | `ruff format src/ tests/` | Code formatting (replaces black) | -| **Pytest** | `pytest tests/` | Run test suite | -| **Pytest (verbose)** | `pytest tests/ -v --tb=short` | Run with verbose output | -| **Type check** | `pyright src/` | Static type checking | - -## Testing expectations - -- **Unit tests** for every public function and class -- **Failure tests** for every error path (timeout, validation, policy denial) -- **Integration tests** for module boundaries (runner ↔ LLM, runner ↔ tools) -- **Eval tests** for agent behavior (prompt produces expected output) - - - **Test failure semantics first.** The happy path usually works. The failure - paths are where bugs hide. For every new feature, write at least 2 failure - tests for every 1 success test. - +Use `afk-coder` when building with AFK. Use `afk-maintainer` when reviewing or changing AFK itself. + +## Repository boundaries + +| Package | Responsibility | +| --- | --- | +| `afk.agents` | Agent definitions, policy, prompts, skills, lifecycle, workflow, A2A | +| `afk.core` | Runner, interaction providers, streaming handles, telemetry contracts | +| `afk.llms` | Provider-portable LLM runtime, adapters, retry/timeout/cache/routing policies | +| `afk.tools` | Typed tools, decorators, registry, sandboxing, output limiting | +| `afk.memory` | Memory stores, checkpoints, retention, compaction, vector helpers | +| `afk.queues` | Async task queues, execution contracts, workers, retry/DLQ behavior | +| `afk.observability` | Telemetry collectors, projectors, exporters | +| `afk.mcp`, `afk.messaging`, `afk.debugger`, `afk.evals` | Optional integration and quality layers | + +Keep the Agent/Runner/Runtime boundary intact: + +- agents are configuration; +- runners execute; +- tools, memory, queues, LLM adapters, and telemetry provide runtime capabilities. + +## Change workflow + +1. Identify the public contract affected by the change. +2. Inspect the existing module and tests before editing. +3. Make the smallest behavior change that preserves package boundaries. +4. Add or update focused tests for success and failure paths. +5. Update docs when behavior, public imports, configuration, env vars, or examples change. +6. Regenerate agent-facing docs when docs navigation, snippets, or skill metadata change. + +## Documentation workflow + +User-facing docs must: + +- import from public package surfaces such as `afk.agents` and `afk.core`; +- explain behavior before internals; +- use Python 3.13+ guidance; +- distinguish `afk-py` distribution install from `afk` imports; +- include new pages in `docs/docs.json`; +- keep examples aligned with current `AgentResult`, `RunnerConfig`, and `FailSafeConfig` fields. + +Maintainer docs may reference internal files, but should state which public contract or invariant is being protected. + +## High-risk areas + +Use targeted tests when touching: + +| Area | Risk | +| --- | --- | +| `src/afk/core/runner/` | execution loop, checkpoints, resume, policy/failure routing | +| `src/afk/core/streaming.py` | stream events and handle lifecycle | +| `src/afk/tools/core/base.py`, `src/afk/tools/registry.py` | tool invocation semantics | +| `src/afk/tools/security.py` | sandbox, secret scope, output limiting | +| `src/afk/llms/runtime/` | retries, circuit breakers, rate limits, caching, routing | +| `src/afk/memory/` | persistence, checkpoint keys, compaction, vector search | +| `src/afk/queues/` | execution contracts, retry/DLQ, worker lifecycle | +| `src/afk/agents/a2a/` | auth, delivery guarantees, protocol compatibility | + +## Public API checklist + +Before changing exports or constructor fields: + +- update package-level `__all__`; +- update [API Reference](/library/api-reference); +- update [Configuration Reference](/library/configuration-reference) if defaults changed; +- update snippets that use the changed field; +- add public-import tests where useful. ## Next steps - Module boundaries and design principles. + Package boundaries and runtime flow. + + + Maintainer rules for stable imports and docs examples. + + + Behaviors that tests protect. - - Behavioral testing framework. + + Generated source-level symbol map. diff --git a/docs/library/environment-variables.mdx b/docs/library/environment-variables.mdx index 2e57615..1d39895 100644 --- a/docs/library/environment-variables.mdx +++ b/docs/library/environment-variables.mdx @@ -16,7 +16,8 @@ These variables configure the default LLM provider and behavior. They are read b | Variable | Default | Description | | ------------------------------- | -------------- | ------------------------------------------------------------ | | `AFK_LLM_PROVIDER` | `litellm` | Default provider id (`openai`, `litellm`, `anthropic_agent`) | -| `AFK_LLM_MODEL` | `gpt-5.2-mini` | Default model name | +| `AFK_LLM_PROVIDER_ORDER` | _(none)_ | Comma-separated provider preference order for routing helpers | +| `AFK_LLM_MODEL` | `gpt-4.1-mini` | Default model name | | `AFK_EMBED_MODEL` | _(none)_ | Embedding model for vector operations | | `AFK_LLM_API_BASE_URL` | _(none)_ | Provider API base URL | | `AFK_LLM_API_KEY` | _(none)_ | Provider API key | @@ -41,10 +42,24 @@ Configure the persistent memory backend for thread-based conversations. | Variable | Default | Description | | -------------------- | -------------------- | ------------------------------------------------------- | -| `AFK_MEMORY_BACKEND` | `sqlite` | Backend type: `inmemory`, `sqlite`, `redis`, `postgres` | +| `AFK_MEMORY_BACKEND` | `memory` | Backend type: `inmemory`/`memory`, `sqlite`, `redis`, `postgres` | | `AFK_SQLITE_PATH` | `afk_memory.sqlite3` | SQLite file path | -| `AFK_REDIS_URL` | _(none)_ | Redis connection URL (e.g. `redis://localhost:6379`) | +| `AFK_REDIS_URL` | _(none)_ | Redis connection URL (for example `redis://localhost:6379`) | +| `AFK_REDIS_HOST` | `localhost` | Redis host when URL is not set | +| `AFK_REDIS_PORT` | `6379` | Redis port when URL is not set | +| `AFK_REDIS_DB` | `0` | Redis DB when URL is not set | +| `AFK_REDIS_PASSWORD` | _(none)_ | Redis password when URL is not set | +| `AFK_REDIS_EVENTS_MAX` | `2000` | Max Redis memory events per thread | | `AFK_PG_DSN` | _(none)_ | PostgreSQL connection string | +| `AFK_PG_HOST` | `localhost` | PostgreSQL host when DSN is not set | +| `AFK_PG_PORT` | `5432` | PostgreSQL port when DSN is not set | +| `AFK_PG_USER` | `postgres` | PostgreSQL user when DSN is not set | +| `AFK_PG_PASSWORD` | _(none)_ | PostgreSQL password when DSN is not set | +| `AFK_PG_DB` | `afk` | PostgreSQL database when DSN is not set | +| `AFK_PG_SSL` | `false` | Enable PostgreSQL SSL | +| `AFK_PG_POOL_MIN` | `1` | PostgreSQL pool minimum size | +| `AFK_PG_POOL_MAX` | `10` | PostgreSQL pool maximum size | +| `AFK_VECTOR_DIM` | _(required for Postgres)_ | Vector dimension for Postgres memory search | ```python # Override in code — takes precedence over env vars @@ -60,6 +75,12 @@ Configure the task queue backend for async agent execution. | Variable | Default | Description | | ---------------------------------- | ---------- | --------------------------------- | | `AFK_QUEUE_BACKEND` | `inmemory` | Backend type: `inmemory`, `redis` | +| `AFK_QUEUE_REDIS_URL` | falls back to `AFK_REDIS_URL` | Redis URL for queue backend | +| `AFK_QUEUE_REDIS_HOST` | falls back to `AFK_REDIS_HOST`, then `localhost` | Redis queue host | +| `AFK_QUEUE_REDIS_PORT` | falls back to `AFK_REDIS_PORT`, then `6379` | Redis queue port | +| `AFK_QUEUE_REDIS_DB` | falls back to `AFK_REDIS_DB`, then `0` | Redis queue DB | +| `AFK_QUEUE_REDIS_PASSWORD` | falls back to `AFK_REDIS_PASSWORD` | Redis queue password | +| `AFK_QUEUE_REDIS_PREFIX` | `afk:queue` | Redis key prefix | | `AFK_QUEUE_RETRY_BACKOFF_BASE_S` | `0.5` | Retry base delay in seconds | | `AFK_QUEUE_RETRY_BACKOFF_MAX_S` | `30` | Retry max delay in seconds | | `AFK_QUEUE_RETRY_BACKOFF_JITTER_S` | `0.2` | Random jitter added to backoff | @@ -77,6 +98,33 @@ Configure the task queue backend for async agent execution. Agents resolve `instruction_file` paths relative to this directory. See [System Prompts](/library/system-prompts) for details. +## Runner and tool command defaults + +| Variable | Default | Description | +| --- | --- | --- | +| `AFK_ALLOWED_COMMANDS` | _(none)_ | Comma-separated default allowlist for runtime command tools | + +Runner constructors and `RunnerConfig` fields remain the preferred way to set command policy. Use this environment variable only for process-wide defaults. + +## MCP server + +These variables are read by the MCP server configuration helpers in `afk.config`. + +| Variable | Default | Description | +| --- | --- | --- | +| `AFK_CORS_ORIGINS` | _(none)_ | Comma-separated CORS origins | +| `AFK_MCP_NAME` | `afk-mcp-server` | Server name | +| `AFK_MCP_VERSION` | `1.0.0` | Server version string | +| `AFK_MCP_HOST` | `127.0.0.1` | Bind host | +| `AFK_MCP_PORT` | `8000` | Bind port | +| `AFK_MCP_INSTRUCTIONS` | _(none)_ | Optional server instructions | +| `AFK_MCP_PATH` | `/mcp` | HTTP MCP endpoint path | +| `AFK_MCP_SSE_PATH` | `/mcp/sse` | SSE endpoint path | +| `AFK_MCP_HEALTH_PATH` | `/health` | Health endpoint path | +| `AFK_MCP_ENABLE_SSE` | `true` | Enable SSE endpoint | +| `AFK_MCP_ENABLE_HEALTH` | `true` | Enable health endpoint | +| `AFK_MCP_ALLOW_BATCH` | `true` | Allow batched MCP requests | + ## A2A No default environment variables are required. Configure A2A host and authentication in code for an explicit security posture. See [A2A](/library/a2a) for details. diff --git a/docs/library/evals.mdx b/docs/library/evals.mdx index 43e7b28..2bb9f76 100644 --- a/docs/library/evals.mdx +++ b/docs/library/evals.mdx @@ -3,19 +3,18 @@ title: Evals description: Behavioral testing for agent quality — assertions, budgets, and CI integration. --- -AFK's eval framework tests agent behavior with real LLM calls. Define test cases, run them against your agents, and gate releases on pass rate. Think of evals as integration tests for AI — they verify that prompts, tools, and orchestration produce correct behavior. +AFK evals run agents against named inputs and check the resulting state, text, tool usage, budgets, and telemetry. Use them for prompt changes, tool changes, routing changes, and regression tests. They can run against real providers, test adapters, or agents configured with deterministic tools. ## Your first eval ```python from afk.agents import Agent from afk.core import Runner -from afk.evals import run_suite -from afk.evals.models import EvalCase, EvalBudget +from afk.evals import EvalCase, EvalSuiteConfig, StateCompletedAssertion, run_suite agent = Agent( name="classifier", - model="gpt-5.2-mini", + model="gpt-4.1-mini", instructions="Classify as: billing, technical, account, other. Output only the label.", ) @@ -26,15 +25,16 @@ suite = run_suite( name="billing-question", agent=agent, user_message="Why was I charged twice?", - assertions=[lambda r: r.final_text.strip().lower() == "billing"], ), EvalCase( name="technical-question", agent=agent, user_message="The API returns a 500 error", - assertions=[lambda r: r.final_text.strip().lower() == "technical"], ), ], + config=EvalSuiteConfig( + assertions=(StateCompletedAssertion(),), + ), ) print(f"Passed: {suite.passed}/{suite.total}") @@ -54,14 +54,14 @@ flowchart LR - Each case specifies an agent, input message, and assertions to verify. + Each case specifies an agent and input message. Suite-level assertions and budgets verify the result. The scheduler runs cases sequentially or in parallel, respecting concurrency limits. - Each case runs through a full agent loop with real LLM calls. + Each case runs through the same runner path your application uses. Assertions verify the result — text content, state, tool usage, cost, @@ -87,10 +87,6 @@ flowchart LR name="basic-greeting", agent=agent, user_message="Hello!", - assertions=[ - lambda r: r.state == "completed", - lambda r: len(r.final_text) > 0, - ], ) ``` @@ -103,9 +99,6 @@ flowchart LR name="invalid-input", agent=agent, user_message="", - assertions=[ - lambda r: r.state in ("completed", "degraded"), - ], ) ``` @@ -118,10 +111,6 @@ flowchart LR name="uses-search", agent=agent, user_message="Find docs about caching", - assertions=[ - lambda r: any(t.tool_name == "search_docs" for t in r.tool_executions), - lambda r: r.state == "completed", - ], ) ``` @@ -134,10 +123,7 @@ flowchart LR name="within-budget", agent=agent, user_message="Analyze this dataset", - budget=EvalBudget(max_total_cost_usd=0.05, max_steps=5), - assertions=[ - lambda r: r.usage.total_cost_usd <= 0.05, - ], + budget=EvalBudget(max_total_cost_usd=0.05, max_total_tokens=4_000), ) ``` @@ -146,35 +132,26 @@ flowchart LR ## Assertions -Assertions are functions that take an `AgentResult` and return `True` (pass) or `False` (fail): +Assertions are suite-level callables. Import the built-ins from `afk.evals`, or implement the `EvalAssertion` protocol. ```python -# Text content assertions -lambda r: "error budget" in r.final_text.lower() -lambda r: len(r.final_text) < 500 - -# State assertions -lambda r: r.state == "completed" -lambda r: r.state != "failed" +from afk.evals import FinalTextContainsAssertion, StateCompletedAssertion -# Tool usage assertions -lambda r: len(r.tool_executions) > 0 -lambda r: all(t.success for t in r.tool_executions) - -# Cost assertions -lambda r: r.usage.total_cost_usd < 0.10 +assertions = ( + StateCompletedAssertion(), + FinalTextContainsAssertion("error budget"), +) ``` ## Suite configuration ```python -from afk.evals.models import EvalSuiteConfig +from afk.evals import EvalBudget, EvalSuiteConfig config = EvalSuiteConfig( max_concurrency=3, # Run up to 3 cases in parallel fail_fast=True, # Stop on first failure - timeout_per_case_s=60.0, # Max time per case - total_budget=EvalBudget( + budget=EvalBudget( max_total_cost_usd=1.00, # Total suite budget ), ) @@ -200,7 +177,7 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - - run: pip install afk + - run: python -m pip install afk-py pytest - run: python -m pytest tests/evals/ -v env: OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} @@ -218,12 +195,13 @@ Gate releases on eval pass rate: ```python suite = run_suite(runner_factory=lambda: Runner(), cases=cases) +pass_rate = suite.passed / suite.total if suite.total else 0.0 -if suite.pass_rate < 0.95: - print(f"Release blocked: {suite.pass_rate:.0%} pass rate (need 95%)") +if pass_rate < 0.95: + print(f"Release blocked: {pass_rate:.0%} pass rate (need 95%)") sys.exit(1) -print(f"Release approved: {suite.pass_rate:.0%} pass rate") +print(f"Release approved: {pass_rate:.0%} pass rate") ``` ## Next steps diff --git a/docs/library/examples/index.mdx b/docs/library/examples/index.mdx index d705464..1bd3057 100644 --- a/docs/library/examples/index.mdx +++ b/docs/library/examples/index.mdx @@ -33,6 +33,7 @@ The examples are ordered by complexity. If you are new to AFK, start at the top | Intermediate | Cost Monitoring | Budget limits, real-time cost tracking, batch budgets | | Advanced | MCP Client | Connect to external MCP servers, hybrid local+remote tools | | Advanced | Multi-Model Fallback | Fallback chains, circuit breakers, model tier strategies | +| Advanced | Production Client | Timeout middleware, Redis connection pooling, graceful shutdown | ## Scenario catalog @@ -88,7 +89,7 @@ The examples are ordered by complexity. If you are new to AFK, start at the top Attach pre-execution and post-execution hooks to tools. Chain middleware for logging, validation, and transformation. @@ -141,4 +142,12 @@ The examples are ordered by complexity. If you are new to AFK, start at the top Configure fallback model chains for LLM resilience, circuit breakers, and cost optimization. + + Production-ready LLM client with timeout middleware, Redis connection pooling, + and graceful shutdown handling. + diff --git a/docs/library/failure-policy-matrix.mdx b/docs/library/failure-policy-matrix.mdx index c86e693..dcae997 100644 --- a/docs/library/failure-policy-matrix.mdx +++ b/docs/library/failure-policy-matrix.mdx @@ -65,7 +65,7 @@ agent = Agent( ..., fail_safe=FailSafeConfig( llm_failure_policy="degrade", # "fail" or "degrade" - fallback_model_chain=["gpt-5.2-mini"], # Try cheaper model + fallback_model_chain=["gpt-4.1-mini"], # Try cheaper model ), ) ``` diff --git a/docs/library/full-module-reference.mdx b/docs/library/full-module-reference.mdx index 98bd364..fd96915 100644 --- a/docs/library/full-module-reference.mdx +++ b/docs/library/full-module-reference.mdx @@ -3,24 +3,24 @@ title: Full Module Reference description: Module inventory and responsibilities. --- -The Agent Forge Kit (AFK) Python SDK is organized into a set of top-level packages, each owning a distinct responsibility. This page provides a comprehensive inventory of every package, its sub-modules, key classes and functions, and example imports. +The Agent Forge Kit (AFK) Python SDK is organized into top-level packages, each owning a distinct responsibility. This page summarizes the package map, key public imports, and extension boundaries. -Use this reference when you need to find where a comprehensive inventory of every package, its sub-modules, key classes and functions, and example imports. +Use this reference when you need to find which package owns a capability. For field-level details, read the focused reference pages linked from the sidebar. ## Module dependencies -The AFK architecture is layered. Dependencies flow strictly downwards: +The AFK architecture is organized around a few stable boundaries: -- **`afk.agents`** (High-level definitions) -> depends on `afk.core`, `afk.tools`, `afk.llms` -- **`afk.core`** (Execution engine) -> depends on `afk.model`, `afk.memory`, `afk.observability` -- **`afk.llms`** (Provider I/O) -> independent -- **`afk.tools`** (Capabilities) -> independent +- **`afk.agents`** owns declarative agent configuration, policy, prompts, skills, delegation metadata, and A2A contracts. +- **`afk.core`** owns execution: runner lifecycle, streaming, interaction providers, delegation scheduling, checkpointing, memory wiring, and telemetry wiring. +- **`afk.llms`** owns model provider adapters, runtime policies, structured output helpers, routing, caching, and LLM request/response types. +- **`afk.tools`** owns tool definitions, registries, hooks, middleware, sandbox profiles, and prebuilt runtime tools. ### Extension points The framework is designed to be extended at specific protocol boundaries. **Do not subclass internal classes**. Instead, implement these protocols: -- **`InteractionProvider`** (`afk.core`): Build custom human-in-the-loop interaces (e.g. Slack, Discord, Web). +- **`InteractionProvider`** (`afk.core`): Build custom human-in-the-loop interfaces (for example Slack, Discord, or web approval UIs). - **`MemoryStore`** (`afk.memory`): Add support for new databases (Mongo, DynamoDB). - **`LLMProvider`** (`afk.llms`): Integrate new model providers (local inference, exotic APIs). - **`TelemetrySink`** (`afk.observability`): Export metrics/traces to custom backends (Datadog, Honeycomb). @@ -115,7 +115,7 @@ Tool definition, registry, hooks, middleware, security, and prebuilt runtime too | `afk.tools.core` | `tool` decorator, `ToolSpec`, `ToolContext`, `ToolResult`, `ToolRegistry`. | | `afk.tools.security` | `SandboxProfile`, `SandboxProfileProvider`, `SecretScopeProvider`, argument validation. | | `afk.tools.prebuilts` | Built-in runtime tools (file read, directory list, command execution, skill tools). | -| `afk.tools.hooks` | Pre/post execution hook system for tool middleware. | +| `afk.tools.registry` | Shared registry and registry middleware helpers. | **Key imports:** @@ -136,7 +136,7 @@ Pluggable memory stores, compaction, retention policies, long-term memory, vecto | `afk.memory.types` | `MemoryEvent`, `LongTermMemory`, `JsonValue`, retention policy models. | | `afk.memory.lifecycle` | `apply_event_retention()`, `apply_state_retention()`, `compact_thread_memory()`, `MemoryCompactionResult`. | | `afk.memory.vector` | `cosine_similarity()`, vector scoring utilities for embedding-based search. | -| `afk.memory.factory` | `create_memory_store()` factory for environment-based backend selection. | +| `afk.memory.factory` | `create_memory_store_from_env()` factory for environment-based backend selection. | | `afk.memory.adapters` | Backend implementations: `InMemoryMemoryStore`, `SQLiteMemoryStore`, `PostgresMemoryStore`, `RedisMemoryStore`. | **Key imports:** @@ -167,7 +167,7 @@ Task queue abstraction, worker lifecycle, failure classification, and metrics. **Key imports:** ```python -from afk.queues import QueueWorker, TaskQueue +from afk.queues import TaskQueue, TaskWorker ``` ## `afk.observability` @@ -236,18 +236,18 @@ from afk.mcp import get_mcp_store ## `afk.messaging` -Internal agent-to-agent messaging primitives with delivery guarantees. +Protocol-first agent-to-agent messaging exports. **Sub-modules:** | Sub-module | Responsibility | | ------------------------ | ---------------------------------------------------------------------------------------- | -| `afk.messaging.bus` | `MessageBus` protocol for pub/sub messaging between agents. | -| `afk.messaging.delivery` | Delivery tracking, acknowledgment, and retry for inter-agent messages. | -| `afk.messaging.types` | `AgentMessage`, `MessageEnvelope`, delivery status types. | +| `afk.messaging` | Public re-exports for A2A contracts, envelopes, auth providers, hosts, and delivery stores. | +| `afk.agents.a2a` | Internal implementation modules for protocol, auth, delivery, hosting, and Google adapter. | **Key imports:** ```python -from afk.messaging import MessageBus, AgentMessage +from afk.messaging import InternalA2AEnvelope, InternalA2AProtocol +from afk.messaging import AgentInvocationRequest, AgentInvocationResponse ``` diff --git a/docs/library/how-to-use-afk.mdx b/docs/library/how-to-use-afk.mdx index 5f302a1..45bd536 100644 --- a/docs/library/how-to-use-afk.mdx +++ b/docs/library/how-to-use-afk.mdx @@ -1,228 +1,127 @@ --- title: How to Use AFK -description: A phased adoption guide — from first agent to production deployment. +description: A practical adoption path from first agent to production release. --- -AFK is designed for incremental adoption. Start with the simplest possible agent, add capabilities as you understand the problem space, and layer in production controls before you ship. This guide walks through four phases, each building on the last. +Use AFK incrementally. Start with one narrow agent, add tools only when the task needs action, and add production controls before real users depend on the system. - - **You don't need to read this page front-to-back.** Find your current phase - and start there. Each phase tells you what to build, what to add, and when to - move on. - +## Phase 1: narrow agent -## Adoption path +Build one agent that solves one task without tools. -```mermaid -flowchart LR - P1["Phase 1\nNarrow Slice"] --> P2["Phase 2\nAdd Controls"] - P2 --> P3["Phase 3\nProduction"] - P3 --> P4["Phase 4\nRelease Discipline"] +```python +from afk.agents import Agent +from afk.core import Runner - style P1 fill:#d4edda,stroke:#28a745 - style P2 fill:#fff3cd,stroke:#ffc107 - style P3 fill:#cce5ff,stroke:#007bff - style P4 fill:#f8d7da,stroke:#dc3545 +agent = Agent( + name="ticket-classifier", + model="gpt-4.1-mini", + instructions=""" + Classify the support ticket as exactly one of: + billing, technical, account, or other. + Return only the category. + """, +) + +result = Runner().run_sync( + agent, + user_message="I cannot log into my account.", +) +print(result.final_text) ``` ---- +Move on when the agent is reliable on real examples for the narrow task. - - - Build one focused agent that does one thing well. - - **What to build:** - - Single agent with specific instructions - - 0–2 tools for the core capability - - Synchronous execution (`run_sync`) - - **Code example:** - - ```python - from afk.agents import Agent - from afk.core import Runner - - agent = Agent( - name="ticket-classifier", - model="gpt-5.2-mini", - instructions=""" - Read the support ticket and classify it as exactly one of: - billing, technical, account, or other. - Output only the category name. - """, - ) - - runner = Runner() - result = runner.run_sync(agent, user_message="I can't log into my account") - print(result.final_text) # "account" - ``` - - **Focus on:** - - Getting the prompt right — iterate until the agent reliably produces correct output - - Testing with 10–20 real examples before adding any complexity - - Keeping everything in a single file - - **Move to Phase 2 when:** Your agent works correctly for the core use case and you need it to take actions or handle edge cases. - - - - - Give the agent capabilities and constrain them with safety controls. - - **What to add:** - - Tools with Pydantic argument models - - `FailSafeConfig` with step, cost, and time limits - - Policy rules for dangerous operations - - Basic error handling - - **Code example:** - - ```python - from pydantic import BaseModel - from afk.agents import Agent, FailSafeConfig, PolicyEngine, PolicyRule - from afk.tools import tool - from afk.core import Runner, RunnerConfig - - class TicketArgs(BaseModel): - ticket_id: str - - @tool(args_model=TicketArgs, name="lookup_ticket", description="Fetch ticket details.") - def lookup_ticket(args: TicketArgs) -> dict: - return {"ticket_id": args.ticket_id, "status": "open", "priority": "high"} - - policy = PolicyEngine(rules=[ - PolicyRule( - rule_id="gate-updates", - condition=lambda e: e.tool_name and "update" in e.tool_name, - action="request_approval", - reason="Ticket updates need human approval", - ), - ]) - - agent = Agent( - name="ticket-agent", - model="gpt-5.2-mini", - instructions="Look up tickets and classify priority. Never modify without approval.", - tools=[lookup_ticket], - fail_safe=FailSafeConfig( - max_steps=10, - max_tool_calls=5, - max_total_cost_usd=0.10, - ), - ) +## Phase 2: tools and safety - runner = Runner( - policy_engine=policy, - config=RunnerConfig(sanitize_tool_output=True), - ) - ``` +Add typed tools and hard limits. - **Focus on:** - - Every tool has a Pydantic model — no untyped arguments - - Cost limits are always set (`max_total_cost_usd`) - - Dangerous operations require approval +```python +from pydantic import BaseModel - **Move to Phase 3 when:** Your agent works reliably with tools and policies, and you're ready to deploy. +from afk.agents import Agent, FailSafeConfig +from afk.core import Runner, RunnerConfig +from afk.tools import tool - - - Add the infrastructure for monitoring, testing, and safe deployment. +class TicketArgs(BaseModel): + ticket_id: str - **What to add:** - - Telemetry (console for dev, OTEL for prod) - - Eval suite with at least 5 test cases - - Memory persistence for multi-turn conversations - - Streaming for user-facing UIs - **Code example:** +@tool(args_model=TicketArgs, name="lookup_ticket", description="Fetch ticket details.") +def lookup_ticket(args: TicketArgs) -> dict: + return {"ticket_id": args.ticket_id, "status": "open", "priority": "high"} - ```python - from afk.core import Runner, RunnerConfig - from afk.evals import run_suite, EvalBudget - from afk.evals.models import EvalCase, EvalSuiteConfig - # Production runner - runner = Runner( - telemetry="otel", - telemetry_config={"service_name": "ticket-agent"}, - config=RunnerConfig(interaction_mode="headless"), - ) +agent = Agent( + name="ticket-agent", + model="gpt-4.1-mini", + instructions="Look up tickets before answering. Never modify data.", + tools=[lookup_ticket], + fail_safe=FailSafeConfig( + max_steps=8, + max_tool_calls=4, + max_total_cost_usd=0.10, + ), +) - # Eval suite for CI - suite = run_suite( - runner_factory=lambda: Runner(), - cases=[ - EvalCase( - name="basic-lookup", - agent=agent, - user_message="Check ticket TK-1234", - budget=EvalBudget(max_total_cost_usd=0.05), - ), - EvalCase( - name="unknown-ticket", - agent=agent, - user_message="Check ticket TK-9999", - ), - ], - config=EvalSuiteConfig(fail_fast=True), +runner = Runner( + config=RunnerConfig( + sanitize_tool_output=True, + tool_output_max_chars=8_000, ) - assert suite.failed == 0, f"Eval failures: {suite.failed}/{suite.total}" - ``` - - **Focus on:** - - Evals run in CI on every pull request - - Alerting on error rate and latency - - Memory compaction for long-running threads +) +``` - **Move to Phase 4 when:** Your agent is in production and you need structured release management. +Move on when all tools have Pydantic argument models, cost limits are set, and mutating operations are gated or absent. - +## Phase 3: production controls - - Establish processes for safe agent evolution. +Before shipping, add the controls that make failures diagnosable: - **What to add:** - - Golden trace comparison for regression detection - - Budget-gated CI (releases blocked if evals fail) - - Canary deployments with cost monitoring - - Documentation for on-call and incident response +- evals for expected behavior; +- telemetry for latency, usage, errors, and tool calls; +- persistent memory or queues if runs must survive process restarts; +- security controls for sandboxing, secret scope, and tool output limits; +- troubleshooting docs for on-call and operators. - **Practices:** - - Run the full eval suite before every release - - Compare golden traces to catch behavioral drift - - Monitor cost-per-run trends for budget anomalies - - Keep system prompts in version-controlled files (not inline strings) +Useful pages: - **This phase never ends** — it's the ongoing discipline of running agents in production. + + + Test agent behavior and enforce budgets. + + + Export metrics, traces, and run records. + + + Understand policy gates, sandboxing, and secret isolation. + + + Run agents through distributed workers. + + - - +## Phase 4: release discipline ---- +Once the agent is in production: -## Phase checklist +- run evals in CI before prompt, tool, or model changes; +- compare behavior across releases with golden traces where appropriate; +- monitor cost per run and failure rate; +- version system prompts in files; +- document operator actions for approval, resume, and rollback flows. -Use this to track your progress: +## Common mistakes -| Phase | Milestone | Status | -| ----- | --------------------------------------------------- | ----------------------------------------- | -| 1 | Agent produces correct output for 20+ test cases | | -| 2 | Tools have Pydantic models, FailSafe limits are set | | -| 2 | Policy rules gate all mutating operations | | -| 3 | Telemetry is configured and exporting | | -| 3 | Eval suite runs in CI with ≥ 5 cases | | -| 3 | Memory persistence is configured | | -| 4 | Golden traces are captured and compared | | -| 4 | Releases are gated by eval pass rate | | +| Mistake | Better approach | +| --- | --- | +| Starting with a multi-agent system | Start with one narrow agent and split only when roles are genuinely different | +| Writing untyped tools | Use Pydantic argument models for every tool | +| Treating prompts as the only safety layer | Add `FailSafeConfig`, policy gates, sandboxing, and evals | +| Hiding internals in public docs | Keep builder docs behavior-first and maintainer docs internals-first | +| Shipping without run records | Export telemetry and inspect `AgentResult` fields | ## Next steps - - - Production patterns, common architectures, and anti-patterns. - - - Capability maturity model — know when to add complexity. - - +Read [Building with AI](/library/building-with-ai) for production design patterns, then [Troubleshooting](/library/troubleshooting) for common operational failures. diff --git a/docs/library/learn-in-15-minutes.mdx b/docs/library/learn-in-15-minutes.mdx index 067f819..ca8a1ae 100644 --- a/docs/library/learn-in-15-minutes.mdx +++ b/docs/library/learn-in-15-minutes.mdx @@ -1,18 +1,11 @@ --- title: Learn AFK in 15 Minutes -description: A hands-on tutorial covering agents, tools, streaming, multi-agent, and memory. +description: A guided path through agents, tools, streaming, memory, and safety. --- -This tutorial walks you through every major AFK concept in five hands-on sections. Each section takes about 3 minutes and builds on the previous one. By the end, you'll have a working understanding of agents, tools, streaming, multi-agent orchestration, and memory. +This tutorial expands the quickstart into the core workflow used by most AFK applications. Each section introduces one concept and keeps the code small enough to copy into a single file. - - **Prerequisites:** `pip install afk` and an OpenAI API key in your environment - (`OPENAI_API_KEY`). - - -## Minute 1–3: Your first agent - -An AFK agent is a configuration object. You describe _what_ it is — the runner handles _how_ it executes. +## 1. Agent + runner ```python from afk.agents import Agent @@ -20,260 +13,187 @@ from afk.core import Runner agent = Agent( name="tutor", - model="gpt-5.2-mini", - instructions=""" - You are a programming tutor. - Explain concepts using simple language and short code examples. - Keep answers under 100 words. - """, + model="gpt-4.1-mini", + instructions="Explain programming concepts in plain language.", ) runner = Runner() result = runner.run_sync(agent, user_message="What is recursion?") -print(result.final_text) # The model's explanation -print(result.state) # "completed" -print(result.run_id) # Unique run ID for tracing +print(result.final_text) +print(result.run_id) ``` -**Key takeaway:** `Agent` is pure configuration (no execution logic). `Runner` is the engine. `AgentResult` is the output. These three types are the foundation of everything in AFK. - ---- - -## Minute 4–6: Give your agent tools +Key point: an agent is declarative. The runner owns execution. -Tools let agents take actions beyond text generation. Define a tool as a Python function with a Pydantic model for its arguments. +## 2. Typed tools ```python from pydantic import BaseModel + from afk.agents import Agent -from afk.tools import tool from afk.core import Runner +from afk.tools import tool + class LookupArgs(BaseModel): topic: str max_results: int = 3 -@tool(args_model=LookupArgs, name="search_docs", description="Search the documentation by topic.") + +@tool(args_model=LookupArgs, name="search_docs", description="Search docs by topic.") def search_docs(args: LookupArgs) -> dict: - # In production, this would call a real search API return { "results": [ - {"title": f"Guide: {args.topic}", "relevance": 0.95}, - {"title": f"FAQ: {args.topic}", "relevance": 0.82}, - ][:args.max_results] + {"title": f"Guide: {args.topic}", "score": 0.95}, + {"title": f"FAQ: {args.topic}", "score": 0.82}, + ][: args.max_results] } + agent = Agent( name="research-tutor", - model="gpt-5.2-mini", - instructions="Use search_docs to find relevant documentation before answering.", - tools=[search_docs], # ← Attach tools here + model="gpt-4.1-mini", + instructions="Search docs before answering user questions.", + tools=[search_docs], ) -runner = Runner() -result = runner.run_sync(agent, user_message="How do I handle errors in Python?") +result = Runner().run_sync( + agent, + user_message="How do I handle errors in Python?", +) print(result.final_text) -print(f"Tool calls made: {len(result.tool_executions)}") - -# Inspect what tools were called -for rec in result.tool_executions: - print(f" {rec.tool_name}: {'[OK]' if rec.success else '[ERR]'} ({rec.latency_ms:.0f}ms)") +print(result.tool_executions) ``` -**Key takeaway:** Tools are typed functions. AFK validates arguments via Pydantic, executes the handler, sanitizes the output, and feeds results back to the LLM. The model decides when and how to call tools. - ---- +Key point: tool arguments are validated before your function runs. -## Minute 7–9: Stream responses in real time +## 3. Streaming -For chat UIs and CLI tools, streaming gives you text as it's generated instead of waiting for the full response. +Use streaming when a UI or CLI should show progress before the final result is ready. ```python import asyncio + from afk.agents import Agent from afk.core import Runner + agent = Agent( - name="explainer", - model="gpt-5.2-mini", - instructions="Give detailed, well-structured explanations.", - tools=[search_docs], # Reuse the tool from above + name="streamer", + model="gpt-4.1-mini", + instructions="Explain topics clearly.", ) -async def main(): - runner = Runner() - handle = await runner.run_stream( - agent, user_message="Explain the Python GIL and its impact on threading" + +async def main() -> None: + handle = await Runner().run_stream( + agent, + user_message="Explain Python decorators in three bullets.", ) async for event in handle: - match event.type: - case "text_delta": - print(event.text_delta, end="", flush=True) - case "tool_started": - print(f"\n[TOOL] Calling {event.tool_name}...") - case "tool_completed": - status = "[OK]" if event.success else "[ERR]" - print(f" {status} {event.tool_name} done") - case "completed": - print(f"\n\n[DONE] Run completed ({event.result.state})") - - # Access the full result after streaming - result = handle.result - print(f"Total tokens: {result.usage.total_tokens}") + if event.type == "text_delta": + print(event.text_delta, end="", flush=True) + elif event.type == "completed": + print(f"\n\nstate={event.result.state}") + asyncio.run(main()) ``` -**Key takeaway:** `run_stream()` returns an async iterator of events. You get `text_delta` for incremental text, tool lifecycle events, and a final `completed` event with the full `AgentResult`. +Key point: `run_stream()` returns an `AgentStreamHandle`. Consume it to receive text, tool lifecycle events, errors, and the terminal result. ---- - -## Minute 10–12: Multi-agent orchestration +## 4. Memory continuity -When a task requires different expertise, split it across specialist subagents. +Pass the same `thread_id` to keep conversation context attached to a thread. ```python import asyncio + from afk.agents import Agent from afk.core import Runner -# Specialist 1: Research agent -researcher = Agent( - name="researcher", - model="gpt-5.2-mini", - instructions=""" - You are a research specialist. - Find relevant facts and present them as bullet points. - Be thorough but concise. - """, -) -# Specialist 2: Writing agent -writer = Agent( - name="writer", - model="gpt-5.2-mini", - instructions=""" - You are a technical writer. - Take research findings and write a clear, well-structured summary. - Use simple language suitable for a general audience. - """, +agent = Agent( + name="memory-tutor", + model="gpt-4.1-mini", + instructions="Remember the user's earlier questions in this thread.", ) -# Coordinator: delegates and combines -coordinator = Agent( - name="coordinator", - model="gpt-5.2-mini", - instructions=""" - You manage a team: - - Delegate research tasks to 'researcher' - - Delegate writing tasks to 'writer' - Combine their work into a polished final response. - """, - subagents=[researcher, writer], -) -async def main(): +async def main() -> None: runner = Runner() - result = await runner.run( - coordinator, - user_message="Write a brief explainer about quantum computing for beginners", + thread_id = "student-session-42" + + first = await runner.run( + agent, + user_message="What are Python decorators?", + thread_id=thread_id, ) + second = await runner.run( + agent, + user_message="Give me a short example using that idea.", + thread_id=thread_id, + ) + + print(first.final_text) + print(second.final_text) - print(result.final_text) - print(f"\nSubagent calls: {len(result.subagent_executions)}") - for sub in result.subagent_executions: - print(f" -> {sub.subagent_name}: {'[OK]' if sub.success else '[ERR]'}") asyncio.run(main()) ``` -**Key takeaway:** Subagents appear as auto-generated tools to the coordinator (`transfer_to_researcher`, `transfer_to_writer`). Each subagent runs a full agent loop with its own model and instructions. - ---- +Key point: thread continuity is explicit. Use the same `thread_id` for related turns. -## Minute 13–15: Memory and thread continuity +## 5. Safety limits -AFK supports multi-turn conversations through thread-based memory. Pass a `thread_id` to maintain context across runs. +Production agents need hard limits even when prompts and tools are well designed. ```python -import asyncio -from afk.agents import Agent -from afk.core import Runner +from afk.agents import Agent, FailSafeConfig +from afk.core import Runner, RunnerConfig agent = Agent( - name="tutor", - model="gpt-5.2-mini", - instructions="You are a Python tutor. Remember what the student has asked before.", + name="bounded-agent", + model="gpt-4.1-mini", + instructions="Help users, but stop if the task becomes too large.", + fail_safe=FailSafeConfig( + max_steps=8, + max_llm_calls=12, + max_tool_calls=20, + max_wall_time_s=45.0, + max_total_cost_usd=0.25, + ), ) -async def main(): - runner = Runner() - thread = "session-42" # ← Same thread_id = same conversation - - # Turn 1 - r1 = await runner.run(agent, user_message="What are decorators?", thread_id=thread) - print(f"Turn 1: {r1.final_text[:80]}...") - - # Turn 2 — the agent remembers Turn 1 - r2 = await runner.run(agent, user_message="Give me an example", thread_id=thread) - print(f"Turn 2: {r2.final_text[:80]}...") - - # Turn 3 — still has context - r3 = await runner.run(agent, user_message="How is that different from a wrapper function?", thread_id=thread) - print(f"Turn 3: {r3.final_text[:80]}...") - -asyncio.run(main()) -``` - - - If a run is interrupted (process crash, timeout, pause for human approval), you can resume it from the last checkpoint: - -```python -# Start a run -result = await runner.run(agent, user_message="Analyze this data...") - -# Later, resume from the checkpoint -resumed = await runner.resume( - agent, - run_id=result.run_id, - thread_id=result.thread_id, +runner = Runner( + config=RunnerConfig( + sanitize_tool_output=True, + tool_output_max_chars=8_000, + ), ) -print(resumed.state) # "completed" -``` - -AFK persists checkpoints at key boundaries (step start, post-LLM, post-tool batch). On resume, completed tool calls are replayed from cache — no duplicate side effects. - - -**Key takeaway:** `thread_id` connects runs into a conversation. Memory persists automatically. For long threads, use `runner.compact_thread()` to summarize old messages and control storage growth. - ---- - -## You just learned AFK - -Here's what you covered: +result = runner.run_sync(agent, user_message="Summarize the tradeoffs of caching.") +print(result.final_text) +``` -| Minute | Concept | Key API | -| ------ | --------------- | ----------------------------------------------------- | -| 1–3 | Agents & Runner | `Agent(...)`, `Runner().run_sync(...)` | -| 4–6 | Tools | `@tool(...)`, `result.tool_executions` | -| 7–9 | Streaming | `runner.run_stream(...)`, `async for event in handle` | -| 10–12 | Multi-agent | `agent.subagents=[...]`, `result.subagent_executions` | -| 13–15 | Memory | `thread_id=...`, `runner.resume(...)` | +Key point: limits are part of the agent contract. Set them before shipping. -## Go deeper +## What to read next - - - Deep dive into agents, runner, tools, memory, and more. + + + Agent fields, prompt resolution, subagents, skills, and MCP tools. + + + Tool schemas, context, hooks, middleware, sandboxing, and output limits. - - Production patterns, anti-patterns, and deployment playbook. + + Event types, stream handles, cancellation, and UI patterns. - - Runnable examples for every major feature. + + Evals, telemetry, security controls, queues, and deployment. diff --git a/docs/library/llm-interaction.mdx b/docs/library/llm-interaction.mdx index 5dd2450..5f2a3a4 100644 --- a/docs/library/llm-interaction.mdx +++ b/docs/library/llm-interaction.mdx @@ -31,7 +31,7 @@ Each step in this flow is described in detail below. At each step of the agent loop, the runner builds an `LLMRequest` and sends it to the configured LLM provider. Here is what goes into the request: -**Model resolution.** The agent's `model` field (e.g., `"gpt-5.2-mini"`) is resolved through the model resolution chain. This maps the requested model name to a provider, adapter, and normalized model identifier. Custom `model_resolver` functions can override this mapping. +**Model resolution.** The agent's `model` field (e.g., `"gpt-4.1-mini"`) is resolved through the model resolution chain. This maps the requested model name to a provider, adapter, and normalized model identifier. Custom `model_resolver` functions can override this mapping. **Message history.** The runner maintains a list of `Message` objects representing the conversation history. This includes: - A `system` message containing the agent's instructions, skill manifests, and (when enabled) an untrusted-data preamble. @@ -48,7 +48,7 @@ At each step of the agent loop, the runner builds an `LLMRequest` and sends it t ```python # Simplified view of what the runner builds request = LLMRequest( - model="gpt-5.2-mini", + model="gpt-4.1-mini", request_id=f"{run_id}:step:{step}", messages=messages, # Full conversation history tools=tool_definitions, # OpenAI-format function tools diff --git a/docs/library/mcp-server.mdx b/docs/library/mcp-server.mdx index f16fe3f..5dd64e8 100644 --- a/docs/library/mcp-server.mdx +++ b/docs/library/mcp-server.mdx @@ -49,9 +49,17 @@ Make your AFK tools available to any MCP-compatible client: class CalcArgs(BaseModel): expression: str + _OPS = { + "+": lambda a, b: a + b, + "-": lambda a, b: a - b, + "*": lambda a, b: a * b, + "/": lambda a, b: a / b, + } + @tool(args_model=CalcArgs, name="calculate", description="Evaluate a math expression.") def calculate(args: CalcArgs) -> dict: - return {"result": eval(args.expression)} + left, op, right = args.expression.split() + return {"result": _OPS[op](float(left), float(right))} ``` @@ -103,7 +111,7 @@ Discover and use tools from any MCP server: agent = Agent( name="assistant", - model="gpt-5.2-mini", + model="gpt-4.1-mini", instructions="Use available tools to help the user.", tools=tools, # ← MCP tools work like local tools ) diff --git a/docs/library/memory.mdx b/docs/library/memory.mdx index 448d5da..2b344bf 100644 --- a/docs/library/memory.mdx +++ b/docs/library/memory.mdx @@ -12,7 +12,7 @@ import asyncio from afk.agents import Agent from afk.core import Runner -agent = Agent(name="tutor", model="gpt-5.2-mini", instructions="You are a Python tutor.") +agent = Agent(name="tutor", model="gpt-4.1-mini", instructions="You are a Python tutor.") async def main(): runner = Runner() @@ -166,13 +166,36 @@ AFK ships with four backends. All implement the `MemoryStore` protocol. +### Connection pooling for Redis + +For production Redis deployments, use connection pooling for better performance: + +```python +from afk.llms.cache.redis_pool import get_redis_pool, PoolConfig + +pool = await get_redis_pool( + "redis://localhost:6379", + config=PoolConfig( + max_connections=50, + socket_timeout=5.0, + socket_connect_timeout=5.0, + ), +) + +# Use the pool with RedisMemoryStore +from afk.memory.adapters.redis import RedisMemoryStore + +runner = Runner( + memory_store=RedisMemoryStore(url="redis://localhost:6379"), +) +``` + ### Environment-based selection Set environment variables to auto-select a backend without code changes: ```bash -export AFK_MEMORY_BACKEND=sqlite -export AFK_MEMORY_SQLITE_PATH=./agent_memory.sqlite3 +export AFK_MEMORY_BACKEND=memory ``` The runner falls back to in-memory if the configured backend fails to initialize. diff --git a/docs/library/mental-model.mdx b/docs/library/mental-model.mdx index f1c8f1d..1162648 100644 --- a/docs/library/mental-model.mdx +++ b/docs/library/mental-model.mdx @@ -54,7 +54,7 @@ flowchart LR # The Decision Loop is shaped by these fields agent = Agent( instructions="...", # ← What the model knows - model="gpt-5.2-mini", # ← How the model thinks + model="gpt-4.1-mini", # ← How the model thinks tools=[...], # ← What the model can do ) ``` diff --git a/docs/library/messaging.mdx b/docs/library/messaging.mdx index 8bedc54..25ba3be 100644 --- a/docs/library/messaging.mdx +++ b/docs/library/messaging.mdx @@ -31,26 +31,34 @@ sequenceDiagram Every message is wrapped in a typed envelope: ```python -from afk.a2a import InternalA2AEnvelope +from afk.messaging import InternalA2AEnvelope envelope = InternalA2AEnvelope( - sender="researcher", - receiver="writer", + message_type="request", + run_id="run-42", + thread_id="thread-42", + conversation_id="conversation-42", + correlation_id="task-42", payload={"findings": ["fact 1", "fact 2"]}, - correlation_id="task-42", # ← Group related messages together - idempotency_key="unique-msg-01", # ← Prevent duplicate processing + idempotency_key="task-42-research", + source_agent="researcher", + target_agent="writer", ) ``` | Field | Type | Purpose | | ----------------- | ---------- | -------------------------------------------- | -| `sender` | `str` | Name of the sending agent | -| `receiver` | `str` | Name of the receiving agent | +| `message_type` | `str` | `request`, `response`, or `event` | +| `run_id` | `str` | Run identifier for tracing | +| `thread_id` | `str` | Memory thread identifier | +| `conversation_id` | `str` | Cross-run conversation identifier | | `payload` | `dict` | Message data (any JSON-serializable content) | | `correlation_id` | `str` | Groups related messages in a workflow | | `idempotency_key` | `str` | Deduplication — same key = same message | -| `created_at` | `datetime` | Timestamp of creation | -| `attempt` | `int` | Current delivery attempt number | +| `source_agent` | `str` | Name of the sending agent | +| `target_agent` | `str` | Name of the receiving agent | +| `metadata` | `dict` | JSON-safe tracing or routing metadata | +| `timestamp_ms` | `int` | Creation timestamp in milliseconds | ## Delivery behavior @@ -99,16 +107,26 @@ envelope = InternalA2AEnvelope( correlation = f"analysis-{run_id}" await send(InternalA2AEnvelope( - sender="coordinator", receiver="researcher", - payload={"query": "AI trends"}, + message_type="request", + run_id=run_id, + thread_id=thread_id, + conversation_id=conversation_id, correlation_id=correlation, + source_agent="coordinator", + target_agent="researcher", + payload={"query": "AI trends"}, idempotency_key=f"{correlation}-research", )) await send(InternalA2AEnvelope( - sender="coordinator", receiver="writer", - payload={"topic": "AI trends"}, + message_type="request", + run_id=run_id, + thread_id=thread_id, + conversation_id=conversation_id, correlation_id=correlation, + source_agent="coordinator", + target_agent="writer", + payload={"topic": "AI trends"}, idempotency_key=f"{correlation}-write", )) ``` @@ -120,8 +138,9 @@ await send(InternalA2AEnvelope( Default. Fast, no setup. State lost on restart. ```python - from afk.a2a import InMemoryDeliveryStore - store = InMemoryDeliveryStore() + from afk.messaging import InMemoryA2ADeliveryStore + + store = InMemoryA2ADeliveryStore() ``` @@ -129,13 +148,13 @@ await send(InternalA2AEnvelope( Implement the `DeliveryStore` protocol for durable messaging: ```python - from afk.a2a import DeliveryStore + from afk.messaging import A2ADeliveryStore, InternalA2AEnvelope - class RedisDeliveryStore(DeliveryStore): - async def submit(self, envelope: InternalA2AEnvelope) -> bool: ... - async def ack(self, idempotency_key: str): ... - async def nack(self, idempotency_key: str): ... - async def get_dead_letters(self) -> list[InternalA2AEnvelope]: ... + class RedisDeliveryStore(A2ADeliveryStore): + async def get_success(self, idempotency_key: str): ... + async def record_success(self, idempotency_key: str, response): ... + async def record_dead_letter(self, dead_letter): ... + async def list_dead_letters(self): ... ``` diff --git a/docs/library/migration.mdx b/docs/library/migration.mdx new file mode 100644 index 0000000..12effff --- /dev/null +++ b/docs/library/migration.mdx @@ -0,0 +1,426 @@ +--- +title: Migration Guide +description: Move from LangChain, OpenAI Assistants, or custom agents to AFK. +--- + +This guide helps you migrate existing agent code from other frameworks to AFK. + +## From LangChain + +### LangChain → AFK concepts + +| LangChain Concept | AFK Equivalent | Key Difference | +|-------------------|----------------|----------------| +| `ChatOpenAI` | `LLMBuilder` | Provider-portable, typed contracts | +| `Agent` | `Agent` | Config object, not runtime | +| `Tool` | `@tool` decorator | Pydantic-based, typed arguments | +| `Chain` | `Runner` | Explicit execution loop | +| `Memory` | `MemoryStore` | Multiple backends, checkpointing | +| `Callback` | `Middleware` / `Hooks` | Request/response interception | +| `LangSmith` | `Telemetry` | Built-in OTEL support | + +### Basic agent migration + +**LangChain:** +```python +from langchain_openai import ChatOpenAI +from langchain.agents import initialize_agent, Tool + +llm = ChatOpenAI(model="gpt-4") + +def search(query: str) -> str: + return f"Results for: {query}" + +tools = [Tool(name="search", func=search, description="Search the web")] + +agent = initialize_agent( + tools, llm, agent="zero-shot-react-description", verbose=True +) + +result = agent.run("Search for AI news") +``` + +**AFK:** +```python +from afk.agents import Agent +from afk.tools import tool +from afk.core import Runner +from pydantic import BaseModel + +class SearchArgs(BaseModel): + query: str + +@tool(args_model=SearchArgs, name="search", description="Search the web") +def search(args: SearchArgs) -> dict: + return {"results": f"Results for: {args.query}"} + +agent = Agent( + name="assistant", + model="gpt-4.1-mini", + instructions="Use the search tool to find information.", + tools=[search], +) + +runner = Runner() +result = runner.run_sync(agent, user_message="Search for AI news") +print(result.final_text) +``` + +### Key differences + +**1. Agent is a config object, not a runtime:** + +```python +# LangChain: agent is callable +result = agent.run(input) + +# AFK: Agent defines what, Runner executes how +runner = Runner() +result = runner.run_sync(agent, user_message="input") +``` + +**2. Tools use Pydantic for validation:** + +```python +# LangChain: function signature and docstring +def search(query: str) -> str: + """Search the web for information.""" + ... + +# AFK: Pydantic model for typed arguments +class SearchArgs(BaseModel): + query: str + +@tool(args_model=SearchArgs, name="search", description="Search the web") +def search(args: SearchArgs) -> dict: + return {"results": f"Results for: {args.query}"} +``` + +**3. Explicit execution modes:** + +```python +# LangChain: single run() method +result = agent.run(input) + +# AFK: explicit sync, async, or streaming +result = runner.run_sync(agent, user_message=input) # Blocking +result = await runner.run(agent, user_message=input) # Async +handle = await runner.run_stream(agent, user_message=input) # Streaming +``` + +### Tool migration + +**LangChain tools:** +```python +from langchain.tools import tool +from langchain_core.tools import StructuredTool + +# Simple tool +@tool +def get_weather(city: str) -> str: + """Get weather for a city.""" + return f"Weather in {city}: 72°F" + +# Structured tool with custom logic +def custom_search(query: str, limit: int = 10) -> dict: + ... + +search_tool = StructuredTool.from_function( + func=custom_search, + name="search", + description="Search for documents", + args_schema=CustomSearchSchema, +) +``` + +**AFK equivalents:** +```python +from afk.tools import tool +from pydantic import BaseModel, Field + +# Simple tool +class WeatherArgs(BaseModel): + city: str + +@tool(args_model=WeatherArgs, name="get_weather", description="Get weather for a city.") +def get_weather(args: WeatherArgs) -> dict: + return {"weather": f"Weather in {args.city}: 72°F"} + +# Tool with constraints +class SearchArgs(BaseModel): + query: str + limit: int = Field(default=10, ge=1, le=100) + +@tool(args_model=SearchArgs, name="search", description="Search for documents.") +def search(args: SearchArgs) -> dict: + return {"results": [], "count": 0} +``` + +### Memory migration + +**LangChain memory:** +```python +from langchain.memory import ConversationBufferMemory +from langchain.agents import AgentExecutor + +memory = ConversationBufferMemory(memory_key="chat_history") + +agent_executor = AgentExecutor.from_agent_and_tools( + agent=agent, + tools=tools, + memory=memory, + verbose=True, +) +``` + +**AFK memory:** +```python +from afk.memory import SQLiteMemoryStore +from afk.core import Runner + +# Configure memory backend +runner = Runner( + memory_store=SQLiteMemoryStore(path="./memory.sqlite3") +) + +# Use thread_id for conversation continuity +thread_id = "user-123-session-1" + +result1 = await runner.run(agent, user_message="Hi", thread_id=thread_id) +result2 = await runner.run(agent, user_message="What did I say?", thread_id=thread_id) +``` + +### Callback → Middleware migration + +**LangChain callbacks:** +```python +from langchain.callbacks import CallbackManager +from langchain.tracing.openai import OpenAICallbackHandler + +callback_manager = CallbackManager([OpenAICallbackHandler()]) + +agent = Agent(..., callback_manager=callback_manager) +``` + +**AFK middleware:** +```python +from afk.llms import LLMBuilder +from afk.llms.middleware import MiddlewareStack +from afk.llms.middleware.timeout import TimeoutMiddleware, TimeoutConfig + +stack = MiddlewareStack( + chat=[TimeoutMiddleware(TimeoutConfig(default_timeout_s=30.0))], +) + +client = ( + LLMBuilder() + .provider("openai") + .model("gpt-4.1-mini") + .with_middlewares(stack) + .build() +) +``` + +### RAG migration + +**LangChain retrieval:** +```python +from langchain_community.vectorstores import Chroma +from langchain_openai import OpenAIEmbeddings +from langchain.chains import RetrievalQA + +embeddings = OpenAIEmbeddings() +vectorstore = Chroma(persist_directory="./db", embedding_function=embeddings) +retriever = vectorstore.as_retriever() + +qa_chain = RetrievalQA.from_chain_type( + llm=llm, + chain_type="stuff", + retriever=retriever, +) +``` + +**AFK approach:** +```python +from afk.memory import PostgresMemoryStore +from afk.memory.types import LongTermMemory + +memory_store = PostgresMemoryStore(dsn="postgresql://...") + +# Store documents with embeddings +await memory_store.upsert_long_term_memory( + LongTermMemory( + id="doc-1", + user_id="user-123", + scope="knowledge", + text="Document content here...", + embedding=embedding_vector, + tags=["product", "faq"], + ) +) + +# Retrieve in tool +class RetrieveArgs(BaseModel): + query: str + +@tool(args_model=RetrieveArgs, name="retrieve", description="Search knowledge base.") +async def retrieve(args: RetrieveArgs) -> dict: + results = await memory_store.search_long_term_memory_vector( + user_id=None, + query_embedding=get_embedding(args.query), + scope="knowledge", + limit=5, + ) + return {"results": [r[0].text for r in results]} +``` + +## From OpenAI Assistants API + +### Assistants → AFK concepts + +| OpenAI Concept | AFK Equivalent | +|----------------|----------------| +| Assistant | Agent | +| Thread | Memory + thread_id | +| Run | Runner execution | +| Message | MemoryEvent | +| Tool | @tool decorator | +| Function | @tool with Pydantic | +| File search | Long-term memory + vector search | + +### Basic migration + +**OpenAI Assistants:** +```python +from openai import OpenAI + +client = OpenAI() + +assistant = client.beta.assistants.create( + name="Helper", + instructions="You are a helpful assistant.", + tools=[{"type": "function", "function": {...}}], + model="gpt-4", +) + +thread = client.beta.threads.create() + +client.beta.threads.messages.create( + thread_id=thread.id, + role="user", + content="Hello!", +) + +run = client.beta.threads.runs.create( + thread_id=thread.id, + assistant_id=assistant.id, +) +``` + +**AFK:** +```python +from afk.agents import Agent +from afk.tools import tool +from afk.core import Runner + +@tool(name="help", description="Provide helpful responses.") +def help(args) -> dict: + return {"response": "Hello!"} + +agent = Agent( + name="helper", + model="gpt-4.1-mini", + instructions="You are a helpful assistant.", + tools=[help], +) + +runner = Runner() +result = runner.run_sync(agent, user_message="Hello!") +``` + +### Key advantages of AFK over Assistants API + +1. **Local execution** — No API calls needed for simple tasks +2. **Portable** — Switch LLM providers without code changes +3. **Debuggable** — Step through agent logic locally +4. **Testable** — Run evals locally in CI +5. **Controllable** — Full access to prompts, tools, and behavior + +## From custom agent code + +### Common patterns migration + +**Custom retry logic:** +```python +# Before: Custom retry implementation +import time + +def call_with_retry(func, max_attempts=3): + for attempt in range(max_attempts): + try: + return func() + except Exception as e: + if attempt == max_attempts - 1: + raise + time.sleep(2 ** attempt) +``` + +**AFK:** +```python +client = ( + LLMBuilder() + .provider("openai") + .model("gpt-4.1-mini") + .profile("production") + .build() +) +``` + +**Custom circuit breaker:** +```python +# Before: Custom circuit breaker +class CircuitBreaker: + def __init__(self, failure_threshold=5): + self.failures = 0 + self.threshold = failure_threshold + self.state = "closed" + + def call(self, func): + if self.state == "open": + raise CircuitOpenError() + try: + return func() + except Exception: + self.failures += 1 + if self.failures >= self.threshold: + self.state = "open" + raise +``` + +**AFK:** +```python +client = ( + LLMBuilder() + .provider("openai") + .model("gpt-4.1-mini") + .profile("production") + .build() +) +``` + +## Next steps + + + + Build your first AFK agent in 5 minutes. + + + Understand AFK's design philosophy. + + + Complete API documentation. + + + Runnable examples for every feature. + + diff --git a/docs/library/observability.mdx b/docs/library/observability.mdx index d0ea889..ffc21ce 100644 --- a/docs/library/observability.mdx +++ b/docs/library/observability.mdx @@ -67,11 +67,12 @@ Every completed run produces a `RunMetrics` object: ```python result = runner.run_sync(agent, user_message="...") -m = result.usage +usage = result.usage_aggregate -print(f"Steps: {m.total_steps}") -print(f"Cost: ${m.total_cost_usd:.4f}") -print(f"Duration: {m.wall_time_s:.1f}s") +print(f"State: {result.state}") +print(f"Tokens: {usage.total_tokens}") +print(f"Cost: ${result.total_cost_usd or 0:.4f}") +print(f"Tool calls: {len(result.tool_executions)}") ``` ## Choosing an exporter diff --git a/docs/library/overview.mdx b/docs/library/overview.mdx index 5b3903e..275218d 100644 --- a/docs/library/overview.mdx +++ b/docs/library/overview.mdx @@ -1,217 +1,76 @@ --- title: What is AFK? -description: A contract-first Python framework for building reliable AI agents. +description: The short mental model for building and maintaining AFK agents. --- -AFK (**Agent Forge Kit**) is a Python SDK for building AI agents that are predictable, testable, and production-ready. It separates **what your agent does** (contracts) from **how it runs** (runtime), so you can iterate on behavior without touching infrastructure. +AFK is an agent runtime for Python applications. It is designed for teams that need agent behavior to be typed, observable, testable, and bounded by explicit controls. - - If you've used the OpenAI Assistants API or LangChain, think of AFK as the - layer that adds **typed contracts, policy gates, failure handling, and - observability** to your agent code — without locking you into a single LLM - provider. - +The core idea is simple: -## Why AFK? +```mermaid +flowchart LR + Agent["Agent definition"] --> Runner["Runner"] + Runner --> Result["AgentResult"] + Runner --> Runtime["LLM, tools, memory, policy, telemetry"] +``` -AFK is designed for engineering teams that need agent systems to be reliable in -production, not just functional in a demo. +## The three core objects -| If you need... | AFK gives you... | -| ----------------------------------------- | -------------------------------------------------------------------------------------- | -| Predictable multi-step behavior | Deterministic runner loop with typed contracts and explicit lifecycle events | -| Safe tool execution | Pydantic validation, policy gates, approval flows, sandbox profile integration | -| Production controls | Cost/step/time limits, retry and circuit breaker policies, bounded tool outputs | -| Portability across LLM providers | Normalized request/response contracts with pluggable provider adapters | -| CI confidence for prompt and tool changes | Built-in eval suites, golden traces, structured metrics, and telemetry export backends | +| Object | What it owns | What it does not own | +| --- | --- | --- | +| `Agent` | Name, model, instructions, tools, subagents, skills, MCP servers, defaults, fail-safe limits | Network calls, event loops, persistence, telemetry export | +| `Runner` | Execution loop, tool dispatch, streaming, memory, checkpointing, policy/HITL, telemetry | Agent identity or instructions | +| `AgentResult` | Final text, terminal state, tool/subagent records, usage aggregate, cost, run/thread ids | Future execution | -Use AFK when your agent will touch real systems and needs guardrails, debuggability, -and repeatability. If you only need a simple single-turn chat, a direct provider SDK -may be lighter. +This separation is the main design rule. Agents describe behavior. Runners execute behavior. Runtime subsystems provide capabilities. -## The core idea +## When AFK is a good fit -Every AFK application is built from three pieces: +Use AFK when your agent will: -```mermaid -flowchart LR - Agent["Agent - (what to do)"] --> Runner["Runner - (how to run it)"] - Runner --> Result["AgentResult - (what happened)"] -``` +- call tools or external systems; +- run for more than one step; +- need cost, time, step, or tool-call limits; +- stream progress to a UI; +- persist memory or resume a run; +- require evals, telemetry, or production incident debugging; +- coordinate specialist subagents. + +A direct LLM provider SDK may be simpler for one-off single-turn text generation. AFK is useful when the agent needs operational structure. + +## Builder path + +If you are building an app with AFK, read in this order: + +1. [Quickstart](/library/quickstart) for the smallest complete agent. +2. [Learn AFK in 15 Minutes](/library/learn-in-15-minutes) for the guided path. +3. [Agents](/library/agents), [Runner](/library/core-runner), and [Tools](/library/tools) for the core building blocks. +4. [Building with AI](/library/building-with-ai), [Evals](/library/evals), and [Observability](/library/observability) before production. + +## Maintainer path + +If you are changing AFK itself, read in this order: + +1. [Developer Guide](/library/developer-guide) for local setup, commands, and docs workflow. +2. [Architecture](/library/architecture) for package boundaries. +3. [Public API Rules](/library/public-imports-and-function-improvement) before changing exports or examples. +4. [Tested Behaviors](/library/tested-behaviors) before changing runner, tools, LLM runtime, memory, queues, or telemetry. -| Piece | What it is | You define | -| --------------- | --------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------- | -| **Agent** | A configuration object describing the agent's identity, model, instructions, tools, and subagents. | Name, model, instructions, tools, subagents, policies | -| **Runner** | The execution engine that runs agents through a step loop, managing LLM calls, tool execution, and state. | Configuration defaults, telemetry, memory backend | -| **AgentResult** | The output of a run — containing the final text, run state, tool/subagent execution records, and usage metrics. | Nothing — this is what you read after a run | +## Source-of-truth rules -## What AFK gives you - - - - Define agents as configuration objects. Attach tools as typed Python functions with Pydantic models for argument validation. The framework handles schema generation, execution, and output sanitization. - - ```python - from afk.agents import Agent - from afk.tools import tool - from pydantic import BaseModel - - class SearchArgs(BaseModel): - query: str - - @tool(args_model=SearchArgs, name="search", description="Search the knowledge base.") - def search(args: SearchArgs) -> dict: - return {"results": ["doc1", "doc2"]} - - agent = Agent( - name="assistant", - model="gpt-5.2-mini", - instructions="Help users find information.", - tools=[search], - ) - ``` - - - - - Run agents synchronously (blocking), asynchronously (awaitable), or with real-time streaming. Pause, resume, and cancel runs at any point. - - ```python - from afk.core import Runner - - runner = Runner() - - # Synchronous (simplest) - result = runner.run_sync(agent, user_message="Find docs about caching") - - # Async - result = await runner.run(agent, user_message="Find docs about caching") - - # Streaming - handle = await runner.run_stream(agent, user_message="Find docs about caching") - async for event in handle: - if event.type == "text_delta": - print(event.text_delta, end="") - ``` - - - - - Build systems where a coordinator agent delegates tasks to specialist subagents. AFK manages the DAG execution, join policies, and backpressure automatically. - - ```python - researcher = Agent(name="researcher", model="gpt-5.2-mini", instructions="Find facts.") - writer = Agent(name="writer", model="gpt-5.2-mini", instructions="Write summaries.") - - coordinator = Agent( - name="coordinator", - model="gpt-5.2-mini", - instructions="Delegate research, then summarize.", - subagents=[researcher, writer], - ) - ``` - - - - - Gate tool calls with policy rules. Require human approval for dangerous operations. Set hard limits on steps, cost, and wall time. - - ```python - from afk.agents import PolicyEngine, PolicyRule, FailSafeConfig - - policy = PolicyEngine(rules=[ - PolicyRule( - rule_id="gate-writes", - condition=lambda e: e.tool_name and "write" in e.tool_name, - action="request_approval", - reason="Write operations need human approval", - ), - ]) - - agent = Agent( - name="safe-agent", - model="gpt-5.2-mini", - instructions="...", - fail_safe=FailSafeConfig( - max_steps=10, - max_tool_calls=5, - max_total_cost_usd=0.25, - ), - ) - ``` - - - - - Swap LLM providers without changing agent code. AFK normalizes requests and responses across OpenAI, Anthropic, and 100+ providers via LiteLLM. Built-in retry, circuit breaking, caching, and rate limiting. - - ```python - from afk.llms import LLMBuilder - - client = ( - LLMBuilder() - .provider("openai") - .model("gpt-5.2-mini") - .profile("production") # ← retry, timeout, rate limit, circuit breaker - .build() - ) - ``` - - - - - Built-in telemetry pipeline with spans and metrics. Eval framework for CI-gated release quality. Export to console, JSON, or OpenTelemetry. - - ```python - # Enable OTEL telemetry - runner = Runner(telemetry="otel") - - # Run evals in CI - from afk.evals import run_suite, EvalBudget - from afk.evals.models import EvalCase - - suite = run_suite( - runner_factory=lambda: Runner(), - cases=[EvalCase(name="basic", agent=agent, user_message="Hello")], - ) - assert suite.failed == 0 - ``` - - - - -## When should you use AFK? - -| Scenario | AFK is a good fit | Consider alternatives | -| ----------------------------------------- | ---------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------- | -| Building a production AI agent with tools | Typed contracts, policy gates, observability | — | -| Prototyping a quick LLM chat | `Agent` + `Runner` in 5 lines | Raw API calls may be simpler | -| Multi-agent orchestration | DAG delegation, join policies, backpressure | — | -| Fine-tuning or training models | — | AFK is for inference, not training | -| Simple text completion without tools | Works, but may be more than you need | Direct SDK call is lighter | -| Enterprise deployment with compliance | Policy engine, audit events, secret isolation | — | - -## Key design decisions - -- **Contract-first:** Every interface is a Pydantic model or Python protocol. Tools, agents, results, and events are all typed contracts — not magic strings. -- **Separation of concerns:** Orchestration logic (runner, step loop) is completely separate from execution adapters (LLM providers, tool handlers). Swap providers without touching agent code. -- **Fail-safe by default:** Every agent run has configurable limits on steps, tool calls, cost, and wall time. The runner classifies failures as retryable, terminal, or non-fatal automatically. -- **Provider-portable:** Your agent code never touches provider-specific types. The LLM runtime normalizes everything into `LLMRequest` and `LLMResponse`. +- Public examples import from `afk.*`, never `src.afk.*`. +- `Runner` is imported from `afk.core`. +- User-facing docs should explain behavior before internals. +- Maintainer docs may reference internal modules, but must identify the public contract affected by a change. +- Generated agent-facing docs and skill indexes must be refreshed when navigation, examples, or skill references change. ## Next steps - Build an agent with tools and streaming in 5 minutes. + Build one agent and one typed tool. - - Hands-on tutorial covering every major AFK feature. + + Set up the repo and understand the contributor workflow. diff --git a/docs/library/performance.mdx b/docs/library/performance.mdx new file mode 100644 index 0000000..39c7ccf --- /dev/null +++ b/docs/library/performance.mdx @@ -0,0 +1,166 @@ +--- +title: Performance +description: Improve AFK latency, throughput, and cost without relying on internal APIs. +--- + +Performance work in AFK usually comes from four levers: choosing the right model, reducing unnecessary tool/LLM calls, keeping memory bounded, and moving long-running work into queues. + +## Latency + +Use the smallest model that can reliably handle the task, and reserve larger models for tasks that need deeper reasoning. + +```python +from afk.agents import Agent + +classifier = Agent( + name="classifier", + model="gpt-4.1-nano", + instructions="Classify the request. Return only one label.", +) + +analyst = Agent( + name="analyst", + model="gpt-4.1", + instructions="Perform detailed technical analysis.", +) +``` + +Other latency controls: + +- keep system prompts short and specific; +- make I/O-bound tools async; +- avoid tools for information already present in context; +- stream user-facing runs with `runner.run_stream(...)`; +- set tight `max_steps`, `max_llm_calls`, and `max_wall_time_s` limits. + +## Tool execution + +Tools are often the slowest part of a run. Keep them typed, narrow, and bounded. + +```python +from pydantic import BaseModel + +from afk.tools import tool + + +class FetchArgs(BaseModel): + url: str + + +@tool(args_model=FetchArgs, name="fetch_json", description="Fetch JSON from a URL.") +async def fetch_json(args: FetchArgs) -> dict: + # Use your HTTP client here and enforce its own timeout. + return {"url": args.url, "status": "ok"} +``` + +Tool guidance: + +- validate inputs with Pydantic models; +- enforce timeouts in external clients; +- return compact JSON-safe payloads; +- truncate or summarize large external responses before returning them; +- use `RunnerConfig(tool_output_max_chars=...)` as a final bound. + +## Throughput + +Use async runner APIs for services and workers. + +```python +import asyncio + +from afk.core import Runner + + +runner = Runner() + + +async def run_one(message: str) -> str: + result = await runner.run(agent, user_message=message) + return result.final_text + + +async def main() -> list[str]: + messages = [f"Process request {i}" for i in range(10)] + return await asyncio.gather(*(run_one(message) for message in messages)) +``` + +For durable background work, use task queues instead of keeping HTTP requests open. See [Task Queues](/library/task-queues). + +## Cost + +Set cost and loop limits on every production agent. + +```python +from afk.agents import Agent, FailSafeConfig + +agent = Agent( + name="bounded-agent", + model="gpt-4.1-mini", + instructions="Answer concisely and use tools only when needed.", + fail_safe=FailSafeConfig( + max_total_cost_usd=0.10, + max_steps=8, + max_llm_calls=12, + max_tool_calls=20, + max_wall_time_s=45.0, + ), +) +``` + +Read cost from the terminal result: + +```python +result = runner.run_sync(agent, user_message="Summarize this issue.") + +print(result.usage_aggregate.total_tokens) +print(result.total_cost_usd) +``` + +## Memory + +Long threads increase prompt size and storage. Use explicit thread ids and compact retained state when threads grow. + +```python +await runner.compact_thread(thread_id="customer-123") +``` + +Choose the memory backend by deployment shape: + +| Backend | Use case | +| --- | --- | +| In-memory | Tests and local experiments | +| SQLite | Single-process local or small deployments | +| Redis | Shared state across processes | +| Postgres | Persistent production storage and vector search | + +Configure backends with environment variables or pass a public `MemoryStore` implementation to `Runner(memory_store=...)`. + +## Measurement + +Measure from `AgentResult` first: + +```python +import time + +start = time.perf_counter() +result = await runner.run(agent, user_message="Analyze this task.") +elapsed_s = time.perf_counter() - start + +print(f"state={result.state}") +print(f"elapsed_s={elapsed_s:.2f}") +print(f"tokens={result.usage_aggregate.total_tokens}") +print(f"cost={result.total_cost_usd or 0:.4f}") +print(f"tools={len(result.tool_executions)}") +``` + +For production, export telemetry through [Observability](/library/observability) and track latency, token usage, tool failures, degraded runs, and cost per run. + +## Checklist + +- Use async runner APIs in servers and workers. +- Stream user-facing runs. +- Keep prompts and tool outputs compact. +- Set fail-safe limits and cost budgets. +- Compact long-running threads. +- Move durable background work into queues. +- Monitor token usage, tool count, state, and cost per run. diff --git a/docs/library/public-imports-and-function-improvement.mdx b/docs/library/public-imports-and-function-improvement.mdx index 5926aaf..7edaadc 100644 --- a/docs/library/public-imports-and-function-improvement.mdx +++ b/docs/library/public-imports-and-function-improvement.mdx @@ -1,157 +1,81 @@ --- -title: Public Imports and API Surface -description: Stable import policy and API hygiene rules. +title: Public API Rules +description: Maintainer rules for preserving AFK's public import contract. --- -AFK maintains a clear boundary between its public API surface and internal implementation details. This page explains the import policy, shows canonical import patterns organized by use case, and provides guidance for migrating from internal imports to stable public imports. +This page is for AFK maintainers. It explains how to change public exports without making downstream code or docs confusing. -Understanding this boundary is important: public imports are covered by semantic versioning guarantees, while internal imports may change without notice in any release. +For the user-facing import table, see [API Reference](/library/api-reference). -## Import policy +## Contract -AFK follows three rules for its public API surface: +The public API is the set of names exported by package-level `__init__.py` files: -**1. Prefer public package exports.** Every AFK package defines an `__all__` list in its `__init__.py` file. These exports are the public API. Import from the package level rather than reaching into internal sub-modules. +- `afk.agents` +- `afk.core` +- `afk.tools` +- `afk.llms` +- `afk.memory` +- `afk.queues` +- `afk.mcp` +- `afk.messaging` +- `afk.observability` +- `afk.evals` -```python -# Preferred: import from public package surface -from afk.agents import Agent, PolicyEngine, AgentResult -from afk.core import Runner, RunnerConfig -from afk.llms import LLMBuilder, LLMRequest, Message - -# Avoid: importing from internal sub-modules -from afk.agents.core.base import Agent # internal path -from afk.core.runner.api import RunnerAPIMixin # internal mixin -from afk.llms.builder import LLMBuilder # internal module -``` - -**Why this matters:** Internal module paths may be reorganized between releases (sub-modules renamed, code moved between files). The package-level `__init__.py` exports are the stable contract. If you import from the package level, your code survives internal refactors without changes. - -**2. Avoid importing private or internal modules.** Any module with a leading underscore (`_helpers.py`), or any path containing `_internal` or `_private`, is not part of the public API. These modules exist to support the framework's internal implementation and may change without notice. +Public docs and examples should import from these package surfaces. They should not use `src.afk` imports or deep implementation modules such as `afk.core.runner.api`. -**3. Keep interfaces contract-first and explicit.** When extending AFK (implementing custom tools, assertions, interaction providers, etc.), implement the documented protocol interfaces rather than subclassing internal classes. Protocol-based contracts are stable; internal class hierarchies are not. +## Rules for maintainers -## Canonical import patterns by use case +1. If a downstream user should import a symbol, export it from the package-level `__init__.py`. +2. If a symbol is not exported, do not use it in builder docs or examples. +3. Keep `Agent` and `Runner` separate: `Agent` comes from `afk.agents`; `Runner` comes from `afk.core`. +4. Prefer protocols, dataclasses, Pydantic models, and explicit error classes for public contracts. +5. When removing or renaming a public symbol, update migration docs and tests in the same change. +6. When changing a public constructor, update [API Reference](/library/api-reference), [Configuration Reference](/library/configuration-reference), examples, and generated agent-facing docs. -### Agent definition and execution +## Preferred examples ```python -from afk.agents import Agent, BaseAgent, ChatAgent -from afk.agents import AgentResult, AgentRunEvent, AgentRunHandle, AgentState -from afk.agents import FailSafeConfig, UsageAggregate +from afk.agents import Agent, FailSafeConfig from afk.core import Runner, RunnerConfig +from afk.tools import ToolContext, tool ``` -### Policy and security - -```python -from afk.agents import PolicyEngine, PolicyRule, PolicyEvent, PolicyDecision -from afk.agents import ApprovalRequest, ApprovalDecision -from afk.agents import UserInputRequest, UserInputDecision -from afk.tools.security import SandboxProfile, SandboxProfileProvider, SecretScopeProvider -``` - -### Tool development - -```python -from afk.tools import tool, ToolResult, ToolContext -``` - -### LLM client (direct usage) - -```python -from afk.llms import LLMBuilder, LLMClient, LLMConfig, LLMSettings -from afk.llms import LLMRequest, LLMResponse, Message, ToolCall, Usage -from afk.llms import RetryPolicy, TimeoutPolicy, CircuitBreakerPolicy -from afk.llms import PROFILES -``` - -### Delegation and subagents - -```python -from afk.agents import DelegationPlan, DelegationNode, DelegationEdge -from afk.agents import RetryPolicy, JoinPolicy, DelegationResult -from afk.agents import SubagentExecutionRecord, ToolExecutionRecord -``` - -### A2A protocol - -```python -from afk.agents import InternalA2AProtocol, A2AServiceHost -from afk.agents import AgentInvocationRequest, AgentInvocationResponse -from afk.agents import JWTA2AAuthProvider, APIKeyA2AAuthProvider -``` - -### Eval suite - ```python -from afk.evals import run_suite, EvalBudget -from afk.evals.models import EvalCase, EvalSuiteConfig, EvalSuiteResult -from afk.evals.models import EvalCaseResult, EvalAssertionResult -from afk.evals.reporting import write_suite_report_json, suite_report_payload -from afk.evals.golden import write_golden_trace, compare_event_types +from afk.llms import LLMBuilder, RetryPolicy, TimeoutPolicy +from afk.memory import InMemoryMemoryStore, SQLiteMemoryStore +from afk.queues import InMemoryTaskQueue, TaskWorker ``` -### Observability +## Imports to avoid in public docs -```python -from afk.observability.models import RunMetrics -from afk.observability.contracts import SPAN_AGENT_RUN, SPAN_AGENT_LLM_CALL -from afk.observability.exporters.console import ConsoleRunMetricsExporter -from afk.observability.projectors import project_run_metrics_from_result -``` - -### Interaction providers - -```python -from afk.core import InteractionProvider, HeadlessInteractionProvider -from afk.core import InMemoryInteractiveProvider -``` +| Avoid | Prefer | +| --- | --- | +| `src.afk.agents.Agent` | `afk.agents.Agent` | +| `afk.agents.core.base.Agent` | `afk.agents.Agent` | +| `afk.core.runner.api.RunnerAPIMixin` | `afk.core.Runner` | +| `afk.tools.core.decorator.tool` | `afk.tools.tool` | +| `afk.llms.builder.LLMBuilder` | `afk.llms.LLMBuilder` | -### Streaming +Deep imports are acceptable in internal tests only when the test is specifically covering an internal unit. Integration tests and examples should exercise the public surface. -```python -from afk.core import AgentStreamHandle, AgentStreamEvent -``` +## Change checklist -### Memory +Before merging a public API change: -```python -from afk.memory import MemoryStore, RetentionPolicy, StateRetentionPolicy -from afk.memory import compact_thread_memory, MemoryCompactionResult -``` +- Update the relevant package `__all__`. +- Add or update tests that import through the public package. +- Update user-facing docs if a builder would see the changed behavior. +- Update maintainer docs if an invariant or subsystem boundary changed. +- Run `PYTHONPATH=src pytest -q` or targeted tests for the affected subsystem. +- Regenerate agent-facing docs with `./scripts/build_agentic_ai_assets.sh` when docs, examples, skill metadata, or navigation changes. -### Errors +## Search commands -```python -from afk.agents import ( - AgentError, - AgentExecutionError, - AgentCancelledError, - AgentInterruptedError, - AgentLoopLimitError, - AgentBudgetExceededError, - SubagentRoutingError, - SubagentExecutionError, -) -from afk.llms import LLMError, LLMTimeoutError, LLMRetryableError +```bash +rg -n "src\\.afk|import src\\.afk" docs README.md CONTRIBUTING.md ENV_VARS.md skills +rg -n "from afk\\.agents import Runner" docs README.md CONTRIBUTING.md ENV_VARS.md skills +rg -n "from afk\\.[a-z_]+\\.[a-z_]+\\." docs README.md CONTRIBUTING.md ENV_VARS.md skills ``` -## Migration guide - -If you have existing code that imports from internal module paths, use this table to find the correct public import: - -| Internal Import (avoid) | Public Import (use instead) | -| --- | --- | -| `from afk.agents.core.base import Agent` | `from afk.agents import Agent` | -| `from afk.agents.types.result import AgentResult` | `from afk.agents import AgentResult` | -| `from afk.agents.policy.engine import PolicyEngine` | `from afk.agents import PolicyEngine` | -| `from afk.core.runner.api import RunnerAPIMixin` | `from afk.core import Runner` | -| `from afk.core.runner.types import RunnerConfig` | `from afk.core import RunnerConfig` | -| `from afk.llms.builder import LLMBuilder` | `from afk.llms import LLMBuilder` | -| `from afk.llms.types import LLMRequest` | `from afk.llms import LLMRequest` | -| `from afk.evals.suite import run_suite` | `from afk.evals import run_suite` | -| `from afk.evals.budgets import EvalBudget` | `from afk.evals import EvalBudget` | -| `from afk.tools.core.base import ToolResult` | `from afk.tools import ToolResult` | - -To find all internal imports in your project, search for patterns like `from afk.*.*.` (three or more dots of depth) and check whether a shallower public import exists. +The last command intentionally finds deep imports for review. Some maintainer references may be valid, but builder docs should avoid them. diff --git a/docs/library/quickstart.mdx b/docs/library/quickstart.mdx index 9b1bade..467d4e7 100644 --- a/docs/library/quickstart.mdx +++ b/docs/library/quickstart.mdx @@ -1,25 +1,27 @@ --- title: Quickstart -description: Build an agent with tools and streaming in 5 minutes. +description: Build one AFK agent with one typed tool. --- -This guide takes you from zero to a working agent in five steps. Each step adds one new concept and is runnable on its own. +This page is the shortest useful AFK path: install the package, define an agent, attach one typed tool, and run it. - - **Prerequisites:** Python 3.10+ and an LLM API key. Set `OPENAI_API_KEY` in - your environment, or configure any provider via - [LiteLLM](https://docs.litellm.ai/docs/providers). - +## Prerequisites -## Setup +- Python 3.13+ +- An LLM provider key, such as `OPENAI_API_KEY` ```bash -pip install afk +python -m pip install afk-py +export OPENAI_API_KEY="..." ``` -## Step 1 — Minimal agent +When working from this repository instead of an installed package: -The simplest possible agent: a model, a name, and instructions. +```bash +python -m pip install -e . pytest +``` + +## 1. Define an agent ```python from afk.agents import Agent @@ -27,270 +29,99 @@ from afk.core import Runner agent = Agent( name="assistant", - model="gpt-5.2-mini", - instructions="You are a helpful assistant. Be concise.", + model="gpt-4.1-mini", + instructions="Answer directly. Keep responses under 120 words.", ) -runner = Runner() -result = runner.run_sync(agent, user_message="What is a mutex?") - -print(result.final_text) # The model's answer -print(result.state) # "completed" -``` - - - - `Agent(...)` is a **configuration object** — it describes *what* the agent - is, not how it runs. - `Runner()` is the **execution engine** — it manages the - LLM call loop and state. - `run_sync()` blocks until the run finishes. Under - the hood, it creates an async loop, sends the user message to the LLM, and - returns an `AgentResult`. - `result.final_text` always contains the model's - final response. - - -## Step 2 — Add a tool - -Give your agent a capability by defining a typed tool function. - -```python -from pydantic import BaseModel -from afk.agents import Agent -from afk.tools import tool -from afk.core import Runner - -# 1. Define the tool's argument schema -class CalcArgs(BaseModel): - expression: str - -# 2. Create the tool -@tool(args_model=CalcArgs, name="calculate", description="Evaluate a math expression.") -def calculate(args: CalcArgs) -> dict: - return {"result": eval(args.expression)} # ← simplified for demo - -# 3. Attach it to an agent -agent = Agent( - name="math-agent", - model="gpt-5.2-mini", - instructions="Use the calculate tool to solve math problems. Show your work.", - tools=[calculate], +result = Runner().run_sync( + agent, + user_message="What is a mutex?", ) -runner = Runner() -result = runner.run_sync(agent, user_message="What is 1234 * 5678?") -print(result.final_text) # "1234 × 5678 = 7,006,652" -print(len(result.tool_executions)) # 1 (one tool call was made) +print(result.final_text) +print(result.state) ``` - - 1. The runner sends the tool's JSON schema to the LLM along with the user message. - 2. The LLM decides to call the tool and returns a `ToolCall` with the function name and arguments. - 3. AFK validates the arguments against the Pydantic model, executes the function, sanitizes the output, and feeds the result back to the LLM. - 4. The LLM uses the tool result to compose its final answer. - -Tool arguments are always validated. If the LLM passes bad arguments, AFK returns the validation error to the model so it can self-correct. - - - -## Step 3 — Stream the response - -For real-time UIs, use streaming to receive text as it's generated. - - - - ```python - import asyncio - from afk.agents import Agent - from afk.core import Runner - - agent = Agent( - name="streamer", - model="gpt-5.2-mini", - instructions="Explain topics clearly and concisely.", - ) - - async def main(): - runner = Runner() - handle = await runner.run_stream( - agent, user_message="Explain containers in 3 sentences." - ) - - async for event in handle: - if event.type == "text_delta": - print(event.text_delta, end="", flush=True) - elif event.type == "completed": - print(f"\n\n[DONE] Done (state={event.result.state})") +`Agent` stores configuration. `Runner` executes the run. `AgentResult.final_text` is the assistant response. - asyncio.run(main()) - ``` +## 2. Add one typed tool - - - ```python - from afk.agents import Agent - from afk.core import Runner - - agent = Agent( - name="streamer", - model="gpt-5.2-mini", - instructions="Explain topics clearly and concisely.", - ) - - runner = Runner() - result = runner.run_sync(agent, user_message="Explain containers in 3 sentences.") - print(result.final_text) - ``` - - - - - - | Event | When it fires | Key field | | --- | --- | --- | | `text_delta` | - Incremental text from the model | `event.text_delta` | | `step_started` | New - step in the agent loop | `event.step` | | `tool_started` | A tool is about to - execute | `event.tool_name` | | `tool_completed` | A tool finished executing | - `event.success` | | `error` | Something went wrong | `event.error` | | - `completed` | Run finished | `event.result` | - - -## Step 4 — Delegate to subagents - -Build a multi-agent system where a coordinator delegates to specialists. +Tools are Python functions with Pydantic argument models. AFK turns the model into a tool schema, validates model-provided arguments, executes the function, and feeds the result back into the agent loop. ```python -import asyncio -from afk.agents import Agent -from afk.tools import tool -from afk.core import Runner from pydantic import BaseModel -# Specialist agent: code reviewer -class ReviewArgs(BaseModel): - code: str +from afk.agents import Agent, FailSafeConfig +from afk.core import Runner +from afk.tools import tool -@tool(args_model=ReviewArgs, name="lint_code", description="Check code for style issues.") -def lint_code(args: ReviewArgs) -> dict: - issues = [] - if "print(" in args.code: - issues.append("Consider using logging instead of print()") - return {"issues": issues, "passed": len(issues) == 0} -reviewer = Agent( - name="reviewer", - model="gpt-5.2-mini", - instructions="Review code for quality issues using the lint_code tool.", - tools=[lint_code], -) +class OrderArgs(BaseModel): + order_id: str -# Specialist agent: documentation writer -writer = Agent( - name="writer", - model="gpt-5.2-mini", - instructions="Write clear, concise documentation for code snippets.", -) -# Coordinator: delegates work -coordinator = Agent( - name="coordinator", - model="gpt-5.2-mini", - instructions=""" - You manage a team of specialists: - - 'reviewer' checks code quality - - 'writer' creates documentation - - Delegate tasks to the appropriate specialist. - Combine their outputs into a final response. - """, - subagents=[reviewer, writer], +@tool( + args_model=OrderArgs, + name="lookup_order", + description="Look up a customer order by id.", ) +def lookup_order(args: OrderArgs) -> dict: + return { + "order_id": args.order_id, + "status": "shipped", + "eta": "Friday", + } -async def main(): - runner = Runner() - result = await runner.run( - coordinator, - user_message="Review this code and write docs for it: def add(a, b): return a + b", - ) - print(result.final_text) - -asyncio.run(main()) -``` - - - - The coordinator sees its subagents as available tools (automatically - generated `transfer_to_reviewer`, `transfer_to_writer` tools). - When the - coordinator decides to delegate, AFK routes the task to the subagent, runs it - through a full agent loop, and returns the result to the coordinator. - Each - subagent uses its own model and instructions, and has access to its own tools. - - The coordinator can delegate to multiple subagents and combine their - results. - - -## Step 5 — Add safety limits - -Protect against runaway agents with `FailSafeConfig`. - -```python -from afk.agents import Agent, FailSafeConfig -from afk.core import Runner, RunnerConfig agent = Agent( - name="safe-agent", - model="gpt-5.2-mini", - instructions="Help users with their questions.", - tools=[calculate], + name="support-agent", + model="gpt-4.1-mini", + instructions="Use lookup_order when the user asks about an order.", + tools=[lookup_order], fail_safe=FailSafeConfig( - max_steps=10, # ← Stop after 10 agent loop iterations - max_tool_calls=5, # ← Stop after 5 tool calls - max_total_cost_usd=0.10, # ← Stop if estimated cost exceeds $0.10 - max_wall_time_s=30.0, # ← Stop after 30 seconds + max_steps=8, + max_tool_calls=4, + max_total_cost_usd=0.10, ), ) -runner = Runner( - config=RunnerConfig( - sanitize_tool_output=True, # ← Prevent prompt injection from tool output - tool_output_max_chars=8000, # ← Truncate oversized tool responses - ), +result = Runner().run_sync( + agent, + user_message="Where is order A123?", ) -result = runner.run_sync(agent, user_message="Calculate 2 + 2") print(result.final_text) +print(len(result.tool_executions)) ``` - - **Always set `max_total_cost_usd`** in production. A runaway agent loop can - consume significant API credits in minutes. Even during development, $0.50 is - a reasonable safety limit. - +## 3. Read the result -## What you just built +Common fields on `AgentResult`: -In five steps, you've covered the core AFK workflow: +| Field | Meaning | +| --- | --- | +| `final_text` | Final assistant text | +| `state` | Terminal state such as `completed`, `failed`, `cancelled`, or `degraded` | +| `run_id` | Unique id for this run | +| `thread_id` | Conversation/thread id used by memory | +| `tool_executions` | Ordered records for tool calls | +| `subagent_executions` | Ordered records for subagent calls | +| `usage_aggregate` | Aggregated token usage | +| `total_cost_usd` | Estimated total run cost when available | -| Step | Concept | What you learned | -| ---- | -------------- | ------------------------------- | -| 1 | Agent + Runner | Define an agent and execute it | -| 2 | Tools | Give agents typed capabilities | -| 3 | Streaming | Real-time text output for UIs | -| 4 | Multi-agent | Coordinate specialist subagents | -| 5 | Safety | Protect against runaway agents | - -## Next steps +## 4. Keep going - - Deep-dive tutorial covering memory, policies, observability, and more. - - - Production playbook: common patterns, anti-patterns, and deployment - guidance. + + Add streaming, memory, and safety controls. - Runnable examples for every major AFK feature. + Find complete snippets for common scenarios. + + + Understand the agent configuration object. - - Deep dive into agents, tools, runner, memory, and more. + + Understand sync, async, and streaming execution. diff --git a/docs/library/run-event-contract.mdx b/docs/library/run-event-contract.mdx index 196e285..00fa03a 100644 --- a/docs/library/run-event-contract.mdx +++ b/docs/library/run-event-contract.mdx @@ -74,7 +74,7 @@ The primary way to consume events is through the run handle's `events` async ite from afk.agents import Agent from afk.core import Runner -agent = Agent(name="demo", model="gpt-5.2-mini", instructions="Be helpful.") +agent = Agent(name="demo", model="gpt-4.1-mini", instructions="Be helpful.") runner = Runner() handle = await runner.run_handle(agent, user_message="Hello") diff --git a/docs/library/security-model.mdx b/docs/library/security-model.mdx index 2b54f25..d0b7304 100644 --- a/docs/library/security-model.mdx +++ b/docs/library/security-model.mdx @@ -96,14 +96,18 @@ flowchart TB External communication requires authentication and per-caller authorization. ```python - from afk.a2a import TokenAuthProvider + from afk.agents import A2AServiceHost, APIKeyA2AAuthProvider - auth = TokenAuthProvider( - valid_tokens={"system-a": "token-abc"}, - allowed_agents={"system-a": ["analyzer"]}, + auth = APIKeyA2AAuthProvider( + keys={"system-a": "token-abc"}, + server_secret="hmac-secret-for-key-hashing", ) - server = A2AServer(agents={"analyzer": agent}, auth_provider=auth) + server = A2AServiceHost( + agents={"analyzer": agent}, + runner_factory=lambda: Runner(), + auth_provider=auth, + ) ``` diff --git a/docs/library/snippets/01_minimal_chat_agent.mdx b/docs/library/snippets/01_minimal_chat_agent.mdx index 18f6d27..dc2fd0a 100644 --- a/docs/library/snippets/01_minimal_chat_agent.mdx +++ b/docs/library/snippets/01_minimal_chat_agent.mdx @@ -11,7 +11,7 @@ If you are new to AFK, start here. Every other example builds on this foundation from afk.agents import Agent from afk.core import Runner -agent = Agent(name="chat", model="gpt-5.2-mini", instructions="Answer directly with concrete detail.") +agent = Agent(name="chat", model="gpt-4.1-mini", instructions="Answer directly with concrete detail.") runner = Runner() result = runner.run_sync(agent, user_message="Define error budget in SRE.") print(result.final_text) @@ -43,6 +43,6 @@ The `AgentResult` dataclass returned by `run_sync` includes: ## Expected behavior -When you run this example, the runner makes a single LLM call to `gpt-5.2-mini` with the system instructions and user message. Since no tools are registered, the model responds with text only. The run completes in one step with `state="completed"`, and `final_text` contains a concise definition of error budgets in SRE. +When you run this example, the runner makes a single LLM call to `gpt-4.1-mini` with the system instructions and user message. Since no tools are registered, the model responds with text only. The run completes in one step with `state="completed"`, and `final_text` contains a concise definition of error budgets in SRE. No network calls, API keys, or external services are required beyond access to the specified LLM provider (configured via environment variables like `OPENAI_API_KEY`). diff --git a/docs/library/snippets/02_policy_with_hitl.mdx b/docs/library/snippets/02_policy_with_hitl.mdx index 7b8ad8d..320973a 100644 --- a/docs/library/snippets/02_policy_with_hitl.mdx +++ b/docs/library/snippets/02_policy_with_hitl.mdx @@ -29,7 +29,7 @@ policy = PolicyEngine(rules=[ agent = Agent( name="change-manager", - model="gpt-5.2-mini", + model="gpt-4.1-mini", instructions="You manage infrastructure changes. Always use available tools.", ) diff --git a/docs/library/snippets/03_subagents_with_router.mdx b/docs/library/snippets/03_subagents_with_router.mdx index b155036..cf44d02 100644 --- a/docs/library/snippets/03_subagents_with_router.mdx +++ b/docs/library/snippets/03_subagents_with_router.mdx @@ -30,24 +30,24 @@ from afk.core import Runner # Define specialist subagents triage = Agent( name="triage", - model="gpt-5.2-mini", + model="gpt-4.1-mini", instructions="Classify incident severity as SEV1, SEV2, SEV3, or SEV4 based on the description.", ) analysis = Agent( name="analysis", - model="gpt-5.2-mini", + model="gpt-4.1-mini", instructions="Identify the most likely root causes for the described incident.", ) comms = Agent( name="comms", - model="gpt-5.2-mini", + model="gpt-4.1-mini", instructions="Draft a concise stakeholder update email summarizing the incident and current status.", ) # Define the coordinator agent lead = Agent( name="lead", - model="gpt-5.2-mini", + model="gpt-4.1-mini", instructions="Delegate to specialists and synthesize their outputs into a final response.", subagents=[triage, analysis, comms], ) @@ -99,7 +99,7 @@ from afk.agents import FailSafeConfig lead = Agent( name="lead", - model="gpt-5.2-mini", + model="gpt-4.1-mini", instructions="Delegate to specialists. If any specialist fails, work with available results.", subagents=[triage, analysis, comms], fail_safe=FailSafeConfig( diff --git a/docs/library/snippets/04_resume_and_compact.mdx b/docs/library/snippets/04_resume_and_compact.mdx index f511e49..b78669a 100644 --- a/docs/library/snippets/04_resume_and_compact.mdx +++ b/docs/library/snippets/04_resume_and_compact.mdx @@ -18,7 +18,7 @@ from afk.core import Runner, RunnerConfig agent = Agent( name="research-bot", - model="gpt-5.2-mini", + model="gpt-4.1-mini", instructions="You help users research topics thoroughly.", ) diff --git a/docs/library/snippets/05_direct_llm_structured_output.mdx b/docs/library/snippets/05_direct_llm_structured_output.mdx index 6769638..6b798a6 100644 --- a/docs/library/snippets/05_direct_llm_structured_output.mdx +++ b/docs/library/snippets/05_direct_llm_structured_output.mdx @@ -20,7 +20,7 @@ class Summary(BaseModel): bullets: list[str] # Build an LLM client using the fluent builder -client = LLMBuilder().provider("openai").model("gpt-5.2-mini").profile("production").build() +client = LLMBuilder().provider("openai").model("gpt-4.1-mini").profile("production").build() # Make a structured request resp = await client.chat( @@ -39,10 +39,8 @@ print(resp.text) # The raw text response client = ( LLMBuilder() .provider("openai") # Which LLM provider to use - .model("gpt-5.2-mini") # Which model + .model("gpt-4.1-mini") # Which model .profile("production") # Apply a preset profile (retry, timeout, etc.) - .temperature(0.0) # Override sampling temperature - .max_tokens(1000) # Set max response tokens .build() # Return the configured LLMClient ) ``` @@ -56,12 +54,15 @@ Available builder methods: | `.provider(name)` | Set the LLM provider (`"openai"`, `"litellm"`, `"anthropic_agent"`). | | `.model(name)` | Set the model identifier. | | `.profile(name)` | Apply a named configuration profile (`"production"`, `"development"`, etc.). | -| `.temperature(value)` | Set sampling temperature (0.0-2.0). | -| `.max_tokens(value)` | Set maximum response tokens. | -| `.top_p(value)` | Set nucleus sampling parameter. | -| `.timeout(seconds)` | Set request timeout. | +| `.settings(settings)` | Replace the loaded `LLMSettings`. | +| `.with_middlewares(stack)` | Attach chat, stream, or embedding middleware. | +| `.with_observers(observers)` | Attach LLM lifecycle observers. | +| `.with_cache(cache_backend)` | Attach a cache backend instance or registered backend id. | +| `.with_router(router)` | Attach a router instance or registered router id. | | `.build()` | Construct and return the `LLMClient`. | +Sampling controls are request fields, not builder methods. Set them on `LLMRequest`, for example `LLMRequest(..., temperature=0.0, max_tokens=1000)`. + ## Structured output with Pydantic When you pass `response_model=YourModel` to `client.chat()`, the client instructs the LLM to return output that conforms to the model's JSON schema. The response is parsed and validated against the Pydantic model: diff --git a/docs/library/snippets/06_tool_registry_security.mdx b/docs/library/snippets/06_tool_registry_security.mdx index e463227..4e908e7 100644 --- a/docs/library/snippets/06_tool_registry_security.mdx +++ b/docs/library/snippets/06_tool_registry_security.mdx @@ -77,7 +77,7 @@ policy = PolicyEngine(rules=[ agent = Agent( name="resource-manager", - model="gpt-5.2-mini", + model="gpt-4.1-mini", instructions="Manage resources using the available tools. Always look up a resource before modifying it.", tools=[get_resource, delete_resource], fail_safe=FailSafeConfig( diff --git a/docs/library/snippets/07_tool_hooks_and_middleware.mdx b/docs/library/snippets/07_tool_hooks_and_middleware.mdx index 4281474..bd55d42 100644 --- a/docs/library/snippets/07_tool_hooks_and_middleware.mdx +++ b/docs/library/snippets/07_tool_hooks_and_middleware.mdx @@ -177,7 +177,7 @@ async def add_request_metadata(call_next, req: LLMRequest) -> LLMResponse: client = ( LLMBuilder() .provider("openai") - .model("gpt-5.2-mini") + .model("gpt-4.1-mini") .profile("production") .with_middlewares(MiddlewareStack( chat=[add_request_metadata], @@ -198,6 +198,57 @@ client = ( Each middleware receives `call_next` (the next middleware or transport in the chain) and the request object. It can modify the request before calling `call_next`, modify the response after, or short-circuit entirely by returning a response without calling `call_next`. +## Built-in LLM middleware + +AFK ships with pre-built middleware for common patterns: + +### Timeout middleware + +Apply per-request timeouts to prevent runaway calls: + +```python +from afk.llms.middleware.timeout import ( + TimeoutMiddleware, + EmbedTimeoutMiddleware, + StreamTimeoutMiddleware, + TimeoutConfig, +) +from afk.llms.middleware import MiddlewareStack + +# Configure timeouts +config = TimeoutConfig( + default_timeout_s=30.0, + chat_timeout_s=60.0, + embed_timeout_s=15.0, + stream_timeout_s=45.0, +) + +# Add to middleware stack +stack = MiddlewareStack( + chat=[TimeoutMiddleware(config)], + embed=[EmbedTimeoutMiddleware(config)], + stream=[StreamTimeoutMiddleware(config)], +) + +# Build client +client = ( + LLMBuilder() + .provider("openai") + .model("gpt-4.1-mini") + .with_middlewares(stack) + .build() +) +``` + +The timeout middleware respects `TimeoutPolicy` from the request if provided: +```python +req = LLMRequest( + model="gpt-4.1-mini", + messages=[...], + timeout_policy=TimeoutPolicy(request_timeout_s=45.0), # Override config +) +``` + ## When to use each layer | Layer | Scope | Use for | diff --git a/docs/library/snippets/08_prebuilt_runtime_tools.mdx b/docs/library/snippets/08_prebuilt_runtime_tools.mdx index 6dcc892..774075e 100644 --- a/docs/library/snippets/08_prebuilt_runtime_tools.mdx +++ b/docs/library/snippets/08_prebuilt_runtime_tools.mdx @@ -22,7 +22,7 @@ runtime_tools = build_runtime_tools(root_dir=Path("./workspace")) agent = Agent( name="file-assistant", - model="gpt-5.2-mini", + model="gpt-4.1-mini", instructions=( "You help users explore and read files in the workspace directory. " "Use list_directory to browse the directory structure and read_file " @@ -97,7 +97,7 @@ policy = PolicyEngine( agent = Agent( name="ops-assistant", - model="gpt-5.2-mini", + model="gpt-4.1-mini", instructions="Use approved runtime tools only. Never read sensitive configuration without approval.", tools=build_runtime_tools(root_dir=Path("./project")), ) @@ -137,7 +137,7 @@ all_tools = build_runtime_tools(root_dir=Path("./workspace")) + [grep_files] agent = Agent( name="dev-assistant", - model="gpt-5.2-mini", + model="gpt-4.1-mini", instructions="Help developers explore and search the codebase.", tools=all_tools, ) diff --git a/docs/library/snippets/09_system_prompt_loader.mdx b/docs/library/snippets/09_system_prompt_loader.mdx index d972a71..7bdadf9 100644 --- a/docs/library/snippets/09_system_prompt_loader.mdx +++ b/docs/library/snippets/09_system_prompt_loader.mdx @@ -35,14 +35,14 @@ from afk.agents import Agent # Option 1: Inline instructions (highest priority) agent = Agent( name="ChatAgent", - model="gpt-5.2-mini", + model="gpt-4.1-mini", instructions="Answer customer questions concisely.", ) # Option 2: Explicit instruction file agent = Agent( name="ChatAgent", - model="gpt-5.2-mini", + model="gpt-4.1-mini", instruction_file="chat_agent_system.md", # Loaded from prompts_dir prompts_dir=".agents/prompt", ) @@ -51,7 +51,7 @@ agent = Agent( # Loads .agents/prompt/CHAT_AGENT.md automatically agent = Agent( name="ChatAgent", - model="gpt-5.2-mini", + model="gpt-4.1-mini", prompts_dir=".agents/prompt", ) ``` @@ -79,14 +79,14 @@ The prompts directory is resolved through its own priority chain: ```python # Explicit -agent = Agent(name="Bot", model="gpt-5.2-mini", prompts_dir="/opt/prompts") +agent = Agent(name="Bot", model="gpt-4.1-mini", prompts_dir="/opt/prompts") # Environment variable # export AFK_AGENT_PROMPTS_DIR=/opt/prompts -agent = Agent(name="Bot", model="gpt-5.2-mini") +agent = Agent(name="Bot", model="gpt-4.1-mini") # Default: .agents/prompt/ -agent = Agent(name="Bot", model="gpt-5.2-mini") +agent = Agent(name="Bot", model="gpt-4.1-mini") ``` ## Jinja2 templating @@ -117,7 +117,7 @@ from afk.core import Runner, RunnerConfig agent = Agent( name="SupportAgent", - model="gpt-5.2-mini", + model="gpt-4.1-mini", prompts_dir=".agents/prompt", ) @@ -166,7 +166,7 @@ The prompt loader enforces strict path containment. The resolved prompt file pat # This would raise PromptAccessError: agent = Agent( name="Agent", - model="gpt-5.2-mini", + model="gpt-4.1-mini", instruction_file="../../etc/passwd", # Escapes prompts root prompts_dir=".agents/prompt", ) diff --git a/docs/library/snippets/10_streaming_chat_with_memory.mdx b/docs/library/snippets/10_streaming_chat_with_memory.mdx index 8cdbe4d..8343459 100644 --- a/docs/library/snippets/10_streaming_chat_with_memory.mdx +++ b/docs/library/snippets/10_streaming_chat_with_memory.mdx @@ -16,7 +16,7 @@ from afk.core import Runner, RunnerConfig agent = Agent( name="chat-assistant", - model="gpt-5.2-mini", + model="gpt-4.1-mini", instructions=""" You are a helpful assistant. Remember context from earlier in the conversation. Be concise but thorough. If the user refers to something from a previous message, diff --git a/docs/library/snippets/11_cost_monitoring.mdx b/docs/library/snippets/11_cost_monitoring.mdx index 660ceb9..367354b 100644 --- a/docs/library/snippets/11_cost_monitoring.mdx +++ b/docs/library/snippets/11_cost_monitoring.mdx @@ -16,7 +16,7 @@ from afk.agents import Agent, FailSafeConfig agent = Agent( name="budget-agent", - model="gpt-5.2-mini", + model="gpt-4.1-mini", instructions="Be helpful and concise.", fail_safe=FailSafeConfig( max_total_cost_usd=0.50, # Hard cost ceiling @@ -40,12 +40,11 @@ runner = Runner() result = runner.run_sync(agent, user_message="Analyze this dataset...") # Access usage statistics -usage = result.usage +usage = result.usage_aggregate print(f"Input tokens: {usage.input_tokens}") print(f"Output tokens: {usage.output_tokens}") print(f"Total tokens: {usage.total_tokens}") -print(f"Estimated cost: ${usage.estimated_cost_usd:.4f}") -print(f"LLM calls: {usage.llm_call_count}") +print(f"Estimated cost: ${result.total_cost_usd or 0:.4f}") print(f"Tool calls: {len(result.tool_executions)}") ``` @@ -60,7 +59,7 @@ from afk.core import Runner agent = Agent( name="analyst", - model="gpt-5.2", + model="gpt-4.1", instructions="Provide detailed analysis.", fail_safe=FailSafeConfig( max_total_cost_usd=1.00, @@ -72,7 +71,7 @@ agent = Agent( async def monitor_cost(): runner = Runner() handle = await runner.run_stream( - agent, user_message="Provide a comprehensive analysis of Python async patterns" + agent, user_message="Compare Python async patterns for service code" ) step_count = 0 @@ -85,12 +84,11 @@ async def monitor_cost(): case "tool_completed": print(f"\n [STEP] Step {step_count} | Tool: {event.tool_name}") case "completed" if event.result is not None: - usage = event.result.usage + usage = event.result.usage_aggregate print(f"\n\n--- Cost Summary ---") print(f"State: {event.result.state}") print(f"Tokens: {usage.total_tokens}") - print(f"Cost: ${usage.estimated_cost_usd:.4f}") - print(f"LLM calls: {usage.llm_call_count}") + print(f"Cost: ${event.result.total_cost_usd or 0:.4f}") print(f"Tools: {len(event.result.tool_executions)}") asyncio.run(monitor_cost()) @@ -116,7 +114,7 @@ async def batch_process(items: list[str], budget_usd: float): remaining = budget_usd - cumulative_cost agent = Agent( name="batch-processor", - model="gpt-5.2-mini", + model="gpt-4.1-mini", instructions="Process the item concisely.", fail_safe=FailSafeConfig( max_total_cost_usd=min(remaining, 0.10), # Per-item cap @@ -125,22 +123,23 @@ async def batch_process(items: list[str], budget_usd: float): ) result = await runner.run(agent, user_message=item) - cumulative_cost += result.usage.estimated_cost_usd + item_cost = result.total_cost_usd or 0.0 + cumulative_cost += item_cost results.append(result) - print(f" [OK] {item[:40]}... (${result.usage.estimated_cost_usd:.4f})") + print(f" [OK] {item[:40]}... (${item_cost:.4f})") print(f"\nTotal: {len(results)} items, ${cumulative_cost:.4f}") return results ``` -## Production recommendations +## Operating recommendations 1. **Always set `max_total_cost_usd`** — even generous limits prevent runaway costs 2. **Layer defenses** — combine cost limits with `max_llm_calls`, `max_steps`, and `max_wall_time_s` 3. **Use telemetry for dashboards** — export metrics to monitor cost trends over time 4. **Set per-item budgets in batches** — prevent one expensive item from consuming the entire budget -5. **Use cheaper models for iteration** — use `gpt-5.2-mini` for development, `gpt-5.2` for production +5. **Choose models by task** — use smaller models for routine work and reserve larger models for requests that need them ## What to read next diff --git a/docs/library/snippets/12_mcp_client_integration.mdx b/docs/library/snippets/12_mcp_client_integration.mdx index 9118683..fac0039 100644 --- a/docs/library/snippets/12_mcp_client_integration.mdx +++ b/docs/library/snippets/12_mcp_client_integration.mdx @@ -31,7 +31,7 @@ async def main(): # 3. Attach MCP tools to an agent — they work like local tools agent = Agent( name="mcp-assistant", - model="gpt-5.2-mini", + model="gpt-4.1-mini", instructions=""" Use the available tools to help the user. Always explain what tool you're using and why. @@ -70,7 +70,7 @@ from afk.agents import Agent # The agent connects to MCP servers automatically during startup agent = Agent( name="connected-agent", - model="gpt-5.2-mini", + model="gpt-4.1-mini", instructions="Use available tools to help the user.", mcp_servers=[ "https://tools.example.com:3001", # Simple URL @@ -110,7 +110,7 @@ async def build_agent(): # Combine local + external tools agent = Agent( name="hybrid-agent", - model="gpt-5.2-mini", + model="gpt-4.1-mini", instructions="Use search tools for research and summarize for concise output.", tools=[summarize] + mcp_tools, # ← Mix freely ) diff --git a/docs/library/snippets/13_multi_model_fallback.mdx b/docs/library/snippets/13_multi_model_fallback.mdx index 82595a1..9de71ef 100644 --- a/docs/library/snippets/13_multi_model_fallback.mdx +++ b/docs/library/snippets/13_multi_model_fallback.mdx @@ -14,13 +14,13 @@ from afk.agents import Agent, FailSafeConfig agent = Agent( name="resilient-agent", - model="gpt-5.2", # Primary model + model="gpt-4.1", # Primary model instructions="Be helpful and thorough.", fail_safe=FailSafeConfig( # Fallback chain: try these models in order if the primary fails fallback_model_chain=[ - "gpt-5.2-mini", # First fallback: cheaper, faster - "gpt-5.2-nano", # Last resort: fastest, cheapest + "gpt-4.1-mini", # First fallback: cheaper, faster + "gpt-4.1-nano", # Last resort: fastest, cheapest ], # When LLM calls fail, retry then degrade @@ -32,11 +32,11 @@ agent = Agent( ) ``` -When `gpt-5.2` fails (timeout, rate limit, outage): +When `gpt-4.1` fails (timeout, rate limit, outage): 1. AFK retries with the primary model (controlled by retry policy) -2. If retries exhaust, it falls through to `gpt-5.2-mini` -3. If that also fails, it tries `gpt-5.2-nano` +2. If retries exhaust, it falls through to `gpt-4.1-mini` +3. If that also fails, it tries `gpt-4.1-nano` 4. If all models fail, the `llm_failure_policy` determines the outcome ## Cost-optimized fallback @@ -50,13 +50,13 @@ from afk.core import Runner # Start cheap, escalate if quality is insufficient simple_agent = Agent( name="classifier", - model="gpt-5.2-nano", # Start with cheapest + model="gpt-4.1-nano", # Start with cheapest instructions=""" Classify the support ticket. Output exactly one label: billing, technical, account, other. """, fail_safe=FailSafeConfig( - fallback_model_chain=["gpt-5.2-mini", "gpt-5.2"], + fallback_model_chain=["gpt-4.1-mini", "gpt-4.1"], max_total_cost_usd=0.05, ), ) @@ -64,13 +64,13 @@ simple_agent = Agent( # Complex tasks get the big model with fallbacks analysis_agent = Agent( name="analyst", - model="gpt-5.2", # Start with most capable + model="gpt-4.1", # Start with most capable instructions=""" Provide detailed technical analysis with code examples. Be thorough and precise. """, fail_safe=FailSafeConfig( - fallback_model_chain=["gpt-5.2-mini"], + fallback_model_chain=["gpt-4.1-mini"], llm_failure_policy="retry_then_degrade", max_total_cost_usd=2.00, ), @@ -78,13 +78,13 @@ analysis_agent = Agent( runner = Runner() -# Simple task → cheap model handles it +# Simple task -> cheap model handles it r1 = runner.run_sync(simple_agent, user_message="I can't log in") -print(f"Classification: {r1.final_text} (${r1.usage.estimated_cost_usd:.4f})") +print(f"Classification: {r1.final_text} (${r1.total_cost_usd or 0:.4f})") -# Complex task → powerful model with safety net +# Complex task -> powerful model with safety net r2 = runner.run_sync(analysis_agent, user_message="Analyze Python's asyncio event loop") -print(f"Analysis: {r2.final_text[:100]}... (${r2.usage.estimated_cost_usd:.4f})") +print(f"Analysis: {r2.final_text[:100]}... (${r2.total_cost_usd or 0:.4f})") ``` ## Circuit breaker integration @@ -94,10 +94,10 @@ AFK's built-in circuit breaker works with fallback chains. When a model triggers ```python agent = Agent( name="breaker-demo", - model="gpt-5.2", + model="gpt-4.1", instructions="...", fail_safe=FailSafeConfig( - fallback_model_chain=["gpt-5.2-mini", "gpt-5.2-nano"], + fallback_model_chain=["gpt-4.1-mini", "gpt-4.1-nano"], # Circuit breaker settings breaker_failure_threshold=5, # Open after 5 consecutive failures @@ -112,10 +112,10 @@ agent = Agent( ```mermaid flowchart LR - A["gpt-5.2 fails 5x"] --> B["Circuit opens"] - B --> C["Skip to gpt-5.2-mini"] + A["gpt-4.1 fails 5x"] --> B["Circuit opens"] + B --> C["Skip to gpt-4.1-mini"] C --> D["30s cooldown"] - D --> E["gpt-5.2 retried"] + D --> E["gpt-4.1 retried"] E -->|"succeeds"| F["Circuit closes"] E -->|"fails again"| B ``` @@ -130,27 +130,27 @@ from afk.agents import Agent, FailSafeConfig # Cheap model for simple classification router = Agent( name="router", - model="gpt-5.2-nano", + model="gpt-4.1-nano", instructions="Route to the correct specialist.", - fail_safe=FailSafeConfig(fallback_model_chain=["gpt-5.2-mini"]), + fail_safe=FailSafeConfig(fallback_model_chain=["gpt-4.1-mini"]), subagents=[ # Powerful model for complex analysis Agent( name="analyst", - model="gpt-5.2", + model="gpt-4.1", instructions="Provide deep technical analysis.", fail_safe=FailSafeConfig( - fallback_model_chain=["gpt-5.2-mini"], + fallback_model_chain=["gpt-4.1-mini"], max_total_cost_usd=1.00, ), ), # Mid-tier model for summarization Agent( name="summarizer", - model="gpt-5.2-mini", + model="gpt-4.1-mini", instructions="Summarize findings concisely.", fail_safe=FailSafeConfig( - fallback_model_chain=["gpt-5.2-nano"], + fallback_model_chain=["gpt-4.1-nano"], max_total_cost_usd=0.25, ), ), @@ -160,26 +160,28 @@ router = Agent( ## Inspecting which model was used -After a run, check the usage to see which model handled the request: +After a run, check the result metadata and usage aggregate: ```python result = runner.run_sync(agent, user_message="Analyze this...") -# Usage aggregate includes model info print(f"State: {result.state}") -print(f"Total cost: ${result.usage.estimated_cost_usd:.4f}") -print(f"LLM calls: {result.usage.llm_call_count}") +print(f"Requested model: {result.requested_model}") +print(f"Normalized model: {result.normalized_model}") +print(f"Provider: {result.provider_adapter}") +print(f"Total tokens: {result.usage_aggregate.total_tokens}") +print(f"Total cost: ${result.total_cost_usd or 0:.4f}") ``` ## Recommendations | Scenario | Primary Model | Fallback Chain | | ------------------------ | -------------- | ------------------------------- | -| **Classification** | `gpt-5.2-nano` | `gpt-5.2-mini` | -| **General chat** | `gpt-5.2-mini` | `gpt-5.2-nano` | -| **Complex analysis** | `gpt-5.2` | `gpt-5.2-mini` → `gpt-5.2-nano` | -| **Code generation** | `gpt-5.2` | `gpt-5.2-mini` | -| **Cost-sensitive batch** | `gpt-5.2-nano` | _(none)_ | +| **Classification** | `gpt-4.1-nano` | `gpt-4.1-mini` | +| **General chat** | `gpt-4.1-mini` | `gpt-4.1-nano` | +| **Complex analysis** | `gpt-4.1` | `gpt-4.1-mini` → `gpt-4.1-nano` | +| **Code generation** | `gpt-4.1` | `gpt-4.1-mini` | +| **Cost-sensitive batch** | `gpt-4.1-nano` | _(none)_ | ## What to read next diff --git a/docs/library/snippets/14_production_client.mdx b/docs/library/snippets/14_production_client.mdx new file mode 100644 index 0000000..68cb010 --- /dev/null +++ b/docs/library/snippets/14_production_client.mdx @@ -0,0 +1,231 @@ +--- +title: "14: Client Timeouts and Redis Pooling" +description: Configure LLM client timeouts and Redis connection pooling. +--- + +## What this snippet demonstrates + +This snippet shows how to configure: +1. **Timeout middleware** to bound slow provider calls +2. **Redis connection pooling** for shared cache or memory connections +3. **Shutdown handling** so runners and Redis pools close cleanly + +## Timeout middleware + +Apply per-request timeouts to prevent runaway LLM calls: + +```python +import asyncio +from afk.llms import LLMBuilder, LLMRequest +from afk.llms.middleware import MiddlewareStack +from afk.llms.middleware.timeout import ( + TimeoutMiddleware, + EmbedTimeoutMiddleware, + StreamTimeoutMiddleware, + TimeoutConfig, +) + +config = TimeoutConfig( + default_timeout_s=30.0, + chat_timeout_s=60.0, + embed_timeout_s=15.0, + stream_timeout_s=45.0, +) + +stack = MiddlewareStack( + chat=[TimeoutMiddleware(config)], + embed=[EmbedTimeoutMiddleware(config)], + stream=[StreamTimeoutMiddleware(config)], +) + +production_client = ( + LLMBuilder() + .provider("openai") + .model("gpt-4.1-mini") + .profile("production") + .with_middlewares(stack) + .build() +) +``` + +### Per-request timeout override + +```python +from afk.llms import TimeoutPolicy + +req = LLMRequest( + model="gpt-4.1-mini", + messages=[...], + timeout_policy=TimeoutPolicy(request_timeout_s=120.0), # Override default +) + +response = await production_client.chat(req) +``` + +## Redis connection pooling + +For Redis deployments, use connection pooling instead of creating a new client per request: + +```python +from afk.llms.cache.redis_pool import ( + get_redis_pool, + PoolConfig, + close_all_pools, +) + +async def setup_redis_pool(): + pool = await get_redis_pool( + "redis://localhost:6379/0", + config=PoolConfig( + max_connections=50, + max_idle_connections=10, + socket_timeout=5.0, + socket_connect_timeout=5.0, + socket_keepalive=True, + health_check_interval_s=30.0, + ), + ) + + if await pool.health_check(): + print("Redis connection pool healthy") + + return pool +``` + +### Using with memory store + +```python +import asyncio +from afk.memory.adapters.redis import RedisMemoryStore +from afk.core import Runner + +async def main(): + pool = await get_redis_pool( + "redis://localhost:6379/0", + config=PoolConfig(max_connections=50), + ) + + runner = Runner( + memory_store=RedisMemoryStore(url="redis://localhost:6379/0"), + ) + + result = await runner.run(agent, user_message="Hello") + print(result.final_text) + + await runner.close() + await close_all_pools() + +asyncio.run(main()) +``` + +## Full example + +```python +import asyncio +from afk.llms import LLMBuilder +from afk.llms.middleware import MiddlewareStack +from afk.llms.middleware.timeout import ( + TimeoutMiddleware, + TimeoutConfig, +) +from afk.llms.cache.redis_pool import ( + get_redis_pool, + PoolConfig, + close_all_pools, +) +from afk.memory.adapters.redis import RedisMemoryStore +from afk.core import Runner +from afk.agents import Agent + +class ProductionSetup: + def __init__(self): + self.llm_client = None + self.runner = None + self.pool = None + + async def __aenter__(self): + pool_config = PoolConfig( + max_connections=50, + max_idle_connections=10, + socket_timeout=5.0, + socket_connect_timeout=5.0, + ) + self.pool = await get_redis_pool( + "redis://localhost:6379/0", + config=pool_config, + ) + + timeout_config = TimeoutConfig( + default_timeout_s=30.0, + chat_timeout_s=60.0, + ) + stack = MiddlewareStack( + chat=[TimeoutMiddleware(timeout_config)], + ) + + self.llm_client = ( + LLMBuilder() + .provider("openai") + .model("gpt-4.1-mini") + .profile("production") + .with_middlewares(stack) + .build() + ) + + self.runner = Runner( + memory_store=RedisMemoryStore(url="redis://localhost:6379/0"), + ) + + return self + + async def __aexit__(self, exc_type, exc_val, exc_tb): + if self.runner: + await self.runner.close() + await close_all_pools() + return False + + +async def main(): + agent = Agent( + name="assistant", + model="gpt-4.1-mini", + instructions="You are a helpful assistant.", + ) + + async with ProductionSetup() as setup: + result = await setup.runner.run( + agent, + user_message="Hello, world!", + ) + print(result.final_text) + +asyncio.run(main()) +``` + +## Configuration reference + +### TimeoutConfig + +| Parameter | Default | Description | +| --- | --- | --- | +| `default_timeout_s` | 30.0 | Default timeout for all operations | +| `chat_timeout_s` | None | Specific timeout for chat requests | +| `embed_timeout_s` | None | Specific timeout for embeddings | +| `stream_timeout_s` | None | Specific timeout for streaming | + +### PoolConfig + +| Parameter | Default | Description | +| --- | --- | --- | +| `max_connections` | 50 | Maximum total connections | +| `max_idle_connections` | 10 | Maximum idle connections | +| `socket_timeout` | 5.0 | Socket read/write timeout | +| `socket_connect_timeout` | 5.0 | Connection establishment timeout | +| `socket_keepalive` | False | Enable TCP keepalive | +| `health_check_interval_s` | 30.0 | Interval for health checks | + +## What to read next + +- [LLM Control & Session](/llms/control-and-session) -- Retry, caching, and circuit breaker policies +- [Deployment Guide](/library/deployment) -- Production deployment with Docker and Kubernetes +- [Performance Guide](/library/performance) -- Optimize latency and throughput diff --git a/docs/library/streaming.mdx b/docs/library/streaming.mdx index 93dc5be..d3b2aea 100644 --- a/docs/library/streaming.mdx +++ b/docs/library/streaming.mdx @@ -12,7 +12,7 @@ import asyncio from afk.agents import Agent from afk.core import Runner -agent = Agent(name="explainer", model="gpt-5.2-mini", instructions="Explain clearly.") +agent = Agent(name="explainer", model="gpt-4.1-mini", instructions="Explain clearly.") async def main(): runner = Runner() diff --git a/docs/library/system-prompts.mdx b/docs/library/system-prompts.mdx index 1604fc7..ea02977 100644 --- a/docs/library/system-prompts.mdx +++ b/docs/library/system-prompts.mdx @@ -14,7 +14,7 @@ System prompts define what your agent knows and how it behaves. AFK supports thr ```python agent = Agent( name="assistant", - model="gpt-5.2-mini", + model="gpt-4.1-mini", instructions="You are a helpful assistant. Be concise and accurate.", ) ``` @@ -26,7 +26,7 @@ System prompts define what your agent knows and how it behaves. AFK supports thr ```python agent = Agent( name="analyst", - model="gpt-5.2-mini", + model="gpt-4.1-mini", instruction_file="prompts/analyst.md", ) ``` @@ -46,7 +46,7 @@ System prompts define what your agent knows and how it behaves. AFK supports thr AFK automatically looks for an instruction file based on the agent's name: ```python - agent = Agent(name="writer", model="gpt-5.2-mini") + agent = Agent(name="writer", model="gpt-4.1-mini") # AFK looks for: prompts/writer.md, prompts/writer.txt, writer.md, etc. ``` @@ -84,7 +84,7 @@ Use `{{ variable }}` syntax to inject dynamic values into prompts: ```python agent = Agent( name="support-agent", - model="gpt-5.2-mini", + model="gpt-4.1-mini", instructions=""" You are a support agent for {{ company_name }}. Today's date is {{ current_date }}. @@ -119,7 +119,7 @@ Templates work in instruction files too: ```python agent = Agent( name="reviewer", - model="gpt-5.2-mini", + model="gpt-4.1-mini", instruction_file="prompts/reviewer.md", context={ "language": "Python", @@ -144,7 +144,7 @@ You are a {{ language }} code reviewer following {{ style_guide }}. | Error | Cause | Resolution | | ----------------------- | ------------------------------------- | -------------------------------------------------------------------------------------------- | -| `PromptResolutionError` | `instruction_file` path doesn't exist | Check the file path. Make sure it's relative to the working directory or absolute. | +| `PromptResolutionError` | `instruction_file` path doesn't exist | Check that the file exists under the configured `prompts_dir`. | | `PromptTemplateError` | Jinja2 template syntax error | Check for unclosed `{{ }}` or `{% %}` blocks. | | `PromptTemplateError` | Missing template variable | Add the variable to `Agent.context` or provide a default: `{{ var \| default("fallback") }}` | diff --git a/docs/library/task-queues.mdx b/docs/library/task-queues.mdx index 0981cdb..caa9447 100644 --- a/docs/library/task-queues.mdx +++ b/docs/library/task-queues.mdx @@ -8,21 +8,22 @@ Task queues decouple agent work producers from consumers. Push a task, a worker ## Quick start ```python -from afk.queues import TaskQueue, TaskItem +from afk.queues import InMemoryTaskQueue, RUNNER_CHAT_CONTRACT, TaskWorker from afk.agents import Agent from afk.core import Runner -agent = Agent(name="analyzer", model="gpt-5.2-mini", instructions="Analyze data.") +agent = Agent(name="analyzer", model="gpt-4.1-mini", instructions="Analyze data.") # Push a task -queue = TaskQueue() -await queue.push(TaskItem( - contract="runner.chat.v1", - agent=agent, - user_message="Analyze Q4 revenue trends", -)) - -# A worker picks it up automatically +queue = InMemoryTaskQueue() +await queue.enqueue_contract( + RUNNER_CHAT_CONTRACT, + payload={"user_message": "Analyze Q4 revenue trends"}, + agent_name="analyzer", +) + +# A worker consumes queued tasks +worker = TaskWorker(queue, agents={"analyzer": agent}, runner_factory=lambda: Runner()) ``` ## Task lifecycle @@ -56,12 +57,13 @@ Every task has a **contract** that defines what kind of work it represents: Standard agent chat. Runs an agent with a user message. ```python - task = TaskItem( - contract="runner.chat.v1", - agent=agent, - user_message="Analyze this data", - thread_id="t-42", # Optional: for multi-turn - timeout_s=120.0, # Max execution time + task = await queue.enqueue_contract( + RUNNER_CHAT_CONTRACT, + payload={ + "user_message": "Analyze this data", + "context": {}, + }, + agent_name="analyzer", ) ``` @@ -70,11 +72,12 @@ Every task has a **contract** that defines what kind of work it represents: Generic job dispatch. Runs a custom handler function. ```python - task = TaskItem( - contract="job.dispatch.v1", - handler="myapp.jobs.process_report", - payload={"report_id": "R-123", "format": "pdf"}, - timeout_s=300.0, + task = await queue.enqueue_contract( + JOB_DISPATCH_CONTRACT, + payload={ + "job_type": "process_report", + "arguments": {"report_id": "R-123", "format": "pdf"}, + }, ) ``` @@ -83,8 +86,8 @@ Every task has a **contract** that defines what kind of work it represents: Define your own contract for specialized workloads: ```python - task = TaskItem( - contract="myapp.batch.v1", + task = await queue.enqueue_contract( + "myapp.batch.v1", payload={ "batch_id": "B-456", "items": ["item1", "item2", "item3"], @@ -103,23 +106,33 @@ Every task has a **contract** that defines what kind of work it represents: ```python - from afk.queues import Worker + from afk.queues import InMemoryTaskQueue, TaskWorker - worker = Worker( + queue = InMemoryTaskQueue() + worker = TaskWorker( queue=queue, - runner_factory=lambda: Runner(), - max_concurrency=5, # Process up to 5 tasks in parallel + agents={"analyzer": agent}, ) ``` ```python - @worker.handler("myapp.batch.v1") - async def handle_batch(task: TaskItem) -> dict: - # Custom processing logic - results = await process_batch(task.payload["items"]) - return {"processed": len(results)} + from afk.queues import ExecutionContract, ExecutionContractContext + + class BatchContract(ExecutionContract): + contract_id = "myapp.batch.v1" + requires_agent = False + + async def execute(self, task_item, *, agent, worker_context: ExecutionContractContext) -> dict: + results = await process_batch(task_item.payload["items"]) + return {"processed": len(results)} + + worker = TaskWorker( + queue=queue, + agents={"analyzer": agent}, + execution_contracts={"myapp.batch.v1": BatchContract()}, + ) ``` @@ -135,16 +148,16 @@ When a task exhausts all retries, it moves to the dead-letter queue (DLQ): ```python # Check for dead letters -dead_letters = await queue.get_dead_letters() +dead_letters = await queue.list_dead_letters() for task in dead_letters: - print(f"Failed: {task.contract} — {task.error}") + print(f"Failed: {task.id} — {task.error}") - # Retry manually after fixing the issue - await queue.retry_dead_letter(task.task_id) +# Retry manually after fixing the issue +moved = await queue.redrive_dead_letters() - # Or discard it - await queue.discard_dead_letter(task.task_id) +# Or discard them +removed = await queue.purge_dead_letters() ``` ## Error classification @@ -164,7 +177,7 @@ The queue uses error classification to decide whether to retry: State lives in process memory. No setup required. ```python - queue = TaskQueue() # In-memory by default + queue = InMemoryTaskQueue() ``` **Use for:** Development, testing, prototyping. @@ -174,10 +187,9 @@ The queue uses error classification to decide whether to retry: Durable queue with persistence and multi-worker support. ```python - queue = TaskQueue( - backend="redis", - redis_url="redis://localhost:6379/0", - ) + from afk.queues import create_task_queue_from_env + + queue = create_task_queue_from_env() ``` Set via environment variables: @@ -191,6 +203,40 @@ The queue uses error classification to decide whether to retry: +### Connection pooling + +For high-throughput production workloads, use `RedisConnectionPool` to manage connections efficiently: + +```python +from afk.llms.cache.redis_pool import get_redis_pool, PoolConfig + +# Create a pooled connection +pool = await get_redis_pool( + "redis://localhost:6379/0", + config=PoolConfig( + max_connections=50, + max_idle_connections=10, + socket_timeout=5.0, + ), +) + +# Use in queue +queue = TaskQueue( + backend="redis", + redis_url="redis://localhost:6379/0", +) + +# Health check +if await pool.health_check(): + print("Redis connection healthy") +``` + +The pool provides: +- Configurable max connections (default: 50) +- Idle connection management +- Automatic health checks +- Singleton access via `get_redis_pool()` + ## Next steps diff --git a/docs/library/tested-behaviors.mdx b/docs/library/tested-behaviors.mdx index 5e52da9..13dee6b 100644 --- a/docs/library/tested-behaviors.mdx +++ b/docs/library/tested-behaviors.mdx @@ -3,9 +3,9 @@ title: Tested Behaviors description: Contract-level guarantees and what test suites validate. --- -AFK maintains a comprehensive test suite that validates contract-level guarantees across every major subsystem. These tests are not implementation details -- they define the behavioral contracts that the framework promises to uphold across releases. If a test fails, it means a contract has been broken. +AFK's tests describe the behavior contributors should preserve when changing the runtime, tools, memory, queues, LLM adapters, observability, and evals. Treat this page as a map from behavior to the test files that cover it. -This page documents what each test category validates, why those guarantees matter for your application, and how to run and extend the test suite. +If you change a public contract, update the matching tests and the docs that explain that contract. A passing test suite is necessary, but it is not a release guarantee by itself; review the affected behavior and run the focused suite for the code you touched. ## Guaranteed behaviors @@ -101,9 +101,9 @@ PYTHONPATH=src pytest -v --tb=short ## Interpreting test results -- **All tests pass**: The framework's behavioral contracts are intact. Safe to upgrade or deploy. -- **A test fails**: A contract has been broken. The failure message identifies which guarantee was violated. Do not deploy until the contract is restored. -- **A new test is marked as `xfail`**: The team has acknowledged a known limitation and documented the expected behavior. +- **All relevant tests pass**: The checked behavior still matches the test suite. Review docs and public imports before merging user-visible changes. +- **A test fails**: Inspect the assertion before changing code. The test may expose a real contract regression, or the intended contract may have changed and need a deliberate test/doc update. +- **A new test is marked as `xfail`**: Include a reason and keep the scope narrow so known limitations do not hide unrelated regressions. ## Adding new tests @@ -119,7 +119,7 @@ When adding a new feature or fixing a bug, follow this pattern: ## CI pipeline guidance -The AFK test suite is designed to run in CI without external dependencies. All tests use in-memory backends and mock LLM providers, so no API keys or infrastructure are required. +Most tests run without API keys and use in-memory or mocked providers. Some integration tests cover optional backends such as Redis, SQLite, or Postgres behavior; keep those isolated and skippable when the backing service is unavailable. Recommended CI configuration: @@ -127,7 +127,7 @@ Recommended CI configuration: # Example GitHub Actions step - name: Run AFK tests run: | - pip install -e ".[dev]" + pip install -e . pytest PYTHONPATH=src pytest -q --tb=short --junitxml=reports/test-results.xml env: PYTHONDONTWRITEBYTECODE: "1" diff --git a/docs/library/tools-system-walkthrough.mdx b/docs/library/tools-system-walkthrough.mdx index a48e987..729c060 100644 --- a/docs/library/tools-system-walkthrough.mdx +++ b/docs/library/tools-system-walkthrough.mdx @@ -96,7 +96,7 @@ def search_docs(args: SearchArgs, ctx: ToolContext) -> dict: # 2. Create an agent with the tool agent = Agent( name="ops-assistant", - model="gpt-5.2-mini", + model="gpt-4.1-mini", instructions="Use search_docs when the user asks for references.", tools=[search_docs], ) diff --git a/docs/library/tools.mdx b/docs/library/tools.mdx index 1bfc2f9..360eade 100644 --- a/docs/library/tools.mdx +++ b/docs/library/tools.mdx @@ -223,7 +223,7 @@ from afk.agents import Agent, PolicyEngine, PolicyRule agent = Agent( name="ops", - model="gpt-5.2-mini", + model="gpt-4.1-mini", tools=[list_files, delete_file], policy_engine=PolicyEngine(rules=[ PolicyRule( @@ -396,10 +396,17 @@ flowchart LR class CalcArgs(BaseModel): expression: str + _OPS = { + "+": lambda a, b: a + b, + "-": lambda a, b: a - b, + "*": lambda a, b: a * b, + "/": lambda a, b: a / b, + } + @tool(args_model=CalcArgs, name="calculate", description="Evaluate a math expression.") def calculate(args: CalcArgs) -> dict: - import ast - result = eval(compile(ast.parse(args.expression, mode='eval'), '', 'eval')) + left, op, right = args.expression.split() + result = _OPS[op](float(left), float(right)) return {"expression": args.expression, "result": result} ``` @@ -423,7 +430,7 @@ tools = build_runtime_tools(root_dir="/workspace/project") agent = Agent( name="explorer", - model="gpt-5.2-mini", + model="gpt-4.1-mini", instructions="Explore the project directory structure.", tools=tools, ) @@ -449,7 +456,7 @@ from afk.agents.types import SkillToolPolicy agent = Agent( name="maintainer", - model="gpt-5.2-mini", + model="gpt-4.1-mini", skills=["maintainer"], skill_tool_policy=SkillToolPolicy( command_allowlist=["rg", "git", "python"], diff --git a/docs/library/troubleshooting.mdx b/docs/library/troubleshooting.mdx new file mode 100644 index 0000000..7112a42 --- /dev/null +++ b/docs/library/troubleshooting.mdx @@ -0,0 +1,445 @@ +--- +title: Troubleshooting +description: Common issues and solutions when building with AFK. +--- + +This guide covers common issues encountered when building and deploying AFK agents, with solutions and debugging tips. + +## Agent behavior issues + +### Agent keeps calling the same tool repeatedly + +**Symptoms:** Agent enters a loop, calling the same tool multiple times without making progress. + +**Causes:** +- Tool output doesn't provide the information the agent needs +- Agent instructions don't clarify when to stop +- Missing a tool that would help the agent determine completion + +**Solutions:** + +```python +from afk.agents import FailSafeConfig + +# Add hard limits to prevent runaway loops +agent = Agent( + name="safe-agent", + model="gpt-4.1-mini", + instructions="Complete the task in at most 3 tool calls. If you can't solve it, say so.", + fail_safe=FailSafeConfig( + max_tool_calls=5, # Stop after 5 calls + ), +) +``` + +**Debug:** Enable verbose logging to see tool call inputs/outputs: + +```python +import logging +logging.basicConfig(level=logging.DEBUG) + +runner = Runner(telemetry="console") +``` + +--- + +### Agent ignores tools and doesn't call them + +**Symptoms:** Agent responds with text but doesn't use available tools. + +**Causes:** +- Instructions don't mention the tools or when to use them +- Tool descriptions are unclear +- Model being used doesn't support function calling well + +**Solutions:** + +```python +agent = Agent( + name="helpful", + model="gpt-4.1-mini", + instructions=""" + You have access to the following tools: + - search_docs: Use this to find information in the knowledge base + - calculator: Use this for any math calculations + + Always use tools when the user asks questions that require specific information or calculations. + """, + tools=[search_docs, calculator], +) +``` + +--- + +### Agent produces inconsistent outputs + +**Symptoms:** Same input produces different outputs on different runs. + +**Causes:** +- Temperature is set too high +- Missing structured output configuration +- Non-deterministic system prompt + +**Solutions:** + +```python +# Use request-level sampling controls for direct LLM calls +from afk.llms import LLMBuilder, LLMRequest, Message + +client = ( + LLMBuilder() + .provider("openai") + .model("gpt-4.1-mini") + .build() +) + +response = await client.chat( + LLMRequest( + model="gpt-4.1-mini", + messages=[Message(role="user", content="Classify this ticket")], + temperature=0.0, + ) +) + +agent = Agent( + name="deterministic", + model=client, + instructions="Always respond in JSON format as specified.", +) +``` + +## Memory issues + +### Conversation doesn't persist between runs + +**Symptoms:** Agent doesn't remember previous messages. + +**Causes:** +- Not using `thread_id` to link conversations +- Memory store not configured correctly +- Using in-memory store (loses state on restart) + +**Solution:** + +```python +# Always use thread_id for multi-turn conversations +thread_id = "user-123-session-1" # Consistent per user/conversation + +r1 = await runner.run(agent, user_message="Hi", thread_id=thread_id) +r2 = await runner.run(agent, user_message="What did I just say?", thread_id=thread_id) +# r2 will remember r1's context +``` + +**Check memory backend:** + +```python +# Verify memory is configured +print(runner._memory_store) # Should not be None + +# For production, use persistent storage +runner = Runner( + memory_store=SQLiteMemoryStore(path="./memory.sqlite3") +) +``` + +--- + +### Resume doesn't work + +**Symptoms:** Calling `runner.resume()` doesn't continue from where the run stopped. + +**Solutions:** + +```python +# Check run_id and thread_id are correct +print(result.run_id) # Use this for resume +print(result.thread_id) # Use this for thread + +# Resume correctly +resumed = await runner.resume( + agent, + run_id=result.run_id, + thread_id=result.thread_id, +) +``` + +**Debug checkpoints:** + +```python +# Check checkpoint state directly from the configured memory store +rows = await runner._memory_store.list_state(result.thread_id, prefix=f"checkpoint:{result.run_id}:") +print(f"Found {len(rows)} checkpoint records") +``` + +## LLM issues + +### Rate limit errors + +**Symptoms:** `RateLimitError` or `429` responses from LLM provider. + +**Solutions:** + +```python +from afk.llms import LLMSettings, RateLimitPolicy, create_llm_client + +client = create_llm_client( + provider="openai", + settings=LLMSettings(default_model="gpt-4.1-mini"), + rate_limit_policy=RateLimitPolicy(requests_per_second=0.5, burst=5), +) + +# Or use exponential backoff for retries +from afk.llms import RetryPolicy + +client = create_llm_client( + provider="openai", + settings=LLMSettings(default_model="gpt-4.1-mini"), + retry_policy=RetryPolicy(max_retries=5, backoff_base_s=2.0), +) +``` + +--- + +### Timeout errors + +**Symptoms:** Requests hang or timeout before completing. + +**Solutions:** + +```python +# Set appropriate timeouts +from afk.llms import TimeoutPolicy + +client = create_llm_client( + provider="openai", + settings=LLMSettings(default_model="gpt-4.1-mini"), + timeout_policy=TimeoutPolicy(request_timeout_s=120.0), +) + +# Or per-request timeout via middleware +from afk.llms.middleware.timeout import TimeoutMiddleware, TimeoutConfig + +config = TimeoutConfig( + default_timeout_s=60.0, + chat_timeout_s=120.0, # Longer for complex reasoning +) +``` + +--- + +### Model not found errors + +**Symptoms:** `ModelNotFoundError` or `InvalidRequestError`. + +**Solutions:** + +```python +# Verify model name is correct +client = ( + LLMBuilder() + .provider("openai") + .model("gpt-4.1-mini") # Check exact model name + .build() +) + +# Use fallback for resilience +agent = Agent( + name="resilient", + model="gpt-4.1", # Primary model + fail_safe=FailSafeConfig( + fallback_model_chain=["gpt-4.1-mini", "gpt-4.1-nano"], + ), +) +``` + +## Streaming issues + +### Streaming doesn't work + +**Symptoms:** `run_stream()` doesn't return events or returns them all at once. + +**Solutions:** + +```python +# Make sure you're iterating correctly +handle = await runner.run_stream(agent, user_message="Tell me a story") + +async for event in handle: + if event.type == "text_delta": + print(event.text_delta, end="") + elif event.type == "completed": + print(f"\n\nDone: {event.result.state}") + +# Don't mix sync and async +# WRONG: +result = runner.run_sync(agent, ...) # Sync +handle = await runner.run_stream(...) # Async on same runner + +# RIGHT: +handle = await runner.run_stream(agent, ...) +``` + +--- + +### Streaming disconnects early + +**Symptoms:** Stream ends before completion. + +**Solutions:** + +```python +# Use timeout middleware for streaming +from afk.llms.middleware.timeout import TimeoutMiddleware, TimeoutConfig + +config = TimeoutConfig(stream_timeout_s=180.0) # 3 min for long streams + +handle = await runner.run_stream(agent, user_message="Write a long essay...") +try: + async for event in handle: + # process events + pass +except asyncio.TimeoutError: + print("Stream timed out") +``` + +## Cost issues + +### Unexpected high costs + +**Symptoms:** API costs much higher than expected. + +**Causes:** +- Agent in a loop making many LLM calls +- No cost limits configured +- Expensive model being used unnecessarily + +**Solutions:** + +```python +# ALWAYS set cost limits +agent = Agent( + name="safe", + model="gpt-4.1-mini", + fail_safe=FailSafeConfig( + max_total_cost_usd=0.50, # Stop at $0.50 + ), +) + +from afk.observability import project_run_metrics_from_result + +metrics = project_run_metrics_from_result(result) +print(metrics.estimated_cost_usd) +``` + +--- + +### Token limit errors + +**Symptoms:** `ContextLengthExceeded` or similar errors. + +**Solutions:** + +```python +# Compact memory to reduce context +await runner.compact_thread( + thread_id=thread_id, + event_policy=RetentionPolicy(max_events_per_thread=100), +) + +# Or use a model with larger context +client = ( + LLMBuilder() + .provider("openai") + .model("gpt-4.1") # Larger context than gpt-4.1-mini + .build() +) +``` + +## Tool issues + +### Tool validation errors + +**Symptoms:** `ToolValidationError` when tools are called. + +**Solutions:** + +```python +# Ensure Pydantic model matches tool implementation +class SearchArgs(BaseModel): + query: str + limit: int = Field(default=10, ge=1, le=100) # Add constraints + +@tool(args_model=SearchArgs, name="search", description="Search for documents.") +def search(args: SearchArgs) -> dict: + # Implementation + return {"results": []} +``` + +--- + +### Tool not found errors + +**Symptoms:** Agent can't find or call a tool. + +**Solutions:** + +```python +# Verify tool is attached to agent +print(agent.tools) # Should include your tool + +# Verify tool name matches +@tool(name="my_tool", description="Do the thing.") +def my_tool(args): + return {"ok": True} + +# Call with exact name +agent = Agent( + name="demo", + tools=[my_tool], # Tool function, not name string +) +``` + +## Debug mode + +Enable debug mode for detailed logging: + +```python +from afk.core import Runner, RunnerConfig + +runner = Runner( + config=RunnerConfig( + debug=True, + sanitize_tool_output=True, + ), +) +``` + +## Getting help + +If you can't resolve an issue: + +1. Check the [GitHub Issues](https://github.com/your-org/afk/issues) for known issues +2. Enable debug logging and capture the full traceback +3. Include these details when reporting: + - AFK version (`pip show afk`) + - Python version + - LLM provider and model + - Minimal reproduction code + - Full error traceback + +## Next steps + + + + Understand how AFK components work together. + + + Test agent behavior before shipping. + + + Common patterns and anti-patterns. + + + Detailed API documentation. + + diff --git a/docs/llms/adapters.mdx b/docs/llms/adapters.mdx index 6f5c93d..9004cbd 100644 --- a/docs/llms/adapters.mdx +++ b/docs/llms/adapters.mdx @@ -3,7 +3,7 @@ title: Adapters description: Built-in LLM providers and custom adapter registration. --- -Adapters translate between AFK's normalized contracts (`LLMRequest`/`LLMResponse`) and provider-specific APIs. AFK ships with three built-in adapters and supports custom adapters for any provider. +Providers translate between AFK's normalized contracts (`LLMRequest`/`LLMResponse`) and provider-specific APIs. AFK ships with three built-in providers and supports custom providers for internal deployments. ## Built-in providers @@ -38,7 +38,7 @@ Adapters translate between AFK's normalized contracts (`LLMRequest`/`LLMResponse from afk.llms import LLMBuilder # OpenAI -openai_client = LLMBuilder().provider("openai").model("gpt-5.2-mini").build() +openai_client = LLMBuilder().provider("openai").model("gpt-4.1-mini").build() # Anthropic anthropic_client = LLMBuilder().provider("anthropic").model("claude-opus-4-5").build() @@ -47,46 +47,37 @@ anthropic_client = LLMBuilder().provider("anthropic").model("claude-opus-4-5").b gemini_client = LLMBuilder().provider("litellm").model("gemini/gemini-2.5-pro").build() ``` -## Custom adapter +## Custom provider -Register your own adapter for unsupported providers or custom inference servers: +Register your own provider for unsupported inference servers: - + ```python - from afk.llms import LLMAdapter, LLMRequest, LLMResponse + from afk.llms import LLMProvider, LLMRequest, LLMResponse, LLMTransport - class MyCustomAdapter(LLMAdapter): - def __init__(self, base_url: str, api_key: str): - self.base_url = base_url - self.api_key = api_key + class MyTransport(LLMTransport): + provider_id = "my-provider" - async def generate(self, request: LLMRequest) -> LLMResponse: + async def chat(self, request: LLMRequest, *, response_model=None) -> LLMResponse: # Translate LLMRequest → your provider's format payload = self._build_payload(request) - - # Make the API call - async with httpx.AsyncClient() as client: - resp = await client.post( - f"{self.base_url}/v1/complete", - json=payload, - headers={"Authorization": f"Bearer {self.api_key}"}, - ) - - # Translate provider response → LLMResponse + resp = await self._post(payload) return self._parse_response(resp.json()) - async def generate_stream(self, request: LLMRequest): - # Optional: implement streaming - ... + class MyProvider(LLMProvider): + provider_id = "my-provider" + + def create_transport(self, *, settings, middlewares=None, observers=None, provider_settings=None): + return MyTransport() ``` - + ```python - from afk.llms import register_adapter + from afk.llms import register_llm_provider - register_adapter("my-provider", MyCustomAdapter) + register_llm_provider(MyProvider()) ``` @@ -102,21 +93,14 @@ Register your own adapter for unsupported providers or custom inference servers: ## Custom transport -Override the HTTP transport layer for any adapter (useful for proxies, mTLS, or custom retry logic): +Use provider-specific settings for custom endpoints, proxy URLs, or credentials: ```python -import httpx - -transport = httpx.AsyncHTTPTransport( - limits=httpx.Limits(max_connections=50, max_keepalive_connections=10), - retries=3, -) - client = ( LLMBuilder() .provider("openai") - .model("gpt-5.2-mini") - .transport(transport) + .model("gpt-4.1-mini") + .with_provider_settings("openai", {"base_url": "https://proxy.example.com/v1"}) .build() ) ``` diff --git a/docs/llms/agent-integration.mdx b/docs/llms/agent-integration.mdx index 5bb2be6..cbc86dc 100644 --- a/docs/llms/agent-integration.mdx +++ b/docs/llms/agent-integration.mdx @@ -14,7 +14,11 @@ Agents can specify their model in two ways: Pass a model name string. AFK resolves it to an LLM client using the default provider. ```python - agent = Agent(name="demo", model="gpt-5.2-mini", ...) + agent = Agent( + name="demo", + model="gpt-4.1-mini", + instructions="Answer directly.", + ) ``` The resolution order: @@ -32,12 +36,16 @@ Agents can specify their model in two ways: client = ( LLMBuilder() .provider("openai") - .model("gpt-5.2-mini") + .model("gpt-4.1-mini") .profile("production") .build() ) - agent = Agent(name="demo", model=client, ...) + agent = Agent( + name="demo", + model=client, + instructions="Answer directly.", + ) ``` @@ -120,13 +128,15 @@ LLM errors are classified and handled automatically: ## Model selection guide -| Task | Recommended model | Why | +Treat these as example starting points. Confirm current model names, prices, and rate limits with the provider you use. + +| Task | Starting model | Why | | -------------------------- | ------------------------------ | ----------------------------------- | -| Simple Q&A, classification | `gpt-5.2-nano` | Fast, cheap, good enough | -| General purpose with tools | `gpt-5.2-mini` | Best balance of cost and capability | -| Complex reasoning, coding | `gpt-5.2` or `claude-opus-4-5` | Better at multi-step reasoning | -| Cost-sensitive batch | `gpt-5.2-nano` | Lowest cost per token | -| Maximum quality | `gpt-5.2` + `temperature=0.0` | Deterministic, highest quality | +| Simple Q&A, classification | `gpt-4.1-nano` | Fast, cheap, good enough | +| General purpose with tools | `gpt-4.1-mini` | Best balance of cost and capability | +| Complex reasoning, coding | `gpt-4.1` or `claude-opus-4-5` | Better at multi-step reasoning | +| Cost-sensitive batch | `gpt-4.1-nano` | Lowest cost per token | +| Higher quality generation | `gpt-4.1` + low temperature | More capability, less variation | ## Next steps diff --git a/docs/llms/contracts.mdx b/docs/llms/contracts.mdx index 8ddaa81..92d2472 100644 --- a/docs/llms/contracts.mdx +++ b/docs/llms/contracts.mdx @@ -41,7 +41,7 @@ The adapter translates between AFK's normalized contracts and the provider's nat Message(role="system", content="You are a helpful assistant."), Message(role="user", content="What is Python?"), ], - model="gpt-5.2-mini", + model="gpt-4.1-mini", temperature=0.7, max_tokens=500, ) @@ -90,7 +90,7 @@ class Sentiment(BaseModel): request = LLMRequest( messages=[Message(role="user", content="I love this product!")], - model="gpt-5.2-mini", + model="gpt-4.1-mini", response_format=Sentiment, # ← Forces structured JSON output ) diff --git a/docs/llms/control-and-session.mdx b/docs/llms/control-and-session.mdx index 1a1a370..41628a9 100644 --- a/docs/llms/control-and-session.mdx +++ b/docs/llms/control-and-session.mdx @@ -30,10 +30,10 @@ Use `profile()` to apply a curated set of policies: from afk.llms import LLMBuilder # Development: no retry, no caching, fast failures -dev = LLMBuilder().provider("openai").model("gpt-5.2-mini").profile("development").build() +dev = LLMBuilder().provider("openai").model("gpt-4.1-mini").profile("development").build() # Production: retry, circuit breaker, rate limiting, caching -prod = LLMBuilder().provider("openai").model("gpt-5.2-mini").profile("production").build() +prod = LLMBuilder().provider("openai").model("gpt-4.1-mini").profile("production").build() ``` | Profile | Retry | Cache | Rate Limit | Circuit Breaker | Timeout | @@ -44,40 +44,43 @@ prod = LLMBuilder().provider("openai").model("gpt-5.2-mini").profile("production ## Individual policies -Configure each policy independently: +Configure each policy independently with `create_llm_client()`: Retry transient LLM failures with exponential backoff. ```python - client = ( - LLMBuilder() - .provider("openai") - .model("gpt-5.2-mini") - .retry(max_attempts=3, backoff_base=1.0, backoff_max=30.0) - .build() + from afk.llms import LLMSettings, RetryPolicy, create_llm_client + + client = create_llm_client( + provider="openai", + settings=LLMSettings(default_model="gpt-4.1-mini"), + retry_policy=RetryPolicy(max_retries=3, backoff_base_s=1.0), ) ``` | Parameter | Default | Description | | --- | --- | --- | - | `max_attempts` | 3 | Total attempts (1 initial + 2 retries) | - | `backoff_base` | 1.0 | Initial delay in seconds | - | `backoff_max` | 30.0 | Maximum delay between retries | - | `retryable_errors` | `[429, 500, 502, 503]` | HTTP status codes to retry | + | `max_retries` | 3 | Retry attempts after the initial request | + | `backoff_base_s` | 0.5 | Initial delay in seconds | + | `backoff_jitter_s` | 0.15 | Jitter added to retry delays | + | `require_idempotency_key` | `True` | Require idempotency keys for retried requests | Stop calling a failing provider to prevent cascading failures. ```python - client = ( - LLMBuilder() - .provider("openai") - .model("gpt-5.2-mini") - .circuit_breaker(failure_threshold=5, recovery_timeout=30.0) - .build() + from afk.llms import CircuitBreakerPolicy, LLMSettings, create_llm_client + + client = create_llm_client( + provider="openai", + settings=LLMSettings(default_model="gpt-4.1-mini"), + circuit_breaker_policy=CircuitBreakerPolicy( + failure_threshold=5, + cooldown_s=30.0, + ), ) ``` @@ -95,12 +98,12 @@ Configure each policy independently: Prevent exceeding provider rate limits. ```python - client = ( - LLMBuilder() - .provider("openai") - .model("gpt-5.2-mini") - .rate_limit(requests_per_minute=60, tokens_per_minute=100_000) - .build() + from afk.llms import LLMSettings, RateLimitPolicy, create_llm_client + + client = create_llm_client( + provider="openai", + settings=LLMSettings(default_model="gpt-4.1-mini"), + rate_limit_policy=RateLimitPolicy(requests_per_second=1.0, burst=10), ) ``` @@ -109,28 +112,31 @@ Configure each policy independently: Cache identical requests to reduce cost and latency. ```python - client = ( - LLMBuilder() - .provider("openai") - .model("gpt-5.2-mini") - .cache(ttl_seconds=300, max_entries=1000) - .build() + from afk.llms import CachePolicy, LLMSettings, create_llm_client + + client = create_llm_client( + provider="openai", + settings=LLMSettings(default_model="gpt-4.1-mini"), + cache_policy=CachePolicy(enabled=True, ttl_s=300, namespace="docs"), ) ``` - Cache keys are derived from the request content (messages, model, temperature, tools). Identical requests return cached responses. + Cache keys are derived from request content, model settings, response model, + session/checkpoint tokens, and any configured cache namespace. Cached rows do + not retain provider request IDs, session tokens, checkpoint tokens, or raw + provider payloads. Hard timeout on LLM requests. ```python - client = ( - LLMBuilder() - .provider("openai") - .model("gpt-5.2-mini") - .timeout(connect_s=5.0, read_s=60.0) - .build() + from afk.llms import LLMSettings, TimeoutPolicy, create_llm_client + + client = create_llm_client( + provider="openai", + settings=LLMSettings(default_model="gpt-4.1-mini"), + timeout_policy=TimeoutPolicy(request_timeout_s=60.0), ) ``` @@ -146,12 +152,12 @@ from afk.agents import Agent, FailSafeConfig agent = Agent( name="resilient", - model="gpt-5.2", + model="gpt-4.1", fail_safe=FailSafeConfig( - fallback_model_chain=["gpt-5.2-mini", "gpt-5.2-nano"], + fallback_model_chain=["gpt-4.1-mini", "gpt-4.1-nano"], ), ) -# If gpt-5.2 fails → try gpt-5.2-mini → try gpt-5.2-nano +# If gpt-4.1 fails → try gpt-4.1-mini → try gpt-4.1-nano ``` ## Tuning cheat sheet diff --git a/docs/llms/index.mdx b/docs/llms/index.mdx index a139e44..6c1d052 100644 --- a/docs/llms/index.mdx +++ b/docs/llms/index.mdx @@ -1,6 +1,6 @@ --- title: LLM Layer -description: Provider-portable LLM runtime with retry, caching, and circuit breaking. +description: Provider-portable LLM runtime with retry, caching, circuit breaking, and middleware. --- The LLM layer normalizes communication with language models across all supported providers. Your agent code uses provider-agnostic contracts (`LLMRequest` / `LLMResponse`) while built-in adapters handle the provider-specific details. @@ -15,7 +15,7 @@ from afk.llms import LLMBuilder client = ( LLMBuilder() .provider("openai") - .model("gpt-5.2-mini") + .model("gpt-4.1-mini") .build() ) ``` @@ -32,7 +32,7 @@ client = ( ```python - builder = builder.model("gpt-5.2-mini") + builder = builder.model("gpt-4.1-mini") ``` @@ -53,6 +53,78 @@ client = ( +## Middleware + +The LLM layer supports middleware for intercepting and transforming requests and responses. Use middleware for logging, tracing, caching, and custom request/response handling. + +### Built-in middleware + +```python +from afk.llms import LLMBuilder +from afk.llms.middleware import MiddlewareStack +from afk.llms.middleware.timeout import ( + TimeoutMiddleware, + EmbedTimeoutMiddleware, + StreamTimeoutMiddleware, + TimeoutConfig, +) + +config = TimeoutConfig( + default_timeout_s=30.0, + chat_timeout_s=60.0, + embed_timeout_s=15.0, + stream_timeout_s=45.0, +) + +stack = MiddlewareStack( + chat=[TimeoutMiddleware(config)], + embed=[EmbedTimeoutMiddleware(config)], + stream=[StreamTimeoutMiddleware(config)], +) + +client = ( + LLMBuilder() + .provider("openai") + .model("gpt-4.1-mini") + .with_middlewares(stack) + .build() +) +``` + +### Custom middleware + +```python +from afk.llms import LLMBuilder, LLMRequest, LLMResponse +from afk.llms.middleware import MiddlewareStack + +async def tracing_middleware(call_next, req: LLMRequest) -> LLMResponse: + """Add tracing metadata to requests.""" + req.metadata = req.metadata or {} + req.metadata["trace_id"] = generate_trace_id() + req.metadata["span_name"] = "llm.chat" + return await call_next(req) + +client = ( + LLMBuilder() + .provider("openai") + .model("gpt-4.1-mini") + .with_middlewares(MiddlewareStack( + chat=[tracing_middleware], + embed=[], + stream=[], + )) + .build() +) +``` + +### Middleware protocols + +| Protocol | Operation | Signature | +| --- | --- | --- | +| `LLMChatMiddleware` | Non-streaming chat | `async (call_next, req: LLMRequest) -> LLMResponse` | +| `LLMEmbedMiddleware` | Embeddings | `async (call_next, req: EmbeddingRequest) -> EmbeddingResponse` | +| `LLMStreamMiddleware` | Streaming chat | `(call_next, req: LLMRequest) -> AsyncIterator[LLMStreamEvent]` | + ## Supported providers @@ -60,7 +132,7 @@ client = ( GPT-4.1, GPT-4.1-mini, GPT-4.1-nano, o-series - Claude Opus 4.5, Claude Opus 4.5 + Claude Opus and Sonnet families 100+ providers via the LiteLLM proxy @@ -69,13 +141,15 @@ client = ( All providers expose the same `LLMClient` interface. Your agent code never touches provider-specific types. -## Which provider should I use? +## Example model choices + +Use these as starting points, then verify model availability, pricing, and context limits with your provider account. -| Scenario | Recommended | +| Scenario | Starting point | | -------------------------- | ----------------------------------------------- | -| General purpose | OpenAI `gpt-5.2-mini` | -| Complex reasoning | OpenAI `gpt-5.2` or Anthropic `claude-opus-4-5` | -| Cost-sensitive | OpenAI `gpt-5.2-nano` | +| General purpose | OpenAI `gpt-4.1-mini` | +| Complex reasoning | OpenAI `gpt-4.1` or Anthropic `claude-opus-4-5` | +| Cost-sensitive | OpenAI `gpt-4.1-nano` | | Non-OpenAI/Anthropic model | LiteLLM adapter | | Custom or self-hosted | Custom adapter | @@ -85,11 +159,11 @@ You rarely build `LLMClient` directly. Agents resolve their model automatically: ```python # Option 1: Model name (auto-resolved) -agent = Agent(name="demo", model="gpt-5.2-mini", ...) +agent = Agent(name="demo", model="gpt-4.1-mini", instructions="Answer directly.") # Option 2: Pre-built client (full control) -client = LLMBuilder().provider("openai").model("gpt-5.2-mini").profile("production").build() -agent = Agent(name="demo", model=client, ...) +client = LLMBuilder().provider("openai").model("gpt-4.1-mini").profile("production").build() +agent = Agent(name="demo", model=client, instructions="Answer directly.") ``` ## Next steps diff --git a/pyproject.toml b/pyproject.toml index ab1a87a..072bdda 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,29 +1,47 @@ [project] name = "afk-py" version = "0.1.0" -description = "Add your description here" +description = "Python SDK for building typed, observable AI agents." readme = "README.md" requires-python = ">=3.13" dependencies = [ "aiosqlite>=0.22.1", "async-lru>=2.1.0", - "asyncpg>=0.31.0", "cachetools>=7.0.1", "claude-agent-sdk==0.1.37", - "fastapi>=0.129.0", "jinja2>=3.1.6", - "litellm==1.81.13", + "litellm>=1.83.10", "numpy>=2.3.0", "openai==2.21.0", "prometheus-client>=0.24.1", - "pytest>=9.0.2", - "redis==7.2.0", ] [project.optional-dependencies] a2a = [ "PyJWT>=2.10.1", ] +mcp = [ + "fastapi>=0.129.0", + "uvicorn>=0.38.0", +] +postgres = [ + "asyncpg>=0.31.0", +] +redis = [ + "redis==7.2.0", +] +dev = [ + "pytest>=9.0.2", +] +evals = [] +all = [ + "fastapi>=0.129.0", + "uvicorn>=0.38.0", + "asyncpg>=0.31.0", + "redis==7.2.0", + "pytest>=9.0.2", + "PyJWT>=2.10.1", +] [tool.uv.workspace] members = [ diff --git a/scripts/build_agentic_ai_assets.sh b/scripts/build_agentic_ai_assets.sh index 0dcffc7..1e1e65e 100755 --- a/scripts/build_agentic_ai_assets.sh +++ b/scripts/build_agentic_ai_assets.sh @@ -3,7 +3,7 @@ set -euo pipefail ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" DOCS_DIR="${AFK_DOCS_DIR:-$ROOT_DIR/docs}" -SKILLS_DIR="${AFK_AGENT_SKILLS_DIR:-$ROOT_DIR/agent-skill}" +SKILLS_DIR="${AFK_AGENT_SKILLS_DIR:-$ROOT_DIR/skills}" OUT_DIR="${AFK_AI_INDEX_DIR:-$ROOT_DIR/ai-index}" BUNDLE_SKILL_DOCS=true @@ -11,7 +11,7 @@ usage() { cat <<'EOF' Build AFK AI assets: 1) creates searchable index files from docs/ -2) creates/refreshes skill metadata index in agent-skill/ +2) creates/refreshes skill metadata index in skills/ 3) bundles docs assets into each skill folder (optional) Usage: @@ -268,8 +268,8 @@ for skill_md in sorted(skills_dir.glob("*/SKILL.md")): "id": skill_id, "name": name, "description": description, - "path": f"agent-skill/{rel_path}", - "github_url": f"https://github.com/socioy/afk/tree/main/agent-skill/{skill_md.parent.name}", + "path": f"skills/{rel_path}", + "github_url": f"https://github.com/arpan404/afk/tree/main/skills/{skill_md.parent.name}", } ) @@ -299,7 +299,7 @@ manifest = { "examples.md", "records/", "text/", - "../agent-skill/index.json", + "../skills/index.json", ], } (out_dir / "manifest.json").write_text( @@ -453,7 +453,7 @@ bundle_docs_into_skills() { cp "$OUT_DIR/id-to-path.json" "$refs_dir/id-to-path.json" cp "$OUT_DIR/path-to-id.json" "$refs_dir/path-to-id.json" cp "$OUT_DIR/manifest.json" "$refs_dir/manifest.json" - cp "$OUT_DIR/examples.md" "$skill_dir/references/examples.md" + cp "$OUT_DIR/examples.md" "$skill_dir/references/afk-examples.md" cat > "$skill_dir/references/README.md" < doc id map - \`afk-docs/id-to-path.json\`: doc id -> docs path -- \`examples.md\`: merged runnable examples from snippets +- \`afk-examples.md\`: merged runnable examples from snippets Quick search: diff --git a/scripts/docs_dev.sh b/scripts/docs_dev.sh new file mode 100755 index 0000000..0e72165 --- /dev/null +++ b/scripts/docs_dev.sh @@ -0,0 +1,10 @@ +#!/usr/bin/env bash +set -euo pipefail + +ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" + +cd "$ROOT_DIR/docs" + +# Mintlify currently rejects Node 25+. Use an LTS runtime even when the +# globally active Node is newer. +exec npx -y -p node@22 -p mintlify@4.2.370 mintlify dev "$@" diff --git a/security_best_practices_report.md b/security_best_practices_report.md new file mode 100644 index 0000000..4df666c --- /dev/null +++ b/security_best_practices_report.md @@ -0,0 +1,301 @@ +# AFK Production Security and Readiness Review + +Date: 2026-05-31 + +Scope: `src/afk`, tests, packaging metadata, runtime defaults, persistence, queues, MCP/A2A-adjacent integration boundaries, and public SDK production posture. + +## Executive Summary + +The framework is in good baseline shape for correctness: the full test suite passed (`1677 passed, 4 skipped`) and `ruff check src tests` passed. The main production risks are not broad code-quality failures; they are default-privilege and deployment-safety issues: + +- AFK currently auto-registers local filesystem runtime tools for every runner, rooted at the process working directory. +- The MCP server defaults to listening on all interfaces, accepts wildcard CORS, and has no authentication gate before tool invocation. +- `litellm==1.81.13` currently has multiple known vulnerabilities with fixes available in newer releases. +- Remote MCP server references are only lightly validated, creating SSRF/internal-network risk if untrusted configuration can influence them. +- Audit and memory persistence can store sensitive payloads without deep redaction, retention controls, or file-permission hardening. + +## Verification Performed + +- `PYTHONPATH=src pytest -q` + - Result: `1677 passed, 4 skipped in 4.73s` +- `ruff check src tests` + - Result: all checks passed +- `bandit -r src/afk` + - Result: highlighted the runtime/tooling items discussed below, plus several low-risk scanner findings. +- `pip-audit . --format json --progress-spinner off --desc off --aliases off` + - Result: 6 known vulnerabilities in `litellm==1.81.13` +- `python -m build --outdir /tmp/afk-py-dist` + - Result: wheel and sdist built successfully +- `python -m twine check /tmp/afk-py-dist/*` + - Result: both artifacts passed metadata checks + +## High Severity + +### AFK-SEC-001: Local filesystem tools are registered by default for every runner + +Evidence: + +- `src/afk/core/runner/execution.py:314` starts per-turn tool resolution. +- `src/afk/core/runner/execution.py:347` unconditionally appends `build_runtime_tools(root_dir=Path.cwd())`. +- `src/afk/tools/prebuilts/runtime.py:47` exposes `list_directory`. +- `src/afk/tools/prebuilts/runtime.py:80` exposes `read_file`. +- `src/afk/core/runner/types.py:103` has `default_sandbox_profile=None`. +- `src/afk/core/runner/execution.py:1267` only applies sandboxing when an effective sandbox profile exists. +- `src/afk/agents/security/__init__.py:23` redacts prompt-injection markers, not secrets. + +Impact: + +Any agent run receives model-callable filesystem tools even if the application developer did not explicitly grant file access. The tools are root-bound to the current working directory, but that directory commonly contains `.env`, local config, checked-out source, credentials, test fixtures, and private customer data. Tool outputs can be sent back to the model provider, audit logs, telemetry, or memory stores. + +Recommendation: + +- Do not auto-register runtime filesystem tools by default. +- Add an explicit opt-in setting such as `RunnerConfig(enable_runtime_tools=False, runtime_tool_root=...)`. +- Prefer per-tool allowlists over broad current-working-directory access. +- Apply sandbox profiles to runtime tools by default when enabled. +- Add secret-pattern redaction for model-visible tool output, not only prompt-injection marker redaction. + +Suggested production gate: + +- No release should ship with implicit file tools unless the default root is empty, synthetic, or explicitly configured by the host application. + +### AFK-SEC-002: MCP server defaults permit unauthenticated network tool execution + +Evidence: + +- `src/afk/mcp/server/runtime.py:68` defaults to `host="0.0.0.0"`. +- `src/afk/mcp/server/runtime.py:71` defaults to `cors_origins=["*"]`. +- `src/afk/mcp/server/runtime.py:199` handles `/mcp` POST requests. +- `src/afk/mcp/server/protocol.py:149` dispatches `tools/call`. +- `src/afk/mcp/server/protocol.py:163` calls the registered tool. +- `src/afk/mcp/server/runtime.py:283` installs CORS middleware with wildcard methods and headers. +- `src/afk/mcp/server/runtime.py:317` runs the service with the configured host and port. + +Impact: + +If a developer exposes the MCP server using the default configuration, any reachable client can invoke registered tools. If sensitive tools are present, this can become remote data access, remote mutation, or remote command execution depending on the tool set. `allow_credentials=True` combined with permissive CORS is also unsafe for browser-adjacent deployments. + +Recommendation: + +- Change the default host to `127.0.0.1`. +- Require an auth provider, bearer token, or explicit insecure-development flag before accepting `tools/call` over HTTP. +- Reject `cors_origins=["*"]` when credentials are enabled. +- Add startup warnings or hard failures for production-unsafe combinations. +- Add tests proving unauthenticated calls are rejected in production mode. + +### AFK-SEC-003: Vulnerable `litellm` dependency pin + +Evidence: + +- `pyproject.toml:15` pins `litellm==1.81.13`. +- `uv.lock:795` locks `litellm` at `1.81.13`. +- `pip-audit` reported: + - `CVE-2026-35029`, fixed in `1.83.0` + - `CVE-2026-35030`, fixed in `1.83.0` + - `GHSA-69x8-hrgq-fjj8`, fixed in `1.83.0` + - `CVE-2026-42203`, fixed in `1.83.7` + - `CVE-2026-42271`, fixed in `1.83.7` + - `CVE-2026-40217`, fixed in `1.83.10` + +Impact: + +The LLM adapter dependency is directly in the SDK runtime dependency graph. Known vulnerabilities in this layer are production blockers because LiteLLM processes model requests, headers, provider configuration, and sometimes credentials. + +Recommendation: + +- Upgrade to `litellm>=1.83.10`. +- Regenerate the lockfile. +- Run the LLM adapter test suite and at least one integration smoke test per supported provider path. +- Add dependency-audit CI for both direct dependencies and lockfiles. + +## Medium Severity + +### AFK-SEC-004: Remote MCP client accepts arbitrary HTTP(S) targets with minimal SSRF protection + +Evidence: + +- `src/afk/mcp/store/utils.py:29` validates only that the URL scheme is `http` or `https`. +- `src/afk/mcp/store/utils.py:35` requires only `netloc`. +- `src/afk/mcp/store/registry.py:86` resolves and registers arbitrary refs. +- `src/afk/mcp/store/transport.py:68` builds the remote JSON-RPC request. +- `src/afk/mcp/store/transport.py:81` sends the request via `urllib.request.urlopen`. + +Impact: + +If untrusted users, tools, or configuration can influence MCP server references, AFK can be used to send requests to localhost, link-local metadata services, private subnets, or other internal services. This is a classic SSRF class even though the intended use is developer-configured MCP endpoints. + +Recommendation: + +- Enforce URL validation in the transport layer, not only the resolver. +- Deny loopback, link-local, multicast, private, and metadata-service IP ranges by default for remote refs. +- Add an explicit `allow_private_networks=True` escape hatch for local development. +- Require HTTPS for non-local production refs. +- Add tests for `127.0.0.1`, `[::1]`, `169.254.169.254`, RFC1918 ranges, DNS rebinding-sensitive hostnames, and direct `MCPServerRef` construction. + +### AFK-SEC-005: Audit logging redaction is shallow and file permissions are not hardened + +Evidence: + +- `src/afk/agents/policy/audit.py:84` defaults `include_payloads=True`. +- `src/afk/agents/policy/audit.py:163` opens the audit log path for append. +- `src/afk/agents/policy/audit.py:194` performs redaction. +- `src/afk/agents/policy/audit.py:401` formats events for output. + +Impact: + +The redactor handles selected top-level metadata keys, but nested event payloads can still contain API keys, auth headers, tool arguments, customer prompts, or returned secrets. Log files are created with process-default permissions, which may be broader than intended on shared hosts. + +Recommendation: + +- Make redaction recursive for dictionaries, lists, dataclasses, and Pydantic models. +- Redact both sensitive keys and sensitive-looking string values. +- Default production audit logs to `0600` file permissions where supported. +- Consider `include_payloads=False` as the safer default for production, or require explicit opt-in. +- Add tests with nested `authorization`, `api_key`, `password`, `token`, and tool-output payloads. + +### AFK-SEC-006: Memory persistence stores raw payloads without code-level privacy controls + +Evidence: + +- `src/afk/memory/factory.py:28` defaults `AFK_MEMORY_BACKEND` to `sqlite`. +- `src/afk/memory/factory.py:34` defaults the SQLite path to `afk_memory.sqlite3`. +- `src/afk/memory/adapters/sqlite.py:65` creates tables for raw JSON payloads. +- `src/afk/memory/adapters/sqlite.py:120` appends event payload JSON. +- `src/afk/memory/adapters/sqlite.py:165` persists state values. +- `src/afk/memory/adapters/postgres.py:89` creates raw JSONB event/state/memory storage. + +Impact: + +Production agents often process sensitive inputs and tool outputs. By default, memory can persist this data to a local SQLite database in the working directory, without encryption, redaction, retention limits, or environment-specific consent checks. + +Recommendation: + +- Make production persistence an explicit application choice. +- Add retention controls and documented defaults for local and server backends. +- Provide a redaction hook before memory writes. +- Document encryption-at-rest expectations for SQLite and Postgres deployments. +- Ensure generated local memory files are covered by `.gitignore` and docs. + +### AFK-OPS-001: Queue worker cancellation can leave task outcome ambiguous + +Evidence: + +- `src/afk/queues/worker.py:281` starts shutdown and cancels the worker loop after timeout. +- `src/afk/queues/worker.py:295` cancels active task execution. +- `src/afk/queues/worker.py:388` executes an individual task. +- `src/afk/queues/worker.py:420` handles general exceptions, but `asyncio.CancelledError` is not handled there. + +Impact: + +When shutdown cancels active task execution, the queue may not consistently mark the task as failed, retryable, cancelled, or requeued. Redis-backed queues may recover through inflight recovery, but in-memory or custom queue implementations can observe ambiguous task state. + +Recommendation: + +- Handle `asyncio.CancelledError` in `_execute_task`. +- Decide whether shutdown cancellation should requeue, fail, or mark cancelled. +- Add tests for worker shutdown while a task is executing for every queue backend. + +### AFK-OPS-002: Runtime dependency surface is broader than necessary + +Evidence: + +- `pyproject.toml:7` declares runtime dependencies. +- `pyproject.toml:13` includes `fastapi` as a mandatory runtime dependency. +- `pyproject.toml:14` includes `pytest` as a mandatory runtime dependency. +- `pyproject.toml:18` and `pyproject.toml:19` include Redis and Postgres dependencies as mandatory runtime dependencies. + +Impact: + +The installed SDK pulls in development and integration dependencies for every user, increasing supply-chain surface, install time, and vulnerability exposure. `pytest` should not normally be a runtime dependency of an SDK. + +Recommendation: + +- Move test dependencies to a `dev` extra. +- Move optional integrations to extras such as `mcp`, `redis`, `postgres`, `evals`, and `all`. +- Keep the base SDK dependency set minimal. +- Re-run build, twine check, import smoke tests, and a dependency audit after restructuring extras. + +## Low Severity and Scanner Notes + +### AFK-LOW-001: Jinja autoescape warning is not HTML XSS, but prompt templates are trusted code + +Evidence: + +- `src/afk/agents/prompts/store.py:172` creates a Jinja `Environment` with `autoescape=False`. +- `src/afk/agents/prompts/store.py:231` renders templates. +- `src/afk/agents/prompts/store.py:241` returns rendered prompt text. + +Assessment: + +Bandit reports this as high because Jinja without autoescape is dangerous for HTML output. AFK renders prompts, not HTML, so the direct XSS finding is likely a false positive. The actual risk is different: untrusted prompt templates can inspect exposed context and generate adversarial model instructions. + +Recommendation: + +- Document prompt templates as trusted configuration. +- Use `SandboxedEnvironment` if end users can edit templates. +- Keep autoescape disabled for non-HTML prompt output unless HTML rendering is added later. + +### AFK-LOW-002: SQL f-string findings appear constrained, but should be documented + +Evidence: + +- `src/afk/memory/adapters/postgres.py:317` +- `src/afk/memory/adapters/postgres.py:459` +- `src/afk/memory/adapters/postgres.py:473` +- `src/afk/memory/adapters/sqlite.py:330` +- `src/afk/memory/adapters/sqlite.py:382` + +Assessment: + +The dynamic SQL paths reviewed use internal fragments or vector dimensions coerced through integer parsing rather than raw user strings. These do not look like exploitable SQL injection paths as written. + +Recommendation: + +- Bound-check vector dimensions. +- Keep all user values parameterized. +- Add precise `# nosec` comments only where the project intentionally accepts the pattern. + +### AFK-LOW-003: Random jitter, asserts in testing helpers, and swallowed telemetry exceptions + +Assessment: + +Bandit also reported non-cryptographic `random` usage for retry jitter, `assert` in testing helpers, and swallowed exceptions in best-effort telemetry/audit paths. These are mostly acceptable as written, but swallowed exceptions should be observable in debug mode. + +Recommendation: + +- Keep retry jitter non-cryptographic unless used for security decisions. +- Keep asserts in test-only helpers. +- Add optional debug logging for swallowed telemetry/audit errors. + +## Production Checklist + +Before a production release: + +- Disable implicit runtime filesystem tools, or make them explicit opt-in with a narrow root. +- Change MCP server network/auth defaults and add production guardrails. +- Upgrade `litellm` to at least `1.83.10` and regenerate the lockfile. +- Add dependency-audit CI. +- Add recursive redaction for audit logs and memory writes. +- Add retention and encryption-at-rest guidance for every persistence backend. +- Add queue shutdown tests for running tasks. +- Move test and integration dependencies out of the base runtime dependency set. + +Recommended CI gates: + +- `PYTHONPATH=src pytest -q` +- `ruff check src tests` +- `bandit -r src/afk` +- `pip-audit .` +- `python -m build` +- `python -m twine check dist/*` +- Import smoke tests for the minimal install and each optional extra. + +Recommended security tests to add: + +- A runner test proving filesystem tools are absent unless explicitly enabled. +- MCP server tests proving unauthenticated `tools/call` is rejected in production mode. +- MCP remote-ref tests rejecting private, loopback, link-local, and metadata IPs. +- Audit redaction tests for nested secrets. +- Memory redaction/retention tests. +- Queue cancellation tests for in-flight task shutdown. + diff --git a/skills/afk-coder/SKILL.md b/skills/afk-coder/SKILL.md index bc6eb85..a5a847d 100644 --- a/skills/afk-coder/SKILL.md +++ b/skills/afk-coder/SKILL.md @@ -1,201 +1,109 @@ --- name: afk-coder -description: Build production-grade AI agents with the AFK Python library. Covers Agent declaration, Runner execution, tools, memory, streaming, policies, multi-agent delegation, LLM configuration, and evaluation. +description: Build applications with the AFK Python SDK. Use when creating or modifying AFK agents, runners, tools, memory, streaming, policy/HITL, LLM runtime configuration, queues, MCP/A2A integrations, evals, or production examples using public `afk.*` imports. --- -# AFK Coder Skill +# AFK Coder -Use this skill when building AI agents with the **AFK** Python library (`afk-py`). +Use this skill to build with AFK as an application developer. Keep examples runnable, public-import only, and aligned with the current docs index bundled in this skill. -## When to Use This Skill +## First Steps -- Creating new agents with `Agent()` and running them with `Runner` -- Defining custom tools with `@tool` and composing hook/middleware pipelines -- Configuring LLM providers, models, and runtime settings -- Adding conversation memory (SQLite, Postgres, Redis, in-memory) -- Implementing streaming output and human-in-the-loop workflows -- Setting up multi-agent delegation and subagent orchestration -- Applying security policies, sandbox profiles, and fail-safe guards -- Writing eval suites to test agent behavior -- Deploying agents as MCP or A2A servers +1. Search bundled docs before guessing API details: -## Reference Files + ```bash + python skills/afk-coder/scripts/search_afk_docs.py "runner streaming tools" + ``` -Read these files for detailed API references. Recommended order: +2. Read only the references needed for the task. +3. Prefer the simplest working AFK pattern before adding memory, subagents, queues, or custom runtime configuration. +4. Validate examples against public imports when practical. -| # | File | Purpose | -|---|------|---------| -| 1 | [agents-and-runner.md](./references/agents-and-runner.md) | Agent declaration, Runner API, RunnerConfig, AgentResult | -| 2 | [tools-system.md](./references/tools-system.md) | @tool decorator, ToolResult, hooks, middleware, ToolRegistry | -| 3 | [llm-configuration.md](./references/llm-configuration.md) | Model strings, LLMBuilder, LLMSettings, provider system | -| 4 | [memory-and-state.md](./references/memory-and-state.md) | MemoryStore backends, factory, vector search, retention | -| 5 | [streaming-and-interaction.md](./references/streaming-and-interaction.md) | AgentStreamHandle, AgentRunHandle, InteractionProvider | -| 6 | [security-and-policies.md](./references/security-and-policies.md) | PolicyEngine, SandboxProfile, SkillToolPolicy, fail-safe | -| 7 | [multi-agent-and-delegation.md](./references/multi-agent-and-delegation.md) | Subagents, DelegationPlan, A2A protocol, MCP | -| 8 | [evals-and-testing.md](./references/evals-and-testing.md) | EvalCase, EvalSuite, assertions, budgets, testing patterns | -| 9 | [debugger.md](./references/debugger.md) | Debugger, DebuggerConfig, debug instrumentation | -| 10 | [queues.md](./references/queues.md) | TaskQueue, TaskWorker, retry policies | -| 11 | [environment-variables.md](./references/environment-variables.md) | All AFK_* env vars, loading from .env | -| 12 | [cookbook-examples.md](./references/cookbook-examples.md) | 11 complete runnable examples | +## Reference Map -## Architecture +| Need | Read | +| --- | --- | +| Agent/Runner/result basics | `references/agents-and-runner.md` | +| Tools, hooks, middleware, registries | `references/tools-system.md` | +| LLM providers, builder, settings | `references/llm-configuration.md` | +| Memory, checkpoints, retention | `references/memory-and-state.md` | +| Streaming and interaction providers | `references/streaming-and-interaction.md` | +| Policies, sandboxing, fail-safes | `references/security-and-policies.md` | +| Subagents, delegation, MCP, A2A | `references/multi-agent-and-delegation.md` | +| Evals and test patterns | `references/evals-and-testing.md` | +| Queues and workers | `references/queues.md` | +| Env vars | `references/environment-variables.md` | +| Complete examples | `references/cookbook-examples.md` and `references/afk-examples.md` | +| Current generated docs index | `references/afk-docs/docs-index.jsonl` | -AFK uses a **three-pillar** architecture: - -``` -Agent (stateless config) --> Runner (stateful execution) --> Runtime (LLM, tools, memory, telemetry) -``` - -**Three tiers**: -- **Orchestration** (`afk.core`): Runner, streaming, interaction -- **Adapters** (`afk.llms`, `afk.tools`, `afk.memory`): Provider-portable integrations -- **Extensions** (`afk.evals`, `afk.observability`, `afk.queues`, `afk.mcp`, `afk.messaging`): Optional capabilities - -## Core Philosophy - -1. **Progressive disclosure** -- Simple things are simple (5-line agent), advanced things are possible -2. **Composition over inheritance** -- Middleware, hooks, policies, registries -3. **Contract-first** -- Pydantic models, Protocols, ABCs at every boundary -4. **Provider-portable** -- Zero lock-in via normalized LLM types -5. **Safety by default** -- Deny fallbacks, output sanitization, sandbox profiles -6. **Observable** -- Built-in telemetry, structured events, cost tracking -7. **Async-native** -- `async/await` throughout with `run_sync()` for scripts - -## Essential Patterns - -### Minimal Agent +## Core Pattern ```python -from afk.agents import Agent, Runner +from afk.agents import Agent +from afk.core import Runner + +agent = Agent( + name="assistant", + model="gpt-4.1-mini", + instructions="Answer directly with concrete detail.", +) -agent = Agent(model="gpt-4.1-mini", instructions="You are helpful.") -result = Runner().run_sync(agent, user_message="Hello") +result = Runner().run_sync(agent, user_message="What is an error budget?") print(result.final_text) ``` -### Agent with Tools +## Tool Pattern ```python from pydantic import BaseModel -from afk.agents import Agent, Runner + +from afk.agents import Agent, FailSafeConfig +from afk.core import Runner from afk.tools import tool + class SearchArgs(BaseModel): query: str -@tool(args_model=SearchArgs) -async def search(args: SearchArgs): - """Search the web.""" - return {"results": [f"Result for {args.query}"]} -agent = Agent(model="gpt-4.1-mini", tools=[search]) -result = Runner().run_sync(agent, user_message="Search for AFK docs") -``` - -### Streaming - -```python -handle = await runner.run_stream(agent, user_message="Explain AFK") -async for event in handle: - if event.type == "text_delta": - print(event.text_delta, end="", flush=True) -``` +@tool(args_model=SearchArgs, name="search_docs", description="Search docs.") +async def search_docs(args: SearchArgs) -> dict: + return {"results": [args.query]} -### Multi-Agent -```python -researcher = Agent(model="gpt-4.1-mini", name="researcher", instructions="Research topics.") -writer = Agent(model="gpt-4.1-mini", name="writer", instructions="Write articles.") -lead = Agent(model="gpt-4.1", subagents=[researcher, writer], instructions="Coordinate.") -result = Runner().run_sync(lead, user_message="Write about AI agents") -``` - -### Memory - -```python -from afk.memory import create_memory_store +agent = Agent( + name="researcher", + model="gpt-4.1-mini", + instructions="Use search_docs before answering documentation questions.", + tools=[search_docs], + fail_safe=FailSafeConfig(max_steps=8, max_tool_calls=4, max_total_cost_usd=0.10), +) -store = create_memory_store("sqlite", database_path="./memory.db") -await store.setup() -runner = Runner(memory_store=store) -result = await runner.run(agent, user_message="Hi", thread_id="thread-1") +result = Runner().run_sync(agent, user_message="Find docs about tools.") ``` -## Mandatory Guardrails - -When generating AFK agent code, always ensure: - -1. **Use public imports only** -- `from afk.agents import Agent`, never internal paths -2. **Pydantic v2 args models** -- Every `@tool` requires `args_model=SomeBaseModel` -3. **Async by default** -- Use `async def` for tools; sync tools run in threadpool -4. **Handle tool errors** -- Default `raise_on_error=False` returns `ToolResult(success=False)`; enable strict mode for critical tools -5. **Set `max_steps`** -- Always configure `Agent(max_steps=N)` to prevent runaway loops -6. **Thread IDs for memory** -- Pass `thread_id=` to `runner.run()` when using memory -7. **Sandbox in production** -- Configure `SandboxProfile` and `RunnerConfig` for deployed agents -8. **Test with evals** -- Write `EvalCase` assertions for expected behavior - -## Decision Workflow - -When building an AFK agent, follow this sequence: - -1. **Define the agent's purpose** -- What instructions and capabilities does it need? -2. **Choose tools** -- Define `@tool` functions or use prebuilts -3. **Pick a model** -- Model string or `LLMBuilder` for advanced config -4. **Add memory** (if needed) -- Select backend, call `create_memory_store()` -5. **Configure safety** -- `FailSafeConfig`, `SandboxProfile`, `PolicyEngine` -6. **Set up interaction** (if HITL needed) -- Implement `InteractionProvider` -7. **Add subagents** (if multi-agent) -- Define specialists, attach to parent -8. **Configure runner** -- `RunnerConfig` with timeouts, limits, debug settings -9. **Write evals** -- `EvalCase` + `EvalSuite` for automated testing -10. **Deploy** -- MCP server, A2A server, or direct integration - -## Documentation - -- **Web docs**: https://afk.arpan.sh -- **Library docs**: https://afk.arpan.sh/library/agents -- **Doc files** (for direct reading): - - `docs/library/agents.mdx` -- Agent and Runner - - `docs/library/tools.mdx` -- Tools system - - `docs/library/memory.mdx` -- Memory stores - - `docs/library/a2a.mdx` -- Agent-to-Agent protocol - - `docs/library/mcp.mdx` -- MCP integration - - `docs/library/full-module-reference.mdx` -- Complete module reference - -## Source Paths - -Key source files for implementation details: - -| Module | Path | -|--------|------| -| Agent core | `src/afk/agents/core/base.py` | -| Agent types | `src/afk/agents/types/` | -| Runner API | `src/afk/core/runner/api.py` | -| Runner config | `src/afk/core/runner/types.py` | -| Runner execution | `src/afk/core/runner/execution.py` | -| Tool base | `src/afk/tools/core/base.py` | -| Tool decorator | `src/afk/tools/core/decorator.py` | -| Tool registry | `src/afk/tools/registry.py` | -| Tool errors | `src/afk/tools/core/errors.py` | -| Tool security | `src/afk/tools/security.py` | -| LLM builder | `src/afk/llms/builder.py` | -| LLM settings | `src/afk/llms/settings.py` | -| LLM client | `src/afk/llms/runtime/client.py` | -| Memory store | `src/afk/memory/store.py` | -| Memory factory | `src/afk/memory/factory.py` | -| Memory backends | `src/afk/memory/adapters/` | -| Memory lifecycle | `src/afk/memory/lifecycle.py` | -| Streaming | `src/afk/core/streaming.py` | -| Interaction | `src/afk/core/interaction.py` | -| Policy engine | `src/afk/agents/policy/engine.py` | -| Delegation | `src/afk/core/runtime/dispatcher.py` | -| Evals | `src/afk/evals/` | -| Debugger | `src/afk/debugger/` | -| Queues | `src/afk/queues/` | -| MCP server | `src/afk/mcp/server/` | -| A2A protocol | `src/afk/agents/a2a/` | - -## Utilities - -- **Search docs**: `python agent-skill/coder/scripts/search_afk_docs.py "query"` -- **LLM reference**: `agent-skill/coder/llms.txt` (self-contained API reference) -- **Config**: `agent-skill/coder/assets/coder-config.yaml` +## Guardrails + +- Use public imports: `afk.agents`, `afk.core`, `afk.tools`, `afk.llms`, `afk.memory`, `afk.queues`, `afk.mcp`, `afk.messaging`, `afk.evals`. +- Import `Runner` from `afk.core`, not `afk.agents`. +- Do not use `src.afk` or deep internal imports in user-facing examples. +- Use Python 3.13+ assumptions. +- Use `afk-py` for package installation and `afk` for imports. +- Give every `@tool` a Pydantic `args_model`. +- Use `thread_id=` for multi-turn memory. +- Prefer `await runner.run(...)` in async services and `runner.run_sync(...)` in scripts. +- Read cost and tokens from `result.total_cost_usd` and `result.usage_aggregate`. +- Set production limits with `FailSafeConfig`. +- Add evals for behavior that must not regress. + +## Build Workflow + +1. Define the narrow user task and success criteria. +2. Start with one `Agent` and one `Runner`. +3. Add typed tools only when the agent must take action or fetch external data. +4. Add memory only when turns need continuity. +5. Add streaming only for user-facing progress. +6. Add policies, sandboxing, and budgets before production. +7. Add subagents only when roles are genuinely different. +8. Add queues for durable background work. +9. Add evals and telemetry before release. diff --git a/skills/afk-coder/agents/openai.yaml b/skills/afk-coder/agents/openai.yaml new file mode 100644 index 0000000..64216e8 --- /dev/null +++ b/skills/afk-coder/agents/openai.yaml @@ -0,0 +1,4 @@ +interface: + display_name: "AFK Coder" + short_description: "Build AFK agents and apps." + default_prompt: "Use $afk-coder to build or update an AFK agent with public imports and validated examples." diff --git a/skills/afk-coder/llms.txt b/skills/afk-coder/llms.txt index c2c2d46..4a4df21 100644 --- a/skills/afk-coder/llms.txt +++ b/skills/afk-coder/llms.txt @@ -21,26 +21,24 @@ Three tiers: ```python # Agents -from afk.agents import Agent, Runner -from afk.agents.types import AgentResult, AgentState, AgentRunEvent, FailSafeConfig -from afk.agents.types import ApprovalRequest, ApprovalDecision, UserInputRequest, UserInputDecision, DeferredDecision -from afk.agents.types import AgentRunHandle, DecisionKind, InteractionMode +from afk.agents import Agent, AgentResult, AgentState, AgentRunEvent, FailSafeConfig +from afk.agents import ApprovalRequest, ApprovalDecision, UserInputRequest, UserInputDecision +from afk.agents import AgentRunHandle # Runner -from afk.core.runner import RunnerConfig -from afk.core.streaming import AgentStreamEvent, AgentStreamHandle +from afk.core import Runner, RunnerConfig, AgentStreamEvent, AgentStreamHandle # Tools from afk.tools import tool, prehook, posthook, middleware, registry_middleware from afk.tools import ToolResult, ToolContext, ToolSpec, ToolDeferredHandle, ToolRegistry -from afk.tools.core.errors import AFKToolError, ToolValidationError, ToolExecutionError, ToolTimeoutError, ToolPolicyError, ToolNotFoundError, ToolPermissionError +from afk.tools import ToolValidationError, ToolExecutionError, ToolTimeoutError, ToolPolicyError, ToolNotFoundError # LLM from afk.llms import LLMBuilder from afk.llms.settings import LLMSettings # Memory -from afk.memory import create_memory_store, MemoryStore +from afk.memory import create_memory_store_from_env, MemoryStore # Interaction from afk.core.interaction import InteractionProvider, HeadlessInteractionProvider, InMemoryInteractiveProvider @@ -52,7 +50,7 @@ from afk.agents.policy import PolicyEngine, PolicyRule, PolicyRuleCondition, Pol from afk.tools.security import SandboxProfile, SandboxProfileProvider, SecretScopeProvider # Evals -from afk.evals import EvalCase, EvalSuite, BudgetConfig +from afk.evals import EvalCase, EvalSuiteConfig, EvalBudget ``` --- @@ -369,19 +367,21 @@ Pass models as strings to Agent(model=...): ### Factory ```python -from afk.memory import create_memory_store +import os +from afk.memory import InMemoryMemoryStore, SQLiteMemoryStore, create_memory_store_from_env # In-memory (testing) -store = create_memory_store("memory") +store = InMemoryMemoryStore() # SQLite (local persistence) -store = create_memory_store("sqlite", database_path="./memory.db") +store = SQLiteMemoryStore(path="./memory.db") -# PostgreSQL (production) -store = create_memory_store("postgres", connection_string="postgresql://...") +# Environment-based selection +os.environ["AFK_MEMORY_BACKEND"] = "sqlite" +os.environ["AFK_SQLITE_PATH"] = "./memory.db" +store = create_memory_store_from_env() -# Redis (distributed) -store = create_memory_store("redis", redis_url="redis://localhost:6379") +# Redis/Postgres are available when optional dependencies and env vars are configured. await store.setup() # Initialize (idempotent) ``` @@ -403,9 +403,10 @@ await store.teardown() ### Environment Variables - AFK_MEMORY_BACKEND: memory | sqlite | postgres | redis -- AFK_MEMORY_SQLITE_PATH: SQLite file path -- AFK_MEMORY_POSTGRES_URL: PostgreSQL connection string -- AFK_MEMORY_REDIS_URL: Redis URL +- AFK_SQLITE_PATH: SQLite file path +- AFK_PG_DSN: PostgreSQL connection string +- AFK_REDIS_URL: Redis URL +- AFK_VECTOR_DIM: Vector dimension required by Postgres --- @@ -507,7 +508,7 @@ event_type, tool_name, tool_name_pattern, subagent_name, context_equals, context SandboxProfile( profile_id: str = "default", allow_network: bool = False, - allow_command_execution: bool = True, + allow_command_execution: bool = False, allowed_command_prefixes: list[str] = [], deny_shell_operators: bool = True, allowed_paths: list[str] = [], @@ -581,10 +582,11 @@ BudgetConfig(max_steps=10, timeout_s=60.0, max_cost_usd=1.0) | AFK_LLM_JSON_MAX_RETRIES | 2 | JSON parse retries | | AFK_LLM_MAX_INPUT_CHARS | 200000 | Input char limit | | AFK_LLM_STREAM_IDLE_TIMEOUT_S | 45 | Stream idle timeout | -| AFK_MEMORY_BACKEND | memory | Memory backend | -| AFK_MEMORY_SQLITE_PATH | (none) | SQLite path | -| AFK_MEMORY_POSTGRES_URL | (none) | Postgres URL | -| AFK_MEMORY_REDIS_URL | (none) | Redis URL | +| AFK_MEMORY_BACKEND | sqlite | Memory backend | +| AFK_SQLITE_PATH | afk_memory.sqlite3 | SQLite path | +| AFK_PG_DSN | (none) | Postgres DSN | +| AFK_REDIS_URL | (none) | Redis URL | +| AFK_VECTOR_DIM | required for Postgres | Vector dimension | --- @@ -592,20 +594,26 @@ BudgetConfig(max_steps=10, timeout_s=60.0, max_cost_usd=1.0) ### Minimal Agent ```python -from afk.agents import Agent, Runner -result = Runner().run_sync(Agent(model="gpt-4.1-mini", instructions="Help."), user_message="Hi") +from afk.agents import Agent +from afk.core import Runner +result = Runner().run_sync(Agent(model="gpt-5.2-mini", instructions="Help."), user_message="Hi") ``` ### Tool + Agent ```python @tool(args_model=Args) async def my_tool(args): return "result" -agent = Agent(model="gpt-4.1-mini", tools=[my_tool]) +agent = Agent(model="gpt-5.2-mini", tools=[my_tool]) ``` ### Memory ```python -store = create_memory_store("sqlite", database_path="./db.sqlite") +import os +from afk.memory import create_memory_store_from_env + +os.environ["AFK_MEMORY_BACKEND"] = "sqlite" +os.environ["AFK_SQLITE_PATH"] = "./db.sqlite" +store = create_memory_store_from_env() await store.setup() runner = Runner(memory_store=store) result = await runner.run(agent, user_message="Hi", thread_id="t1") diff --git a/skills/afk-coder/references/README.md b/skills/afk-coder/references/README.md index d7c9a97..bc6b3fa 100644 --- a/skills/afk-coder/references/README.md +++ b/skills/afk-coder/references/README.md @@ -1,38 +1,21 @@ -# Coder Skill References +# Bundled AFK Docs Index -Reference documents for the AFK coder skill. These are loaded on demand -when the agent needs detailed guidance on a specific topic. +This folder is generated by: ---- +```bash +./scripts/build_agentic_ai_assets.sh +``` -## Reference Documents +It keeps machine-readable AFK docs inside this skill package for agent runtimes that expect docs to live with `SKILL.md`. -| # | File | Purpose | -|---|------|---------| -| 1 | `agents-and-runner.md` | Agent declaration, Runner API, AgentResult, FailSafeConfig | -| 2 | `tools-system.md` | @tool decorator, ToolResult, hooks, middleware, registry | -| 3 | `memory-and-state.md` | MemoryStore backends, retention, compaction, vector search | -| 4 | `llm-configuration.md` | LLMBuilder, model strings, providers, profiles, runtime client | -| 5 | `streaming-and-interaction.md` | Streaming events, run handles, InteractionProvider, HITL | -| 6 | `security-and-policies.md` | PolicyEngine, SandboxProfile, failure policies, security checklist | -| 7 | `multi-agent-and-delegation.md` | Subagents, delegation DAGs, A2A protocol, MCP integration | -| 8 | `evals-and-testing.md` | Eval framework, assertions, scorers, budgets, testing patterns | -| 9 | `cookbook-examples.md` | Complete runnable code examples for all major patterns | +Files: +- `afk-docs/docs-index.jsonl`: searchable doc records +- `afk-docs/inverted-index.json`: token -> doc id map +- `afk-docs/id-to-path.json`: doc id -> docs path +- `afk-examples.md`: merged runnable examples from snippets ---- +Quick search: -## Recommended Reading Order - -**Start here:** `agents-and-runner.md` -> `tools-system.md` -> `cookbook-examples.md` - -**Then as needed:** `memory-and-state.md`, `llm-configuration.md`, `streaming-and-interaction.md`, `security-and-policies.md`, `multi-agent-and-delegation.md`, `evals-and-testing.md` - ---- - -## Quick Reference Links - -- Documentation: https://afk.arpan.sh -- API Reference: https://afk.arpan.sh/library/api-reference -- Quickstart: https://afk.arpan.sh/library/quickstart -- Configuration: https://afk.arpan.sh/library/configuration-reference -- Full Module Reference: https://afk.arpan.sh/library/full-module-reference +```bash +python scripts/search_afk_docs.py "system prompts" +``` diff --git a/skills/afk-coder/references/afk-docs/docs-index.json b/skills/afk-coder/references/afk-docs/docs-index.json new file mode 100644 index 0000000..70ff6f9 --- /dev/null +++ b/skills/afk-coder/references/afk-docs/docs-index.json @@ -0,0 +1,1421 @@ +{ + "generated_at_utc": "2026-05-31T13:25:20.446492+00:00", + "docs_root": "/Users/arpanbhandari/Code/afk-py/docs", + "document_count": 63, + "documents": [ + { + "id": "doc_3aa0b3792d22", + "path": "docs/docs.json", + "url": "/docs.json", + "title": "docs", + "description": "", + "headings": [], + "content_sha256": "9808c9300f5fc80e270e826e601d4e2ec57dbe59c97606f751af389bc1cd54d5", + "content": "{ \"$schema\": \"https://mintlify.com/schema.json\", \"name\": \"AFK\", \"logo\": { \"light\": \"/logo-light.svg\", \"dark\": \"/logo-dark.svg\", \"href\": \"https://github.com/arpan404/afk\" }, \"theme\": \"mint\", \"layout\": \"sidenav\", \"favicon\": \"/favicon.svg\", \"topbar\": { \"style\": \"gradient\" }, \"topbarCtaButton\": { \"type\": \"github\", \"url\": \"https://github.com/arpan404/afk\" }, \"topbarLinks\": [ { \"type\": \"link\", \"name\": \"Quickstart\", \"url\": \"/library/quickstart\", \"arrow\": false }, { \"type\": \"link\", \"name\": \"Examples\", \"url\": \"/library/examples/\", \"arrow\": false }, { \"type\": \"link\", \"name\": \"API\", \"url\": \"/library/api-reference\", \"arrow\": false }, { \"type\": \"link\", \"name\": \"Skills\", \"url\": \"/library/agent-skills\", \"arrow\": false } ], \"sidebar\": { \"items\": \"undecorated\" }, \"rounded\": \"default\", \"colors\": { \"primary\": \"#0B6E4F\", \"light\": \"#2FB67E\", \"dark\": \"#084C38\" }, \"footer\": { \"socials\": { \"github\": \"https://github.com/arpan404/afk\" } }, \"navigation\": { \"tabs\": [ { \"tab\": \"Build with AFK\", \"groups\": [ { \"group\": \"Start Here\", \"pages\": [ \"index\", \"library/overview\", \"library/mental-model\", \"library/quickstart\", \"library/learn-in-15-minutes\", \"library/how-to-use-afk\" ] }, { \"group\": \"Core Building Blocks\", \"pages\": [ \"library/agents\", \"library/core-runner\", \"library/tools\", \"library/streaming\", \"library/memory\", \"library/system-prompts\", \"library/agent-skills\" ] }, { \"group\": \"LLM Runtime\", \"pages\": [ \"llms/index\", \"llms/contracts\", \"llms/adapters\", \"llms/control-and-session\", \"llms/agent-integration\" ] }, { \"group\": \"Production\", \"pages\": [ \"library/building-with-ai\", \"library/evals\", \"library/observability\", \"library/security-model\", \"library/task-queues\", \"library/deployment\", \"library/performance\", \"library/troubleshooting\" ] }, { \"group\": \"Integrations\", \"pages\": [ \"library/mcp-server\", \"library/a2a\", \"library/messaging\" ] } ] }, { \"tab\": \"Maintain AFK\", \"groups\": [ { \"group\": \"Contributor Guide\", \"pages\": [ \"library/developer-guide\", \"library/architecture\", \"library/public-imports-and-function-improvement\", \"library/tested-behaviors\" ] }, { \"group\": \"Internal Contracts\", \"pages\": [ \"library/agentic-behavior\", \"library/agentic-levels\", \"library/failure-policy-matrix\", \"library/tool-call-lifecycle\", \"library/tools-system-walkthrough\", \"library/run-event-contract\", \"library/checkpoint-schema\", \"library/llm-interaction\", \"library/debugger\" ] }, { \"group\": \"Migration\", \"pages\": [\"library/migration\"] } ] }, { \"tab\": \"Reference\", \"groups\": [ { \"group\": \"API and Configuration\", \"pages\": [ \"library/api-reference\", \"library/configuration-reference\", \"library/environment-variables\", \"library/full-module-reference\" ] } ] }, { \"tab\": \"Examples\", \"groups\": [ { \"group\": \"Runnable Snippets\", \"pages\": [ \"library/examples/index\", \"library/snippets/01_minimal_chat_agent\", \"library/snippets/02_policy_with_hitl\", \"library/snippets/03_subagents_with_router\", \"library/snippets/04_resume_and_compact\", \"library/snippets/05_direct_llm_structured_output\", \"library/snippets/06_tool_registry_security\", \"library/snippets/07_tool_hooks_and_middleware\", \"library/snippets/08_prebuilt_runtime_tools\", \"library/snippets/09_system_prompt_loader\", \"library/snippets/10_streaming_chat_with_memory\", \"library/snippets/11_cost_monitoring\", \"library/snippets/12_mcp_client_integration\", \"library/snippets/13_multi_model_fallback\", \"library/snippets/14_production_client\" ] } ] } ] } }", + "token_count": 289 + }, + { + "id": "doc_deb460ffa5ee", + "path": "docs/index.mdx", + "url": "/", + "title": "AFK - Agent Forge Kit", + "description": "Build reliable Python agents with typed tools, runtime controls, and production observability.", + "headings": [ + "Choose your path", + "Install", + "First agent", + "Core capabilities", + "AFK skills", + "How AFK fits together", + "Next steps" + ], + "content_sha256": "e7a820d1c12c3306b696c759c97965bfc83dd0ae9847e39832b1caa2dd31a7b9", + "content": "AFK is a Python 3.13+ SDK for building AI agents that need to run reliably outside a demo. You define agents, tools, policies, and memory as typed Python contracts. The runner handles the agent loop, LLM calls, tool execution, streaming, checkpoints, telemetry, and safety limits. Choose your path Start here if you are building an application, workflow, chatbot, coding tool, or production agent on top of AFK. Start here if you are changing AFK itself, reviewing internals, or updating public contracts. Install The distribution package is afk-py; the import package is afk. Set an LLM provider key before running examples: First agent What matters: - Agent is configuration: model, instructions, tools, policies, subagents, and defaults. - Runner is execution: LLM calls, tool calls, memory, streaming, checkpoints, and telemetry. - AgentResult is the run record: final text, terminal state, tool/subagent records, usage, and cost. Core capabilities Declarative agent definitions with instructions, tools, subagents, skills, MCP servers, and safety limits. Sync, async, and streaming execution with lifecycle control and thread continuity. Typed Python tools with Pydantic validation, hooks, middleware, sandboxing, and bounded output. Provider-portable LLM clients with retries, timeouts, rate limits, caching, circuit breakers, and fallback chains. Thread state, checkpoints, retention, compaction, and persistent stores. Evals, observability, queues, security controls, deployment guidance, and troubleshooting. AFK skills Install the AFK skills with Vercel's Skills CLI when you want Codex or another supported agent to use AFK-specific guidance: Use afk-coder when building applications with AFK. Use afk-maintainer when reviewing or changing AFK itself. See Agent Skills for details. How AFK fits together Next steps Build one runnable agent with one typed tool. Walk through agents, tools, streaming, memory, and safety. Find complete snippets by scenario. Check canonical imports, signatures, result fields, and stability rules.", + "token_count": 228 + }, + { + "id": "doc_1c5f37ea0ad3", + "path": "docs/library/a2a.mdx", + "url": "/library/a2a", + "title": "Agent-to-Agent (A2A)", + "description": "Cross-system agent communication with authentication and delivery guarantees.", + "headings": [ + "Architecture", + "Three integration layers", + "Request flow", + "Invocation contracts", + "Hosting an A2A service", + "Define the agent", + "Create auth provider", + "Start the server", + "Authentication providers", + "Google A2A adapter", + "Security considerations", + "Next steps" + ], + "content_sha256": "654fb4bacfee24f10a177606048474a5012fa7cb25ee7df203b1869ede8447cd", + "content": "The A2A protocol enables agent communication **across system boundaries** \u2014 between services, organizations, or deployment environments. It builds on internal messaging by adding authentication, authorization, and external transport. Architecture Three integration layers | Layer | What it does | When you need it | | --------------------- | --------------------------------- | ------------------------------------- | | **Internal Protocol** | Typed envelopes with idempotency | Always (agents in the same system) | | **Auth Provider** | Token validation, caller identity | When agents are in different services | | **External Adapter** | HTTP/gRPC transport, discovery | When agents are in different systems | Request flow The server validates the auth token, extracts the caller identity, and checks authorization rules. The target agent executes locally with a Runner, using the invocation request as input. Invocation contracts Hosting an A2A service Expose your agents as an A2A-accessible service: Authentication providers AFK ships with three auth providers: Permits all requests without authentication. **Never use in production.** Validates requests against pre-shared API keys with HMAC-SHA256 hashing. Validates JWT tokens with configurable issuer and audience claims. Google A2A adapter For interoperability with Google's A2A protocol, use the Google adapter: The adapter wraps a configured Google A2A SDK client behind AFK's AgentCommunicationProtocol. Security considerations | Concern | Mechanism | | -------------------- | ---------------------------------------------------------------- | | **Authentication** | Token-based (API keys, JWT, OAuth) | | **Authorization** | Per-agent access control (which callers can invoke which agents) | | **Idempotency** | idempotency_key prevents duplicate processing on retries | | **Rate limiting** | Configure per-caller request limits | | **Input validation** | All requests validated against AgentInvocationRequest schema | | **Cost isolation** | Each invocation has its own FailSafeConfig budget | **Always authenticate A2A endpoints.** An unauthenticated A2A server allows anyone to invoke your agents, consuming your LLM API credits. Next steps Async job processing for long-running work. Expose tools via the Model Context Protocol.", + "token_count": 216 + }, + { + "id": "doc_b6088784caab", + "path": "docs/library/agent-skills.mdx", + "url": "/library/agent-skills", + "title": "Agent Skills", + "description": "Reusable knowledge bundles that agents load on demand.", + "headings": [ + "When to use skills vs tools", + "Creating a skill", + "SKILL.md format", + "Deployment Guide", + "Prerequisites", + "Deploy Steps", + "Rollback", + "Monitoring", + "Installing repository skills", + "Registering skills on an agent", + "Built-in skill tools", + "How the agent uses skills", + "Security controls", + "Design guidelines", + "Next steps" + ], + "content_sha256": "69777dc325761bdda7d06e8dee698faab76ddd3ee55f7301e7c9143018f6aa8f", + "content": "**Skills** are reusable knowledge bundles \u2014 directories of instructions, scripts, and resources that extend an agent's capabilities without being hard-coded into the system prompt. Agents discover and read skills at runtime using built-in skill tools. When to use skills vs tools | Feature | Tools | Skills | | ---------------- | --------------------------------------------------- | ---------------------------------------- | | **What it is** | A Python function the agent calls | A directory of knowledge the agent reads | | **When it runs** | During the agent loop (function execution) | During the agent loop (file reads) | | **Best for** | Taking actions (API calls, calculations, mutations) | How-to guides, playbooks, reference docs | | **Schema** | Typed Pydantic model | Unstructured markdown + files | | **Side effects** | Yes (executes code) | Read-only (by default) | **Rule of thumb:** If the agent needs to *do something*, use a tool. If it needs to *know something*, use a skill. Creating a skill A skill is a directory with a SKILL.md file: SKILL.md format The YAML frontmatter (name, description) helps the agent decide which skill is relevant. The markdown body is the knowledge content. Installing repository skills This repository includes two skills for agentic development: - afk-coder for building applications with AFK. - afk-maintainer for reviewing or changing AFK itself. Install them with Vercel's Skills CLI: Install one skill at a time: For another repository, use the same shape: The Skills CLI installs the selected skill into the configured agent environment. See skills.sh for CLI details and supported agents. Registering skills on an agent AFK scans the skills_dir and registers each subdirectory as an available skill. The agent can discover and read skills using built-in tools. Built-in skill tools When an agent has skills, AFK automatically provides these tools: | Tool | Purpose | Returns | | ------------------- | ---------------------------------------- | ----------------------------------------- | | list_skills | List all available skills | [{\"name\": \"...\", \"description\": \"...\"}] | | read_skill_md | Read a skill's SKILL.md | Markdown content | | read_skill_file | Read any file in a skill directory | File content | | run_skill_command | Execute a command from a skill's scripts | Command output | How the agent uses skills The agent autonomously decides which skills to read based on the user's question. You don't need to explicitly tell it which skill to use. Security controls By default, read_skill_file only allows reading files within the skill directory. Path traversal (../) is blocked. run_skill_command is gated by policy. You can allowlist specific commands: To disable command execution entirely, only register the read-only skill tools: Design guidelines - **One skill per topic.** Keep skills focused (e.g., deployment, monitoring, database) rather than creating one giant knowledge base. - **Write SKILL.md for the agent, not a human.** The agent reads this file, so be explicit about what to do in each scenario. - **Include examples.** Show the agent what good output looks like. - **Version your skills.** Store skills in git alongside your agent code. - **Gate commands.** Always use policy rules for run_skill_command. Next steps Define typed functions for taking actions. Policy gates for tools and skill commands.", + "token_count": 347 + }, + { + "id": "doc_f12bbbc027a7", + "path": "docs/library/agentic-behavior.mdx", + "url": "/library/agentic-behavior", + "title": "Agentic Behavior", + "description": "Orchestrate multi-agent DAGs with fan-out, join policies, and backpressure.", + "headings": [ + "Quick example", + "Specialist agents", + "Coordinator", + "Delegation DAG model", + "Orchestration pipeline", + "Join policies", + "Failure handling", + "Backpressure", + "When to use multi-agent delegation", + "Next steps" + ], + "content_sha256": "01c6fa33586359217ba0e6ce21454895f25b3d20c57a0b496abe2ca8423d3e25", + "content": "When a single agent isn't enough, AFK lets you orchestrate teams of specialist agents using **delegation DAGs** \u2014 directed acyclic graphs where a coordinator fans out work, collects results, and combines them. Quick example Delegation DAG model The coordinator makes all delegation decisions. Subagents don't talk to each other directly \u2014 they report back to the coordinator, which decides what to do next. Orchestration pipeline The coordinator decides which subagents to call and in what order, based on the user's request and its instructions. AFK validates the delegation request: does the subagent exist? Are the arguments valid? Does the policy allow it? The subagent is enqueued for execution. With fan-out, multiple subagents can run in parallel. Each subagent runs a full agent loop (LLM calls, tool execution, etc.) and returns an AgentResult. Results are collected according to the join policy. The coordinator receives them and decides whether to delegate more or produce a final response. Join policies When multiple subagents run in parallel (fan-out), the **join policy** controls how the coordinator handles results: All subagents must succeed. Any failure fails the entire delegation batch. **Use when:** Every subagent's output is essential for the final result. Failures are tolerated. The coordinator proceeds with whatever results are available. **Use when:** Some subagents provide nice-to-have additions (e.g., fact-checking, sentiment analysis). Return as soon as one subagent succeeds. Cancel the rest. **Use when:** Multiple agents attempt the same task with different strategies. First correct answer wins. Succeed when N out of M subagents complete successfully. **Use when:** You need consensus from a majority of agents. Failure handling | Failure | all_required | allow_optional_failures | first_success | quorum | | ------------------ | ------------------------------------------------------------------ | ------------------------------------------------------------------------------------------- | ------------------------------------------------------------------ | ------------------------------------------------------------------ | | One subagent fails | Batch fails | Continue with others | Continue waiting | Continue if quorum not needed | | All subagents fail | Batch fails | Batch fails | Batch fails | Batch fails | | Timeout | Batch fails | Use available results | Batch fails | Depends on completed count | Backpressure AFK limits concurrent subagent executions to prevent resource exhaustion: When the concurrency limit is reached, additional subagent calls are queued and execute as slots become available. When to use multi-agent delegation | Scenario | Single agent | Multi-agent | | --------------------------------- | ------------------------------------------------------------------------ | -------------------------------------------------------------------------------------- | | Simple Q&A or classification | | Overkill | | Task needs different expertise | Consider | | | Need to parallelize work | N/A | | | Task needs consensus/verification | N/A | | | Tight latency budget | (fewer LLM calls) | (more LLM calls) | Next steps Capability maturity model \u2014 when to add agents. How orchestration and execution are separated.", + "token_count": 303 + }, + { + "id": "doc_57e2139f0873", + "path": "docs/library/agentic-levels.mdx", + "url": "/library/agentic-levels", + "title": "Agentic Levels", + "description": "A capability maturity model \u2014 know when to add complexity.", + "headings": [ + "The five levels", + "Capability comparison", + "Decision guide", + "Signals to level up", + "Next steps" + ], + "content_sha256": "05994fd901c3045dec62b4bac7a8f98bcb6c12d58749ed51680689589ed90bba", + "content": "AFK applications progress through five levels of capability. Each level adds features and complexity. Start at Level 1 and move up only when you have clear signals that the current level isn't enough. The five levels One agent, one model, no tools. The simplest possible setup. **AFK features used:** Agent, Runner, run_sync **Good for:** Text classification, summarization, translation, simple Q&A **Move up when:** The agent needs to take actions or access external data Add typed tool functions for the agent to call. **AFK features added:** @tool, Pydantic models, FailSafeConfig **Good for:** RAG, data lookup, calculations, API integrations **Move up when:** Different parts of the task need different expertise or models Coordinator delegates to specialist subagents. **AFK features added:** subagents, join policies, backpressure **Good for:** Complex tasks, parallel work, specialist expertise, consensus **Move up when:** Tasks take minutes, need async processing, or need queue-based reliability Decouple producers and consumers with task queues. Long-running jobs execute asynchronously. **AFK features added:** TaskQueue, TaskItem, workers, dead-letter handling **Good for:** Batch processing, background jobs, retryable pipelines, high-throughput **Move up when:** You need cross-system communication or external agent interop Agents communicate across systems using the A2A protocol with authenticated endpoints. **AFK features added:** InternalA2AProtocol, A2AServiceHost, auth providers, external adapters **Good for:** Microservice agent meshes, third-party integrations, federated AI systems **This is the ceiling** \u2014 most applications don't need Level 5. Capability comparison | Capability | L1 | L2 | L3 | L4 | L5 | | -------------------------- | ------------------------------------------------------ | ------------------------------------------------------ | ------------------------------------------------------ | ------------------------------------------------------ | ------------------------------------------------------ | | Text generation | | | | | | | Tool calling | \u2014 | | | | | | Multi-agent delegation | \u2014 | \u2014 | | | | | Async processing | \u2014 | \u2014 | \u2014 | | | | Cross-system communication | \u2014 | \u2014 | \u2014 | \u2014 | | | Policy engine | | | | | | | Observability | | | | | | | Evals | | | | | | = essential at this level    = recommended    \u2014 = not applicable Decision guide > [!TIP] > **Start at Level 1.** The simplest system that works is the best system. Premature complexity is the most common mistake in agent design. Signals to level up | Signal | Current Level | Move to | | ----------------------------------------------- | ------------- | -------------------------- | | Agent needs external data | 1 | 2 \u2014 Add tools | | One prompt can't cover all expertise | 2 | 3 \u2014 Split into specialists | | Users waiting too long for results | 3 | 4 \u2014 Queue for async | | Tasks fail and need to be retried automatically | 3 | 4 \u2014 Queue with DLQ | | Other systems need to invoke your agents | 4 | 5 \u2014 Expose A2A | | Third-party agents need to use your tools | 4 | 5 \u2014 Use MCP server | Next steps DAG orchestration, join policies, and fan-out. How AFK separates orchestration from execution.", + "token_count": 296 + }, + { + "id": "doc_e67e762fb6ab", + "path": "docs/library/agents.mdx", + "url": "/library/agents", + "title": "Agents", + "description": "Define agents with instructions, tools, and subagents.", + "headings": [ + "Your first agent", + "Agent fields reference", + "Single agent vs multi-agent", + "How subagent delegation works", + "Adding safety limits", + "Policy-aware agents", + "Design guidelines", + "Next steps" + ], + "content_sha256": "41b56ac8a4f6f74fd32265ec3b5c0ad682960f13a814fb9faf3517c23c943143", + "content": "An **Agent** is a configuration object that describes _what_ your AI agent is \u2014 its identity, capabilities, and boundaries. Agents don't execute themselves; they're run by a Runner. Your first agent Those are the fields most examples should set. model is the only required constructor argument, but name and instructions make traces and behavior easier to understand. Agent fields reference | Field | Type | Default | Purpose | | ------------------ | -------------------- | -------- | --------------------------------------------------------------- | | model | str or LLM | required | LLM model name or pre-built client instance | | name | str | None | Agent identity for logs, telemetry, and subagent routing | | instructions | str | None | System prompt \u2014 what the agent knows and how it behaves | | instruction_file | str or Path | None | Path to a .txt or .md file containing the system prompt | | prompts_dir | str or Path | None | Directory containing prompt files resolved by the prompt store | | tools | list[tool] | None | Typed functions the agent can call | | subagents | list[Agent] | None | Specialist agents this agent can delegate to | | skills | list[str] | None | Skill names to resolve from skills_dir | | skills_dir | str or Path | \".agents/skills\" | Directory containing skill packs | | mcp_servers | list[MCPServerLike]| None | MCP server configs for external tool discovery | | fail_safe | FailSafeConfig | defaults | Step limits, cost budgets, timeout, and failure policies | | context_defaults | dict | None | Default JSON context merged into each run before caller context | | inherit_context_keys | list[str] | None | Context keys inherited from parent agent in delegation | | model_resolver | callable | None | Custom function to resolve model names to LLM clients | | instruction_roles | list[InstructionRole] | None | Structured instruction sections with role-based ordering | | policy_roles | list[PolicyRole] | None | Role-based policy rules applied during execution | | policy_engine | PolicyEngine | None | Policy engine for tool/action gating | | subagent_router | SubagentRouter | None | Custom routing logic for subagent delegation | | max_steps | int | 20 | Maximum agent loop iterations | | tool_parallelism | int | None | Max concurrent tool executions per step | | subagent_parallelism_mode | str | \"configurable\" | How subagent concurrency is managed | | reasoning_enabled| bool | None | Enable provider-supported reasoning controls | | reasoning_effort | str | None | Thinking effort level (e.g. \"low\", \"medium\", \"high\") | | reasoning_max_tokens | int | None | Token budget for extended thinking | | skill_tool_policy| SkillToolPolicy | None | Security policy for skill-provided tool execution | | enable_skill_tools | bool | True | Whether to expose skill tools to the agent | | enable_mcp_tools | bool | True | Whether to expose MCP-discovered tools to the agent | | runner | Runner | None | Pre-bound runner instance (advanced usage) | Single agent vs multi-agent A single agent handles everything. Best for focused tasks. **Use when:** The task is well-defined and doesn't need specialized sub-expertise. A coordinator delegates to specialist subagents. **Use when:** Different parts of the task need different expertise, models, or tools. How subagent delegation works When an agent has subagents, AFK automatically generates transfer tools (transfer_to_researcher, transfer_to_writer). The coordinator calls these like any other tool. Each subagent runs a **full agent loop** with its own model, instructions, and tools. The coordinator sees only the subagent's final_text. Adding safety limits Every agent should have a FailSafeConfig in production: **Always set max_total_cost_usd** in production. A runaway agent loop can spend significant API credits in minutes. Policy-aware agents Attach a PolicyEngine to control what the agent can do: Policy decisions: allow (default), deny, request_approval (human-in-the-loop), or request_user_input. Design guidelines - **Start with one agent.** Only add subagents when you have clear evidence that the task needs specialized expertise. - **Keep instructions focused.** Vague instructions produce vague results. Tell the agent exactly what to do and what not to do. - **Use typed tools.** Every tool argument should be a Pydantic model. Untyped arguments bypass validation. - **Set cost limits early.** Add FailSafeConfig before your first deployment, not after your first runaway bill. Next steps How agents are executed \u2014 lifecycle, API modes, and state management. Define typed tool functions with validation and policy gates.", + "token_count": 481 + }, + { + "id": "doc_bbc97cbff254", + "path": "docs/library/api-reference.mdx", + "url": "/library/api-reference", + "title": "API Reference", + "description": "Canonical public imports and core signatures for AFK.", + "headings": [ + "Import map", + "Agent", + "Runner", + "RunnerConfig", + "FailSafeConfig", + "Tool decorator", + "AgentResult", + "API stability" + ], + "content_sha256": "15f83948b7edc7c528d459c0fd141ae9ab71d30f83a5635e768c12bdc145e3fa", + "content": "This page is the public import contract for application developers. Use these imports in docs, examples, tests that exercise public behavior, and downstream applications. For field-by-field configuration, see Configuration Reference. For generated module detail, see Full Module Reference. Import map | Task | Public import | | --- | --- | | Define an agent | from afk.agents import Agent | | Configure fail-safe limits | from afk.agents import FailSafeConfig | | Configure policy rules | from afk.agents import PolicyEngine, PolicyRule | | Implement dynamic policy hooks | from afk.agents import PolicyRole | | Run an agent | from afk.core import Runner | | Configure the runner | from afk.core import RunnerConfig | | Consume streaming events | from afk.core import AgentStreamEvent, AgentStreamHandle | | Define a tool | from afk.tools import tool | | Access tool context | from afk.tools import ToolContext | | Build an LLM client | from afk.llms import LLMBuilder | | Run eval suites | from afk.evals import run_suite | | Define eval cases | from afk.evals import EvalCase, EvalBudget | | Create memory stores | from afk.memory import InMemoryMemoryStore, SQLiteMemoryStore | | Create task queues | from afk.queues import InMemoryTaskQueue, RedisTaskQueue, TaskWorker | | Expose MCP tools | from afk.mcp import MCPServer | | Use A2A messaging | from afk.messaging import InternalA2AProtocol | Do not use src.afk.* imports in user-facing docs. Agent Only model is required. Most examples should also set name and instructions for clear traces and predictable behavior. Runner Common methods: | Method | Use when | Returns | | --- | --- | --- | | runner.run_sync(agent, user_message=..., thread_id=...) | Scripts, CLIs, tests without an existing event loop | AgentResult | | await runner.run(agent, user_message=..., thread_id=...) | Async services, workers, APIs | AgentResult | | await runner.run_stream(agent, user_message=..., thread_id=...) | Chat UIs and progress streams | AgentStreamHandle | | await runner.resume(agent, run_id=..., thread_id=..., context=...) | Continue from persisted state | AgentResult | | await runner.compact_thread(thread_id=...) | Apply memory retention/compaction | MemoryCompactionResult | run_sync() is a convenience wrapper for synchronous code. Use await runner.run(...) inside async applications. RunnerConfig Use Configuration Reference for the complete set of runner fields and defaults. FailSafeConfig Set max_total_cost_usd for production agents. The default is intentionally unset because acceptable budgets vary by application. Tool decorator Tool functions may be sync or async. They may accept (args), (args, ctx), or (ctx, args). AgentResult Important fields: | Field | Meaning | | --- | --- | | final_text | Final assistant text | | state | Terminal run state | | requested_model | Model requested by the caller or agent | | normalized_model | Effective normalized model when available | | provider_adapter | Provider/adapter used when available | | tool_executions | Ordered tool execution records | | subagent_executions | Ordered subagent execution records | | usage_aggregate | Aggregated input/output/total tokens | | total_cost_usd | Estimated total cost when available | | state_snapshot | Terminal runtime snapshot payload | API stability - Public docs and examples should use imports from afk.agents, afk.core, afk.tools, afk.llms, afk.memory, afk.queues, afk.mcp, afk.messaging, afk.observability, and afk.evals. - Internal modules under package subdirectories may change faster than public exports. - When changing public exports, update this page, Public API Rules, examples, and generated agent-facing docs.", + "token_count": 391 + }, + { + "id": "doc_4e1ebaf40267", + "path": "docs/library/architecture.mdx", + "url": "/library/architecture", + "title": "Architecture", + "description": "How AFK separates orchestration from execution.", + "headings": [ + "Layered architecture", + "Module boundaries", + "Runtime sequence", + "Contract boundaries", + "Error isolation", + "Design principles", + "Next steps" + ], + "content_sha256": "ff09cde4ea0e12906a54edc1fad4c85abd20bbc0a45370728060b8067e7d3cf8", + "content": "AFK's architecture is built on one principle: **orchestration and execution are separate concerns**. The runner (orchestration) manages the step loop, state, and policies. Adapters (execution) handle LLM calls, tool execution, and external communication. You can swap any adapter without touching your agent code. Layered architecture Module boundaries | Module | Responsibility | Dependencies | | --------------- | ------------------------------------------------------ | --------------------------------------------------- | | afk.agents | Agent definition, configuration, fail-safe | pydantic | | afk.core | Runner, step loop, state management, policies | afk.agents, afk.llms, afk.tools, afk.memory | | afk.llms | LLM runtime, provider adapters, retry/circuit breaking | Provider SDKs | | afk.tools | Tool registry, execution, validation, hooks | pydantic | | afk.memory | State persistence, checkpoints, compaction | Backend drivers | | afk.observability | Event pipeline, metrics, exporters | OTEL SDK (optional) | | afk.messaging | Public A2A protocol, auth, host, and delivery exports | afk.agents | | afk.evals | Eval runner, assertions, reporting | afk.core | **Key rule:** Modules in the Adapters layer never import from each other. afk.llms doesn't know about afk.tools. Only afk.core wires them together. Runtime sequence What happens when you call runner.run(agent, user_message=\"...\"): Contract boundaries Every boundary between modules uses a typed contract: All contracts are Pydantic models. This means: - **Validation at boundaries** \u2014 malformed data causes clear errors - **Serializable** \u2014 every contract can be logged, stored, or sent over the wire - **Versionable** \u2014 contracts have stable shapes for backward compatibility Error isolation Failures in one adapter don't crash the others: | Failure | Impact | Runner behavior | | -------------------- | ------------------------------ | --------------------------------------- | | LLM call fails | No response for this step | Retry (if retryable) or fail the run | | Tool execution fails | Tool result is an error object | Return error to LLM for self-correction | | Memory backend fails | State not persisted | Run continues (degraded mode) | | Telemetry fails | Events not exported | Run continues (events dropped silently) | **Telemetry failures are always silent.** A broken exporter should never block an agent run. If telemetry is critical to your use case, add a separate monitoring check. Design principles 1. **Contracts first.** Define the interface (Pydantic model), then implement the behavior. Never skip the contract. 2. **No cross-adapter imports.** afk.llms doesn't import afk.tools. Only afk.core wires modules together. 3. **Classify failures.** Every error is retryable, terminal, or non-fatal. The runner uses this classification to decide what to do. 4. **Least privilege.** Adapters get only the data they need. LLM adapters don't see tool results until the runner decides to include them. Next steps Provider-portable LLM runtime in detail. Contribute to AFK \u2014 patterns and conventions.", + "token_count": 330 + }, + { + "id": "doc_18d38fdacc9e", + "path": "docs/library/building-with-ai.mdx", + "url": "/library/building-with-ai", + "title": "Building with AI", + "description": "Production playbook \u2014 patterns, anti-patterns, and deployment guidance.", + "headings": [ + "Start narrow, iterate fast", + "Common patterns", + "Anti-patterns", + "Production readiness checklist", + "Next steps" + ], + "content_sha256": "4fb4252f5d2f1024c6863df494ee1627ef492fb5eb8a1c5ff8673f47773df3e4", + "content": "This guide covers the engineering patterns for building production-quality AI features with AFK. It's organized around three phases: starting narrow, adding capabilities, and scaling safely. Start narrow, iterate fast The most common mistake is building for complexity you don't have yet. Start with the simplest version that solves the problem, then add capabilities based on real evidence. > [!TIP] > **Test at every step.** Don't add tools before the base prompt works. Don't add subagents before single-agent tools work. Each layer should be proven before adding the next. Common patterns Categorize input into predefined labels. No tools needed. **Tips:** - Constrain output format explicitly in the prompt - Test with a diverse set of inputs - Add evals for each category Retrieve context, then generate an answer. **Tips:** - Always search before answering - Instruct the model to cite sources - Set tool_output_max_chars to prevent context overflow Generate, review, and test code. **Tips:** - Higher cost limits (coding agents iterate more) - Gate write_file with policy approval - Use sandbox profiles for code execution Route tasks to specialist subagents. **Tips:** - Keep the coordinator's prompt focused on routing - Don't give the coordinator tools \u2014 let specialists handle execution - Use join_policy=\"allow_optional_failures\" if some specialists are non-critical Anti-patterns These are the most common mistakes. Avoiding them will save you significant debugging time. | Anti-pattern | Problem | Fix | | -------------------------------------- | ---------------------------------------------------- | -------------------------------------------------- | | **No cost limits** | Runaway agent loops spend $100s in minutes | Always set max_total_cost_usd | | **Vague instructions** | Model produces inconsistent output | Be specific: \"Output only the category name\" | | **Too many tools** | Model gets confused choosing between tools | Keep \u2264 5 tools per agent. Split into subagents. | | **Mixing orchestration and execution** | Runner logic leaks into tool handlers | Tools should be pure functions. No runner imports. | | **Skipping evals** | Prompt changes break behavior silently | Run evals in CI on every PR | | **Untyped tool arguments** | Missing validation, hard-to-debug errors | Always use Pydantic models | | **Not classifying failures** | Retryable errors treated as terminal (or vice versa) | Return clear error types from tools | | **Giant system prompts** | Token waste, instruction drift | Split into skills. Use templates. | Production readiness checklist | Area | Requirement | Status | | ----------------- | ------------------------------------------------- | ----------------------------------------- | | **Safety** | FailSafeConfig with cost, step, and time limits | | | **Safety** | Policy rules for all mutating tools | | | **Observability** | Telemetry exporter configured (OTEL recommended) | | | **Observability** | Alerts on error rate and latency | | | **Testing** | Eval suite with \u2265 5 cases running in CI | | | **Testing** | Golden traces captured for regression detection | | | **Memory** | Persistent backend for multi-turn conversations | | | **Memory** | Thread compaction configured | | | **Security** | Secrets in environment variables, not code | | | **Security** | Sandbox profiles for code execution tools | | Next steps Set up monitoring, alerting, and dashboards. Write behavioral tests for agents.", + "token_count": 341 + }, + { + "id": "doc_4a1cf8a90e83", + "path": "docs/library/checkpoint-schema.mdx", + "url": "/library/checkpoint-schema", + "title": "Checkpoint Schema", + "description": "Checkpoint structure and resume correctness guarantees.", + "headings": [ + "Checkpoint model", + "Field reference", + "Resume behavior", + "Resume code example", + "Start a run that might be interrupted", + "Later, resume from the checkpoint", + "What gets stored in the payload", + "Phase-specific payloads", + "Async write-behind behavior", + "Effect replay and idempotency", + "Common failure scenarios" + ], + "content_sha256": "a0ba36ffceea5975afae78511697808cc3bf9231e2d04812cdc954536f971a1b", + "content": "Checkpoints are the persistence mechanism that allows AFK agent runs to survive process restarts, crashes, and intentional pauses. At key boundaries during execution (step start, pre-LLM call, post-tool batch, run terminal), the runner writes a checkpoint record to the memory store. Each checkpoint captures enough state to reconstruct the execution context and resume from where the run left off. Checkpoints matter for three reasons: 1. **Fault tolerance** -- If the process crashes mid-run, the latest checkpoint lets you resume without re-executing already-completed work. 2. **Human-in-the-loop** -- When a run pauses for approval, the checkpoint preserves the full conversation and pending state so the run can resume hours or days later. 3. **Auditability** -- The checkpoint chain provides a step-by-step record of every phase the run passed through, useful for debugging and compliance. Checkpoint model Field reference | Field | Type | Description | | --- | --- | --- | | run_id | str | Unique identifier for the agent run. Generated at run start or provided when resuming. Used as the primary key for checkpoint lookup. | | schema_version | str | Checkpoint schema version (for migration/compat validation). | | phase | str | The execution phase when the checkpoint was written. Values include: run_started, step_started, pre_llm, post_llm, pre_tool_batch, post_tool_batch, pre_subagent_batch, post_subagent_batch, run_terminal. | | payload | dict | Phase-specific data. The contents vary by phase -- see the payload section below. | | timestamp_ms | int | Unix timestamp in milliseconds when the checkpoint was written. Used for ordering when multiple checkpoints exist for the same run. | Resume behavior The runner calls memory.get_state(thread_id, checkpoint_latest_key(run_id)) to fetch the most recent checkpoint for the given run. If no checkpoint exists, a AgentCheckpointCorruptionError is raised. The checkpoint record must be a dict with the required fields (run_id, thread_id, phase, payload). Missing or malformed fields cause an AgentCheckpointCorruptionError. The runner also normalizes legacy checkpoint formats through _normalize_checkpoint_record(). If the checkpoint's phase is run_terminal and the payload contains a terminal_result, the run is already complete. The runner returns a pre-resolved handle with the deserialized AgentResult -- no re-execution occurs. For non-terminal checkpoints, the runner loads the full runtime snapshot which contains the conversation messages, counters, usage aggregates, and any pending LLM response. This snapshot is used to reconstruct the execution context. The runner calls run_handle() with the restored snapshot. Execution continues from the step where the run was interrupted. If a pending_llm_response exists in the snapshot, the runner skips the LLM call and proceeds directly to tool execution for that response. Resume code example What gets stored in the payload The payload field carries different data depending on the checkpoint phase. The most important payload is the **runtime snapshot** persisted at step_started and post_llm phases, which contains everything needed for full resume: | Payload key | Type | Description | | --- | --- | --- | | messages | list[dict] | Serialized conversation history (system, user, assistant, tool messages). | | step | int | Current step counter in the execution loop. | | state | str | Current run state (running, degraded, etc.). | | context | dict | Run context dict merged from agent defaults and caller-provided context. | | llm_calls | int | Number of LLM calls made so far. | | tool_calls | int | Number of tool calls made so far. | | started_at_s | float | Unix timestamp when the run originally started. | | usage | dict | Token usage aggregate (input_tokens, output_tokens, total_tokens). | | total_cost_usd | float | Accumulated estimated cost in USD. | | session_token | str \\| None | Provider session token for session-aware providers. | | checkpoint_token | str \\| None | Provider checkpoint token for checkpoint-aware providers. | | pending_llm_response | dict \\| None | Serialized LLM response that was received but whose tool calls have not yet been executed. On resume, the runner skips the LLM call and processes these tool calls directly. | | tool_executions | list[dict] | Serialized ToolExecutionRecord entries for all tools executed so far. | | subagent_executions | list[dict] | Serialized SubagentExecutionRecord entries. | | requested_model | str | The model string originally requested by the agent. | | normalized_model | str | The model string after resolution and normalization. | | provider_adapter | str | The provider adapter type used (e.g., openai, litellm). | | final_text | str | The final text output accumulated so far. | | final_structured | dict \\| None | Structured output if the LLM returned schema-validated JSON. | Phase-specific payloads Beyond the runtime snapshot, individual phase checkpoints carry lighter payloads: | Phase | Key payload fields | | --- | --- | | run_started | agent_name, resumed | | step_started | state, message_count | | pre_llm | model, provider, message_count | | post_llm | model, provider, finish_reason, tool_call_count, session_token, checkpoint_token, total_cost_usd | | pre_tool_batch | tool_call_count | | post_tool_batch | tool_calls_total, tool_failures | | run_terminal | state, final_text, requested_model, normalized_model, provider_adapter, terminal_result | Async write-behind behavior By default, checkpoint writes are asynchronous (RunnerConfig.checkpoint_async_writes=True): - Writes are queued and flushed by a background writer. - Repeated runtime_state writes may be coalesced (checkpoint_coalesce_runtime_state=True). - Terminal states perform a bounded flush (checkpoint_flush_timeout_s) before returning. This improves loop throughput while preserving terminal durability. Effect replay and idempotency When a run resumes and re-enters a tool batch, the runner checks for previously persisted effect results before re-executing tools. Each tool call's result is stored with an input_hash (derived from tool name and arguments) and an output_hash. On resume, if a matching effect result exists for a tool call ID with a matching input hash, the stored result is replayed instead of re-executing the tool. This guarantees idempotent resume for tools with side effects. The replayed_effect_count field in the runtime snapshot tracks how many tool calls were satisfied from replay rather than fresh execution. Common failure scenarios **Missing checkpoint** -- Calling runner.resume() with a run_id that has no checkpoint raises AgentCheckpointCorruptionError. This can happen if the memory store was cleared or the run never persisted its first checkpoint (crashed before run_started). **Corrupted payload** -- If the checkpoint record exists but is not a valid dict or is missing required keys, AgentCheckpointCorruptionError is raised. The runner does not attempt partial recovery from corrupted checkpoints. **Pending LLM response corruption** -- If a checkpoint has pending_llm_response set but the serialized response cannot be deserialized, the runner raises AgentCheckpointCorruptionError rather than making a duplicate LLM call. **Stale session tokens** -- Provider session tokens stored in checkpoints may expire between the original run and the resume attempt. The runner passes the stored session_token and checkpoint_token to the provider, but the provider may reject them. In that case, the LLM call fails and follows the normal retry/fallback chain.", + "token_count": 744 + }, + { + "id": "doc_a24b92541657", + "path": "docs/library/configuration-reference.mdx", + "url": "/library/configuration-reference", + "title": "Configuration Reference", + "description": "Every configurable field across Agent, Runner, FailSafeConfig, and SandboxProfile.", + "headings": [ + "Agent", + "Reasoning override precedence", + "RunnerConfig", + "Deep Dive: Interaction Models", + "FailSafeConfig", + "Deep Dive: Failure & Recovery", + "FailurePolicy values", + "SandboxProfile", + "Runner constructor", + "@tool decorator", + "Next steps" + ], + "content_sha256": "70a5ac606bb9dfba32cbba5a3a6f7791cf0437ae14f45dc29bf3491141978df6", + "content": "This page is a single-source reference for every configuration knob in AFK. Each section lists fields with their type, default value, and purpose. Agent The Agent constructor defines what your agent is \u2014 identity, model, tools, and behavior. | Field | Type | Default | Description | | --------------------------- | ----------------- | ---------------- | -------------------------------------------------------------- | | model | str \\| LLM | **required** | LLM model name or pre-built client instance | | name | str | class name | Agent identity for logs, telemetry, and subagent routing | | instructions | str \\| callable | None | System prompt \u2014 static string or callable provider | | instruction_file | str \\| Path | None | Path to a prompt file that must resolve under prompts_dir | | prompts_dir | str \\| Path | None | Root directory for prompt files (env: AFK_AGENT_PROMPTS_DIR) | | tools | list | [] | Typed functions the agent can call | | subagents | list[Agent] | [] | Specialist agents this agent can delegate to | | context_defaults | dict | {} | Default JSON context merged into each run | | inherit_context_keys | list[str] | [] | Context keys accepted from a parent subagent | | model_resolver | callable | None | Custom function to resolve model names to LLM clients | | skills | list[str] | [] | Skill names to resolve under skills_dir | | mcp_servers | list | [] | External MCP server refs whose tools to expose | | skills_dir | str \\| Path | .agents/skills | Root directory for skill definitions | | instruction_roles | list | [] | Callbacks that append dynamic instruction text | | policy_roles | list | [] | Callbacks that can allow/deny/defer runtime actions | | policy_engine | PolicyEngine | None | Deterministic rule engine applied before policy roles | | subagent_router | SubagentRouter | None | Router callback deciding subagent targets | | max_steps | int | 20 | Maximum reasoning/tool loop steps | | tool_parallelism | int | None | Max concurrent tool calls (falls back to fail_safe) | | subagent_parallelism_mode | str | configurable | single, parallel, or configurable | | fail_safe | FailSafeConfig | defaults | Runtime limits and failure policies | | reasoning_enabled | bool \\| None | None | Default request thinking flag for this agent | | reasoning_effort | str \\| None | None | Default request thinking_effort label | | reasoning_max_tokens | int \\| None | None | Default request max_thinking_tokens | | enable_skill_tools | bool | True | Auto-register built-in skill tools | | enable_mcp_tools | bool | True | Auto-register tools from configured MCP servers | | runner | Runner | None | Optional runner override | Reasoning override precedence Reasoning values are resolved in this order: 1. Run context override: context[\\\"_afk\\\"][\\\"reasoning\\\"] 2. Agent defaults: reasoning_enabled, reasoning_effort, reasoning_max_tokens 3. Provider defaults/validation in the LLM layer --- RunnerConfig Passed to Runner(config=RunnerConfig(...)) to control runtime behavior. | Field | Type | Default | Description | | ----------------------------------------- | ---------------- | ------------------------------------------ | ---------------------------------------------------------- | | interaction_mode | str | headless | headless, interactive, or external | | approval_timeout_s | float | 300.0 | Timeout for deferred approval decisions | | input_timeout_s | float | 300.0 | Timeout for deferred user-input decisions | | approval_fallback | str | deny | Fallback when approval times out: allow, deny, defer | | input_fallback | str | deny | Fallback when user input times out | | sanitize_tool_output | bool | True | Sanitize model-visible tool output | | untrusted_tool_preamble | bool | True | Inject untrusted-data warning preamble | | tool_output_max_chars | int | 12_000 | Max tool output characters forwarded to model | | default_sandbox_profile | SandboxProfile | None | Default sandbox profile for tool execution | | sandbox_profile_provider | callable | None | Runtime sandbox profile resolver | | secret_scope_provider | callable | None | Secret-scope resolver per tool call | | default_allowlisted_commands | tuple[str] | ls, cat, head, tail, rg, find, pwd, echo | Allowlisted shell commands for runtime tools | | max_parallel_subagents_global | int | 64 | Global cap for concurrent subagent tasks | | max_parallel_subagents_per_parent | int | 8 | Per-parent-run cap for concurrent subagent fanout | | max_parallel_subagents_per_target_agent | int | 4 | Per-target-agent cap | | subagent_queue_backpressure_limit | int | 512 | Max pending subagent nodes before backpressure | | checkpoint_async_writes | bool | True | Enable background checkpoint/state writes | | checkpoint_queue_maxsize | int | 1024 | Max queued checkpoint write jobs | | checkpoint_flush_timeout_s | float | 10.0 | Timeout for terminal checkpoint flush | | checkpoint_coalesce_runtime_state | bool | True | Coalesce repeated runtime_state checkpoint writes | | debug | bool | False | Enable debug metadata in emitted run events | | debug_config | RunnerDebugConfig \\| None | None | Advanced debug controls (verbosity/content/redaction) | | background_tools_enabled | bool | True | Enable deferred/background tool orchestration | | background_tool_default_grace_s | float | 0.0 | Wait this long after defer to allow fast background completion before continuing | | background_tool_max_pending | int | 256 | Maximum unresolved background tool tickets per run | | background_tool_poll_interval_s | float | 0.5 | Poll interval for external/persisted ticket resolution | | background_tool_result_ttl_s | float | 3600.0 | TTL before pending ticket is marked failed (runtime floor is 1.0s) | | background_tool_interrupt_on_resolve | bool | True | When enabled, resolved tickets can be ingested immediately after defer/grace in the same step | Deep Dive: Interaction Models The interaction_mode setting fundamentally changes how the Runner handles decision points like tool approval or user input requests. - **headless (default)**: The Runner never pauses. - If a policy returns defer or request_user_input, the Runner immediately uses the configured approval_fallback or input_fallback (default: deny). - _Use case:_ Backend workers, cron jobs, automated testing. - **interactive**: The Runner pauses execution and uses the configured InteractionProvider to ask for human input. - For CLI apps, this prints to stdout and reads from stdin. - The run blocks until input is received or approval_timeout_s expires. - _Use case:_ Local CLI tools, scripts run by humans. - **external**: The Runner emits a run_paused event and **suspends execution**. - The run() loop exits (or yields a paused state). The state is persisted to memory. - The system waits for an external API call to runner.resume_with_input(). - _Use case:_ Chatbots, web UIs, Slack bots where the user is asynchronous. --- FailSafeConfig Passed to Agent(fail_safe=FailSafeConfig(...)) to set runtime limits and failure policies. | Field | Type | Default | Description | | ------------------------------ | --------------- | --------------------- | --------------------------------------------- | | llm_failure_policy | FailurePolicy | retry_then_fail | Strategy when LLM calls fail | | tool_failure_policy | FailurePolicy | continue_with_error | Strategy when tool calls fail | | subagent_failure_policy | FailurePolicy | continue | Strategy when subagent calls fail | | approval_denial_policy | FailurePolicy | skip_action | Strategy when approval is denied or times out | | max_steps | int | 20 | Maximum run loop iterations | | max_wall_time_s | float | 300.0 | Maximum wall-clock runtime in seconds | | max_llm_calls | int | 50 | Maximum number of LLM invocations | | max_tool_calls | int | 200 | Maximum number of tool invocations | | max_parallel_tools | int | 16 | Max concurrent tools per batch | | max_subagent_depth | int | 3 | Maximum subagent recursion depth | | max_subagent_fanout_per_step | int | 4 | Maximum subagents selected per step | | max_total_cost_usd | float \\| None | None | Cost ceiling for run termination | | fallback_model_chain | list[str] | [] | Ordered fallback model list for LLM retries | | breaker_failure_threshold | int | 5 | Circuit breaker open threshold | | breaker_cooldown_s | float | 30.0 | Circuit breaker cooldown window in seconds | Deep Dive: Failure & Recovery FailSafeConfig controls the agent's resilience. The policies work in a hierarchy: 1. **Lower-level retries**: Transient errors (network glitches, rate limits) are retried automatically by the LLM client, guided by AFK_LLM_MAX_RETRIES. 2. **llm_failure_policy**: If the LLM call fails after all retries (or hits a terminal error like 401 Unauthorized): - retry_then_fail: Tries a few more times at the agent level, then fails the run. - retry_then_degrade: Tries again, then marks the run as degraded but returns partial results (useful for \"best effort\" responses). 3. **tool_failure_policy**: If a tool raises an exception: - continue_with_error (default): The error message is fed back to the model. The model can then try to fix its mistake or apologize. **This is usually the best setting** for capable models. - fail_run: Immediately stops the run. Use this for critical transactional agents where any error is unacceptable. 4. **Circuit Breakers**: - If a model provider fails breaker_failure_threshold times in a row, the circuit opens. - Subsequent calls fail _instantly_ without network traffic until breaker_cooldown_s passes. - This protects your system (and wallet) from hammering a down service. FailurePolicy values | Value | Behavior | | --------------------- | ---------------------------------------------------- | | retry_then_fail | Retry with backoff, then fail the run | | retry_then_degrade | Retry with backoff, then degrade (partial result) | | retry_then_continue | Retry with backoff, then continue without the result | | fail_fast | Fail immediately, no retries | | fail_run | Fail the entire run | | continue_with_error | Continue, passing the error to the model | | continue | Continue silently, ignoring the failure | | skip_action | Skip the action entirely | --- SandboxProfile Controls execution restrictions for tool handlers. Configured via RunnerConfig.default_sandbox_profile. | Field | Type | Default | Description | | -------------------------- | --------------- | --------- | ---------------------------------------------- | | profile_id | str | default | Profile identifier for logs and policy | | allow_network | bool | False | Whether tools can make network requests | | allow_command_execution | bool | False | Whether tools can execute shell commands | | allowed_command_prefixes | list[str] | [] | Allowed command prefixes (empty = none) | | deny_shell_operators | bool | True | Block pipes, redirects, semicolons | | allowed_paths | list[str] | [] | Restrict file access to these paths | | denied_paths | list[str] | [] | Explicitly deny access to these paths | | command_timeout_s | float \\| None | None | Kill commands after this duration | | max_output_chars | int | 20_000 | Truncate command output beyond this limit | --- Runner constructor The Runner accepts these arguments directly (outside of RunnerConfig): | Argument | Type | Default | Description | | ---------------------- | ---------------------- | ---------------- | ------------------------------------------------------ | | memory_store | MemoryStore | None | Memory backend (resolved from env if None) | | interaction_provider | InteractionProvider | None | Human-in-the-loop provider (required for non-headless) | | policy_engine | PolicyEngine | None | Deterministic policy engine shared across runs | | telemetry | str \\| TelemetrySink | None | Telemetry sink instance or backend id | | telemetry_config | dict | None | Backend-specific sink configuration | | config | RunnerConfig | RunnerConfig() | Runner configuration | --- @tool decorator | Argument | Type | Default | Description | | ---------------- | ------------------ | ------------- | ----------------------------------------------------------------- | | args_model | Type[BaseModel] | **required** | Pydantic model for argument validation | | name | str | function name | Tool name exposed to the LLM | | description | str | docstring | Tool description for the LLM | | timeout | float | None | Execution timeout in seconds | | prehooks | list[PreHook] | None | Argument transform hooks | | posthooks | list[PostHook] | None | Output transform hooks | | middlewares | list[Middleware] | None | Execution wrappers (logging, caching, etc.) | | raise_on_error | bool | False | Raise exceptions instead of returning ToolResult(success=False) | Next steps Environment variable defaults and backend selection. Quick import reference.", + "token_count": 1143 + }, + { + "id": "doc_e7d508371e5c", + "path": "docs/library/core-runner.mdx", + "url": "/library/core-runner", + "title": "Core Runner", + "description": "Execute agents \u2014 lifecycle, API modes, streaming, and state management.", + "headings": [ + "Quick example", + "Synchronous (simplest)", + "Three API modes", + "Run lifecycle", + "Terminal states", + "The step loop", + "Runner configuration", + "Run handles and lifecycle control", + "Monitor events in real time", + "Lifecycle controls", + "Wait for the final result", + "Thread-based memory", + "Turn 1", + "Turn 2 \u2014 agent remembers Turn 1", + "Resume from checkpoint", + "Resume an interrupted run", + "Compact long threads", + "Summarize old messages to control storage growth", + "AgentResult reference", + "ToolExecutionRecord fields", + "Next steps" + ], + "content_sha256": "1a47bf2eb1a77425072db12e35a71d725d7b826102f87dd74a81c747487df506", + "content": "The **Runner** is the execution engine that runs agents. It manages the step loop, LLM calls, tool execution, state persistence, and telemetry. You create a Runner, hand it an Agent, and get back an AgentResult. Quick example Three API modes Blocks until complete. Best for scripts, tests, and simple integrations. Awaitable. Best for async applications and API servers. Real-time events. Best for chat UIs and CLI tools. Run lifecycle Every agent run follows this state machine: Terminal states | State | Meaning | final_text | | ------------- | ---------------------------------------- | ------------------------------------------------------------------------------------------ | | completed | Run finished successfully | Model's response | | failed | Unrecoverable error occurred | Error message | | degraded | Partial result (some failures tolerated) | Best-effort response | | cancelled | Caller cancelled the run | Empty or partial | | interrupted | Timeout or external interrupt | Empty or partial | The step loop Each \"step\" is one iteration of the agent's decision cycle: The runner constructs an LLMRequest with the conversation history, tool schemas, and model configuration. The request is sent through the LLM runtime (with retry, circuit breaker, rate limiting, and caching policies). If the LLM returns **text only** \u2192 the run is complete. If it returns **tool calls** \u2192 proceed to tool execution. Each tool call is validated, policy-checked, executed, and its output is sanitized and fed back to the LLM. The runner returns to Step 1 for the next LLM turn. This continues until the model produces a text-only response or a limit is hit. Runner configuration Run handles and lifecycle control For advanced control, use run_handle(): Thread-based memory Pass a thread_id to maintain conversation context across runs: Resume from checkpoint Compact long threads AgentResult reference | Field | Type | Description | | --------------------- | ------------------------------- | ----------------------------------------------------------------------------- | | final_text | str | The agent's final response | | state | str | Terminal state: completed, failed, degraded, cancelled, interrupted | | run_id | str | Unique run identifier | | thread_id | str | Thread identifier for memory continuity | | tool_executions | list[ToolExecutionRecord] | All tool calls with name, success, output, latency | | subagent_executions | list[SubagentExecutionRecord] | All subagent invocations | | usage_aggregate | UsageAggregate | Token counts aggregate across LLM calls | | state_snapshot | dict[str, JSONValue] | Final runtime counters/snapshot metadata | ToolExecutionRecord fields AgentResult.tool_executions entries include: | Field | Type | Description | | --- | --- | --- | | tool_name | str | Tool name | | tool_call_id | str \\| None | Tool call id from the model/provider | | success | bool | Execution success | | output | JSONValue \\| None | Tool output payload | | error | str \\| None | Error text (when failed) | | latency_ms | float \\| None | Tool latency | | agent_name | str \\| None | Agent that executed the tool (parent or subagent) | | agent_depth | int \\| None | Nesting depth where tool ran | | agent_path | str \\| None | Agent lineage path for nested calls | Next steps Real-time event streaming for chat UIs. Thread-based state persistence and resume.", + "token_count": 326 + }, + { + "id": "doc_3f1383f72c5f", + "path": "docs/library/debugger.mdx", + "url": "/library/debugger", + "title": "Debugger", + "description": "Debug-mode runner setup with redacted event payloads and live event taps.", + "headings": [ + "Quick start", + "Attach to a run handle", + "Redaction behavior", + "Verbosity modes", + "Background tool scenario (coding + build + docs)", + "External worker completion" + ], + "content_sha256": "7349668744c3913398f016c1e3b0f07fd6b3659d0cf075176717e7a6a4003dcb", + "content": "Use afk.debugger.Debugger to run agents in a structured debug mode without changing your core runtime architecture. Quick start You can also enable debug mode directly on the runner: Attach to a run handle Redaction behavior When redact_secrets=True, payload keys containing any of these markers are masked: - api_key - token - secret - authorization - password Verbosity modes - basic: lightweight metadata and step markers. - detailed: includes payload previews and step metadata. - trace: highest detail for deep diagnostics. Background tool scenario (coding + build + docs) Flow: 1. Agent writes code. 2. Agent calls build_project and receives tool_deferred. 3. Agent continues with write_docs or other tasks. 4. Runner emits tool_background_resolved when build completes. 5. Agent uses resolved build output in a later step to finalize/fix. External worker completion For out-of-process execution, an external worker can complete a ticket by writing background state into memory: The runner poller detects this update, emits tool_background_resolved or tool_background_failed, and injects a synthetic tool message for the next step.", + "token_count": 122 + }, + { + "id": "doc_067033cfc945", + "path": "docs/library/deployment.mdx", + "url": "/library/deployment", + "title": "Deployment", + "description": "Deploy AFK agents to production with Docker, Kubernetes, and scaling patterns.", + "headings": [ + "Docker deployment", + "Basic Dockerfile", + "Install dependencies", + "Copy application code", + "Run your application entrypoint that creates AFK agents/runners", + "Production Dockerfile with multi-stage build", + "Production image", + "Run as non-root user", + "docker-compose.yml", + "Environment configuration", + "Required environment variables", + "LLM Provider", + "or", + "Memory backend", + "Queue backend ", + "Observability", + "Server mode", + "Production configuration file", + "Scaling patterns", + "Horizontal scaling with workers", + "Kubernetes deployment", + "Kubernetes HPA for auto-scaling", + "Health checks", + "Database schema", + "SQLite (development)", + "PostgreSQL", + "Security checklist", + "Monitoring", + "Next steps" + ], + "content_sha256": "7cc0c692d113fd9dc1af4de8235cb8b0c1c4b512d6079d164413d60837a5aa32", + "content": "This guide covers deploying AFK agents to production environments, from single-container setups to distributed, multi-worker deployments. Docker deployment Basic Dockerfile Production Dockerfile with multi-stage build docker-compose.yml Environment configuration Required environment variables Production configuration file Create config/production.yaml: Scaling patterns Horizontal scaling with workers Kubernetes deployment Kubernetes HPA for auto-scaling Health checks Implement health endpoints in your server: Database schema SQLite (development) SQLite requires no schema setup \u2014 tables are created automatically on first use. PostgreSQL Security checklist Store API keys in secrets managers (AWS Secrets Manager, HashiCorp Vault, Kubernetes Secrets). Never commit keys to version control. Restrict traffic between services. Agents should only reach LLM providers and necessary databases. Configure rate limits on public endpoints to prevent abuse. Always set max_total_cost_usd in FailSafeConfig for production agents. Enable telemetry export to your logging infrastructure for compliance. Monitoring Key metrics to track: | Metric | What it indicates | Alert threshold | |--------|------------------|-----------------| | agent.run.duration | How long runs take | > 60s p95 | | agent.run.cost | Token spend per run | > $0.50 per run | | agent.run.failures | Failed runs | > 5% error rate | | llm.latency | LLM response time | > 10s p95 | | llm.errors | LLM API errors | > 1% error rate | | queue.depth | Pending tasks | > 100 items | | queue.dead_letters | Failed tasks | > 0 | Next steps Set up telemetry and alerting for production monitoring. Security hardening checklist and best practices. CI-gated quality checks for agent releases. Production patterns and anti-patterns.", + "token_count": 191 + }, + { + "id": "doc_d147a81bac29", + "path": "docs/library/developer-guide.mdx", + "url": "/library/developer-guide", + "title": "Developer Guide", + "description": "Maintainer workflow for changing AFK itself.", + "headings": [ + "Local setup", + "Repository boundaries", + "Change workflow", + "Documentation workflow", + "High-risk areas", + "Public API checklist", + "Next steps" + ], + "content_sha256": "8ac363b758f8ae9d391d26db304b7bcf931f1fb24bbf0dd159c8de2e3ebc1789", + "content": "This page is for contributors changing the AFK framework. If you are building an application with AFK, start with Quickstart or Building with AI. Local setup AFK targets Python 3.13+. Common commands: Preview docs: Regenerate agent-facing docs and skill indexes: Install the repository skills with Vercel's Skills CLI: Use afk-coder when building with AFK. Use afk-maintainer when reviewing or changing AFK itself. Repository boundaries | Package | Responsibility | | --- | --- | | afk.agents | Agent definitions, policy, prompts, skills, lifecycle, workflow, A2A | | afk.core | Runner, interaction providers, streaming handles, telemetry contracts | | afk.llms | Provider-portable LLM runtime, adapters, retry/timeout/cache/routing policies | | afk.tools | Typed tools, decorators, registry, sandboxing, output limiting | | afk.memory | Memory stores, checkpoints, retention, compaction, vector helpers | | afk.queues | Async task queues, execution contracts, workers, retry/DLQ behavior | | afk.observability | Telemetry collectors, projectors, exporters | | afk.mcp, afk.messaging, afk.debugger, afk.evals | Optional integration and quality layers | Keep the Agent/Runner/Runtime boundary intact: - agents are configuration; - runners execute; - tools, memory, queues, LLM adapters, and telemetry provide runtime capabilities. Change workflow 1. Identify the public contract affected by the change. 2. Inspect the existing module and tests before editing. 3. Make the smallest behavior change that preserves package boundaries. 4. Add or update focused tests for success and failure paths. 5. Update docs when behavior, public imports, configuration, env vars, or examples change. 6. Regenerate agent-facing docs when docs navigation, snippets, or skill metadata change. Documentation workflow User-facing docs must: - import from public package surfaces such as afk.agents and afk.core; - explain behavior before internals; - use Python 3.13+ guidance; - distinguish afk-py distribution install from afk imports; - include new pages in docs/docs.json; - keep examples aligned with current AgentResult, RunnerConfig, and FailSafeConfig fields. Maintainer docs may reference internal files, but should state which public contract or invariant is being protected. High-risk areas Use targeted tests when touching: | Area | Risk | | --- | --- | | src/afk/core/runner/ | execution loop, checkpoints, resume, policy/failure routing | | src/afk/core/streaming.py | stream events and handle lifecycle | | src/afk/tools/core/base.py, src/afk/tools/registry.py | tool invocation semantics | | src/afk/tools/security.py | sandbox, secret scope, output limiting | | src/afk/llms/runtime/ | retries, circuit breakers, rate limits, caching, routing | | src/afk/memory/ | persistence, checkpoint keys, compaction, vector search | | src/afk/queues/ | execution contracts, retry/DLQ, worker lifecycle | | src/afk/agents/a2a/ | auth, delivery guarantees, protocol compatibility | Public API checklist Before changing exports or constructor fields: - update package-level __all__; - update API Reference; - update Configuration Reference if defaults changed; - update snippets that use the changed field; - add public-import tests where useful. Next steps Package boundaries and runtime flow. Maintainer rules for stable imports and docs examples. Behaviors that tests protect. Generated source-level symbol map.", + "token_count": 387 + }, + { + "id": "doc_61fd9f682847", + "path": "docs/library/environment-variables.mdx", + "url": "/library/environment-variables", + "title": "Environment Variables", + "description": "Environment variable reference for AFK defaults and backend configuration.", + "headings": [ + "LLM defaults", + "Memory", + "Override in code \u2014 takes precedence over env vars", + "Queue", + "Prompts", + "Runner and tool command defaults", + "MCP server", + "A2A", + "Precedence", + "Next steps" + ], + "content_sha256": "89122dd488be8932d0ac65bf7169acebead8cb49223e178df9d0f049082cd073", + "content": "AFK is configured primarily through code. Environment variables provide fallback defaults for LLM settings, memory backends, queues, and prompt directories. **Runtime configuration APIs always take precedence.** All variables are optional. If unset, AFK uses the defaults listed below. LLM defaults These variables configure the default LLM provider and behavior. They are read by LLMSettings.from_env() at startup. | Variable | Default | Description | | ------------------------------- | -------------- | ------------------------------------------------------------ | | AFK_LLM_PROVIDER | litellm | Default provider id (openai, litellm, anthropic_agent) | | AFK_LLM_PROVIDER_ORDER | _(none)_ | Comma-separated provider preference order for routing helpers | | AFK_LLM_MODEL | gpt-4.1-mini | Default model name | | AFK_EMBED_MODEL | _(none)_ | Embedding model for vector operations | | AFK_LLM_API_BASE_URL | _(none)_ | Provider API base URL | | AFK_LLM_API_KEY | _(none)_ | Provider API key | | AFK_LLM_TIMEOUT_S | 30 | Request timeout in seconds | | AFK_LLM_STREAM_IDLE_TIMEOUT_S | 45 | Stream idle timeout in seconds | | AFK_LLM_MAX_RETRIES | 3 | Retry attempts on transient failures | | AFK_LLM_BACKOFF_BASE_S | 0.5 | Exponential backoff base in seconds | | AFK_LLM_BACKOFF_JITTER_S | 0.15 | Random jitter added to backoff | | AFK_LLM_JSON_MAX_RETRIES | 2 | Structured output repair attempts | | AFK_LLM_MAX_INPUT_CHARS | 200000 | Input truncation ceiling in characters | **API keys for specific providers** (e.g. OPENAI_API_KEY, ANTHROPIC_API_KEY) are read by the underlying provider libraries, not by AFK directly. Set AFK_LLM_API_KEY only if you want a single key shared across providers. Memory Configure the persistent memory backend for thread-based conversations. | Variable | Default | Description | | -------------------- | -------------------- | ------------------------------------------------------- | | AFK_MEMORY_BACKEND | sqlite | Backend type: inmemory/memory, sqlite, redis, postgres | | AFK_SQLITE_PATH | afk_memory.sqlite3 | SQLite file path | | AFK_REDIS_URL | _(none)_ | Redis connection URL (for example redis://localhost:6379) | | AFK_REDIS_HOST | localhost | Redis host when URL is not set | | AFK_REDIS_PORT | 6379 | Redis port when URL is not set | | AFK_REDIS_DB | 0 | Redis DB when URL is not set | | AFK_REDIS_PASSWORD | _(none)_ | Redis password when URL is not set | | AFK_REDIS_EVENTS_MAX | 2000 | Max Redis memory events per thread | | AFK_PG_DSN | _(none)_ | PostgreSQL connection string | | AFK_PG_HOST | localhost | PostgreSQL host when DSN is not set | | AFK_PG_PORT | 5432 | PostgreSQL port when DSN is not set | | AFK_PG_USER | postgres | PostgreSQL user when DSN is not set | | AFK_PG_PASSWORD | _(none)_ | PostgreSQL password when DSN is not set | | AFK_PG_DB | afk | PostgreSQL database when DSN is not set | | AFK_PG_SSL | false | Enable PostgreSQL SSL | | AFK_PG_POOL_MIN | 1 | PostgreSQL pool minimum size | | AFK_PG_POOL_MAX | 10 | PostgreSQL pool maximum size | | AFK_VECTOR_DIM | _(required for Postgres)_ | Vector dimension for Postgres memory search | Queue Configure the task queue backend for async agent execution. | Variable | Default | Description | | ---------------------------------- | ---------- | --------------------------------- | | AFK_QUEUE_BACKEND | inmemory | Backend type: inmemory, redis | | AFK_QUEUE_REDIS_URL | falls back to AFK_REDIS_URL | Redis URL for queue backend | | AFK_QUEUE_REDIS_HOST | falls back to AFK_REDIS_HOST, then localhost | Redis queue host | | AFK_QUEUE_REDIS_PORT | falls back to AFK_REDIS_PORT, then 6379 | Redis queue port | | AFK_QUEUE_REDIS_DB | falls back to AFK_REDIS_DB, then 0 | Redis queue DB | | AFK_QUEUE_REDIS_PASSWORD | falls back to AFK_REDIS_PASSWORD | Redis queue password | | AFK_QUEUE_REDIS_PREFIX | afk:queue | Redis key prefix | | AFK_QUEUE_RETRY_BACKOFF_BASE_S | 0.5 | Retry base delay in seconds | | AFK_QUEUE_RETRY_BACKOFF_MAX_S | 30 | Retry max delay in seconds | | AFK_QUEUE_RETRY_BACKOFF_JITTER_S | 0.2 | Random jitter added to backoff | The inmemory queue backend does not persist across restarts. Use redis for production workloads that need reliable task delivery. Prompts | Variable | Default | Description | | ----------------------- | ---------------- | -------------------------------------- | | AFK_AGENT_PROMPTS_DIR | .agents/prompt | Root directory for system prompt files | Agents resolve instruction_file paths relative to this directory. See System Prompts for details. Runner and tool command defaults | Variable | Default | Description | | --- | --- | --- | | AFK_ALLOWED_COMMANDS | _(none)_ | Comma-separated default allowlist for runtime command tools | Runner constructors and RunnerConfig fields remain the preferred way to set command policy. Use this environment variable only for process-wide defaults. MCP server These variables are read by the MCP server configuration helpers in afk.config. | Variable | Default | Description | | --- | --- | --- | | AFK_CORS_ORIGINS | _(none)_ | Comma-separated CORS origins | | AFK_MCP_NAME | afk-mcp-server | Server name | | AFK_MCP_VERSION | 1.0.0 | Server version string | | AFK_MCP_HOST | 0.0.0.0 | Bind host | | AFK_MCP_PORT | 8000 | Bind port | | AFK_MCP_INSTRUCTIONS | _(none)_ | Optional server instructions | | AFK_MCP_PATH | /mcp | HTTP MCP endpoint path | | AFK_MCP_SSE_PATH | /mcp/sse | SSE endpoint path | | AFK_MCP_HEALTH_PATH | /health | Health endpoint path | | AFK_MCP_ENABLE_SSE | true | Enable SSE endpoint | | AFK_MCP_ENABLE_HEALTH | true | Enable health endpoint | | AFK_MCP_ALLOW_BATCH | true | Allow batched MCP requests | A2A No default environment variables are required. Configure A2A host and authentication in code for an explicit security posture. See A2A for details. Precedence Configuration is resolved in this order (highest priority first): 1. **Constructor arguments** \u2014 Runner(memory_store=...), Agent(model=...) 2. **RunnerConfig / LLMSettings objects** \u2014 passed to constructors 3. **Environment variables** \u2014 fallback defaults listed on this page 4. **Built-in defaults** \u2014 hardcoded in the source Next steps Full reference for all configurable fields across Agent, Runner, and more. Runner lifecycle and configuration options.", + "token_count": 557 + }, + { + "id": "doc_c93c115aeb85", + "path": "docs/library/evals.mdx", + "url": "/library/evals", + "title": "Evals", + "description": "Behavioral testing for agent quality \u2014 assertions, budgets, and CI integration.", + "headings": [ + "Your first eval", + "Eval lifecycle", + "Eval case types", + "Assertions", + "Suite configuration", + "CI integration", + ".github/workflows/evals.yml", + "Release gating", + "Next steps" + ], + "content_sha256": "89a16478fcd1a7d21a0539b75ab780f2634444483626668f05e16f25320bd0ed", + "content": "AFK evals run agents against named inputs and check the resulting state, text, tool usage, budgets, and telemetry. Use them for prompt changes, tool changes, routing changes, and regression tests. They can run against real providers, test adapters, or agents configured with deterministic tools. Your first eval Eval lifecycle Each case specifies an agent and input message. Suite-level assertions and budgets verify the result. The scheduler runs cases sequentially or in parallel, respecting concurrency limits. Each case runs through the same runner path your application uses. Assertions verify the result \u2014 text content, state, tool usage, cost, latency, etc. Budget limits gate individual case costs and the total suite cost. Pass/fail results, assertion details, and metrics are collected into a report. Eval case types Verify correct behavior under normal conditions. Verify graceful handling of errors and edge cases. Verify that the agent uses tools correctly. Verify that the agent stays within cost limits. Assertions Assertions are suite-level callables. Import the built-ins from afk.evals, or implement the EvalAssertion protocol. Suite configuration CI integration Run evals in your CI pipeline to gate releases: **Set a budget for CI evals.** Without a budget, a broken prompt can drain your API credits during a CI run. Use EvalBudget(max_total_cost_usd=2.00) as a reasonable CI limit. Release gating Gate releases on eval pass rate: Next steps Security boundaries and production hardening. Production playbook with anti-patterns.", + "token_count": 181 + }, + { + "id": "doc_a5120de838fb", + "path": "docs/library/examples/index.mdx", + "url": "/library/examples/", + "title": "Getting Started", + "description": "Scenario-based examples for every major AFK feature.", + "headings": [ + "Which example should I start with?", + "Complexity progression", + "Scenario catalog" + ], + "content_sha256": "e209b35c682236c60b0dd47344bf686c23ea09e6000e11aab2c25e3078800062", + "content": "This catalog provides runnable code examples for every major AFK capability. Each example is self-contained and demonstrates a specific pattern -- from the simplest possible agent run to advanced tool security configurations. The examples are ordered by complexity. If you are new to AFK, start at the top and work your way down. Each example builds on concepts introduced in the previous ones. Which example should I start with? - **First time using AFK?** Start with Minimal Chat Agent. It shows the absolute simplest agent run in under 10 lines of code. - **Need to gate dangerous actions?** Go to Policy + HITL. It demonstrates how policy engines and human approval work together. - **Building a multi-agent system?** See Subagent Router. It shows how to delegate work to specialist subagents and merge results. - **Need to persist and resume runs?** Check Resume + Compact. It covers checkpoint-based resumption and memory compaction. - **Using LLMs without the agent loop?** See Structured Output. It shows how to use LLMBuilder directly with Pydantic models. - **Locking down tool capabilities?** Go to Tool Security. It demonstrates scoped tool registration and policy gates. Complexity progression | Level | Example | What You Learn | | ------------ | -------------------- | ---------------------------------------------------------- | | Beginner | Minimal Chat Agent | Agent + Runner basics, final_text access | | Beginner | Structured Output | Direct LLM usage, Pydantic schema validation | | Intermediate | Policy + HITL | Policy engine, approval flows, interaction modes | | Intermediate | Tool Security | Tool scoping, sandbox profiles, policy gates | | Intermediate | Hooks + Middleware | Pre/post tool execution hooks, middleware chains | | Intermediate | Runtime Tools | Built-in file, search, and command tools | | Intermediate | Prompt Loader | External prompt files, template variables | | Advanced | Subagent Router | Multi-agent coordination, delegation patterns | | Advanced | Resume + Compact | Checkpointing, memory management, long-running workflows | | Intermediate | Streaming + Memory | Real-time streaming with multi-turn thread persistence | | Intermediate | Cost Monitoring | Budget limits, real-time cost tracking, batch budgets | | Advanced | MCP Client | Connect to external MCP servers, hybrid local+remote tools | | Advanced | Multi-Model Fallback | Fallback chains, circuit breakers, model tier strategies | | Advanced | Production Client | Timeout middleware, Redis connection pooling, graceful shutdown | Scenario catalog The simplest possible AFK agent. Define an agent, run it synchronously, and read the output. Start here. Gate sensitive actions through a policy engine and route approval requests to a human operator. Delegate workload to specialist subagents using a coordinator pattern. Merge outputs into a unified response. Checkpoint a run, resume it later from the last known state, and compact memory to control storage growth. Use the LLM builder directly (without the agent loop) to get schema-validated structured responses via Pydantic. Register destructive tools with tight scoping, enforce sandbox profiles, and gate actions through policy. Attach pre-execution and post-execution hooks to tools. Chain middleware for logging, validation, and transformation. Use AFK's built-in file reading, directory listing, and command execution tools with agents. Load system prompts from external files with template variable substitution and automatic caching. Combine real-time streaming with thread-based memory for multi-turn chat UIs. Track and control agent costs using FailSafeConfig budgets and telemetry events. Connect to external MCP servers, discover tools, and combine with local tools. Configure fallback model chains for LLM resilience, circuit breakers, and cost optimization. Production-ready LLM client with timeout middleware, Redis connection pooling, and graceful shutdown handling.", + "token_count": 412 + }, + { + "id": "doc_4055659fe573", + "path": "docs/library/failure-policy-matrix.mdx", + "url": "/library/failure-policy-matrix", + "title": "Failure Policy Matrix", + "description": "How errors flow through the system \u2014 classification, handling, and escalation.", + "headings": [ + "Error classification", + "Failure decision flow", + "Failure matrix by source", + "LLM failures", + "Tool failures", + "Subagent failures", + "Infrastructure failures", + "Failure policies", + "Budget-triggered limits", + "Next steps" + ], + "content_sha256": "3e2a501934ed7fabdbb4f5ccd429435c4948ed699b688168a9746b611f61f030", + "content": "AFK classifies every error and applies a policy-driven response. Understanding the failure matrix helps you configure the right behavior for your use case. Error classification Every error is classified into one of three categories: | Classification | Meaning | Default behavior | | -------------- | ---------------------------------------------- | ------------------------------ | | **Retryable** | Transient failure, may succeed on retry | Retry with exponential backoff | | **Terminal** | Permanent failure, will not recover | Stop and report error | | **Non-fatal** | Something went wrong, but the run can continue | Log warning, continue | Failure decision flow Failure matrix by source LLM failures | Error | Classification | Example | | -------------------------- | ------------------------ | ------------------------------------- | | Rate limit (429) | Retryable | \"Rate limit exceeded, retry after 2s\" | | Server error (500/502/503) | Retryable | \"Internal server error\" | | Timeout | Retryable | \"Request timed out after 60s\" | | Auth error (401/403) | Terminal | \"Invalid API key\" | | Invalid request (400) | Terminal | \"Model does not exist\" | | Circuit breaker open | Terminal (with fallback) | \"Circuit breaker open for provider\" | **Configuration:** Tool failures | Error | Classification | Example | | ----------------- | -------------- | --------------------------------------------------------- | | Validation error | Non-fatal | \"Invalid arguments\" (returned to LLM for self-correction) | | Handler exception | Configurable | \"Tool raised an error\" | | Timeout | Configurable | \"Tool exceeded 10s timeout\" | | Policy denial | Non-fatal | \"Action denied by policy\" (returned to LLM) | **Configuration:** Subagent failures | Error | Classification | Example | | --------------------- | -------------- | --------------------------------------------------- | | Subagent run failed | Configurable | \"Subagent 'researcher' completed with state=failed\" | | Subagent timeout | Configurable | \"Subagent exceeded wall time\" | | Join policy violation | Terminal | \"Required subagent failed (all_required policy)\" | **Configuration:** Infrastructure failures | Error | Classification | Example | | -------------------------- | ------------------ | ------------------------------ | | Memory backend unavailable | Non-fatal | \"Could not persist checkpoint\" | | Telemetry export failed | Non-fatal (silent) | \"OTEL exporter timed out\" | | Queue push failed | Retryable | \"Redis connection refused\" | Failure policies Any failure causes the run to fail immediately. **Use when:** All operations are critical and partial results are worse than no results. Failures are tolerated. The run completes with state=\"degraded\" instead of \"completed\". **Use when:** Partial results are better than no results (e.g., some tool fails but the agent can still answer). Failures are logged but ignored. The run continues as if nothing happened. **Use when:** The failing component is non-essential (e.g., analytics tool, optional enrichment). Budget-triggered limits When a budget limit is hit, the run is stopped immediately: | Limit | Triggered by | Run state | | -------------------- | ------------------------ | ---------------------- | | max_steps | Step count exceeded | failed or degraded | | max_tool_calls | Tool call count exceeded | failed or degraded | | max_total_cost_usd | Estimated cost exceeded | failed | | max_wall_time_s | Wall time exceeded | interrupted | Next steps Security boundaries and hardening checklist. Run lifecycle and state management.", + "token_count": 310 + }, + { + "id": "doc_2a18de009d56", + "path": "docs/library/full-module-reference.mdx", + "url": "/library/full-module-reference", + "title": "Full Module Reference", + "description": "Module inventory and responsibilities.", + "headings": [ + "Module dependencies", + "Extension points", + "`afk.agents`", + "`afk.core`", + "`afk.llms`", + "`afk.tools`", + "`afk.memory`", + "`afk.queues`", + "`afk.observability`", + "`afk.evals`", + "`afk.mcp`", + "`afk.messaging`" + ], + "content_sha256": "294699e90c8917af6690bcdb9d7d9511ab0ed5cb92595b96f6436621c88ad4e1", + "content": "The Agent Forge Kit (AFK) Python SDK is organized into top-level packages, each owning a distinct responsibility. This page summarizes the package map, key public imports, and extension boundaries. Use this reference when you need to find which package owns a capability. For field-level details, read the focused reference pages linked from the sidebar. Module dependencies The AFK architecture is organized around a few stable boundaries: - **afk.agents** owns declarative agent configuration, policy, prompts, skills, delegation metadata, and A2A contracts. - **afk.core** owns execution: runner lifecycle, streaming, interaction providers, delegation scheduling, checkpointing, memory wiring, and telemetry wiring. - **afk.llms** owns model provider adapters, runtime policies, structured output helpers, routing, caching, and LLM request/response types. - **afk.tools** owns tool definitions, registries, hooks, middleware, sandbox profiles, and prebuilt runtime tools. Extension points The framework is designed to be extended at specific protocol boundaries. **Do not subclass internal classes**. Instead, implement these protocols: - **InteractionProvider** (afk.core): Build custom human-in-the-loop interfaces (for example Slack, Discord, or web approval UIs). - **MemoryStore** (afk.memory): Add support for new databases (Mongo, DynamoDB). - **LLMProvider** (afk.llms): Integrate new model providers (local inference, exotic APIs). - **TelemetrySink** (afk.observability): Export metrics/traces to custom backends (Datadog, Honeycomb). --- afk.agents Agent definitions, lifecycle types, delegation, policy, A2A protocol, and error hierarchy. **Sub-modules:** | Sub-module | Responsibility | | ----------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | afk.agents.core | Agent and BaseAgent definitions, ChatAgent variant. | | afk.agents.types | Runtime types: AgentResult, AgentRunEvent, AgentRunHandle, AgentState, PolicyEvent, PolicyDecision, FailSafeConfig, UsageAggregate, execution records. | | afk.agents.policy | PolicyEngine, PolicyRule, PolicyEvaluation, policy subject inference. | | afk.agents.delegation | DelegationPlan, DelegationNode, DelegationEdge, RetryPolicy, JoinPolicy, DelegationResult. | | afk.agents.a2a | InternalA2AProtocol, A2A auth providers (AllowAllA2AAuthProvider, APIKeyA2AAuthProvider, JWTA2AAuthProvider), A2AServiceHost, delivery stores. | | afk.agents.contracts | AgentCommunicationProtocol, AgentInvocationRequest, AgentInvocationResponse, AgentDeadLetter. | | afk.agents.prompts | PromptStore, prompt file resolution, template rendering. | | afk.agents.lifecycle | Checkpoint versioning, schema migration, runtime helpers (circuit breaker, effect journal, state snapshots). | | afk.agents.skills | SkillStore, SkillDoc, SKILL.md parsing, checksum verification, and process-wide caching. | | afk.agents.security | Prompt-injection sanitization, untrusted tool-output redaction, and channel markers for trusted vs untrusted content. | | afk.agents.errors | Full error hierarchy: AgentError, AgentExecutionError, AgentCancelledError, SubagentRoutingError, etc. | **Key imports:** afk.core Runner execution engine, streaming, interaction providers, and delegation engine. **Sub-modules:** | Sub-module | Responsibility | | ---------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------- | | afk.core.runner | Runner class with run(), run_sync(), run_handle(), run_stream(), resume(), compact_thread(). RunnerConfig for safety and behavior defaults. | | afk.core.streaming | AgentStreamHandle, AgentStreamEvent, stream event constructors (text_delta, tool_started, stream_completed). | | afk.core.interaction | InteractionProvider protocol, HeadlessInteractionProvider, InMemoryInteractiveProvider. | | afk.core.runtime | DelegationEngine, DelegationPlanner, DelegationScheduler, backpressure and graph validation. | **Key imports:** afk.llms LLM client builder, provider registry, runtime policies, caching, routing, middleware, and type definitions. **Sub-modules:** | Sub-module | Responsibility | | --------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------ | | afk.llms.builder | LLMBuilder -- fluent builder for constructing LLM client instances. | | afk.llms.providers | Provider registry: OpenAIProvider, LiteLLMProvider, AnthropicAgentProvider, register_llm_provider(). | | afk.llms.runtime | LLMClient with production policies: RetryPolicy, TimeoutPolicy, RateLimitPolicy, CircuitBreakerPolicy, HedgingPolicy, CachePolicy. | | afk.llms.routing | Router registry: create_llm_router(), register_llm_router(). | | afk.llms.cache | Cache backends: create_llm_cache(), in-memory and Redis implementations. | | afk.llms.middleware | MiddlewareStack for request/response transformation pipelines. | | afk.llms.types | LLMRequest, LLMResponse, Message, ToolCall, Usage, streaming event types. | | afk.llms.config | LLMConfig dataclass for model configuration. | | afk.llms.profiles | Production/development profile presets. | | afk.llms.errors | LLMError, LLMTimeoutError, LLMRetryableError, etc. | **Key imports:** afk.tools Tool definition, registry, hooks, middleware, security, and prebuilt runtime tools. **Sub-modules:** | Sub-module | Responsibility | | --------------------- | --------------------------------------------------------------------------------------- | | afk.tools.core | tool decorator, ToolSpec, ToolContext, ToolResult, ToolRegistry. | | afk.tools.security | SandboxProfile, SandboxProfileProvider, SecretScopeProvider, argument validation. | | afk.tools.prebuilts | Built-in runtime tools (file read, directory list, command execution, skill tools). | | afk.tools.registry | Shared registry and registry middleware helpers. | **Key imports:** afk.memory Pluggable memory stores, compaction, retention policies, long-term memory, vector search, and thread state persistence. **Sub-modules:** | Sub-module | Responsibility | | ----------------------- | ----------------------------------------------------------------------------------------------------------------------------- | | afk.memory.store | MemoryStore abstract base class, MemoryCapabilities declarations. | | afk.memory.types | MemoryEvent, LongTermMemory, JsonValue, retention policy models. | | afk.memory.lifecycle | apply_event_retention(), apply_state_retention(), compact_thread_memory(), MemoryCompactionResult. | | afk.memory.vector | cosine_similarity(), vector scoring utilities for embedding-based search. | | afk.memory.factory | create_memory_store_from_env() factory for environment-based backend selection. | | afk.memory.adapters | Backend implementations: InMemoryMemoryStore, SQLiteMemoryStore, PostgresMemoryStore, RedisMemoryStore. | **Key imports:** afk.queues Task queue abstraction, worker lifecycle, failure classification, and metrics. **Sub-modules:** | Sub-module | Responsibility | | ---------------------- | --------------------------------------------------- | | afk.queues.base | Base queue types and enqueue/dequeue contracts. | | afk.queues.contracts | Queue protocol definitions. | | afk.queues.factory | Queue backend factory for creating queue instances. | | afk.queues.worker | Worker lifecycle management and task dispatch. | | afk.queues.metrics | Queue-level operational metrics. | **Key imports:** afk.observability Telemetry collection, projection, and export for runtime monitoring. **Sub-modules:** | Sub-module | Responsibility | | ------------------------------ | ---------------------------------------------------------------------------------------- | | afk.observability.models | RunMetrics dataclass with aggregated run statistics. | | afk.observability.contracts | Span and metric name constants (SPAN_AGENT_RUN, METRIC_AGENT_LLM_CALLS_TOTAL, etc.). | | afk.observability.projectors | Projection functions that transform run results into RunMetrics. | | afk.observability.exporters | ConsoleRunMetricsExporter and other output formatters. | | afk.observability.backends | Telemetry sink creation (create_telemetry_sink). | **Key imports:** afk.evals Eval suite execution, assertion contracts, budget constraints, and report serialization. **Sub-modules:** | Sub-module | Responsibility | | --------------------- | ------------------------------------------------------------------------------------------ | | afk.evals.suite | run_suite() and arun_suite() entrypoints. | | afk.evals.executor | Single-case execution via run_case() and arun_case(). | | afk.evals.models | EvalCase, EvalCaseResult, EvalSuiteConfig, EvalSuiteResult, EvalAssertionResult. | | afk.evals.contracts | EvalAssertion, AsyncEvalAssertion, EvalScorer protocols. | | afk.evals.budgets | EvalBudget dataclass and evaluate_budget() function. | | afk.evals.reporting | suite_report_payload(), write_suite_report_json(). | | afk.evals.golden | write_golden_trace(), compare_event_types() for deterministic trace comparison. | **Key imports:** afk.mcp Model Context Protocol server implementation, tool store, and server registration. **Sub-modules:** | Sub-module | Responsibility | | ---------------- | --------------------------------------------------------------- | | afk.mcp.server | MCP protocol handler. | | afk.mcp.store | Tool store registry and utility functions for MCP tool loading. | **Key imports:** afk.messaging Protocol-first agent-to-agent messaging exports. **Sub-modules:** | Sub-module | Responsibility | | ------------------------ | ---------------------------------------------------------------------------------------- | | afk.messaging | Public re-exports for A2A contracts, envelopes, auth providers, hosts, and delivery stores. | | afk.agents.a2a | Internal implementation modules for protocol, auth, delivery, hosting, and Google adapter. | **Key imports:**", + "token_count": 834 + }, + { + "id": "doc_3ae3aaf0c12c", + "path": "docs/library/how-to-use-afk.mdx", + "url": "/library/how-to-use-afk", + "title": "How to Use AFK", + "description": "A practical adoption path from first agent to production release.", + "headings": [ + "Phase 1: narrow agent", + "Phase 2: tools and safety", + "Phase 3: production controls", + "Phase 4: release discipline", + "Common mistakes", + "Next steps" + ], + "content_sha256": "c96092176ab2a302d53853ea5fa4e97e93a3082241ca8210f0aa16ef50e803f4", + "content": "Use AFK incrementally. Start with one narrow agent, add tools only when the task needs action, and add production controls before real users depend on the system. Phase 1: narrow agent Build one agent that solves one task without tools. Move on when the agent is reliable on real examples for the narrow task. Phase 2: tools and safety Add typed tools and hard limits. Move on when all tools have Pydantic argument models, cost limits are set, and mutating operations are gated or absent. Phase 3: production controls Before shipping, add the controls that make failures diagnosable: - evals for expected behavior; - telemetry for latency, usage, errors, and tool calls; - persistent memory or queues if runs must survive process restarts; - security controls for sandboxing, secret scope, and tool output limits; - troubleshooting docs for on-call and operators. Useful pages: Test agent behavior and enforce budgets. Export metrics, traces, and run records. Understand policy gates, sandboxing, and secret isolation. Run agents through distributed workers. Phase 4: release discipline Once the agent is in production: - run evals in CI before prompt, tool, or model changes; - compare behavior across releases with golden traces where appropriate; - monitor cost per run and failure rate; - version system prompts in files; - document operator actions for approval, resume, and rollback flows. Common mistakes | Mistake | Better approach | | --- | --- | | Starting with a multi-agent system | Start with one narrow agent and split only when roles are genuinely different | | Writing untyped tools | Use Pydantic argument models for every tool | | Treating prompts as the only safety layer | Add FailSafeConfig, policy gates, sandboxing, and evals | | Hiding internals in public docs | Keep builder docs behavior-first and maintainer docs internals-first | | Shipping without run records | Export telemetry and inspect AgentResult fields | Next steps Read Building with AI for production design patterns, then Troubleshooting for common operational failures.", + "token_count": 236 + }, + { + "id": "doc_f2ae50c638a4", + "path": "docs/library/learn-in-15-minutes.mdx", + "url": "/library/learn-in-15-minutes", + "title": "Learn AFK in 15 Minutes", + "description": "A guided path through agents, tools, streaming, memory, and safety.", + "headings": [ + "1. Agent + runner", + "2. Typed tools", + "3. Streaming", + "4. Memory continuity", + "5. Safety limits", + "What to read next" + ], + "content_sha256": "1d734acb805aeb148e75286864ddd24fd7e503a8a895b294749e25cf59c849d9", + "content": "This tutorial expands the quickstart into the core workflow used by most AFK applications. Each section introduces one concept and keeps the code small enough to copy into a single file. 1. Agent + runner Key point: an agent is declarative. The runner owns execution. 2. Typed tools Key point: tool arguments are validated before your function runs. 3. Streaming Use streaming when a UI or CLI should show progress before the final result is ready. Key point: run_stream() returns an AgentStreamHandle. Consume it to receive text, tool lifecycle events, errors, and the terminal result. 4. Memory continuity Pass the same thread_id to keep conversation context attached to a thread. Key point: thread continuity is explicit. Use the same thread_id for related turns. 5. Safety limits Production agents need hard limits even when prompts and tools are well designed. Key point: limits are part of the agent contract. Set them before shipping. What to read next Agent fields, prompt resolution, subagents, skills, and MCP tools. Tool schemas, context, hooks, middleware, sandboxing, and output limits. Event types, stream handles, cancellation, and UI patterns. Evals, telemetry, security controls, queues, and deployment.", + "token_count": 142 + }, + { + "id": "doc_5ee8bbdbd8a8", + "path": "docs/library/llm-interaction.mdx", + "url": "/library/llm-interaction", + "title": "LLM Interaction", + "description": "How runner and llms runtime exchange requests and responses.", + "headings": [ + "Interaction diagram", + "Request construction", + "Simplified view of what the runner builds", + "Response handling", + "LLMRequest fields", + "LLMResponse fields", + "Streaming interaction", + "Error handling at the LLM boundary" + ], + "content_sha256": "1ea841c0c4ab463b019f66f66da07cdc0d9e893b77493001e1f6d45d0dbd18eb", + "content": "The runner communicates with LLM providers through a well-defined request/response boundary. Understanding this boundary is important for debugging model behavior, diagnosing latency issues, and implementing custom providers or middleware. This page explains how the runner constructs LLM requests, how responses are interpreted, how streaming works, and how errors are handled at the LLM boundary. Interaction diagram Each step in this flow is described in detail below. Request construction At each step of the agent loop, the runner builds an LLMRequest and sends it to the configured LLM provider. Here is what goes into the request: **Model resolution.** The agent's model field (e.g., \"gpt-4.1-mini\") is resolved through the model resolution chain. This maps the requested model name to a provider, adapter, and normalized model identifier. Custom model_resolver functions can override this mapping. **Message history.** The runner maintains a list of Message objects representing the conversation history. This includes: - A system message containing the agent's instructions, skill manifests, and (when enabled) an untrusted-data preamble. - The initial user message. - assistant messages from previous LLM responses. - tool messages containing the results of tool executions. **Tool definitions.** If the agent has registered tools, they are exported as OpenAI-compatible function tool definitions and included in LLMRequest.tools. The tool_choice is set to \"auto\" so the model can decide whether to call tools. **Session and checkpoint tokens.** For providers that support stateful sessions (like Anthropic's agent SDK), the runner passes session_token and checkpoint_token from previous responses to maintain session continuity. **Metadata.** The request includes metadata about the run (run_id, thread_id, agent_name) and content channel markers that help the provider distinguish trusted system content from untrusted tool output. Response handling The LLM runtime normalizes provider-specific responses into an LLMResponse dataclass. The runner then interprets this response to decide what happens next: **If the response contains no tool calls** (resp.tool_calls is empty), the run is complete. The runner extracts resp.text as final_text, captures any resp.structured_response, and transitions to state=\"completed\". **If the response contains tool calls**, the runner enters the tool execution phase: 1. Each tool call is evaluated by the policy engine. 2. Approved tools are executed (in parallel, up to the batch limit). 3. Tool results are appended to the message history as tool messages. 4. The runner loops back to make another LLM call with the updated history. **Usage tracking.** The LLMResponse.usage field contains token counts (input_tokens, output_tokens, total_tokens). The runner accumulates these into UsageAggregate for cost estimation and budget enforcement. **Session continuity.** If the response includes updated session_token or checkpoint_token, the runner stores these for the next request. LLMRequest fields | Field | Type | Purpose | | --- | --- | --- | | model | str | Normalized model identifier. | | request_id | str | Unique request ID for tracing. | | messages | list[Message] | Conversation history. | | tools | list[dict] or None | OpenAI-format tool definitions. | | tool_choice | str or None | Tool selection strategy (\"auto\", \"none\", or specific tool). | | max_tokens | int or None | Maximum response tokens. | | temperature | float or None | Sampling temperature. | | top_p | float or None | Nucleus sampling parameter. | | stop | list[str] or None | Stop sequences. | | idempotency_key | str or None | Key for request deduplication. | | session_token | str or None | Provider session continuity token. | | checkpoint_token | str or None | Provider checkpoint continuity token. | | timeout_s | float or None | Request-level timeout in seconds. | | metadata | dict | Run context metadata. | LLMResponse fields | Field | Type | Purpose | | --- | --- | --- | | text | str | Model-generated text content. | | tool_calls | list[ToolCall] | Requested tool invocations. | | finish_reason | str or None | Why the model stopped generating (\"stop\", \"tool_calls\", etc.). | | usage | Usage | Token counts: input_tokens, output_tokens, total_tokens. | | structured_response | dict or None | Parsed structured output when using response_model. | | session_token | str or None | Updated session token for next request. | | checkpoint_token | str or None | Updated checkpoint token for next request. | Streaming interaction For real-time UIs, the runner supports streaming via runner.run_stream(). The streaming path works differently from the batch path: The stream produces AgentStreamEvent instances that include: - text_delta -- incremental text (provider stream deltas, or fallback chunking for non-streaming providers). - step_started -- signals a new step in the agent loop. - tool_started / tool_completed -- tool lifecycle events. - error -- error notification. - completed -- terminal event containing the final AgentResult. Error handling at the LLM boundary Errors at the LLM boundary are classified and handled according to the failure policy matrix: **Retryable errors** (timeouts, rate limits, server errors) are retried with exponential backoff. The runner supports a fallback model chain: if the primary model fails after retries, it tries the next model in FailSafeConfig.fallback_model_chain. **Terminal errors** (auth failures, invalid payloads) are not retried. The llm_failure_policy determines what happens next: - \"fail\" -- the run aborts with state=\"failed\". - \"degrade\" -- the run terminates with state=\"degraded\" and the error message as final_text. **Circuit breaker** protection prevents cascading failures. After breaker_failure_threshold consecutive failures to the same model+provider, the circuit opens and subsequent calls fail fast until the cooldown expires. **Policy denial** of LLM calls is handled before the call is made. If the policy engine denies an LLM call, the runner applies the llm_failure_policy without ever contacting the provider.", + "token_count": 607 + }, + { + "id": "doc_e34fe24f9585", + "path": "docs/library/mcp-server.mdx", + "url": "/library/mcp-server", + "title": "MCP Server", + "description": "Expose and consume tools via the Model Context Protocol.", + "headings": [ + "Architecture", + "Expose tools via MCP server", + "Consume tools from external MCP servers", + "Security", + "MCP server vs A2A", + "Next steps" + ], + "content_sha256": "92cdc3393c943799a3a2ff6409f04605595a4b4e78bc36eb42d27cead0181fe3", + "content": "The Model Context Protocol (MCP) is an open standard for sharing tools between AI systems. AFK supports both **exposing** your tools via an MCP server and **consuming** tools from external MCP servers. Architecture Expose tools via MCP server Make your AFK tools available to any MCP-compatible client: Consume tools from external MCP servers Discover and use tools from any MCP server: **MCP tools are transparent.** Once attached to an agent, MCP tools behave exactly like local tools \u2014 same validation, same policy gates, same telemetry. The agent doesn't know (or care) whether a tool is local or remote. Security Require auth tokens for all MCP connections: Configure allowed origins for browser-based clients: Apply policy rules to MCP-exposed tools: Limit request rates per client: **Always authenticate MCP servers in production.** An unauthenticated server exposes your tools to anyone who can reach the endpoint. MCP server vs A2A | Feature | MCP Server | A2A | | --------------- | ---------------------------- | ---------------------------- | | **Shares** | Individual tools | Full agents | | **Protocol** | MCP standard | AFK A2A protocol | | **Use case** | Tool sharing between systems | Agent-to-agent communication | | **Client sees** | Tool schemas and results | Agent responses | | **Interop** | Any MCP client | AFK agents | Next steps Share full agents across systems. Full security architecture.", + "token_count": 162 + }, + { + "id": "doc_0383dbf277ba", + "path": "docs/library/memory.mdx", + "url": "/library/memory", + "title": "Memory", + "description": "Persist conversation state, resume runs, and compact threads.", + "headings": [ + "Quick start: multi-turn conversation", + "What gets stored", + "State lifecycle", + "Resume interrupted runs", + "Start a run that might be long", + "If interrupted, resume later", + "Compact long threads", + "Memory backends", + "Connection pooling for Redis", + "Use the pool with RedisMemoryStore", + "Environment-based selection", + "Custom backends", + "Long-term memory", + "Store a long-term memory", + "Retrieve memories", + "Vector search", + "Search by embedding similarity", + "Text search", + "Design guidelines", + "Next steps" + ], + "content_sha256": "6df7c441a722d1ed17a8489a0f085e0a05f8e9dd2829cad72addc5a7a93bfec1", + "content": "AFK's memory system persists conversation state across runs. Use it for multi-turn conversations, run resumption after interrupts, long-term knowledge retention, and vector-based semantic search. Quick start: multi-turn conversation The thread_id links runs into a conversation. AFK automatically persists messages between runs. What gets stored | Record type | What it contains | When it's written | | ------------------ | ---------------------------------------------------------- | --------------------------------------- | | **Event** | User messages, assistant responses, tool calls and results | After each run step | | **Checkpoint** | Full run state at a point in time | At step boundaries (pre-LLM, post-tool) | | **State (KV)** | Checkpoint pointers, effect journal, background tool state | During and after runs | | **Long-term memory** | Persistent knowledge with optional embeddings | Via upsert_long_term_memory | **What's NOT stored automatically:** Raw LLM provider responses or internal framework temporaries. Only conversation-visible records and explicit state writes are persisted. State lifecycle Resume interrupted runs If a run is interrupted (crash, timeout, pause for approval), resume from the last checkpoint: **Checkpoints are written at key boundaries:** before each LLM call, after each tool batch, and after each step completes. On resume, completed tool calls are replayed from the effect journal \u2014 no duplicate side effects. Compact long threads Over time, conversation threads grow and consume tokens. Use compaction to trim old events: Compaction applies retention rules: protected event types are preserved first, then the most recent remaining events fill the budget. Memory backends AFK ships with four backends. All implement the MemoryStore protocol. State lives in process memory. Fast, no setup, but lost on restart. **Use for:** Development, testing, short-lived scripts. Persistent local storage with JSON serialization and local vector search. Features: WAL mode, text search, vector similarity search (cosine), atomic upsert. **Use for:** Local development with persistence, single-process deployments. Production-grade backend with pgvector support for vector search. **Use for:** Production multi-process deployments. In-memory store backed by Redis for shared state across processes. **Use for:** Shared state across workers, ephemeral but durable-enough sessions. Connection pooling for Redis For production Redis deployments, use connection pooling for better performance: Environment-based selection Set environment variables to auto-select a backend without code changes: The runner falls back to in-memory if the configured backend fails to initialize. Custom backends Implement the MemoryStore abstract class to add support for any database: Declare capabilities to tell the framework which features your backend supports. Features like vector search are only used when the backend declares support. Long-term memory Beyond conversation events, AFK supports persistent long-term memories scoped per user and purpose: Vector search Backends that support vector search (SQLite, Postgres) can find semantically similar memories: Text search All backends support basic text search across memory content: Design guidelines - **Always use thread_id for conversations.** Without it, each run starts fresh. - **Compact threads proactively.** Don't wait until you hit token limits. A good rule: compact when the thread exceeds ~500 events. - **Use checkpoints for long-running agents.** If a run might take minutes, checkpoints let you resume on failure. - **Don't store secrets in memory.** Thread events are persisted and may be readable. - **Choose the right backend.** In-memory for dev, SQLite for local persistence, Postgres/Redis for production. - **Use scopes for long-term memory.** Organize memories by purpose (preferences, knowledge, history) to keep queries efficient. Next steps Resume and compact APIs on the Runner. Template prompts with context from memory.", + "token_count": 430 + }, + { + "id": "doc_50ac650fd8f6", + "path": "docs/library/mental-model.mdx", + "url": "/library/mental-model", + "title": "Mental Model", + "description": "Three coordinated loops that drive every AFK agent.", + "headings": [ + "The three loops", + "Think in contracts", + "Decision tree: how complex should my system be?", + "What success looks like" + ], + "content_sha256": "cf294e0c707755bfe6bfc555054eaefbfb62e65f3ea92ecd9e2b4cfec5bb792c", + "content": "AFK agents execute through three coordinated loops \u2014 **Decision**, **Execution**, and **Assurance**. Understanding these loops helps you reason about agent behavior, debug issues, and design systems that scale predictably. The three loops The Decision Loop is the model's turn. On each step: 1. The runner sends the conversation history + tool schemas to the LLM 2. The LLM decides whether to respond with text (done) or request tool calls (continue) 3. If tool calls are requested, they flow to the Execution Loop **You control this with:** agent instructions, model choice, tool availability The Execution Loop handles every tool call: 1. **Validate** arguments against the Pydantic schema 2. **Policy gate** \u2014 allow, deny, or defer for human approval 3. **Execute** the handler (with hooks and middleware) 4. **Sanitize** the output (truncate, strip injection vectors) 5. **Return** the result to the Decision Loop **You control this with:** tool definitions, policy rules, sandbox profiles The Assurance Loop runs continuously, enforcing limits on both other loops: - **Step count** \u2014 stops the agent after N iterations - **Tool call count** \u2014 prevents excessive tool usage - **Cost budget** \u2014 stops if estimated cost exceeds the limit - **Wall time** \u2014 hard timeout on the entire run - **Failure classification** \u2014 retryable, terminal, or non-fatal **You control this with:** FailSafeConfig Think in contracts AFK is built on a contract-first design. Every interaction between components is defined by typed data structures: | Boundary | Contract | What flows | | ------------------ | ---------------------------------------------------- | --------------------------------------- | | Runner \u2192 LLM | LLMRequest / LLMResponse | Messages, tool schemas, model responses | | Runner \u2192 Tool | ToolCall / ToolResult | Validated arguments, execution output | | Runner \u2192 Subagent | AgentInvocationRequest / AgentInvocationResponse | Delegate task and receive result | | Runner \u2192 Memory | Checkpoint records | Conversation state for resume/replay | | Runner \u2192 Telemetry | AgentRunEvent, RunMetrics | Spans, metrics, audit trail | **Contracts are Pydantic models.** This means every boundary is validated at runtime \u2014 malformed data causes clear errors, not silent bugs. When you see a validation error, it's AFK telling you exactly where the contract was violated. Decision tree: how complex should my system be? Not sure what to build? Start at the top and follow the path that matches your use case. > [!TIP] > **Start at Level 1.** Only move up when you have clear evidence that your current level isn't enough. Each level adds complexity that you need to manage and test. What success looks like A mature AFK implementation exhibits these properties: - **Every tool has a Pydantic model** \u2014 no untyped arguments - **Every run has cost limits** \u2014 max_total_cost_usd is always set - **Policy gates protect mutations** \u2014 dangerous actions require approval - **Evals cover core behaviors** \u2014 regression tests catch prompt drift - **Observability is on from day one** \u2014 even if it's just the console exporter - **Failures are classified** \u2014 the system knows what to retry and what to abort", + "token_count": 325 + }, + { + "id": "doc_75887f91c7df", + "path": "docs/library/messaging.mdx", + "url": "/library/messaging", + "title": "Internal Messaging", + "description": "Agent-to-agent messaging with delivery guarantees and idempotency.", + "headings": [ + "Message lifecycle", + "The InternalA2AEnvelope", + "Delivery behavior", + "Idempotency and correlation", + "All messages in this workflow share the same correlation_id", + "Delivery store backends", + "Next steps" + ], + "content_sha256": "e8d90b294c3ed2ebbbff257fa28d0be7ba2cce6d689a3161f2306ddf95ba1ee2", + "content": "AFK's internal messaging system lets agents communicate via structured envelopes with **at-least-once delivery** and **idempotency**. Use it when agents in the same system need to exchange data reliably. Message lifecycle The InternalA2AEnvelope Every message is wrapped in a typed envelope: | Field | Type | Purpose | | ----------------- | ---------- | -------------------------------------------- | | message_type | str | request, response, or event | | run_id | str | Run identifier for tracing | | thread_id | str | Memory thread identifier | | conversation_id | str | Cross-run conversation identifier | | payload | dict | Message data (any JSON-serializable content) | | correlation_id | str | Groups related messages in a workflow | | idempotency_key | str | Deduplication \u2014 same key = same message | | source_agent | str | Name of the sending agent | | target_agent | str | Name of the receiving agent | | metadata | dict | JSON-safe tracing or routing metadata | | timestamp_ms | int | Creation timestamp in milliseconds | Delivery behavior 1. Sender creates and submits the envelope 2. Delivery store checks the idempotency key (rejects duplicates) 3. Message is delivered to the receiver 4. Receiver processes and ACKs 5. Store marks as delivered **Result:** Message processed exactly once. 1. Delivery fails (receiver timeout, transient error) 2. Store schedules retry with exponential backoff 3. Message is redelivered (same idempotency key) 4. Receiver processes and ACKs on retry **Result:** At-least-once delivery. Receiver must be idempotent. 1. All retry attempts exhausted 2. Message moves to dead-letter queue 3. Alert generated (if configured) **Result:** Message is not lost \u2014 it's in the DLQ for manual review. Idempotency and correlation **Always set an idempotency_key.** Without it, retry deliveries can cause duplicate processing. Use a deterministic key derived from the task context (e.g., f\"{task_id}-{step_name}\"). **Correlation IDs** group related messages across a workflow. When debugging, filter by correlation_id to see the full message chain for a task. Delivery store backends Default. Fast, no setup. State lost on restart. Implement the DeliveryStore protocol for durable messaging: Next steps Cross-system agent communication. Async job processing with queue backends.", + "token_count": 226 + }, + { + "id": "doc_5a65ddab1445", + "path": "docs/library/migration.mdx", + "url": "/library/migration", + "title": "Migration Guide", + "description": "Move from LangChain, OpenAI Assistants, or custom agents to AFK.", + "headings": [ + "From LangChain", + "LangChain \u2192 AFK concepts", + "Basic agent migration", + "Key differences", + "LangChain: agent is callable", + "AFK: Agent defines what, Runner executes how", + "LangChain: function signature and docstring", + "AFK: Pydantic model for typed arguments", + "LangChain: single run() method", + "AFK: explicit sync, async, or streaming", + "Tool migration", + "Simple tool", + "Structured tool with custom logic", + "Simple tool", + "Tool with constraints", + "Memory migration", + "Configure memory backend", + "Use thread_id for conversation continuity", + "Callback \u2192 Middleware migration", + "RAG migration", + "Store documents with embeddings", + "Retrieve in tool", + "From OpenAI Assistants API", + "Assistants \u2192 AFK concepts", + "Basic migration", + "Key advantages of AFK over Assistants API", + "From custom agent code", + "Common patterns migration", + "Before: Custom retry implementation", + "Before: Custom circuit breaker", + "Next steps" + ], + "content_sha256": "5918b39a7bcd88e65ba3aba1c07491fdab8b878f8eb6dea163cffe3b02ab8b7f", + "content": "This guide helps you migrate existing agent code from other frameworks to AFK. From LangChain LangChain \u2192 AFK concepts | LangChain Concept | AFK Equivalent | Key Difference | |-------------------|----------------|----------------| | ChatOpenAI | LLMBuilder | Provider-portable, typed contracts | | Agent | Agent | Config object, not runtime | | Tool | @tool decorator | Pydantic-based, typed arguments | | Chain | Runner | Explicit execution loop | | Memory | MemoryStore | Multiple backends, checkpointing | | Callback | Middleware / Hooks | Request/response interception | | LangSmith | Telemetry | Built-in OTEL support | Basic agent migration **LangChain:** **AFK:** Key differences **1. Agent is a config object, not a runtime:** **2. Tools use Pydantic for validation:** **3. Explicit execution modes:** Tool migration **LangChain tools:** **AFK equivalents:** Memory migration **LangChain memory:** **AFK memory:** Callback \u2192 Middleware migration **LangChain callbacks:** **AFK middleware:** RAG migration **LangChain retrieval:** **AFK approach:** From OpenAI Assistants API Assistants \u2192 AFK concepts | OpenAI Concept | AFK Equivalent | |----------------|----------------| | Assistant | Agent | | Thread | Memory + thread_id | | Run | Runner execution | | Message | MemoryEvent | | Tool | @tool decorator | | Function | @tool with Pydantic | | File search | Long-term memory + vector search | Basic migration **OpenAI Assistants:** **AFK:** Key advantages of AFK over Assistants API 1. **Local execution** \u2014 No API calls needed for simple tasks 2. **Portable** \u2014 Switch LLM providers without code changes 3. **Debuggable** \u2014 Step through agent logic locally 4. **Testable** \u2014 Run evals locally in CI 5. **Controllable** \u2014 Full access to prompts, tools, and behavior From custom agent code Common patterns migration **Custom retry logic:** **AFK:** **Custom circuit breaker:** **AFK:** Next steps Build your first AFK agent in 5 minutes. Understand AFK's design philosophy. Complete API documentation. Runnable examples for every feature.", + "token_count": 208 + }, + { + "id": "doc_78a8125d2c7e", + "path": "docs/library/observability.mdx", + "url": "/library/observability", + "title": "Observability", + "description": "Built-in telemetry with spans, metrics, and exporters.", + "headings": [ + "Setup in one line", + "Console output (development)", + "OpenTelemetry (production)", + "JSON lines (log files)", + "Telemetry pipeline", + "RunMetrics reference", + "Choosing an exporter", + "Telemetry spans", + "Alerting recommendations", + "Next steps" + ], + "content_sha256": "125c3f894dc8006a91e85aa44be8559452e56c5c7984b6cc2fab73dc99763e43", + "content": "AFK includes a telemetry pipeline that captures every agent run event \u2014 LLM calls, tool executions, state transitions, and performance metrics. Export to the console for development, JSON for log aggregation, or OpenTelemetry for production. Setup in one line Telemetry pipeline RunMetrics reference Every completed run produces a RunMetrics object: | Metric | Type | Description | | --------------------- | ------- | ---------------------------------- | | total_steps | int | Number of agent loop iterations | | total_llm_calls | int | Number of LLM API calls | | total_tool_calls | int | Number of tool executions | | total_tokens | int | Total tokens (prompt + completion) | | total_cost_usd | float | Estimated cost in USD | | wall_time_s | float | Total run duration in seconds | | first_token_ms | float | Time to first token (streaming) | | tool_latency_p50_ms | float | Median tool execution latency | | tool_latency_p99_ms | float | 99th percentile tool latency | | error_count | int | Number of errors during the run | Choosing an exporter Human-readable output to stdout. Best for development and debugging. Structured output for log aggregation (ELK, Datadog, etc.). Each event is a JSON line: Export spans and metrics to any OTEL-compatible backend (Jaeger, Grafana, Honeycomb, etc.). **Use for:** Production deployments with existing observability infrastructure. Telemetry spans AFK creates spans for key operations: Spans capture timing, success/failure, and metadata. In OTEL mode, these map directly to traces visible in your observability dashboard. Alerting recommendations | Alert | Condition | Severity | | -------------------- | -------------------------------------------- | ---------- | | Error rate spike | error_count / total_runs > 5% over 5 min | **High** | | LLM latency spike | p99 > 10s for 5 min | **Medium** | | Cost anomaly | daily_cost > 2x rolling average | **High** | | Tool failure rate | tool_failures / tool_calls > 10% for 5 min | **Medium** | | Circuit breaker open | Any LLM circuit breaker trips | **High** | **Start with the console exporter** even in staging. It costs nothing and gives you immediate visibility into agent behavior. Switch to OTEL when you have a monitoring stack. Next steps Behavioral testing for agent quality. Security boundaries and hardening.", + "token_count": 223 + }, + { + "id": "doc_f18f523b6ecc", + "path": "docs/library/overview.mdx", + "url": "/library/overview", + "title": "What is AFK?", + "description": "The short mental model for building and maintaining AFK agents.", + "headings": [ + "The three core objects", + "When AFK is a good fit", + "Builder path", + "Maintainer path", + "Source-of-truth rules", + "Next steps" + ], + "content_sha256": "3d4dbe025de523cf7ac36a6ca13721c14fd9857f6f45bb8f3acebae1cb31452a", + "content": "AFK is an agent runtime for Python applications. It is designed for teams that need agent behavior to be typed, observable, testable, and bounded by explicit controls. The core idea is simple: The three core objects | Object | What it owns | What it does not own | | --- | --- | --- | | Agent | Name, model, instructions, tools, subagents, skills, MCP servers, defaults, fail-safe limits | Network calls, event loops, persistence, telemetry export | | Runner | Execution loop, tool dispatch, streaming, memory, checkpointing, policy/HITL, telemetry | Agent identity or instructions | | AgentResult | Final text, terminal state, tool/subagent records, usage aggregate, cost, run/thread ids | Future execution | This separation is the main design rule. Agents describe behavior. Runners execute behavior. Runtime subsystems provide capabilities. When AFK is a good fit Use AFK when your agent will: - call tools or external systems; - run for more than one step; - need cost, time, step, or tool-call limits; - stream progress to a UI; - persist memory or resume a run; - require evals, telemetry, or production incident debugging; - coordinate specialist subagents. A direct LLM provider SDK may be simpler for one-off single-turn text generation. AFK is useful when the agent needs operational structure. Builder path If you are building an app with AFK, read in this order: 1. Quickstart for the smallest complete agent. 2. Learn AFK in 15 Minutes for the guided path. 3. Agents, Runner, and Tools for the core building blocks. 4. Building with AI, Evals, and Observability before production. Maintainer path If you are changing AFK itself, read in this order: 1. Developer Guide for local setup, commands, and docs workflow. 2. Architecture for package boundaries. 3. Public API Rules before changing exports or examples. 4. Tested Behaviors before changing runner, tools, LLM runtime, memory, queues, or telemetry. Source-of-truth rules - Public examples import from afk.*, never src.afk.*. - Runner is imported from afk.core. - User-facing docs should explain behavior before internals. - Maintainer docs may reference internal modules, but must identify the public contract affected by a change. - Generated agent-facing docs and skill indexes must be refreshed when navigation, examples, or skill references change. Next steps Build one agent and one typed tool. Set up the repo and understand the contributor workflow.", + "token_count": 272 + }, + { + "id": "doc_f5f256549d84", + "path": "docs/library/performance.mdx", + "url": "/library/performance", + "title": "Performance", + "description": "Improve AFK latency, throughput, and cost without relying on internal APIs.", + "headings": [ + "Latency", + "Tool execution", + "Throughput", + "Cost", + "Memory", + "Measurement", + "Checklist" + ], + "content_sha256": "4ded523bc48148cc20cacc7af3c5ac32a07a19a6376e3d8d69bb08c2aa878c6f", + "content": "Performance work in AFK usually comes from four levers: choosing the right model, reducing unnecessary tool/LLM calls, keeping memory bounded, and moving long-running work into queues. Latency Use the smallest model that can reliably handle the task, and reserve larger models for tasks that need deeper reasoning. Other latency controls: - keep system prompts short and specific; - make I/O-bound tools async; - avoid tools for information already present in context; - stream user-facing runs with runner.run_stream(...); - set tight max_steps, max_llm_calls, and max_wall_time_s limits. Tool execution Tools are often the slowest part of a run. Keep them typed, narrow, and bounded. Tool guidance: - validate inputs with Pydantic models; - enforce timeouts in external clients; - return compact JSON-safe payloads; - truncate or summarize large external responses before returning them; - use RunnerConfig(tool_output_max_chars=...) as a final bound. Throughput Use async runner APIs for services and workers. For durable background work, use task queues instead of keeping HTTP requests open. See Task Queues. Cost Set cost and loop limits on every production agent. Read cost from the terminal result: Memory Long threads increase prompt size and storage. Use explicit thread ids and compact retained state when threads grow. Choose the memory backend by deployment shape: | Backend | Use case | | --- | --- | | In-memory | Tests and local experiments | | SQLite | Single-process local or small deployments | | Redis | Shared state across processes | | Postgres | Persistent production storage and vector search | Configure backends with environment variables or pass a public MemoryStore implementation to Runner(memory_store=...). Measurement Measure from AgentResult first: For production, export telemetry through Observability and track latency, token usage, tool failures, degraded runs, and cost per run. Checklist - Use async runner APIs in servers and workers. - Stream user-facing runs. - Keep prompts and tool outputs compact. - Set fail-safe limits and cost budgets. - Compact long-running threads. - Move durable background work into queues. - Monitor token usage, tool count, state, and cost per run.", + "token_count": 250 + }, + { + "id": "doc_3eeb75d383b8", + "path": "docs/library/public-imports-and-function-improvement.mdx", + "url": "/library/public-imports-and-function-improvement", + "title": "Public API Rules", + "description": "Maintainer rules for preserving AFK's public import contract.", + "headings": [ + "Contract", + "Rules for maintainers", + "Preferred examples", + "Imports to avoid in public docs", + "Change checklist", + "Search commands" + ], + "content_sha256": "4ad0dbc7fa46f0049e5638a702fa2d73dbf1da35cb09099c277e41dd1fefbabc", + "content": "This page is for AFK maintainers. It explains how to change public exports without making downstream code or docs confusing. For the user-facing import table, see API Reference. Contract The public API is the set of names exported by package-level __init__.py files: - afk.agents - afk.core - afk.tools - afk.llms - afk.memory - afk.queues - afk.mcp - afk.messaging - afk.observability - afk.evals Public docs and examples should import from these package surfaces. They should not use src.afk imports or deep implementation modules such as afk.core.runner.api. Rules for maintainers 1. If a downstream user should import a symbol, export it from the package-level __init__.py. 2. If a symbol is not exported, do not use it in builder docs or examples. 3. Keep Agent and Runner separate: Agent comes from afk.agents; Runner comes from afk.core. 4. Prefer protocols, dataclasses, Pydantic models, and explicit error classes for public contracts. 5. When removing or renaming a public symbol, update migration docs and tests in the same change. 6. When changing a public constructor, update API Reference, Configuration Reference, examples, and generated agent-facing docs. Preferred examples Imports to avoid in public docs | Avoid | Prefer | | --- | --- | | src.afk.agents.Agent | afk.agents.Agent | | afk.agents.core.base.Agent | afk.agents.Agent | | afk.core.runner.api.RunnerAPIMixin | afk.core.Runner | | afk.tools.core.decorator.tool | afk.tools.tool | | afk.llms.builder.LLMBuilder | afk.llms.LLMBuilder | Deep imports are acceptable in internal tests only when the test is specifically covering an internal unit. Integration tests and examples should exercise the public surface. Change checklist Before merging a public API change: - Update the relevant package __all__. - Add or update tests that import through the public package. - Update user-facing docs if a builder would see the changed behavior. - Update maintainer docs if an invariant or subsystem boundary changed. - Run PYTHONPATH=src pytest -q or targeted tests for the affected subsystem. - Regenerate agent-facing docs with ./scripts/build_agentic_ai_assets.sh when docs, examples, skill metadata, or navigation changes. Search commands The last command intentionally finds deep imports for review. Some maintainer references may be valid, but builder docs should avoid them.", + "token_count": 277 + }, + { + "id": "doc_bf91f8d5da74", + "path": "docs/library/quickstart.mdx", + "url": "/library/quickstart", + "title": "Quickstart", + "description": "Build one AFK agent with one typed tool.", + "headings": [ + "Prerequisites", + "1. Define an agent", + "2. Add one typed tool", + "3. Read the result", + "4. Keep going" + ], + "content_sha256": "898fc17c3d87708f8ce6850453a470a6fad5eb53aa22488f66cd3b31e8ed4886", + "content": "This page is the shortest useful AFK path: install the package, define an agent, attach one typed tool, and run it. Prerequisites - Python 3.13+ - An LLM provider key, such as OPENAI_API_KEY When working from this repository instead of an installed package: 1. Define an agent Agent stores configuration. Runner executes the run. AgentResult.final_text is the assistant response. 2. Add one typed tool Tools are Python functions with Pydantic argument models. AFK turns the model into a tool schema, validates model-provided arguments, executes the function, and feeds the result back into the agent loop. 3. Read the result Common fields on AgentResult: | Field | Meaning | | --- | --- | | final_text | Final assistant text | | state | Terminal state such as completed, failed, cancelled, or degraded | | run_id | Unique id for this run | | thread_id | Conversation/thread id used by memory | | tool_executions | Ordered records for tool calls | | subagent_executions | Ordered records for subagent calls | | usage_aggregate | Aggregated token usage | | total_cost_usd | Estimated total run cost when available | 4. Keep going Add streaming, memory, and safety controls. Find complete snippets for common scenarios. Understand the agent configuration object. Understand sync, async, and streaming execution.", + "token_count": 135 + }, + { + "id": "doc_f54217a0f2cf", + "path": "docs/library/run-event-contract.mdx", + "url": "/library/run-event-contract", + "title": "Run Event Contract", + "description": "Runtime event schema and event consumption guidance.", + "headings": [ + "Event stream model", + "Event reference", + "AgentRunEvent structure", + "Consuming events", + "Pattern: event-type branching", + "Forward compatibility" + ], + "content_sha256": "a4e2b6d7119af19a8eafb23eb7f8f84bf514594c4d2dd076e5bb6983d4097b19", + "content": "Every AFK agent run produces a stream of AgentRunEvent instances that describe what happened during execution. These events form the run's audit trail -- they tell you when the run started, when LLM calls were made, when tools executed, when policy decisions were rendered, and how the run terminated. Understanding the event contract is essential for building real-time UIs, logging pipelines, eval assertions, and debugging tools. This page documents every event type, explains when each fires, describes the data it carries, and provides patterns for consuming events safely. Event stream model Every run begins with run_started and ends with exactly one terminal event: run_completed, run_failed, run_interrupted, or run_cancelled. Between those boundaries, the runner emits step, LLM, tool, policy, and subagent events in the order they occur. Event reference | Event Type | When It Fires | Key Fields in data | | --- | --- | --- | | run_started | At the beginning of a new or resumed run. | agent_name, resumed (bool) | | step_started | At the start of each step iteration. | step (int), state | | llm_called | Before sending a request to the LLM provider. | model, provider | | llm_completed | After receiving the LLM response. | tool_call_count, finish_reason, text | | tool_batch_started | Before executing a batch of tool calls from one LLM response. | tool_call_count, tool_names, tool_call_ids | | tool_completed | After each individual tool finishes executing. | tool_name, tool_call_id, success (bool), output, error, agent_name, agent_depth, agent_path | | tool_deferred | Tool accepted and moved to background processing. | tool_name, tool_call_id, ticket_id, status, summary, resume_hint | | tool_background_resolved | Deferred tool completed successfully. | tool_name, tool_call_id, ticket_id, output | | tool_background_failed | Deferred tool failed or expired. | tool_name, tool_call_id, ticket_id, error | | policy_decision | After the policy engine evaluates a tool, LLM, or subagent event. | event_type, action, reason, policy_id, matched_rules | | subagent_started | Before dispatching a subagent invocation. | subagent_name, correlation_id | | subagent_completed | After a subagent finishes (success or failure). | subagent_name, success, latency_ms, error (if failed) | | text_delta | During streamed runs when incremental model text arrives. | delta | | warning | When a non-fatal issue occurs (memory fallback, dead letters, etc.). | Varies by warning type | | run_completed | When the run reaches successful terminal state. | None (terminal) | | run_failed | When the run terminates due to an unrecoverable error. | error message in event.message | | run_interrupted | When the run is interrupted by the caller or by timeout. | Interruption reason in event.message | | run_cancelled | When the run is cancelled before completion. | None (terminal) | AgentRunEvent structure Each event is an AgentRunEvent dataclass with the following fields: | Field | Type | Description | | --- | --- | --- | | type | str | Event type identifier (see table above). | | run_id | str | Unique run identifier. | | thread_id | str | Thread identifier for memory continuity. | | state | str | Current run state when the event was emitted. | | step | int | Current step number (0 if not yet in a step). | | message | str or None | Human-readable description of what happened. | | data | dict or None | Structured payload with event-specific fields. | Consuming events The primary way to consume events is through the run handle's events async iterator: Pattern: event-type branching The recommended consumption pattern is a simple if/elif chain that branches on event.type. This is explicit, readable, and easy to extend: Forward compatibility The event contract is append-only. New event types may be added in future releases, but existing event types will not be removed or have their data fields changed. Your event consumer should always handle unknown event types gracefully. The simplest approach is a default branch that logs or ignores unknown types: This ensures your code continues to work when AFK adds new event types without requiring a code update.", + "token_count": 434 + }, + { + "id": "doc_567be1281e2f", + "path": "docs/library/security-model.mdx", + "url": "/library/security-model", + "title": "Security Model", + "description": "Security boundaries, policy engine, and production hardening.", + "headings": [ + "Security boundaries", + "Default posture", + "Production hardening checklist", + "Secret isolation", + "Threat model overview", + "Next steps" + ], + "content_sha256": "9c2f919368fe1ccad33c5509761ad84c85f2b3ccee485a59fc99402ad5ef9694", + "content": "AFK implements security through **four boundaries** \u2014 policy engine, tool runtime, A2A/MCP bridges, and sandbox. Each boundary enforces least-privilege defaults and requires explicit opt-in for elevated permissions. Security boundaries Gate tool calls and agent actions with configurable rules. **Actions:** allow (default), deny, request_approval, request_user_input Every tool call passes through validation, policy checks, and output sanitization. **Sandbox profiles** are configured at the runner level, not per-tool: External communication requires authentication and per-caller authorization. Hard limits prevent runaway agents. Default posture AFK defaults to **least privilege**: | Setting | Default | Meaning | | ------------------------ | ---------------------------------------------------------------------------- | ------------------------------------- | | Tool policy | allow | Tools run unless explicitly denied | | Tool output sanitization | True | Output is sanitized by default | | A2A authentication | Required | No unauthenticated A2A | | MCP authentication | Required | No unauthenticated MCP | | Cost limits | None ( ) | **You must set max_total_cost_usd** | | Sandbox | None | Tools run in the host process | **Cost limits are not set by default.** Always configure max_total_cost_usd in production to prevent runaway spending. Production hardening checklist | Area | Action | Status | | -------------- | ----------------------------------------------- | ----------------------------------------- | | **Cost** | Set max_total_cost_usd on all agents | | | **Cost** | Set max_steps and max_tool_calls | | | **Policy** | Add deny rules for admin/destructive tools | | | **Policy** | Add request_approval for mutating operations | | | **Tools** | Enable sanitize_tool_output=True | | | **Tools** | Set tool_output_max_chars | | | **Tools** | Use sandbox profiles for code execution | | | **A2A/MCP** | Configure auth providers with valid tokens | | | **A2A/MCP** | Set per-caller agent access lists | | | **A2A/MCP** | Enable rate limiting | | | **Secrets** | Store API keys in environment variables | | | **Secrets** | Use secret scope isolation per tool call | | | **Monitoring** | Configure telemetry exporter (OTEL) | | | **Monitoring** | Set up alerts for error rate and cost anomalies | | Secret isolation AFK recommends isolating secrets at the environment level. Use separate environment scopes and the runner's ToolContext.metadata to control which credentials are available to each tool: Threat model overview | Threat | Mitigation | | ----------------------- | ------------------------------------------- | | **Prompt injection** | Output sanitization, input validation | | **Runaway agents** | Cost limits, step limits, wall time | | **Tool abuse** | Policy engine, sandbox profiles | | **Unauthorized access** | A2A/MCP auth, per-caller authorization | | **Secret leakage** | Secret scope isolation, output sanitization | | **Cost explosion** | max_total_cost_usd, circuit breakers | Next steps How errors flow through the system. Production playbook and anti-patterns.", + "token_count": 288 + }, + { + "id": "doc_1e46cbc6bfae", + "path": "docs/library/snippets/01_minimal_chat_agent.mdx", + "url": "/library/snippets/01_minimal_chat_agent", + "title": "01: Minimal Chat Agent", + "description": "Smallest synchronous AFK agent run.", + "headings": [ + "Line-by-line explanation", + "What AgentResult contains", + "Expected behavior" + ], + "content_sha256": "1232b15cabfce1a8b2463d495eb4dca40efebc440a22e456b95cb6e2a4af8225", + "content": "This is the simplest possible AFK agent. It demonstrates the three core concepts you need to get started: defining an Agent with a model and instructions, creating a Runner to execute it, and reading the result from final_text. If you are new to AFK, start here. Every other example builds on this foundation. Line-by-line explanation **Agent(...)** defines the agent's identity and behavior. The name is used for telemetry and logging. The model specifies which LLM to use. The instructions become the system prompt that guides the model's behavior. **Runner()** creates the execution engine. With no arguments, it uses in-memory defaults: headless interaction mode, no telemetry sink, and no policy engine. This is the fastest way to get started during development. **runner.run_sync(...)** executes the agent synchronously, blocking until the run completes. Under the hood, this creates an async event loop, runs the agent through the full lifecycle (LLM call, optional tool execution, optional subagent delegation), and returns the terminal AgentResult. The user_message is the initial prompt sent to the model. **result.final_text** contains the model's final text response. This is the primary output field on AgentResult. Always use final_text (not output_text) to access the agent's response. What AgentResult contains The AgentResult dataclass returned by run_sync includes: | Field | Type | Description | | --- | --- | --- | | final_text | str | The agent's final text response. | | state | str | Terminal state: \"completed\", \"failed\", \"cancelled\", or \"degraded\". | | run_id | str | Unique identifier for this run. | | thread_id | str | Thread identifier for memory continuity across runs. | | tool_executions | list | Records of all tool calls made during the run. | | subagent_executions | list | Records of all subagent invocations. | | usage | UsageAggregate | Token usage and cost estimates across all LLM calls. | Expected behavior When you run this example, the runner makes a single LLM call to gpt-4.1-mini with the system instructions and user message. Since no tools are registered, the model responds with text only. The run completes in one step with state=\"completed\", and final_text contains a concise definition of error budgets in SRE. No network calls, API keys, or external services are required beyond access to the specified LLM provider (configured via environment variables like OPENAI_API_KEY).", + "token_count": 251 + }, + { + "id": "doc_d6ad61a79371", + "path": "docs/library/snippets/02_policy_with_hitl.mdx", + "url": "/library/snippets/02_policy_with_hitl", + "title": "02: Policy with Human Interaction", + "description": "Route sensitive actions through policy and human approval.", + "headings": [ + "Basic example", + "Define a policy that gates destructive operations", + "Headless mode: approval requests are auto-resolved using approval_fallback", + "In headless mode with approval_fallback=\"deny\", the destructive action is blocked.", + "Interactive mode with an InteractionProvider", + "In-memory provider for testing (simulates human approval)", + "In a real application, a separate process or UI would call:", + "provider.resolve_approval(request_id, ApprovalDecision(kind=\"allow\"))", + "Headless vs interactive modes", + "How the policy flow works", + "Policy decision actions" + ], + "content_sha256": "e2c2e11ab5f307f4f1a3008e902cbd49a39845c3d0f0c1a792cf3068c1581a5f", + "content": "Human-in-the-loop (HITL) is the pattern where the agent pauses execution to request approval or input from a human operator before proceeding with a sensitive action. This is critical for any agent that can take destructive or irreversible actions -- deleting data, modifying production systems, sending communications, or spending money. AFK implements HITL through two components: a PolicyEngine that decides which actions require human intervention, and an InteractionProvider that routes the approval request to a human and returns their decision. Basic example Interactive mode with an InteractionProvider In production, you typically want a real human to review approval requests. Use interaction_mode=\"interactive\" with a custom InteractionProvider: Headless vs interactive modes AFK supports three interaction modes, configured via RunnerConfig.interaction_mode: | Mode | Behavior | When to Use | | --- | --- | --- | | \"headless\" | Approval requests are auto-resolved using approval_fallback (default: \"deny\"). No human is involved. | CI/CD pipelines, batch processing, testing, automated workflows where no human is available. | | \"interactive\" | Approval requests are routed to the configured InteractionProvider. The runner pauses until a decision is returned or approval_timeout_s expires. | Production applications with human operators, chat UIs with approval buttons, Slack-based approval workflows. | | \"external\" | Similar to interactive, but designed for use in external orchestration systems where the approval mechanism is managed outside AFK. | Enterprise systems with external approval platforms. | How the policy flow works 1. The agent calls a tool (e.g., drop_table). 2. Before executing, the runner sends a PolicyEvent to the PolicyEngine. 3. The engine evaluates all rules. If a rule matches, it returns a PolicyDecision with the configured action. 4. If the action is request_approval: - In **headless** mode: the runner auto-resolves using approval_fallback. - In **interactive** mode: the runner creates an ApprovalRequest and sends it to the InteractionProvider. Execution pauses until the provider returns an ApprovalDecision. 5. If approved (kind=\"allow\"): the tool executes normally. 6. If denied (kind=\"deny\"): the tool execution is skipped and the model receives a denial message as the tool result. 7. A policy_decision event is emitted in the run event stream for audit purposes. Policy decision actions | Action | Effect | | --- | --- | | \"allow\" | Proceed with execution. No human interaction needed. | | \"deny\" | Block execution immediately. The denial reason is reported to the model. | | \"request_approval\" | Pause and request human approval through the InteractionProvider. | | \"request_user_input\" | Pause and request freeform text input from a human operator. |", + "token_count": 262 + }, + { + "id": "doc_a2e84472f1f9", + "path": "docs/library/snippets/03_subagents_with_router.mdx", + "url": "/library/snippets/03_subagents_with_router", + "title": "03: Subagents with Router", + "description": "Delegate workload to specialist subagents and merge outputs.", + "headings": [ + "Delegation flow", + "Example", + "Define specialist subagents", + "Define the coordinator agent", + "How the coordinator pattern works", + "What subagent_executions contains", + "Subagent failure handling" + ], + "content_sha256": "40c49cfa7a9868920008a50344415cbed7990803b125758a43cb66376458cff8", + "content": "When a task is too complex for a single agent, AFK supports delegating subtasks to specialist subagents. The coordinator (or \"lead\") agent decides what to delegate, and the runner handles dispatching work to subagents, collecting their results, and feeding those results back to the coordinator for synthesis. This pattern is useful for incident response, research workflows, content pipelines, and any scenario where different aspects of a task require distinct expertise or instructions. Delegation flow Example How the coordinator pattern works 1. The **lead agent** receives the user message and decides how to delegate. It can invoke subagents through tool-like calls that the runner intercepts. 2. The **runner** dispatches each subagent invocation as a separate run. Subagents execute independently with their own instructions and model configuration. The runner manages concurrency, timeout, and failure handling for each subagent. 3. **Subagent results** are returned to the lead agent as execution records. Each record contains the subagent's output_text and optional error information. 4. The **lead agent** receives all subagent outputs and synthesizes them into a unified response. This final synthesis step is what produces the coordinator's final_text. What subagent_executions contains The AgentResult returned by the lead agent includes a subagent_executions list. Each entry is a SubagentExecutionRecord with: | Field | Type | Description | | --- | --- | --- | | subagent_name | str | Name of the subagent that was invoked. | | success | bool | Whether the subagent completed successfully. | | output_text | str or None | The subagent's response text, if it completed. | | latency_ms | float | Wall-clock execution time in milliseconds. | | error | str or None | Error message if the subagent failed. | You can inspect these records to understand what each subagent contributed: Subagent failure handling By default, subagent failure policy is continue. You can configure stricter or more resilient behavior using FailSafeConfig: With subagent_failure_policy=\"retry_then_degrade\", the lead agent receives error information for failed subagents alongside successful results and can produce a best-effort synthesis.", + "token_count": 215 + }, + { + "id": "doc_07174a8633ac", + "path": "docs/library/snippets/04_resume_and_compact.mdx", + "url": "/library/snippets/04_resume_and_compact", + "title": "04: Resume and Compact", + "description": "Resume interrupted runs from their last checkpoint and compact retained thread memory to control storage growth.", + "headings": [ + "What this snippet demonstrates", + "Resuming an interrupted run", + "How resume works internally", + "Resume method signature", + "Compacting thread memory", + "How compaction works", + "When to compact", + "Error handling", + "What to read next" + ], + "content_sha256": "1926eddc956569f4ccf828f2605bfd5848af1b4ba75eba3d35876e2235c15491", + "content": "What this snippet demonstrates Agent runs can be interrupted by timeouts, cancellations, infrastructure failures, or intentional pauses (such as waiting for human approval). When a run is interrupted, the runner persists a checkpoint containing the run's state at the point of interruption. The resume() method picks up from that checkpoint, restoring the conversation history, tool execution records, and step counter so the agent continues where it left off rather than starting from scratch. Over time, long-running threads accumulate checkpoint records, event logs, and state entries. The compact_thread() method prunes old records according to retention policies, keeping storage bounded without losing the data needed for active runs. Resuming an interrupted run How resume works internally The runner follows this sequence when resume() is called: 1. **Checkpoint lookup** -- The runner queries the memory store for the latest checkpoint matching the given run_id and thread_id. If no checkpoint exists, it raises AgentCheckpointCorruptionError. 2. **Terminal check** -- If the checkpoint already contains a terminal result (the run completed before the resume was requested), the runner returns that result immediately without re-executing. 3. **Snapshot restoration** -- The runner loads the runtime snapshot from the checkpoint, which includes the conversation message history, step counter, tool execution records, and any pending subagent state. 4. **Continued execution** -- The runner calls run_handle() internally with the restored snapshot, continuing the step loop from where it was interrupted. Resume method signature | Parameter | Type | Description | | --- | --- | --- | | agent | BaseAgent | The agent definition used for continued execution. Must match the agent that started the original run. | | run_id | str | The unique run identifier from the interrupted run. Found on result.run_id. | | thread_id | str | The thread identifier from the interrupted run. Found on result.thread_id. | | context | dict or None | Optional context overlay. Merged with the original run context. | Compacting thread memory How compaction works Compaction operates on two dimensions of stored data: - **Event retention** -- Controlled by RetentionPolicy. Removes event records older than max_age_ms. Events are the raw telemetry log entries (LLM calls, tool executions, state transitions) that accumulate over the lifetime of a thread. - **State retention** -- Controlled by StateRetentionPolicy. Removes state entries that exceed max_entries, keeping only the most recent ones. State entries include checkpoint snapshots, conversation summaries, and key-value metadata. Both policies are optional. If you omit a policy, that dimension is not compacted. The method returns a MemoryCompactionResult with counts of removed records so you can log or alert on compaction activity. When to compact - **After long conversations** -- Threads with hundreds of turns accumulate large checkpoint histories. Compact after the conversation ends or reaches a natural break point. - **On a schedule** -- Run compaction as a background task (e.g., hourly or daily) for threads that are still active but have grown large. - **Before resume** -- If you know a thread has extensive history, compacting before resume reduces the data the runner needs to load. Error handling What to read next - Memory -- Full memory architecture, checkpoint schema, and retention policies. - Core Runner -- Step loop lifecycle, state machine, and all runner API methods. - Checkpoint Schema -- Exact structure of checkpoint records stored in memory.", + "token_count": 372 + }, + { + "id": "doc_3f60413b3a06", + "path": "docs/library/snippets/05_direct_llm_structured_output.mdx", + "url": "/library/snippets/05_direct_llm_structured_output", + "title": "05: Direct LLM Structured Output", + "description": "Use afk.llms with schema-validated responses.", + "headings": [ + "Example", + "Define the output schema as a Pydantic model", + "Build an LLM client using the fluent builder", + "Make a structured request", + "The builder pattern", + "Structured output with Pydantic", + "When to use LLMBuilder vs Runner" + ], + "content_sha256": "6f54fe64ae8f7ca3c497962bf8febe155784c84d094d323bdfb99581cc086743", + "content": "Not every use case needs the full agent loop. Sometimes you want to call an LLM directly with a specific prompt and get back a structured, schema-validated response. AFK's LLMBuilder provides a fluent API for constructing LLM clients that can return Pydantic-validated objects directly, without the overhead of the agent run lifecycle. Use this pattern for classification, extraction, summarization, and any scenario where you want a single LLM call with a guaranteed output schema. Example The builder pattern LLMBuilder uses a fluent (method-chaining) API to construct an LLM client with the exact configuration you need: Each method returns the builder instance, so calls can be chained. The .build() call at the end constructs the final LLMClient with all specified settings. Available builder methods: | Method | Purpose | | --- | --- | | .provider(name) | Set the LLM provider (\"openai\", \"litellm\", \"anthropic_agent\"). | | .model(name) | Set the model identifier. | | .profile(name) | Apply a named configuration profile (\"production\", \"development\", etc.). | | .settings(settings) | Replace the loaded LLMSettings. | | .with_middlewares(stack) | Attach chat, stream, or embedding middleware. | | .with_observers(observers) | Attach LLM lifecycle observers. | | .with_cache(cache_backend) | Attach a cache backend instance or registered backend id. | | .with_router(router) | Attach a router instance or registered router id. | | .build() | Construct and return the LLMClient. | Sampling controls are request fields, not builder methods. Set them on LLMRequest, for example LLMRequest(..., temperature=0.0, max_tokens=1000). Structured output with Pydantic When you pass response_model=YourModel to client.chat(), the client instructs the LLM to return output that conforms to the model's JSON schema. The response is parsed and validated against the Pydantic model: - If the LLM returns valid structured output, resp.structured_response contains the parsed dictionary and resp.text contains the raw response. - If the LLM returns output that does not match the schema, a LLMInvalidResponseError is raised. This is powered by the LLM provider's native structured output support (e.g., OpenAI's response_format parameter) when available, with a fallback to prompt-based JSON extraction. When to use LLMBuilder vs Runner | Use Case | Approach | | --- | --- | | Single LLM call, no tools, no memory | LLMBuilder -- simpler, faster, no lifecycle overhead. | | Structured extraction or classification | LLMBuilder with response_model. | | Multi-turn conversation with tools | Runner -- provides the full agent loop with tool execution, policy, and memory. | | Subagent delegation | Runner -- only the runner supports subagent dispatch. | | Event streaming to a UI | Runner with run_stream(). | | Eval-driven development | Runner -- evals require the full AgentResult lifecycle. | Use LLMBuilder when you want precision and control over a single LLM interaction. Use Runner when you need the full agentic lifecycle.", + "token_count": 305 + }, + { + "id": "doc_43ad71a4ad0c", + "path": "docs/library/snippets/06_tool_registry_security.mdx", + "url": "/library/snippets/06_tool_registry_security", + "title": "06: Tool Registry Security", + "description": "Safe tool registration and guardrail practices.", + "headings": [ + "Read-only vs mutating tools", + "--- Read-only tool: safe, broadly permitted ---", + "--- Mutating tool: destructive, requires policy gate ---", + "Policy gate setup", + "Define policy rules that distinguish read vs write operations", + "In headless mode, the delete is auto-denied. The model sees the denial and responds accordingly.", + "Sandbox profiles for filesystem tools", + "Scoping destructive tools" + ], + "content_sha256": "a1ef90a4e46b3a7e305f3a953816f7601fc4a0b6fb3d32a17d5f5faa7ba3a2e3", + "content": "Tools are the primary way agents interact with external systems. A tool that reads data is fundamentally different from a tool that deletes resources -- and your security model should reflect this. AFK provides multiple layers of defense for tool security: scoped tool definitions with typed arguments, sandbox profiles that restrict execution capabilities, and policy gates that require human approval for destructive operations. This page demonstrates how to register tools safely, distinguish between read-only and mutating tools, and configure policy gates to protect against unintended destructive actions. Read-only vs mutating tools The most important security distinction is between tools that observe (read-only) and tools that act (mutating). Read-only tools are generally safe to allow broadly. Mutating tools should be tightly scoped and policy-gated. Notice the differences: - The read-only tool (get_resource) has a description that explicitly says \"Read-only.\" This signals to both the model and human reviewers that the tool is safe. - The mutating tool (delete_resource) has a description warning about irreversibility. This helps the model understand the severity, and helps policy rules identify destructive operations. Policy gate setup Use a PolicyEngine to require human approval before any mutating tool executes: Sandbox profiles for filesystem tools For tools that interact with the filesystem or execute commands, use SandboxProfile to restrict their capabilities: Scoping destructive tools Follow these principles when registering destructive tools: 1. **Name them clearly.** Use verb prefixes that signal intent: delete_, remove_, drop_, update_, modify_. This makes policy rules easy to write and audit. 2. **Type all arguments.** Use Pydantic models for argument validation. Never accept freeform dict arguments for mutating operations. 3. **Describe irreversibility.** Include \"irreversible\", \"destructive\", or \"permanent\" in the tool description. This helps both the model and policy reviewers understand the risk. 4. **Gate with policy rules.** Every mutating tool should have a corresponding policy rule. Use request_approval for interactive environments and deny as the fallback in headless mode. 5. **Set cost limits.** Use FailSafeConfig.max_tool_calls and max_total_cost_usd to prevent runaway tool usage, especially when the agent has access to APIs with per-call costs. 6. **Audit everything.** Policy decisions are emitted as policy_decision events in the run event stream. Persist these events for compliance and debugging.", + "token_count": 261 + }, + { + "id": "doc_c346f13ce597", + "path": "docs/library/snippets/07_tool_hooks_and_middleware.mdx", + "url": "/library/snippets/07_tool_hooks_and_middleware", + "title": "07: Tool Hooks and Middleware", + "description": "Add pre-execution validation, post-execution transformation, and cross-cutting middleware to tools and the LLM client pipeline.", + "headings": [ + "What this snippet demonstrates", + "Tool pre-hooks", + "Pre-hook argument model matches the main tool's argument shape", + "Pre-hook: sanitize and normalize the query before the tool runs", + "Main tool with the pre-hook attached", + "Pre-hook execution flow", + "Tool post-hooks", + "Tool-level middleware", + "Attaching middleware to a tool", + "Registry-level middleware", + "LLM client middleware", + "Chat middleware: intercepts non-streaming chat requests", + "Build client with middleware", + "LLM middleware protocols", + "Built-in LLM middleware", + "Timeout middleware", + "Configure timeouts", + "Add to middleware stack", + "Build client", + "When to use each layer", + "What to read next" + ], + "content_sha256": "cacc5663484a5478e3025773c47ac9ffe5ed840192da35d5ac11d46d5ff94c5d", + "content": "What this snippet demonstrates AFK provides two distinct hook/middleware systems that operate at different layers: 1. **Tool hooks and middleware** -- Pre-hooks, post-hooks, and middleware that wrap individual tool executions. These use Pydantic models for typed arguments and run inside the tool execution pipeline. 2. **LLM middleware** -- Middleware that wraps LLM client operations (chat, stream, embed). These intercept requests and responses at the provider transport layer. Both systems follow the same pattern: define a callable, wire it into the pipeline, and the runner executes it at the appropriate point in the lifecycle. Tool pre-hooks A pre-hook runs before the main tool function executes. It receives the tool's arguments (validated against its own Pydantic model) and returns a dictionary of transformed arguments that the main tool will receive. Use pre-hooks for input sanitization, enrichment, or validation that should happen before execution. Pre-hook execution flow The pre-hook receives validated arguments and must return a dictionary compatible with the main tool's args_model. If the returned dictionary fails validation against the tool's model, the tool call fails with a ToolValidationError. Tool post-hooks A post-hook runs after the main tool function completes. It receives the tool output and can transform or annotate the result before it is returned to the LLM. Use post-hooks for output sanitization, logging, or enrichment. AFK passes post-hooks a payload dictionary with the shape {\"output\": , \"tool_name\": \" \"}. The post-hook must return a dictionary with the same shape. Tool-level middleware Tool-level middleware wraps around the entire tool execution, including pre-hooks and post-hooks. Middleware receives a call_next function and the tool arguments, and can modify behavior before, after, or around execution. Attaching middleware to a tool Middleware executes in the order listed. The first middleware in the list is the outermost wrapper. Each middleware calls call_next to pass control to the next middleware (or the actual tool function if it is the last one). Registry-level middleware Registry-level middleware applies to every tool in a ToolRegistry, not just a single tool. Use this for cross-cutting concerns like audit logging, rate limiting, or policy enforcement that should apply uniformly. LLM client middleware LLM middleware operates at the provider transport layer, intercepting requests to and responses from the LLM API. AFK defines three middleware protocols for the three LLM operations: LLM middleware protocols | Protocol | Operation | Signature | | --- | --- | --- | | LLMChatMiddleware | Non-streaming chat | async (call_next, req: LLMRequest) -> LLMResponse | | LLMEmbedMiddleware | Embeddings | async (call_next, req: EmbeddingRequest) -> EmbeddingResponse | | LLMStreamMiddleware | Streaming chat | (call_next, req: LLMRequest) -> AsyncIterator[LLMStreamEvent] | Each middleware receives call_next (the next middleware or transport in the chain) and the request object. It can modify the request before calling call_next, modify the response after, or short-circuit entirely by returning a response without calling call_next. Built-in LLM middleware AFK ships with pre-built middleware for common patterns: Timeout middleware Apply per-request timeouts to prevent runaway calls: The timeout middleware respects TimeoutPolicy from the request if provided: When to use each layer | Layer | Scope | Use for | | --- | --- | --- | | **Tool pre-hook** | Single tool, before execution | Input sanitization, argument enrichment, validation | | **Tool post-hook** | Single tool, after execution | Output sanitization, redaction, annotation | | **Tool middleware** | Single tool, wraps execution | Timing, retries, caching, error handling | | **Registry middleware** | All tools in registry | Audit logging, rate limiting, policy enforcement | | **LLM middleware** | All LLM calls through client | Request metadata, response logging, tracing | What to read next - Tools -- Full tool system architecture, the 6-step execution pipeline, and design guidelines. - Tool Call Lifecycle -- Detailed lifecycle of a tool call from LLM proposal to result delivery. - LLMs Overview -- Builder workflow, runtime profiles, and provider selection.", + "token_count": 432 + }, + { + "id": "doc_464a2b4a8d35", + "path": "docs/library/snippets/08_prebuilt_runtime_tools.mdx", + "url": "/library/snippets/08_prebuilt_runtime_tools", + "title": "08: Prebuilt Runtime Tools", + "description": "Use AFK's built-in filesystem tools with directory-scoped security constraints and compose them with policy checks.", + "headings": [ + "What this snippet demonstrates", + "Building runtime tools", + "Create filesystem tools scoped to a specific directory", + "Available prebuilt tools", + "list_directory", + "read_file", + "Security: directory traversal prevention", + "Composing with policy checks", + "Define a policy that requires approval for reading certain files", + "Composing with custom tools", + "Combine prebuilt + custom tools", + "Command allowlists and sandbox profiles", + "Create a read-only sandbox that restricts what operations tools can perform", + "What to read next" + ], + "content_sha256": "0b75873372d50bad1c2ec5d4bcd1e04445f683e855b896766f09d4bf86609937", + "content": "What this snippet demonstrates AFK ships prebuilt tools for common runtime operations like listing directories and reading files. These tools are designed with security-first defaults: every tool is scoped to an explicit root directory that prevents directory traversal attacks. This snippet shows how to create, configure, and compose prebuilt tools with agents and policy guards. Building runtime tools The build_runtime_tools() factory creates a set of filesystem tools bound to a specific root directory. All path operations within these tools are resolved against this root, and any attempt to access files outside it raises a FileAccessError. Available prebuilt tools The build_runtime_tools() factory produces two tools: list_directory Lists entries in a directory under the configured root. Returns entry names, paths, and type flags (file or directory). | Parameter | Type | Default | Description | | ------------- | ----- | ------- | ----------------------------------------------------------------- | | path | str | \".\" | Relative path to list, resolved against the root directory. | | max_entries | int | 200 | Maximum entries to return (1--5000). Prevents unbounded listings. | **Returns:** A dictionary with root, path, and entries (list of {name, path, is_dir, is_file}). read_file Reads the contents of a file under the configured root, with configurable truncation to prevent excessive token consumption. | Parameter | Type | Default | Description | | ----------- | ----- | ---------- | -------------------------------------------------------------------------------- | | path | str | (required) | Relative path to the file, resolved against the root directory. | | max_chars | int | 20_000 | Maximum characters to read (1--500,000). Content is truncated beyond this limit. | **Returns:** A dictionary with root, path, content, and truncated (boolean indicating whether content was truncated). Security: directory traversal prevention Every path operation is validated with an internal containment check that uses Python's Path.relative_to() to verify that the resolved path stays within the configured root. This prevents attacks like: If a path escapes the root, the tool raises FileAccessError immediately, before any file I/O occurs. Composing with policy checks For additional security, pair runtime tools with a policy engine that gates specific operations on approval: Composing with custom tools You can combine prebuilt tools with your own custom tools in a single agent: Command allowlists and sandbox profiles For production environments, restrict tool capabilities further using sandbox profiles: This ensures that even if the LLM attempts to use tools for unauthorized operations, the sandbox profile blocks execution before any I/O occurs. What to read next - Tools -- Full tool system architecture, including the @tool decorator, ToolResult, and execution pipeline. - Snippet 06: Tool Registry Security -- Security scoping, policy gates, and sandbox profiles in detail. - Security Model -- Threat model, defense layers, and RunnerConfig security fields.", + "token_count": 294 + }, + { + "id": "doc_64090a20f830", + "path": "docs/library/snippets/09_system_prompt_loader.mdx", + "url": "/library/snippets/09_system_prompt_loader", + "title": "09: System Prompt Loader", + "description": "Resolve agent system prompts from a file hierarchy with deterministic precedence, Jinja templating, and stat-based caching.", + "headings": [ + "What this snippet demonstrates", + "Resolution precedence", + "Basic usage", + "Option 1: Inline instructions (highest priority)", + "Option 2: Explicit instruction file", + "Option 3: Auto-detected file (uses agent name)", + "Loads .agents/prompt/CHAT_AGENT.md automatically", + "Name-to-filename conversion", + "Prompts directory resolution", + "Explicit", + "Environment variable", + "export AFK_AGENT_PROMPTS_DIR=/opt/prompts", + "Default: .agents/prompt/", + "Jinja2 templating", + "Template context variables", + "Caching and hot-reload", + "Security: path containment", + "This would raise PromptAccessError:", + "What to read next" + ], + "content_sha256": "a5dcded5d70453d7ede1e44f292d953ee51243eec18fc5b383e2a4231e130fee", + "content": "What this snippet demonstrates AFK agents need system prompts (instructions) that tell the LLM how to behave. Rather than hardcoding instructions as inline strings, AFK provides a file-based prompt resolution system that loads prompts from a directory hierarchy. This keeps prompts version-controlled, editable by non-developers, and reusable across agents. The prompt loader resolves instructions through a deterministic precedence chain, supports Jinja2 templating for dynamic prompts, and caches compiled templates using stat-based invalidation for hot-reload during development. Resolution precedence The prompt system resolves agent instructions through this priority chain: 1. **Inline instructions** -- If the agent has a non-empty instructions string, it is used directly. No file loading occurs. 2. **Explicit instruction_file** -- If set, the file is loaded from the configured prompts_dir. The path must resolve to a file inside the prompts root (no directory traversal). 3. **Auto-detected file** -- If neither is set, the agent's name is converted to UPPER_SNAKE_CASE.md and loaded from prompts_dir. Basic usage Name-to-filename conversion The auto-detection algorithm converts the agent name to a filename using these rules: | Agent Name | Derived Filename | Rule Applied | | --- | --- | --- | | ChatAgent | CHAT_AGENT.md | CamelCase split on boundaries | | chatagent | CHAT_AGENT.md | Lowercase agent suffix detected and split | | research-assistant | RESEARCH_ASSISTANT.md | Hyphens replaced with underscores | | QA Bot v2 | QA_BOT_V2.md | Spaces and non-alphanumeric chars become underscores | The conversion is handled by derive_auto_prompt_filename() internally. It splits camelCase boundaries, normalizes non-alphanumeric characters to underscores, collapses consecutive underscores, and uppercases the result. Prompts directory resolution The prompts directory is resolved through its own priority chain: 1. Explicit prompts_dir argument on the Agent constructor. 2. AFK_AGENT_PROMPTS_DIR environment variable. 3. Default: .agents/prompt relative to the current working directory. Jinja2 templating Prompt files support Jinja2 template syntax. When the runner resolves a prompt, it renders the template with a context dictionary that includes agent metadata and any custom context passed to the run. **File: .agents/prompt/SUPPORT_AGENT.md** **Agent code:** Template context variables The following variables are available in every prompt template: | Variable | Type | Description | | --- | --- | --- | | agent_name | str | The agent's name field. | | agent_class | str | The Python class name of the agent. | | context | dict | The full context dictionary passed to the run. | | ctx | dict | Alias for context (shorthand). | Any keys in the context dictionary that are not reserved names (context, ctx, agent_name, agent_class) are also available as top-level template variables. So {{ company_name }} works as a shorthand for {{ ctx.company_name }}. Caching and hot-reload The prompt system uses a process-wide PromptStore singleton that caches at three levels: 1. **File cache** -- Keyed by resolved file path. Uses stat() metadata (mtime, size, inode) as the cache signature. If the file changes on disk, the cache entry is invalidated automatically. 2. **Text pool** -- Deduplicates prompt text by SHA-256 hash. If multiple agents use the same prompt content (even from different files), only one copy is stored in memory. 3. **Template cache** -- Compiled Jinja2 templates are cached by content hash. Re-rendering with different context variables reuses the compiled template. This means that during development, you can edit prompt files and they will be picked up on the next run without restarting the process. In production, the stat-based check is a single os.stat() call per prompt resolution, which is negligible overhead. Security: path containment The prompt loader enforces strict path containment. The resolved prompt file path must be inside the configured prompts_dir. If an instruction_file path resolves outside the prompts root (via ../ traversal or an absolute path pointing elsewhere), the loader raises PromptAccessError immediately. What to read next - System Prompts -- Full system prompt architecture, resolution pipeline, and design guidelines. - Agents -- Agent model, configuration fields, and composition patterns. - Security Model -- Threat model and defense layers including prompt injection considerations.", + "token_count": 448 + }, + { + "id": "doc_7b26cbd9a7ae", + "path": "docs/library/snippets/10_streaming_chat_with_memory.mdx", + "url": "/library/snippets/10_streaming_chat_with_memory", + "title": "10: Streaming Chat with Memory", + "description": "Combine real-time streaming with thread-based memory for multi-turn chat UIs.", + "headings": [ + "What this snippet demonstrates", + "Full example", + "Key patterns", + "Thread ID connects turns", + "These two calls share memory", + "Access the result after streaming", + "Cancel mid-stream", + "The run transitions to \"cancelled\" state", + "What to read next" + ], + "content_sha256": "747e1945af154333e226917cadea057d5fc51e87933b13672f562d0190008ae2", + "content": "What this snippet demonstrates Most chat applications need two things simultaneously: **real-time streaming** (so users see text as it's generated) and **memory continuity** (so the agent remembers previous turns). This snippet shows how to combine run_stream() with thread_id to build a multi-turn streaming chat handler. Full example Key patterns Thread ID connects turns Pass the same thread_id across run_stream() calls to maintain conversation context: Access the result after streaming The handle.result is available after the stream completes: Cancel mid-stream If the user navigates away or clicks \"stop\": What to read next - Streaming \u2014 Full event reference and stream control API. - Memory \u2014 Thread persistence, compaction, and backend configuration. - Snippet 04: Resume + Compact \u2014 Checkpoint-based resumption and memory management.", + "token_count": 92 + }, + { + "id": "doc_fdf48d427fc0", + "path": "docs/library/snippets/11_cost_monitoring.mdx", + "url": "/library/snippets/11_cost_monitoring", + "title": "11: Cost Monitoring", + "description": "Track and control agent costs using FailSafeConfig budgets and telemetry events.", + "headings": [ + "What this snippet demonstrates", + "Setting cost budgets", + "Monitoring cost from results", + "Access usage statistics", + "Real-time cost monitoring via streaming", + "Cost-aware batch processing", + "Operating recommendations", + "What to read next" + ], + "content_sha256": "f2d31922b3ae2362b1bc68b3e2c53fe59d0b80c0806fa5cfe522b204e0410131", + "content": "What this snippet demonstrates Runaway agent loops are the most common source of unexpected API costs. AFK provides two defense layers: **cost budgets** that kill runs when spending exceeds a threshold, and **telemetry events** that let you observe cost in real time. This snippet shows how to configure both. Setting cost budgets The simplest defense is a hard cost ceiling on every agent: When the estimated cost exceeds max_total_cost_usd, the runner terminates the run with a degraded state and returns the best partial result. Monitoring cost from results Every AgentResult includes token counts and cost estimates: Real-time cost monitoring via streaming For long-running agents, monitor cost during execution: Cost-aware batch processing When running multiple agents in a batch, track cumulative cost: Operating recommendations 1. **Always set max_total_cost_usd** \u2014 even generous limits prevent runaway costs 2. **Layer defenses** \u2014 combine cost limits with max_llm_calls, max_steps, and max_wall_time_s 3. **Use telemetry for dashboards** \u2014 export metrics to monitor cost trends over time 4. **Set per-item budgets in batches** \u2014 prevent one expensive item from consuming the entire budget 5. **Choose models by task** \u2014 use smaller models for routine work and reserve larger models for requests that need them What to read next - Observability \u2014 Telemetry pipeline for metrics and dashboards. - Failure Policy Matrix \u2014 How cost limit breaches flow through the system. - Configuration Reference \u2014 Full FailSafeConfig field reference.", + "token_count": 171 + }, + { + "id": "doc_40e30666bfa8", + "path": "docs/library/snippets/12_mcp_client_integration.mdx", + "url": "/library/snippets/12_mcp_client_integration", + "title": "12: MCP Client Integration", + "description": "Discover and use tools from external MCP servers in your agents.", + "headings": [ + "What this snippet demonstrates", + "Consuming MCP tools", + "Connect, discover, and attach", + "Using the Agent's built-in MCP support", + "The agent connects to MCP servers automatically during startup", + "Mixing local and MCP tools", + "Security with MCP tools", + "What to read next" + ], + "content_sha256": "5b44bbbb025f2b01a553bacf0e510a09070c771c3fb99d91656df5ad80fd03df", + "content": "What this snippet demonstrates AFK agents can consume tools from external MCP (Model Context Protocol) servers just like local tools. This snippet shows how to connect to an MCP server, discover available tools, and attach them to an agent \u2014 all with the same validation, policy gates, and telemetry as local tools. Consuming MCP tools Connect, discover, and attach Using the Agent's built-in MCP support For simpler setups, pass MCP server refs directly to the agent: Mixing local and MCP tools Combine your own tools with external MCP tools: Security with MCP tools Apply policy rules to MCP-sourced tools just like local tools: **MCP tools are transparent.** Once attached to an agent, they go through the same validation, policy gates, sanitization, and telemetry as local tools. The agent doesn't know whether a tool is local or remote. What to read next - MCP Server \u2014 Expose your own tools via MCP, plus authentication and rate limiting. - Tools \u2014 Full tool system architecture. - Snippet 06: Tool Security \u2014 Policy gates and sandbox profiles.", + "token_count": 129 + }, + { + "id": "doc_d1f7d83e0824", + "path": "docs/library/snippets/13_multi_model_fallback.mdx", + "url": "/library/snippets/13_multi_model_fallback", + "title": "13: Multi-Model Fallback", + "description": "Configure fallback model chains for LLM resilience and cost optimization.", + "headings": [ + "What this snippet demonstrates", + "Basic fallback chain", + "Cost-optimized fallback", + "Start cheap, escalate if quality is insufficient", + "Complex tasks get the big model with fallbacks", + "Simple task -> cheap model handles it", + "Complex task -> powerful model with safety net", + "Circuit breaker integration", + "Multi-agent with different model tiers", + "Cheap model for simple classification", + "Inspecting which model was used", + "Recommendations", + "What to read next" + ], + "content_sha256": "f217ecec21c2dde7c33c194035ef4d3e46de6ffea14b1c9f76e5e8691024b281", + "content": "What this snippet demonstrates LLM API calls fail \u2014 rate limits, outages, timeouts. AFK's fallback_model_chain lets you define an ordered list of models to try when the primary model fails. This snippet shows how to configure fallback chains for resilience, cost optimization, and provider diversification. Basic fallback chain When gpt-4.1 fails (timeout, rate limit, outage): 1. AFK retries with the primary model (controlled by retry policy) 2. If retries exhaust, it falls through to gpt-4.1-mini 3. If that also fails, it tries gpt-4.1-nano 4. If all models fail, the llm_failure_policy determines the outcome Cost-optimized fallback Use expensive models only when needed: Circuit breaker integration AFK's built-in circuit breaker works with fallback chains. When a model triggers too many failures, the breaker opens and the system skips straight to the next fallback: Multi-agent with different model tiers Use different model tiers for different specialists: Inspecting which model was used After a run, check the result metadata and usage aggregate: Recommendations | Scenario | Primary Model | Fallback Chain | | ------------------------ | -------------- | ------------------------------- | | **Classification** | gpt-4.1-nano | gpt-4.1-mini | | **General chat** | gpt-4.1-mini | gpt-4.1-nano | | **Complex analysis** | gpt-4.1 | gpt-4.1-mini \u2192 gpt-4.1-nano | | **Code generation** | gpt-4.1 | gpt-4.1-mini | | **Cost-sensitive batch** | gpt-4.1-nano | _(none)_ | What to read next - Configuration Reference \u2014 Full FailSafeConfig fields including circuit breaker settings. - Failure Policy Matrix \u2014 How failures flow through the system. - Snippet 11: Cost Monitoring \u2014 Track and control costs in real time.", + "token_count": 183 + }, + { + "id": "doc_4462f67b1c03", + "path": "docs/library/snippets/14_production_client.mdx", + "url": "/library/snippets/14_production_client", + "title": "14: Client Timeouts and Redis Pooling", + "description": "Configure LLM client timeouts and Redis connection pooling.", + "headings": [ + "What this snippet demonstrates", + "Timeout middleware", + "Per-request timeout override", + "Redis connection pooling", + "Using with memory store", + "Full example", + "Configuration reference", + "TimeoutConfig", + "PoolConfig", + "What to read next" + ], + "content_sha256": "3ad51d99b7437da1d6f78dd70ba9238005ea9818864ec8eeb4a97c4d39790c49", + "content": "What this snippet demonstrates This snippet shows how to configure: 1. **Timeout middleware** to bound slow provider calls 2. **Redis connection pooling** for shared cache or memory connections 3. **Shutdown handling** so runners and Redis pools close cleanly Timeout middleware Apply per-request timeouts to prevent runaway LLM calls: Per-request timeout override Redis connection pooling For Redis deployments, use connection pooling instead of creating a new client per request: Using with memory store Full example Configuration reference TimeoutConfig | Parameter | Default | Description | | --- | --- | --- | | default_timeout_s | 30.0 | Default timeout for all operations | | chat_timeout_s | None | Specific timeout for chat requests | | embed_timeout_s | None | Specific timeout for embeddings | | stream_timeout_s | None | Specific timeout for streaming | PoolConfig | Parameter | Default | Description | | --- | --- | --- | | max_connections | 50 | Maximum total connections | | max_idle_connections | 10 | Maximum idle connections | | socket_timeout | 5.0 | Socket read/write timeout | | socket_connect_timeout | 5.0 | Connection establishment timeout | | socket_keepalive | False | Enable TCP keepalive | | health_check_interval_s | 30.0 | Interval for health checks | What to read next - LLM Control & Session -- Retry, caching, and circuit breaker policies - Deployment Guide -- Production deployment with Docker and Kubernetes - Performance Guide -- Optimize latency and throughput", + "token_count": 138 + }, + { + "id": "doc_109157c0d2d7", + "path": "docs/library/streaming.mdx", + "url": "/library/streaming", + "title": "Streaming", + "description": "Real-time event streaming for chat UIs and CLI tools.", + "headings": [ + "Quick example", + "Event reference", + "Streaming modes", + "Stream control", + "Lifecycle control variant", + "Background tools example", + "Error handling", + "Next steps" + ], + "content_sha256": "dffb536cc9129c079ef96ab842c4ea0ae01a309ee01259823f89e4c20a130350", + "content": "AFK supports real-time streaming via Runner.run_stream(). Instead of waiting for the full response, you receive events as they happen: incremental text, tool lifecycle updates, and terminal status. Quick example Event reference run_stream() emits AgentStreamEvent values with these types: | Event type | When it fires | Key fields | | --- | --- | --- | | text_delta | Incremental text chunks from streaming or fallback path | event.text_delta, event.step | | step_started | New step in the agent loop | event.step, event.state | | tool_started | A tool call is about to execute | event.tool_name, event.tool_call_id, event.step | | tool_completed | A tool call finished | event.tool_name, event.tool_call_id, event.tool_success, event.tool_output, event.tool_error | | tool_deferred | A tool call was accepted but deferred | event.tool_name, event.tool_call_id, event.tool_ticket_id, event.data.resume_hint | | tool_background_resolved | A deferred tool finished successfully | event.tool_name, event.tool_ticket_id, event.tool_output | | tool_background_failed | A deferred tool failed/expired | event.tool_name, event.tool_ticket_id, event.tool_error | | completed | Run finished | event.result (full AgentResult) | | error | Stream/runtime bridge error | event.error | Streaming modes - If the provider supports streaming, AFK forwards model deltas as text_delta. - If the provider is non-streaming, AFK emits chunked fallback text_delta from final text so UI behavior stays consistent. Stream control AgentStreamHandle is read-only. For lifecycle controls (pause(), resume(), cancel(), interrupt()), use run_handle(). Background tools example Error handling error events indicate stream/runtime bridge failures. Tool failures are reported through tool_completed (tool_success=False) and do not necessarily fail the run. Next steps Persist state across streaming runs. Full lifecycle and API reference.", + "token_count": 205 + }, + { + "id": "doc_2798948c3080", + "path": "docs/library/system-prompts.mdx", + "url": "/library/system-prompts", + "title": "System Prompts", + "description": "Configure agent instructions with files, templates, and dynamic context.", + "headings": [ + "Three ways to set instructions", + "Precedence chain", + "Template variables with Jinja2", + "Available template variables", + "Prompt from file with templates", + "Rules", + "Error handling", + "Design guidelines", + "Next steps" + ], + "content_sha256": "6fcbb09ca2206bb61c328929099af4a832216459bbe1880e90c8c7993766a6b5", + "content": "System prompts define what your agent knows and how it behaves. AFK supports three methods \u2014 inline strings, instruction files, and auto-detection \u2014 with Jinja2 templating for dynamic values. Three ways to set instructions Set instructions directly on the Agent. Best for simple, static prompts. Store the prompt in a separate file. Best for long or version-controlled prompts. AFK automatically looks for an instruction file based on the agent's name: The resolution order: 1. prompts/{agent_name}.md 2. prompts/{agent_name}.txt 3. {agent_name}.md 4. {agent_name}.txt Precedence chain When multiple sources are available, AFK uses this order: The first non-empty source wins. If you set both instructions and instruction_file, the inline string takes priority. Template variables with Jinja2 Use {{ variable }} syntax to inject dynamic values into prompts: Available template variables | Source | Variables | Example | | -------------------- | ------------------------------------ | --------------------------------------- | | Agent.context dict | Any key-value pairs you set | {{ company_name }}, {{ user_role }} | | Built-in | agent_name, model_name | {{ agent_name }} | | Runtime | run_id, thread_id (if available) | {{ thread_id }} | Prompt from file with templates Templates work in instruction files too: Error handling | Error | Cause | Resolution | | ----------------------- | ------------------------------------- | -------------------------------------------------------------------------------------------- | | PromptResolutionError | instruction_file path doesn't exist | Check that the file exists under the configured prompts_dir. | | PromptTemplateError | Jinja2 template syntax error | Check for unclosed {{ }} or {% %} blocks. | | PromptTemplateError | Missing template variable | Add the variable to Agent.context or provide a default: {{ var \\| default(\"fallback\") }} | **Keep prompts in version control.** Store instruction files in a prompts/ directory and track changes in git. This gives you prompt history, diffs, and the ability to A/B test prompt versions. Design guidelines - **Inline for prototyping,** files for production. Switch to instruction files when your prompt exceeds ~5 lines. - **Use templates for anything dynamic.** Don't concatenate strings \u2014 use {{ variable }} and set values in context. - **Be specific in instructions.** Tell the agent what to do _and_ what not to do. Include output format expectations. - **Test prompt changes with evals.** A small prompt change can dramatically shift behavior. Run your eval suite after any edit. Next steps Reusable knowledge bundles loaded on demand. Test prompt changes with behavioral assertions.", + "token_count": 249 + }, + { + "id": "doc_78b1717fafe3", + "path": "docs/library/task-queues.mdx", + "url": "/library/task-queues", + "title": "Task Queues", + "description": "Async job processing with execution contracts and dead-letter handling.", + "headings": [ + "Quick start", + "Push a task", + "A worker consumes queued tasks", + "Task lifecycle", + "Execution contracts", + "Worker setup", + "Dead-letter handling", + "Check for dead letters", + "Retry manually after fixing the issue", + "Or discard them", + "Error classification", + "Queue backends", + "Connection pooling", + "Create a pooled connection", + "Use in queue", + "Health check", + "Next steps" + ], + "content_sha256": "da3a4f6c098ee01fffa8507b3bd9ee9116cf103123e29dcbcc58b4a1facb5f5e", + "content": "Task queues decouple agent work producers from consumers. Push a task, a worker picks it up, and the result is stored \u2014 independently of the caller's lifecycle. Use queues for long-running jobs, batch processing, and reliable retries. Quick start Task lifecycle | State | Meaning | | ------------- | ------------------------------------------- | | queued | Waiting to be picked up by a worker | | running | A worker is executing the task | | completed | Task finished successfully | | failed | Task hit an error (may be retried) | | dead_letter | All retries exhausted \u2014 needs manual review | Execution contracts Every task has a **contract** that defines what kind of work it represents: Standard agent chat. Runs an agent with a user message. Generic job dispatch. Runs a custom handler function. Define your own contract for specialized workloads: Register a handler for the contract on the worker side. Worker setup Dead-letter handling When a task exhausts all retries, it moves to the dead-letter queue (DLQ): Error classification The queue uses error classification to decide whether to retry: | Error type | Retried? | Example | | ------------- | ------------------------------------------------------------------------------------ | ------------------------------------------------ | | **Retryable** | (with backoff) | Network timeout, rate limit, transient LLM error | | **Terminal** | (sent to DLQ) | Invalid arguments, auth failure, missing model | | **Non-fatal** | (task completes with warning) | Telemetry export failure | Queue backends State lives in process memory. No setup required. **Use for:** Development, testing, prototyping. Durable queue with persistence and multi-worker support. Set via environment variables: **Use for:** Production deployments, multi-process workers. Connection pooling For high-throughput production workloads, use RedisConnectionPool to manage connections efficiently: The pool provides: - Configurable max connections (default: 50) - Idle connection management - Automatic health checks - Singleton access via get_redis_pool() Next steps Expose tools via the Model Context Protocol. Monitor queue performance and worker health.", + "token_count": 209 + }, + { + "id": "doc_618e34441fa7", + "path": "docs/library/tested-behaviors.mdx", + "url": "/library/tested-behaviors", + "title": "Tested Behaviors", + "description": "Contract-level guarantees and what test suites validate.", + "headings": [ + "Guaranteed behaviors", + "Deterministic delegation ordering", + "Queue contract failure classification", + "Stream lifecycle correctness", + "Telemetry projection stability", + "Eval report schema consistency", + "Test categories", + "Running the test suite", + "Delegation and subagent tests", + "Queue contract tests", + "Eval suite tests", + "Tool execution tests", + "Observability tests", + "LLM runtime tests", + "Interpreting test results", + "Adding new tests", + "CI pipeline guidance", + "Example GitHub Actions step" + ], + "content_sha256": "80884b0dc0cbc8a2eda294fc1ca74d89dea1b5e98a80ea48892237808e33f2e2", + "content": "AFK's tests describe the behavior contributors should preserve when changing the runtime, tools, memory, queues, LLM adapters, observability, and evals. Treat this page as a map from behavior to the test files that cover it. If you change a public contract, update the matching tests and the docs that explain that contract. A passing test suite is necessary, but it is not a release guarantee by itself; review the affected behavior and run the focused suite for the code you touched. Guaranteed behaviors Deterministic delegation ordering **What is tested:** When a parent agent dispatches work to subagents, the delegation engine executes nodes in a deterministic, topologically sorted order. Parallel batches respect concurrency limits. Edge dependencies (where one subagent's output feeds into another's input) are resolved before the dependent node starts. **Why it matters:** If delegation ordering were non-deterministic, the same agent configuration could produce different results depending on task scheduling. Deterministic ordering means your subagent pipelines are reproducible and debuggable. **Test coverage:** Tests verify that DAG plans produce consistent node execution sequences, that edge-based data flow resolves correctly, and that backpressure limits prevent unbounded queue growth. Queue contract failure classification **What is tested:** The task queue system classifies failures into retryable, terminal, and degraded categories. Retryable failures trigger retry with backoff. Terminal failures stop execution immediately. Degraded states allow partial results. **Why it matters:** Incorrect failure classification can cause infinite retry loops (if terminal failures are marked retryable) or premature task abandonment (if retryable failures are marked terminal). The failure classification contract ensures that each failure type triggers the correct recovery behavior. **Test coverage:** Tests verify that each failure category maps to the correct queue behavior, that retry counts and backoff intervals are respected, and that dead-letter handling works correctly. Stream lifecycle correctness **What is tested:** The streaming API (runner.run_stream()) produces events in the correct lifecycle order: stream starts, text deltas arrive, tool events fire at the right times, and the stream terminates with a completed event containing the final AgentResult. Error conditions produce error stream events rather than unhandled exceptions. **Why it matters:** Streaming consumers (such as chat UIs) depend on events arriving in the correct order. A misplaced completed event before all text deltas have been emitted would cause truncated output. An unhandled exception would crash the consumer. **Test coverage:** Tests verify event ordering, ensure that all text content is captured before the terminal event, and confirm that error conditions produce structured error events. Telemetry projection stability **What is tested:** The telemetry projector produces consistent RunMetrics from the same input data. Field names, types, and computed properties (like avg_llm_latency_ms and success) are stable across versions. **Why it matters:** Downstream dashboards, alerting rules, and eval assertions depend on RunMetrics having a stable schema. If a field is renamed or its type changes, every consumer breaks silently. **Test coverage:** Tests verify that projected metrics match expected values for known input data, that computed properties produce correct results, and that to_dict() serialization is stable. Eval report schema consistency **What is tested:** The eval report serializer (suite_report_payload) produces output with a stable schema_version and consistent field structure. The report includes summary statistics, per-case results, assertion details, budget violations, and projected metrics. **Why it matters:** CI pipelines parse eval reports to make release-gating decisions. If the report schema changes, CI scripts break and deployments may be incorrectly blocked or allowed. **Test coverage:** Tests verify the report envelope structure, confirm that schema_version is set correctly, and validate that all expected fields are present and correctly typed. Test categories | Category | Description | Key Modules Covered | | --- | --- | --- | | Agent delegation | Deterministic DAG execution, subagent routing, backpressure limits. | afk.core.runtime, afk.agents.delegation | | Queue contracts | Failure classification, retry behavior, dead-letter handling. | afk.queues | | Stream lifecycle | Event ordering, text delta capture, terminal event correctness. | afk.core.streaming | | Telemetry projection | Metric stability, computed property correctness, serialization. | afk.observability.projectors, afk.observability.models | | Eval reports | Report schema stability, assertion result structure, budget violations. | afk.evals.reporting, afk.evals.models | | Tool execution | Pydantic validation, timeout enforcement, hook/middleware chains. | afk.tools | | Policy evaluation | Rule matching, decision actions, audit event emission. | afk.agents.policy | | LLM runtime | Provider routing, retry/circuit-breaker behavior, streaming correctness. | afk.llms.runtime | | Security | Sandbox profile enforcement, secret scope isolation, command allowlists. | afk.tools.security | Running the test suite Run all tests from the repository root: Run a specific test category: Run with verbose output for debugging: Interpreting test results - **All relevant tests pass**: The checked behavior still matches the test suite. Review docs and public imports before merging user-visible changes. - **A test fails**: Inspect the assertion before changing code. The test may expose a real contract regression, or the intended contract may have changed and need a deliberate test/doc update. - **A new test is marked as xfail**: Include a reason and keep the scope narrow so known limitations do not hide unrelated regressions. Adding new tests When adding a new feature or fixing a bug, follow this pattern: 1. **Identify the contract.** What behavioral guarantee should your change preserve or introduce? Write this as a plain-English statement (e.g., \"Subagent timeout should produce a SubagentExecutionRecord with success=False\"). 2. **Write the test first.** Create a test in the appropriate tests/ subdirectory that asserts the expected behavior. Use descriptive test names that read as contract statements. 3. **Make the test pass.** Implement the feature or fix. The test should pass without any special-casing or mocking of the behavior under test. 4. **Verify stability.** Run the full suite to confirm your change does not break existing contracts. CI pipeline guidance Most tests run without API keys and use in-memory or mocked providers. Some integration tests cover optional backends such as Redis, SQLite, or Postgres behavior; keep those isolated and skippable when the backing service is unavailable. Recommended CI configuration: Store the JUnit XML report as a CI artifact for trend analysis and failure investigation.", + "token_count": 763 + }, + { + "id": "doc_0ac01bce8c79", + "path": "docs/library/tool-call-lifecycle.mdx", + "url": "/library/tool-call-lifecycle", + "title": "Tool Call Lifecycle", + "description": "Detailed event-level view of one tool invocation.", + "headings": [ + "Event timeline", + "Event reference table", + "Policy decisions: allow, deny, defer, and request_user_input", + "Timing and latency tracking", + "How denied tools affect the agent run" + ], + "content_sha256": "9426358483fa8300d96c0f0ec8bc520958e881b7f6827a7fdfe5301576f8f306", + "content": "While the Tools System Walkthrough covers the end-to-end pipeline, this page zooms into the event-level lifecycle of a single tool call. Every tool invocation produces a deterministic sequence of events that flow through the run handle. Understanding this sequence is critical for building observability dashboards, audit logs, and human-in-the-loop approval flows. A tool call begins when the runner receives a ToolCall from the LLM response. Before the handler ever runs, the runner evaluates a policy gate that can allow, deny, or defer the call for human approval. The outcome of that gate determines whether the tool executes at all, and the events emitted along the way tell you exactly what happened and why. Event timeline Event reference table Every event is an AgentRunEvent published on the run handle. The type field identifies the event, and the data dict carries event-specific fields. | Event type | When it fires | Key data fields | Description | | --- | --- | --- | --- | | tool_batch_started | After LLM response contains tool calls | tool_call_count | Signals the start of a batch of one or more tool calls from a single LLM turn. | | policy_decision | After policy evaluation for each tool | tool_name, action, reason | Records the policy engine's decision for one tool call. Emitted for allow, deny, defer, and request_user_input actions. | | run_paused | When a tool call requires human approval | tool_name, reason, payload | The run is suspended waiting for external input. Only emitted when policy returns defer or request_approval. | | run_resumed | After human approval or denial is received | approved | The run resumes after the approval decision. | | tool_completed | After each tool call resolves (success or failure) | tool_name, success, output, error, tool_call_id | The terminal event for one tool call. Always emitted regardless of whether the tool ran, was denied, or timed out. | | warning | When non-fatal issues occur | message | Advisory events such as sandbox violations handled under a continue policy. | Policy decisions: allow, deny, defer, and request_user_input The PolicyEngine evaluates a PolicyEvent with event_type=\"tool_before_execute\" for every tool call. The decision drives the rest of the lifecycle: **allow** -- The tool executes immediately. No human interaction required. This is the default when no policy rules match. **deny** -- The tool is blocked. A ToolExecutionRecord with success=False and error=\"Denied by policy\" is recorded. The denied result is fed back to the model as a tool message so it can adjust its behavior. After a denial, the runner consults fail_safe.approval_denial_policy to decide the run-level outcome: - continue (default) -- The run proceeds. The model sees the denial and may try a different approach. - degrade -- The run transitions to a degraded state and terminates with a degradation message. - fail -- The run raises an AgentExecutionError and transitions to failed. **defer / request_approval** -- The runner pauses the run, emits a run_paused event, and waits for the InteractionProvider to deliver an approval decision. If the provider is HeadlessInteractionProvider, the configured approval_fallback (default: deny) is used. The approval has a configurable timeout (approval_timeout_s, default 300s). **request_user_input** -- Similar to defer, but instead of a yes/no approval, the runner asks the user for a value (such as a confirmation code or corrected parameter). The policy's request_payload can specify a target_arg field, and the user's input is injected into the tool arguments before execution. Timing and latency tracking Every tool execution is timed from the moment registry.call() is invoked to the moment it returns. The latency is captured in milliseconds and recorded in two places: 1. **ToolExecutionRecord.latency_ms** -- Available on the AgentResult.tool_executions list after the run completes. 2. **Telemetry histogram** -- The agent.tool_call.latency_ms metric is emitted with attributes tool_name, result (success/error), and source (execute/replay). For batch-level timing, the agent.tool_batch.latency_ms histogram captures the wall-clock duration of the entire batch (all concurrent tool calls), with attributes for call_count and failure_count. Denied tool calls have no execution latency since the handler never runs. How denied tools affect the agent run When a tool is denied, the model still receives feedback. The runner appends a tool message to the conversation with the denial reason: This feedback loop is important: the model sees that its proposed action was rejected and can adapt. In practice, models respond to denials by either rephrasing the request, choosing a different tool, or providing a text-only response. The run-level impact depends on fail_safe.approval_denial_policy: | Policy | Behavior after denial | | --- | --- | | continue | Run proceeds normally. Model sees the denial and may self-correct. | | degrade | Run transitions to degraded state and exits the step loop with a degradation message. | | fail | Run raises AgentExecutionError and transitions to failed state. | Multiple denials in a single batch are each evaluated independently. If any denial triggers a fail or degrade policy, the remaining tool calls in the batch are skipped.", + "token_count": 535 + }, + { + "id": "doc_745f751f1344", + "path": "docs/library/tools-system-walkthrough.mdx", + "url": "/library/tools-system-walkthrough", + "title": "Tools System Walkthrough", + "description": "From tool registration to runtime execution and model feedback.", + "headings": [ + "Registration to execution map", + "Step-by-step breakdown", + "Full example: tool definition through result inspection", + "1. Define a tool with a Pydantic args model", + "2. Create an agent with the tool", + "3. Run the agent", + "4. Inspect the result", + "Inspecting tool executions", + "Hook and middleware pipeline", + "Error handling" + ], + "content_sha256": "1ed41f9431123214684ef929c17c9f9886933e7e7c218f1ac668e47a6e02e499", + "content": "Every agent capability beyond pure text generation flows through the AFK tool system. A tool starts as a decorated Python function, gets registered in a ToolRegistry, has its JSON Schema exposed to the LLM, and then participates in a tightly controlled loop: the model proposes a call, AFK validates the arguments, evaluates policy gates, executes the handler, sanitizes the output, and feeds the result back into the conversation for the model's next turn. This page walks through every stage of that pipeline end-to-end. Registration to execution map Step-by-step breakdown 1. **Define @tool** -- You write a Python function and decorate it with @tool. The decorator extracts the function name, docstring, and a Pydantic model for its arguments to produce a ToolSpec(name, description, parameters_schema). 2. **Tool registry** -- When the agent starts, all tools (declared on the agent plus runtime-injected extras like MCP tools and skill tools) are collected into a ToolRegistry. The registry is the single source of truth for tool lookup, schema export, and call dispatch. 3. **Expose tool schema to model** -- The registry converts every registered tool into the OpenAI function-calling format via registry.to_openai_function_tools(). This list is attached to the LLMRequest.tools field so the model knows what tools are available. 4. **Model emits tool call** -- The LLM response (LLMResponse) may contain one or more ToolCall objects. Each carries an id, a tool_name, and a JSON arguments dict. AFK processes these as a batch. 5. **Schema validation** -- Before execution, each tool call's raw arguments are validated against the tool's Pydantic args_model via BaseTool.validate(). If validation fails, a ToolResult(success=False) is returned immediately and the tool handler is never invoked. 6. **Policy gate** -- The runner evaluates the PolicyEngine for each tool call via a tool_before_execute policy event. The policy can allow, deny, defer (require human approval), or request_user_input. See the Tool Call Lifecycle page for the full decision matrix. 7. **Execute handler** -- If the policy allows execution, AFK runs the tool through the full hook/middleware chain: PreHooks (argument transforms) -> Middleware chain -> core handler -> PostHooks (output transforms). Timeout enforcement applies at every layer. 8. **Sanitize output** -- The raw ToolResult.output is passed through apply_tool_output_limits() which enforces max_output_chars and sandbox output policies. The runner then wraps the result in an untrusted-data content envelope via render_untrusted_tool_message() to prevent prompt injection from tool output. 9. **Emit events** -- A tool_completed event is emitted on the run handle, carrying the tool name, success flag, output, and any error. Telemetry counters and histograms are recorded for latency and success/failure counts. 10. **Tool result fed back to model** -- The sanitized output is appended to the conversation as a Message(role=\"tool\", name=tool_name, content=...). The loop returns to step 4 for the next LLM turn. Full example: tool definition through result inspection Inspecting tool executions Every completed run exposes a tool_executions list on AgentResult. Each entry is a ToolExecutionRecord: | Field | Type | Description | | -------------- | ------------------- | ------------------------------------ | | tool_name | str | Name of the executed tool. | | tool_call_id | str \\| None | Provider/LLM tool-call identifier. | | success | bool | Whether execution succeeded. | | output | JSONValue \\| None | JSON-safe tool output payload. | | error | str \\| None | Error message when execution failed. | | latency_ms | float \\| None | Execution latency in milliseconds. | Hook and middleware pipeline Tools support three extension points that wrap the core handler: | Extension | When it runs | Signature | Purpose | | -------------- | ------------------- | ------------------------------------- | ------------------------------------------------------------------------------------------- | | **PreHook** | Before core handler | (args) or (args, ctx) | Transform or validate arguments. Must return a dict compatible with the tool's args model. | | **Middleware** | Wraps core handler | (call_next, args, ctx) | Cross-cutting concerns (logging, timing, caching). Calls call_next(args, ctx) to proceed. | | **PostHook** | After core handler | ({\"output\": ..., \"tool_name\": ...}) | Transform or audit the output before it reaches the model. | The execution order is: validate args -> run PreHooks sequentially -> run Middleware chain (outermost first) -> core handler -> run PostHooks sequentially -> wrap in ToolResult. Error handling The tool system is designed to be non-throwing by default. BaseTool.call() catches all exceptions and wraps them in a ToolResult(success=False, error_message=...) unless raise_on_error=True is set on the tool. **Validation errors** -- When the model passes arguments that do not match the Pydantic schema, a ToolValidationError is caught and the error message is returned to the model so it can self-correct on the next turn. **Timeout errors** -- Each tool can set a default_timeout in seconds. If execution exceeds this limit, an asyncio.TimeoutError is caught and surfaced as a ToolTimeoutError in the result. **Execution errors** -- Any unhandled exception from the handler, PreHook, or PostHook is caught and wrapped in a ToolExecutionError result. **Hook/middleware failures** -- If a PreHook returns a non-dict value or produces args that fail re-validation against the main tool's model, execution stops and a failure result is returned. PostHook failures similarly short-circuit the chain. **Policy-driven behavior** -- After a tool failure, the runner consults the agent's fail_safe.tool_failure_policy to decide whether to continue (default), degrade the run, or fail the entire run. This is configurable per agent.", + "token_count": 582 + }, + { + "id": "doc_9e9118ed2141", + "path": "docs/library/tools.mdx", + "url": "/library/tools", + "title": "Tools", + "description": "Give agents typed capabilities through Python functions.", + "headings": [ + "Your first tool", + "How tool calling works", + "Tool patterns", + "Deferred background tool calls", + "Policy-gated tools", + "Hooks and middleware", + "Execution order", + "Common tools cookbook", + "Prebuilt tools", + "Runtime tools", + "Tools scoped to a specific directory", + "Returns: [read_file, list_directory, ...]", + "Skill tools", + "Next steps" + ], + "content_sha256": "517d6cb680faa87f107bc90d44a3e46e807dbc7146a40390f7385f83568e4743", + "content": "Tools let agents take actions \u2014 query databases, call APIs, run calculations, write files, or anything you can express in Python. AFK handles schema generation, argument validation, policy gates, execution, and output sanitization. Your first tool That's a complete tool. The @tool decorator generates the JSON schema from the Pydantic model, which the LLM uses to understand what arguments to pass. How tool calling works Based on the user's message and the tool schemas, the LLM emits a tool_call with the function name and arguments. AFK parses the arguments through the Pydantic model. Invalid arguments generate a validation error that's sent back to the LLM for self-correction. If a PolicyEngine is attached, the tool call is checked against policy rules (allow, deny, or request_approval). The tool function runs with validated arguments. Pre/post hooks and middleware execute around the handler. The output is truncated to tool_output_max_chars, stripped of potential prompt injection vectors (if sanitize_tool_output=True), and formatted for the LLM. The sanitized result is appended to the conversation and the LLM generates its next response. Tool patterns Return a string or dict directly. Return a dict for structured data. Use async def for I/O-heavy tools. Access ToolContext in tool handlers. The second parameter must be named ctx or annotated as ToolContext. Deferred background tool calls For long-running operations, a tool can return a deferred handle so the run can continue while work completes in the background. When deferred: 1. Runner emits tool_deferred. 2. Agent continues with other work in the same run. 3. Runner emits tool_background_resolved or tool_background_failed. 4. Resolved tool output is injected back into conversation for next steps. External workers can resolve tickets by writing: - bgtool:{run_id}:{ticket_id}:state - bgtool:{run_id}:latest Status payload example: This pattern is useful for coding agents that start a long build, continue writing docs, then consume build results once available. You can also use runner helpers instead of writing raw state keys: Policy-gated tools Use the PolicyEngine to gate sensitive tool calls: **Policy best practice:** Gate all mutating tools with request_approval or deny by default. Only allow read-only tools without gates. Hooks and middleware AFK provides four extension points for tool execution: **prehooks**, **posthooks**, **tool-level middleware**, and **registry-level middleware**. Each has its own decorator. Prehooks run before the tool handler. They receive the tool's arguments and **must return a dict** compatible with the tool's args_model. Posthooks run after the tool handler. They receive a dict {\"output\": , \"tool_name\": \" \"} and should return a dict with the same shape. Middleware wraps the entire tool execution. It receives call_next, the validated args, and optionally ctx. Registry-level middleware applies to **every tool** in a ToolRegistry. Use for audit logging, rate limiting, or global policy enforcement. Execution order | Layer | Scope | Decorator | Returns | | --------------- | --------------------------- | -------------------------------- | ------------------------------ | | **Prehook** | Single tool, before handler | @prehook(args_model=...) | dict of transformed args | | **Middleware** | Single tool, wraps handler | @middleware(name=...) | Tool output (via call_next) | | **Posthook** | Single tool, after handler | @posthook(args_model=...) | dict with output key | | **Registry MW** | All tools in registry | @registry_middleware(name=...) | ToolResult (via call_next) | Common tools cookbook Prebuilt tools AFK ships with ready-to-use tools for common agent capabilities. These are in the afk.tools.prebuilts module. Runtime tools Filesystem tools scoped to a directory for safe agent exploration: Runtime tools enforce directory-scoped access \u2014 the agent cannot read or list files outside the configured root_dir. Skill tools When an agent has skills configured, AFK generates four skill tools automatically: | Tool | Purpose | | ----------------- | ----------------------------------------------------- | | list_skills | Return metadata for all enabled skills | | read_skill_md | Read a skill's SKILL.md content and checksum | | read_skill_file | Read additional files under a skill directory | | run_skill_command| Execute allowlisted commands with timeout and limits | Skill tools are gated by a SkillToolPolicy that controls command allowlists, output limits, and shell operator restrictions: Next steps Watch tool calls happen in real time. Policy gates, sandbox profiles, and tool allowlists.", + "token_count": 481 + }, + { + "id": "doc_136d7a909d51", + "path": "docs/library/troubleshooting.mdx", + "url": "/library/troubleshooting", + "title": "Troubleshooting", + "description": "Common issues and solutions when building with AFK.", + "headings": [ + "Agent behavior issues", + "Agent keeps calling the same tool repeatedly", + "Add hard limits to prevent runaway loops", + "Agent ignores tools and doesn't call them", + "Agent produces inconsistent outputs", + "Use request-level sampling controls for direct LLM calls", + "Memory issues", + "Conversation doesn't persist between runs", + "Always use thread_id for multi-turn conversations", + "r2 will remember r1's context", + "Verify memory is configured", + "For production, use persistent storage", + "Resume doesn't work", + "Check run_id and thread_id are correct", + "Resume correctly", + "Check checkpoint state directly from the configured memory store", + "LLM issues", + "Rate limit errors", + "Or use exponential backoff for retries", + "Timeout errors", + "Set appropriate timeouts", + "Or per-request timeout via middleware", + "Model not found errors", + "Verify model name is correct", + "Use fallback for resilience", + "Streaming issues", + "Streaming doesn't work", + "Make sure you're iterating correctly", + "Don't mix sync and async", + "WRONG:", + "RIGHT:", + "Streaming disconnects early", + "Use timeout middleware for streaming", + "Cost issues", + "Unexpected high costs", + "ALWAYS set cost limits", + "Token limit errors", + "Compact memory to reduce context", + "Or use a model with larger context", + "Tool issues", + "Tool validation errors", + "Ensure Pydantic model matches tool implementation", + "Tool not found errors", + "Verify tool is attached to agent", + "Verify tool name matches", + "Call with exact name", + "Debug mode", + "Getting help", + "Next steps" + ], + "content_sha256": "b21eb3b6aac297783f21d0585242a6ba2ccdb8038df41fa08915007fc1521053", + "content": "This guide covers common issues encountered when building and deploying AFK agents, with solutions and debugging tips. Agent behavior issues Agent keeps calling the same tool repeatedly **Symptoms:** Agent enters a loop, calling the same tool multiple times without making progress. **Causes:** - Tool output doesn't provide the information the agent needs - Agent instructions don't clarify when to stop - Missing a tool that would help the agent determine completion **Solutions:** **Debug:** Enable verbose logging to see tool call inputs/outputs: --- Agent ignores tools and doesn't call them **Symptoms:** Agent responds with text but doesn't use available tools. **Causes:** - Instructions don't mention the tools or when to use them - Tool descriptions are unclear - Model being used doesn't support function calling well **Solutions:** --- Agent produces inconsistent outputs **Symptoms:** Same input produces different outputs on different runs. **Causes:** - Temperature is set too high - Missing structured output configuration - Non-deterministic system prompt **Solutions:** Memory issues Conversation doesn't persist between runs **Symptoms:** Agent doesn't remember previous messages. **Causes:** - Not using thread_id to link conversations - Memory store not configured correctly - Using in-memory store (loses state on restart) **Solution:** **Check memory backend:** --- Resume doesn't work **Symptoms:** Calling runner.resume() doesn't continue from where the run stopped. **Solutions:** **Debug checkpoints:** LLM issues Rate limit errors **Symptoms:** RateLimitError or 429 responses from LLM provider. **Solutions:** --- Timeout errors **Symptoms:** Requests hang or timeout before completing. **Solutions:** --- Model not found errors **Symptoms:** ModelNotFoundError or InvalidRequestError. **Solutions:** Streaming issues Streaming doesn't work **Symptoms:** run_stream() doesn't return events or returns them all at once. **Solutions:** --- Streaming disconnects early **Symptoms:** Stream ends before completion. **Solutions:** Cost issues Unexpected high costs **Symptoms:** API costs much higher than expected. **Causes:** - Agent in a loop making many LLM calls - No cost limits configured - Expensive model being used unnecessarily **Solutions:** --- Token limit errors **Symptoms:** ContextLengthExceeded or similar errors. **Solutions:** Tool issues Tool validation errors **Symptoms:** ToolValidationError when tools are called. **Solutions:** --- Tool not found errors **Symptoms:** Agent can't find or call a tool. **Solutions:** Debug mode Enable debug mode for detailed logging: Getting help If you can't resolve an issue: 1. Check the GitHub Issues for known issues 2. Enable debug logging and capture the full traceback 3. Include these details when reporting: - AFK version (pip show afk) - Python version - LLM provider and model - Minimal reproduction code - Full error traceback Next steps Understand how AFK components work together. Test agent behavior before shipping. Common patterns and anti-patterns. Detailed API documentation.", + "token_count": 347 + }, + { + "id": "doc_f1e32ed2ffce", + "path": "docs/llms/adapters.mdx", + "url": "/llms/adapters", + "title": "Adapters", + "description": "Built-in LLM providers and custom adapter registration.", + "headings": [ + "Built-in providers", + "Capability comparison", + "Usage", + "OpenAI", + "Anthropic", + "LiteLLM (any provider)", + "Custom provider", + "Custom transport", + "Next steps" + ], + "content_sha256": "4062f20b10ef8ec55493c8418a3fe20da61715e29f3f285979f8f696d0a1872c", + "content": "Providers translate between AFK's normalized contracts (LLMRequest/LLMResponse) and provider-specific APIs. AFK ships with three built-in providers and supports custom providers for internal deployments. Built-in providers Direct integration via the OpenAI Python SDK. Supports all GPT-4.1 and o-series models. Direct integration via the Anthropic SDK. Supports Claude Opus 4.5 and Opus. Proxy adapter for 100+ providers (Azure, Bedrock, Gemini, Mistral, local models, etc.). Capability comparison | Feature | OpenAI | Anthropic | LiteLLM | | -------------------- | ------------------------------------------------------ | ------------------------------------------------------ | --------------------------------------------------------------------------- | | Text generation | | | | | Tool calling | | | (provider-dependent) | | Structured output | | | Provider-dependent | | Streaming | | | | | Vision (image input) | | | Provider-dependent | | Custom endpoints | | | | Usage Custom provider Register your own provider for unsupported inference servers: Custom transport Use provider-specific settings for custom endpoints, proxy URLs, or credentials: Next steps Retry, caching, rate limiting, and circuit breaking. How agents resolve and use LLM clients.", + "token_count": 106 + }, + { + "id": "doc_eeed52120ccf", + "path": "docs/llms/agent-integration.mdx", + "url": "/llms/agent-integration", + "title": "Agent Integration", + "description": "How agents resolve models and interact with the LLM layer.", + "headings": [ + "Model resolution", + "How the runner uses the LLM", + "Request construction", + "Streaming integration", + "Error handling", + "Model selection guide", + "Next steps" + ], + "content_sha256": "8f69ff34bc2ca6056745652bec1d0ebcb8e395d7930aaa87e81518f5e05b8619", + "content": "This page explains how agents connect to the LLM layer \u2014 from model resolution to request construction, streaming, and error handling. Model resolution Agents can specify their model in two ways: Pass a model name string. AFK resolves it to an LLM client using the default provider. The resolution order: 1. Check agent.model_resolver (custom function) 2. Check registered adapters for matching provider prefix 3. Default to OpenAI adapter Pass a pre-configured LLMClient for full control over provider settings. How the runner uses the LLM On each step of the agent loop: Request construction The runner builds an LLMRequest from multiple sources: | Source | Contributes | Priority | | -------------------- | ----------------------- | -------- | | Agent.instructions | System message | Highest | | Thread history | Previous messages | \u2014 | | user_message | User message | \u2014 | | Agent.tools | Tool schemas | \u2014 | | Agent.subagents | Transfer tool schemas | \u2014 | | RunnerConfig | Temperature, max_tokens | Lowest | Streaming integration When using run_stream(), the runner passes through streaming events from the LLM: text_delta events come from two paths: - Provider streaming when the adapter supports stream deltas. - Runner fallback chunking of final text for non-streaming providers. Tool and step events are generated by the runner. Error handling LLM errors are classified and handled automatically: | Error | Classification | Runner behavior | | ---------------------------- | -------------- | -------------------------- | | Rate limit (429) | Retryable | Retry with backoff | | Server error (500, 502, 503) | Retryable | Retry with backoff | | Auth error (401, 403) | Terminal | Fail the run | | Invalid request (400) | Terminal | Fail the run | | Timeout | Retryable | Retry (if attempts remain) | | Circuit breaker open | Terminal | Try fallback model or fail | | All retries exhausted | Terminal | Try fallback model or fail | Model selection guide Treat these as example starting points. Confirm current model names, prices, and rate limits with the provider you use. | Task | Starting model | Why | | -------------------------- | ------------------------------ | ----------------------------------- | | Simple Q&A, classification | gpt-4.1-nano | Fast, cheap, good enough | | General purpose with tools | gpt-4.1-mini | Best balance of cost and capability | | Complex reasoning, coding | gpt-4.1 or claude-opus-4-5 | Better at multi-step reasoning | | Cost-sensitive batch | gpt-4.1-nano | Lowest cost per token | | Higher quality generation | gpt-4.1 + low temperature | More capability, less variation | Next steps The step loop and execution engine. Real-time event streaming API.", + "token_count": 261 + }, + { + "id": "doc_e8794369a1a4", + "path": "docs/llms/contracts.mdx", + "url": "/llms/contracts", + "title": "Contracts", + "description": "LLMRequest and LLMResponse \u2014 the data that flows across the LLM boundary.", + "headings": [ + "The flow", + "LLMRequest", + "LLMResponse", + "Structured output", + "Message types", + "Next steps" + ], + "content_sha256": "0ef6386027791d9122a28e938ca2f5752a67f5289f01989c62554bea9d29d5ac", + "content": "Every LLM interaction in AFK flows through two typed contracts: LLMRequest (what you send) and LLMResponse (what you get back). These contracts normalize the differences between providers so your agent code never touches provider-specific types. The flow The adapter translates between AFK's normalized contracts and the provider's native format. LLMRequest | Field | Type | Purpose | | --- | --- | --- | | messages | list[Message] | Conversation history (system, user, assistant, tool) | | model | str | Model identifier | | tools | list[ToolSchema] | Available tool schemas | | temperature | float | Sampling temperature (0.0\u20132.0) | | max_tokens | int \\| None | Max output tokens | | top_p | float \\| None | Nucleus sampling | | response_format | ResponseFormat \\| None | Structured output format | | stop | list[str] \\| None | Stop sequences | LLMResponse | Field | Type | Purpose | | --- | --- | --- | | content | str \\| None | Text response from the model | | tool_calls | list[ToolCall] | Tool calls requested by the model | | model | str | Model that generated the response | | usage | Usage | Token counts (prompt, completion, total) | | finish_reason | str | Why generation stopped (stop, tool_calls, length) | | latency_ms | float | End-to-end request latency | Structured output Request structured JSON output with a Pydantic model: Message types | Role | Purpose | Source | | ----------- | ------------------ | ----------------------------- | | system | Agent instructions | From Agent.instructions | | user | User input | From user_message parameter | | assistant | Model responses | Generated by the LLM | | tool | Tool results | From tool execution | Next steps Built-in providers and custom adapters. Retry, caching, rate limiting, and circuit breaking.", + "token_count": 175 + }, + { + "id": "doc_7320ab982fab", + "path": "docs/llms/control-and-session.mdx", + "url": "/llms/control-and-session", + "title": "Control & Session", + "description": "Retry, caching, rate limiting, and circuit breaking for LLM calls.", + "headings": [ + "Policy pipeline", + "Built-in profiles", + "Development: no retry, no caching, fast failures", + "Production: retry, circuit breaker, rate limiting, caching", + "Individual policies", + "Fallback chains", + "If gpt-4.1 fails \u2192 try gpt-4.1-mini \u2192 try gpt-4.1-nano", + "Tuning cheat sheet", + "Next steps" + ], + "content_sha256": "10d156ced976efb12c419c33d65e5686c7429902256c9e33be26a1add88c39a7", + "content": "The LLM runtime includes built-in policies for handling transient failures, managing costs, and protecting against provider outages. Configure them via the builder profile or individual policy settings. Policy pipeline Every LLM request passes through this policy chain: Built-in profiles Use profile() to apply a curated set of policies: | Profile | Retry | Cache | Rate Limit | Circuit Breaker | Timeout | | ------------- | ------------------------------- | ------------------- | ---------- | ---------------------- | ------- | | development | None | None | None | None | 30s | | production | 3 attempts, exponential backoff | In-memory, 5min TTL | 60 req/min | 5 failures \u2192 30s open | 60s | | batch | 5 attempts, longer backoff | None | 20 req/min | 10 failures \u2192 60s open | 120s | Individual policies Configure each policy independently with create_llm_client(): Retry transient LLM failures with exponential backoff. | Parameter | Default | Description | | --- | --- | --- | | max_retries | 3 | Retry attempts after the initial request | | backoff_base_s | 0.5 | Initial delay in seconds | | backoff_jitter_s | 0.15 | Jitter added to retry delays | | require_idempotency_key | True | Require idempotency keys for retried requests | Stop calling a failing provider to prevent cascading failures. Prevent exceeding provider rate limits. Cache identical requests to reduce cost and latency. Cache keys are derived from request content, model settings, response model, session/checkpoint tokens, and any configured cache namespace. Cached rows do not retain provider request IDs, session tokens, checkpoint tokens, or raw provider payloads. Hard timeout on LLM requests. Fallback chains Configure a chain of models to try when the primary model fails: Tuning cheat sheet | Goal | Setting | | ----------------- | ------------------------------------------------------------- | | Reduce costs | Lower temperature, use cheaper model, enable caching | | Reduce latency | Enable caching, use faster model, set tight timeout | | Handle outages | Enable retry + circuit breaker, add fallback chain | | High throughput | Set rate limits high, use batch profile, increase concurrency | | Consistent output | Set temperature=0.0, enable structured output | Next steps How agents resolve and use LLM clients. Monitor LLM call latency, errors, and costs.", + "token_count": 238 + }, + { + "id": "doc_f92685a34f7e", + "path": "docs/llms/index.mdx", + "url": "/llms/", + "title": "LLM Layer", + "description": "Provider-portable LLM runtime with retry, caching, circuit breaking, and middleware.", + "headings": [ + "The LLMBuilder", + "Middleware", + "Built-in middleware", + "Custom middleware", + "Middleware protocols", + "Supported providers", + "Example model choices", + "How agents use the LLM layer", + "Option 1: Model name (auto-resolved)", + "Option 2: Pre-built client (full control)", + "Next steps" + ], + "content_sha256": "32c7ac94efe23450adc9126031e9f87e3e689349369a82f600c67fbdd16f86a1", + "content": "The LLM layer normalizes communication with language models across all supported providers. Your agent code uses provider-agnostic contracts (LLMRequest / LLMResponse) while built-in adapters handle the provider-specific details. The LLMBuilder Create LLM clients with the builder pattern: Middleware The LLM layer supports middleware for intercepting and transforming requests and responses. Use middleware for logging, tracing, caching, and custom request/response handling. Built-in middleware Custom middleware Middleware protocols | Protocol | Operation | Signature | | --- | --- | --- | | LLMChatMiddleware | Non-streaming chat | async (call_next, req: LLMRequest) -> LLMResponse | | LLMEmbedMiddleware | Embeddings | async (call_next, req: EmbeddingRequest) -> EmbeddingResponse | | LLMStreamMiddleware | Streaming chat | (call_next, req: LLMRequest) -> AsyncIterator[LLMStreamEvent] | Supported providers GPT-4.1, GPT-4.1-mini, GPT-4.1-nano, o-series Claude Opus and Sonnet families 100+ providers via the LiteLLM proxy All providers expose the same LLMClient interface. Your agent code never touches provider-specific types. Example model choices Use these as starting points, then verify model availability, pricing, and context limits with your provider account. | Scenario | Starting point | | -------------------------- | ----------------------------------------------- | | General purpose | OpenAI gpt-4.1-mini | | Complex reasoning | OpenAI gpt-4.1 or Anthropic claude-opus-4-5 | | Cost-sensitive | OpenAI gpt-4.1-nano | | Non-OpenAI/Anthropic model | LiteLLM adapter | | Custom or self-hosted | Custom adapter | How agents use the LLM layer You rarely build LLMClient directly. Agents resolve their model automatically: Next steps LLMRequest / LLMResponse \u2014 what flows across the boundary. Built-in providers and custom adapter registration.", + "token_count": 176 + } + ] +} diff --git a/skills/afk-coder/references/afk-docs/docs-index.jsonl b/skills/afk-coder/references/afk-docs/docs-index.jsonl new file mode 100644 index 0000000..70a2e81 --- /dev/null +++ b/skills/afk-coder/references/afk-docs/docs-index.jsonl @@ -0,0 +1,63 @@ +{"id": "doc_3aa0b3792d22", "path": "docs/docs.json", "url": "/docs.json", "title": "docs", "description": "", "headings": [], "content_sha256": "9808c9300f5fc80e270e826e601d4e2ec57dbe59c97606f751af389bc1cd54d5", "content": "{ \"$schema\": \"https://mintlify.com/schema.json\", \"name\": \"AFK\", \"logo\": { \"light\": \"/logo-light.svg\", \"dark\": \"/logo-dark.svg\", \"href\": \"https://github.com/arpan404/afk\" }, \"theme\": \"mint\", \"layout\": \"sidenav\", \"favicon\": \"/favicon.svg\", \"topbar\": { \"style\": \"gradient\" }, \"topbarCtaButton\": { \"type\": \"github\", \"url\": \"https://github.com/arpan404/afk\" }, \"topbarLinks\": [ { \"type\": \"link\", \"name\": \"Quickstart\", \"url\": \"/library/quickstart\", \"arrow\": false }, { \"type\": \"link\", \"name\": \"Examples\", \"url\": \"/library/examples/\", \"arrow\": false }, { \"type\": \"link\", \"name\": \"API\", \"url\": \"/library/api-reference\", \"arrow\": false }, { \"type\": \"link\", \"name\": \"Skills\", \"url\": \"/library/agent-skills\", \"arrow\": false } ], \"sidebar\": { \"items\": \"undecorated\" }, \"rounded\": \"default\", \"colors\": { \"primary\": \"#0B6E4F\", \"light\": \"#2FB67E\", \"dark\": \"#084C38\" }, \"footer\": { \"socials\": { \"github\": \"https://github.com/arpan404/afk\" } }, \"navigation\": { \"tabs\": [ { \"tab\": \"Build with AFK\", \"groups\": [ { \"group\": \"Start Here\", \"pages\": [ \"index\", \"library/overview\", \"library/mental-model\", \"library/quickstart\", \"library/learn-in-15-minutes\", \"library/how-to-use-afk\" ] }, { \"group\": \"Core Building Blocks\", \"pages\": [ \"library/agents\", \"library/core-runner\", \"library/tools\", \"library/streaming\", \"library/memory\", \"library/system-prompts\", \"library/agent-skills\" ] }, { \"group\": \"LLM Runtime\", \"pages\": [ \"llms/index\", \"llms/contracts\", \"llms/adapters\", \"llms/control-and-session\", \"llms/agent-integration\" ] }, { \"group\": \"Production\", \"pages\": [ \"library/building-with-ai\", \"library/evals\", \"library/observability\", \"library/security-model\", \"library/task-queues\", \"library/deployment\", \"library/performance\", \"library/troubleshooting\" ] }, { \"group\": \"Integrations\", \"pages\": [ \"library/mcp-server\", \"library/a2a\", \"library/messaging\" ] } ] }, { \"tab\": \"Maintain AFK\", \"groups\": [ { \"group\": \"Contributor Guide\", \"pages\": [ \"library/developer-guide\", \"library/architecture\", \"library/public-imports-and-function-improvement\", \"library/tested-behaviors\" ] }, { \"group\": \"Internal Contracts\", \"pages\": [ \"library/agentic-behavior\", \"library/agentic-levels\", \"library/failure-policy-matrix\", \"library/tool-call-lifecycle\", \"library/tools-system-walkthrough\", \"library/run-event-contract\", \"library/checkpoint-schema\", \"library/llm-interaction\", \"library/debugger\" ] }, { \"group\": \"Migration\", \"pages\": [\"library/migration\"] } ] }, { \"tab\": \"Reference\", \"groups\": [ { \"group\": \"API and Configuration\", \"pages\": [ \"library/api-reference\", \"library/configuration-reference\", \"library/environment-variables\", \"library/full-module-reference\" ] } ] }, { \"tab\": \"Examples\", \"groups\": [ { \"group\": \"Runnable Snippets\", \"pages\": [ \"library/examples/index\", \"library/snippets/01_minimal_chat_agent\", \"library/snippets/02_policy_with_hitl\", \"library/snippets/03_subagents_with_router\", \"library/snippets/04_resume_and_compact\", \"library/snippets/05_direct_llm_structured_output\", \"library/snippets/06_tool_registry_security\", \"library/snippets/07_tool_hooks_and_middleware\", \"library/snippets/08_prebuilt_runtime_tools\", \"library/snippets/09_system_prompt_loader\", \"library/snippets/10_streaming_chat_with_memory\", \"library/snippets/11_cost_monitoring\", \"library/snippets/12_mcp_client_integration\", \"library/snippets/13_multi_model_fallback\", \"library/snippets/14_production_client\" ] } ] } ] } }", "token_count": 289} +{"id": "doc_deb460ffa5ee", "path": "docs/index.mdx", "url": "/", "title": "AFK - Agent Forge Kit", "description": "Build reliable Python agents with typed tools, runtime controls, and production observability.", "headings": ["Choose your path", "Install", "First agent", "Core capabilities", "AFK skills", "How AFK fits together", "Next steps"], "content_sha256": "e7a820d1c12c3306b696c759c97965bfc83dd0ae9847e39832b1caa2dd31a7b9", "content": "AFK is a Python 3.13+ SDK for building AI agents that need to run reliably outside a demo. You define agents, tools, policies, and memory as typed Python contracts. The runner handles the agent loop, LLM calls, tool execution, streaming, checkpoints, telemetry, and safety limits. Choose your path Start here if you are building an application, workflow, chatbot, coding tool, or production agent on top of AFK. Start here if you are changing AFK itself, reviewing internals, or updating public contracts. Install The distribution package is afk-py; the import package is afk. Set an LLM provider key before running examples: First agent What matters: - Agent is configuration: model, instructions, tools, policies, subagents, and defaults. - Runner is execution: LLM calls, tool calls, memory, streaming, checkpoints, and telemetry. - AgentResult is the run record: final text, terminal state, tool/subagent records, usage, and cost. Core capabilities Declarative agent definitions with instructions, tools, subagents, skills, MCP servers, and safety limits. Sync, async, and streaming execution with lifecycle control and thread continuity. Typed Python tools with Pydantic validation, hooks, middleware, sandboxing, and bounded output. Provider-portable LLM clients with retries, timeouts, rate limits, caching, circuit breakers, and fallback chains. Thread state, checkpoints, retention, compaction, and persistent stores. Evals, observability, queues, security controls, deployment guidance, and troubleshooting. AFK skills Install the AFK skills with Vercel's Skills CLI when you want Codex or another supported agent to use AFK-specific guidance: Use afk-coder when building applications with AFK. Use afk-maintainer when reviewing or changing AFK itself. See Agent Skills for details. How AFK fits together Next steps Build one runnable agent with one typed tool. Walk through agents, tools, streaming, memory, and safety. Find complete snippets by scenario. Check canonical imports, signatures, result fields, and stability rules.", "token_count": 228} +{"id": "doc_1c5f37ea0ad3", "path": "docs/library/a2a.mdx", "url": "/library/a2a", "title": "Agent-to-Agent (A2A)", "description": "Cross-system agent communication with authentication and delivery guarantees.", "headings": ["Architecture", "Three integration layers", "Request flow", "Invocation contracts", "Hosting an A2A service", "Define the agent", "Create auth provider", "Start the server", "Authentication providers", "Google A2A adapter", "Security considerations", "Next steps"], "content_sha256": "654fb4bacfee24f10a177606048474a5012fa7cb25ee7df203b1869ede8447cd", "content": "The A2A protocol enables agent communication **across system boundaries** \u2014 between services, organizations, or deployment environments. It builds on internal messaging by adding authentication, authorization, and external transport. Architecture Three integration layers | Layer | What it does | When you need it | | --------------------- | --------------------------------- | ------------------------------------- | | **Internal Protocol** | Typed envelopes with idempotency | Always (agents in the same system) | | **Auth Provider** | Token validation, caller identity | When agents are in different services | | **External Adapter** | HTTP/gRPC transport, discovery | When agents are in different systems | Request flow The server validates the auth token, extracts the caller identity, and checks authorization rules. The target agent executes locally with a Runner, using the invocation request as input. Invocation contracts Hosting an A2A service Expose your agents as an A2A-accessible service: Authentication providers AFK ships with three auth providers: Permits all requests without authentication. **Never use in production.** Validates requests against pre-shared API keys with HMAC-SHA256 hashing. Validates JWT tokens with configurable issuer and audience claims. Google A2A adapter For interoperability with Google's A2A protocol, use the Google adapter: The adapter wraps a configured Google A2A SDK client behind AFK's AgentCommunicationProtocol. Security considerations | Concern | Mechanism | | -------------------- | ---------------------------------------------------------------- | | **Authentication** | Token-based (API keys, JWT, OAuth) | | **Authorization** | Per-agent access control (which callers can invoke which agents) | | **Idempotency** | idempotency_key prevents duplicate processing on retries | | **Rate limiting** | Configure per-caller request limits | | **Input validation** | All requests validated against AgentInvocationRequest schema | | **Cost isolation** | Each invocation has its own FailSafeConfig budget | **Always authenticate A2A endpoints.** An unauthenticated A2A server allows anyone to invoke your agents, consuming your LLM API credits. Next steps Async job processing for long-running work. Expose tools via the Model Context Protocol.", "token_count": 216} +{"id": "doc_b6088784caab", "path": "docs/library/agent-skills.mdx", "url": "/library/agent-skills", "title": "Agent Skills", "description": "Reusable knowledge bundles that agents load on demand.", "headings": ["When to use skills vs tools", "Creating a skill", "SKILL.md format", "Deployment Guide", "Prerequisites", "Deploy Steps", "Rollback", "Monitoring", "Installing repository skills", "Registering skills on an agent", "Built-in skill tools", "How the agent uses skills", "Security controls", "Design guidelines", "Next steps"], "content_sha256": "69777dc325761bdda7d06e8dee698faab76ddd3ee55f7301e7c9143018f6aa8f", "content": "**Skills** are reusable knowledge bundles \u2014 directories of instructions, scripts, and resources that extend an agent's capabilities without being hard-coded into the system prompt. Agents discover and read skills at runtime using built-in skill tools. When to use skills vs tools | Feature | Tools | Skills | | ---------------- | --------------------------------------------------- | ---------------------------------------- | | **What it is** | A Python function the agent calls | A directory of knowledge the agent reads | | **When it runs** | During the agent loop (function execution) | During the agent loop (file reads) | | **Best for** | Taking actions (API calls, calculations, mutations) | How-to guides, playbooks, reference docs | | **Schema** | Typed Pydantic model | Unstructured markdown + files | | **Side effects** | Yes (executes code) | Read-only (by default) | **Rule of thumb:** If the agent needs to *do something*, use a tool. If it needs to *know something*, use a skill. Creating a skill A skill is a directory with a SKILL.md file: SKILL.md format The YAML frontmatter (name, description) helps the agent decide which skill is relevant. The markdown body is the knowledge content. Installing repository skills This repository includes two skills for agentic development: - afk-coder for building applications with AFK. - afk-maintainer for reviewing or changing AFK itself. Install them with Vercel's Skills CLI: Install one skill at a time: For another repository, use the same shape: The Skills CLI installs the selected skill into the configured agent environment. See skills.sh for CLI details and supported agents. Registering skills on an agent AFK scans the skills_dir and registers each subdirectory as an available skill. The agent can discover and read skills using built-in tools. Built-in skill tools When an agent has skills, AFK automatically provides these tools: | Tool | Purpose | Returns | | ------------------- | ---------------------------------------- | ----------------------------------------- | | list_skills | List all available skills | [{\"name\": \"...\", \"description\": \"...\"}] | | read_skill_md | Read a skill's SKILL.md | Markdown content | | read_skill_file | Read any file in a skill directory | File content | | run_skill_command | Execute a command from a skill's scripts | Command output | How the agent uses skills The agent autonomously decides which skills to read based on the user's question. You don't need to explicitly tell it which skill to use. Security controls By default, read_skill_file only allows reading files within the skill directory. Path traversal (../) is blocked. run_skill_command is gated by policy. You can allowlist specific commands: To disable command execution entirely, only register the read-only skill tools: Design guidelines - **One skill per topic.** Keep skills focused (e.g., deployment, monitoring, database) rather than creating one giant knowledge base. - **Write SKILL.md for the agent, not a human.** The agent reads this file, so be explicit about what to do in each scenario. - **Include examples.** Show the agent what good output looks like. - **Version your skills.** Store skills in git alongside your agent code. - **Gate commands.** Always use policy rules for run_skill_command. Next steps Define typed functions for taking actions. Policy gates for tools and skill commands.", "token_count": 347} +{"id": "doc_f12bbbc027a7", "path": "docs/library/agentic-behavior.mdx", "url": "/library/agentic-behavior", "title": "Agentic Behavior", "description": "Orchestrate multi-agent DAGs with fan-out, join policies, and backpressure.", "headings": ["Quick example", "Specialist agents", "Coordinator", "Delegation DAG model", "Orchestration pipeline", "Join policies", "Failure handling", "Backpressure", "When to use multi-agent delegation", "Next steps"], "content_sha256": "01c6fa33586359217ba0e6ce21454895f25b3d20c57a0b496abe2ca8423d3e25", "content": "When a single agent isn't enough, AFK lets you orchestrate teams of specialist agents using **delegation DAGs** \u2014 directed acyclic graphs where a coordinator fans out work, collects results, and combines them. Quick example Delegation DAG model The coordinator makes all delegation decisions. Subagents don't talk to each other directly \u2014 they report back to the coordinator, which decides what to do next. Orchestration pipeline The coordinator decides which subagents to call and in what order, based on the user's request and its instructions. AFK validates the delegation request: does the subagent exist? Are the arguments valid? Does the policy allow it? The subagent is enqueued for execution. With fan-out, multiple subagents can run in parallel. Each subagent runs a full agent loop (LLM calls, tool execution, etc.) and returns an AgentResult. Results are collected according to the join policy. The coordinator receives them and decides whether to delegate more or produce a final response. Join policies When multiple subagents run in parallel (fan-out), the **join policy** controls how the coordinator handles results: All subagents must succeed. Any failure fails the entire delegation batch. **Use when:** Every subagent's output is essential for the final result. Failures are tolerated. The coordinator proceeds with whatever results are available. **Use when:** Some subagents provide nice-to-have additions (e.g., fact-checking, sentiment analysis). Return as soon as one subagent succeeds. Cancel the rest. **Use when:** Multiple agents attempt the same task with different strategies. First correct answer wins. Succeed when N out of M subagents complete successfully. **Use when:** You need consensus from a majority of agents. Failure handling | Failure | all_required | allow_optional_failures | first_success | quorum | | ------------------ | ------------------------------------------------------------------ | ------------------------------------------------------------------------------------------- | ------------------------------------------------------------------ | ------------------------------------------------------------------ | | One subagent fails | Batch fails | Continue with others | Continue waiting | Continue if quorum not needed | | All subagents fail | Batch fails | Batch fails | Batch fails | Batch fails | | Timeout | Batch fails | Use available results | Batch fails | Depends on completed count | Backpressure AFK limits concurrent subagent executions to prevent resource exhaustion: When the concurrency limit is reached, additional subagent calls are queued and execute as slots become available. When to use multi-agent delegation | Scenario | Single agent | Multi-agent | | --------------------------------- | ------------------------------------------------------------------------ | -------------------------------------------------------------------------------------- | | Simple Q&A or classification | | Overkill | | Task needs different expertise | Consider | | | Need to parallelize work | N/A | | | Task needs consensus/verification | N/A | | | Tight latency budget | (fewer LLM calls) | (more LLM calls) | Next steps Capability maturity model \u2014 when to add agents. How orchestration and execution are separated.", "token_count": 303} +{"id": "doc_57e2139f0873", "path": "docs/library/agentic-levels.mdx", "url": "/library/agentic-levels", "title": "Agentic Levels", "description": "A capability maturity model \u2014 know when to add complexity.", "headings": ["The five levels", "Capability comparison", "Decision guide", "Signals to level up", "Next steps"], "content_sha256": "05994fd901c3045dec62b4bac7a8f98bcb6c12d58749ed51680689589ed90bba", "content": "AFK applications progress through five levels of capability. Each level adds features and complexity. Start at Level 1 and move up only when you have clear signals that the current level isn't enough. The five levels One agent, one model, no tools. The simplest possible setup. **AFK features used:** Agent, Runner, run_sync **Good for:** Text classification, summarization, translation, simple Q&A **Move up when:** The agent needs to take actions or access external data Add typed tool functions for the agent to call. **AFK features added:** @tool, Pydantic models, FailSafeConfig **Good for:** RAG, data lookup, calculations, API integrations **Move up when:** Different parts of the task need different expertise or models Coordinator delegates to specialist subagents. **AFK features added:** subagents, join policies, backpressure **Good for:** Complex tasks, parallel work, specialist expertise, consensus **Move up when:** Tasks take minutes, need async processing, or need queue-based reliability Decouple producers and consumers with task queues. Long-running jobs execute asynchronously. **AFK features added:** TaskQueue, TaskItem, workers, dead-letter handling **Good for:** Batch processing, background jobs, retryable pipelines, high-throughput **Move up when:** You need cross-system communication or external agent interop Agents communicate across systems using the A2A protocol with authenticated endpoints. **AFK features added:** InternalA2AProtocol, A2AServiceHost, auth providers, external adapters **Good for:** Microservice agent meshes, third-party integrations, federated AI systems **This is the ceiling** \u2014 most applications don't need Level 5. Capability comparison | Capability | L1 | L2 | L3 | L4 | L5 | | -------------------------- | ------------------------------------------------------ | ------------------------------------------------------ | ------------------------------------------------------ | ------------------------------------------------------ | ------------------------------------------------------ | | Text generation | | | | | | | Tool calling | \u2014 | | | | | | Multi-agent delegation | \u2014 | \u2014 | | | | | Async processing | \u2014 | \u2014 | \u2014 | | | | Cross-system communication | \u2014 | \u2014 | \u2014 | \u2014 | | | Policy engine | | | | | | | Observability | | | | | | | Evals | | | | | | = essential at this level    = recommended    \u2014 = not applicable Decision guide > [!TIP] > **Start at Level 1.** The simplest system that works is the best system. Premature complexity is the most common mistake in agent design. Signals to level up | Signal | Current Level | Move to | | ----------------------------------------------- | ------------- | -------------------------- | | Agent needs external data | 1 | 2 \u2014 Add tools | | One prompt can't cover all expertise | 2 | 3 \u2014 Split into specialists | | Users waiting too long for results | 3 | 4 \u2014 Queue for async | | Tasks fail and need to be retried automatically | 3 | 4 \u2014 Queue with DLQ | | Other systems need to invoke your agents | 4 | 5 \u2014 Expose A2A | | Third-party agents need to use your tools | 4 | 5 \u2014 Use MCP server | Next steps DAG orchestration, join policies, and fan-out. How AFK separates orchestration from execution.", "token_count": 296} +{"id": "doc_e67e762fb6ab", "path": "docs/library/agents.mdx", "url": "/library/agents", "title": "Agents", "description": "Define agents with instructions, tools, and subagents.", "headings": ["Your first agent", "Agent fields reference", "Single agent vs multi-agent", "How subagent delegation works", "Adding safety limits", "Policy-aware agents", "Design guidelines", "Next steps"], "content_sha256": "41b56ac8a4f6f74fd32265ec3b5c0ad682960f13a814fb9faf3517c23c943143", "content": "An **Agent** is a configuration object that describes _what_ your AI agent is \u2014 its identity, capabilities, and boundaries. Agents don't execute themselves; they're run by a Runner. Your first agent Those are the fields most examples should set. model is the only required constructor argument, but name and instructions make traces and behavior easier to understand. Agent fields reference | Field | Type | Default | Purpose | | ------------------ | -------------------- | -------- | --------------------------------------------------------------- | | model | str or LLM | required | LLM model name or pre-built client instance | | name | str | None | Agent identity for logs, telemetry, and subagent routing | | instructions | str | None | System prompt \u2014 what the agent knows and how it behaves | | instruction_file | str or Path | None | Path to a .txt or .md file containing the system prompt | | prompts_dir | str or Path | None | Directory containing prompt files resolved by the prompt store | | tools | list[tool] | None | Typed functions the agent can call | | subagents | list[Agent] | None | Specialist agents this agent can delegate to | | skills | list[str] | None | Skill names to resolve from skills_dir | | skills_dir | str or Path | \".agents/skills\" | Directory containing skill packs | | mcp_servers | list[MCPServerLike]| None | MCP server configs for external tool discovery | | fail_safe | FailSafeConfig | defaults | Step limits, cost budgets, timeout, and failure policies | | context_defaults | dict | None | Default JSON context merged into each run before caller context | | inherit_context_keys | list[str] | None | Context keys inherited from parent agent in delegation | | model_resolver | callable | None | Custom function to resolve model names to LLM clients | | instruction_roles | list[InstructionRole] | None | Structured instruction sections with role-based ordering | | policy_roles | list[PolicyRole] | None | Role-based policy rules applied during execution | | policy_engine | PolicyEngine | None | Policy engine for tool/action gating | | subagent_router | SubagentRouter | None | Custom routing logic for subagent delegation | | max_steps | int | 20 | Maximum agent loop iterations | | tool_parallelism | int | None | Max concurrent tool executions per step | | subagent_parallelism_mode | str | \"configurable\" | How subagent concurrency is managed | | reasoning_enabled| bool | None | Enable provider-supported reasoning controls | | reasoning_effort | str | None | Thinking effort level (e.g. \"low\", \"medium\", \"high\") | | reasoning_max_tokens | int | None | Token budget for extended thinking | | skill_tool_policy| SkillToolPolicy | None | Security policy for skill-provided tool execution | | enable_skill_tools | bool | True | Whether to expose skill tools to the agent | | enable_mcp_tools | bool | True | Whether to expose MCP-discovered tools to the agent | | runner | Runner | None | Pre-bound runner instance (advanced usage) | Single agent vs multi-agent A single agent handles everything. Best for focused tasks. **Use when:** The task is well-defined and doesn't need specialized sub-expertise. A coordinator delegates to specialist subagents. **Use when:** Different parts of the task need different expertise, models, or tools. How subagent delegation works When an agent has subagents, AFK automatically generates transfer tools (transfer_to_researcher, transfer_to_writer). The coordinator calls these like any other tool. Each subagent runs a **full agent loop** with its own model, instructions, and tools. The coordinator sees only the subagent's final_text. Adding safety limits Every agent should have a FailSafeConfig in production: **Always set max_total_cost_usd** in production. A runaway agent loop can spend significant API credits in minutes. Policy-aware agents Attach a PolicyEngine to control what the agent can do: Policy decisions: allow (default), deny, request_approval (human-in-the-loop), or request_user_input. Design guidelines - **Start with one agent.** Only add subagents when you have clear evidence that the task needs specialized expertise. - **Keep instructions focused.** Vague instructions produce vague results. Tell the agent exactly what to do and what not to do. - **Use typed tools.** Every tool argument should be a Pydantic model. Untyped arguments bypass validation. - **Set cost limits early.** Add FailSafeConfig before your first deployment, not after your first runaway bill. Next steps How agents are executed \u2014 lifecycle, API modes, and state management. Define typed tool functions with validation and policy gates.", "token_count": 481} +{"id": "doc_bbc97cbff254", "path": "docs/library/api-reference.mdx", "url": "/library/api-reference", "title": "API Reference", "description": "Canonical public imports and core signatures for AFK.", "headings": ["Import map", "Agent", "Runner", "RunnerConfig", "FailSafeConfig", "Tool decorator", "AgentResult", "API stability"], "content_sha256": "15f83948b7edc7c528d459c0fd141ae9ab71d30f83a5635e768c12bdc145e3fa", "content": "This page is the public import contract for application developers. Use these imports in docs, examples, tests that exercise public behavior, and downstream applications. For field-by-field configuration, see Configuration Reference. For generated module detail, see Full Module Reference. Import map | Task | Public import | | --- | --- | | Define an agent | from afk.agents import Agent | | Configure fail-safe limits | from afk.agents import FailSafeConfig | | Configure policy rules | from afk.agents import PolicyEngine, PolicyRule | | Implement dynamic policy hooks | from afk.agents import PolicyRole | | Run an agent | from afk.core import Runner | | Configure the runner | from afk.core import RunnerConfig | | Consume streaming events | from afk.core import AgentStreamEvent, AgentStreamHandle | | Define a tool | from afk.tools import tool | | Access tool context | from afk.tools import ToolContext | | Build an LLM client | from afk.llms import LLMBuilder | | Run eval suites | from afk.evals import run_suite | | Define eval cases | from afk.evals import EvalCase, EvalBudget | | Create memory stores | from afk.memory import InMemoryMemoryStore, SQLiteMemoryStore | | Create task queues | from afk.queues import InMemoryTaskQueue, RedisTaskQueue, TaskWorker | | Expose MCP tools | from afk.mcp import MCPServer | | Use A2A messaging | from afk.messaging import InternalA2AProtocol | Do not use src.afk.* imports in user-facing docs. Agent Only model is required. Most examples should also set name and instructions for clear traces and predictable behavior. Runner Common methods: | Method | Use when | Returns | | --- | --- | --- | | runner.run_sync(agent, user_message=..., thread_id=...) | Scripts, CLIs, tests without an existing event loop | AgentResult | | await runner.run(agent, user_message=..., thread_id=...) | Async services, workers, APIs | AgentResult | | await runner.run_stream(agent, user_message=..., thread_id=...) | Chat UIs and progress streams | AgentStreamHandle | | await runner.resume(agent, run_id=..., thread_id=..., context=...) | Continue from persisted state | AgentResult | | await runner.compact_thread(thread_id=...) | Apply memory retention/compaction | MemoryCompactionResult | run_sync() is a convenience wrapper for synchronous code. Use await runner.run(...) inside async applications. RunnerConfig Use Configuration Reference for the complete set of runner fields and defaults. FailSafeConfig Set max_total_cost_usd for production agents. The default is intentionally unset because acceptable budgets vary by application. Tool decorator Tool functions may be sync or async. They may accept (args), (args, ctx), or (ctx, args). AgentResult Important fields: | Field | Meaning | | --- | --- | | final_text | Final assistant text | | state | Terminal run state | | requested_model | Model requested by the caller or agent | | normalized_model | Effective normalized model when available | | provider_adapter | Provider/adapter used when available | | tool_executions | Ordered tool execution records | | subagent_executions | Ordered subagent execution records | | usage_aggregate | Aggregated input/output/total tokens | | total_cost_usd | Estimated total cost when available | | state_snapshot | Terminal runtime snapshot payload | API stability - Public docs and examples should use imports from afk.agents, afk.core, afk.tools, afk.llms, afk.memory, afk.queues, afk.mcp, afk.messaging, afk.observability, and afk.evals. - Internal modules under package subdirectories may change faster than public exports. - When changing public exports, update this page, Public API Rules, examples, and generated agent-facing docs.", "token_count": 391} +{"id": "doc_4e1ebaf40267", "path": "docs/library/architecture.mdx", "url": "/library/architecture", "title": "Architecture", "description": "How AFK separates orchestration from execution.", "headings": ["Layered architecture", "Module boundaries", "Runtime sequence", "Contract boundaries", "Error isolation", "Design principles", "Next steps"], "content_sha256": "ff09cde4ea0e12906a54edc1fad4c85abd20bbc0a45370728060b8067e7d3cf8", "content": "AFK's architecture is built on one principle: **orchestration and execution are separate concerns**. The runner (orchestration) manages the step loop, state, and policies. Adapters (execution) handle LLM calls, tool execution, and external communication. You can swap any adapter without touching your agent code. Layered architecture Module boundaries | Module | Responsibility | Dependencies | | --------------- | ------------------------------------------------------ | --------------------------------------------------- | | afk.agents | Agent definition, configuration, fail-safe | pydantic | | afk.core | Runner, step loop, state management, policies | afk.agents, afk.llms, afk.tools, afk.memory | | afk.llms | LLM runtime, provider adapters, retry/circuit breaking | Provider SDKs | | afk.tools | Tool registry, execution, validation, hooks | pydantic | | afk.memory | State persistence, checkpoints, compaction | Backend drivers | | afk.observability | Event pipeline, metrics, exporters | OTEL SDK (optional) | | afk.messaging | Public A2A protocol, auth, host, and delivery exports | afk.agents | | afk.evals | Eval runner, assertions, reporting | afk.core | **Key rule:** Modules in the Adapters layer never import from each other. afk.llms doesn't know about afk.tools. Only afk.core wires them together. Runtime sequence What happens when you call runner.run(agent, user_message=\"...\"): Contract boundaries Every boundary between modules uses a typed contract: All contracts are Pydantic models. This means: - **Validation at boundaries** \u2014 malformed data causes clear errors - **Serializable** \u2014 every contract can be logged, stored, or sent over the wire - **Versionable** \u2014 contracts have stable shapes for backward compatibility Error isolation Failures in one adapter don't crash the others: | Failure | Impact | Runner behavior | | -------------------- | ------------------------------ | --------------------------------------- | | LLM call fails | No response for this step | Retry (if retryable) or fail the run | | Tool execution fails | Tool result is an error object | Return error to LLM for self-correction | | Memory backend fails | State not persisted | Run continues (degraded mode) | | Telemetry fails | Events not exported | Run continues (events dropped silently) | **Telemetry failures are always silent.** A broken exporter should never block an agent run. If telemetry is critical to your use case, add a separate monitoring check. Design principles 1. **Contracts first.** Define the interface (Pydantic model), then implement the behavior. Never skip the contract. 2. **No cross-adapter imports.** afk.llms doesn't import afk.tools. Only afk.core wires modules together. 3. **Classify failures.** Every error is retryable, terminal, or non-fatal. The runner uses this classification to decide what to do. 4. **Least privilege.** Adapters get only the data they need. LLM adapters don't see tool results until the runner decides to include them. Next steps Provider-portable LLM runtime in detail. Contribute to AFK \u2014 patterns and conventions.", "token_count": 330} +{"id": "doc_18d38fdacc9e", "path": "docs/library/building-with-ai.mdx", "url": "/library/building-with-ai", "title": "Building with AI", "description": "Production playbook \u2014 patterns, anti-patterns, and deployment guidance.", "headings": ["Start narrow, iterate fast", "Common patterns", "Anti-patterns", "Production readiness checklist", "Next steps"], "content_sha256": "4fb4252f5d2f1024c6863df494ee1627ef492fb5eb8a1c5ff8673f47773df3e4", "content": "This guide covers the engineering patterns for building production-quality AI features with AFK. It's organized around three phases: starting narrow, adding capabilities, and scaling safely. Start narrow, iterate fast The most common mistake is building for complexity you don't have yet. Start with the simplest version that solves the problem, then add capabilities based on real evidence. > [!TIP] > **Test at every step.** Don't add tools before the base prompt works. Don't add subagents before single-agent tools work. Each layer should be proven before adding the next. Common patterns Categorize input into predefined labels. No tools needed. **Tips:** - Constrain output format explicitly in the prompt - Test with a diverse set of inputs - Add evals for each category Retrieve context, then generate an answer. **Tips:** - Always search before answering - Instruct the model to cite sources - Set tool_output_max_chars to prevent context overflow Generate, review, and test code. **Tips:** - Higher cost limits (coding agents iterate more) - Gate write_file with policy approval - Use sandbox profiles for code execution Route tasks to specialist subagents. **Tips:** - Keep the coordinator's prompt focused on routing - Don't give the coordinator tools \u2014 let specialists handle execution - Use join_policy=\"allow_optional_failures\" if some specialists are non-critical Anti-patterns These are the most common mistakes. Avoiding them will save you significant debugging time. | Anti-pattern | Problem | Fix | | -------------------------------------- | ---------------------------------------------------- | -------------------------------------------------- | | **No cost limits** | Runaway agent loops spend $100s in minutes | Always set max_total_cost_usd | | **Vague instructions** | Model produces inconsistent output | Be specific: \"Output only the category name\" | | **Too many tools** | Model gets confused choosing between tools | Keep \u2264 5 tools per agent. Split into subagents. | | **Mixing orchestration and execution** | Runner logic leaks into tool handlers | Tools should be pure functions. No runner imports. | | **Skipping evals** | Prompt changes break behavior silently | Run evals in CI on every PR | | **Untyped tool arguments** | Missing validation, hard-to-debug errors | Always use Pydantic models | | **Not classifying failures** | Retryable errors treated as terminal (or vice versa) | Return clear error types from tools | | **Giant system prompts** | Token waste, instruction drift | Split into skills. Use templates. | Production readiness checklist | Area | Requirement | Status | | ----------------- | ------------------------------------------------- | ----------------------------------------- | | **Safety** | FailSafeConfig with cost, step, and time limits | | | **Safety** | Policy rules for all mutating tools | | | **Observability** | Telemetry exporter configured (OTEL recommended) | | | **Observability** | Alerts on error rate and latency | | | **Testing** | Eval suite with \u2265 5 cases running in CI | | | **Testing** | Golden traces captured for regression detection | | | **Memory** | Persistent backend for multi-turn conversations | | | **Memory** | Thread compaction configured | | | **Security** | Secrets in environment variables, not code | | | **Security** | Sandbox profiles for code execution tools | | Next steps Set up monitoring, alerting, and dashboards. Write behavioral tests for agents.", "token_count": 341} +{"id": "doc_4a1cf8a90e83", "path": "docs/library/checkpoint-schema.mdx", "url": "/library/checkpoint-schema", "title": "Checkpoint Schema", "description": "Checkpoint structure and resume correctness guarantees.", "headings": ["Checkpoint model", "Field reference", "Resume behavior", "Resume code example", "Start a run that might be interrupted", "Later, resume from the checkpoint", "What gets stored in the payload", "Phase-specific payloads", "Async write-behind behavior", "Effect replay and idempotency", "Common failure scenarios"], "content_sha256": "a0ba36ffceea5975afae78511697808cc3bf9231e2d04812cdc954536f971a1b", "content": "Checkpoints are the persistence mechanism that allows AFK agent runs to survive process restarts, crashes, and intentional pauses. At key boundaries during execution (step start, pre-LLM call, post-tool batch, run terminal), the runner writes a checkpoint record to the memory store. Each checkpoint captures enough state to reconstruct the execution context and resume from where the run left off. Checkpoints matter for three reasons: 1. **Fault tolerance** -- If the process crashes mid-run, the latest checkpoint lets you resume without re-executing already-completed work. 2. **Human-in-the-loop** -- When a run pauses for approval, the checkpoint preserves the full conversation and pending state so the run can resume hours or days later. 3. **Auditability** -- The checkpoint chain provides a step-by-step record of every phase the run passed through, useful for debugging and compliance. Checkpoint model Field reference | Field | Type | Description | | --- | --- | --- | | run_id | str | Unique identifier for the agent run. Generated at run start or provided when resuming. Used as the primary key for checkpoint lookup. | | schema_version | str | Checkpoint schema version (for migration/compat validation). | | phase | str | The execution phase when the checkpoint was written. Values include: run_started, step_started, pre_llm, post_llm, pre_tool_batch, post_tool_batch, pre_subagent_batch, post_subagent_batch, run_terminal. | | payload | dict | Phase-specific data. The contents vary by phase -- see the payload section below. | | timestamp_ms | int | Unix timestamp in milliseconds when the checkpoint was written. Used for ordering when multiple checkpoints exist for the same run. | Resume behavior The runner calls memory.get_state(thread_id, checkpoint_latest_key(run_id)) to fetch the most recent checkpoint for the given run. If no checkpoint exists, a AgentCheckpointCorruptionError is raised. The checkpoint record must be a dict with the required fields (run_id, thread_id, phase, payload). Missing or malformed fields cause an AgentCheckpointCorruptionError. The runner also normalizes legacy checkpoint formats through _normalize_checkpoint_record(). If the checkpoint's phase is run_terminal and the payload contains a terminal_result, the run is already complete. The runner returns a pre-resolved handle with the deserialized AgentResult -- no re-execution occurs. For non-terminal checkpoints, the runner loads the full runtime snapshot which contains the conversation messages, counters, usage aggregates, and any pending LLM response. This snapshot is used to reconstruct the execution context. The runner calls run_handle() with the restored snapshot. Execution continues from the step where the run was interrupted. If a pending_llm_response exists in the snapshot, the runner skips the LLM call and proceeds directly to tool execution for that response. Resume code example What gets stored in the payload The payload field carries different data depending on the checkpoint phase. The most important payload is the **runtime snapshot** persisted at step_started and post_llm phases, which contains everything needed for full resume: | Payload key | Type | Description | | --- | --- | --- | | messages | list[dict] | Serialized conversation history (system, user, assistant, tool messages). | | step | int | Current step counter in the execution loop. | | state | str | Current run state (running, degraded, etc.). | | context | dict | Run context dict merged from agent defaults and caller-provided context. | | llm_calls | int | Number of LLM calls made so far. | | tool_calls | int | Number of tool calls made so far. | | started_at_s | float | Unix timestamp when the run originally started. | | usage | dict | Token usage aggregate (input_tokens, output_tokens, total_tokens). | | total_cost_usd | float | Accumulated estimated cost in USD. | | session_token | str \\| None | Provider session token for session-aware providers. | | checkpoint_token | str \\| None | Provider checkpoint token for checkpoint-aware providers. | | pending_llm_response | dict \\| None | Serialized LLM response that was received but whose tool calls have not yet been executed. On resume, the runner skips the LLM call and processes these tool calls directly. | | tool_executions | list[dict] | Serialized ToolExecutionRecord entries for all tools executed so far. | | subagent_executions | list[dict] | Serialized SubagentExecutionRecord entries. | | requested_model | str | The model string originally requested by the agent. | | normalized_model | str | The model string after resolution and normalization. | | provider_adapter | str | The provider adapter type used (e.g., openai, litellm). | | final_text | str | The final text output accumulated so far. | | final_structured | dict \\| None | Structured output if the LLM returned schema-validated JSON. | Phase-specific payloads Beyond the runtime snapshot, individual phase checkpoints carry lighter payloads: | Phase | Key payload fields | | --- | --- | | run_started | agent_name, resumed | | step_started | state, message_count | | pre_llm | model, provider, message_count | | post_llm | model, provider, finish_reason, tool_call_count, session_token, checkpoint_token, total_cost_usd | | pre_tool_batch | tool_call_count | | post_tool_batch | tool_calls_total, tool_failures | | run_terminal | state, final_text, requested_model, normalized_model, provider_adapter, terminal_result | Async write-behind behavior By default, checkpoint writes are asynchronous (RunnerConfig.checkpoint_async_writes=True): - Writes are queued and flushed by a background writer. - Repeated runtime_state writes may be coalesced (checkpoint_coalesce_runtime_state=True). - Terminal states perform a bounded flush (checkpoint_flush_timeout_s) before returning. This improves loop throughput while preserving terminal durability. Effect replay and idempotency When a run resumes and re-enters a tool batch, the runner checks for previously persisted effect results before re-executing tools. Each tool call's result is stored with an input_hash (derived from tool name and arguments) and an output_hash. On resume, if a matching effect result exists for a tool call ID with a matching input hash, the stored result is replayed instead of re-executing the tool. This guarantees idempotent resume for tools with side effects. The replayed_effect_count field in the runtime snapshot tracks how many tool calls were satisfied from replay rather than fresh execution. Common failure scenarios **Missing checkpoint** -- Calling runner.resume() with a run_id that has no checkpoint raises AgentCheckpointCorruptionError. This can happen if the memory store was cleared or the run never persisted its first checkpoint (crashed before run_started). **Corrupted payload** -- If the checkpoint record exists but is not a valid dict or is missing required keys, AgentCheckpointCorruptionError is raised. The runner does not attempt partial recovery from corrupted checkpoints. **Pending LLM response corruption** -- If a checkpoint has pending_llm_response set but the serialized response cannot be deserialized, the runner raises AgentCheckpointCorruptionError rather than making a duplicate LLM call. **Stale session tokens** -- Provider session tokens stored in checkpoints may expire between the original run and the resume attempt. The runner passes the stored session_token and checkpoint_token to the provider, but the provider may reject them. In that case, the LLM call fails and follows the normal retry/fallback chain.", "token_count": 744} +{"id": "doc_a24b92541657", "path": "docs/library/configuration-reference.mdx", "url": "/library/configuration-reference", "title": "Configuration Reference", "description": "Every configurable field across Agent, Runner, FailSafeConfig, and SandboxProfile.", "headings": ["Agent", "Reasoning override precedence", "RunnerConfig", "Deep Dive: Interaction Models", "FailSafeConfig", "Deep Dive: Failure & Recovery", "FailurePolicy values", "SandboxProfile", "Runner constructor", "@tool decorator", "Next steps"], "content_sha256": "70a5ac606bb9dfba32cbba5a3a6f7791cf0437ae14f45dc29bf3491141978df6", "content": "This page is a single-source reference for every configuration knob in AFK. Each section lists fields with their type, default value, and purpose. Agent The Agent constructor defines what your agent is \u2014 identity, model, tools, and behavior. | Field | Type | Default | Description | | --------------------------- | ----------------- | ---------------- | -------------------------------------------------------------- | | model | str \\| LLM | **required** | LLM model name or pre-built client instance | | name | str | class name | Agent identity for logs, telemetry, and subagent routing | | instructions | str \\| callable | None | System prompt \u2014 static string or callable provider | | instruction_file | str \\| Path | None | Path to a prompt file that must resolve under prompts_dir | | prompts_dir | str \\| Path | None | Root directory for prompt files (env: AFK_AGENT_PROMPTS_DIR) | | tools | list | [] | Typed functions the agent can call | | subagents | list[Agent] | [] | Specialist agents this agent can delegate to | | context_defaults | dict | {} | Default JSON context merged into each run | | inherit_context_keys | list[str] | [] | Context keys accepted from a parent subagent | | model_resolver | callable | None | Custom function to resolve model names to LLM clients | | skills | list[str] | [] | Skill names to resolve under skills_dir | | mcp_servers | list | [] | External MCP server refs whose tools to expose | | skills_dir | str \\| Path | .agents/skills | Root directory for skill definitions | | instruction_roles | list | [] | Callbacks that append dynamic instruction text | | policy_roles | list | [] | Callbacks that can allow/deny/defer runtime actions | | policy_engine | PolicyEngine | None | Deterministic rule engine applied before policy roles | | subagent_router | SubagentRouter | None | Router callback deciding subagent targets | | max_steps | int | 20 | Maximum reasoning/tool loop steps | | tool_parallelism | int | None | Max concurrent tool calls (falls back to fail_safe) | | subagent_parallelism_mode | str | configurable | single, parallel, or configurable | | fail_safe | FailSafeConfig | defaults | Runtime limits and failure policies | | reasoning_enabled | bool \\| None | None | Default request thinking flag for this agent | | reasoning_effort | str \\| None | None | Default request thinking_effort label | | reasoning_max_tokens | int \\| None | None | Default request max_thinking_tokens | | enable_skill_tools | bool | True | Auto-register built-in skill tools | | enable_mcp_tools | bool | True | Auto-register tools from configured MCP servers | | runner | Runner | None | Optional runner override | Reasoning override precedence Reasoning values are resolved in this order: 1. Run context override: context[\\\"_afk\\\"][\\\"reasoning\\\"] 2. Agent defaults: reasoning_enabled, reasoning_effort, reasoning_max_tokens 3. Provider defaults/validation in the LLM layer --- RunnerConfig Passed to Runner(config=RunnerConfig(...)) to control runtime behavior. | Field | Type | Default | Description | | ----------------------------------------- | ---------------- | ------------------------------------------ | ---------------------------------------------------------- | | interaction_mode | str | headless | headless, interactive, or external | | approval_timeout_s | float | 300.0 | Timeout for deferred approval decisions | | input_timeout_s | float | 300.0 | Timeout for deferred user-input decisions | | approval_fallback | str | deny | Fallback when approval times out: allow, deny, defer | | input_fallback | str | deny | Fallback when user input times out | | sanitize_tool_output | bool | True | Sanitize model-visible tool output | | untrusted_tool_preamble | bool | True | Inject untrusted-data warning preamble | | tool_output_max_chars | int | 12_000 | Max tool output characters forwarded to model | | default_sandbox_profile | SandboxProfile | None | Default sandbox profile for tool execution | | sandbox_profile_provider | callable | None | Runtime sandbox profile resolver | | secret_scope_provider | callable | None | Secret-scope resolver per tool call | | default_allowlisted_commands | tuple[str] | ls, cat, head, tail, rg, find, pwd, echo | Allowlisted shell commands for runtime tools | | max_parallel_subagents_global | int | 64 | Global cap for concurrent subagent tasks | | max_parallel_subagents_per_parent | int | 8 | Per-parent-run cap for concurrent subagent fanout | | max_parallel_subagents_per_target_agent | int | 4 | Per-target-agent cap | | subagent_queue_backpressure_limit | int | 512 | Max pending subagent nodes before backpressure | | checkpoint_async_writes | bool | True | Enable background checkpoint/state writes | | checkpoint_queue_maxsize | int | 1024 | Max queued checkpoint write jobs | | checkpoint_flush_timeout_s | float | 10.0 | Timeout for terminal checkpoint flush | | checkpoint_coalesce_runtime_state | bool | True | Coalesce repeated runtime_state checkpoint writes | | debug | bool | False | Enable debug metadata in emitted run events | | debug_config | RunnerDebugConfig \\| None | None | Advanced debug controls (verbosity/content/redaction) | | background_tools_enabled | bool | True | Enable deferred/background tool orchestration | | background_tool_default_grace_s | float | 0.0 | Wait this long after defer to allow fast background completion before continuing | | background_tool_max_pending | int | 256 | Maximum unresolved background tool tickets per run | | background_tool_poll_interval_s | float | 0.5 | Poll interval for external/persisted ticket resolution | | background_tool_result_ttl_s | float | 3600.0 | TTL before pending ticket is marked failed (runtime floor is 1.0s) | | background_tool_interrupt_on_resolve | bool | True | When enabled, resolved tickets can be ingested immediately after defer/grace in the same step | Deep Dive: Interaction Models The interaction_mode setting fundamentally changes how the Runner handles decision points like tool approval or user input requests. - **headless (default)**: The Runner never pauses. - If a policy returns defer or request_user_input, the Runner immediately uses the configured approval_fallback or input_fallback (default: deny). - _Use case:_ Backend workers, cron jobs, automated testing. - **interactive**: The Runner pauses execution and uses the configured InteractionProvider to ask for human input. - For CLI apps, this prints to stdout and reads from stdin. - The run blocks until input is received or approval_timeout_s expires. - _Use case:_ Local CLI tools, scripts run by humans. - **external**: The Runner emits a run_paused event and **suspends execution**. - The run() loop exits (or yields a paused state). The state is persisted to memory. - The system waits for an external API call to runner.resume_with_input(). - _Use case:_ Chatbots, web UIs, Slack bots where the user is asynchronous. --- FailSafeConfig Passed to Agent(fail_safe=FailSafeConfig(...)) to set runtime limits and failure policies. | Field | Type | Default | Description | | ------------------------------ | --------------- | --------------------- | --------------------------------------------- | | llm_failure_policy | FailurePolicy | retry_then_fail | Strategy when LLM calls fail | | tool_failure_policy | FailurePolicy | continue_with_error | Strategy when tool calls fail | | subagent_failure_policy | FailurePolicy | continue | Strategy when subagent calls fail | | approval_denial_policy | FailurePolicy | skip_action | Strategy when approval is denied or times out | | max_steps | int | 20 | Maximum run loop iterations | | max_wall_time_s | float | 300.0 | Maximum wall-clock runtime in seconds | | max_llm_calls | int | 50 | Maximum number of LLM invocations | | max_tool_calls | int | 200 | Maximum number of tool invocations | | max_parallel_tools | int | 16 | Max concurrent tools per batch | | max_subagent_depth | int | 3 | Maximum subagent recursion depth | | max_subagent_fanout_per_step | int | 4 | Maximum subagents selected per step | | max_total_cost_usd | float \\| None | None | Cost ceiling for run termination | | fallback_model_chain | list[str] | [] | Ordered fallback model list for LLM retries | | breaker_failure_threshold | int | 5 | Circuit breaker open threshold | | breaker_cooldown_s | float | 30.0 | Circuit breaker cooldown window in seconds | Deep Dive: Failure & Recovery FailSafeConfig controls the agent's resilience. The policies work in a hierarchy: 1. **Lower-level retries**: Transient errors (network glitches, rate limits) are retried automatically by the LLM client, guided by AFK_LLM_MAX_RETRIES. 2. **llm_failure_policy**: If the LLM call fails after all retries (or hits a terminal error like 401 Unauthorized): - retry_then_fail: Tries a few more times at the agent level, then fails the run. - retry_then_degrade: Tries again, then marks the run as degraded but returns partial results (useful for \"best effort\" responses). 3. **tool_failure_policy**: If a tool raises an exception: - continue_with_error (default): The error message is fed back to the model. The model can then try to fix its mistake or apologize. **This is usually the best setting** for capable models. - fail_run: Immediately stops the run. Use this for critical transactional agents where any error is unacceptable. 4. **Circuit Breakers**: - If a model provider fails breaker_failure_threshold times in a row, the circuit opens. - Subsequent calls fail _instantly_ without network traffic until breaker_cooldown_s passes. - This protects your system (and wallet) from hammering a down service. FailurePolicy values | Value | Behavior | | --------------------- | ---------------------------------------------------- | | retry_then_fail | Retry with backoff, then fail the run | | retry_then_degrade | Retry with backoff, then degrade (partial result) | | retry_then_continue | Retry with backoff, then continue without the result | | fail_fast | Fail immediately, no retries | | fail_run | Fail the entire run | | continue_with_error | Continue, passing the error to the model | | continue | Continue silently, ignoring the failure | | skip_action | Skip the action entirely | --- SandboxProfile Controls execution restrictions for tool handlers. Configured via RunnerConfig.default_sandbox_profile. | Field | Type | Default | Description | | -------------------------- | --------------- | --------- | ---------------------------------------------- | | profile_id | str | default | Profile identifier for logs and policy | | allow_network | bool | False | Whether tools can make network requests | | allow_command_execution | bool | False | Whether tools can execute shell commands | | allowed_command_prefixes | list[str] | [] | Allowed command prefixes (empty = none) | | deny_shell_operators | bool | True | Block pipes, redirects, semicolons | | allowed_paths | list[str] | [] | Restrict file access to these paths | | denied_paths | list[str] | [] | Explicitly deny access to these paths | | command_timeout_s | float \\| None | None | Kill commands after this duration | | max_output_chars | int | 20_000 | Truncate command output beyond this limit | --- Runner constructor The Runner accepts these arguments directly (outside of RunnerConfig): | Argument | Type | Default | Description | | ---------------------- | ---------------------- | ---------------- | ------------------------------------------------------ | | memory_store | MemoryStore | None | Memory backend (resolved from env if None) | | interaction_provider | InteractionProvider | None | Human-in-the-loop provider (required for non-headless) | | policy_engine | PolicyEngine | None | Deterministic policy engine shared across runs | | telemetry | str \\| TelemetrySink | None | Telemetry sink instance or backend id | | telemetry_config | dict | None | Backend-specific sink configuration | | config | RunnerConfig | RunnerConfig() | Runner configuration | --- @tool decorator | Argument | Type | Default | Description | | ---------------- | ------------------ | ------------- | ----------------------------------------------------------------- | | args_model | Type[BaseModel] | **required** | Pydantic model for argument validation | | name | str | function name | Tool name exposed to the LLM | | description | str | docstring | Tool description for the LLM | | timeout | float | None | Execution timeout in seconds | | prehooks | list[PreHook] | None | Argument transform hooks | | posthooks | list[PostHook] | None | Output transform hooks | | middlewares | list[Middleware] | None | Execution wrappers (logging, caching, etc.) | | raise_on_error | bool | False | Raise exceptions instead of returning ToolResult(success=False) | Next steps Environment variable defaults and backend selection. Quick import reference.", "token_count": 1143} +{"id": "doc_e7d508371e5c", "path": "docs/library/core-runner.mdx", "url": "/library/core-runner", "title": "Core Runner", "description": "Execute agents \u2014 lifecycle, API modes, streaming, and state management.", "headings": ["Quick example", "Synchronous (simplest)", "Three API modes", "Run lifecycle", "Terminal states", "The step loop", "Runner configuration", "Run handles and lifecycle control", "Monitor events in real time", "Lifecycle controls", "Wait for the final result", "Thread-based memory", "Turn 1", "Turn 2 \u2014 agent remembers Turn 1", "Resume from checkpoint", "Resume an interrupted run", "Compact long threads", "Summarize old messages to control storage growth", "AgentResult reference", "ToolExecutionRecord fields", "Next steps"], "content_sha256": "1a47bf2eb1a77425072db12e35a71d725d7b826102f87dd74a81c747487df506", "content": "The **Runner** is the execution engine that runs agents. It manages the step loop, LLM calls, tool execution, state persistence, and telemetry. You create a Runner, hand it an Agent, and get back an AgentResult. Quick example Three API modes Blocks until complete. Best for scripts, tests, and simple integrations. Awaitable. Best for async applications and API servers. Real-time events. Best for chat UIs and CLI tools. Run lifecycle Every agent run follows this state machine: Terminal states | State | Meaning | final_text | | ------------- | ---------------------------------------- | ------------------------------------------------------------------------------------------ | | completed | Run finished successfully | Model's response | | failed | Unrecoverable error occurred | Error message | | degraded | Partial result (some failures tolerated) | Best-effort response | | cancelled | Caller cancelled the run | Empty or partial | | interrupted | Timeout or external interrupt | Empty or partial | The step loop Each \"step\" is one iteration of the agent's decision cycle: The runner constructs an LLMRequest with the conversation history, tool schemas, and model configuration. The request is sent through the LLM runtime (with retry, circuit breaker, rate limiting, and caching policies). If the LLM returns **text only** \u2192 the run is complete. If it returns **tool calls** \u2192 proceed to tool execution. Each tool call is validated, policy-checked, executed, and its output is sanitized and fed back to the LLM. The runner returns to Step 1 for the next LLM turn. This continues until the model produces a text-only response or a limit is hit. Runner configuration Run handles and lifecycle control For advanced control, use run_handle(): Thread-based memory Pass a thread_id to maintain conversation context across runs: Resume from checkpoint Compact long threads AgentResult reference | Field | Type | Description | | --------------------- | ------------------------------- | ----------------------------------------------------------------------------- | | final_text | str | The agent's final response | | state | str | Terminal state: completed, failed, degraded, cancelled, interrupted | | run_id | str | Unique run identifier | | thread_id | str | Thread identifier for memory continuity | | tool_executions | list[ToolExecutionRecord] | All tool calls with name, success, output, latency | | subagent_executions | list[SubagentExecutionRecord] | All subagent invocations | | usage_aggregate | UsageAggregate | Token counts aggregate across LLM calls | | state_snapshot | dict[str, JSONValue] | Final runtime counters/snapshot metadata | ToolExecutionRecord fields AgentResult.tool_executions entries include: | Field | Type | Description | | --- | --- | --- | | tool_name | str | Tool name | | tool_call_id | str \\| None | Tool call id from the model/provider | | success | bool | Execution success | | output | JSONValue \\| None | Tool output payload | | error | str \\| None | Error text (when failed) | | latency_ms | float \\| None | Tool latency | | agent_name | str \\| None | Agent that executed the tool (parent or subagent) | | agent_depth | int \\| None | Nesting depth where tool ran | | agent_path | str \\| None | Agent lineage path for nested calls | Next steps Real-time event streaming for chat UIs. Thread-based state persistence and resume.", "token_count": 326} +{"id": "doc_3f1383f72c5f", "path": "docs/library/debugger.mdx", "url": "/library/debugger", "title": "Debugger", "description": "Debug-mode runner setup with redacted event payloads and live event taps.", "headings": ["Quick start", "Attach to a run handle", "Redaction behavior", "Verbosity modes", "Background tool scenario (coding + build + docs)", "External worker completion"], "content_sha256": "7349668744c3913398f016c1e3b0f07fd6b3659d0cf075176717e7a6a4003dcb", "content": "Use afk.debugger.Debugger to run agents in a structured debug mode without changing your core runtime architecture. Quick start You can also enable debug mode directly on the runner: Attach to a run handle Redaction behavior When redact_secrets=True, payload keys containing any of these markers are masked: - api_key - token - secret - authorization - password Verbosity modes - basic: lightweight metadata and step markers. - detailed: includes payload previews and step metadata. - trace: highest detail for deep diagnostics. Background tool scenario (coding + build + docs) Flow: 1. Agent writes code. 2. Agent calls build_project and receives tool_deferred. 3. Agent continues with write_docs or other tasks. 4. Runner emits tool_background_resolved when build completes. 5. Agent uses resolved build output in a later step to finalize/fix. External worker completion For out-of-process execution, an external worker can complete a ticket by writing background state into memory: The runner poller detects this update, emits tool_background_resolved or tool_background_failed, and injects a synthetic tool message for the next step.", "token_count": 122} +{"id": "doc_067033cfc945", "path": "docs/library/deployment.mdx", "url": "/library/deployment", "title": "Deployment", "description": "Deploy AFK agents to production with Docker, Kubernetes, and scaling patterns.", "headings": ["Docker deployment", "Basic Dockerfile", "Install dependencies", "Copy application code", "Run your application entrypoint that creates AFK agents/runners", "Production Dockerfile with multi-stage build", "Production image", "Run as non-root user", "docker-compose.yml", "Environment configuration", "Required environment variables", "LLM Provider", "or", "Memory backend", "Queue backend ", "Observability", "Server mode", "Production configuration file", "Scaling patterns", "Horizontal scaling with workers", "Kubernetes deployment", "Kubernetes HPA for auto-scaling", "Health checks", "Database schema", "SQLite (development)", "PostgreSQL", "Security checklist", "Monitoring", "Next steps"], "content_sha256": "7cc0c692d113fd9dc1af4de8235cb8b0c1c4b512d6079d164413d60837a5aa32", "content": "This guide covers deploying AFK agents to production environments, from single-container setups to distributed, multi-worker deployments. Docker deployment Basic Dockerfile Production Dockerfile with multi-stage build docker-compose.yml Environment configuration Required environment variables Production configuration file Create config/production.yaml: Scaling patterns Horizontal scaling with workers Kubernetes deployment Kubernetes HPA for auto-scaling Health checks Implement health endpoints in your server: Database schema SQLite (development) SQLite requires no schema setup \u2014 tables are created automatically on first use. PostgreSQL Security checklist Store API keys in secrets managers (AWS Secrets Manager, HashiCorp Vault, Kubernetes Secrets). Never commit keys to version control. Restrict traffic between services. Agents should only reach LLM providers and necessary databases. Configure rate limits on public endpoints to prevent abuse. Always set max_total_cost_usd in FailSafeConfig for production agents. Enable telemetry export to your logging infrastructure for compliance. Monitoring Key metrics to track: | Metric | What it indicates | Alert threshold | |--------|------------------|-----------------| | agent.run.duration | How long runs take | > 60s p95 | | agent.run.cost | Token spend per run | > $0.50 per run | | agent.run.failures | Failed runs | > 5% error rate | | llm.latency | LLM response time | > 10s p95 | | llm.errors | LLM API errors | > 1% error rate | | queue.depth | Pending tasks | > 100 items | | queue.dead_letters | Failed tasks | > 0 | Next steps Set up telemetry and alerting for production monitoring. Security hardening checklist and best practices. CI-gated quality checks for agent releases. Production patterns and anti-patterns.", "token_count": 191} +{"id": "doc_d147a81bac29", "path": "docs/library/developer-guide.mdx", "url": "/library/developer-guide", "title": "Developer Guide", "description": "Maintainer workflow for changing AFK itself.", "headings": ["Local setup", "Repository boundaries", "Change workflow", "Documentation workflow", "High-risk areas", "Public API checklist", "Next steps"], "content_sha256": "8ac363b758f8ae9d391d26db304b7bcf931f1fb24bbf0dd159c8de2e3ebc1789", "content": "This page is for contributors changing the AFK framework. If you are building an application with AFK, start with Quickstart or Building with AI. Local setup AFK targets Python 3.13+. Common commands: Preview docs: Regenerate agent-facing docs and skill indexes: Install the repository skills with Vercel's Skills CLI: Use afk-coder when building with AFK. Use afk-maintainer when reviewing or changing AFK itself. Repository boundaries | Package | Responsibility | | --- | --- | | afk.agents | Agent definitions, policy, prompts, skills, lifecycle, workflow, A2A | | afk.core | Runner, interaction providers, streaming handles, telemetry contracts | | afk.llms | Provider-portable LLM runtime, adapters, retry/timeout/cache/routing policies | | afk.tools | Typed tools, decorators, registry, sandboxing, output limiting | | afk.memory | Memory stores, checkpoints, retention, compaction, vector helpers | | afk.queues | Async task queues, execution contracts, workers, retry/DLQ behavior | | afk.observability | Telemetry collectors, projectors, exporters | | afk.mcp, afk.messaging, afk.debugger, afk.evals | Optional integration and quality layers | Keep the Agent/Runner/Runtime boundary intact: - agents are configuration; - runners execute; - tools, memory, queues, LLM adapters, and telemetry provide runtime capabilities. Change workflow 1. Identify the public contract affected by the change. 2. Inspect the existing module and tests before editing. 3. Make the smallest behavior change that preserves package boundaries. 4. Add or update focused tests for success and failure paths. 5. Update docs when behavior, public imports, configuration, env vars, or examples change. 6. Regenerate agent-facing docs when docs navigation, snippets, or skill metadata change. Documentation workflow User-facing docs must: - import from public package surfaces such as afk.agents and afk.core; - explain behavior before internals; - use Python 3.13+ guidance; - distinguish afk-py distribution install from afk imports; - include new pages in docs/docs.json; - keep examples aligned with current AgentResult, RunnerConfig, and FailSafeConfig fields. Maintainer docs may reference internal files, but should state which public contract or invariant is being protected. High-risk areas Use targeted tests when touching: | Area | Risk | | --- | --- | | src/afk/core/runner/ | execution loop, checkpoints, resume, policy/failure routing | | src/afk/core/streaming.py | stream events and handle lifecycle | | src/afk/tools/core/base.py, src/afk/tools/registry.py | tool invocation semantics | | src/afk/tools/security.py | sandbox, secret scope, output limiting | | src/afk/llms/runtime/ | retries, circuit breakers, rate limits, caching, routing | | src/afk/memory/ | persistence, checkpoint keys, compaction, vector search | | src/afk/queues/ | execution contracts, retry/DLQ, worker lifecycle | | src/afk/agents/a2a/ | auth, delivery guarantees, protocol compatibility | Public API checklist Before changing exports or constructor fields: - update package-level __all__; - update API Reference; - update Configuration Reference if defaults changed; - update snippets that use the changed field; - add public-import tests where useful. Next steps Package boundaries and runtime flow. Maintainer rules for stable imports and docs examples. Behaviors that tests protect. Generated source-level symbol map.", "token_count": 387} +{"id": "doc_61fd9f682847", "path": "docs/library/environment-variables.mdx", "url": "/library/environment-variables", "title": "Environment Variables", "description": "Environment variable reference for AFK defaults and backend configuration.", "headings": ["LLM defaults", "Memory", "Override in code \u2014 takes precedence over env vars", "Queue", "Prompts", "Runner and tool command defaults", "MCP server", "A2A", "Precedence", "Next steps"], "content_sha256": "89122dd488be8932d0ac65bf7169acebead8cb49223e178df9d0f049082cd073", "content": "AFK is configured primarily through code. Environment variables provide fallback defaults for LLM settings, memory backends, queues, and prompt directories. **Runtime configuration APIs always take precedence.** All variables are optional. If unset, AFK uses the defaults listed below. LLM defaults These variables configure the default LLM provider and behavior. They are read by LLMSettings.from_env() at startup. | Variable | Default | Description | | ------------------------------- | -------------- | ------------------------------------------------------------ | | AFK_LLM_PROVIDER | litellm | Default provider id (openai, litellm, anthropic_agent) | | AFK_LLM_PROVIDER_ORDER | _(none)_ | Comma-separated provider preference order for routing helpers | | AFK_LLM_MODEL | gpt-4.1-mini | Default model name | | AFK_EMBED_MODEL | _(none)_ | Embedding model for vector operations | | AFK_LLM_API_BASE_URL | _(none)_ | Provider API base URL | | AFK_LLM_API_KEY | _(none)_ | Provider API key | | AFK_LLM_TIMEOUT_S | 30 | Request timeout in seconds | | AFK_LLM_STREAM_IDLE_TIMEOUT_S | 45 | Stream idle timeout in seconds | | AFK_LLM_MAX_RETRIES | 3 | Retry attempts on transient failures | | AFK_LLM_BACKOFF_BASE_S | 0.5 | Exponential backoff base in seconds | | AFK_LLM_BACKOFF_JITTER_S | 0.15 | Random jitter added to backoff | | AFK_LLM_JSON_MAX_RETRIES | 2 | Structured output repair attempts | | AFK_LLM_MAX_INPUT_CHARS | 200000 | Input truncation ceiling in characters | **API keys for specific providers** (e.g. OPENAI_API_KEY, ANTHROPIC_API_KEY) are read by the underlying provider libraries, not by AFK directly. Set AFK_LLM_API_KEY only if you want a single key shared across providers. Memory Configure the persistent memory backend for thread-based conversations. | Variable | Default | Description | | -------------------- | -------------------- | ------------------------------------------------------- | | AFK_MEMORY_BACKEND | sqlite | Backend type: inmemory/memory, sqlite, redis, postgres | | AFK_SQLITE_PATH | afk_memory.sqlite3 | SQLite file path | | AFK_REDIS_URL | _(none)_ | Redis connection URL (for example redis://localhost:6379) | | AFK_REDIS_HOST | localhost | Redis host when URL is not set | | AFK_REDIS_PORT | 6379 | Redis port when URL is not set | | AFK_REDIS_DB | 0 | Redis DB when URL is not set | | AFK_REDIS_PASSWORD | _(none)_ | Redis password when URL is not set | | AFK_REDIS_EVENTS_MAX | 2000 | Max Redis memory events per thread | | AFK_PG_DSN | _(none)_ | PostgreSQL connection string | | AFK_PG_HOST | localhost | PostgreSQL host when DSN is not set | | AFK_PG_PORT | 5432 | PostgreSQL port when DSN is not set | | AFK_PG_USER | postgres | PostgreSQL user when DSN is not set | | AFK_PG_PASSWORD | _(none)_ | PostgreSQL password when DSN is not set | | AFK_PG_DB | afk | PostgreSQL database when DSN is not set | | AFK_PG_SSL | false | Enable PostgreSQL SSL | | AFK_PG_POOL_MIN | 1 | PostgreSQL pool minimum size | | AFK_PG_POOL_MAX | 10 | PostgreSQL pool maximum size | | AFK_VECTOR_DIM | _(required for Postgres)_ | Vector dimension for Postgres memory search | Queue Configure the task queue backend for async agent execution. | Variable | Default | Description | | ---------------------------------- | ---------- | --------------------------------- | | AFK_QUEUE_BACKEND | inmemory | Backend type: inmemory, redis | | AFK_QUEUE_REDIS_URL | falls back to AFK_REDIS_URL | Redis URL for queue backend | | AFK_QUEUE_REDIS_HOST | falls back to AFK_REDIS_HOST, then localhost | Redis queue host | | AFK_QUEUE_REDIS_PORT | falls back to AFK_REDIS_PORT, then 6379 | Redis queue port | | AFK_QUEUE_REDIS_DB | falls back to AFK_REDIS_DB, then 0 | Redis queue DB | | AFK_QUEUE_REDIS_PASSWORD | falls back to AFK_REDIS_PASSWORD | Redis queue password | | AFK_QUEUE_REDIS_PREFIX | afk:queue | Redis key prefix | | AFK_QUEUE_RETRY_BACKOFF_BASE_S | 0.5 | Retry base delay in seconds | | AFK_QUEUE_RETRY_BACKOFF_MAX_S | 30 | Retry max delay in seconds | | AFK_QUEUE_RETRY_BACKOFF_JITTER_S | 0.2 | Random jitter added to backoff | The inmemory queue backend does not persist across restarts. Use redis for production workloads that need reliable task delivery. Prompts | Variable | Default | Description | | ----------------------- | ---------------- | -------------------------------------- | | AFK_AGENT_PROMPTS_DIR | .agents/prompt | Root directory for system prompt files | Agents resolve instruction_file paths relative to this directory. See System Prompts for details. Runner and tool command defaults | Variable | Default | Description | | --- | --- | --- | | AFK_ALLOWED_COMMANDS | _(none)_ | Comma-separated default allowlist for runtime command tools | Runner constructors and RunnerConfig fields remain the preferred way to set command policy. Use this environment variable only for process-wide defaults. MCP server These variables are read by the MCP server configuration helpers in afk.config. | Variable | Default | Description | | --- | --- | --- | | AFK_CORS_ORIGINS | _(none)_ | Comma-separated CORS origins | | AFK_MCP_NAME | afk-mcp-server | Server name | | AFK_MCP_VERSION | 1.0.0 | Server version string | | AFK_MCP_HOST | 0.0.0.0 | Bind host | | AFK_MCP_PORT | 8000 | Bind port | | AFK_MCP_INSTRUCTIONS | _(none)_ | Optional server instructions | | AFK_MCP_PATH | /mcp | HTTP MCP endpoint path | | AFK_MCP_SSE_PATH | /mcp/sse | SSE endpoint path | | AFK_MCP_HEALTH_PATH | /health | Health endpoint path | | AFK_MCP_ENABLE_SSE | true | Enable SSE endpoint | | AFK_MCP_ENABLE_HEALTH | true | Enable health endpoint | | AFK_MCP_ALLOW_BATCH | true | Allow batched MCP requests | A2A No default environment variables are required. Configure A2A host and authentication in code for an explicit security posture. See A2A for details. Precedence Configuration is resolved in this order (highest priority first): 1. **Constructor arguments** \u2014 Runner(memory_store=...), Agent(model=...) 2. **RunnerConfig / LLMSettings objects** \u2014 passed to constructors 3. **Environment variables** \u2014 fallback defaults listed on this page 4. **Built-in defaults** \u2014 hardcoded in the source Next steps Full reference for all configurable fields across Agent, Runner, and more. Runner lifecycle and configuration options.", "token_count": 557} +{"id": "doc_c93c115aeb85", "path": "docs/library/evals.mdx", "url": "/library/evals", "title": "Evals", "description": "Behavioral testing for agent quality \u2014 assertions, budgets, and CI integration.", "headings": ["Your first eval", "Eval lifecycle", "Eval case types", "Assertions", "Suite configuration", "CI integration", ".github/workflows/evals.yml", "Release gating", "Next steps"], "content_sha256": "89a16478fcd1a7d21a0539b75ab780f2634444483626668f05e16f25320bd0ed", "content": "AFK evals run agents against named inputs and check the resulting state, text, tool usage, budgets, and telemetry. Use them for prompt changes, tool changes, routing changes, and regression tests. They can run against real providers, test adapters, or agents configured with deterministic tools. Your first eval Eval lifecycle Each case specifies an agent and input message. Suite-level assertions and budgets verify the result. The scheduler runs cases sequentially or in parallel, respecting concurrency limits. Each case runs through the same runner path your application uses. Assertions verify the result \u2014 text content, state, tool usage, cost, latency, etc. Budget limits gate individual case costs and the total suite cost. Pass/fail results, assertion details, and metrics are collected into a report. Eval case types Verify correct behavior under normal conditions. Verify graceful handling of errors and edge cases. Verify that the agent uses tools correctly. Verify that the agent stays within cost limits. Assertions Assertions are suite-level callables. Import the built-ins from afk.evals, or implement the EvalAssertion protocol. Suite configuration CI integration Run evals in your CI pipeline to gate releases: **Set a budget for CI evals.** Without a budget, a broken prompt can drain your API credits during a CI run. Use EvalBudget(max_total_cost_usd=2.00) as a reasonable CI limit. Release gating Gate releases on eval pass rate: Next steps Security boundaries and production hardening. Production playbook with anti-patterns.", "token_count": 181} +{"id": "doc_a5120de838fb", "path": "docs/library/examples/index.mdx", "url": "/library/examples/", "title": "Getting Started", "description": "Scenario-based examples for every major AFK feature.", "headings": ["Which example should I start with?", "Complexity progression", "Scenario catalog"], "content_sha256": "e209b35c682236c60b0dd47344bf686c23ea09e6000e11aab2c25e3078800062", "content": "This catalog provides runnable code examples for every major AFK capability. Each example is self-contained and demonstrates a specific pattern -- from the simplest possible agent run to advanced tool security configurations. The examples are ordered by complexity. If you are new to AFK, start at the top and work your way down. Each example builds on concepts introduced in the previous ones. Which example should I start with? - **First time using AFK?** Start with Minimal Chat Agent. It shows the absolute simplest agent run in under 10 lines of code. - **Need to gate dangerous actions?** Go to Policy + HITL. It demonstrates how policy engines and human approval work together. - **Building a multi-agent system?** See Subagent Router. It shows how to delegate work to specialist subagents and merge results. - **Need to persist and resume runs?** Check Resume + Compact. It covers checkpoint-based resumption and memory compaction. - **Using LLMs without the agent loop?** See Structured Output. It shows how to use LLMBuilder directly with Pydantic models. - **Locking down tool capabilities?** Go to Tool Security. It demonstrates scoped tool registration and policy gates. Complexity progression | Level | Example | What You Learn | | ------------ | -------------------- | ---------------------------------------------------------- | | Beginner | Minimal Chat Agent | Agent + Runner basics, final_text access | | Beginner | Structured Output | Direct LLM usage, Pydantic schema validation | | Intermediate | Policy + HITL | Policy engine, approval flows, interaction modes | | Intermediate | Tool Security | Tool scoping, sandbox profiles, policy gates | | Intermediate | Hooks + Middleware | Pre/post tool execution hooks, middleware chains | | Intermediate | Runtime Tools | Built-in file, search, and command tools | | Intermediate | Prompt Loader | External prompt files, template variables | | Advanced | Subagent Router | Multi-agent coordination, delegation patterns | | Advanced | Resume + Compact | Checkpointing, memory management, long-running workflows | | Intermediate | Streaming + Memory | Real-time streaming with multi-turn thread persistence | | Intermediate | Cost Monitoring | Budget limits, real-time cost tracking, batch budgets | | Advanced | MCP Client | Connect to external MCP servers, hybrid local+remote tools | | Advanced | Multi-Model Fallback | Fallback chains, circuit breakers, model tier strategies | | Advanced | Production Client | Timeout middleware, Redis connection pooling, graceful shutdown | Scenario catalog The simplest possible AFK agent. Define an agent, run it synchronously, and read the output. Start here. Gate sensitive actions through a policy engine and route approval requests to a human operator. Delegate workload to specialist subagents using a coordinator pattern. Merge outputs into a unified response. Checkpoint a run, resume it later from the last known state, and compact memory to control storage growth. Use the LLM builder directly (without the agent loop) to get schema-validated structured responses via Pydantic. Register destructive tools with tight scoping, enforce sandbox profiles, and gate actions through policy. Attach pre-execution and post-execution hooks to tools. Chain middleware for logging, validation, and transformation. Use AFK's built-in file reading, directory listing, and command execution tools with agents. Load system prompts from external files with template variable substitution and automatic caching. Combine real-time streaming with thread-based memory for multi-turn chat UIs. Track and control agent costs using FailSafeConfig budgets and telemetry events. Connect to external MCP servers, discover tools, and combine with local tools. Configure fallback model chains for LLM resilience, circuit breakers, and cost optimization. Production-ready LLM client with timeout middleware, Redis connection pooling, and graceful shutdown handling.", "token_count": 412} +{"id": "doc_4055659fe573", "path": "docs/library/failure-policy-matrix.mdx", "url": "/library/failure-policy-matrix", "title": "Failure Policy Matrix", "description": "How errors flow through the system \u2014 classification, handling, and escalation.", "headings": ["Error classification", "Failure decision flow", "Failure matrix by source", "LLM failures", "Tool failures", "Subagent failures", "Infrastructure failures", "Failure policies", "Budget-triggered limits", "Next steps"], "content_sha256": "3e2a501934ed7fabdbb4f5ccd429435c4948ed699b688168a9746b611f61f030", "content": "AFK classifies every error and applies a policy-driven response. Understanding the failure matrix helps you configure the right behavior for your use case. Error classification Every error is classified into one of three categories: | Classification | Meaning | Default behavior | | -------------- | ---------------------------------------------- | ------------------------------ | | **Retryable** | Transient failure, may succeed on retry | Retry with exponential backoff | | **Terminal** | Permanent failure, will not recover | Stop and report error | | **Non-fatal** | Something went wrong, but the run can continue | Log warning, continue | Failure decision flow Failure matrix by source LLM failures | Error | Classification | Example | | -------------------------- | ------------------------ | ------------------------------------- | | Rate limit (429) | Retryable | \"Rate limit exceeded, retry after 2s\" | | Server error (500/502/503) | Retryable | \"Internal server error\" | | Timeout | Retryable | \"Request timed out after 60s\" | | Auth error (401/403) | Terminal | \"Invalid API key\" | | Invalid request (400) | Terminal | \"Model does not exist\" | | Circuit breaker open | Terminal (with fallback) | \"Circuit breaker open for provider\" | **Configuration:** Tool failures | Error | Classification | Example | | ----------------- | -------------- | --------------------------------------------------------- | | Validation error | Non-fatal | \"Invalid arguments\" (returned to LLM for self-correction) | | Handler exception | Configurable | \"Tool raised an error\" | | Timeout | Configurable | \"Tool exceeded 10s timeout\" | | Policy denial | Non-fatal | \"Action denied by policy\" (returned to LLM) | **Configuration:** Subagent failures | Error | Classification | Example | | --------------------- | -------------- | --------------------------------------------------- | | Subagent run failed | Configurable | \"Subagent 'researcher' completed with state=failed\" | | Subagent timeout | Configurable | \"Subagent exceeded wall time\" | | Join policy violation | Terminal | \"Required subagent failed (all_required policy)\" | **Configuration:** Infrastructure failures | Error | Classification | Example | | -------------------------- | ------------------ | ------------------------------ | | Memory backend unavailable | Non-fatal | \"Could not persist checkpoint\" | | Telemetry export failed | Non-fatal (silent) | \"OTEL exporter timed out\" | | Queue push failed | Retryable | \"Redis connection refused\" | Failure policies Any failure causes the run to fail immediately. **Use when:** All operations are critical and partial results are worse than no results. Failures are tolerated. The run completes with state=\"degraded\" instead of \"completed\". **Use when:** Partial results are better than no results (e.g., some tool fails but the agent can still answer). Failures are logged but ignored. The run continues as if nothing happened. **Use when:** The failing component is non-essential (e.g., analytics tool, optional enrichment). Budget-triggered limits When a budget limit is hit, the run is stopped immediately: | Limit | Triggered by | Run state | | -------------------- | ------------------------ | ---------------------- | | max_steps | Step count exceeded | failed or degraded | | max_tool_calls | Tool call count exceeded | failed or degraded | | max_total_cost_usd | Estimated cost exceeded | failed | | max_wall_time_s | Wall time exceeded | interrupted | Next steps Security boundaries and hardening checklist. Run lifecycle and state management.", "token_count": 310} +{"id": "doc_2a18de009d56", "path": "docs/library/full-module-reference.mdx", "url": "/library/full-module-reference", "title": "Full Module Reference", "description": "Module inventory and responsibilities.", "headings": ["Module dependencies", "Extension points", "`afk.agents`", "`afk.core`", "`afk.llms`", "`afk.tools`", "`afk.memory`", "`afk.queues`", "`afk.observability`", "`afk.evals`", "`afk.mcp`", "`afk.messaging`"], "content_sha256": "294699e90c8917af6690bcdb9d7d9511ab0ed5cb92595b96f6436621c88ad4e1", "content": "The Agent Forge Kit (AFK) Python SDK is organized into top-level packages, each owning a distinct responsibility. This page summarizes the package map, key public imports, and extension boundaries. Use this reference when you need to find which package owns a capability. For field-level details, read the focused reference pages linked from the sidebar. Module dependencies The AFK architecture is organized around a few stable boundaries: - **afk.agents** owns declarative agent configuration, policy, prompts, skills, delegation metadata, and A2A contracts. - **afk.core** owns execution: runner lifecycle, streaming, interaction providers, delegation scheduling, checkpointing, memory wiring, and telemetry wiring. - **afk.llms** owns model provider adapters, runtime policies, structured output helpers, routing, caching, and LLM request/response types. - **afk.tools** owns tool definitions, registries, hooks, middleware, sandbox profiles, and prebuilt runtime tools. Extension points The framework is designed to be extended at specific protocol boundaries. **Do not subclass internal classes**. Instead, implement these protocols: - **InteractionProvider** (afk.core): Build custom human-in-the-loop interfaces (for example Slack, Discord, or web approval UIs). - **MemoryStore** (afk.memory): Add support for new databases (Mongo, DynamoDB). - **LLMProvider** (afk.llms): Integrate new model providers (local inference, exotic APIs). - **TelemetrySink** (afk.observability): Export metrics/traces to custom backends (Datadog, Honeycomb). --- afk.agents Agent definitions, lifecycle types, delegation, policy, A2A protocol, and error hierarchy. **Sub-modules:** | Sub-module | Responsibility | | ----------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | afk.agents.core | Agent and BaseAgent definitions, ChatAgent variant. | | afk.agents.types | Runtime types: AgentResult, AgentRunEvent, AgentRunHandle, AgentState, PolicyEvent, PolicyDecision, FailSafeConfig, UsageAggregate, execution records. | | afk.agents.policy | PolicyEngine, PolicyRule, PolicyEvaluation, policy subject inference. | | afk.agents.delegation | DelegationPlan, DelegationNode, DelegationEdge, RetryPolicy, JoinPolicy, DelegationResult. | | afk.agents.a2a | InternalA2AProtocol, A2A auth providers (AllowAllA2AAuthProvider, APIKeyA2AAuthProvider, JWTA2AAuthProvider), A2AServiceHost, delivery stores. | | afk.agents.contracts | AgentCommunicationProtocol, AgentInvocationRequest, AgentInvocationResponse, AgentDeadLetter. | | afk.agents.prompts | PromptStore, prompt file resolution, template rendering. | | afk.agents.lifecycle | Checkpoint versioning, schema migration, runtime helpers (circuit breaker, effect journal, state snapshots). | | afk.agents.skills | SkillStore, SkillDoc, SKILL.md parsing, checksum verification, and process-wide caching. | | afk.agents.security | Prompt-injection sanitization, untrusted tool-output redaction, and channel markers for trusted vs untrusted content. | | afk.agents.errors | Full error hierarchy: AgentError, AgentExecutionError, AgentCancelledError, SubagentRoutingError, etc. | **Key imports:** afk.core Runner execution engine, streaming, interaction providers, and delegation engine. **Sub-modules:** | Sub-module | Responsibility | | ---------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------- | | afk.core.runner | Runner class with run(), run_sync(), run_handle(), run_stream(), resume(), compact_thread(). RunnerConfig for safety and behavior defaults. | | afk.core.streaming | AgentStreamHandle, AgentStreamEvent, stream event constructors (text_delta, tool_started, stream_completed). | | afk.core.interaction | InteractionProvider protocol, HeadlessInteractionProvider, InMemoryInteractiveProvider. | | afk.core.runtime | DelegationEngine, DelegationPlanner, DelegationScheduler, backpressure and graph validation. | **Key imports:** afk.llms LLM client builder, provider registry, runtime policies, caching, routing, middleware, and type definitions. **Sub-modules:** | Sub-module | Responsibility | | --------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------ | | afk.llms.builder | LLMBuilder -- fluent builder for constructing LLM client instances. | | afk.llms.providers | Provider registry: OpenAIProvider, LiteLLMProvider, AnthropicAgentProvider, register_llm_provider(). | | afk.llms.runtime | LLMClient with production policies: RetryPolicy, TimeoutPolicy, RateLimitPolicy, CircuitBreakerPolicy, HedgingPolicy, CachePolicy. | | afk.llms.routing | Router registry: create_llm_router(), register_llm_router(). | | afk.llms.cache | Cache backends: create_llm_cache(), in-memory and Redis implementations. | | afk.llms.middleware | MiddlewareStack for request/response transformation pipelines. | | afk.llms.types | LLMRequest, LLMResponse, Message, ToolCall, Usage, streaming event types. | | afk.llms.config | LLMConfig dataclass for model configuration. | | afk.llms.profiles | Production/development profile presets. | | afk.llms.errors | LLMError, LLMTimeoutError, LLMRetryableError, etc. | **Key imports:** afk.tools Tool definition, registry, hooks, middleware, security, and prebuilt runtime tools. **Sub-modules:** | Sub-module | Responsibility | | --------------------- | --------------------------------------------------------------------------------------- | | afk.tools.core | tool decorator, ToolSpec, ToolContext, ToolResult, ToolRegistry. | | afk.tools.security | SandboxProfile, SandboxProfileProvider, SecretScopeProvider, argument validation. | | afk.tools.prebuilts | Built-in runtime tools (file read, directory list, command execution, skill tools). | | afk.tools.registry | Shared registry and registry middleware helpers. | **Key imports:** afk.memory Pluggable memory stores, compaction, retention policies, long-term memory, vector search, and thread state persistence. **Sub-modules:** | Sub-module | Responsibility | | ----------------------- | ----------------------------------------------------------------------------------------------------------------------------- | | afk.memory.store | MemoryStore abstract base class, MemoryCapabilities declarations. | | afk.memory.types | MemoryEvent, LongTermMemory, JsonValue, retention policy models. | | afk.memory.lifecycle | apply_event_retention(), apply_state_retention(), compact_thread_memory(), MemoryCompactionResult. | | afk.memory.vector | cosine_similarity(), vector scoring utilities for embedding-based search. | | afk.memory.factory | create_memory_store_from_env() factory for environment-based backend selection. | | afk.memory.adapters | Backend implementations: InMemoryMemoryStore, SQLiteMemoryStore, PostgresMemoryStore, RedisMemoryStore. | **Key imports:** afk.queues Task queue abstraction, worker lifecycle, failure classification, and metrics. **Sub-modules:** | Sub-module | Responsibility | | ---------------------- | --------------------------------------------------- | | afk.queues.base | Base queue types and enqueue/dequeue contracts. | | afk.queues.contracts | Queue protocol definitions. | | afk.queues.factory | Queue backend factory for creating queue instances. | | afk.queues.worker | Worker lifecycle management and task dispatch. | | afk.queues.metrics | Queue-level operational metrics. | **Key imports:** afk.observability Telemetry collection, projection, and export for runtime monitoring. **Sub-modules:** | Sub-module | Responsibility | | ------------------------------ | ---------------------------------------------------------------------------------------- | | afk.observability.models | RunMetrics dataclass with aggregated run statistics. | | afk.observability.contracts | Span and metric name constants (SPAN_AGENT_RUN, METRIC_AGENT_LLM_CALLS_TOTAL, etc.). | | afk.observability.projectors | Projection functions that transform run results into RunMetrics. | | afk.observability.exporters | ConsoleRunMetricsExporter and other output formatters. | | afk.observability.backends | Telemetry sink creation (create_telemetry_sink). | **Key imports:** afk.evals Eval suite execution, assertion contracts, budget constraints, and report serialization. **Sub-modules:** | Sub-module | Responsibility | | --------------------- | ------------------------------------------------------------------------------------------ | | afk.evals.suite | run_suite() and arun_suite() entrypoints. | | afk.evals.executor | Single-case execution via run_case() and arun_case(). | | afk.evals.models | EvalCase, EvalCaseResult, EvalSuiteConfig, EvalSuiteResult, EvalAssertionResult. | | afk.evals.contracts | EvalAssertion, AsyncEvalAssertion, EvalScorer protocols. | | afk.evals.budgets | EvalBudget dataclass and evaluate_budget() function. | | afk.evals.reporting | suite_report_payload(), write_suite_report_json(). | | afk.evals.golden | write_golden_trace(), compare_event_types() for deterministic trace comparison. | **Key imports:** afk.mcp Model Context Protocol server implementation, tool store, and server registration. **Sub-modules:** | Sub-module | Responsibility | | ---------------- | --------------------------------------------------------------- | | afk.mcp.server | MCP protocol handler. | | afk.mcp.store | Tool store registry and utility functions for MCP tool loading. | **Key imports:** afk.messaging Protocol-first agent-to-agent messaging exports. **Sub-modules:** | Sub-module | Responsibility | | ------------------------ | ---------------------------------------------------------------------------------------- | | afk.messaging | Public re-exports for A2A contracts, envelopes, auth providers, hosts, and delivery stores. | | afk.agents.a2a | Internal implementation modules for protocol, auth, delivery, hosting, and Google adapter. | **Key imports:**", "token_count": 834} +{"id": "doc_3ae3aaf0c12c", "path": "docs/library/how-to-use-afk.mdx", "url": "/library/how-to-use-afk", "title": "How to Use AFK", "description": "A practical adoption path from first agent to production release.", "headings": ["Phase 1: narrow agent", "Phase 2: tools and safety", "Phase 3: production controls", "Phase 4: release discipline", "Common mistakes", "Next steps"], "content_sha256": "c96092176ab2a302d53853ea5fa4e97e93a3082241ca8210f0aa16ef50e803f4", "content": "Use AFK incrementally. Start with one narrow agent, add tools only when the task needs action, and add production controls before real users depend on the system. Phase 1: narrow agent Build one agent that solves one task without tools. Move on when the agent is reliable on real examples for the narrow task. Phase 2: tools and safety Add typed tools and hard limits. Move on when all tools have Pydantic argument models, cost limits are set, and mutating operations are gated or absent. Phase 3: production controls Before shipping, add the controls that make failures diagnosable: - evals for expected behavior; - telemetry for latency, usage, errors, and tool calls; - persistent memory or queues if runs must survive process restarts; - security controls for sandboxing, secret scope, and tool output limits; - troubleshooting docs for on-call and operators. Useful pages: Test agent behavior and enforce budgets. Export metrics, traces, and run records. Understand policy gates, sandboxing, and secret isolation. Run agents through distributed workers. Phase 4: release discipline Once the agent is in production: - run evals in CI before prompt, tool, or model changes; - compare behavior across releases with golden traces where appropriate; - monitor cost per run and failure rate; - version system prompts in files; - document operator actions for approval, resume, and rollback flows. Common mistakes | Mistake | Better approach | | --- | --- | | Starting with a multi-agent system | Start with one narrow agent and split only when roles are genuinely different | | Writing untyped tools | Use Pydantic argument models for every tool | | Treating prompts as the only safety layer | Add FailSafeConfig, policy gates, sandboxing, and evals | | Hiding internals in public docs | Keep builder docs behavior-first and maintainer docs internals-first | | Shipping without run records | Export telemetry and inspect AgentResult fields | Next steps Read Building with AI for production design patterns, then Troubleshooting for common operational failures.", "token_count": 236} +{"id": "doc_f2ae50c638a4", "path": "docs/library/learn-in-15-minutes.mdx", "url": "/library/learn-in-15-minutes", "title": "Learn AFK in 15 Minutes", "description": "A guided path through agents, tools, streaming, memory, and safety.", "headings": ["1. Agent + runner", "2. Typed tools", "3. Streaming", "4. Memory continuity", "5. Safety limits", "What to read next"], "content_sha256": "1d734acb805aeb148e75286864ddd24fd7e503a8a895b294749e25cf59c849d9", "content": "This tutorial expands the quickstart into the core workflow used by most AFK applications. Each section introduces one concept and keeps the code small enough to copy into a single file. 1. Agent + runner Key point: an agent is declarative. The runner owns execution. 2. Typed tools Key point: tool arguments are validated before your function runs. 3. Streaming Use streaming when a UI or CLI should show progress before the final result is ready. Key point: run_stream() returns an AgentStreamHandle. Consume it to receive text, tool lifecycle events, errors, and the terminal result. 4. Memory continuity Pass the same thread_id to keep conversation context attached to a thread. Key point: thread continuity is explicit. Use the same thread_id for related turns. 5. Safety limits Production agents need hard limits even when prompts and tools are well designed. Key point: limits are part of the agent contract. Set them before shipping. What to read next Agent fields, prompt resolution, subagents, skills, and MCP tools. Tool schemas, context, hooks, middleware, sandboxing, and output limits. Event types, stream handles, cancellation, and UI patterns. Evals, telemetry, security controls, queues, and deployment.", "token_count": 142} +{"id": "doc_5ee8bbdbd8a8", "path": "docs/library/llm-interaction.mdx", "url": "/library/llm-interaction", "title": "LLM Interaction", "description": "How runner and llms runtime exchange requests and responses.", "headings": ["Interaction diagram", "Request construction", "Simplified view of what the runner builds", "Response handling", "LLMRequest fields", "LLMResponse fields", "Streaming interaction", "Error handling at the LLM boundary"], "content_sha256": "1ea841c0c4ab463b019f66f66da07cdc0d9e893b77493001e1f6d45d0dbd18eb", "content": "The runner communicates with LLM providers through a well-defined request/response boundary. Understanding this boundary is important for debugging model behavior, diagnosing latency issues, and implementing custom providers or middleware. This page explains how the runner constructs LLM requests, how responses are interpreted, how streaming works, and how errors are handled at the LLM boundary. Interaction diagram Each step in this flow is described in detail below. Request construction At each step of the agent loop, the runner builds an LLMRequest and sends it to the configured LLM provider. Here is what goes into the request: **Model resolution.** The agent's model field (e.g., \"gpt-4.1-mini\") is resolved through the model resolution chain. This maps the requested model name to a provider, adapter, and normalized model identifier. Custom model_resolver functions can override this mapping. **Message history.** The runner maintains a list of Message objects representing the conversation history. This includes: - A system message containing the agent's instructions, skill manifests, and (when enabled) an untrusted-data preamble. - The initial user message. - assistant messages from previous LLM responses. - tool messages containing the results of tool executions. **Tool definitions.** If the agent has registered tools, they are exported as OpenAI-compatible function tool definitions and included in LLMRequest.tools. The tool_choice is set to \"auto\" so the model can decide whether to call tools. **Session and checkpoint tokens.** For providers that support stateful sessions (like Anthropic's agent SDK), the runner passes session_token and checkpoint_token from previous responses to maintain session continuity. **Metadata.** The request includes metadata about the run (run_id, thread_id, agent_name) and content channel markers that help the provider distinguish trusted system content from untrusted tool output. Response handling The LLM runtime normalizes provider-specific responses into an LLMResponse dataclass. The runner then interprets this response to decide what happens next: **If the response contains no tool calls** (resp.tool_calls is empty), the run is complete. The runner extracts resp.text as final_text, captures any resp.structured_response, and transitions to state=\"completed\". **If the response contains tool calls**, the runner enters the tool execution phase: 1. Each tool call is evaluated by the policy engine. 2. Approved tools are executed (in parallel, up to the batch limit). 3. Tool results are appended to the message history as tool messages. 4. The runner loops back to make another LLM call with the updated history. **Usage tracking.** The LLMResponse.usage field contains token counts (input_tokens, output_tokens, total_tokens). The runner accumulates these into UsageAggregate for cost estimation and budget enforcement. **Session continuity.** If the response includes updated session_token or checkpoint_token, the runner stores these for the next request. LLMRequest fields | Field | Type | Purpose | | --- | --- | --- | | model | str | Normalized model identifier. | | request_id | str | Unique request ID for tracing. | | messages | list[Message] | Conversation history. | | tools | list[dict] or None | OpenAI-format tool definitions. | | tool_choice | str or None | Tool selection strategy (\"auto\", \"none\", or specific tool). | | max_tokens | int or None | Maximum response tokens. | | temperature | float or None | Sampling temperature. | | top_p | float or None | Nucleus sampling parameter. | | stop | list[str] or None | Stop sequences. | | idempotency_key | str or None | Key for request deduplication. | | session_token | str or None | Provider session continuity token. | | checkpoint_token | str or None | Provider checkpoint continuity token. | | timeout_s | float or None | Request-level timeout in seconds. | | metadata | dict | Run context metadata. | LLMResponse fields | Field | Type | Purpose | | --- | --- | --- | | text | str | Model-generated text content. | | tool_calls | list[ToolCall] | Requested tool invocations. | | finish_reason | str or None | Why the model stopped generating (\"stop\", \"tool_calls\", etc.). | | usage | Usage | Token counts: input_tokens, output_tokens, total_tokens. | | structured_response | dict or None | Parsed structured output when using response_model. | | session_token | str or None | Updated session token for next request. | | checkpoint_token | str or None | Updated checkpoint token for next request. | Streaming interaction For real-time UIs, the runner supports streaming via runner.run_stream(). The streaming path works differently from the batch path: The stream produces AgentStreamEvent instances that include: - text_delta -- incremental text (provider stream deltas, or fallback chunking for non-streaming providers). - step_started -- signals a new step in the agent loop. - tool_started / tool_completed -- tool lifecycle events. - error -- error notification. - completed -- terminal event containing the final AgentResult. Error handling at the LLM boundary Errors at the LLM boundary are classified and handled according to the failure policy matrix: **Retryable errors** (timeouts, rate limits, server errors) are retried with exponential backoff. The runner supports a fallback model chain: if the primary model fails after retries, it tries the next model in FailSafeConfig.fallback_model_chain. **Terminal errors** (auth failures, invalid payloads) are not retried. The llm_failure_policy determines what happens next: - \"fail\" -- the run aborts with state=\"failed\". - \"degrade\" -- the run terminates with state=\"degraded\" and the error message as final_text. **Circuit breaker** protection prevents cascading failures. After breaker_failure_threshold consecutive failures to the same model+provider, the circuit opens and subsequent calls fail fast until the cooldown expires. **Policy denial** of LLM calls is handled before the call is made. If the policy engine denies an LLM call, the runner applies the llm_failure_policy without ever contacting the provider.", "token_count": 607} +{"id": "doc_e34fe24f9585", "path": "docs/library/mcp-server.mdx", "url": "/library/mcp-server", "title": "MCP Server", "description": "Expose and consume tools via the Model Context Protocol.", "headings": ["Architecture", "Expose tools via MCP server", "Consume tools from external MCP servers", "Security", "MCP server vs A2A", "Next steps"], "content_sha256": "92cdc3393c943799a3a2ff6409f04605595a4b4e78bc36eb42d27cead0181fe3", "content": "The Model Context Protocol (MCP) is an open standard for sharing tools between AI systems. AFK supports both **exposing** your tools via an MCP server and **consuming** tools from external MCP servers. Architecture Expose tools via MCP server Make your AFK tools available to any MCP-compatible client: Consume tools from external MCP servers Discover and use tools from any MCP server: **MCP tools are transparent.** Once attached to an agent, MCP tools behave exactly like local tools \u2014 same validation, same policy gates, same telemetry. The agent doesn't know (or care) whether a tool is local or remote. Security Require auth tokens for all MCP connections: Configure allowed origins for browser-based clients: Apply policy rules to MCP-exposed tools: Limit request rates per client: **Always authenticate MCP servers in production.** An unauthenticated server exposes your tools to anyone who can reach the endpoint. MCP server vs A2A | Feature | MCP Server | A2A | | --------------- | ---------------------------- | ---------------------------- | | **Shares** | Individual tools | Full agents | | **Protocol** | MCP standard | AFK A2A protocol | | **Use case** | Tool sharing between systems | Agent-to-agent communication | | **Client sees** | Tool schemas and results | Agent responses | | **Interop** | Any MCP client | AFK agents | Next steps Share full agents across systems. Full security architecture.", "token_count": 162} +{"id": "doc_0383dbf277ba", "path": "docs/library/memory.mdx", "url": "/library/memory", "title": "Memory", "description": "Persist conversation state, resume runs, and compact threads.", "headings": ["Quick start: multi-turn conversation", "What gets stored", "State lifecycle", "Resume interrupted runs", "Start a run that might be long", "If interrupted, resume later", "Compact long threads", "Memory backends", "Connection pooling for Redis", "Use the pool with RedisMemoryStore", "Environment-based selection", "Custom backends", "Long-term memory", "Store a long-term memory", "Retrieve memories", "Vector search", "Search by embedding similarity", "Text search", "Design guidelines", "Next steps"], "content_sha256": "6df7c441a722d1ed17a8489a0f085e0a05f8e9dd2829cad72addc5a7a93bfec1", "content": "AFK's memory system persists conversation state across runs. Use it for multi-turn conversations, run resumption after interrupts, long-term knowledge retention, and vector-based semantic search. Quick start: multi-turn conversation The thread_id links runs into a conversation. AFK automatically persists messages between runs. What gets stored | Record type | What it contains | When it's written | | ------------------ | ---------------------------------------------------------- | --------------------------------------- | | **Event** | User messages, assistant responses, tool calls and results | After each run step | | **Checkpoint** | Full run state at a point in time | At step boundaries (pre-LLM, post-tool) | | **State (KV)** | Checkpoint pointers, effect journal, background tool state | During and after runs | | **Long-term memory** | Persistent knowledge with optional embeddings | Via upsert_long_term_memory | **What's NOT stored automatically:** Raw LLM provider responses or internal framework temporaries. Only conversation-visible records and explicit state writes are persisted. State lifecycle Resume interrupted runs If a run is interrupted (crash, timeout, pause for approval), resume from the last checkpoint: **Checkpoints are written at key boundaries:** before each LLM call, after each tool batch, and after each step completes. On resume, completed tool calls are replayed from the effect journal \u2014 no duplicate side effects. Compact long threads Over time, conversation threads grow and consume tokens. Use compaction to trim old events: Compaction applies retention rules: protected event types are preserved first, then the most recent remaining events fill the budget. Memory backends AFK ships with four backends. All implement the MemoryStore protocol. State lives in process memory. Fast, no setup, but lost on restart. **Use for:** Development, testing, short-lived scripts. Persistent local storage with JSON serialization and local vector search. Features: WAL mode, text search, vector similarity search (cosine), atomic upsert. **Use for:** Local development with persistence, single-process deployments. Production-grade backend with pgvector support for vector search. **Use for:** Production multi-process deployments. In-memory store backed by Redis for shared state across processes. **Use for:** Shared state across workers, ephemeral but durable-enough sessions. Connection pooling for Redis For production Redis deployments, use connection pooling for better performance: Environment-based selection Set environment variables to auto-select a backend without code changes: The runner falls back to in-memory if the configured backend fails to initialize. Custom backends Implement the MemoryStore abstract class to add support for any database: Declare capabilities to tell the framework which features your backend supports. Features like vector search are only used when the backend declares support. Long-term memory Beyond conversation events, AFK supports persistent long-term memories scoped per user and purpose: Vector search Backends that support vector search (SQLite, Postgres) can find semantically similar memories: Text search All backends support basic text search across memory content: Design guidelines - **Always use thread_id for conversations.** Without it, each run starts fresh. - **Compact threads proactively.** Don't wait until you hit token limits. A good rule: compact when the thread exceeds ~500 events. - **Use checkpoints for long-running agents.** If a run might take minutes, checkpoints let you resume on failure. - **Don't store secrets in memory.** Thread events are persisted and may be readable. - **Choose the right backend.** In-memory for dev, SQLite for local persistence, Postgres/Redis for production. - **Use scopes for long-term memory.** Organize memories by purpose (preferences, knowledge, history) to keep queries efficient. Next steps Resume and compact APIs on the Runner. Template prompts with context from memory.", "token_count": 430} +{"id": "doc_50ac650fd8f6", "path": "docs/library/mental-model.mdx", "url": "/library/mental-model", "title": "Mental Model", "description": "Three coordinated loops that drive every AFK agent.", "headings": ["The three loops", "Think in contracts", "Decision tree: how complex should my system be?", "What success looks like"], "content_sha256": "cf294e0c707755bfe6bfc555054eaefbfb62e65f3ea92ecd9e2b4cfec5bb792c", "content": "AFK agents execute through three coordinated loops \u2014 **Decision**, **Execution**, and **Assurance**. Understanding these loops helps you reason about agent behavior, debug issues, and design systems that scale predictably. The three loops The Decision Loop is the model's turn. On each step: 1. The runner sends the conversation history + tool schemas to the LLM 2. The LLM decides whether to respond with text (done) or request tool calls (continue) 3. If tool calls are requested, they flow to the Execution Loop **You control this with:** agent instructions, model choice, tool availability The Execution Loop handles every tool call: 1. **Validate** arguments against the Pydantic schema 2. **Policy gate** \u2014 allow, deny, or defer for human approval 3. **Execute** the handler (with hooks and middleware) 4. **Sanitize** the output (truncate, strip injection vectors) 5. **Return** the result to the Decision Loop **You control this with:** tool definitions, policy rules, sandbox profiles The Assurance Loop runs continuously, enforcing limits on both other loops: - **Step count** \u2014 stops the agent after N iterations - **Tool call count** \u2014 prevents excessive tool usage - **Cost budget** \u2014 stops if estimated cost exceeds the limit - **Wall time** \u2014 hard timeout on the entire run - **Failure classification** \u2014 retryable, terminal, or non-fatal **You control this with:** FailSafeConfig Think in contracts AFK is built on a contract-first design. Every interaction between components is defined by typed data structures: | Boundary | Contract | What flows | | ------------------ | ---------------------------------------------------- | --------------------------------------- | | Runner \u2192 LLM | LLMRequest / LLMResponse | Messages, tool schemas, model responses | | Runner \u2192 Tool | ToolCall / ToolResult | Validated arguments, execution output | | Runner \u2192 Subagent | AgentInvocationRequest / AgentInvocationResponse | Delegate task and receive result | | Runner \u2192 Memory | Checkpoint records | Conversation state for resume/replay | | Runner \u2192 Telemetry | AgentRunEvent, RunMetrics | Spans, metrics, audit trail | **Contracts are Pydantic models.** This means every boundary is validated at runtime \u2014 malformed data causes clear errors, not silent bugs. When you see a validation error, it's AFK telling you exactly where the contract was violated. Decision tree: how complex should my system be? Not sure what to build? Start at the top and follow the path that matches your use case. > [!TIP] > **Start at Level 1.** Only move up when you have clear evidence that your current level isn't enough. Each level adds complexity that you need to manage and test. What success looks like A mature AFK implementation exhibits these properties: - **Every tool has a Pydantic model** \u2014 no untyped arguments - **Every run has cost limits** \u2014 max_total_cost_usd is always set - **Policy gates protect mutations** \u2014 dangerous actions require approval - **Evals cover core behaviors** \u2014 regression tests catch prompt drift - **Observability is on from day one** \u2014 even if it's just the console exporter - **Failures are classified** \u2014 the system knows what to retry and what to abort", "token_count": 325} +{"id": "doc_75887f91c7df", "path": "docs/library/messaging.mdx", "url": "/library/messaging", "title": "Internal Messaging", "description": "Agent-to-agent messaging with delivery guarantees and idempotency.", "headings": ["Message lifecycle", "The InternalA2AEnvelope", "Delivery behavior", "Idempotency and correlation", "All messages in this workflow share the same correlation_id", "Delivery store backends", "Next steps"], "content_sha256": "e8d90b294c3ed2ebbbff257fa28d0be7ba2cce6d689a3161f2306ddf95ba1ee2", "content": "AFK's internal messaging system lets agents communicate via structured envelopes with **at-least-once delivery** and **idempotency**. Use it when agents in the same system need to exchange data reliably. Message lifecycle The InternalA2AEnvelope Every message is wrapped in a typed envelope: | Field | Type | Purpose | | ----------------- | ---------- | -------------------------------------------- | | message_type | str | request, response, or event | | run_id | str | Run identifier for tracing | | thread_id | str | Memory thread identifier | | conversation_id | str | Cross-run conversation identifier | | payload | dict | Message data (any JSON-serializable content) | | correlation_id | str | Groups related messages in a workflow | | idempotency_key | str | Deduplication \u2014 same key = same message | | source_agent | str | Name of the sending agent | | target_agent | str | Name of the receiving agent | | metadata | dict | JSON-safe tracing or routing metadata | | timestamp_ms | int | Creation timestamp in milliseconds | Delivery behavior 1. Sender creates and submits the envelope 2. Delivery store checks the idempotency key (rejects duplicates) 3. Message is delivered to the receiver 4. Receiver processes and ACKs 5. Store marks as delivered **Result:** Message processed exactly once. 1. Delivery fails (receiver timeout, transient error) 2. Store schedules retry with exponential backoff 3. Message is redelivered (same idempotency key) 4. Receiver processes and ACKs on retry **Result:** At-least-once delivery. Receiver must be idempotent. 1. All retry attempts exhausted 2. Message moves to dead-letter queue 3. Alert generated (if configured) **Result:** Message is not lost \u2014 it's in the DLQ for manual review. Idempotency and correlation **Always set an idempotency_key.** Without it, retry deliveries can cause duplicate processing. Use a deterministic key derived from the task context (e.g., f\"{task_id}-{step_name}\"). **Correlation IDs** group related messages across a workflow. When debugging, filter by correlation_id to see the full message chain for a task. Delivery store backends Default. Fast, no setup. State lost on restart. Implement the DeliveryStore protocol for durable messaging: Next steps Cross-system agent communication. Async job processing with queue backends.", "token_count": 226} +{"id": "doc_5a65ddab1445", "path": "docs/library/migration.mdx", "url": "/library/migration", "title": "Migration Guide", "description": "Move from LangChain, OpenAI Assistants, or custom agents to AFK.", "headings": ["From LangChain", "LangChain \u2192 AFK concepts", "Basic agent migration", "Key differences", "LangChain: agent is callable", "AFK: Agent defines what, Runner executes how", "LangChain: function signature and docstring", "AFK: Pydantic model for typed arguments", "LangChain: single run() method", "AFK: explicit sync, async, or streaming", "Tool migration", "Simple tool", "Structured tool with custom logic", "Simple tool", "Tool with constraints", "Memory migration", "Configure memory backend", "Use thread_id for conversation continuity", "Callback \u2192 Middleware migration", "RAG migration", "Store documents with embeddings", "Retrieve in tool", "From OpenAI Assistants API", "Assistants \u2192 AFK concepts", "Basic migration", "Key advantages of AFK over Assistants API", "From custom agent code", "Common patterns migration", "Before: Custom retry implementation", "Before: Custom circuit breaker", "Next steps"], "content_sha256": "5918b39a7bcd88e65ba3aba1c07491fdab8b878f8eb6dea163cffe3b02ab8b7f", "content": "This guide helps you migrate existing agent code from other frameworks to AFK. From LangChain LangChain \u2192 AFK concepts | LangChain Concept | AFK Equivalent | Key Difference | |-------------------|----------------|----------------| | ChatOpenAI | LLMBuilder | Provider-portable, typed contracts | | Agent | Agent | Config object, not runtime | | Tool | @tool decorator | Pydantic-based, typed arguments | | Chain | Runner | Explicit execution loop | | Memory | MemoryStore | Multiple backends, checkpointing | | Callback | Middleware / Hooks | Request/response interception | | LangSmith | Telemetry | Built-in OTEL support | Basic agent migration **LangChain:** **AFK:** Key differences **1. Agent is a config object, not a runtime:** **2. Tools use Pydantic for validation:** **3. Explicit execution modes:** Tool migration **LangChain tools:** **AFK equivalents:** Memory migration **LangChain memory:** **AFK memory:** Callback \u2192 Middleware migration **LangChain callbacks:** **AFK middleware:** RAG migration **LangChain retrieval:** **AFK approach:** From OpenAI Assistants API Assistants \u2192 AFK concepts | OpenAI Concept | AFK Equivalent | |----------------|----------------| | Assistant | Agent | | Thread | Memory + thread_id | | Run | Runner execution | | Message | MemoryEvent | | Tool | @tool decorator | | Function | @tool with Pydantic | | File search | Long-term memory + vector search | Basic migration **OpenAI Assistants:** **AFK:** Key advantages of AFK over Assistants API 1. **Local execution** \u2014 No API calls needed for simple tasks 2. **Portable** \u2014 Switch LLM providers without code changes 3. **Debuggable** \u2014 Step through agent logic locally 4. **Testable** \u2014 Run evals locally in CI 5. **Controllable** \u2014 Full access to prompts, tools, and behavior From custom agent code Common patterns migration **Custom retry logic:** **AFK:** **Custom circuit breaker:** **AFK:** Next steps Build your first AFK agent in 5 minutes. Understand AFK's design philosophy. Complete API documentation. Runnable examples for every feature.", "token_count": 208} +{"id": "doc_78a8125d2c7e", "path": "docs/library/observability.mdx", "url": "/library/observability", "title": "Observability", "description": "Built-in telemetry with spans, metrics, and exporters.", "headings": ["Setup in one line", "Console output (development)", "OpenTelemetry (production)", "JSON lines (log files)", "Telemetry pipeline", "RunMetrics reference", "Choosing an exporter", "Telemetry spans", "Alerting recommendations", "Next steps"], "content_sha256": "125c3f894dc8006a91e85aa44be8559452e56c5c7984b6cc2fab73dc99763e43", "content": "AFK includes a telemetry pipeline that captures every agent run event \u2014 LLM calls, tool executions, state transitions, and performance metrics. Export to the console for development, JSON for log aggregation, or OpenTelemetry for production. Setup in one line Telemetry pipeline RunMetrics reference Every completed run produces a RunMetrics object: | Metric | Type | Description | | --------------------- | ------- | ---------------------------------- | | total_steps | int | Number of agent loop iterations | | total_llm_calls | int | Number of LLM API calls | | total_tool_calls | int | Number of tool executions | | total_tokens | int | Total tokens (prompt + completion) | | total_cost_usd | float | Estimated cost in USD | | wall_time_s | float | Total run duration in seconds | | first_token_ms | float | Time to first token (streaming) | | tool_latency_p50_ms | float | Median tool execution latency | | tool_latency_p99_ms | float | 99th percentile tool latency | | error_count | int | Number of errors during the run | Choosing an exporter Human-readable output to stdout. Best for development and debugging. Structured output for log aggregation (ELK, Datadog, etc.). Each event is a JSON line: Export spans and metrics to any OTEL-compatible backend (Jaeger, Grafana, Honeycomb, etc.). **Use for:** Production deployments with existing observability infrastructure. Telemetry spans AFK creates spans for key operations: Spans capture timing, success/failure, and metadata. In OTEL mode, these map directly to traces visible in your observability dashboard. Alerting recommendations | Alert | Condition | Severity | | -------------------- | -------------------------------------------- | ---------- | | Error rate spike | error_count / total_runs > 5% over 5 min | **High** | | LLM latency spike | p99 > 10s for 5 min | **Medium** | | Cost anomaly | daily_cost > 2x rolling average | **High** | | Tool failure rate | tool_failures / tool_calls > 10% for 5 min | **Medium** | | Circuit breaker open | Any LLM circuit breaker trips | **High** | **Start with the console exporter** even in staging. It costs nothing and gives you immediate visibility into agent behavior. Switch to OTEL when you have a monitoring stack. Next steps Behavioral testing for agent quality. Security boundaries and hardening.", "token_count": 223} +{"id": "doc_f18f523b6ecc", "path": "docs/library/overview.mdx", "url": "/library/overview", "title": "What is AFK?", "description": "The short mental model for building and maintaining AFK agents.", "headings": ["The three core objects", "When AFK is a good fit", "Builder path", "Maintainer path", "Source-of-truth rules", "Next steps"], "content_sha256": "3d4dbe025de523cf7ac36a6ca13721c14fd9857f6f45bb8f3acebae1cb31452a", "content": "AFK is an agent runtime for Python applications. It is designed for teams that need agent behavior to be typed, observable, testable, and bounded by explicit controls. The core idea is simple: The three core objects | Object | What it owns | What it does not own | | --- | --- | --- | | Agent | Name, model, instructions, tools, subagents, skills, MCP servers, defaults, fail-safe limits | Network calls, event loops, persistence, telemetry export | | Runner | Execution loop, tool dispatch, streaming, memory, checkpointing, policy/HITL, telemetry | Agent identity or instructions | | AgentResult | Final text, terminal state, tool/subagent records, usage aggregate, cost, run/thread ids | Future execution | This separation is the main design rule. Agents describe behavior. Runners execute behavior. Runtime subsystems provide capabilities. When AFK is a good fit Use AFK when your agent will: - call tools or external systems; - run for more than one step; - need cost, time, step, or tool-call limits; - stream progress to a UI; - persist memory or resume a run; - require evals, telemetry, or production incident debugging; - coordinate specialist subagents. A direct LLM provider SDK may be simpler for one-off single-turn text generation. AFK is useful when the agent needs operational structure. Builder path If you are building an app with AFK, read in this order: 1. Quickstart for the smallest complete agent. 2. Learn AFK in 15 Minutes for the guided path. 3. Agents, Runner, and Tools for the core building blocks. 4. Building with AI, Evals, and Observability before production. Maintainer path If you are changing AFK itself, read in this order: 1. Developer Guide for local setup, commands, and docs workflow. 2. Architecture for package boundaries. 3. Public API Rules before changing exports or examples. 4. Tested Behaviors before changing runner, tools, LLM runtime, memory, queues, or telemetry. Source-of-truth rules - Public examples import from afk.*, never src.afk.*. - Runner is imported from afk.core. - User-facing docs should explain behavior before internals. - Maintainer docs may reference internal modules, but must identify the public contract affected by a change. - Generated agent-facing docs and skill indexes must be refreshed when navigation, examples, or skill references change. Next steps Build one agent and one typed tool. Set up the repo and understand the contributor workflow.", "token_count": 272} +{"id": "doc_f5f256549d84", "path": "docs/library/performance.mdx", "url": "/library/performance", "title": "Performance", "description": "Improve AFK latency, throughput, and cost without relying on internal APIs.", "headings": ["Latency", "Tool execution", "Throughput", "Cost", "Memory", "Measurement", "Checklist"], "content_sha256": "4ded523bc48148cc20cacc7af3c5ac32a07a19a6376e3d8d69bb08c2aa878c6f", "content": "Performance work in AFK usually comes from four levers: choosing the right model, reducing unnecessary tool/LLM calls, keeping memory bounded, and moving long-running work into queues. Latency Use the smallest model that can reliably handle the task, and reserve larger models for tasks that need deeper reasoning. Other latency controls: - keep system prompts short and specific; - make I/O-bound tools async; - avoid tools for information already present in context; - stream user-facing runs with runner.run_stream(...); - set tight max_steps, max_llm_calls, and max_wall_time_s limits. Tool execution Tools are often the slowest part of a run. Keep them typed, narrow, and bounded. Tool guidance: - validate inputs with Pydantic models; - enforce timeouts in external clients; - return compact JSON-safe payloads; - truncate or summarize large external responses before returning them; - use RunnerConfig(tool_output_max_chars=...) as a final bound. Throughput Use async runner APIs for services and workers. For durable background work, use task queues instead of keeping HTTP requests open. See Task Queues. Cost Set cost and loop limits on every production agent. Read cost from the terminal result: Memory Long threads increase prompt size and storage. Use explicit thread ids and compact retained state when threads grow. Choose the memory backend by deployment shape: | Backend | Use case | | --- | --- | | In-memory | Tests and local experiments | | SQLite | Single-process local or small deployments | | Redis | Shared state across processes | | Postgres | Persistent production storage and vector search | Configure backends with environment variables or pass a public MemoryStore implementation to Runner(memory_store=...). Measurement Measure from AgentResult first: For production, export telemetry through Observability and track latency, token usage, tool failures, degraded runs, and cost per run. Checklist - Use async runner APIs in servers and workers. - Stream user-facing runs. - Keep prompts and tool outputs compact. - Set fail-safe limits and cost budgets. - Compact long-running threads. - Move durable background work into queues. - Monitor token usage, tool count, state, and cost per run.", "token_count": 250} +{"id": "doc_3eeb75d383b8", "path": "docs/library/public-imports-and-function-improvement.mdx", "url": "/library/public-imports-and-function-improvement", "title": "Public API Rules", "description": "Maintainer rules for preserving AFK's public import contract.", "headings": ["Contract", "Rules for maintainers", "Preferred examples", "Imports to avoid in public docs", "Change checklist", "Search commands"], "content_sha256": "4ad0dbc7fa46f0049e5638a702fa2d73dbf1da35cb09099c277e41dd1fefbabc", "content": "This page is for AFK maintainers. It explains how to change public exports without making downstream code or docs confusing. For the user-facing import table, see API Reference. Contract The public API is the set of names exported by package-level __init__.py files: - afk.agents - afk.core - afk.tools - afk.llms - afk.memory - afk.queues - afk.mcp - afk.messaging - afk.observability - afk.evals Public docs and examples should import from these package surfaces. They should not use src.afk imports or deep implementation modules such as afk.core.runner.api. Rules for maintainers 1. If a downstream user should import a symbol, export it from the package-level __init__.py. 2. If a symbol is not exported, do not use it in builder docs or examples. 3. Keep Agent and Runner separate: Agent comes from afk.agents; Runner comes from afk.core. 4. Prefer protocols, dataclasses, Pydantic models, and explicit error classes for public contracts. 5. When removing or renaming a public symbol, update migration docs and tests in the same change. 6. When changing a public constructor, update API Reference, Configuration Reference, examples, and generated agent-facing docs. Preferred examples Imports to avoid in public docs | Avoid | Prefer | | --- | --- | | src.afk.agents.Agent | afk.agents.Agent | | afk.agents.core.base.Agent | afk.agents.Agent | | afk.core.runner.api.RunnerAPIMixin | afk.core.Runner | | afk.tools.core.decorator.tool | afk.tools.tool | | afk.llms.builder.LLMBuilder | afk.llms.LLMBuilder | Deep imports are acceptable in internal tests only when the test is specifically covering an internal unit. Integration tests and examples should exercise the public surface. Change checklist Before merging a public API change: - Update the relevant package __all__. - Add or update tests that import through the public package. - Update user-facing docs if a builder would see the changed behavior. - Update maintainer docs if an invariant or subsystem boundary changed. - Run PYTHONPATH=src pytest -q or targeted tests for the affected subsystem. - Regenerate agent-facing docs with ./scripts/build_agentic_ai_assets.sh when docs, examples, skill metadata, or navigation changes. Search commands The last command intentionally finds deep imports for review. Some maintainer references may be valid, but builder docs should avoid them.", "token_count": 277} +{"id": "doc_bf91f8d5da74", "path": "docs/library/quickstart.mdx", "url": "/library/quickstart", "title": "Quickstart", "description": "Build one AFK agent with one typed tool.", "headings": ["Prerequisites", "1. Define an agent", "2. Add one typed tool", "3. Read the result", "4. Keep going"], "content_sha256": "898fc17c3d87708f8ce6850453a470a6fad5eb53aa22488f66cd3b31e8ed4886", "content": "This page is the shortest useful AFK path: install the package, define an agent, attach one typed tool, and run it. Prerequisites - Python 3.13+ - An LLM provider key, such as OPENAI_API_KEY When working from this repository instead of an installed package: 1. Define an agent Agent stores configuration. Runner executes the run. AgentResult.final_text is the assistant response. 2. Add one typed tool Tools are Python functions with Pydantic argument models. AFK turns the model into a tool schema, validates model-provided arguments, executes the function, and feeds the result back into the agent loop. 3. Read the result Common fields on AgentResult: | Field | Meaning | | --- | --- | | final_text | Final assistant text | | state | Terminal state such as completed, failed, cancelled, or degraded | | run_id | Unique id for this run | | thread_id | Conversation/thread id used by memory | | tool_executions | Ordered records for tool calls | | subagent_executions | Ordered records for subagent calls | | usage_aggregate | Aggregated token usage | | total_cost_usd | Estimated total run cost when available | 4. Keep going Add streaming, memory, and safety controls. Find complete snippets for common scenarios. Understand the agent configuration object. Understand sync, async, and streaming execution.", "token_count": 135} +{"id": "doc_f54217a0f2cf", "path": "docs/library/run-event-contract.mdx", "url": "/library/run-event-contract", "title": "Run Event Contract", "description": "Runtime event schema and event consumption guidance.", "headings": ["Event stream model", "Event reference", "AgentRunEvent structure", "Consuming events", "Pattern: event-type branching", "Forward compatibility"], "content_sha256": "a4e2b6d7119af19a8eafb23eb7f8f84bf514594c4d2dd076e5bb6983d4097b19", "content": "Every AFK agent run produces a stream of AgentRunEvent instances that describe what happened during execution. These events form the run's audit trail -- they tell you when the run started, when LLM calls were made, when tools executed, when policy decisions were rendered, and how the run terminated. Understanding the event contract is essential for building real-time UIs, logging pipelines, eval assertions, and debugging tools. This page documents every event type, explains when each fires, describes the data it carries, and provides patterns for consuming events safely. Event stream model Every run begins with run_started and ends with exactly one terminal event: run_completed, run_failed, run_interrupted, or run_cancelled. Between those boundaries, the runner emits step, LLM, tool, policy, and subagent events in the order they occur. Event reference | Event Type | When It Fires | Key Fields in data | | --- | --- | --- | | run_started | At the beginning of a new or resumed run. | agent_name, resumed (bool) | | step_started | At the start of each step iteration. | step (int), state | | llm_called | Before sending a request to the LLM provider. | model, provider | | llm_completed | After receiving the LLM response. | tool_call_count, finish_reason, text | | tool_batch_started | Before executing a batch of tool calls from one LLM response. | tool_call_count, tool_names, tool_call_ids | | tool_completed | After each individual tool finishes executing. | tool_name, tool_call_id, success (bool), output, error, agent_name, agent_depth, agent_path | | tool_deferred | Tool accepted and moved to background processing. | tool_name, tool_call_id, ticket_id, status, summary, resume_hint | | tool_background_resolved | Deferred tool completed successfully. | tool_name, tool_call_id, ticket_id, output | | tool_background_failed | Deferred tool failed or expired. | tool_name, tool_call_id, ticket_id, error | | policy_decision | After the policy engine evaluates a tool, LLM, or subagent event. | event_type, action, reason, policy_id, matched_rules | | subagent_started | Before dispatching a subagent invocation. | subagent_name, correlation_id | | subagent_completed | After a subagent finishes (success or failure). | subagent_name, success, latency_ms, error (if failed) | | text_delta | During streamed runs when incremental model text arrives. | delta | | warning | When a non-fatal issue occurs (memory fallback, dead letters, etc.). | Varies by warning type | | run_completed | When the run reaches successful terminal state. | None (terminal) | | run_failed | When the run terminates due to an unrecoverable error. | error message in event.message | | run_interrupted | When the run is interrupted by the caller or by timeout. | Interruption reason in event.message | | run_cancelled | When the run is cancelled before completion. | None (terminal) | AgentRunEvent structure Each event is an AgentRunEvent dataclass with the following fields: | Field | Type | Description | | --- | --- | --- | | type | str | Event type identifier (see table above). | | run_id | str | Unique run identifier. | | thread_id | str | Thread identifier for memory continuity. | | state | str | Current run state when the event was emitted. | | step | int | Current step number (0 if not yet in a step). | | message | str or None | Human-readable description of what happened. | | data | dict or None | Structured payload with event-specific fields. | Consuming events The primary way to consume events is through the run handle's events async iterator: Pattern: event-type branching The recommended consumption pattern is a simple if/elif chain that branches on event.type. This is explicit, readable, and easy to extend: Forward compatibility The event contract is append-only. New event types may be added in future releases, but existing event types will not be removed or have their data fields changed. Your event consumer should always handle unknown event types gracefully. The simplest approach is a default branch that logs or ignores unknown types: This ensures your code continues to work when AFK adds new event types without requiring a code update.", "token_count": 434} +{"id": "doc_567be1281e2f", "path": "docs/library/security-model.mdx", "url": "/library/security-model", "title": "Security Model", "description": "Security boundaries, policy engine, and production hardening.", "headings": ["Security boundaries", "Default posture", "Production hardening checklist", "Secret isolation", "Threat model overview", "Next steps"], "content_sha256": "9c2f919368fe1ccad33c5509761ad84c85f2b3ccee485a59fc99402ad5ef9694", "content": "AFK implements security through **four boundaries** \u2014 policy engine, tool runtime, A2A/MCP bridges, and sandbox. Each boundary enforces least-privilege defaults and requires explicit opt-in for elevated permissions. Security boundaries Gate tool calls and agent actions with configurable rules. **Actions:** allow (default), deny, request_approval, request_user_input Every tool call passes through validation, policy checks, and output sanitization. **Sandbox profiles** are configured at the runner level, not per-tool: External communication requires authentication and per-caller authorization. Hard limits prevent runaway agents. Default posture AFK defaults to **least privilege**: | Setting | Default | Meaning | | ------------------------ | ---------------------------------------------------------------------------- | ------------------------------------- | | Tool policy | allow | Tools run unless explicitly denied | | Tool output sanitization | True | Output is sanitized by default | | A2A authentication | Required | No unauthenticated A2A | | MCP authentication | Required | No unauthenticated MCP | | Cost limits | None ( ) | **You must set max_total_cost_usd** | | Sandbox | None | Tools run in the host process | **Cost limits are not set by default.** Always configure max_total_cost_usd in production to prevent runaway spending. Production hardening checklist | Area | Action | Status | | -------------- | ----------------------------------------------- | ----------------------------------------- | | **Cost** | Set max_total_cost_usd on all agents | | | **Cost** | Set max_steps and max_tool_calls | | | **Policy** | Add deny rules for admin/destructive tools | | | **Policy** | Add request_approval for mutating operations | | | **Tools** | Enable sanitize_tool_output=True | | | **Tools** | Set tool_output_max_chars | | | **Tools** | Use sandbox profiles for code execution | | | **A2A/MCP** | Configure auth providers with valid tokens | | | **A2A/MCP** | Set per-caller agent access lists | | | **A2A/MCP** | Enable rate limiting | | | **Secrets** | Store API keys in environment variables | | | **Secrets** | Use secret scope isolation per tool call | | | **Monitoring** | Configure telemetry exporter (OTEL) | | | **Monitoring** | Set up alerts for error rate and cost anomalies | | Secret isolation AFK recommends isolating secrets at the environment level. Use separate environment scopes and the runner's ToolContext.metadata to control which credentials are available to each tool: Threat model overview | Threat | Mitigation | | ----------------------- | ------------------------------------------- | | **Prompt injection** | Output sanitization, input validation | | **Runaway agents** | Cost limits, step limits, wall time | | **Tool abuse** | Policy engine, sandbox profiles | | **Unauthorized access** | A2A/MCP auth, per-caller authorization | | **Secret leakage** | Secret scope isolation, output sanitization | | **Cost explosion** | max_total_cost_usd, circuit breakers | Next steps How errors flow through the system. Production playbook and anti-patterns.", "token_count": 288} +{"id": "doc_1e46cbc6bfae", "path": "docs/library/snippets/01_minimal_chat_agent.mdx", "url": "/library/snippets/01_minimal_chat_agent", "title": "01: Minimal Chat Agent", "description": "Smallest synchronous AFK agent run.", "headings": ["Line-by-line explanation", "What AgentResult contains", "Expected behavior"], "content_sha256": "1232b15cabfce1a8b2463d495eb4dca40efebc440a22e456b95cb6e2a4af8225", "content": "This is the simplest possible AFK agent. It demonstrates the three core concepts you need to get started: defining an Agent with a model and instructions, creating a Runner to execute it, and reading the result from final_text. If you are new to AFK, start here. Every other example builds on this foundation. Line-by-line explanation **Agent(...)** defines the agent's identity and behavior. The name is used for telemetry and logging. The model specifies which LLM to use. The instructions become the system prompt that guides the model's behavior. **Runner()** creates the execution engine. With no arguments, it uses in-memory defaults: headless interaction mode, no telemetry sink, and no policy engine. This is the fastest way to get started during development. **runner.run_sync(...)** executes the agent synchronously, blocking until the run completes. Under the hood, this creates an async event loop, runs the agent through the full lifecycle (LLM call, optional tool execution, optional subagent delegation), and returns the terminal AgentResult. The user_message is the initial prompt sent to the model. **result.final_text** contains the model's final text response. This is the primary output field on AgentResult. Always use final_text (not output_text) to access the agent's response. What AgentResult contains The AgentResult dataclass returned by run_sync includes: | Field | Type | Description | | --- | --- | --- | | final_text | str | The agent's final text response. | | state | str | Terminal state: \"completed\", \"failed\", \"cancelled\", or \"degraded\". | | run_id | str | Unique identifier for this run. | | thread_id | str | Thread identifier for memory continuity across runs. | | tool_executions | list | Records of all tool calls made during the run. | | subagent_executions | list | Records of all subagent invocations. | | usage | UsageAggregate | Token usage and cost estimates across all LLM calls. | Expected behavior When you run this example, the runner makes a single LLM call to gpt-4.1-mini with the system instructions and user message. Since no tools are registered, the model responds with text only. The run completes in one step with state=\"completed\", and final_text contains a concise definition of error budgets in SRE. No network calls, API keys, or external services are required beyond access to the specified LLM provider (configured via environment variables like OPENAI_API_KEY).", "token_count": 251} +{"id": "doc_d6ad61a79371", "path": "docs/library/snippets/02_policy_with_hitl.mdx", "url": "/library/snippets/02_policy_with_hitl", "title": "02: Policy with Human Interaction", "description": "Route sensitive actions through policy and human approval.", "headings": ["Basic example", "Define a policy that gates destructive operations", "Headless mode: approval requests are auto-resolved using approval_fallback", "In headless mode with approval_fallback=\"deny\", the destructive action is blocked.", "Interactive mode with an InteractionProvider", "In-memory provider for testing (simulates human approval)", "In a real application, a separate process or UI would call:", "provider.resolve_approval(request_id, ApprovalDecision(kind=\"allow\"))", "Headless vs interactive modes", "How the policy flow works", "Policy decision actions"], "content_sha256": "e2c2e11ab5f307f4f1a3008e902cbd49a39845c3d0f0c1a792cf3068c1581a5f", "content": "Human-in-the-loop (HITL) is the pattern where the agent pauses execution to request approval or input from a human operator before proceeding with a sensitive action. This is critical for any agent that can take destructive or irreversible actions -- deleting data, modifying production systems, sending communications, or spending money. AFK implements HITL through two components: a PolicyEngine that decides which actions require human intervention, and an InteractionProvider that routes the approval request to a human and returns their decision. Basic example Interactive mode with an InteractionProvider In production, you typically want a real human to review approval requests. Use interaction_mode=\"interactive\" with a custom InteractionProvider: Headless vs interactive modes AFK supports three interaction modes, configured via RunnerConfig.interaction_mode: | Mode | Behavior | When to Use | | --- | --- | --- | | \"headless\" | Approval requests are auto-resolved using approval_fallback (default: \"deny\"). No human is involved. | CI/CD pipelines, batch processing, testing, automated workflows where no human is available. | | \"interactive\" | Approval requests are routed to the configured InteractionProvider. The runner pauses until a decision is returned or approval_timeout_s expires. | Production applications with human operators, chat UIs with approval buttons, Slack-based approval workflows. | | \"external\" | Similar to interactive, but designed for use in external orchestration systems where the approval mechanism is managed outside AFK. | Enterprise systems with external approval platforms. | How the policy flow works 1. The agent calls a tool (e.g., drop_table). 2. Before executing, the runner sends a PolicyEvent to the PolicyEngine. 3. The engine evaluates all rules. If a rule matches, it returns a PolicyDecision with the configured action. 4. If the action is request_approval: - In **headless** mode: the runner auto-resolves using approval_fallback. - In **interactive** mode: the runner creates an ApprovalRequest and sends it to the InteractionProvider. Execution pauses until the provider returns an ApprovalDecision. 5. If approved (kind=\"allow\"): the tool executes normally. 6. If denied (kind=\"deny\"): the tool execution is skipped and the model receives a denial message as the tool result. 7. A policy_decision event is emitted in the run event stream for audit purposes. Policy decision actions | Action | Effect | | --- | --- | | \"allow\" | Proceed with execution. No human interaction needed. | | \"deny\" | Block execution immediately. The denial reason is reported to the model. | | \"request_approval\" | Pause and request human approval through the InteractionProvider. | | \"request_user_input\" | Pause and request freeform text input from a human operator. |", "token_count": 262} +{"id": "doc_a2e84472f1f9", "path": "docs/library/snippets/03_subagents_with_router.mdx", "url": "/library/snippets/03_subagents_with_router", "title": "03: Subagents with Router", "description": "Delegate workload to specialist subagents and merge outputs.", "headings": ["Delegation flow", "Example", "Define specialist subagents", "Define the coordinator agent", "How the coordinator pattern works", "What subagent_executions contains", "Subagent failure handling"], "content_sha256": "40c49cfa7a9868920008a50344415cbed7990803b125758a43cb66376458cff8", "content": "When a task is too complex for a single agent, AFK supports delegating subtasks to specialist subagents. The coordinator (or \"lead\") agent decides what to delegate, and the runner handles dispatching work to subagents, collecting their results, and feeding those results back to the coordinator for synthesis. This pattern is useful for incident response, research workflows, content pipelines, and any scenario where different aspects of a task require distinct expertise or instructions. Delegation flow Example How the coordinator pattern works 1. The **lead agent** receives the user message and decides how to delegate. It can invoke subagents through tool-like calls that the runner intercepts. 2. The **runner** dispatches each subagent invocation as a separate run. Subagents execute independently with their own instructions and model configuration. The runner manages concurrency, timeout, and failure handling for each subagent. 3. **Subagent results** are returned to the lead agent as execution records. Each record contains the subagent's output_text and optional error information. 4. The **lead agent** receives all subagent outputs and synthesizes them into a unified response. This final synthesis step is what produces the coordinator's final_text. What subagent_executions contains The AgentResult returned by the lead agent includes a subagent_executions list. Each entry is a SubagentExecutionRecord with: | Field | Type | Description | | --- | --- | --- | | subagent_name | str | Name of the subagent that was invoked. | | success | bool | Whether the subagent completed successfully. | | output_text | str or None | The subagent's response text, if it completed. | | latency_ms | float | Wall-clock execution time in milliseconds. | | error | str or None | Error message if the subagent failed. | You can inspect these records to understand what each subagent contributed: Subagent failure handling By default, subagent failure policy is continue. You can configure stricter or more resilient behavior using FailSafeConfig: With subagent_failure_policy=\"retry_then_degrade\", the lead agent receives error information for failed subagents alongside successful results and can produce a best-effort synthesis.", "token_count": 215} +{"id": "doc_07174a8633ac", "path": "docs/library/snippets/04_resume_and_compact.mdx", "url": "/library/snippets/04_resume_and_compact", "title": "04: Resume and Compact", "description": "Resume interrupted runs from their last checkpoint and compact retained thread memory to control storage growth.", "headings": ["What this snippet demonstrates", "Resuming an interrupted run", "How resume works internally", "Resume method signature", "Compacting thread memory", "How compaction works", "When to compact", "Error handling", "What to read next"], "content_sha256": "1926eddc956569f4ccf828f2605bfd5848af1b4ba75eba3d35876e2235c15491", "content": "What this snippet demonstrates Agent runs can be interrupted by timeouts, cancellations, infrastructure failures, or intentional pauses (such as waiting for human approval). When a run is interrupted, the runner persists a checkpoint containing the run's state at the point of interruption. The resume() method picks up from that checkpoint, restoring the conversation history, tool execution records, and step counter so the agent continues where it left off rather than starting from scratch. Over time, long-running threads accumulate checkpoint records, event logs, and state entries. The compact_thread() method prunes old records according to retention policies, keeping storage bounded without losing the data needed for active runs. Resuming an interrupted run How resume works internally The runner follows this sequence when resume() is called: 1. **Checkpoint lookup** -- The runner queries the memory store for the latest checkpoint matching the given run_id and thread_id. If no checkpoint exists, it raises AgentCheckpointCorruptionError. 2. **Terminal check** -- If the checkpoint already contains a terminal result (the run completed before the resume was requested), the runner returns that result immediately without re-executing. 3. **Snapshot restoration** -- The runner loads the runtime snapshot from the checkpoint, which includes the conversation message history, step counter, tool execution records, and any pending subagent state. 4. **Continued execution** -- The runner calls run_handle() internally with the restored snapshot, continuing the step loop from where it was interrupted. Resume method signature | Parameter | Type | Description | | --- | --- | --- | | agent | BaseAgent | The agent definition used for continued execution. Must match the agent that started the original run. | | run_id | str | The unique run identifier from the interrupted run. Found on result.run_id. | | thread_id | str | The thread identifier from the interrupted run. Found on result.thread_id. | | context | dict or None | Optional context overlay. Merged with the original run context. | Compacting thread memory How compaction works Compaction operates on two dimensions of stored data: - **Event retention** -- Controlled by RetentionPolicy. Removes event records older than max_age_ms. Events are the raw telemetry log entries (LLM calls, tool executions, state transitions) that accumulate over the lifetime of a thread. - **State retention** -- Controlled by StateRetentionPolicy. Removes state entries that exceed max_entries, keeping only the most recent ones. State entries include checkpoint snapshots, conversation summaries, and key-value metadata. Both policies are optional. If you omit a policy, that dimension is not compacted. The method returns a MemoryCompactionResult with counts of removed records so you can log or alert on compaction activity. When to compact - **After long conversations** -- Threads with hundreds of turns accumulate large checkpoint histories. Compact after the conversation ends or reaches a natural break point. - **On a schedule** -- Run compaction as a background task (e.g., hourly or daily) for threads that are still active but have grown large. - **Before resume** -- If you know a thread has extensive history, compacting before resume reduces the data the runner needs to load. Error handling What to read next - Memory -- Full memory architecture, checkpoint schema, and retention policies. - Core Runner -- Step loop lifecycle, state machine, and all runner API methods. - Checkpoint Schema -- Exact structure of checkpoint records stored in memory.", "token_count": 372} +{"id": "doc_3f60413b3a06", "path": "docs/library/snippets/05_direct_llm_structured_output.mdx", "url": "/library/snippets/05_direct_llm_structured_output", "title": "05: Direct LLM Structured Output", "description": "Use afk.llms with schema-validated responses.", "headings": ["Example", "Define the output schema as a Pydantic model", "Build an LLM client using the fluent builder", "Make a structured request", "The builder pattern", "Structured output with Pydantic", "When to use LLMBuilder vs Runner"], "content_sha256": "6f54fe64ae8f7ca3c497962bf8febe155784c84d094d323bdfb99581cc086743", "content": "Not every use case needs the full agent loop. Sometimes you want to call an LLM directly with a specific prompt and get back a structured, schema-validated response. AFK's LLMBuilder provides a fluent API for constructing LLM clients that can return Pydantic-validated objects directly, without the overhead of the agent run lifecycle. Use this pattern for classification, extraction, summarization, and any scenario where you want a single LLM call with a guaranteed output schema. Example The builder pattern LLMBuilder uses a fluent (method-chaining) API to construct an LLM client with the exact configuration you need: Each method returns the builder instance, so calls can be chained. The .build() call at the end constructs the final LLMClient with all specified settings. Available builder methods: | Method | Purpose | | --- | --- | | .provider(name) | Set the LLM provider (\"openai\", \"litellm\", \"anthropic_agent\"). | | .model(name) | Set the model identifier. | | .profile(name) | Apply a named configuration profile (\"production\", \"development\", etc.). | | .settings(settings) | Replace the loaded LLMSettings. | | .with_middlewares(stack) | Attach chat, stream, or embedding middleware. | | .with_observers(observers) | Attach LLM lifecycle observers. | | .with_cache(cache_backend) | Attach a cache backend instance or registered backend id. | | .with_router(router) | Attach a router instance or registered router id. | | .build() | Construct and return the LLMClient. | Sampling controls are request fields, not builder methods. Set them on LLMRequest, for example LLMRequest(..., temperature=0.0, max_tokens=1000). Structured output with Pydantic When you pass response_model=YourModel to client.chat(), the client instructs the LLM to return output that conforms to the model's JSON schema. The response is parsed and validated against the Pydantic model: - If the LLM returns valid structured output, resp.structured_response contains the parsed dictionary and resp.text contains the raw response. - If the LLM returns output that does not match the schema, a LLMInvalidResponseError is raised. This is powered by the LLM provider's native structured output support (e.g., OpenAI's response_format parameter) when available, with a fallback to prompt-based JSON extraction. When to use LLMBuilder vs Runner | Use Case | Approach | | --- | --- | | Single LLM call, no tools, no memory | LLMBuilder -- simpler, faster, no lifecycle overhead. | | Structured extraction or classification | LLMBuilder with response_model. | | Multi-turn conversation with tools | Runner -- provides the full agent loop with tool execution, policy, and memory. | | Subagent delegation | Runner -- only the runner supports subagent dispatch. | | Event streaming to a UI | Runner with run_stream(). | | Eval-driven development | Runner -- evals require the full AgentResult lifecycle. | Use LLMBuilder when you want precision and control over a single LLM interaction. Use Runner when you need the full agentic lifecycle.", "token_count": 305} +{"id": "doc_43ad71a4ad0c", "path": "docs/library/snippets/06_tool_registry_security.mdx", "url": "/library/snippets/06_tool_registry_security", "title": "06: Tool Registry Security", "description": "Safe tool registration and guardrail practices.", "headings": ["Read-only vs mutating tools", "--- Read-only tool: safe, broadly permitted ---", "--- Mutating tool: destructive, requires policy gate ---", "Policy gate setup", "Define policy rules that distinguish read vs write operations", "In headless mode, the delete is auto-denied. The model sees the denial and responds accordingly.", "Sandbox profiles for filesystem tools", "Scoping destructive tools"], "content_sha256": "a1ef90a4e46b3a7e305f3a953816f7601fc4a0b6fb3d32a17d5f5faa7ba3a2e3", "content": "Tools are the primary way agents interact with external systems. A tool that reads data is fundamentally different from a tool that deletes resources -- and your security model should reflect this. AFK provides multiple layers of defense for tool security: scoped tool definitions with typed arguments, sandbox profiles that restrict execution capabilities, and policy gates that require human approval for destructive operations. This page demonstrates how to register tools safely, distinguish between read-only and mutating tools, and configure policy gates to protect against unintended destructive actions. Read-only vs mutating tools The most important security distinction is between tools that observe (read-only) and tools that act (mutating). Read-only tools are generally safe to allow broadly. Mutating tools should be tightly scoped and policy-gated. Notice the differences: - The read-only tool (get_resource) has a description that explicitly says \"Read-only.\" This signals to both the model and human reviewers that the tool is safe. - The mutating tool (delete_resource) has a description warning about irreversibility. This helps the model understand the severity, and helps policy rules identify destructive operations. Policy gate setup Use a PolicyEngine to require human approval before any mutating tool executes: Sandbox profiles for filesystem tools For tools that interact with the filesystem or execute commands, use SandboxProfile to restrict their capabilities: Scoping destructive tools Follow these principles when registering destructive tools: 1. **Name them clearly.** Use verb prefixes that signal intent: delete_, remove_, drop_, update_, modify_. This makes policy rules easy to write and audit. 2. **Type all arguments.** Use Pydantic models for argument validation. Never accept freeform dict arguments for mutating operations. 3. **Describe irreversibility.** Include \"irreversible\", \"destructive\", or \"permanent\" in the tool description. This helps both the model and policy reviewers understand the risk. 4. **Gate with policy rules.** Every mutating tool should have a corresponding policy rule. Use request_approval for interactive environments and deny as the fallback in headless mode. 5. **Set cost limits.** Use FailSafeConfig.max_tool_calls and max_total_cost_usd to prevent runaway tool usage, especially when the agent has access to APIs with per-call costs. 6. **Audit everything.** Policy decisions are emitted as policy_decision events in the run event stream. Persist these events for compliance and debugging.", "token_count": 261} +{"id": "doc_c346f13ce597", "path": "docs/library/snippets/07_tool_hooks_and_middleware.mdx", "url": "/library/snippets/07_tool_hooks_and_middleware", "title": "07: Tool Hooks and Middleware", "description": "Add pre-execution validation, post-execution transformation, and cross-cutting middleware to tools and the LLM client pipeline.", "headings": ["What this snippet demonstrates", "Tool pre-hooks", "Pre-hook argument model matches the main tool's argument shape", "Pre-hook: sanitize and normalize the query before the tool runs", "Main tool with the pre-hook attached", "Pre-hook execution flow", "Tool post-hooks", "Tool-level middleware", "Attaching middleware to a tool", "Registry-level middleware", "LLM client middleware", "Chat middleware: intercepts non-streaming chat requests", "Build client with middleware", "LLM middleware protocols", "Built-in LLM middleware", "Timeout middleware", "Configure timeouts", "Add to middleware stack", "Build client", "When to use each layer", "What to read next"], "content_sha256": "cacc5663484a5478e3025773c47ac9ffe5ed840192da35d5ac11d46d5ff94c5d", "content": "What this snippet demonstrates AFK provides two distinct hook/middleware systems that operate at different layers: 1. **Tool hooks and middleware** -- Pre-hooks, post-hooks, and middleware that wrap individual tool executions. These use Pydantic models for typed arguments and run inside the tool execution pipeline. 2. **LLM middleware** -- Middleware that wraps LLM client operations (chat, stream, embed). These intercept requests and responses at the provider transport layer. Both systems follow the same pattern: define a callable, wire it into the pipeline, and the runner executes it at the appropriate point in the lifecycle. Tool pre-hooks A pre-hook runs before the main tool function executes. It receives the tool's arguments (validated against its own Pydantic model) and returns a dictionary of transformed arguments that the main tool will receive. Use pre-hooks for input sanitization, enrichment, or validation that should happen before execution. Pre-hook execution flow The pre-hook receives validated arguments and must return a dictionary compatible with the main tool's args_model. If the returned dictionary fails validation against the tool's model, the tool call fails with a ToolValidationError. Tool post-hooks A post-hook runs after the main tool function completes. It receives the tool output and can transform or annotate the result before it is returned to the LLM. Use post-hooks for output sanitization, logging, or enrichment. AFK passes post-hooks a payload dictionary with the shape {\"output\": , \"tool_name\": \" \"}. The post-hook must return a dictionary with the same shape. Tool-level middleware Tool-level middleware wraps around the entire tool execution, including pre-hooks and post-hooks. Middleware receives a call_next function and the tool arguments, and can modify behavior before, after, or around execution. Attaching middleware to a tool Middleware executes in the order listed. The first middleware in the list is the outermost wrapper. Each middleware calls call_next to pass control to the next middleware (or the actual tool function if it is the last one). Registry-level middleware Registry-level middleware applies to every tool in a ToolRegistry, not just a single tool. Use this for cross-cutting concerns like audit logging, rate limiting, or policy enforcement that should apply uniformly. LLM client middleware LLM middleware operates at the provider transport layer, intercepting requests to and responses from the LLM API. AFK defines three middleware protocols for the three LLM operations: LLM middleware protocols | Protocol | Operation | Signature | | --- | --- | --- | | LLMChatMiddleware | Non-streaming chat | async (call_next, req: LLMRequest) -> LLMResponse | | LLMEmbedMiddleware | Embeddings | async (call_next, req: EmbeddingRequest) -> EmbeddingResponse | | LLMStreamMiddleware | Streaming chat | (call_next, req: LLMRequest) -> AsyncIterator[LLMStreamEvent] | Each middleware receives call_next (the next middleware or transport in the chain) and the request object. It can modify the request before calling call_next, modify the response after, or short-circuit entirely by returning a response without calling call_next. Built-in LLM middleware AFK ships with pre-built middleware for common patterns: Timeout middleware Apply per-request timeouts to prevent runaway calls: The timeout middleware respects TimeoutPolicy from the request if provided: When to use each layer | Layer | Scope | Use for | | --- | --- | --- | | **Tool pre-hook** | Single tool, before execution | Input sanitization, argument enrichment, validation | | **Tool post-hook** | Single tool, after execution | Output sanitization, redaction, annotation | | **Tool middleware** | Single tool, wraps execution | Timing, retries, caching, error handling | | **Registry middleware** | All tools in registry | Audit logging, rate limiting, policy enforcement | | **LLM middleware** | All LLM calls through client | Request metadata, response logging, tracing | What to read next - Tools -- Full tool system architecture, the 6-step execution pipeline, and design guidelines. - Tool Call Lifecycle -- Detailed lifecycle of a tool call from LLM proposal to result delivery. - LLMs Overview -- Builder workflow, runtime profiles, and provider selection.", "token_count": 432} +{"id": "doc_464a2b4a8d35", "path": "docs/library/snippets/08_prebuilt_runtime_tools.mdx", "url": "/library/snippets/08_prebuilt_runtime_tools", "title": "08: Prebuilt Runtime Tools", "description": "Use AFK's built-in filesystem tools with directory-scoped security constraints and compose them with policy checks.", "headings": ["What this snippet demonstrates", "Building runtime tools", "Create filesystem tools scoped to a specific directory", "Available prebuilt tools", "list_directory", "read_file", "Security: directory traversal prevention", "Composing with policy checks", "Define a policy that requires approval for reading certain files", "Composing with custom tools", "Combine prebuilt + custom tools", "Command allowlists and sandbox profiles", "Create a read-only sandbox that restricts what operations tools can perform", "What to read next"], "content_sha256": "0b75873372d50bad1c2ec5d4bcd1e04445f683e855b896766f09d4bf86609937", "content": "What this snippet demonstrates AFK ships prebuilt tools for common runtime operations like listing directories and reading files. These tools are designed with security-first defaults: every tool is scoped to an explicit root directory that prevents directory traversal attacks. This snippet shows how to create, configure, and compose prebuilt tools with agents and policy guards. Building runtime tools The build_runtime_tools() factory creates a set of filesystem tools bound to a specific root directory. All path operations within these tools are resolved against this root, and any attempt to access files outside it raises a FileAccessError. Available prebuilt tools The build_runtime_tools() factory produces two tools: list_directory Lists entries in a directory under the configured root. Returns entry names, paths, and type flags (file or directory). | Parameter | Type | Default | Description | | ------------- | ----- | ------- | ----------------------------------------------------------------- | | path | str | \".\" | Relative path to list, resolved against the root directory. | | max_entries | int | 200 | Maximum entries to return (1--5000). Prevents unbounded listings. | **Returns:** A dictionary with root, path, and entries (list of {name, path, is_dir, is_file}). read_file Reads the contents of a file under the configured root, with configurable truncation to prevent excessive token consumption. | Parameter | Type | Default | Description | | ----------- | ----- | ---------- | -------------------------------------------------------------------------------- | | path | str | (required) | Relative path to the file, resolved against the root directory. | | max_chars | int | 20_000 | Maximum characters to read (1--500,000). Content is truncated beyond this limit. | **Returns:** A dictionary with root, path, content, and truncated (boolean indicating whether content was truncated). Security: directory traversal prevention Every path operation is validated with an internal containment check that uses Python's Path.relative_to() to verify that the resolved path stays within the configured root. This prevents attacks like: If a path escapes the root, the tool raises FileAccessError immediately, before any file I/O occurs. Composing with policy checks For additional security, pair runtime tools with a policy engine that gates specific operations on approval: Composing with custom tools You can combine prebuilt tools with your own custom tools in a single agent: Command allowlists and sandbox profiles For production environments, restrict tool capabilities further using sandbox profiles: This ensures that even if the LLM attempts to use tools for unauthorized operations, the sandbox profile blocks execution before any I/O occurs. What to read next - Tools -- Full tool system architecture, including the @tool decorator, ToolResult, and execution pipeline. - Snippet 06: Tool Registry Security -- Security scoping, policy gates, and sandbox profiles in detail. - Security Model -- Threat model, defense layers, and RunnerConfig security fields.", "token_count": 294} +{"id": "doc_64090a20f830", "path": "docs/library/snippets/09_system_prompt_loader.mdx", "url": "/library/snippets/09_system_prompt_loader", "title": "09: System Prompt Loader", "description": "Resolve agent system prompts from a file hierarchy with deterministic precedence, Jinja templating, and stat-based caching.", "headings": ["What this snippet demonstrates", "Resolution precedence", "Basic usage", "Option 1: Inline instructions (highest priority)", "Option 2: Explicit instruction file", "Option 3: Auto-detected file (uses agent name)", "Loads .agents/prompt/CHAT_AGENT.md automatically", "Name-to-filename conversion", "Prompts directory resolution", "Explicit", "Environment variable", "export AFK_AGENT_PROMPTS_DIR=/opt/prompts", "Default: .agents/prompt/", "Jinja2 templating", "Template context variables", "Caching and hot-reload", "Security: path containment", "This would raise PromptAccessError:", "What to read next"], "content_sha256": "a5dcded5d70453d7ede1e44f292d953ee51243eec18fc5b383e2a4231e130fee", "content": "What this snippet demonstrates AFK agents need system prompts (instructions) that tell the LLM how to behave. Rather than hardcoding instructions as inline strings, AFK provides a file-based prompt resolution system that loads prompts from a directory hierarchy. This keeps prompts version-controlled, editable by non-developers, and reusable across agents. The prompt loader resolves instructions through a deterministic precedence chain, supports Jinja2 templating for dynamic prompts, and caches compiled templates using stat-based invalidation for hot-reload during development. Resolution precedence The prompt system resolves agent instructions through this priority chain: 1. **Inline instructions** -- If the agent has a non-empty instructions string, it is used directly. No file loading occurs. 2. **Explicit instruction_file** -- If set, the file is loaded from the configured prompts_dir. The path must resolve to a file inside the prompts root (no directory traversal). 3. **Auto-detected file** -- If neither is set, the agent's name is converted to UPPER_SNAKE_CASE.md and loaded from prompts_dir. Basic usage Name-to-filename conversion The auto-detection algorithm converts the agent name to a filename using these rules: | Agent Name | Derived Filename | Rule Applied | | --- | --- | --- | | ChatAgent | CHAT_AGENT.md | CamelCase split on boundaries | | chatagent | CHAT_AGENT.md | Lowercase agent suffix detected and split | | research-assistant | RESEARCH_ASSISTANT.md | Hyphens replaced with underscores | | QA Bot v2 | QA_BOT_V2.md | Spaces and non-alphanumeric chars become underscores | The conversion is handled by derive_auto_prompt_filename() internally. It splits camelCase boundaries, normalizes non-alphanumeric characters to underscores, collapses consecutive underscores, and uppercases the result. Prompts directory resolution The prompts directory is resolved through its own priority chain: 1. Explicit prompts_dir argument on the Agent constructor. 2. AFK_AGENT_PROMPTS_DIR environment variable. 3. Default: .agents/prompt relative to the current working directory. Jinja2 templating Prompt files support Jinja2 template syntax. When the runner resolves a prompt, it renders the template with a context dictionary that includes agent metadata and any custom context passed to the run. **File: .agents/prompt/SUPPORT_AGENT.md** **Agent code:** Template context variables The following variables are available in every prompt template: | Variable | Type | Description | | --- | --- | --- | | agent_name | str | The agent's name field. | | agent_class | str | The Python class name of the agent. | | context | dict | The full context dictionary passed to the run. | | ctx | dict | Alias for context (shorthand). | Any keys in the context dictionary that are not reserved names (context, ctx, agent_name, agent_class) are also available as top-level template variables. So {{ company_name }} works as a shorthand for {{ ctx.company_name }}. Caching and hot-reload The prompt system uses a process-wide PromptStore singleton that caches at three levels: 1. **File cache** -- Keyed by resolved file path. Uses stat() metadata (mtime, size, inode) as the cache signature. If the file changes on disk, the cache entry is invalidated automatically. 2. **Text pool** -- Deduplicates prompt text by SHA-256 hash. If multiple agents use the same prompt content (even from different files), only one copy is stored in memory. 3. **Template cache** -- Compiled Jinja2 templates are cached by content hash. Re-rendering with different context variables reuses the compiled template. This means that during development, you can edit prompt files and they will be picked up on the next run without restarting the process. In production, the stat-based check is a single os.stat() call per prompt resolution, which is negligible overhead. Security: path containment The prompt loader enforces strict path containment. The resolved prompt file path must be inside the configured prompts_dir. If an instruction_file path resolves outside the prompts root (via ../ traversal or an absolute path pointing elsewhere), the loader raises PromptAccessError immediately. What to read next - System Prompts -- Full system prompt architecture, resolution pipeline, and design guidelines. - Agents -- Agent model, configuration fields, and composition patterns. - Security Model -- Threat model and defense layers including prompt injection considerations.", "token_count": 448} +{"id": "doc_7b26cbd9a7ae", "path": "docs/library/snippets/10_streaming_chat_with_memory.mdx", "url": "/library/snippets/10_streaming_chat_with_memory", "title": "10: Streaming Chat with Memory", "description": "Combine real-time streaming with thread-based memory for multi-turn chat UIs.", "headings": ["What this snippet demonstrates", "Full example", "Key patterns", "Thread ID connects turns", "These two calls share memory", "Access the result after streaming", "Cancel mid-stream", "The run transitions to \"cancelled\" state", "What to read next"], "content_sha256": "747e1945af154333e226917cadea057d5fc51e87933b13672f562d0190008ae2", "content": "What this snippet demonstrates Most chat applications need two things simultaneously: **real-time streaming** (so users see text as it's generated) and **memory continuity** (so the agent remembers previous turns). This snippet shows how to combine run_stream() with thread_id to build a multi-turn streaming chat handler. Full example Key patterns Thread ID connects turns Pass the same thread_id across run_stream() calls to maintain conversation context: Access the result after streaming The handle.result is available after the stream completes: Cancel mid-stream If the user navigates away or clicks \"stop\": What to read next - Streaming \u2014 Full event reference and stream control API. - Memory \u2014 Thread persistence, compaction, and backend configuration. - Snippet 04: Resume + Compact \u2014 Checkpoint-based resumption and memory management.", "token_count": 92} +{"id": "doc_fdf48d427fc0", "path": "docs/library/snippets/11_cost_monitoring.mdx", "url": "/library/snippets/11_cost_monitoring", "title": "11: Cost Monitoring", "description": "Track and control agent costs using FailSafeConfig budgets and telemetry events.", "headings": ["What this snippet demonstrates", "Setting cost budgets", "Monitoring cost from results", "Access usage statistics", "Real-time cost monitoring via streaming", "Cost-aware batch processing", "Operating recommendations", "What to read next"], "content_sha256": "f2d31922b3ae2362b1bc68b3e2c53fe59d0b80c0806fa5cfe522b204e0410131", "content": "What this snippet demonstrates Runaway agent loops are the most common source of unexpected API costs. AFK provides two defense layers: **cost budgets** that kill runs when spending exceeds a threshold, and **telemetry events** that let you observe cost in real time. This snippet shows how to configure both. Setting cost budgets The simplest defense is a hard cost ceiling on every agent: When the estimated cost exceeds max_total_cost_usd, the runner terminates the run with a degraded state and returns the best partial result. Monitoring cost from results Every AgentResult includes token counts and cost estimates: Real-time cost monitoring via streaming For long-running agents, monitor cost during execution: Cost-aware batch processing When running multiple agents in a batch, track cumulative cost: Operating recommendations 1. **Always set max_total_cost_usd** \u2014 even generous limits prevent runaway costs 2. **Layer defenses** \u2014 combine cost limits with max_llm_calls, max_steps, and max_wall_time_s 3. **Use telemetry for dashboards** \u2014 export metrics to monitor cost trends over time 4. **Set per-item budgets in batches** \u2014 prevent one expensive item from consuming the entire budget 5. **Choose models by task** \u2014 use smaller models for routine work and reserve larger models for requests that need them What to read next - Observability \u2014 Telemetry pipeline for metrics and dashboards. - Failure Policy Matrix \u2014 How cost limit breaches flow through the system. - Configuration Reference \u2014 Full FailSafeConfig field reference.", "token_count": 171} +{"id": "doc_40e30666bfa8", "path": "docs/library/snippets/12_mcp_client_integration.mdx", "url": "/library/snippets/12_mcp_client_integration", "title": "12: MCP Client Integration", "description": "Discover and use tools from external MCP servers in your agents.", "headings": ["What this snippet demonstrates", "Consuming MCP tools", "Connect, discover, and attach", "Using the Agent's built-in MCP support", "The agent connects to MCP servers automatically during startup", "Mixing local and MCP tools", "Security with MCP tools", "What to read next"], "content_sha256": "5b44bbbb025f2b01a553bacf0e510a09070c771c3fb99d91656df5ad80fd03df", "content": "What this snippet demonstrates AFK agents can consume tools from external MCP (Model Context Protocol) servers just like local tools. This snippet shows how to connect to an MCP server, discover available tools, and attach them to an agent \u2014 all with the same validation, policy gates, and telemetry as local tools. Consuming MCP tools Connect, discover, and attach Using the Agent's built-in MCP support For simpler setups, pass MCP server refs directly to the agent: Mixing local and MCP tools Combine your own tools with external MCP tools: Security with MCP tools Apply policy rules to MCP-sourced tools just like local tools: **MCP tools are transparent.** Once attached to an agent, they go through the same validation, policy gates, sanitization, and telemetry as local tools. The agent doesn't know whether a tool is local or remote. What to read next - MCP Server \u2014 Expose your own tools via MCP, plus authentication and rate limiting. - Tools \u2014 Full tool system architecture. - Snippet 06: Tool Security \u2014 Policy gates and sandbox profiles.", "token_count": 129} +{"id": "doc_d1f7d83e0824", "path": "docs/library/snippets/13_multi_model_fallback.mdx", "url": "/library/snippets/13_multi_model_fallback", "title": "13: Multi-Model Fallback", "description": "Configure fallback model chains for LLM resilience and cost optimization.", "headings": ["What this snippet demonstrates", "Basic fallback chain", "Cost-optimized fallback", "Start cheap, escalate if quality is insufficient", "Complex tasks get the big model with fallbacks", "Simple task -> cheap model handles it", "Complex task -> powerful model with safety net", "Circuit breaker integration", "Multi-agent with different model tiers", "Cheap model for simple classification", "Inspecting which model was used", "Recommendations", "What to read next"], "content_sha256": "f217ecec21c2dde7c33c194035ef4d3e46de6ffea14b1c9f76e5e8691024b281", "content": "What this snippet demonstrates LLM API calls fail \u2014 rate limits, outages, timeouts. AFK's fallback_model_chain lets you define an ordered list of models to try when the primary model fails. This snippet shows how to configure fallback chains for resilience, cost optimization, and provider diversification. Basic fallback chain When gpt-4.1 fails (timeout, rate limit, outage): 1. AFK retries with the primary model (controlled by retry policy) 2. If retries exhaust, it falls through to gpt-4.1-mini 3. If that also fails, it tries gpt-4.1-nano 4. If all models fail, the llm_failure_policy determines the outcome Cost-optimized fallback Use expensive models only when needed: Circuit breaker integration AFK's built-in circuit breaker works with fallback chains. When a model triggers too many failures, the breaker opens and the system skips straight to the next fallback: Multi-agent with different model tiers Use different model tiers for different specialists: Inspecting which model was used After a run, check the result metadata and usage aggregate: Recommendations | Scenario | Primary Model | Fallback Chain | | ------------------------ | -------------- | ------------------------------- | | **Classification** | gpt-4.1-nano | gpt-4.1-mini | | **General chat** | gpt-4.1-mini | gpt-4.1-nano | | **Complex analysis** | gpt-4.1 | gpt-4.1-mini \u2192 gpt-4.1-nano | | **Code generation** | gpt-4.1 | gpt-4.1-mini | | **Cost-sensitive batch** | gpt-4.1-nano | _(none)_ | What to read next - Configuration Reference \u2014 Full FailSafeConfig fields including circuit breaker settings. - Failure Policy Matrix \u2014 How failures flow through the system. - Snippet 11: Cost Monitoring \u2014 Track and control costs in real time.", "token_count": 183} +{"id": "doc_4462f67b1c03", "path": "docs/library/snippets/14_production_client.mdx", "url": "/library/snippets/14_production_client", "title": "14: Client Timeouts and Redis Pooling", "description": "Configure LLM client timeouts and Redis connection pooling.", "headings": ["What this snippet demonstrates", "Timeout middleware", "Per-request timeout override", "Redis connection pooling", "Using with memory store", "Full example", "Configuration reference", "TimeoutConfig", "PoolConfig", "What to read next"], "content_sha256": "3ad51d99b7437da1d6f78dd70ba9238005ea9818864ec8eeb4a97c4d39790c49", "content": "What this snippet demonstrates This snippet shows how to configure: 1. **Timeout middleware** to bound slow provider calls 2. **Redis connection pooling** for shared cache or memory connections 3. **Shutdown handling** so runners and Redis pools close cleanly Timeout middleware Apply per-request timeouts to prevent runaway LLM calls: Per-request timeout override Redis connection pooling For Redis deployments, use connection pooling instead of creating a new client per request: Using with memory store Full example Configuration reference TimeoutConfig | Parameter | Default | Description | | --- | --- | --- | | default_timeout_s | 30.0 | Default timeout for all operations | | chat_timeout_s | None | Specific timeout for chat requests | | embed_timeout_s | None | Specific timeout for embeddings | | stream_timeout_s | None | Specific timeout for streaming | PoolConfig | Parameter | Default | Description | | --- | --- | --- | | max_connections | 50 | Maximum total connections | | max_idle_connections | 10 | Maximum idle connections | | socket_timeout | 5.0 | Socket read/write timeout | | socket_connect_timeout | 5.0 | Connection establishment timeout | | socket_keepalive | False | Enable TCP keepalive | | health_check_interval_s | 30.0 | Interval for health checks | What to read next - LLM Control & Session -- Retry, caching, and circuit breaker policies - Deployment Guide -- Production deployment with Docker and Kubernetes - Performance Guide -- Optimize latency and throughput", "token_count": 138} +{"id": "doc_109157c0d2d7", "path": "docs/library/streaming.mdx", "url": "/library/streaming", "title": "Streaming", "description": "Real-time event streaming for chat UIs and CLI tools.", "headings": ["Quick example", "Event reference", "Streaming modes", "Stream control", "Lifecycle control variant", "Background tools example", "Error handling", "Next steps"], "content_sha256": "dffb536cc9129c079ef96ab842c4ea0ae01a309ee01259823f89e4c20a130350", "content": "AFK supports real-time streaming via Runner.run_stream(). Instead of waiting for the full response, you receive events as they happen: incremental text, tool lifecycle updates, and terminal status. Quick example Event reference run_stream() emits AgentStreamEvent values with these types: | Event type | When it fires | Key fields | | --- | --- | --- | | text_delta | Incremental text chunks from streaming or fallback path | event.text_delta, event.step | | step_started | New step in the agent loop | event.step, event.state | | tool_started | A tool call is about to execute | event.tool_name, event.tool_call_id, event.step | | tool_completed | A tool call finished | event.tool_name, event.tool_call_id, event.tool_success, event.tool_output, event.tool_error | | tool_deferred | A tool call was accepted but deferred | event.tool_name, event.tool_call_id, event.tool_ticket_id, event.data.resume_hint | | tool_background_resolved | A deferred tool finished successfully | event.tool_name, event.tool_ticket_id, event.tool_output | | tool_background_failed | A deferred tool failed/expired | event.tool_name, event.tool_ticket_id, event.tool_error | | completed | Run finished | event.result (full AgentResult) | | error | Stream/runtime bridge error | event.error | Streaming modes - If the provider supports streaming, AFK forwards model deltas as text_delta. - If the provider is non-streaming, AFK emits chunked fallback text_delta from final text so UI behavior stays consistent. Stream control AgentStreamHandle is read-only. For lifecycle controls (pause(), resume(), cancel(), interrupt()), use run_handle(). Background tools example Error handling error events indicate stream/runtime bridge failures. Tool failures are reported through tool_completed (tool_success=False) and do not necessarily fail the run. Next steps Persist state across streaming runs. Full lifecycle and API reference.", "token_count": 205} +{"id": "doc_2798948c3080", "path": "docs/library/system-prompts.mdx", "url": "/library/system-prompts", "title": "System Prompts", "description": "Configure agent instructions with files, templates, and dynamic context.", "headings": ["Three ways to set instructions", "Precedence chain", "Template variables with Jinja2", "Available template variables", "Prompt from file with templates", "Rules", "Error handling", "Design guidelines", "Next steps"], "content_sha256": "6fcbb09ca2206bb61c328929099af4a832216459bbe1880e90c8c7993766a6b5", "content": "System prompts define what your agent knows and how it behaves. AFK supports three methods \u2014 inline strings, instruction files, and auto-detection \u2014 with Jinja2 templating for dynamic values. Three ways to set instructions Set instructions directly on the Agent. Best for simple, static prompts. Store the prompt in a separate file. Best for long or version-controlled prompts. AFK automatically looks for an instruction file based on the agent's name: The resolution order: 1. prompts/{agent_name}.md 2. prompts/{agent_name}.txt 3. {agent_name}.md 4. {agent_name}.txt Precedence chain When multiple sources are available, AFK uses this order: The first non-empty source wins. If you set both instructions and instruction_file, the inline string takes priority. Template variables with Jinja2 Use {{ variable }} syntax to inject dynamic values into prompts: Available template variables | Source | Variables | Example | | -------------------- | ------------------------------------ | --------------------------------------- | | Agent.context dict | Any key-value pairs you set | {{ company_name }}, {{ user_role }} | | Built-in | agent_name, model_name | {{ agent_name }} | | Runtime | run_id, thread_id (if available) | {{ thread_id }} | Prompt from file with templates Templates work in instruction files too: Error handling | Error | Cause | Resolution | | ----------------------- | ------------------------------------- | -------------------------------------------------------------------------------------------- | | PromptResolutionError | instruction_file path doesn't exist | Check that the file exists under the configured prompts_dir. | | PromptTemplateError | Jinja2 template syntax error | Check for unclosed {{ }} or {% %} blocks. | | PromptTemplateError | Missing template variable | Add the variable to Agent.context or provide a default: {{ var \\| default(\"fallback\") }} | **Keep prompts in version control.** Store instruction files in a prompts/ directory and track changes in git. This gives you prompt history, diffs, and the ability to A/B test prompt versions. Design guidelines - **Inline for prototyping,** files for production. Switch to instruction files when your prompt exceeds ~5 lines. - **Use templates for anything dynamic.** Don't concatenate strings \u2014 use {{ variable }} and set values in context. - **Be specific in instructions.** Tell the agent what to do _and_ what not to do. Include output format expectations. - **Test prompt changes with evals.** A small prompt change can dramatically shift behavior. Run your eval suite after any edit. Next steps Reusable knowledge bundles loaded on demand. Test prompt changes with behavioral assertions.", "token_count": 249} +{"id": "doc_78b1717fafe3", "path": "docs/library/task-queues.mdx", "url": "/library/task-queues", "title": "Task Queues", "description": "Async job processing with execution contracts and dead-letter handling.", "headings": ["Quick start", "Push a task", "A worker consumes queued tasks", "Task lifecycle", "Execution contracts", "Worker setup", "Dead-letter handling", "Check for dead letters", "Retry manually after fixing the issue", "Or discard them", "Error classification", "Queue backends", "Connection pooling", "Create a pooled connection", "Use in queue", "Health check", "Next steps"], "content_sha256": "da3a4f6c098ee01fffa8507b3bd9ee9116cf103123e29dcbcc58b4a1facb5f5e", "content": "Task queues decouple agent work producers from consumers. Push a task, a worker picks it up, and the result is stored \u2014 independently of the caller's lifecycle. Use queues for long-running jobs, batch processing, and reliable retries. Quick start Task lifecycle | State | Meaning | | ------------- | ------------------------------------------- | | queued | Waiting to be picked up by a worker | | running | A worker is executing the task | | completed | Task finished successfully | | failed | Task hit an error (may be retried) | | dead_letter | All retries exhausted \u2014 needs manual review | Execution contracts Every task has a **contract** that defines what kind of work it represents: Standard agent chat. Runs an agent with a user message. Generic job dispatch. Runs a custom handler function. Define your own contract for specialized workloads: Register a handler for the contract on the worker side. Worker setup Dead-letter handling When a task exhausts all retries, it moves to the dead-letter queue (DLQ): Error classification The queue uses error classification to decide whether to retry: | Error type | Retried? | Example | | ------------- | ------------------------------------------------------------------------------------ | ------------------------------------------------ | | **Retryable** | (with backoff) | Network timeout, rate limit, transient LLM error | | **Terminal** | (sent to DLQ) | Invalid arguments, auth failure, missing model | | **Non-fatal** | (task completes with warning) | Telemetry export failure | Queue backends State lives in process memory. No setup required. **Use for:** Development, testing, prototyping. Durable queue with persistence and multi-worker support. Set via environment variables: **Use for:** Production deployments, multi-process workers. Connection pooling For high-throughput production workloads, use RedisConnectionPool to manage connections efficiently: The pool provides: - Configurable max connections (default: 50) - Idle connection management - Automatic health checks - Singleton access via get_redis_pool() Next steps Expose tools via the Model Context Protocol. Monitor queue performance and worker health.", "token_count": 209} +{"id": "doc_618e34441fa7", "path": "docs/library/tested-behaviors.mdx", "url": "/library/tested-behaviors", "title": "Tested Behaviors", "description": "Contract-level guarantees and what test suites validate.", "headings": ["Guaranteed behaviors", "Deterministic delegation ordering", "Queue contract failure classification", "Stream lifecycle correctness", "Telemetry projection stability", "Eval report schema consistency", "Test categories", "Running the test suite", "Delegation and subagent tests", "Queue contract tests", "Eval suite tests", "Tool execution tests", "Observability tests", "LLM runtime tests", "Interpreting test results", "Adding new tests", "CI pipeline guidance", "Example GitHub Actions step"], "content_sha256": "80884b0dc0cbc8a2eda294fc1ca74d89dea1b5e98a80ea48892237808e33f2e2", "content": "AFK's tests describe the behavior contributors should preserve when changing the runtime, tools, memory, queues, LLM adapters, observability, and evals. Treat this page as a map from behavior to the test files that cover it. If you change a public contract, update the matching tests and the docs that explain that contract. A passing test suite is necessary, but it is not a release guarantee by itself; review the affected behavior and run the focused suite for the code you touched. Guaranteed behaviors Deterministic delegation ordering **What is tested:** When a parent agent dispatches work to subagents, the delegation engine executes nodes in a deterministic, topologically sorted order. Parallel batches respect concurrency limits. Edge dependencies (where one subagent's output feeds into another's input) are resolved before the dependent node starts. **Why it matters:** If delegation ordering were non-deterministic, the same agent configuration could produce different results depending on task scheduling. Deterministic ordering means your subagent pipelines are reproducible and debuggable. **Test coverage:** Tests verify that DAG plans produce consistent node execution sequences, that edge-based data flow resolves correctly, and that backpressure limits prevent unbounded queue growth. Queue contract failure classification **What is tested:** The task queue system classifies failures into retryable, terminal, and degraded categories. Retryable failures trigger retry with backoff. Terminal failures stop execution immediately. Degraded states allow partial results. **Why it matters:** Incorrect failure classification can cause infinite retry loops (if terminal failures are marked retryable) or premature task abandonment (if retryable failures are marked terminal). The failure classification contract ensures that each failure type triggers the correct recovery behavior. **Test coverage:** Tests verify that each failure category maps to the correct queue behavior, that retry counts and backoff intervals are respected, and that dead-letter handling works correctly. Stream lifecycle correctness **What is tested:** The streaming API (runner.run_stream()) produces events in the correct lifecycle order: stream starts, text deltas arrive, tool events fire at the right times, and the stream terminates with a completed event containing the final AgentResult. Error conditions produce error stream events rather than unhandled exceptions. **Why it matters:** Streaming consumers (such as chat UIs) depend on events arriving in the correct order. A misplaced completed event before all text deltas have been emitted would cause truncated output. An unhandled exception would crash the consumer. **Test coverage:** Tests verify event ordering, ensure that all text content is captured before the terminal event, and confirm that error conditions produce structured error events. Telemetry projection stability **What is tested:** The telemetry projector produces consistent RunMetrics from the same input data. Field names, types, and computed properties (like avg_llm_latency_ms and success) are stable across versions. **Why it matters:** Downstream dashboards, alerting rules, and eval assertions depend on RunMetrics having a stable schema. If a field is renamed or its type changes, every consumer breaks silently. **Test coverage:** Tests verify that projected metrics match expected values for known input data, that computed properties produce correct results, and that to_dict() serialization is stable. Eval report schema consistency **What is tested:** The eval report serializer (suite_report_payload) produces output with a stable schema_version and consistent field structure. The report includes summary statistics, per-case results, assertion details, budget violations, and projected metrics. **Why it matters:** CI pipelines parse eval reports to make release-gating decisions. If the report schema changes, CI scripts break and deployments may be incorrectly blocked or allowed. **Test coverage:** Tests verify the report envelope structure, confirm that schema_version is set correctly, and validate that all expected fields are present and correctly typed. Test categories | Category | Description | Key Modules Covered | | --- | --- | --- | | Agent delegation | Deterministic DAG execution, subagent routing, backpressure limits. | afk.core.runtime, afk.agents.delegation | | Queue contracts | Failure classification, retry behavior, dead-letter handling. | afk.queues | | Stream lifecycle | Event ordering, text delta capture, terminal event correctness. | afk.core.streaming | | Telemetry projection | Metric stability, computed property correctness, serialization. | afk.observability.projectors, afk.observability.models | | Eval reports | Report schema stability, assertion result structure, budget violations. | afk.evals.reporting, afk.evals.models | | Tool execution | Pydantic validation, timeout enforcement, hook/middleware chains. | afk.tools | | Policy evaluation | Rule matching, decision actions, audit event emission. | afk.agents.policy | | LLM runtime | Provider routing, retry/circuit-breaker behavior, streaming correctness. | afk.llms.runtime | | Security | Sandbox profile enforcement, secret scope isolation, command allowlists. | afk.tools.security | Running the test suite Run all tests from the repository root: Run a specific test category: Run with verbose output for debugging: Interpreting test results - **All relevant tests pass**: The checked behavior still matches the test suite. Review docs and public imports before merging user-visible changes. - **A test fails**: Inspect the assertion before changing code. The test may expose a real contract regression, or the intended contract may have changed and need a deliberate test/doc update. - **A new test is marked as xfail**: Include a reason and keep the scope narrow so known limitations do not hide unrelated regressions. Adding new tests When adding a new feature or fixing a bug, follow this pattern: 1. **Identify the contract.** What behavioral guarantee should your change preserve or introduce? Write this as a plain-English statement (e.g., \"Subagent timeout should produce a SubagentExecutionRecord with success=False\"). 2. **Write the test first.** Create a test in the appropriate tests/ subdirectory that asserts the expected behavior. Use descriptive test names that read as contract statements. 3. **Make the test pass.** Implement the feature or fix. The test should pass without any special-casing or mocking of the behavior under test. 4. **Verify stability.** Run the full suite to confirm your change does not break existing contracts. CI pipeline guidance Most tests run without API keys and use in-memory or mocked providers. Some integration tests cover optional backends such as Redis, SQLite, or Postgres behavior; keep those isolated and skippable when the backing service is unavailable. Recommended CI configuration: Store the JUnit XML report as a CI artifact for trend analysis and failure investigation.", "token_count": 763} +{"id": "doc_0ac01bce8c79", "path": "docs/library/tool-call-lifecycle.mdx", "url": "/library/tool-call-lifecycle", "title": "Tool Call Lifecycle", "description": "Detailed event-level view of one tool invocation.", "headings": ["Event timeline", "Event reference table", "Policy decisions: allow, deny, defer, and request_user_input", "Timing and latency tracking", "How denied tools affect the agent run"], "content_sha256": "9426358483fa8300d96c0f0ec8bc520958e881b7f6827a7fdfe5301576f8f306", "content": "While the Tools System Walkthrough covers the end-to-end pipeline, this page zooms into the event-level lifecycle of a single tool call. Every tool invocation produces a deterministic sequence of events that flow through the run handle. Understanding this sequence is critical for building observability dashboards, audit logs, and human-in-the-loop approval flows. A tool call begins when the runner receives a ToolCall from the LLM response. Before the handler ever runs, the runner evaluates a policy gate that can allow, deny, or defer the call for human approval. The outcome of that gate determines whether the tool executes at all, and the events emitted along the way tell you exactly what happened and why. Event timeline Event reference table Every event is an AgentRunEvent published on the run handle. The type field identifies the event, and the data dict carries event-specific fields. | Event type | When it fires | Key data fields | Description | | --- | --- | --- | --- | | tool_batch_started | After LLM response contains tool calls | tool_call_count | Signals the start of a batch of one or more tool calls from a single LLM turn. | | policy_decision | After policy evaluation for each tool | tool_name, action, reason | Records the policy engine's decision for one tool call. Emitted for allow, deny, defer, and request_user_input actions. | | run_paused | When a tool call requires human approval | tool_name, reason, payload | The run is suspended waiting for external input. Only emitted when policy returns defer or request_approval. | | run_resumed | After human approval or denial is received | approved | The run resumes after the approval decision. | | tool_completed | After each tool call resolves (success or failure) | tool_name, success, output, error, tool_call_id | The terminal event for one tool call. Always emitted regardless of whether the tool ran, was denied, or timed out. | | warning | When non-fatal issues occur | message | Advisory events such as sandbox violations handled under a continue policy. | Policy decisions: allow, deny, defer, and request_user_input The PolicyEngine evaluates a PolicyEvent with event_type=\"tool_before_execute\" for every tool call. The decision drives the rest of the lifecycle: **allow** -- The tool executes immediately. No human interaction required. This is the default when no policy rules match. **deny** -- The tool is blocked. A ToolExecutionRecord with success=False and error=\"Denied by policy\" is recorded. The denied result is fed back to the model as a tool message so it can adjust its behavior. After a denial, the runner consults fail_safe.approval_denial_policy to decide the run-level outcome: - continue (default) -- The run proceeds. The model sees the denial and may try a different approach. - degrade -- The run transitions to a degraded state and terminates with a degradation message. - fail -- The run raises an AgentExecutionError and transitions to failed. **defer / request_approval** -- The runner pauses the run, emits a run_paused event, and waits for the InteractionProvider to deliver an approval decision. If the provider is HeadlessInteractionProvider, the configured approval_fallback (default: deny) is used. The approval has a configurable timeout (approval_timeout_s, default 300s). **request_user_input** -- Similar to defer, but instead of a yes/no approval, the runner asks the user for a value (such as a confirmation code or corrected parameter). The policy's request_payload can specify a target_arg field, and the user's input is injected into the tool arguments before execution. Timing and latency tracking Every tool execution is timed from the moment registry.call() is invoked to the moment it returns. The latency is captured in milliseconds and recorded in two places: 1. **ToolExecutionRecord.latency_ms** -- Available on the AgentResult.tool_executions list after the run completes. 2. **Telemetry histogram** -- The agent.tool_call.latency_ms metric is emitted with attributes tool_name, result (success/error), and source (execute/replay). For batch-level timing, the agent.tool_batch.latency_ms histogram captures the wall-clock duration of the entire batch (all concurrent tool calls), with attributes for call_count and failure_count. Denied tool calls have no execution latency since the handler never runs. How denied tools affect the agent run When a tool is denied, the model still receives feedback. The runner appends a tool message to the conversation with the denial reason: This feedback loop is important: the model sees that its proposed action was rejected and can adapt. In practice, models respond to denials by either rephrasing the request, choosing a different tool, or providing a text-only response. The run-level impact depends on fail_safe.approval_denial_policy: | Policy | Behavior after denial | | --- | --- | | continue | Run proceeds normally. Model sees the denial and may self-correct. | | degrade | Run transitions to degraded state and exits the step loop with a degradation message. | | fail | Run raises AgentExecutionError and transitions to failed state. | Multiple denials in a single batch are each evaluated independently. If any denial triggers a fail or degrade policy, the remaining tool calls in the batch are skipped.", "token_count": 535} +{"id": "doc_745f751f1344", "path": "docs/library/tools-system-walkthrough.mdx", "url": "/library/tools-system-walkthrough", "title": "Tools System Walkthrough", "description": "From tool registration to runtime execution and model feedback.", "headings": ["Registration to execution map", "Step-by-step breakdown", "Full example: tool definition through result inspection", "1. Define a tool with a Pydantic args model", "2. Create an agent with the tool", "3. Run the agent", "4. Inspect the result", "Inspecting tool executions", "Hook and middleware pipeline", "Error handling"], "content_sha256": "1ed41f9431123214684ef929c17c9f9886933e7e7c218f1ac668e47a6e02e499", "content": "Every agent capability beyond pure text generation flows through the AFK tool system. A tool starts as a decorated Python function, gets registered in a ToolRegistry, has its JSON Schema exposed to the LLM, and then participates in a tightly controlled loop: the model proposes a call, AFK validates the arguments, evaluates policy gates, executes the handler, sanitizes the output, and feeds the result back into the conversation for the model's next turn. This page walks through every stage of that pipeline end-to-end. Registration to execution map Step-by-step breakdown 1. **Define @tool** -- You write a Python function and decorate it with @tool. The decorator extracts the function name, docstring, and a Pydantic model for its arguments to produce a ToolSpec(name, description, parameters_schema). 2. **Tool registry** -- When the agent starts, all tools (declared on the agent plus runtime-injected extras like MCP tools and skill tools) are collected into a ToolRegistry. The registry is the single source of truth for tool lookup, schema export, and call dispatch. 3. **Expose tool schema to model** -- The registry converts every registered tool into the OpenAI function-calling format via registry.to_openai_function_tools(). This list is attached to the LLMRequest.tools field so the model knows what tools are available. 4. **Model emits tool call** -- The LLM response (LLMResponse) may contain one or more ToolCall objects. Each carries an id, a tool_name, and a JSON arguments dict. AFK processes these as a batch. 5. **Schema validation** -- Before execution, each tool call's raw arguments are validated against the tool's Pydantic args_model via BaseTool.validate(). If validation fails, a ToolResult(success=False) is returned immediately and the tool handler is never invoked. 6. **Policy gate** -- The runner evaluates the PolicyEngine for each tool call via a tool_before_execute policy event. The policy can allow, deny, defer (require human approval), or request_user_input. See the Tool Call Lifecycle page for the full decision matrix. 7. **Execute handler** -- If the policy allows execution, AFK runs the tool through the full hook/middleware chain: PreHooks (argument transforms) -> Middleware chain -> core handler -> PostHooks (output transforms). Timeout enforcement applies at every layer. 8. **Sanitize output** -- The raw ToolResult.output is passed through apply_tool_output_limits() which enforces max_output_chars and sandbox output policies. The runner then wraps the result in an untrusted-data content envelope via render_untrusted_tool_message() to prevent prompt injection from tool output. 9. **Emit events** -- A tool_completed event is emitted on the run handle, carrying the tool name, success flag, output, and any error. Telemetry counters and histograms are recorded for latency and success/failure counts. 10. **Tool result fed back to model** -- The sanitized output is appended to the conversation as a Message(role=\"tool\", name=tool_name, content=...). The loop returns to step 4 for the next LLM turn. Full example: tool definition through result inspection Inspecting tool executions Every completed run exposes a tool_executions list on AgentResult. Each entry is a ToolExecutionRecord: | Field | Type | Description | | -------------- | ------------------- | ------------------------------------ | | tool_name | str | Name of the executed tool. | | tool_call_id | str \\| None | Provider/LLM tool-call identifier. | | success | bool | Whether execution succeeded. | | output | JSONValue \\| None | JSON-safe tool output payload. | | error | str \\| None | Error message when execution failed. | | latency_ms | float \\| None | Execution latency in milliseconds. | Hook and middleware pipeline Tools support three extension points that wrap the core handler: | Extension | When it runs | Signature | Purpose | | -------------- | ------------------- | ------------------------------------- | ------------------------------------------------------------------------------------------- | | **PreHook** | Before core handler | (args) or (args, ctx) | Transform or validate arguments. Must return a dict compatible with the tool's args model. | | **Middleware** | Wraps core handler | (call_next, args, ctx) | Cross-cutting concerns (logging, timing, caching). Calls call_next(args, ctx) to proceed. | | **PostHook** | After core handler | ({\"output\": ..., \"tool_name\": ...}) | Transform or audit the output before it reaches the model. | The execution order is: validate args -> run PreHooks sequentially -> run Middleware chain (outermost first) -> core handler -> run PostHooks sequentially -> wrap in ToolResult. Error handling The tool system is designed to be non-throwing by default. BaseTool.call() catches all exceptions and wraps them in a ToolResult(success=False, error_message=...) unless raise_on_error=True is set on the tool. **Validation errors** -- When the model passes arguments that do not match the Pydantic schema, a ToolValidationError is caught and the error message is returned to the model so it can self-correct on the next turn. **Timeout errors** -- Each tool can set a default_timeout in seconds. If execution exceeds this limit, an asyncio.TimeoutError is caught and surfaced as a ToolTimeoutError in the result. **Execution errors** -- Any unhandled exception from the handler, PreHook, or PostHook is caught and wrapped in a ToolExecutionError result. **Hook/middleware failures** -- If a PreHook returns a non-dict value or produces args that fail re-validation against the main tool's model, execution stops and a failure result is returned. PostHook failures similarly short-circuit the chain. **Policy-driven behavior** -- After a tool failure, the runner consults the agent's fail_safe.tool_failure_policy to decide whether to continue (default), degrade the run, or fail the entire run. This is configurable per agent.", "token_count": 582} +{"id": "doc_9e9118ed2141", "path": "docs/library/tools.mdx", "url": "/library/tools", "title": "Tools", "description": "Give agents typed capabilities through Python functions.", "headings": ["Your first tool", "How tool calling works", "Tool patterns", "Deferred background tool calls", "Policy-gated tools", "Hooks and middleware", "Execution order", "Common tools cookbook", "Prebuilt tools", "Runtime tools", "Tools scoped to a specific directory", "Returns: [read_file, list_directory, ...]", "Skill tools", "Next steps"], "content_sha256": "517d6cb680faa87f107bc90d44a3e46e807dbc7146a40390f7385f83568e4743", "content": "Tools let agents take actions \u2014 query databases, call APIs, run calculations, write files, or anything you can express in Python. AFK handles schema generation, argument validation, policy gates, execution, and output sanitization. Your first tool That's a complete tool. The @tool decorator generates the JSON schema from the Pydantic model, which the LLM uses to understand what arguments to pass. How tool calling works Based on the user's message and the tool schemas, the LLM emits a tool_call with the function name and arguments. AFK parses the arguments through the Pydantic model. Invalid arguments generate a validation error that's sent back to the LLM for self-correction. If a PolicyEngine is attached, the tool call is checked against policy rules (allow, deny, or request_approval). The tool function runs with validated arguments. Pre/post hooks and middleware execute around the handler. The output is truncated to tool_output_max_chars, stripped of potential prompt injection vectors (if sanitize_tool_output=True), and formatted for the LLM. The sanitized result is appended to the conversation and the LLM generates its next response. Tool patterns Return a string or dict directly. Return a dict for structured data. Use async def for I/O-heavy tools. Access ToolContext in tool handlers. The second parameter must be named ctx or annotated as ToolContext. Deferred background tool calls For long-running operations, a tool can return a deferred handle so the run can continue while work completes in the background. When deferred: 1. Runner emits tool_deferred. 2. Agent continues with other work in the same run. 3. Runner emits tool_background_resolved or tool_background_failed. 4. Resolved tool output is injected back into conversation for next steps. External workers can resolve tickets by writing: - bgtool:{run_id}:{ticket_id}:state - bgtool:{run_id}:latest Status payload example: This pattern is useful for coding agents that start a long build, continue writing docs, then consume build results once available. You can also use runner helpers instead of writing raw state keys: Policy-gated tools Use the PolicyEngine to gate sensitive tool calls: **Policy best practice:** Gate all mutating tools with request_approval or deny by default. Only allow read-only tools without gates. Hooks and middleware AFK provides four extension points for tool execution: **prehooks**, **posthooks**, **tool-level middleware**, and **registry-level middleware**. Each has its own decorator. Prehooks run before the tool handler. They receive the tool's arguments and **must return a dict** compatible with the tool's args_model. Posthooks run after the tool handler. They receive a dict {\"output\": , \"tool_name\": \" \"} and should return a dict with the same shape. Middleware wraps the entire tool execution. It receives call_next, the validated args, and optionally ctx. Registry-level middleware applies to **every tool** in a ToolRegistry. Use for audit logging, rate limiting, or global policy enforcement. Execution order | Layer | Scope | Decorator | Returns | | --------------- | --------------------------- | -------------------------------- | ------------------------------ | | **Prehook** | Single tool, before handler | @prehook(args_model=...) | dict of transformed args | | **Middleware** | Single tool, wraps handler | @middleware(name=...) | Tool output (via call_next) | | **Posthook** | Single tool, after handler | @posthook(args_model=...) | dict with output key | | **Registry MW** | All tools in registry | @registry_middleware(name=...) | ToolResult (via call_next) | Common tools cookbook Prebuilt tools AFK ships with ready-to-use tools for common agent capabilities. These are in the afk.tools.prebuilts module. Runtime tools Filesystem tools scoped to a directory for safe agent exploration: Runtime tools enforce directory-scoped access \u2014 the agent cannot read or list files outside the configured root_dir. Skill tools When an agent has skills configured, AFK generates four skill tools automatically: | Tool | Purpose | | ----------------- | ----------------------------------------------------- | | list_skills | Return metadata for all enabled skills | | read_skill_md | Read a skill's SKILL.md content and checksum | | read_skill_file | Read additional files under a skill directory | | run_skill_command| Execute allowlisted commands with timeout and limits | Skill tools are gated by a SkillToolPolicy that controls command allowlists, output limits, and shell operator restrictions: Next steps Watch tool calls happen in real time. Policy gates, sandbox profiles, and tool allowlists.", "token_count": 481} +{"id": "doc_136d7a909d51", "path": "docs/library/troubleshooting.mdx", "url": "/library/troubleshooting", "title": "Troubleshooting", "description": "Common issues and solutions when building with AFK.", "headings": ["Agent behavior issues", "Agent keeps calling the same tool repeatedly", "Add hard limits to prevent runaway loops", "Agent ignores tools and doesn't call them", "Agent produces inconsistent outputs", "Use request-level sampling controls for direct LLM calls", "Memory issues", "Conversation doesn't persist between runs", "Always use thread_id for multi-turn conversations", "r2 will remember r1's context", "Verify memory is configured", "For production, use persistent storage", "Resume doesn't work", "Check run_id and thread_id are correct", "Resume correctly", "Check checkpoint state directly from the configured memory store", "LLM issues", "Rate limit errors", "Or use exponential backoff for retries", "Timeout errors", "Set appropriate timeouts", "Or per-request timeout via middleware", "Model not found errors", "Verify model name is correct", "Use fallback for resilience", "Streaming issues", "Streaming doesn't work", "Make sure you're iterating correctly", "Don't mix sync and async", "WRONG:", "RIGHT:", "Streaming disconnects early", "Use timeout middleware for streaming", "Cost issues", "Unexpected high costs", "ALWAYS set cost limits", "Token limit errors", "Compact memory to reduce context", "Or use a model with larger context", "Tool issues", "Tool validation errors", "Ensure Pydantic model matches tool implementation", "Tool not found errors", "Verify tool is attached to agent", "Verify tool name matches", "Call with exact name", "Debug mode", "Getting help", "Next steps"], "content_sha256": "b21eb3b6aac297783f21d0585242a6ba2ccdb8038df41fa08915007fc1521053", "content": "This guide covers common issues encountered when building and deploying AFK agents, with solutions and debugging tips. Agent behavior issues Agent keeps calling the same tool repeatedly **Symptoms:** Agent enters a loop, calling the same tool multiple times without making progress. **Causes:** - Tool output doesn't provide the information the agent needs - Agent instructions don't clarify when to stop - Missing a tool that would help the agent determine completion **Solutions:** **Debug:** Enable verbose logging to see tool call inputs/outputs: --- Agent ignores tools and doesn't call them **Symptoms:** Agent responds with text but doesn't use available tools. **Causes:** - Instructions don't mention the tools or when to use them - Tool descriptions are unclear - Model being used doesn't support function calling well **Solutions:** --- Agent produces inconsistent outputs **Symptoms:** Same input produces different outputs on different runs. **Causes:** - Temperature is set too high - Missing structured output configuration - Non-deterministic system prompt **Solutions:** Memory issues Conversation doesn't persist between runs **Symptoms:** Agent doesn't remember previous messages. **Causes:** - Not using thread_id to link conversations - Memory store not configured correctly - Using in-memory store (loses state on restart) **Solution:** **Check memory backend:** --- Resume doesn't work **Symptoms:** Calling runner.resume() doesn't continue from where the run stopped. **Solutions:** **Debug checkpoints:** LLM issues Rate limit errors **Symptoms:** RateLimitError or 429 responses from LLM provider. **Solutions:** --- Timeout errors **Symptoms:** Requests hang or timeout before completing. **Solutions:** --- Model not found errors **Symptoms:** ModelNotFoundError or InvalidRequestError. **Solutions:** Streaming issues Streaming doesn't work **Symptoms:** run_stream() doesn't return events or returns them all at once. **Solutions:** --- Streaming disconnects early **Symptoms:** Stream ends before completion. **Solutions:** Cost issues Unexpected high costs **Symptoms:** API costs much higher than expected. **Causes:** - Agent in a loop making many LLM calls - No cost limits configured - Expensive model being used unnecessarily **Solutions:** --- Token limit errors **Symptoms:** ContextLengthExceeded or similar errors. **Solutions:** Tool issues Tool validation errors **Symptoms:** ToolValidationError when tools are called. **Solutions:** --- Tool not found errors **Symptoms:** Agent can't find or call a tool. **Solutions:** Debug mode Enable debug mode for detailed logging: Getting help If you can't resolve an issue: 1. Check the GitHub Issues for known issues 2. Enable debug logging and capture the full traceback 3. Include these details when reporting: - AFK version (pip show afk) - Python version - LLM provider and model - Minimal reproduction code - Full error traceback Next steps Understand how AFK components work together. Test agent behavior before shipping. Common patterns and anti-patterns. Detailed API documentation.", "token_count": 347} +{"id": "doc_f1e32ed2ffce", "path": "docs/llms/adapters.mdx", "url": "/llms/adapters", "title": "Adapters", "description": "Built-in LLM providers and custom adapter registration.", "headings": ["Built-in providers", "Capability comparison", "Usage", "OpenAI", "Anthropic", "LiteLLM (any provider)", "Custom provider", "Custom transport", "Next steps"], "content_sha256": "4062f20b10ef8ec55493c8418a3fe20da61715e29f3f285979f8f696d0a1872c", "content": "Providers translate between AFK's normalized contracts (LLMRequest/LLMResponse) and provider-specific APIs. AFK ships with three built-in providers and supports custom providers for internal deployments. Built-in providers Direct integration via the OpenAI Python SDK. Supports all GPT-4.1 and o-series models. Direct integration via the Anthropic SDK. Supports Claude Opus 4.5 and Opus. Proxy adapter for 100+ providers (Azure, Bedrock, Gemini, Mistral, local models, etc.). Capability comparison | Feature | OpenAI | Anthropic | LiteLLM | | -------------------- | ------------------------------------------------------ | ------------------------------------------------------ | --------------------------------------------------------------------------- | | Text generation | | | | | Tool calling | | | (provider-dependent) | | Structured output | | | Provider-dependent | | Streaming | | | | | Vision (image input) | | | Provider-dependent | | Custom endpoints | | | | Usage Custom provider Register your own provider for unsupported inference servers: Custom transport Use provider-specific settings for custom endpoints, proxy URLs, or credentials: Next steps Retry, caching, rate limiting, and circuit breaking. How agents resolve and use LLM clients.", "token_count": 106} +{"id": "doc_eeed52120ccf", "path": "docs/llms/agent-integration.mdx", "url": "/llms/agent-integration", "title": "Agent Integration", "description": "How agents resolve models and interact with the LLM layer.", "headings": ["Model resolution", "How the runner uses the LLM", "Request construction", "Streaming integration", "Error handling", "Model selection guide", "Next steps"], "content_sha256": "8f69ff34bc2ca6056745652bec1d0ebcb8e395d7930aaa87e81518f5e05b8619", "content": "This page explains how agents connect to the LLM layer \u2014 from model resolution to request construction, streaming, and error handling. Model resolution Agents can specify their model in two ways: Pass a model name string. AFK resolves it to an LLM client using the default provider. The resolution order: 1. Check agent.model_resolver (custom function) 2. Check registered adapters for matching provider prefix 3. Default to OpenAI adapter Pass a pre-configured LLMClient for full control over provider settings. How the runner uses the LLM On each step of the agent loop: Request construction The runner builds an LLMRequest from multiple sources: | Source | Contributes | Priority | | -------------------- | ----------------------- | -------- | | Agent.instructions | System message | Highest | | Thread history | Previous messages | \u2014 | | user_message | User message | \u2014 | | Agent.tools | Tool schemas | \u2014 | | Agent.subagents | Transfer tool schemas | \u2014 | | RunnerConfig | Temperature, max_tokens | Lowest | Streaming integration When using run_stream(), the runner passes through streaming events from the LLM: text_delta events come from two paths: - Provider streaming when the adapter supports stream deltas. - Runner fallback chunking of final text for non-streaming providers. Tool and step events are generated by the runner. Error handling LLM errors are classified and handled automatically: | Error | Classification | Runner behavior | | ---------------------------- | -------------- | -------------------------- | | Rate limit (429) | Retryable | Retry with backoff | | Server error (500, 502, 503) | Retryable | Retry with backoff | | Auth error (401, 403) | Terminal | Fail the run | | Invalid request (400) | Terminal | Fail the run | | Timeout | Retryable | Retry (if attempts remain) | | Circuit breaker open | Terminal | Try fallback model or fail | | All retries exhausted | Terminal | Try fallback model or fail | Model selection guide Treat these as example starting points. Confirm current model names, prices, and rate limits with the provider you use. | Task | Starting model | Why | | -------------------------- | ------------------------------ | ----------------------------------- | | Simple Q&A, classification | gpt-4.1-nano | Fast, cheap, good enough | | General purpose with tools | gpt-4.1-mini | Best balance of cost and capability | | Complex reasoning, coding | gpt-4.1 or claude-opus-4-5 | Better at multi-step reasoning | | Cost-sensitive batch | gpt-4.1-nano | Lowest cost per token | | Higher quality generation | gpt-4.1 + low temperature | More capability, less variation | Next steps The step loop and execution engine. Real-time event streaming API.", "token_count": 261} +{"id": "doc_e8794369a1a4", "path": "docs/llms/contracts.mdx", "url": "/llms/contracts", "title": "Contracts", "description": "LLMRequest and LLMResponse \u2014 the data that flows across the LLM boundary.", "headings": ["The flow", "LLMRequest", "LLMResponse", "Structured output", "Message types", "Next steps"], "content_sha256": "0ef6386027791d9122a28e938ca2f5752a67f5289f01989c62554bea9d29d5ac", "content": "Every LLM interaction in AFK flows through two typed contracts: LLMRequest (what you send) and LLMResponse (what you get back). These contracts normalize the differences between providers so your agent code never touches provider-specific types. The flow The adapter translates between AFK's normalized contracts and the provider's native format. LLMRequest | Field | Type | Purpose | | --- | --- | --- | | messages | list[Message] | Conversation history (system, user, assistant, tool) | | model | str | Model identifier | | tools | list[ToolSchema] | Available tool schemas | | temperature | float | Sampling temperature (0.0\u20132.0) | | max_tokens | int \\| None | Max output tokens | | top_p | float \\| None | Nucleus sampling | | response_format | ResponseFormat \\| None | Structured output format | | stop | list[str] \\| None | Stop sequences | LLMResponse | Field | Type | Purpose | | --- | --- | --- | | content | str \\| None | Text response from the model | | tool_calls | list[ToolCall] | Tool calls requested by the model | | model | str | Model that generated the response | | usage | Usage | Token counts (prompt, completion, total) | | finish_reason | str | Why generation stopped (stop, tool_calls, length) | | latency_ms | float | End-to-end request latency | Structured output Request structured JSON output with a Pydantic model: Message types | Role | Purpose | Source | | ----------- | ------------------ | ----------------------------- | | system | Agent instructions | From Agent.instructions | | user | User input | From user_message parameter | | assistant | Model responses | Generated by the LLM | | tool | Tool results | From tool execution | Next steps Built-in providers and custom adapters. Retry, caching, rate limiting, and circuit breaking.", "token_count": 175} +{"id": "doc_7320ab982fab", "path": "docs/llms/control-and-session.mdx", "url": "/llms/control-and-session", "title": "Control & Session", "description": "Retry, caching, rate limiting, and circuit breaking for LLM calls.", "headings": ["Policy pipeline", "Built-in profiles", "Development: no retry, no caching, fast failures", "Production: retry, circuit breaker, rate limiting, caching", "Individual policies", "Fallback chains", "If gpt-4.1 fails \u2192 try gpt-4.1-mini \u2192 try gpt-4.1-nano", "Tuning cheat sheet", "Next steps"], "content_sha256": "10d156ced976efb12c419c33d65e5686c7429902256c9e33be26a1add88c39a7", "content": "The LLM runtime includes built-in policies for handling transient failures, managing costs, and protecting against provider outages. Configure them via the builder profile or individual policy settings. Policy pipeline Every LLM request passes through this policy chain: Built-in profiles Use profile() to apply a curated set of policies: | Profile | Retry | Cache | Rate Limit | Circuit Breaker | Timeout | | ------------- | ------------------------------- | ------------------- | ---------- | ---------------------- | ------- | | development | None | None | None | None | 30s | | production | 3 attempts, exponential backoff | In-memory, 5min TTL | 60 req/min | 5 failures \u2192 30s open | 60s | | batch | 5 attempts, longer backoff | None | 20 req/min | 10 failures \u2192 60s open | 120s | Individual policies Configure each policy independently with create_llm_client(): Retry transient LLM failures with exponential backoff. | Parameter | Default | Description | | --- | --- | --- | | max_retries | 3 | Retry attempts after the initial request | | backoff_base_s | 0.5 | Initial delay in seconds | | backoff_jitter_s | 0.15 | Jitter added to retry delays | | require_idempotency_key | True | Require idempotency keys for retried requests | Stop calling a failing provider to prevent cascading failures. Prevent exceeding provider rate limits. Cache identical requests to reduce cost and latency. Cache keys are derived from request content, model settings, response model, session/checkpoint tokens, and any configured cache namespace. Cached rows do not retain provider request IDs, session tokens, checkpoint tokens, or raw provider payloads. Hard timeout on LLM requests. Fallback chains Configure a chain of models to try when the primary model fails: Tuning cheat sheet | Goal | Setting | | ----------------- | ------------------------------------------------------------- | | Reduce costs | Lower temperature, use cheaper model, enable caching | | Reduce latency | Enable caching, use faster model, set tight timeout | | Handle outages | Enable retry + circuit breaker, add fallback chain | | High throughput | Set rate limits high, use batch profile, increase concurrency | | Consistent output | Set temperature=0.0, enable structured output | Next steps How agents resolve and use LLM clients. Monitor LLM call latency, errors, and costs.", "token_count": 238} +{"id": "doc_f92685a34f7e", "path": "docs/llms/index.mdx", "url": "/llms/", "title": "LLM Layer", "description": "Provider-portable LLM runtime with retry, caching, circuit breaking, and middleware.", "headings": ["The LLMBuilder", "Middleware", "Built-in middleware", "Custom middleware", "Middleware protocols", "Supported providers", "Example model choices", "How agents use the LLM layer", "Option 1: Model name (auto-resolved)", "Option 2: Pre-built client (full control)", "Next steps"], "content_sha256": "32c7ac94efe23450adc9126031e9f87e3e689349369a82f600c67fbdd16f86a1", "content": "The LLM layer normalizes communication with language models across all supported providers. Your agent code uses provider-agnostic contracts (LLMRequest / LLMResponse) while built-in adapters handle the provider-specific details. The LLMBuilder Create LLM clients with the builder pattern: Middleware The LLM layer supports middleware for intercepting and transforming requests and responses. Use middleware for logging, tracing, caching, and custom request/response handling. Built-in middleware Custom middleware Middleware protocols | Protocol | Operation | Signature | | --- | --- | --- | | LLMChatMiddleware | Non-streaming chat | async (call_next, req: LLMRequest) -> LLMResponse | | LLMEmbedMiddleware | Embeddings | async (call_next, req: EmbeddingRequest) -> EmbeddingResponse | | LLMStreamMiddleware | Streaming chat | (call_next, req: LLMRequest) -> AsyncIterator[LLMStreamEvent] | Supported providers GPT-4.1, GPT-4.1-mini, GPT-4.1-nano, o-series Claude Opus and Sonnet families 100+ providers via the LiteLLM proxy All providers expose the same LLMClient interface. Your agent code never touches provider-specific types. Example model choices Use these as starting points, then verify model availability, pricing, and context limits with your provider account. | Scenario | Starting point | | -------------------------- | ----------------------------------------------- | | General purpose | OpenAI gpt-4.1-mini | | Complex reasoning | OpenAI gpt-4.1 or Anthropic claude-opus-4-5 | | Cost-sensitive | OpenAI gpt-4.1-nano | | Non-OpenAI/Anthropic model | LiteLLM adapter | | Custom or self-hosted | Custom adapter | How agents use the LLM layer You rarely build LLMClient directly. Agents resolve their model automatically: Next steps LLMRequest / LLMResponse \u2014 what flows across the boundary. Built-in providers and custom adapter registration.", "token_count": 176} diff --git a/skills/afk-coder/references/afk-docs/id-to-path.json b/skills/afk-coder/references/afk-docs/id-to-path.json new file mode 100644 index 0000000..ac2f619 --- /dev/null +++ b/skills/afk-coder/references/afk-docs/id-to-path.json @@ -0,0 +1,65 @@ +{ + "doc_3aa0b3792d22": "docs/docs.json", + "doc_deb460ffa5ee": "docs/index.mdx", + "doc_1c5f37ea0ad3": "docs/library/a2a.mdx", + "doc_b6088784caab": "docs/library/agent-skills.mdx", + "doc_f12bbbc027a7": "docs/library/agentic-behavior.mdx", + "doc_57e2139f0873": "docs/library/agentic-levels.mdx", + "doc_e67e762fb6ab": "docs/library/agents.mdx", + "doc_bbc97cbff254": "docs/library/api-reference.mdx", + "doc_4e1ebaf40267": "docs/library/architecture.mdx", + "doc_18d38fdacc9e": "docs/library/building-with-ai.mdx", + "doc_4a1cf8a90e83": "docs/library/checkpoint-schema.mdx", + "doc_a24b92541657": "docs/library/configuration-reference.mdx", + "doc_e7d508371e5c": "docs/library/core-runner.mdx", + "doc_3f1383f72c5f": "docs/library/debugger.mdx", + "doc_067033cfc945": "docs/library/deployment.mdx", + "doc_d147a81bac29": "docs/library/developer-guide.mdx", + "doc_61fd9f682847": "docs/library/environment-variables.mdx", + "doc_c93c115aeb85": "docs/library/evals.mdx", + "doc_a5120de838fb": "docs/library/examples/index.mdx", + "doc_4055659fe573": "docs/library/failure-policy-matrix.mdx", + "doc_2a18de009d56": "docs/library/full-module-reference.mdx", + "doc_3ae3aaf0c12c": "docs/library/how-to-use-afk.mdx", + "doc_f2ae50c638a4": "docs/library/learn-in-15-minutes.mdx", + "doc_5ee8bbdbd8a8": "docs/library/llm-interaction.mdx", + "doc_e34fe24f9585": "docs/library/mcp-server.mdx", + "doc_0383dbf277ba": "docs/library/memory.mdx", + "doc_50ac650fd8f6": "docs/library/mental-model.mdx", + "doc_75887f91c7df": "docs/library/messaging.mdx", + "doc_5a65ddab1445": "docs/library/migration.mdx", + "doc_78a8125d2c7e": "docs/library/observability.mdx", + "doc_f18f523b6ecc": "docs/library/overview.mdx", + "doc_f5f256549d84": "docs/library/performance.mdx", + "doc_3eeb75d383b8": "docs/library/public-imports-and-function-improvement.mdx", + "doc_bf91f8d5da74": "docs/library/quickstart.mdx", + "doc_f54217a0f2cf": "docs/library/run-event-contract.mdx", + "doc_567be1281e2f": "docs/library/security-model.mdx", + "doc_1e46cbc6bfae": "docs/library/snippets/01_minimal_chat_agent.mdx", + "doc_d6ad61a79371": "docs/library/snippets/02_policy_with_hitl.mdx", + "doc_a2e84472f1f9": "docs/library/snippets/03_subagents_with_router.mdx", + "doc_07174a8633ac": "docs/library/snippets/04_resume_and_compact.mdx", + "doc_3f60413b3a06": "docs/library/snippets/05_direct_llm_structured_output.mdx", + "doc_43ad71a4ad0c": "docs/library/snippets/06_tool_registry_security.mdx", + "doc_c346f13ce597": "docs/library/snippets/07_tool_hooks_and_middleware.mdx", + "doc_464a2b4a8d35": "docs/library/snippets/08_prebuilt_runtime_tools.mdx", + "doc_64090a20f830": "docs/library/snippets/09_system_prompt_loader.mdx", + "doc_7b26cbd9a7ae": "docs/library/snippets/10_streaming_chat_with_memory.mdx", + "doc_fdf48d427fc0": "docs/library/snippets/11_cost_monitoring.mdx", + "doc_40e30666bfa8": "docs/library/snippets/12_mcp_client_integration.mdx", + "doc_d1f7d83e0824": "docs/library/snippets/13_multi_model_fallback.mdx", + "doc_4462f67b1c03": "docs/library/snippets/14_production_client.mdx", + "doc_109157c0d2d7": "docs/library/streaming.mdx", + "doc_2798948c3080": "docs/library/system-prompts.mdx", + "doc_78b1717fafe3": "docs/library/task-queues.mdx", + "doc_618e34441fa7": "docs/library/tested-behaviors.mdx", + "doc_0ac01bce8c79": "docs/library/tool-call-lifecycle.mdx", + "doc_745f751f1344": "docs/library/tools-system-walkthrough.mdx", + "doc_9e9118ed2141": "docs/library/tools.mdx", + "doc_136d7a909d51": "docs/library/troubleshooting.mdx", + "doc_f1e32ed2ffce": "docs/llms/adapters.mdx", + "doc_eeed52120ccf": "docs/llms/agent-integration.mdx", + "doc_e8794369a1a4": "docs/llms/contracts.mdx", + "doc_7320ab982fab": "docs/llms/control-and-session.mdx", + "doc_f92685a34f7e": "docs/llms/index.mdx" +} diff --git a/skills/afk-coder/references/afk-docs/inverted-index.json b/skills/afk-coder/references/afk-docs/inverted-index.json new file mode 100644 index 0000000..abe2a15 --- /dev/null +++ b/skills/afk-coder/references/afk-docs/inverted-index.json @@ -0,0 +1,18851 @@ +{ + "a2a": [ + "doc_1c5f37ea0ad3", + "doc_2a18de009d56", + "doc_3aa0b3792d22", + "doc_4e1ebaf40267", + "doc_567be1281e2f", + "doc_57e2139f0873", + "doc_61fd9f682847", + "doc_bbc97cbff254", + "doc_d147a81bac29", + "doc_e34fe24f9585" + ], + "a2a-accessible": [ + "doc_1c5f37ea0ad3" + ], + "a2aservicehost": [ + "doc_2a18de009d56", + "doc_57e2139f0873" + ], + "abandonment": [ + "doc_618e34441fa7" + ], + "ability": [ + "doc_2798948c3080" + ], + "abort": [ + "doc_50ac650fd8f6" + ], + "aborts": [ + "doc_5ee8bbdbd8a8" + ], + "about": [ + "doc_109157c0d2d7", + "doc_43ad71a4ad0c", + "doc_4e1ebaf40267", + "doc_50ac650fd8f6", + "doc_5ee8bbdbd8a8", + "doc_b6088784caab" + ], + "above": [ + "doc_f54217a0f2cf" + ], + "absent": [ + "doc_3ae3aaf0c12c" + ], + "absolute": [ + "doc_64090a20f830", + "doc_a5120de838fb" + ], + "abstract": [ + "doc_0383dbf277ba", + "doc_2a18de009d56" + ], + "abstraction": [ + "doc_2a18de009d56" + ], + "abuse": [ + "doc_067033cfc945", + "doc_567be1281e2f" + ], + "accept": [ + "doc_43ad71a4ad0c", + "doc_bbc97cbff254" + ], + "acceptable": [ + "doc_3eeb75d383b8", + "doc_bbc97cbff254" + ], + "accepted": [ + "doc_109157c0d2d7", + "doc_a24b92541657", + "doc_f54217a0f2cf" + ], + "accepts": [ + "doc_a24b92541657" + ], + "access": [ + "doc_1c5f37ea0ad3", + "doc_1e46cbc6bfae", + "doc_43ad71a4ad0c", + "doc_464a2b4a8d35", + "doc_567be1281e2f", + "doc_57e2139f0873", + "doc_5a65ddab1445", + "doc_78b1717fafe3", + "doc_7b26cbd9a7ae", + "doc_9e9118ed2141", + "doc_a24b92541657", + "doc_a5120de838fb", + "doc_bbc97cbff254" + ], + "according": [ + "doc_07174a8633ac", + "doc_5ee8bbdbd8a8", + "doc_f12bbbc027a7" + ], + "account": [ + "doc_f92685a34f7e" + ], + "accumulate": [ + "doc_07174a8633ac" + ], + "accumulated": [ + "doc_4a1cf8a90e83" + ], + "accumulates": [ + "doc_5ee8bbdbd8a8" + ], + "acks": [ + "doc_75887f91c7df" + ], + "across": [ + "doc_0383dbf277ba", + "doc_109157c0d2d7", + "doc_1c5f37ea0ad3", + "doc_1e46cbc6bfae", + "doc_3ae3aaf0c12c", + "doc_57e2139f0873", + "doc_618e34441fa7", + "doc_61fd9f682847", + "doc_64090a20f830", + "doc_75887f91c7df", + "doc_7b26cbd9a7ae", + "doc_a24b92541657", + "doc_e34fe24f9585", + "doc_e7d508371e5c", + "doc_f5f256549d84", + "doc_f92685a34f7e" + ], + "act": [ + "doc_43ad71a4ad0c" + ], + "action": [ + "doc_0ac01bce8c79", + "doc_3ae3aaf0c12c", + "doc_4055659fe573", + "doc_567be1281e2f", + "doc_a24b92541657", + "doc_d6ad61a79371", + "doc_e67e762fb6ab", + "doc_f54217a0f2cf" + ], + "actions": [ + "doc_0ac01bce8c79", + "doc_3ae3aaf0c12c", + "doc_43ad71a4ad0c", + "doc_50ac650fd8f6", + "doc_567be1281e2f", + "doc_57e2139f0873", + "doc_618e34441fa7", + "doc_9e9118ed2141", + "doc_a24b92541657", + "doc_a5120de838fb", + "doc_b6088784caab", + "doc_d6ad61a79371" + ], + "active": [ + "doc_07174a8633ac" + ], + "activity": [ + "doc_07174a8633ac" + ], + "actual": [ + "doc_c346f13ce597" + ], + "acyclic": [ + "doc_f12bbbc027a7" + ], + "adapt": [ + "doc_0ac01bce8c79" + ], + "adapter": [ + "doc_1c5f37ea0ad3", + "doc_2a18de009d56", + "doc_4a1cf8a90e83", + "doc_4e1ebaf40267", + "doc_5ee8bbdbd8a8", + "doc_bbc97cbff254", + "doc_e8794369a1a4", + "doc_eeed52120ccf", + "doc_f1e32ed2ffce", + "doc_f92685a34f7e" + ], + "adapters": [ + "doc_2a18de009d56", + "doc_3aa0b3792d22", + "doc_4e1ebaf40267", + "doc_57e2139f0873", + "doc_618e34441fa7", + "doc_c93c115aeb85", + "doc_d147a81bac29", + "doc_e8794369a1a4", + "doc_eeed52120ccf", + "doc_f92685a34f7e" + ], + "add": [ + "doc_0383dbf277ba", + "doc_18d38fdacc9e", + "doc_2798948c3080", + "doc_2a18de009d56", + "doc_3ae3aaf0c12c", + "doc_3eeb75d383b8", + "doc_4e1ebaf40267", + "doc_567be1281e2f", + "doc_57e2139f0873", + "doc_7320ab982fab", + "doc_bf91f8d5da74", + "doc_d147a81bac29", + "doc_e67e762fb6ab", + "doc_f12bbbc027a7" + ], + "added": [ + "doc_57e2139f0873", + "doc_61fd9f682847", + "doc_7320ab982fab", + "doc_f54217a0f2cf" + ], + "adding": [ + "doc_18d38fdacc9e", + "doc_1c5f37ea0ad3", + "doc_618e34441fa7", + "doc_e67e762fb6ab" + ], + "additional": [ + "doc_464a2b4a8d35", + "doc_9e9118ed2141", + "doc_f12bbbc027a7" + ], + "additions": [ + "doc_f12bbbc027a7" + ], + "adds": [ + "doc_50ac650fd8f6", + "doc_57e2139f0873", + "doc_f54217a0f2cf" + ], + "adjust": [ + "doc_0ac01bce8c79" + ], + "admin": [ + "doc_567be1281e2f" + ], + "advanced": [ + "doc_a24b92541657", + "doc_a5120de838fb", + "doc_e67e762fb6ab", + "doc_e7d508371e5c" + ], + "advantages": [ + "doc_5a65ddab1445" + ], + "advisory": [ + "doc_0ac01bce8c79" + ], + "affect": [ + "doc_0ac01bce8c79" + ], + "affected": [ + "doc_3eeb75d383b8", + "doc_618e34441fa7", + "doc_d147a81bac29", + "doc_f18f523b6ecc" + ], + "afk": [ + "doc_0383dbf277ba", + "doc_067033cfc945", + "doc_109157c0d2d7", + "doc_136d7a909d51", + "doc_18d38fdacc9e", + "doc_1c5f37ea0ad3", + "doc_1e46cbc6bfae", + "doc_2798948c3080", + "doc_2a18de009d56", + "doc_3aa0b3792d22", + "doc_3ae3aaf0c12c", + "doc_3eeb75d383b8", + "doc_3f1383f72c5f", + "doc_3f60413b3a06", + "doc_4055659fe573", + "doc_40e30666bfa8", + "doc_43ad71a4ad0c", + "doc_464a2b4a8d35", + "doc_4a1cf8a90e83", + "doc_4e1ebaf40267", + "doc_50ac650fd8f6", + "doc_567be1281e2f", + "doc_57e2139f0873", + "doc_5a65ddab1445", + "doc_618e34441fa7", + "doc_61fd9f682847", + "doc_64090a20f830", + "doc_745f751f1344", + "doc_75887f91c7df", + "doc_78a8125d2c7e", + "doc_9e9118ed2141", + "doc_a24b92541657", + "doc_a2e84472f1f9", + "doc_a5120de838fb", + "doc_b6088784caab", + "doc_bbc97cbff254", + "doc_bf91f8d5da74", + "doc_c346f13ce597", + "doc_c93c115aeb85", + "doc_d147a81bac29", + "doc_d1f7d83e0824", + "doc_d6ad61a79371", + "doc_deb460ffa5ee", + "doc_e34fe24f9585", + "doc_e67e762fb6ab", + "doc_e8794369a1a4", + "doc_eeed52120ccf", + "doc_f12bbbc027a7", + "doc_f18f523b6ecc", + "doc_f1e32ed2ffce", + "doc_f2ae50c638a4", + "doc_f54217a0f2cf", + "doc_f5f256549d84", + "doc_fdf48d427fc0" + ], + "afk-coder": [ + "doc_b6088784caab", + "doc_d147a81bac29", + "doc_deb460ffa5ee" + ], + "afk-maintainer": [ + "doc_b6088784caab", + "doc_d147a81bac29", + "doc_deb460ffa5ee" + ], + "afk-mcp-server": [ + "doc_61fd9f682847" + ], + "afk-py": [ + "doc_d147a81bac29", + "doc_deb460ffa5ee" + ], + "afk-specific": [ + "doc_deb460ffa5ee" + ], + "afk_agent_prompts_dir": [ + "doc_61fd9f682847", + "doc_64090a20f830", + "doc_a24b92541657" + ], + "afk_allowed_commands": [ + "doc_61fd9f682847" + ], + "afk_cors_origins": [ + "doc_61fd9f682847" + ], + "afk_embed_model": [ + "doc_61fd9f682847" + ], + "afk_llm_api_base_url": [ + "doc_61fd9f682847" + ], + "afk_llm_api_key": [ + "doc_61fd9f682847" + ], + "afk_llm_backoff_base_s": [ + "doc_61fd9f682847" + ], + "afk_llm_backoff_jitter_s": [ + "doc_61fd9f682847" + ], + "afk_llm_json_max_retries": [ + "doc_61fd9f682847" + ], + "afk_llm_max_input_chars": [ + "doc_61fd9f682847" + ], + "afk_llm_max_retries": [ + "doc_61fd9f682847", + "doc_a24b92541657" + ], + "afk_llm_model": [ + "doc_61fd9f682847" + ], + "afk_llm_provider": [ + "doc_61fd9f682847" + ], + "afk_llm_provider_order": [ + "doc_61fd9f682847" + ], + "afk_llm_stream_idle_timeout_s": [ + "doc_61fd9f682847" + ], + "afk_llm_timeout_s": [ + "doc_61fd9f682847" + ], + "afk_mcp_allow_batch": [ + "doc_61fd9f682847" + ], + "afk_mcp_enable_health": [ + "doc_61fd9f682847" + ], + "afk_mcp_enable_sse": [ + "doc_61fd9f682847" + ], + "afk_mcp_health_path": [ + "doc_61fd9f682847" + ], + "afk_mcp_host": [ + "doc_61fd9f682847" + ], + "afk_mcp_instructions": [ + "doc_61fd9f682847" + ], + "afk_mcp_name": [ + "doc_61fd9f682847" + ], + "afk_mcp_path": [ + "doc_61fd9f682847" + ], + "afk_mcp_port": [ + "doc_61fd9f682847" + ], + "afk_mcp_sse_path": [ + "doc_61fd9f682847" + ], + "afk_mcp_version": [ + "doc_61fd9f682847" + ], + "afk_memory": [ + "doc_61fd9f682847" + ], + "afk_memory_backend": [ + "doc_61fd9f682847" + ], + "afk_pg_db": [ + "doc_61fd9f682847" + ], + "afk_pg_dsn": [ + "doc_61fd9f682847" + ], + "afk_pg_host": [ + "doc_61fd9f682847" + ], + "afk_pg_password": [ + "doc_61fd9f682847" + ], + "afk_pg_pool_max": [ + "doc_61fd9f682847" + ], + "afk_pg_pool_min": [ + "doc_61fd9f682847" + ], + "afk_pg_port": [ + "doc_61fd9f682847" + ], + "afk_pg_ssl": [ + "doc_61fd9f682847" + ], + "afk_pg_user": [ + "doc_61fd9f682847" + ], + "afk_queue_backend": [ + "doc_61fd9f682847" + ], + "afk_queue_redis_db": [ + "doc_61fd9f682847" + ], + "afk_queue_redis_host": [ + "doc_61fd9f682847" + ], + "afk_queue_redis_password": [ + "doc_61fd9f682847" + ], + "afk_queue_redis_port": [ + "doc_61fd9f682847" + ], + "afk_queue_redis_prefix": [ + "doc_61fd9f682847" + ], + "afk_queue_redis_url": [ + "doc_61fd9f682847" + ], + "afk_queue_retry_backoff_base_s": [ + "doc_61fd9f682847" + ], + "afk_queue_retry_backoff_jitter_s": [ + "doc_61fd9f682847" + ], + "afk_queue_retry_backoff_max_s": [ + "doc_61fd9f682847" + ], + "afk_redis_db": [ + "doc_61fd9f682847" + ], + "afk_redis_events_max": [ + "doc_61fd9f682847" + ], + "afk_redis_host": [ + "doc_61fd9f682847" + ], + "afk_redis_password": [ + "doc_61fd9f682847" + ], + "afk_redis_port": [ + "doc_61fd9f682847" + ], + "afk_redis_url": [ + "doc_61fd9f682847" + ], + "afk_sqlite_path": [ + "doc_61fd9f682847" + ], + "afk_vector_dim": [ + "doc_61fd9f682847" + ], + "after": [ + "doc_0383dbf277ba", + "doc_07174a8633ac", + "doc_0ac01bce8c79", + "doc_2798948c3080", + "doc_4055659fe573", + "doc_4a1cf8a90e83", + "doc_50ac650fd8f6", + "doc_5ee8bbdbd8a8", + "doc_7320ab982fab", + "doc_745f751f1344", + "doc_7b26cbd9a7ae", + "doc_9e9118ed2141", + "doc_a24b92541657", + "doc_c346f13ce597", + "doc_d1f7d83e0824", + "doc_e67e762fb6ab", + "doc_f54217a0f2cf" + ], + "again": [ + "doc_a24b92541657" + ], + "against": [ + "doc_1c5f37ea0ad3", + "doc_3f60413b3a06", + "doc_43ad71a4ad0c", + "doc_464a2b4a8d35", + "doc_50ac650fd8f6", + "doc_7320ab982fab", + "doc_745f751f1344", + "doc_9e9118ed2141", + "doc_c346f13ce597", + "doc_c93c115aeb85" + ], + "agent": [ + "doc_067033cfc945", + "doc_07174a8633ac", + "doc_0ac01bce8c79", + "doc_109157c0d2d7", + "doc_136d7a909d51", + "doc_18d38fdacc9e", + "doc_1c5f37ea0ad3", + "doc_1e46cbc6bfae", + "doc_2798948c3080", + "doc_2a18de009d56", + "doc_3ae3aaf0c12c", + "doc_3eeb75d383b8", + "doc_3f1383f72c5f", + "doc_3f60413b3a06", + "doc_4055659fe573", + "doc_40e30666bfa8", + "doc_43ad71a4ad0c", + "doc_464a2b4a8d35", + "doc_4a1cf8a90e83", + "doc_4e1ebaf40267", + "doc_50ac650fd8f6", + "doc_567be1281e2f", + "doc_57e2139f0873", + "doc_5a65ddab1445", + "doc_5ee8bbdbd8a8", + "doc_618e34441fa7", + "doc_61fd9f682847", + "doc_64090a20f830", + "doc_745f751f1344", + "doc_75887f91c7df", + "doc_78a8125d2c7e", + "doc_78b1717fafe3", + "doc_7b26cbd9a7ae", + "doc_9e9118ed2141", + "doc_a24b92541657", + "doc_a2e84472f1f9", + "doc_a5120de838fb", + "doc_b6088784caab", + "doc_bbc97cbff254", + "doc_bf91f8d5da74", + "doc_c93c115aeb85", + "doc_d147a81bac29", + "doc_d6ad61a79371", + "doc_deb460ffa5ee", + "doc_e34fe24f9585", + "doc_e67e762fb6ab", + "doc_e7d508371e5c", + "doc_e8794369a1a4", + "doc_eeed52120ccf", + "doc_f12bbbc027a7", + "doc_f18f523b6ecc", + "doc_f2ae50c638a4", + "doc_f54217a0f2cf", + "doc_f5f256549d84", + "doc_f92685a34f7e", + "doc_fdf48d427fc0" + ], + "agent-facing": [ + "doc_3eeb75d383b8", + "doc_bbc97cbff254", + "doc_d147a81bac29", + "doc_f18f523b6ecc" + ], + "agent-integration": [ + "doc_3aa0b3792d22" + ], + "agent-skills": [ + "doc_3aa0b3792d22" + ], + "agent-to-agent": [ + "doc_2a18de009d56", + "doc_e34fe24f9585" + ], + "agent_class": [ + "doc_64090a20f830" + ], + "agent_depth": [ + "doc_e7d508371e5c", + "doc_f54217a0f2cf" + ], + "agent_name": [ + "doc_2798948c3080", + "doc_4a1cf8a90e83", + "doc_5ee8bbdbd8a8", + "doc_64090a20f830", + "doc_e7d508371e5c", + "doc_f54217a0f2cf" + ], + "agent_path": [ + "doc_e7d508371e5c", + "doc_f54217a0f2cf" + ], + "agentcancellederror": [ + "doc_2a18de009d56" + ], + "agentcheckpointcorruptionerror": [ + "doc_07174a8633ac", + "doc_4a1cf8a90e83" + ], + "agentcommunicationprotocol": [ + "doc_1c5f37ea0ad3", + "doc_2a18de009d56" + ], + "agentdeadletter": [ + "doc_2a18de009d56" + ], + "agenterror": [ + "doc_2a18de009d56" + ], + "agentexecutionerror": [ + "doc_0ac01bce8c79", + "doc_2a18de009d56" + ], + "agentic": [ + "doc_3f60413b3a06", + "doc_b6088784caab" + ], + "agentic-behavior": [ + "doc_3aa0b3792d22" + ], + "agentic-levels": [ + "doc_3aa0b3792d22" + ], + "agentinvocationrequest": [ + "doc_1c5f37ea0ad3", + "doc_2a18de009d56", + "doc_50ac650fd8f6" + ], + "agentinvocationresponse": [ + "doc_2a18de009d56", + "doc_50ac650fd8f6" + ], + "agentresult": [ + "doc_0ac01bce8c79", + "doc_109157c0d2d7", + "doc_1e46cbc6bfae", + "doc_2a18de009d56", + "doc_3ae3aaf0c12c", + "doc_3f60413b3a06", + "doc_4a1cf8a90e83", + "doc_5ee8bbdbd8a8", + "doc_618e34441fa7", + "doc_745f751f1344", + "doc_a2e84472f1f9", + "doc_bbc97cbff254", + "doc_bf91f8d5da74", + "doc_d147a81bac29", + "doc_deb460ffa5ee", + "doc_e7d508371e5c", + "doc_f12bbbc027a7", + "doc_f18f523b6ecc", + "doc_f5f256549d84", + "doc_fdf48d427fc0" + ], + "agentrunevent": [ + "doc_0ac01bce8c79", + "doc_2a18de009d56", + "doc_50ac650fd8f6", + "doc_f54217a0f2cf" + ], + "agentrunhandle": [ + "doc_2a18de009d56" + ], + "agents": [ + "doc_0383dbf277ba", + "doc_067033cfc945", + "doc_136d7a909d51", + "doc_18d38fdacc9e", + "doc_1c5f37ea0ad3", + "doc_2a18de009d56", + "doc_3aa0b3792d22", + "doc_3ae3aaf0c12c", + "doc_3eeb75d383b8", + "doc_3f1383f72c5f", + "doc_40e30666bfa8", + "doc_43ad71a4ad0c", + "doc_464a2b4a8d35", + "doc_4e1ebaf40267", + "doc_50ac650fd8f6", + "doc_567be1281e2f", + "doc_57e2139f0873", + "doc_618e34441fa7", + "doc_61fd9f682847", + "doc_64090a20f830", + "doc_7320ab982fab", + "doc_75887f91c7df", + "doc_9e9118ed2141", + "doc_a24b92541657", + "doc_a5120de838fb", + "doc_b6088784caab", + "doc_bbc97cbff254", + "doc_c93c115aeb85", + "doc_d147a81bac29", + "doc_deb460ffa5ee", + "doc_e34fe24f9585", + "doc_e67e762fb6ab", + "doc_e7d508371e5c", + "doc_eeed52120ccf", + "doc_f12bbbc027a7", + "doc_f18f523b6ecc", + "doc_f1e32ed2ffce", + "doc_f2ae50c638a4", + "doc_f92685a34f7e", + "doc_fdf48d427fc0" + ], + "agentstate": [ + "doc_2a18de009d56" + ], + "agentstreamevent": [ + "doc_109157c0d2d7", + "doc_2a18de009d56", + "doc_5ee8bbdbd8a8", + "doc_bbc97cbff254" + ], + "agentstreamhandle": [ + "doc_109157c0d2d7", + "doc_2a18de009d56", + "doc_bbc97cbff254", + "doc_f2ae50c638a4" + ], + "aggregate": [ + "doc_4a1cf8a90e83", + "doc_d1f7d83e0824", + "doc_e7d508371e5c", + "doc_f18f523b6ecc" + ], + "aggregated": [ + "doc_2a18de009d56", + "doc_bbc97cbff254", + "doc_bf91f8d5da74" + ], + "aggregates": [ + "doc_4a1cf8a90e83" + ], + "aggregation": [ + "doc_78a8125d2c7e" + ], + "ai": [ + "doc_18d38fdacc9e", + "doc_3ae3aaf0c12c", + "doc_57e2139f0873", + "doc_d147a81bac29", + "doc_deb460ffa5ee", + "doc_e34fe24f9585", + "doc_e67e762fb6ab", + "doc_f18f523b6ecc" + ], + "alert": [ + "doc_067033cfc945", + "doc_07174a8633ac", + "doc_75887f91c7df", + "doc_78a8125d2c7e" + ], + "alerting": [ + "doc_067033cfc945", + "doc_18d38fdacc9e", + "doc_618e34441fa7", + "doc_78a8125d2c7e" + ], + "alerts": [ + "doc_18d38fdacc9e", + "doc_567be1281e2f" + ], + "algorithm": [ + "doc_64090a20f830" + ], + "alias": [ + "doc_64090a20f830" + ], + "aligned": [ + "doc_d147a81bac29" + ], + "all": [ + "doc_0383dbf277ba", + "doc_07174a8633ac", + "doc_0ac01bce8c79", + "doc_136d7a909d51", + "doc_18d38fdacc9e", + "doc_1c5f37ea0ad3", + "doc_1e46cbc6bfae", + "doc_3ae3aaf0c12c", + "doc_3f60413b3a06", + "doc_4055659fe573", + "doc_40e30666bfa8", + "doc_43ad71a4ad0c", + "doc_4462f67b1c03", + "doc_464a2b4a8d35", + "doc_4a1cf8a90e83", + "doc_4e1ebaf40267", + "doc_567be1281e2f", + "doc_57e2139f0873", + "doc_618e34441fa7", + "doc_61fd9f682847", + "doc_745f751f1344", + "doc_75887f91c7df", + "doc_78b1717fafe3", + "doc_9e9118ed2141", + "doc_a24b92541657", + "doc_a2e84472f1f9", + "doc_b6088784caab", + "doc_c346f13ce597", + "doc_d1f7d83e0824", + "doc_d6ad61a79371", + "doc_e34fe24f9585", + "doc_e7d508371e5c", + "doc_eeed52120ccf", + "doc_f12bbbc027a7", + "doc_f1e32ed2ffce", + "doc_f92685a34f7e" + ], + "all__": [ + "doc_3eeb75d383b8", + "doc_d147a81bac29" + ], + "all_required": [ + "doc_4055659fe573", + "doc_f12bbbc027a7" + ], + "allow": [ + "doc_0ac01bce8c79", + "doc_43ad71a4ad0c", + "doc_50ac650fd8f6", + "doc_567be1281e2f", + "doc_618e34441fa7", + "doc_61fd9f682847", + "doc_745f751f1344", + "doc_9e9118ed2141", + "doc_a24b92541657", + "doc_d6ad61a79371", + "doc_e67e762fb6ab", + "doc_f12bbbc027a7" + ], + "allow_command_execution": [ + "doc_a24b92541657" + ], + "allow_network": [ + "doc_a24b92541657" + ], + "allow_optional_failures": [ + "doc_18d38fdacc9e", + "doc_f12bbbc027a7" + ], + "allowalla2aauthprovider": [ + "doc_2a18de009d56" + ], + "allowed": [ + "doc_618e34441fa7", + "doc_a24b92541657", + "doc_e34fe24f9585" + ], + "allowed_command_prefixes": [ + "doc_a24b92541657" + ], + "allowed_paths": [ + "doc_a24b92541657" + ], + "allowlist": [ + "doc_61fd9f682847", + "doc_b6088784caab" + ], + "allowlisted": [ + "doc_9e9118ed2141", + "doc_a24b92541657" + ], + "allowlists": [ + "doc_464a2b4a8d35", + "doc_618e34441fa7", + "doc_9e9118ed2141" + ], + "allows": [ + "doc_1c5f37ea0ad3", + "doc_4a1cf8a90e83", + "doc_745f751f1344", + "doc_b6088784caab" + ], + "along": [ + "doc_0ac01bce8c79" + ], + "alongside": [ + "doc_a2e84472f1f9", + "doc_b6088784caab" + ], + "already": [ + "doc_07174a8633ac", + "doc_4a1cf8a90e83", + "doc_f5f256549d84" + ], + "already-completed": [ + "doc_4a1cf8a90e83" + ], + "also": [ + "doc_3f1383f72c5f", + "doc_4a1cf8a90e83", + "doc_64090a20f830", + "doc_9e9118ed2141", + "doc_bbc97cbff254", + "doc_d1f7d83e0824" + ], + "always": [ + "doc_0383dbf277ba", + "doc_067033cfc945", + "doc_0ac01bce8c79", + "doc_18d38fdacc9e", + "doc_1c5f37ea0ad3", + "doc_1e46cbc6bfae", + "doc_4e1ebaf40267", + "doc_50ac650fd8f6", + "doc_567be1281e2f", + "doc_61fd9f682847", + "doc_75887f91c7df", + "doc_b6088784caab", + "doc_e34fe24f9585", + "doc_e67e762fb6ab", + "doc_f54217a0f2cf", + "doc_fdf48d427fc0" + ], + "analysis": [ + "doc_618e34441fa7", + "doc_d1f7d83e0824", + "doc_f12bbbc027a7" + ], + "analytics": [ + "doc_4055659fe573" + ], + "and_": [ + "doc_2798948c3080" + ], + "annotate": [ + "doc_c346f13ce597" + ], + "annotated": [ + "doc_9e9118ed2141" + ], + "annotation": [ + "doc_c346f13ce597" + ], + "anomalies": [ + "doc_567be1281e2f" + ], + "anomaly": [ + "doc_78a8125d2c7e" + ], + "another": [ + "doc_5ee8bbdbd8a8", + "doc_618e34441fa7", + "doc_b6088784caab", + "doc_deb460ffa5ee" + ], + "answer": [ + "doc_18d38fdacc9e", + "doc_4055659fe573", + "doc_f12bbbc027a7" + ], + "answering": [ + "doc_18d38fdacc9e" + ], + "anthropic": [ + "doc_5ee8bbdbd8a8", + "doc_f1e32ed2ffce", + "doc_f92685a34f7e" + ], + "anthropic_agent": [ + "doc_3f60413b3a06", + "doc_61fd9f682847" + ], + "anthropic_api_key": [ + "doc_61fd9f682847" + ], + "anthropicagentprovider": [ + "doc_2a18de009d56" + ], + "anti-pattern": [ + "doc_18d38fdacc9e" + ], + "anti-patterns": [ + "doc_067033cfc945", + "doc_136d7a909d51", + "doc_18d38fdacc9e", + "doc_567be1281e2f", + "doc_c93c115aeb85" + ], + "any": [ + "doc_0383dbf277ba", + "doc_07174a8633ac", + "doc_0ac01bce8c79", + "doc_2798948c3080", + "doc_3f1383f72c5f", + "doc_3f60413b3a06", + "doc_4055659fe573", + "doc_43ad71a4ad0c", + "doc_464a2b4a8d35", + "doc_4a1cf8a90e83", + "doc_4e1ebaf40267", + "doc_5ee8bbdbd8a8", + "doc_618e34441fa7", + "doc_64090a20f830", + "doc_7320ab982fab", + "doc_745f751f1344", + "doc_75887f91c7df", + "doc_78a8125d2c7e", + "doc_a24b92541657", + "doc_a2e84472f1f9", + "doc_b6088784caab", + "doc_d6ad61a79371", + "doc_e34fe24f9585", + "doc_e67e762fb6ab", + "doc_f12bbbc027a7" + ], + "anyone": [ + "doc_1c5f37ea0ad3", + "doc_e34fe24f9585" + ], + "anything": [ + "doc_2798948c3080", + "doc_9e9118ed2141" + ], + "api": [ + "doc_067033cfc945", + "doc_07174a8633ac", + "doc_109157c0d2d7", + "doc_136d7a909d51", + "doc_1c5f37ea0ad3", + "doc_1e46cbc6bfae", + "doc_3aa0b3792d22", + "doc_3eeb75d383b8", + "doc_3f60413b3a06", + "doc_4055659fe573", + "doc_567be1281e2f", + "doc_57e2139f0873", + "doc_5a65ddab1445", + "doc_618e34441fa7", + "doc_61fd9f682847", + "doc_78a8125d2c7e", + "doc_7b26cbd9a7ae", + "doc_a24b92541657", + "doc_b6088784caab", + "doc_bbc97cbff254", + "doc_c346f13ce597", + "doc_c93c115aeb85", + "doc_d147a81bac29", + "doc_d1f7d83e0824", + "doc_e67e762fb6ab", + "doc_e7d508371e5c", + "doc_eeed52120ccf", + "doc_f18f523b6ecc", + "doc_fdf48d427fc0" + ], + "api-reference": [ + "doc_3aa0b3792d22" + ], + "api_key": [ + "doc_3f1383f72c5f" + ], + "apikeya2aauthprovider": [ + "doc_2a18de009d56" + ], + "apis": [ + "doc_0383dbf277ba", + "doc_2a18de009d56", + "doc_43ad71a4ad0c", + "doc_61fd9f682847", + "doc_9e9118ed2141", + "doc_bbc97cbff254", + "doc_f1e32ed2ffce", + "doc_f5f256549d84" + ], + "apologize": [ + "doc_a24b92541657" + ], + "app": [ + "doc_f18f523b6ecc" + ], + "append": [ + "doc_a24b92541657" + ], + "append-only": [ + "doc_f54217a0f2cf" + ], + "appended": [ + "doc_5ee8bbdbd8a8", + "doc_745f751f1344", + "doc_9e9118ed2141" + ], + "appends": [ + "doc_0ac01bce8c79" + ], + "applicable": [ + "doc_57e2139f0873" + ], + "application": [ + "doc_bbc97cbff254", + "doc_c93c115aeb85", + "doc_d147a81bac29", + "doc_deb460ffa5ee" + ], + "applications": [ + "doc_57e2139f0873", + "doc_7b26cbd9a7ae", + "doc_b6088784caab", + "doc_bbc97cbff254", + "doc_d6ad61a79371", + "doc_deb460ffa5ee", + "doc_e7d508371e5c", + "doc_f18f523b6ecc", + "doc_f2ae50c638a4" + ], + "applied": [ + "doc_64090a20f830", + "doc_a24b92541657", + "doc_e67e762fb6ab" + ], + "applies": [ + "doc_0383dbf277ba", + "doc_4055659fe573", + "doc_5ee8bbdbd8a8", + "doc_745f751f1344", + "doc_9e9118ed2141", + "doc_c346f13ce597" + ], + "apply": [ + "doc_3f60413b3a06", + "doc_40e30666bfa8", + "doc_4462f67b1c03", + "doc_7320ab982fab", + "doc_bbc97cbff254", + "doc_c346f13ce597", + "doc_e34fe24f9585" + ], + "apply_event_retention": [ + "doc_2a18de009d56" + ], + "apply_state_retention": [ + "doc_2a18de009d56" + ], + "apply_tool_output_limits": [ + "doc_745f751f1344" + ], + "approach": [ + "doc_0ac01bce8c79", + "doc_3ae3aaf0c12c", + "doc_3f60413b3a06", + "doc_5a65ddab1445", + "doc_f54217a0f2cf" + ], + "appropriate": [ + "doc_3ae3aaf0c12c", + "doc_618e34441fa7", + "doc_c346f13ce597" + ], + "approval": [ + "doc_0383dbf277ba", + "doc_07174a8633ac", + "doc_0ac01bce8c79", + "doc_18d38fdacc9e", + "doc_2a18de009d56", + "doc_3ae3aaf0c12c", + "doc_43ad71a4ad0c", + "doc_464a2b4a8d35", + "doc_4a1cf8a90e83", + "doc_50ac650fd8f6", + "doc_745f751f1344", + "doc_a24b92541657", + "doc_a5120de838fb", + "doc_d6ad61a79371" + ], + "approval_denial_policy": [ + "doc_0ac01bce8c79", + "doc_a24b92541657" + ], + "approval_fallback": [ + "doc_0ac01bce8c79", + "doc_a24b92541657", + "doc_d6ad61a79371" + ], + "approval_timeout_s": [ + "doc_0ac01bce8c79", + "doc_a24b92541657", + "doc_d6ad61a79371" + ], + "approvaldecision": [ + "doc_d6ad61a79371" + ], + "approvalrequest": [ + "doc_d6ad61a79371" + ], + "approved": [ + "doc_0ac01bce8c79", + "doc_5ee8bbdbd8a8", + "doc_d6ad61a79371" + ], + "apps": [ + "doc_a24b92541657" + ], + "architecture": [ + "doc_07174a8633ac", + "doc_1c5f37ea0ad3", + "doc_2a18de009d56", + "doc_3aa0b3792d22", + "doc_3f1383f72c5f", + "doc_40e30666bfa8", + "doc_464a2b4a8d35", + "doc_4e1ebaf40267", + "doc_64090a20f830", + "doc_c346f13ce597", + "doc_e34fe24f9585", + "doc_f18f523b6ecc" + ], + "area": [ + "doc_18d38fdacc9e", + "doc_567be1281e2f", + "doc_d147a81bac29" + ], + "areas": [ + "doc_d147a81bac29" + ], + "args": [ + "doc_745f751f1344", + "doc_9e9118ed2141", + "doc_bbc97cbff254" + ], + "args_model": [ + "doc_745f751f1344", + "doc_9e9118ed2141", + "doc_a24b92541657", + "doc_c346f13ce597" + ], + "argument": [ + "doc_2a18de009d56", + "doc_3ae3aaf0c12c", + "doc_43ad71a4ad0c", + "doc_64090a20f830", + "doc_745f751f1344", + "doc_9e9118ed2141", + "doc_a24b92541657", + "doc_bf91f8d5da74", + "doc_c346f13ce597", + "doc_e67e762fb6ab" + ], + "arguments": [ + "doc_0ac01bce8c79", + "doc_18d38fdacc9e", + "doc_1e46cbc6bfae", + "doc_4055659fe573", + "doc_43ad71a4ad0c", + "doc_4a1cf8a90e83", + "doc_50ac650fd8f6", + "doc_5a65ddab1445", + "doc_61fd9f682847", + "doc_745f751f1344", + "doc_78b1717fafe3", + "doc_9e9118ed2141", + "doc_a24b92541657", + "doc_bf91f8d5da74", + "doc_c346f13ce597", + "doc_e67e762fb6ab", + "doc_f12bbbc027a7", + "doc_f2ae50c638a4" + ], + "around": [ + "doc_18d38fdacc9e", + "doc_2a18de009d56", + "doc_9e9118ed2141", + "doc_c346f13ce597" + ], + "arpan404": [ + "doc_3aa0b3792d22" + ], + "arrive": [ + "doc_618e34441fa7" + ], + "arrives": [ + "doc_f54217a0f2cf" + ], + "arriving": [ + "doc_618e34441fa7" + ], + "arrow": [ + "doc_3aa0b3792d22" + ], + "artifact": [ + "doc_618e34441fa7" + ], + "arun_case": [ + "doc_2a18de009d56" + ], + "arun_suite": [ + "doc_2a18de009d56" + ], + "ask": [ + "doc_a24b92541657" + ], + "asks": [ + "doc_0ac01bce8c79" + ], + "aspects": [ + "doc_a2e84472f1f9" + ], + "assertion": [ + "doc_2a18de009d56", + "doc_618e34441fa7", + "doc_c93c115aeb85" + ], + "assertions": [ + "doc_2798948c3080", + "doc_4e1ebaf40267", + "doc_618e34441fa7", + "doc_c93c115aeb85", + "doc_f54217a0f2cf" + ], + "asserts": [ + "doc_618e34441fa7" + ], + "assistant": [ + "doc_0383dbf277ba", + "doc_4a1cf8a90e83", + "doc_5a65ddab1445", + "doc_5ee8bbdbd8a8", + "doc_bbc97cbff254", + "doc_bf91f8d5da74", + "doc_e8794369a1a4" + ], + "assistants": [ + "doc_5a65ddab1445" + ], + "assurance": [ + "doc_50ac650fd8f6" + ], + "async": [ + "doc_1c5f37ea0ad3", + "doc_1e46cbc6bfae", + "doc_4a1cf8a90e83", + "doc_57e2139f0873", + "doc_61fd9f682847", + "doc_75887f91c7df", + "doc_9e9118ed2141", + "doc_bbc97cbff254", + "doc_bf91f8d5da74", + "doc_c346f13ce597", + "doc_d147a81bac29", + "doc_deb460ffa5ee", + "doc_e7d508371e5c", + "doc_f54217a0f2cf", + "doc_f5f256549d84", + "doc_f92685a34f7e" + ], + "asyncevalassertion": [ + "doc_2a18de009d56" + ], + "asynchronous": [ + "doc_4a1cf8a90e83", + "doc_a24b92541657" + ], + "asynchronously": [ + "doc_57e2139f0873" + ], + "asyncio": [ + "doc_745f751f1344" + ], + "asynciterator": [ + "doc_c346f13ce597", + "doc_f92685a34f7e" + ], + "at-least-once": [ + "doc_75887f91c7df" + ], + "atomic": [ + "doc_0383dbf277ba" + ], + "attach": [ + "doc_3f1383f72c5f", + "doc_3f60413b3a06", + "doc_40e30666bfa8", + "doc_a5120de838fb", + "doc_bf91f8d5da74", + "doc_e67e762fb6ab" + ], + "attached": [ + "doc_40e30666bfa8", + "doc_745f751f1344", + "doc_9e9118ed2141", + "doc_e34fe24f9585", + "doc_f2ae50c638a4" + ], + "attaching": [ + "doc_c346f13ce597" + ], + "attacks": [ + "doc_464a2b4a8d35" + ], + "attempt": [ + "doc_464a2b4a8d35", + "doc_4a1cf8a90e83", + "doc_f12bbbc027a7" + ], + "attempts": [ + "doc_464a2b4a8d35", + "doc_61fd9f682847", + "doc_7320ab982fab", + "doc_75887f91c7df", + "doc_eeed52120ccf" + ], + "attributes": [ + "doc_0ac01bce8c79" + ], + "audience": [ + "doc_1c5f37ea0ad3" + ], + "audit": [ + "doc_0ac01bce8c79", + "doc_43ad71a4ad0c", + "doc_50ac650fd8f6", + "doc_618e34441fa7", + "doc_745f751f1344", + "doc_9e9118ed2141", + "doc_c346f13ce597", + "doc_d6ad61a79371", + "doc_f54217a0f2cf" + ], + "auditability": [ + "doc_4a1cf8a90e83" + ], + "auth": [ + "doc_1c5f37ea0ad3", + "doc_2a18de009d56", + "doc_4055659fe573", + "doc_4e1ebaf40267", + "doc_567be1281e2f", + "doc_57e2139f0873", + "doc_5ee8bbdbd8a8", + "doc_78b1717fafe3", + "doc_d147a81bac29", + "doc_e34fe24f9585", + "doc_eeed52120ccf" + ], + "authenticate": [ + "doc_1c5f37ea0ad3", + "doc_e34fe24f9585" + ], + "authenticated": [ + "doc_57e2139f0873" + ], + "authentication": [ + "doc_1c5f37ea0ad3", + "doc_40e30666bfa8", + "doc_567be1281e2f", + "doc_61fd9f682847" + ], + "authorization": [ + "doc_1c5f37ea0ad3", + "doc_3f1383f72c5f", + "doc_567be1281e2f" + ], + "auto": [ + "doc_5ee8bbdbd8a8" + ], + "auto-detected": [ + "doc_64090a20f830" + ], + "auto-detection": [ + "doc_2798948c3080", + "doc_64090a20f830" + ], + "auto-register": [ + "doc_a24b92541657" + ], + "auto-resolved": [ + "doc_d6ad61a79371" + ], + "auto-resolves": [ + "doc_d6ad61a79371" + ], + "auto-scaling": [ + "doc_067033cfc945" + ], + "auto-select": [ + "doc_0383dbf277ba" + ], + "automated": [ + "doc_a24b92541657", + "doc_d6ad61a79371" + ], + "automatic": [ + "doc_78b1717fafe3", + "doc_a5120de838fb" + ], + "automatically": [ + "doc_0383dbf277ba", + "doc_067033cfc945", + "doc_2798948c3080", + "doc_57e2139f0873", + "doc_64090a20f830", + "doc_9e9118ed2141", + "doc_a24b92541657", + "doc_b6088784caab", + "doc_e67e762fb6ab", + "doc_eeed52120ccf", + "doc_f92685a34f7e" + ], + "autonomously": [ + "doc_b6088784caab" + ], + "availability": [ + "doc_50ac650fd8f6", + "doc_f92685a34f7e" + ], + "available": [ + "doc_0ac01bce8c79", + "doc_136d7a909d51", + "doc_2798948c3080", + "doc_3f60413b3a06", + "doc_40e30666bfa8", + "doc_464a2b4a8d35", + "doc_567be1281e2f", + "doc_64090a20f830", + "doc_745f751f1344", + "doc_7b26cbd9a7ae", + "doc_9e9118ed2141", + "doc_b6088784caab", + "doc_bbc97cbff254", + "doc_bf91f8d5da74", + "doc_d6ad61a79371", + "doc_e34fe24f9585", + "doc_e8794369a1a4", + "doc_f12bbbc027a7" + ], + "average": [ + "doc_78a8125d2c7e" + ], + "avg_llm_latency_ms": [ + "doc_618e34441fa7" + ], + "avoid": [ + "doc_3eeb75d383b8", + "doc_f5f256549d84" + ], + "avoiding": [ + "doc_18d38fdacc9e" + ], + "await": [ + "doc_bbc97cbff254" + ], + "awaitable": [ + "doc_e7d508371e5c" + ], + "away": [ + "doc_7b26cbd9a7ae" + ], + "aws": [ + "doc_067033cfc945" + ], + "azure": [ + "doc_f1e32ed2ffce" + ], + "b6e4f": [ + "doc_3aa0b3792d22" + ], + "back": [ + "doc_0383dbf277ba", + "doc_0ac01bce8c79", + "doc_3f60413b3a06", + "doc_5ee8bbdbd8a8", + "doc_61fd9f682847", + "doc_745f751f1344", + "doc_9e9118ed2141", + "doc_a24b92541657", + "doc_a2e84472f1f9", + "doc_bf91f8d5da74", + "doc_e7d508371e5c", + "doc_e8794369a1a4", + "doc_f12bbbc027a7" + ], + "backed": [ + "doc_0383dbf277ba" + ], + "backend": [ + "doc_0383dbf277ba", + "doc_136d7a909d51", + "doc_18d38fdacc9e", + "doc_2a18de009d56", + "doc_3f60413b3a06", + "doc_4055659fe573", + "doc_4e1ebaf40267", + "doc_61fd9f682847", + "doc_78a8125d2c7e", + "doc_7b26cbd9a7ae", + "doc_a24b92541657", + "doc_f5f256549d84" + ], + "backend-specific": [ + "doc_a24b92541657" + ], + "backends": [ + "doc_0383dbf277ba", + "doc_2a18de009d56", + "doc_5a65ddab1445", + "doc_618e34441fa7", + "doc_61fd9f682847", + "doc_75887f91c7df", + "doc_78b1717fafe3", + "doc_f5f256549d84" + ], + "background": [ + "doc_0383dbf277ba", + "doc_07174a8633ac", + "doc_109157c0d2d7", + "doc_3f1383f72c5f", + "doc_4a1cf8a90e83", + "doc_57e2139f0873", + "doc_9e9118ed2141", + "doc_a24b92541657", + "doc_f54217a0f2cf", + "doc_f5f256549d84" + ], + "background_tool_default_grace_s": [ + "doc_a24b92541657" + ], + "background_tool_interrupt_on_resolve": [ + "doc_a24b92541657" + ], + "background_tool_max_pending": [ + "doc_a24b92541657" + ], + "background_tool_poll_interval_s": [ + "doc_a24b92541657" + ], + "background_tool_result_ttl_s": [ + "doc_a24b92541657" + ], + "background_tools_enabled": [ + "doc_a24b92541657" + ], + "backing": [ + "doc_618e34441fa7" + ], + "backoff": [ + "doc_4055659fe573", + "doc_5ee8bbdbd8a8", + "doc_618e34441fa7", + "doc_61fd9f682847", + "doc_7320ab982fab", + "doc_75887f91c7df", + "doc_78b1717fafe3", + "doc_a24b92541657", + "doc_eeed52120ccf" + ], + "backoff_base_s": [ + "doc_7320ab982fab" + ], + "backoff_jitter_s": [ + "doc_7320ab982fab" + ], + "backpressure": [ + "doc_2a18de009d56", + "doc_57e2139f0873", + "doc_618e34441fa7", + "doc_a24b92541657", + "doc_f12bbbc027a7" + ], + "backward": [ + "doc_4e1ebaf40267" + ], + "balance": [ + "doc_eeed52120ccf" + ], + "base": [ + "doc_18d38fdacc9e", + "doc_2a18de009d56", + "doc_3eeb75d383b8", + "doc_61fd9f682847", + "doc_b6088784caab", + "doc_d147a81bac29" + ], + "baseagent": [ + "doc_07174a8633ac", + "doc_2a18de009d56" + ], + "based": [ + "doc_18d38fdacc9e", + "doc_2798948c3080", + "doc_9e9118ed2141", + "doc_b6088784caab", + "doc_f12bbbc027a7" + ], + "basemodel": [ + "doc_a24b92541657" + ], + "basetool": [ + "doc_745f751f1344" + ], + "basic": [ + "doc_0383dbf277ba", + "doc_067033cfc945", + "doc_3f1383f72c5f", + "doc_5a65ddab1445", + "doc_64090a20f830", + "doc_d1f7d83e0824", + "doc_d6ad61a79371" + ], + "basics": [ + "doc_a5120de838fb" + ], + "batch": [ + "doc_0383dbf277ba", + "doc_0ac01bce8c79", + "doc_4a1cf8a90e83", + "doc_57e2139f0873", + "doc_5ee8bbdbd8a8", + "doc_7320ab982fab", + "doc_745f751f1344", + "doc_78b1717fafe3", + "doc_a24b92541657", + "doc_a5120de838fb", + "doc_d1f7d83e0824", + "doc_d6ad61a79371", + "doc_eeed52120ccf", + "doc_f12bbbc027a7", + "doc_f54217a0f2cf", + "doc_fdf48d427fc0" + ], + "batch-level": [ + "doc_0ac01bce8c79" + ], + "batched": [ + "doc_61fd9f682847" + ], + "batches": [ + "doc_618e34441fa7", + "doc_fdf48d427fc0" + ], + "because": [ + "doc_bbc97cbff254" + ], + "become": [ + "doc_1e46cbc6bfae", + "doc_64090a20f830", + "doc_f12bbbc027a7" + ], + "bedrock": [ + "doc_f1e32ed2ffce" + ], + "been": [ + "doc_4a1cf8a90e83", + "doc_618e34441fa7" + ], + "before": [ + "doc_0383dbf277ba", + "doc_07174a8633ac", + "doc_0ac01bce8c79", + "doc_136d7a909d51", + "doc_18d38fdacc9e", + "doc_3ae3aaf0c12c", + "doc_3eeb75d383b8", + "doc_43ad71a4ad0c", + "doc_464a2b4a8d35", + "doc_4a1cf8a90e83", + "doc_5ee8bbdbd8a8", + "doc_618e34441fa7", + "doc_745f751f1344", + "doc_9e9118ed2141", + "doc_a24b92541657", + "doc_c346f13ce597", + "doc_d147a81bac29", + "doc_d6ad61a79371", + "doc_deb460ffa5ee", + "doc_e67e762fb6ab", + "doc_f18f523b6ecc", + "doc_f2ae50c638a4", + "doc_f54217a0f2cf", + "doc_f5f256549d84" + ], + "beginner": [ + "doc_a5120de838fb" + ], + "beginning": [ + "doc_f54217a0f2cf" + ], + "begins": [ + "doc_0ac01bce8c79", + "doc_f54217a0f2cf" + ], + "behave": [ + "doc_64090a20f830", + "doc_e34fe24f9585" + ], + "behaves": [ + "doc_2798948c3080", + "doc_e67e762fb6ab" + ], + "behavior": [ + "doc_0ac01bce8c79", + "doc_109157c0d2d7", + "doc_136d7a909d51", + "doc_18d38fdacc9e", + "doc_1e46cbc6bfae", + "doc_2798948c3080", + "doc_2a18de009d56", + "doc_3ae3aaf0c12c", + "doc_3eeb75d383b8", + "doc_3f1383f72c5f", + "doc_4055659fe573", + "doc_4a1cf8a90e83", + "doc_4e1ebaf40267", + "doc_50ac650fd8f6", + "doc_5a65ddab1445", + "doc_5ee8bbdbd8a8", + "doc_618e34441fa7", + "doc_61fd9f682847", + "doc_745f751f1344", + "doc_75887f91c7df", + "doc_78a8125d2c7e", + "doc_a24b92541657", + "doc_a2e84472f1f9", + "doc_bbc97cbff254", + "doc_c346f13ce597", + "doc_c93c115aeb85", + "doc_d147a81bac29", + "doc_d6ad61a79371", + "doc_e67e762fb6ab", + "doc_eeed52120ccf", + "doc_f18f523b6ecc" + ], + "behavior-first": [ + "doc_3ae3aaf0c12c" + ], + "behavioral": [ + "doc_18d38fdacc9e", + "doc_2798948c3080", + "doc_618e34441fa7", + "doc_78a8125d2c7e" + ], + "behaviors": [ + "doc_50ac650fd8f6", + "doc_618e34441fa7", + "doc_d147a81bac29", + "doc_f18f523b6ecc" + ], + "behind": [ + "doc_1c5f37ea0ad3" + ], + "being": [ + "doc_136d7a909d51", + "doc_b6088784caab", + "doc_d147a81bac29" + ], + "below": [ + "doc_4a1cf8a90e83", + "doc_5ee8bbdbd8a8", + "doc_61fd9f682847" + ], + "best": [ + "doc_067033cfc945", + "doc_2798948c3080", + "doc_57e2139f0873", + "doc_78a8125d2c7e", + "doc_9e9118ed2141", + "doc_a24b92541657", + "doc_b6088784caab", + "doc_e67e762fb6ab", + "doc_e7d508371e5c", + "doc_eeed52120ccf", + "doc_fdf48d427fc0" + ], + "best-effort": [ + "doc_a2e84472f1f9", + "doc_e7d508371e5c" + ], + "better": [ + "doc_0383dbf277ba", + "doc_3ae3aaf0c12c", + "doc_4055659fe573", + "doc_eeed52120ccf" + ], + "between": [ + "doc_0383dbf277ba", + "doc_067033cfc945", + "doc_136d7a909d51", + "doc_18d38fdacc9e", + "doc_1c5f37ea0ad3", + "doc_43ad71a4ad0c", + "doc_4a1cf8a90e83", + "doc_4e1ebaf40267", + "doc_50ac650fd8f6", + "doc_e34fe24f9585", + "doc_e8794369a1a4", + "doc_f1e32ed2ffce", + "doc_f54217a0f2cf" + ], + "beyond": [ + "doc_0383dbf277ba", + "doc_1e46cbc6bfae", + "doc_464a2b4a8d35", + "doc_4a1cf8a90e83", + "doc_745f751f1344", + "doc_a24b92541657" + ], + "bgtool": [ + "doc_9e9118ed2141" + ], + "bill": [ + "doc_e67e762fb6ab" + ], + "bind": [ + "doc_61fd9f682847" + ], + "block": [ + "doc_4e1ebaf40267", + "doc_a24b92541657", + "doc_d6ad61a79371" + ], + "blocked": [ + "doc_0ac01bce8c79", + "doc_618e34441fa7", + "doc_b6088784caab" + ], + "blocking": [ + "doc_1e46cbc6bfae" + ], + "blocks": [ + "doc_2798948c3080", + "doc_3aa0b3792d22", + "doc_464a2b4a8d35", + "doc_a24b92541657", + "doc_e7d508371e5c", + "doc_f18f523b6ecc" + ], + "body": [ + "doc_b6088784caab" + ], + "bool": [ + "doc_745f751f1344", + "doc_a24b92541657", + "doc_a2e84472f1f9", + "doc_e67e762fb6ab", + "doc_e7d508371e5c", + "doc_f54217a0f2cf" + ], + "boolean": [ + "doc_464a2b4a8d35" + ], + "bot": [ + "doc_64090a20f830" + ], + "both": [ + "doc_07174a8633ac", + "doc_2798948c3080", + "doc_43ad71a4ad0c", + "doc_50ac650fd8f6", + "doc_c346f13ce597", + "doc_e34fe24f9585", + "doc_fdf48d427fc0" + ], + "bots": [ + "doc_a24b92541657" + ], + "bound": [ + "doc_4462f67b1c03", + "doc_464a2b4a8d35", + "doc_f5f256549d84" + ], + "boundaries": [ + "doc_0383dbf277ba", + "doc_1c5f37ea0ad3", + "doc_2a18de009d56", + "doc_4055659fe573", + "doc_4a1cf8a90e83", + "doc_4e1ebaf40267", + "doc_567be1281e2f", + "doc_64090a20f830", + "doc_78a8125d2c7e", + "doc_c93c115aeb85", + "doc_d147a81bac29", + "doc_e67e762fb6ab", + "doc_f18f523b6ecc", + "doc_f54217a0f2cf" + ], + "boundary": [ + "doc_3eeb75d383b8", + "doc_4e1ebaf40267", + "doc_50ac650fd8f6", + "doc_567be1281e2f", + "doc_5ee8bbdbd8a8", + "doc_d147a81bac29", + "doc_f92685a34f7e" + ], + "bounded": [ + "doc_07174a8633ac", + "doc_4a1cf8a90e83", + "doc_deb460ffa5ee", + "doc_f18f523b6ecc", + "doc_f5f256549d84" + ], + "branch": [ + "doc_f54217a0f2cf" + ], + "branches": [ + "doc_f54217a0f2cf" + ], + "branching": [ + "doc_f54217a0f2cf" + ], + "breaches": [ + "doc_fdf48d427fc0" + ], + "break": [ + "doc_07174a8633ac", + "doc_18d38fdacc9e", + "doc_618e34441fa7" + ], + "breakdown": [ + "doc_745f751f1344" + ], + "breaker": [ + "doc_2a18de009d56", + "doc_4055659fe573", + "doc_4462f67b1c03", + "doc_5a65ddab1445", + "doc_5ee8bbdbd8a8", + "doc_7320ab982fab", + "doc_78a8125d2c7e", + "doc_a24b92541657", + "doc_d1f7d83e0824", + "doc_e7d508371e5c", + "doc_eeed52120ccf" + ], + "breaker_cooldown_s": [ + "doc_a24b92541657" + ], + "breaker_failure_threshold": [ + "doc_5ee8bbdbd8a8", + "doc_a24b92541657" + ], + "breakers": [ + "doc_567be1281e2f", + "doc_a24b92541657", + "doc_a5120de838fb", + "doc_d147a81bac29", + "doc_deb460ffa5ee" + ], + "breaking": [ + "doc_4e1ebaf40267", + "doc_e8794369a1a4", + "doc_f1e32ed2ffce" + ], + "breaks": [ + "doc_618e34441fa7" + ], + "bridge": [ + "doc_109157c0d2d7" + ], + "bridges": [ + "doc_567be1281e2f" + ], + "broadly": [ + "doc_43ad71a4ad0c" + ], + "broken": [ + "doc_4e1ebaf40267", + "doc_c93c115aeb85" + ], + "browser-based": [ + "doc_e34fe24f9585" + ], + "budget": [ + "doc_0383dbf277ba", + "doc_1c5f37ea0ad3", + "doc_2a18de009d56", + "doc_4055659fe573", + "doc_50ac650fd8f6", + "doc_5ee8bbdbd8a8", + "doc_618e34441fa7", + "doc_a5120de838fb", + "doc_c93c115aeb85", + "doc_e67e762fb6ab", + "doc_f12bbbc027a7", + "doc_fdf48d427fc0" + ], + "budget-triggered": [ + "doc_4055659fe573" + ], + "budgets": [ + "doc_1e46cbc6bfae", + "doc_2a18de009d56", + "doc_3ae3aaf0c12c", + "doc_a5120de838fb", + "doc_bbc97cbff254", + "doc_c93c115aeb85", + "doc_e67e762fb6ab", + "doc_f5f256549d84", + "doc_fdf48d427fc0" + ], + "bug": [ + "doc_618e34441fa7" + ], + "bugs": [ + "doc_50ac650fd8f6" + ], + "build": [ + "doc_067033cfc945", + "doc_2a18de009d56", + "doc_3aa0b3792d22", + "doc_3ae3aaf0c12c", + "doc_3f1383f72c5f", + "doc_3f60413b3a06", + "doc_50ac650fd8f6", + "doc_5a65ddab1445", + "doc_7b26cbd9a7ae", + "doc_9e9118ed2141", + "doc_bbc97cbff254", + "doc_deb460ffa5ee", + "doc_f18f523b6ecc", + "doc_f92685a34f7e" + ], + "build_agentic_ai_assets": [ + "doc_3eeb75d383b8" + ], + "build_project": [ + "doc_3f1383f72c5f" + ], + "build_runtime_tools": [ + "doc_464a2b4a8d35" + ], + "builder": [ + "doc_2a18de009d56", + "doc_3ae3aaf0c12c", + "doc_3eeb75d383b8", + "doc_3f60413b3a06", + "doc_7320ab982fab", + "doc_a5120de838fb", + "doc_c346f13ce597", + "doc_f18f523b6ecc", + "doc_f92685a34f7e" + ], + "building": [ + "doc_0ac01bce8c79", + "doc_136d7a909d51", + "doc_18d38fdacc9e", + "doc_3aa0b3792d22", + "doc_3ae3aaf0c12c", + "doc_464a2b4a8d35", + "doc_a5120de838fb", + "doc_b6088784caab", + "doc_d147a81bac29", + "doc_deb460ffa5ee", + "doc_f18f523b6ecc", + "doc_f54217a0f2cf" + ], + "building-with-ai": [ + "doc_3aa0b3792d22" + ], + "builds": [ + "doc_1c5f37ea0ad3", + "doc_1e46cbc6bfae", + "doc_5ee8bbdbd8a8", + "doc_a5120de838fb", + "doc_eeed52120ccf" + ], + "built": [ + "doc_4e1ebaf40267", + "doc_50ac650fd8f6" + ], + "built-in": [ + "doc_2798948c3080", + "doc_2a18de009d56", + "doc_40e30666bfa8", + "doc_5a65ddab1445", + "doc_61fd9f682847", + "doc_7320ab982fab", + "doc_a24b92541657", + "doc_a5120de838fb", + "doc_b6088784caab", + "doc_c346f13ce597", + "doc_d1f7d83e0824", + "doc_e8794369a1a4", + "doc_f1e32ed2ffce", + "doc_f92685a34f7e" + ], + "built-ins": [ + "doc_c93c115aeb85" + ], + "bundles": [ + "doc_2798948c3080", + "doc_b6088784caab" + ], + "but": [ + "doc_0383dbf277ba", + "doc_07174a8633ac", + "doc_0ac01bce8c79", + "doc_109157c0d2d7", + "doc_136d7a909d51", + "doc_3eeb75d383b8", + "doc_4055659fe573", + "doc_4a1cf8a90e83", + "doc_618e34441fa7", + "doc_a24b92541657", + "doc_d147a81bac29", + "doc_d6ad61a79371", + "doc_e67e762fb6ab", + "doc_f18f523b6ecc", + "doc_f54217a0f2cf" + ], + "buttons": [ + "doc_d6ad61a79371" + ], + "bypass": [ + "doc_e67e762fb6ab" + ], + "c38": [ + "doc_3aa0b3792d22" + ], + "cache": [ + "doc_2a18de009d56", + "doc_3f60413b3a06", + "doc_4462f67b1c03", + "doc_64090a20f830", + "doc_7320ab982fab", + "doc_d147a81bac29" + ], + "cache_backend": [ + "doc_3f60413b3a06" + ], + "cached": [ + "doc_64090a20f830", + "doc_7320ab982fab" + ], + "cachepolicy": [ + "doc_2a18de009d56" + ], + "caches": [ + "doc_64090a20f830" + ], + "caching": [ + "doc_2a18de009d56", + "doc_4462f67b1c03", + "doc_64090a20f830", + "doc_7320ab982fab", + "doc_745f751f1344", + "doc_a24b92541657", + "doc_a5120de838fb", + "doc_c346f13ce597", + "doc_d147a81bac29", + "doc_deb460ffa5ee", + "doc_e7d508371e5c", + "doc_e8794369a1a4", + "doc_f1e32ed2ffce", + "doc_f92685a34f7e" + ], + "calculations": [ + "doc_57e2139f0873", + "doc_9e9118ed2141", + "doc_b6088784caab" + ], + "call": [ + "doc_0383dbf277ba", + "doc_0ac01bce8c79", + "doc_109157c0d2d7", + "doc_136d7a909d51", + "doc_1e46cbc6bfae", + "doc_3f60413b3a06", + "doc_4055659fe573", + "doc_4a1cf8a90e83", + "doc_4e1ebaf40267", + "doc_50ac650fd8f6", + "doc_567be1281e2f", + "doc_57e2139f0873", + "doc_5ee8bbdbd8a8", + "doc_64090a20f830", + "doc_7320ab982fab", + "doc_745f751f1344", + "doc_9e9118ed2141", + "doc_a24b92541657", + "doc_c346f13ce597", + "doc_e67e762fb6ab", + "doc_e7d508371e5c", + "doc_f12bbbc027a7", + "doc_f18f523b6ecc" + ], + "call_count": [ + "doc_0ac01bce8c79" + ], + "call_next": [ + "doc_745f751f1344", + "doc_9e9118ed2141", + "doc_c346f13ce597", + "doc_f92685a34f7e" + ], + "callable": [ + "doc_a24b92541657", + "doc_c346f13ce597", + "doc_e67e762fb6ab" + ], + "callables": [ + "doc_c93c115aeb85" + ], + "callback": [ + "doc_5a65ddab1445", + "doc_a24b92541657" + ], + "callbacks": [ + "doc_5a65ddab1445", + "doc_a24b92541657" + ], + "called": [ + "doc_07174a8633ac", + "doc_136d7a909d51" + ], + "caller": [ + "doc_1c5f37ea0ad3", + "doc_78b1717fafe3", + "doc_bbc97cbff254", + "doc_e67e762fb6ab", + "doc_e7d508371e5c", + "doc_f54217a0f2cf" + ], + "caller-provided": [ + "doc_4a1cf8a90e83" + ], + "callers": [ + "doc_1c5f37ea0ad3" + ], + "calling": [ + "doc_136d7a909d51", + "doc_4a1cf8a90e83", + "doc_57e2139f0873", + "doc_7320ab982fab", + "doc_9e9118ed2141", + "doc_c346f13ce597", + "doc_f1e32ed2ffce" + ], + "calls": [ + "doc_0383dbf277ba", + "doc_07174a8633ac", + "doc_0ac01bce8c79", + "doc_136d7a909d51", + "doc_1e46cbc6bfae", + "doc_3ae3aaf0c12c", + "doc_3f1383f72c5f", + "doc_3f60413b3a06", + "doc_4462f67b1c03", + "doc_4a1cf8a90e83", + "doc_4e1ebaf40267", + "doc_50ac650fd8f6", + "doc_567be1281e2f", + "doc_5a65ddab1445", + "doc_5ee8bbdbd8a8", + "doc_745f751f1344", + "doc_78a8125d2c7e", + "doc_7b26cbd9a7ae", + "doc_9e9118ed2141", + "doc_a24b92541657", + "doc_a2e84472f1f9", + "doc_b6088784caab", + "doc_bf91f8d5da74", + "doc_c346f13ce597", + "doc_d1f7d83e0824", + "doc_d6ad61a79371", + "doc_deb460ffa5ee", + "doc_e67e762fb6ab", + "doc_e7d508371e5c", + "doc_e8794369a1a4", + "doc_f12bbbc027a7", + "doc_f18f523b6ecc", + "doc_f54217a0f2cf", + "doc_f5f256549d84" + ], + "camelcase": [ + "doc_64090a20f830" + ], + "can": [ + "doc_0383dbf277ba", + "doc_07174a8633ac", + "doc_0ac01bce8c79", + "doc_136d7a909d51", + "doc_1c5f37ea0ad3", + "doc_2798948c3080", + "doc_3f1383f72c5f", + "doc_3f60413b3a06", + "doc_4055659fe573", + "doc_40e30666bfa8", + "doc_464a2b4a8d35", + "doc_4a1cf8a90e83", + "doc_4e1ebaf40267", + "doc_57e2139f0873", + "doc_5ee8bbdbd8a8", + "doc_618e34441fa7", + "doc_64090a20f830", + "doc_745f751f1344", + "doc_75887f91c7df", + "doc_9e9118ed2141", + "doc_a24b92541657", + "doc_a2e84472f1f9", + "doc_b6088784caab", + "doc_c346f13ce597", + "doc_c93c115aeb85", + "doc_d6ad61a79371", + "doc_e34fe24f9585", + "doc_e67e762fb6ab", + "doc_eeed52120ccf", + "doc_f12bbbc027a7", + "doc_f5f256549d84" + ], + "cancel": [ + "doc_109157c0d2d7", + "doc_7b26cbd9a7ae", + "doc_f12bbbc027a7" + ], + "cancellation": [ + "doc_f2ae50c638a4" + ], + "cancellations": [ + "doc_07174a8633ac" + ], + "cancelled": [ + "doc_1e46cbc6bfae", + "doc_bf91f8d5da74", + "doc_e7d508371e5c", + "doc_f54217a0f2cf" + ], + "cannot": [ + "doc_4a1cf8a90e83", + "doc_9e9118ed2141" + ], + "canonical": [ + "doc_deb460ffa5ee" + ], + "cap": [ + "doc_a24b92541657" + ], + "capabilities": [ + "doc_0383dbf277ba", + "doc_18d38fdacc9e", + "doc_43ad71a4ad0c", + "doc_464a2b4a8d35", + "doc_9e9118ed2141", + "doc_a5120de838fb", + "doc_b6088784caab", + "doc_d147a81bac29", + "doc_deb460ffa5ee", + "doc_e67e762fb6ab", + "doc_f18f523b6ecc" + ], + "capability": [ + "doc_2a18de009d56", + "doc_57e2139f0873", + "doc_745f751f1344", + "doc_a5120de838fb", + "doc_eeed52120ccf", + "doc_f12bbbc027a7", + "doc_f1e32ed2ffce" + ], + "capable": [ + "doc_a24b92541657" + ], + "capture": [ + "doc_136d7a909d51", + "doc_618e34441fa7", + "doc_78a8125d2c7e" + ], + "captured": [ + "doc_0ac01bce8c79", + "doc_18d38fdacc9e", + "doc_618e34441fa7" + ], + "captures": [ + "doc_0ac01bce8c79", + "doc_4a1cf8a90e83", + "doc_5ee8bbdbd8a8", + "doc_78a8125d2c7e" + ], + "care": [ + "doc_e34fe24f9585" + ], + "carries": [ + "doc_0ac01bce8c79", + "doc_4a1cf8a90e83", + "doc_745f751f1344", + "doc_f54217a0f2cf" + ], + "carry": [ + "doc_4a1cf8a90e83" + ], + "carrying": [ + "doc_745f751f1344" + ], + "cascading": [ + "doc_5ee8bbdbd8a8", + "doc_7320ab982fab" + ], + "case": [ + "doc_3f60413b3a06", + "doc_4055659fe573", + "doc_4a1cf8a90e83", + "doc_4e1ebaf40267", + "doc_50ac650fd8f6", + "doc_a24b92541657", + "doc_c93c115aeb85", + "doc_e34fe24f9585", + "doc_f5f256549d84" + ], + "cases": [ + "doc_18d38fdacc9e", + "doc_bbc97cbff254", + "doc_c93c115aeb85" + ], + "cat": [ + "doc_a24b92541657" + ], + "catalog": [ + "doc_a5120de838fb" + ], + "catch": [ + "doc_50ac650fd8f6" + ], + "catches": [ + "doc_745f751f1344" + ], + "categories": [ + "doc_4055659fe573", + "doc_618e34441fa7" + ], + "categorize": [ + "doc_18d38fdacc9e" + ], + "category": [ + "doc_18d38fdacc9e", + "doc_618e34441fa7" + ], + "caught": [ + "doc_745f751f1344" + ], + "cause": [ + "doc_2798948c3080", + "doc_4a1cf8a90e83", + "doc_618e34441fa7", + "doc_75887f91c7df" + ], + "causes": [ + "doc_136d7a909d51", + "doc_4055659fe573", + "doc_4e1ebaf40267", + "doc_50ac650fd8f6" + ], + "cd": [ + "doc_d6ad61a79371" + ], + "ceiling": [ + "doc_57e2139f0873", + "doc_61fd9f682847", + "doc_a24b92541657", + "doc_fdf48d427fc0" + ], + "chain": [ + "doc_2798948c3080", + "doc_4a1cf8a90e83", + "doc_5a65ddab1445", + "doc_5ee8bbdbd8a8", + "doc_64090a20f830", + "doc_7320ab982fab", + "doc_745f751f1344", + "doc_75887f91c7df", + "doc_a5120de838fb", + "doc_c346f13ce597", + "doc_d1f7d83e0824", + "doc_f54217a0f2cf" + ], + "chained": [ + "doc_3f60413b3a06" + ], + "chains": [ + "doc_618e34441fa7", + "doc_7320ab982fab", + "doc_a5120de838fb", + "doc_d1f7d83e0824", + "doc_deb460ffa5ee" + ], + "change": [ + "doc_2798948c3080", + "doc_3eeb75d383b8", + "doc_618e34441fa7", + "doc_bbc97cbff254", + "doc_d147a81bac29", + "doc_f18f523b6ecc" + ], + "changed": [ + "doc_3eeb75d383b8", + "doc_618e34441fa7", + "doc_d147a81bac29", + "doc_f54217a0f2cf" + ], + "changes": [ + "doc_0383dbf277ba", + "doc_18d38fdacc9e", + "doc_2798948c3080", + "doc_3ae3aaf0c12c", + "doc_3eeb75d383b8", + "doc_5a65ddab1445", + "doc_618e34441fa7", + "doc_64090a20f830", + "doc_a24b92541657", + "doc_c93c115aeb85" + ], + "changing": [ + "doc_3eeb75d383b8", + "doc_3f1383f72c5f", + "doc_618e34441fa7", + "doc_b6088784caab", + "doc_bbc97cbff254", + "doc_d147a81bac29", + "doc_deb460ffa5ee", + "doc_f18f523b6ecc" + ], + "channel": [ + "doc_2a18de009d56", + "doc_5ee8bbdbd8a8" + ], + "characters": [ + "doc_464a2b4a8d35", + "doc_61fd9f682847", + "doc_64090a20f830", + "doc_a24b92541657" + ], + "chars": [ + "doc_64090a20f830" + ], + "chat": [ + "doc_3f60413b3a06", + "doc_4462f67b1c03", + "doc_618e34441fa7", + "doc_78b1717fafe3", + "doc_7b26cbd9a7ae", + "doc_a5120de838fb", + "doc_bbc97cbff254", + "doc_c346f13ce597", + "doc_d1f7d83e0824", + "doc_d6ad61a79371", + "doc_e7d508371e5c", + "doc_f92685a34f7e" + ], + "chat_agent": [ + "doc_64090a20f830" + ], + "chat_timeout_s": [ + "doc_4462f67b1c03" + ], + "chatagent": [ + "doc_2a18de009d56", + "doc_64090a20f830" + ], + "chatbot": [ + "doc_deb460ffa5ee" + ], + "chatbots": [ + "doc_a24b92541657" + ], + "chatopenai": [ + "doc_5a65ddab1445" + ], + "cheap": [ + "doc_eeed52120ccf" + ], + "cheaper": [ + "doc_7320ab982fab" + ], + "cheat": [ + "doc_7320ab982fab" + ], + "check": [ + "doc_07174a8633ac", + "doc_136d7a909d51", + "doc_2798948c3080", + "doc_464a2b4a8d35", + "doc_4e1ebaf40267", + "doc_64090a20f830", + "doc_a5120de838fb", + "doc_c93c115aeb85", + "doc_d1f7d83e0824", + "doc_deb460ffa5ee", + "doc_eeed52120ccf" + ], + "checked": [ + "doc_618e34441fa7", + "doc_9e9118ed2141" + ], + "checklist": [ + "doc_067033cfc945", + "doc_18d38fdacc9e", + "doc_3eeb75d383b8", + "doc_4055659fe573", + "doc_567be1281e2f", + "doc_d147a81bac29", + "doc_f5f256549d84" + ], + "checkpoint": [ + "doc_0383dbf277ba", + "doc_07174a8633ac", + "doc_2a18de009d56", + "doc_4055659fe573", + "doc_4a1cf8a90e83", + "doc_50ac650fd8f6", + "doc_5ee8bbdbd8a8", + "doc_7320ab982fab", + "doc_a24b92541657", + "doc_a5120de838fb", + "doc_d147a81bac29", + "doc_e7d508371e5c" + ], + "checkpoint-aware": [ + "doc_4a1cf8a90e83" + ], + "checkpoint-based": [ + "doc_7b26cbd9a7ae", + "doc_a5120de838fb" + ], + "checkpoint-schema": [ + "doc_3aa0b3792d22" + ], + "checkpoint_async_writes": [ + "doc_4a1cf8a90e83", + "doc_a24b92541657" + ], + "checkpoint_coalesce_runtime_state": [ + "doc_4a1cf8a90e83", + "doc_a24b92541657" + ], + "checkpoint_flush_timeout_s": [ + "doc_4a1cf8a90e83", + "doc_a24b92541657" + ], + "checkpoint_latest_key": [ + "doc_4a1cf8a90e83" + ], + "checkpoint_queue_maxsize": [ + "doc_a24b92541657" + ], + "checkpoint_token": [ + "doc_4a1cf8a90e83", + "doc_5ee8bbdbd8a8" + ], + "checkpointing": [ + "doc_2a18de009d56", + "doc_5a65ddab1445", + "doc_a5120de838fb", + "doc_f18f523b6ecc" + ], + "checkpoints": [ + "doc_0383dbf277ba", + "doc_136d7a909d51", + "doc_4a1cf8a90e83", + "doc_4e1ebaf40267", + "doc_d147a81bac29", + "doc_deb460ffa5ee" + ], + "checks": [ + "doc_067033cfc945", + "doc_1c5f37ea0ad3", + "doc_4462f67b1c03", + "doc_464a2b4a8d35", + "doc_4a1cf8a90e83", + "doc_567be1281e2f", + "doc_75887f91c7df", + "doc_78b1717fafe3" + ], + "checksum": [ + "doc_2a18de009d56", + "doc_9e9118ed2141" + ], + "choice": [ + "doc_50ac650fd8f6" + ], + "choices": [ + "doc_f92685a34f7e" + ], + "choose": [ + "doc_0383dbf277ba", + "doc_deb460ffa5ee", + "doc_f5f256549d84", + "doc_fdf48d427fc0" + ], + "choosing": [ + "doc_0ac01bce8c79", + "doc_18d38fdacc9e", + "doc_78a8125d2c7e", + "doc_f5f256549d84" + ], + "chunked": [ + "doc_109157c0d2d7" + ], + "chunking": [ + "doc_5ee8bbdbd8a8", + "doc_eeed52120ccf" + ], + "chunks": [ + "doc_109157c0d2d7" + ], + "ci": [ + "doc_18d38fdacc9e", + "doc_3ae3aaf0c12c", + "doc_5a65ddab1445", + "doc_618e34441fa7", + "doc_c93c115aeb85", + "doc_d6ad61a79371" + ], + "ci-gated": [ + "doc_067033cfc945" + ], + "circuit": [ + "doc_2a18de009d56", + "doc_4055659fe573", + "doc_4462f67b1c03", + "doc_4e1ebaf40267", + "doc_567be1281e2f", + "doc_5a65ddab1445", + "doc_5ee8bbdbd8a8", + "doc_7320ab982fab", + "doc_78a8125d2c7e", + "doc_a24b92541657", + "doc_a5120de838fb", + "doc_d147a81bac29", + "doc_d1f7d83e0824", + "doc_deb460ffa5ee", + "doc_e7d508371e5c", + "doc_e8794369a1a4", + "doc_eeed52120ccf", + "doc_f1e32ed2ffce" + ], + "circuit-breaker": [ + "doc_618e34441fa7" + ], + "circuitbreakerpolicy": [ + "doc_2a18de009d56" + ], + "cite": [ + "doc_18d38fdacc9e" + ], + "claims": [ + "doc_1c5f37ea0ad3" + ], + "clarify": [ + "doc_136d7a909d51" + ], + "class": [ + "doc_0383dbf277ba", + "doc_2a18de009d56", + "doc_64090a20f830", + "doc_a24b92541657" + ], + "classes": [ + "doc_2a18de009d56", + "doc_3eeb75d383b8" + ], + "classification": [ + "doc_2a18de009d56", + "doc_3f60413b3a06", + "doc_4055659fe573", + "doc_4e1ebaf40267", + "doc_50ac650fd8f6", + "doc_57e2139f0873", + "doc_618e34441fa7", + "doc_78b1717fafe3", + "doc_d1f7d83e0824", + "doc_eeed52120ccf", + "doc_f12bbbc027a7" + ], + "classified": [ + "doc_4055659fe573", + "doc_50ac650fd8f6", + "doc_5ee8bbdbd8a8", + "doc_eeed52120ccf" + ], + "classifies": [ + "doc_4055659fe573", + "doc_618e34441fa7" + ], + "classify": [ + "doc_4e1ebaf40267" + ], + "classifying": [ + "doc_18d38fdacc9e" + ], + "claude": [ + "doc_f1e32ed2ffce", + "doc_f92685a34f7e" + ], + "claude-opus-4-5": [ + "doc_eeed52120ccf", + "doc_f92685a34f7e" + ], + "cleanly": [ + "doc_4462f67b1c03" + ], + "clear": [ + "doc_18d38fdacc9e", + "doc_4e1ebaf40267", + "doc_50ac650fd8f6", + "doc_57e2139f0873", + "doc_bbc97cbff254", + "doc_e67e762fb6ab" + ], + "cleared": [ + "doc_4a1cf8a90e83" + ], + "clearly": [ + "doc_43ad71a4ad0c" + ], + "cli": [ + "doc_a24b92541657", + "doc_b6088784caab", + "doc_d147a81bac29", + "doc_deb460ffa5ee", + "doc_e7d508371e5c", + "doc_f2ae50c638a4" + ], + "clicks": [ + "doc_7b26cbd9a7ae" + ], + "client": [ + "doc_1c5f37ea0ad3", + "doc_2a18de009d56", + "doc_3f60413b3a06", + "doc_4462f67b1c03", + "doc_a24b92541657", + "doc_a5120de838fb", + "doc_bbc97cbff254", + "doc_c346f13ce597", + "doc_e34fe24f9585", + "doc_e67e762fb6ab", + "doc_eeed52120ccf" + ], + "clients": [ + "doc_3f60413b3a06", + "doc_7320ab982fab", + "doc_a24b92541657", + "doc_deb460ffa5ee", + "doc_e34fe24f9585", + "doc_e67e762fb6ab", + "doc_f1e32ed2ffce", + "doc_f5f256549d84", + "doc_f92685a34f7e" + ], + "clis": [ + "doc_bbc97cbff254" + ], + "close": [ + "doc_4462f67b1c03" + ], + "coalesce": [ + "doc_a24b92541657" + ], + "coalesced": [ + "doc_4a1cf8a90e83" + ], + "code": [ + "doc_0383dbf277ba", + "doc_0ac01bce8c79", + "doc_136d7a909d51", + "doc_18d38fdacc9e", + "doc_3eeb75d383b8", + "doc_3f1383f72c5f", + "doc_4a1cf8a90e83", + "doc_4e1ebaf40267", + "doc_567be1281e2f", + "doc_5a65ddab1445", + "doc_618e34441fa7", + "doc_61fd9f682847", + "doc_64090a20f830", + "doc_a5120de838fb", + "doc_b6088784caab", + "doc_bbc97cbff254", + "doc_d1f7d83e0824", + "doc_e8794369a1a4", + "doc_f2ae50c638a4", + "doc_f54217a0f2cf", + "doc_f92685a34f7e" + ], + "codex": [ + "doc_deb460ffa5ee" + ], + "coding": [ + "doc_18d38fdacc9e", + "doc_3f1383f72c5f", + "doc_9e9118ed2141", + "doc_deb460ffa5ee", + "doc_eeed52120ccf" + ], + "collapses": [ + "doc_64090a20f830" + ], + "collected": [ + "doc_745f751f1344", + "doc_c93c115aeb85", + "doc_f12bbbc027a7" + ], + "collecting": [ + "doc_a2e84472f1f9" + ], + "collection": [ + "doc_2a18de009d56" + ], + "collectors": [ + "doc_d147a81bac29" + ], + "collects": [ + "doc_f12bbbc027a7" + ], + "colors": [ + "doc_3aa0b3792d22" + ], + "com": [ + "doc_3aa0b3792d22" + ], + "combine": [ + "doc_40e30666bfa8", + "doc_464a2b4a8d35", + "doc_7b26cbd9a7ae", + "doc_a5120de838fb", + "doc_fdf48d427fc0" + ], + "combines": [ + "doc_f12bbbc027a7" + ], + "come": [ + "doc_eeed52120ccf" + ], + "comes": [ + "doc_3eeb75d383b8", + "doc_f5f256549d84" + ], + "comma-separated": [ + "doc_61fd9f682847" + ], + "command": [ + "doc_2a18de009d56", + "doc_3eeb75d383b8", + "doc_464a2b4a8d35", + "doc_618e34441fa7", + "doc_61fd9f682847", + "doc_9e9118ed2141", + "doc_a24b92541657", + "doc_a5120de838fb", + "doc_b6088784caab" + ], + "command_timeout_s": [ + "doc_a24b92541657" + ], + "commands": [ + "doc_3eeb75d383b8", + "doc_43ad71a4ad0c", + "doc_9e9118ed2141", + "doc_a24b92541657", + "doc_b6088784caab", + "doc_d147a81bac29", + "doc_f18f523b6ecc" + ], + "commit": [ + "doc_067033cfc945" + ], + "common": [ + "doc_136d7a909d51", + "doc_18d38fdacc9e", + "doc_3ae3aaf0c12c", + "doc_464a2b4a8d35", + "doc_4a1cf8a90e83", + "doc_57e2139f0873", + "doc_5a65ddab1445", + "doc_9e9118ed2141", + "doc_bbc97cbff254", + "doc_bf91f8d5da74", + "doc_c346f13ce597", + "doc_d147a81bac29", + "doc_fdf48d427fc0" + ], + "communicate": [ + "doc_57e2139f0873", + "doc_75887f91c7df" + ], + "communicates": [ + "doc_5ee8bbdbd8a8" + ], + "communication": [ + "doc_1c5f37ea0ad3", + "doc_4e1ebaf40267", + "doc_567be1281e2f", + "doc_57e2139f0873", + "doc_75887f91c7df", + "doc_e34fe24f9585", + "doc_f92685a34f7e" + ], + "communications": [ + "doc_d6ad61a79371" + ], + "compact": [ + "doc_0383dbf277ba", + "doc_07174a8633ac", + "doc_7b26cbd9a7ae", + "doc_a5120de838fb", + "doc_e7d508371e5c", + "doc_f5f256549d84" + ], + "compact_thread": [ + "doc_07174a8633ac", + "doc_2a18de009d56", + "doc_bbc97cbff254" + ], + "compact_thread_memory": [ + "doc_2a18de009d56" + ], + "compacted": [ + "doc_07174a8633ac" + ], + "compacting": [ + "doc_07174a8633ac" + ], + "compaction": [ + "doc_0383dbf277ba", + "doc_07174a8633ac", + "doc_18d38fdacc9e", + "doc_2a18de009d56", + "doc_4e1ebaf40267", + "doc_7b26cbd9a7ae", + "doc_a5120de838fb", + "doc_bbc97cbff254", + "doc_d147a81bac29", + "doc_deb460ffa5ee" + ], + "company_name": [ + "doc_2798948c3080", + "doc_64090a20f830" + ], + "compare": [ + "doc_3ae3aaf0c12c" + ], + "compare_event_types": [ + "doc_2a18de009d56" + ], + "comparison": [ + "doc_2a18de009d56", + "doc_57e2139f0873", + "doc_f1e32ed2ffce" + ], + "compat": [ + "doc_4a1cf8a90e83" + ], + "compatibility": [ + "doc_4e1ebaf40267", + "doc_d147a81bac29", + "doc_f54217a0f2cf" + ], + "compatible": [ + "doc_745f751f1344", + "doc_9e9118ed2141", + "doc_c346f13ce597" + ], + "compiled": [ + "doc_64090a20f830" + ], + "complete": [ + "doc_3f1383f72c5f", + "doc_4a1cf8a90e83", + "doc_5a65ddab1445", + "doc_5ee8bbdbd8a8", + "doc_9e9118ed2141", + "doc_bbc97cbff254", + "doc_bf91f8d5da74", + "doc_deb460ffa5ee", + "doc_e7d508371e5c", + "doc_f12bbbc027a7", + "doc_f18f523b6ecc" + ], + "completed": [ + "doc_0383dbf277ba", + "doc_07174a8633ac", + "doc_109157c0d2d7", + "doc_1e46cbc6bfae", + "doc_4055659fe573", + "doc_5ee8bbdbd8a8", + "doc_618e34441fa7", + "doc_745f751f1344", + "doc_78a8125d2c7e", + "doc_78b1717fafe3", + "doc_a2e84472f1f9", + "doc_bf91f8d5da74", + "doc_e7d508371e5c", + "doc_f12bbbc027a7", + "doc_f54217a0f2cf" + ], + "completes": [ + "doc_0383dbf277ba", + "doc_0ac01bce8c79", + "doc_1e46cbc6bfae", + "doc_3f1383f72c5f", + "doc_4055659fe573", + "doc_78b1717fafe3", + "doc_7b26cbd9a7ae", + "doc_9e9118ed2141", + "doc_c346f13ce597" + ], + "completing": [ + "doc_136d7a909d51" + ], + "completion": [ + "doc_136d7a909d51", + "doc_3f1383f72c5f", + "doc_78a8125d2c7e", + "doc_a24b92541657", + "doc_e8794369a1a4", + "doc_f54217a0f2cf" + ], + "complex": [ + "doc_50ac650fd8f6", + "doc_57e2139f0873", + "doc_a2e84472f1f9", + "doc_d1f7d83e0824", + "doc_eeed52120ccf", + "doc_f92685a34f7e" + ], + "complexity": [ + "doc_18d38fdacc9e", + "doc_50ac650fd8f6", + "doc_57e2139f0873", + "doc_a5120de838fb" + ], + "compliance": [ + "doc_067033cfc945", + "doc_43ad71a4ad0c", + "doc_4a1cf8a90e83" + ], + "component": [ + "doc_4055659fe573" + ], + "components": [ + "doc_136d7a909d51", + "doc_50ac650fd8f6", + "doc_d6ad61a79371" + ], + "compose": [ + "doc_464a2b4a8d35" + ], + "composing": [ + "doc_464a2b4a8d35" + ], + "composition": [ + "doc_64090a20f830" + ], + "computed": [ + "doc_618e34441fa7" + ], + "concatenate": [ + "doc_2798948c3080" + ], + "concept": [ + "doc_5a65ddab1445", + "doc_f2ae50c638a4" + ], + "concepts": [ + "doc_1e46cbc6bfae", + "doc_5a65ddab1445", + "doc_a5120de838fb" + ], + "concern": [ + "doc_1c5f37ea0ad3" + ], + "concerns": [ + "doc_4e1ebaf40267", + "doc_745f751f1344", + "doc_c346f13ce597" + ], + "concise": [ + "doc_1e46cbc6bfae" + ], + "concurrency": [ + "doc_618e34441fa7", + "doc_7320ab982fab", + "doc_a2e84472f1f9", + "doc_c93c115aeb85", + "doc_e67e762fb6ab", + "doc_f12bbbc027a7" + ], + "concurrent": [ + "doc_0ac01bce8c79", + "doc_a24b92541657", + "doc_e67e762fb6ab", + "doc_f12bbbc027a7" + ], + "condition": [ + "doc_78a8125d2c7e" + ], + "conditions": [ + "doc_618e34441fa7", + "doc_c93c115aeb85" + ], + "config": [ + "doc_067033cfc945", + "doc_2a18de009d56", + "doc_5a65ddab1445", + "doc_61fd9f682847", + "doc_a24b92541657" + ], + "configs": [ + "doc_e67e762fb6ab" + ], + "configurable": [ + "doc_0ac01bce8c79", + "doc_1c5f37ea0ad3", + "doc_4055659fe573", + "doc_464a2b4a8d35", + "doc_567be1281e2f", + "doc_61fd9f682847", + "doc_745f751f1344", + "doc_78b1717fafe3", + "doc_a24b92541657", + "doc_e67e762fb6ab" + ], + "configuration": [ + "doc_067033cfc945", + "doc_136d7a909d51", + "doc_2a18de009d56", + "doc_3aa0b3792d22", + "doc_3eeb75d383b8", + "doc_3f60413b3a06", + "doc_4055659fe573", + "doc_4462f67b1c03", + "doc_4e1ebaf40267", + "doc_618e34441fa7", + "doc_61fd9f682847", + "doc_64090a20f830", + "doc_7b26cbd9a7ae", + "doc_a24b92541657", + "doc_a2e84472f1f9", + "doc_bbc97cbff254", + "doc_bf91f8d5da74", + "doc_c93c115aeb85", + "doc_d147a81bac29", + "doc_d1f7d83e0824", + "doc_deb460ffa5ee", + "doc_e67e762fb6ab", + "doc_e7d508371e5c", + "doc_fdf48d427fc0" + ], + "configuration-reference": [ + "doc_3aa0b3792d22" + ], + "configurations": [ + "doc_a5120de838fb" + ], + "configure": [ + "doc_067033cfc945", + "doc_1c5f37ea0ad3", + "doc_4055659fe573", + "doc_43ad71a4ad0c", + "doc_4462f67b1c03", + "doc_464a2b4a8d35", + "doc_567be1281e2f", + "doc_61fd9f682847", + "doc_7320ab982fab", + "doc_a2e84472f1f9", + "doc_a5120de838fb", + "doc_bbc97cbff254", + "doc_d1f7d83e0824", + "doc_e34fe24f9585", + "doc_f5f256549d84", + "doc_fdf48d427fc0" + ], + "configured": [ + "doc_0383dbf277ba", + "doc_0ac01bce8c79", + "doc_136d7a909d51", + "doc_18d38fdacc9e", + "doc_1c5f37ea0ad3", + "doc_1e46cbc6bfae", + "doc_2798948c3080", + "doc_464a2b4a8d35", + "doc_567be1281e2f", + "doc_5ee8bbdbd8a8", + "doc_61fd9f682847", + "doc_64090a20f830", + "doc_7320ab982fab", + "doc_75887f91c7df", + "doc_9e9118ed2141", + "doc_a24b92541657", + "doc_b6088784caab", + "doc_c93c115aeb85", + "doc_d6ad61a79371" + ], + "confirm": [ + "doc_618e34441fa7", + "doc_eeed52120ccf" + ], + "confirmation": [ + "doc_0ac01bce8c79" + ], + "conforms": [ + "doc_3f60413b3a06" + ], + "confused": [ + "doc_18d38fdacc9e" + ], + "confusing": [ + "doc_3eeb75d383b8" + ], + "connect": [ + "doc_40e30666bfa8", + "doc_a5120de838fb", + "doc_eeed52120ccf" + ], + "connection": [ + "doc_0383dbf277ba", + "doc_4055659fe573", + "doc_4462f67b1c03", + "doc_61fd9f682847", + "doc_78b1717fafe3", + "doc_a5120de838fb" + ], + "connections": [ + "doc_4462f67b1c03", + "doc_78b1717fafe3", + "doc_e34fe24f9585" + ], + "connects": [ + "doc_7b26cbd9a7ae" + ], + "consecutive": [ + "doc_5ee8bbdbd8a8", + "doc_64090a20f830" + ], + "consensus": [ + "doc_57e2139f0873", + "doc_f12bbbc027a7" + ], + "consider": [ + "doc_f12bbbc027a7" + ], + "considerations": [ + "doc_1c5f37ea0ad3", + "doc_64090a20f830" + ], + "consistency": [ + "doc_618e34441fa7" + ], + "consistent": [ + "doc_109157c0d2d7", + "doc_618e34441fa7", + "doc_7320ab982fab" + ], + "console": [ + "doc_50ac650fd8f6", + "doc_78a8125d2c7e" + ], + "consolerunmetricsexporter": [ + "doc_2a18de009d56" + ], + "constants": [ + "doc_2a18de009d56" + ], + "constrain": [ + "doc_18d38fdacc9e" + ], + "constraints": [ + "doc_2a18de009d56" + ], + "construct": [ + "doc_3f60413b3a06" + ], + "constructing": [ + "doc_2a18de009d56", + "doc_3f60413b3a06" + ], + "construction": [ + "doc_5ee8bbdbd8a8", + "doc_eeed52120ccf" + ], + "constructor": [ + "doc_3eeb75d383b8", + "doc_61fd9f682847", + "doc_64090a20f830", + "doc_a24b92541657", + "doc_d147a81bac29", + "doc_e67e762fb6ab" + ], + "constructors": [ + "doc_2a18de009d56", + "doc_61fd9f682847" + ], + "constructs": [ + "doc_3f60413b3a06", + "doc_5ee8bbdbd8a8", + "doc_e7d508371e5c" + ], + "consults": [ + "doc_0ac01bce8c79", + "doc_745f751f1344" + ], + "consume": [ + "doc_0383dbf277ba", + "doc_40e30666bfa8", + "doc_9e9118ed2141", + "doc_bbc97cbff254", + "doc_e34fe24f9585", + "doc_f2ae50c638a4", + "doc_f54217a0f2cf" + ], + "consumer": [ + "doc_618e34441fa7", + "doc_f54217a0f2cf" + ], + "consumers": [ + "doc_57e2139f0873", + "doc_618e34441fa7", + "doc_78b1717fafe3" + ], + "consuming": [ + "doc_1c5f37ea0ad3", + "doc_40e30666bfa8", + "doc_e34fe24f9585", + "doc_f54217a0f2cf", + "doc_fdf48d427fc0" + ], + "consumption": [ + "doc_464a2b4a8d35", + "doc_f54217a0f2cf" + ], + "contacting": [ + "doc_5ee8bbdbd8a8" + ], + "contain": [ + "doc_745f751f1344" + ], + "containing": [ + "doc_07174a8633ac", + "doc_3f1383f72c5f", + "doc_5ee8bbdbd8a8", + "doc_618e34441fa7", + "doc_e67e762fb6ab" + ], + "containment": [ + "doc_464a2b4a8d35", + "doc_64090a20f830" + ], + "contains": [ + "doc_0383dbf277ba", + "doc_07174a8633ac", + "doc_0ac01bce8c79", + "doc_1e46cbc6bfae", + "doc_3f60413b3a06", + "doc_4a1cf8a90e83", + "doc_5ee8bbdbd8a8", + "doc_a2e84472f1f9" + ], + "content": [ + "doc_0383dbf277ba", + "doc_2a18de009d56", + "doc_464a2b4a8d35", + "doc_5ee8bbdbd8a8", + "doc_618e34441fa7", + "doc_64090a20f830", + "doc_7320ab982fab", + "doc_745f751f1344", + "doc_75887f91c7df", + "doc_9e9118ed2141", + "doc_a24b92541657", + "doc_a2e84472f1f9", + "doc_b6088784caab", + "doc_c93c115aeb85", + "doc_e8794369a1a4" + ], + "contents": [ + "doc_464a2b4a8d35", + "doc_4a1cf8a90e83" + ], + "context": [ + "doc_0383dbf277ba", + "doc_07174a8633ac", + "doc_18d38fdacc9e", + "doc_1c5f37ea0ad3", + "doc_2798948c3080", + "doc_2a18de009d56", + "doc_40e30666bfa8", + "doc_4a1cf8a90e83", + "doc_5ee8bbdbd8a8", + "doc_64090a20f830", + "doc_75887f91c7df", + "doc_78b1717fafe3", + "doc_7b26cbd9a7ae", + "doc_a24b92541657", + "doc_bbc97cbff254", + "doc_e34fe24f9585", + "doc_e67e762fb6ab", + "doc_e7d508371e5c", + "doc_f2ae50c638a4", + "doc_f5f256549d84", + "doc_f92685a34f7e" + ], + "context_defaults": [ + "doc_a24b92541657", + "doc_e67e762fb6ab" + ], + "contextlengthexceeded": [ + "doc_136d7a909d51" + ], + "continue": [ + "doc_0ac01bce8c79", + "doc_136d7a909d51", + "doc_4055659fe573", + "doc_50ac650fd8f6", + "doc_745f751f1344", + "doc_9e9118ed2141", + "doc_a24b92541657", + "doc_a2e84472f1f9", + "doc_bbc97cbff254", + "doc_f12bbbc027a7" + ], + "continue_with_error": [ + "doc_a24b92541657" + ], + "continued": [ + "doc_07174a8633ac" + ], + "continues": [ + "doc_07174a8633ac", + "doc_3f1383f72c5f", + "doc_4055659fe573", + "doc_4a1cf8a90e83", + "doc_4e1ebaf40267", + "doc_9e9118ed2141", + "doc_e7d508371e5c", + "doc_f54217a0f2cf" + ], + "continuing": [ + "doc_07174a8633ac", + "doc_a24b92541657" + ], + "continuity": [ + "doc_1e46cbc6bfae", + "doc_5ee8bbdbd8a8", + "doc_7b26cbd9a7ae", + "doc_deb460ffa5ee", + "doc_e7d508371e5c", + "doc_f2ae50c638a4", + "doc_f54217a0f2cf" + ], + "continuously": [ + "doc_50ac650fd8f6" + ], + "contract": [ + "doc_3eeb75d383b8", + "doc_4e1ebaf40267", + "doc_50ac650fd8f6", + "doc_618e34441fa7", + "doc_78b1717fafe3", + "doc_bbc97cbff254", + "doc_d147a81bac29", + "doc_f18f523b6ecc", + "doc_f2ae50c638a4", + "doc_f54217a0f2cf" + ], + "contract-first": [ + "doc_50ac650fd8f6" + ], + "contracts": [ + "doc_1c5f37ea0ad3", + "doc_2a18de009d56", + "doc_3aa0b3792d22", + "doc_3eeb75d383b8", + "doc_4e1ebaf40267", + "doc_50ac650fd8f6", + "doc_5a65ddab1445", + "doc_618e34441fa7", + "doc_78b1717fafe3", + "doc_d147a81bac29", + "doc_deb460ffa5ee", + "doc_e8794369a1a4", + "doc_f1e32ed2ffce", + "doc_f92685a34f7e" + ], + "contribute": [ + "doc_4e1ebaf40267" + ], + "contributed": [ + "doc_a2e84472f1f9" + ], + "contributes": [ + "doc_eeed52120ccf" + ], + "contributor": [ + "doc_3aa0b3792d22", + "doc_f18f523b6ecc" + ], + "contributors": [ + "doc_618e34441fa7", + "doc_d147a81bac29" + ], + "control": [ + "doc_067033cfc945", + "doc_109157c0d2d7", + "doc_1c5f37ea0ad3", + "doc_2798948c3080", + "doc_3f60413b3a06", + "doc_4462f67b1c03", + "doc_50ac650fd8f6", + "doc_567be1281e2f", + "doc_7b26cbd9a7ae", + "doc_a24b92541657", + "doc_a5120de838fb", + "doc_c346f13ce597", + "doc_d1f7d83e0824", + "doc_deb460ffa5ee", + "doc_e67e762fb6ab", + "doc_e7d508371e5c", + "doc_eeed52120ccf" + ], + "control-and-session": [ + "doc_3aa0b3792d22" + ], + "controllable": [ + "doc_5a65ddab1445" + ], + "controlled": [ + "doc_07174a8633ac", + "doc_745f751f1344", + "doc_d1f7d83e0824" + ], + "controls": [ + "doc_109157c0d2d7", + "doc_3ae3aaf0c12c", + "doc_3f60413b3a06", + "doc_9e9118ed2141", + "doc_a24b92541657", + "doc_b6088784caab", + "doc_bf91f8d5da74", + "doc_deb460ffa5ee", + "doc_e67e762fb6ab", + "doc_f12bbbc027a7", + "doc_f18f523b6ecc", + "doc_f2ae50c638a4", + "doc_f5f256549d84" + ], + "convenience": [ + "doc_bbc97cbff254" + ], + "conventions": [ + "doc_4e1ebaf40267" + ], + "conversation": [ + "doc_0383dbf277ba", + "doc_07174a8633ac", + "doc_0ac01bce8c79", + "doc_136d7a909d51", + "doc_3f60413b3a06", + "doc_4a1cf8a90e83", + "doc_50ac650fd8f6", + "doc_5ee8bbdbd8a8", + "doc_745f751f1344", + "doc_75887f91c7df", + "doc_7b26cbd9a7ae", + "doc_9e9118ed2141", + "doc_bf91f8d5da74", + "doc_e7d508371e5c", + "doc_e8794369a1a4", + "doc_f2ae50c638a4" + ], + "conversation-visible": [ + "doc_0383dbf277ba" + ], + "conversation_id": [ + "doc_75887f91c7df" + ], + "conversations": [ + "doc_0383dbf277ba", + "doc_07174a8633ac", + "doc_136d7a909d51", + "doc_18d38fdacc9e", + "doc_61fd9f682847" + ], + "conversion": [ + "doc_64090a20f830" + ], + "converted": [ + "doc_64090a20f830" + ], + "converts": [ + "doc_64090a20f830", + "doc_745f751f1344" + ], + "cookbook": [ + "doc_9e9118ed2141" + ], + "cooldown": [ + "doc_5ee8bbdbd8a8", + "doc_a24b92541657" + ], + "coordinate": [ + "doc_f18f523b6ecc" + ], + "coordinated": [ + "doc_50ac650fd8f6" + ], + "coordination": [ + "doc_a5120de838fb" + ], + "coordinator": [ + "doc_18d38fdacc9e", + "doc_57e2139f0873", + "doc_a2e84472f1f9", + "doc_a5120de838fb", + "doc_e67e762fb6ab", + "doc_f12bbbc027a7" + ], + "copy": [ + "doc_64090a20f830", + "doc_f2ae50c638a4" + ], + "core": [ + "doc_07174a8633ac", + "doc_1e46cbc6bfae", + "doc_2a18de009d56", + "doc_3aa0b3792d22", + "doc_3eeb75d383b8", + "doc_3f1383f72c5f", + "doc_4e1ebaf40267", + "doc_50ac650fd8f6", + "doc_618e34441fa7", + "doc_745f751f1344", + "doc_bbc97cbff254", + "doc_d147a81bac29", + "doc_deb460ffa5ee", + "doc_f18f523b6ecc", + "doc_f2ae50c638a4" + ], + "core-runner": [ + "doc_3aa0b3792d22" + ], + "correct": [ + "doc_618e34441fa7", + "doc_c93c115aeb85", + "doc_f12bbbc027a7" + ], + "corrected": [ + "doc_0ac01bce8c79" + ], + "correctly": [ + "doc_136d7a909d51", + "doc_618e34441fa7", + "doc_c93c115aeb85" + ], + "correctness": [ + "doc_618e34441fa7" + ], + "correlation": [ + "doc_75887f91c7df" + ], + "correlation_id": [ + "doc_75887f91c7df", + "doc_f54217a0f2cf" + ], + "corresponding": [ + "doc_43ad71a4ad0c" + ], + "corrupted": [ + "doc_4a1cf8a90e83" + ], + "corruption": [ + "doc_4a1cf8a90e83" + ], + "cors": [ + "doc_61fd9f682847" + ], + "cosine": [ + "doc_0383dbf277ba" + ], + "cosine_similarity": [ + "doc_2a18de009d56" + ], + "cost": [ + "doc_067033cfc945", + "doc_136d7a909d51", + "doc_18d38fdacc9e", + "doc_1c5f37ea0ad3", + "doc_1e46cbc6bfae", + "doc_3ae3aaf0c12c", + "doc_4055659fe573", + "doc_43ad71a4ad0c", + "doc_4a1cf8a90e83", + "doc_50ac650fd8f6", + "doc_567be1281e2f", + "doc_5ee8bbdbd8a8", + "doc_7320ab982fab", + "doc_78a8125d2c7e", + "doc_a24b92541657", + "doc_a5120de838fb", + "doc_bbc97cbff254", + "doc_bf91f8d5da74", + "doc_c93c115aeb85", + "doc_d1f7d83e0824", + "doc_deb460ffa5ee", + "doc_e67e762fb6ab", + "doc_eeed52120ccf", + "doc_f18f523b6ecc", + "doc_f5f256549d84", + "doc_fdf48d427fc0" + ], + "cost-aware": [ + "doc_fdf48d427fc0" + ], + "cost-optimized": [ + "doc_d1f7d83e0824" + ], + "cost-sensitive": [ + "doc_d1f7d83e0824", + "doc_eeed52120ccf", + "doc_f92685a34f7e" + ], + "cost_monitoring": [ + "doc_3aa0b3792d22" + ], + "costs": [ + "doc_136d7a909d51", + "doc_43ad71a4ad0c", + "doc_7320ab982fab", + "doc_78a8125d2c7e", + "doc_a5120de838fb", + "doc_c93c115aeb85", + "doc_d1f7d83e0824", + "doc_fdf48d427fc0" + ], + "could": [ + "doc_4055659fe573", + "doc_618e34441fa7" + ], + "count": [ + "doc_4055659fe573", + "doc_50ac650fd8f6", + "doc_f12bbbc027a7", + "doc_f5f256549d84" + ], + "counter": [ + "doc_07174a8633ac", + "doc_4a1cf8a90e83" + ], + "counters": [ + "doc_4a1cf8a90e83", + "doc_745f751f1344", + "doc_e7d508371e5c" + ], + "counts": [ + "doc_07174a8633ac", + "doc_5ee8bbdbd8a8", + "doc_618e34441fa7", + "doc_745f751f1344", + "doc_e7d508371e5c", + "doc_e8794369a1a4", + "doc_fdf48d427fc0" + ], + "cover": [ + "doc_50ac650fd8f6", + "doc_57e2139f0873", + "doc_618e34441fa7" + ], + "coverage": [ + "doc_618e34441fa7" + ], + "covered": [ + "doc_618e34441fa7" + ], + "covering": [ + "doc_3eeb75d383b8" + ], + "covers": [ + "doc_067033cfc945", + "doc_0ac01bce8c79", + "doc_136d7a909d51", + "doc_18d38fdacc9e", + "doc_a5120de838fb" + ], + "crash": [ + "doc_0383dbf277ba", + "doc_4e1ebaf40267", + "doc_618e34441fa7" + ], + "crashed": [ + "doc_4a1cf8a90e83" + ], + "crashes": [ + "doc_4a1cf8a90e83" + ], + "create": [ + "doc_067033cfc945", + "doc_464a2b4a8d35", + "doc_618e34441fa7", + "doc_bbc97cbff254", + "doc_e7d508371e5c", + "doc_f92685a34f7e" + ], + "create_llm_cache": [ + "doc_2a18de009d56" + ], + "create_llm_client": [ + "doc_7320ab982fab" + ], + "create_llm_router": [ + "doc_2a18de009d56" + ], + "create_memory_store_from_env": [ + "doc_2a18de009d56" + ], + "create_telemetry_sink": [ + "doc_2a18de009d56" + ], + "created": [ + "doc_067033cfc945" + ], + "creates": [ + "doc_1e46cbc6bfae", + "doc_464a2b4a8d35", + "doc_75887f91c7df", + "doc_78a8125d2c7e", + "doc_d6ad61a79371" + ], + "creating": [ + "doc_1e46cbc6bfae", + "doc_2a18de009d56", + "doc_4462f67b1c03", + "doc_b6088784caab" + ], + "creation": [ + "doc_2a18de009d56", + "doc_75887f91c7df" + ], + "credentials": [ + "doc_567be1281e2f", + "doc_f1e32ed2ffce" + ], + "credits": [ + "doc_1c5f37ea0ad3", + "doc_c93c115aeb85", + "doc_e67e762fb6ab" + ], + "critical": [ + "doc_0ac01bce8c79", + "doc_4055659fe573", + "doc_4e1ebaf40267", + "doc_a24b92541657", + "doc_d6ad61a79371" + ], + "cron": [ + "doc_a24b92541657" + ], + "cross-adapter": [ + "doc_4e1ebaf40267" + ], + "cross-cutting": [ + "doc_745f751f1344", + "doc_c346f13ce597" + ], + "cross-run": [ + "doc_75887f91c7df" + ], + "cross-system": [ + "doc_57e2139f0873", + "doc_75887f91c7df" + ], + "ctx": [ + "doc_64090a20f830", + "doc_745f751f1344", + "doc_9e9118ed2141", + "doc_bbc97cbff254" + ], + "cumulative": [ + "doc_fdf48d427fc0" + ], + "curated": [ + "doc_7320ab982fab" + ], + "current": [ + "doc_4a1cf8a90e83", + "doc_50ac650fd8f6", + "doc_57e2139f0873", + "doc_64090a20f830", + "doc_d147a81bac29", + "doc_eeed52120ccf", + "doc_f54217a0f2cf" + ], + "custom": [ + "doc_0383dbf277ba", + "doc_2a18de009d56", + "doc_464a2b4a8d35", + "doc_5a65ddab1445", + "doc_5ee8bbdbd8a8", + "doc_64090a20f830", + "doc_78b1717fafe3", + "doc_a24b92541657", + "doc_d6ad61a79371", + "doc_e67e762fb6ab", + "doc_e8794369a1a4", + "doc_eeed52120ccf", + "doc_f1e32ed2ffce", + "doc_f92685a34f7e" + ], + "cycle": [ + "doc_e7d508371e5c" + ], + "dag": [ + "doc_57e2139f0873", + "doc_618e34441fa7", + "doc_f12bbbc027a7" + ], + "dags": [ + "doc_f12bbbc027a7" + ], + "daily": [ + "doc_07174a8633ac" + ], + "daily_cost": [ + "doc_78a8125d2c7e" + ], + "dangerous": [ + "doc_50ac650fd8f6", + "doc_a5120de838fb" + ], + "dark": [ + "doc_3aa0b3792d22" + ], + "dashboard": [ + "doc_78a8125d2c7e" + ], + "dashboards": [ + "doc_0ac01bce8c79", + "doc_18d38fdacc9e", + "doc_618e34441fa7", + "doc_fdf48d427fc0" + ], + "data": [ + "doc_07174a8633ac", + "doc_0ac01bce8c79", + "doc_109157c0d2d7", + "doc_43ad71a4ad0c", + "doc_4a1cf8a90e83", + "doc_4e1ebaf40267", + "doc_50ac650fd8f6", + "doc_57e2139f0873", + "doc_618e34441fa7", + "doc_75887f91c7df", + "doc_9e9118ed2141", + "doc_d6ad61a79371", + "doc_f54217a0f2cf" + ], + "database": [ + "doc_0383dbf277ba", + "doc_067033cfc945", + "doc_61fd9f682847", + "doc_b6088784caab" + ], + "databases": [ + "doc_067033cfc945", + "doc_2a18de009d56", + "doc_9e9118ed2141" + ], + "dataclass": [ + "doc_1e46cbc6bfae", + "doc_2a18de009d56", + "doc_5ee8bbdbd8a8", + "doc_f54217a0f2cf" + ], + "dataclasses": [ + "doc_3eeb75d383b8" + ], + "datadog": [ + "doc_2a18de009d56", + "doc_78a8125d2c7e" + ], + "day": [ + "doc_50ac650fd8f6" + ], + "days": [ + "doc_4a1cf8a90e83" + ], + "db": [ + "doc_61fd9f682847" + ], + "dead": [ + "doc_f54217a0f2cf" + ], + "dead-letter": [ + "doc_57e2139f0873", + "doc_618e34441fa7", + "doc_75887f91c7df", + "doc_78b1717fafe3" + ], + "dead_letter": [ + "doc_78b1717fafe3" + ], + "dead_letters": [ + "doc_067033cfc945" + ], + "debug": [ + "doc_136d7a909d51", + "doc_3f1383f72c5f", + "doc_50ac650fd8f6", + "doc_a24b92541657" + ], + "debug_config": [ + "doc_a24b92541657" + ], + "debuggable": [ + "doc_5a65ddab1445", + "doc_618e34441fa7" + ], + "debugger": [ + "doc_3aa0b3792d22", + "doc_3f1383f72c5f", + "doc_d147a81bac29" + ], + "debugging": [ + "doc_136d7a909d51", + "doc_18d38fdacc9e", + "doc_43ad71a4ad0c", + "doc_4a1cf8a90e83", + "doc_5ee8bbdbd8a8", + "doc_618e34441fa7", + "doc_75887f91c7df", + "doc_78a8125d2c7e", + "doc_f18f523b6ecc", + "doc_f54217a0f2cf" + ], + "decide": [ + "doc_0ac01bce8c79", + "doc_4e1ebaf40267", + "doc_5ee8bbdbd8a8", + "doc_745f751f1344", + "doc_78b1717fafe3", + "doc_b6088784caab" + ], + "decides": [ + "doc_4e1ebaf40267", + "doc_50ac650fd8f6", + "doc_a2e84472f1f9", + "doc_b6088784caab", + "doc_d6ad61a79371", + "doc_f12bbbc027a7" + ], + "deciding": [ + "doc_a24b92541657" + ], + "decision": [ + "doc_0ac01bce8c79", + "doc_4055659fe573", + "doc_50ac650fd8f6", + "doc_57e2139f0873", + "doc_618e34441fa7", + "doc_745f751f1344", + "doc_a24b92541657", + "doc_d6ad61a79371", + "doc_e7d508371e5c" + ], + "decisions": [ + "doc_0ac01bce8c79", + "doc_43ad71a4ad0c", + "doc_618e34441fa7", + "doc_a24b92541657", + "doc_e67e762fb6ab", + "doc_f12bbbc027a7", + "doc_f54217a0f2cf" + ], + "declarations": [ + "doc_2a18de009d56" + ], + "declarative": [ + "doc_2a18de009d56", + "doc_deb460ffa5ee", + "doc_f2ae50c638a4" + ], + "declare": [ + "doc_0383dbf277ba" + ], + "declared": [ + "doc_745f751f1344" + ], + "declares": [ + "doc_0383dbf277ba" + ], + "decorate": [ + "doc_745f751f1344" + ], + "decorated": [ + "doc_745f751f1344" + ], + "decorator": [ + "doc_2a18de009d56", + "doc_3eeb75d383b8", + "doc_464a2b4a8d35", + "doc_5a65ddab1445", + "doc_745f751f1344", + "doc_9e9118ed2141", + "doc_a24b92541657", + "doc_bbc97cbff254" + ], + "decorators": [ + "doc_d147a81bac29" + ], + "decouple": [ + "doc_57e2139f0873", + "doc_78b1717fafe3" + ], + "deduplicates": [ + "doc_64090a20f830" + ], + "deduplication": [ + "doc_5ee8bbdbd8a8", + "doc_75887f91c7df" + ], + "deep": [ + "doc_3eeb75d383b8", + "doc_3f1383f72c5f", + "doc_a24b92541657" + ], + "deeper": [ + "doc_f5f256549d84" + ], + "def": [ + "doc_9e9118ed2141" + ], + "default": [ + "doc_0ac01bce8c79", + "doc_2798948c3080", + "doc_3aa0b3792d22", + "doc_4055659fe573", + "doc_4462f67b1c03", + "doc_464a2b4a8d35", + "doc_4a1cf8a90e83", + "doc_567be1281e2f", + "doc_61fd9f682847", + "doc_64090a20f830", + "doc_7320ab982fab", + "doc_745f751f1344", + "doc_75887f91c7df", + "doc_78b1717fafe3", + "doc_9e9118ed2141", + "doc_a24b92541657", + "doc_a2e84472f1f9", + "doc_b6088784caab", + "doc_bbc97cbff254", + "doc_d6ad61a79371", + "doc_e67e762fb6ab", + "doc_eeed52120ccf", + "doc_f54217a0f2cf" + ], + "default_allowlisted_commands": [ + "doc_a24b92541657" + ], + "default_sandbox_profile": [ + "doc_a24b92541657" + ], + "default_timeout": [ + "doc_745f751f1344" + ], + "default_timeout_s": [ + "doc_4462f67b1c03" + ], + "defaults": [ + "doc_1e46cbc6bfae", + "doc_2a18de009d56", + "doc_464a2b4a8d35", + "doc_4a1cf8a90e83", + "doc_567be1281e2f", + "doc_61fd9f682847", + "doc_a24b92541657", + "doc_bbc97cbff254", + "doc_d147a81bac29", + "doc_deb460ffa5ee", + "doc_e67e762fb6ab", + "doc_f18f523b6ecc" + ], + "defense": [ + "doc_43ad71a4ad0c", + "doc_464a2b4a8d35", + "doc_64090a20f830", + "doc_fdf48d427fc0" + ], + "defenses": [ + "doc_fdf48d427fc0" + ], + "defer": [ + "doc_0ac01bce8c79", + "doc_50ac650fd8f6", + "doc_745f751f1344", + "doc_a24b92541657" + ], + "deferred": [ + "doc_109157c0d2d7", + "doc_9e9118ed2141", + "doc_a24b92541657", + "doc_f54217a0f2cf" + ], + "define": [ + "doc_2798948c3080", + "doc_4e1ebaf40267", + "doc_745f751f1344", + "doc_78b1717fafe3", + "doc_a5120de838fb", + "doc_b6088784caab", + "doc_bbc97cbff254", + "doc_bf91f8d5da74", + "doc_c346f13ce597", + "doc_d1f7d83e0824", + "doc_deb460ffa5ee", + "doc_e67e762fb6ab" + ], + "defined": [ + "doc_50ac650fd8f6" + ], + "defines": [ + "doc_1e46cbc6bfae", + "doc_78b1717fafe3", + "doc_a24b92541657", + "doc_c346f13ce597" + ], + "defining": [ + "doc_1e46cbc6bfae" + ], + "definition": [ + "doc_07174a8633ac", + "doc_1e46cbc6bfae", + "doc_2a18de009d56", + "doc_4e1ebaf40267", + "doc_745f751f1344" + ], + "definitions": [ + "doc_2a18de009d56", + "doc_43ad71a4ad0c", + "doc_50ac650fd8f6", + "doc_5ee8bbdbd8a8", + "doc_a24b92541657", + "doc_d147a81bac29", + "doc_deb460ffa5ee" + ], + "degradation": [ + "doc_0ac01bce8c79" + ], + "degrade": [ + "doc_0ac01bce8c79", + "doc_5ee8bbdbd8a8", + "doc_745f751f1344", + "doc_a24b92541657" + ], + "degraded": [ + "doc_0ac01bce8c79", + "doc_1e46cbc6bfae", + "doc_4055659fe573", + "doc_4a1cf8a90e83", + "doc_4e1ebaf40267", + "doc_5ee8bbdbd8a8", + "doc_618e34441fa7", + "doc_a24b92541657", + "doc_bf91f8d5da74", + "doc_e7d508371e5c", + "doc_f5f256549d84", + "doc_fdf48d427fc0" + ], + "delay": [ + "doc_61fd9f682847", + "doc_7320ab982fab" + ], + "delays": [ + "doc_7320ab982fab" + ], + "delegate": [ + "doc_50ac650fd8f6", + "doc_a24b92541657", + "doc_a2e84472f1f9", + "doc_a5120de838fb", + "doc_e67e762fb6ab", + "doc_f12bbbc027a7" + ], + "delegates": [ + "doc_57e2139f0873", + "doc_e67e762fb6ab" + ], + "delegating": [ + "doc_a2e84472f1f9" + ], + "delegation": [ + "doc_1e46cbc6bfae", + "doc_2a18de009d56", + "doc_3f60413b3a06", + "doc_57e2139f0873", + "doc_618e34441fa7", + "doc_a2e84472f1f9", + "doc_a5120de838fb", + "doc_e67e762fb6ab", + "doc_f12bbbc027a7" + ], + "delegationedge": [ + "doc_2a18de009d56" + ], + "delegationengine": [ + "doc_2a18de009d56" + ], + "delegationnode": [ + "doc_2a18de009d56" + ], + "delegationplan": [ + "doc_2a18de009d56" + ], + "delegationplanner": [ + "doc_2a18de009d56" + ], + "delegationresult": [ + "doc_2a18de009d56" + ], + "delegationscheduler": [ + "doc_2a18de009d56" + ], + "delete_": [ + "doc_43ad71a4ad0c" + ], + "delete_resource": [ + "doc_43ad71a4ad0c" + ], + "deletes": [ + "doc_43ad71a4ad0c" + ], + "deleting": [ + "doc_d6ad61a79371" + ], + "deliberate": [ + "doc_618e34441fa7" + ], + "deliver": [ + "doc_0ac01bce8c79" + ], + "delivered": [ + "doc_75887f91c7df" + ], + "deliveries": [ + "doc_75887f91c7df" + ], + "delivery": [ + "doc_2a18de009d56", + "doc_4e1ebaf40267", + "doc_61fd9f682847", + "doc_75887f91c7df", + "doc_c346f13ce597", + "doc_d147a81bac29" + ], + "deliverystore": [ + "doc_75887f91c7df" + ], + "delta": [ + "doc_618e34441fa7", + "doc_f54217a0f2cf" + ], + "deltas": [ + "doc_109157c0d2d7", + "doc_5ee8bbdbd8a8", + "doc_618e34441fa7", + "doc_eeed52120ccf" + ], + "demand": [ + "doc_2798948c3080" + ], + "demo": [ + "doc_deb460ffa5ee" + ], + "demonstrates": [ + "doc_07174a8633ac", + "doc_1e46cbc6bfae", + "doc_40e30666bfa8", + "doc_43ad71a4ad0c", + "doc_4462f67b1c03", + "doc_464a2b4a8d35", + "doc_64090a20f830", + "doc_7b26cbd9a7ae", + "doc_a5120de838fb", + "doc_c346f13ce597", + "doc_d1f7d83e0824", + "doc_fdf48d427fc0" + ], + "denial": [ + "doc_0ac01bce8c79", + "doc_4055659fe573", + "doc_5ee8bbdbd8a8", + "doc_d6ad61a79371" + ], + "denials": [ + "doc_0ac01bce8c79" + ], + "denied": [ + "doc_0ac01bce8c79", + "doc_4055659fe573", + "doc_567be1281e2f", + "doc_a24b92541657", + "doc_d6ad61a79371" + ], + "denied_paths": [ + "doc_a24b92541657" + ], + "denies": [ + "doc_5ee8bbdbd8a8" + ], + "deny": [ + "doc_0ac01bce8c79", + "doc_43ad71a4ad0c", + "doc_50ac650fd8f6", + "doc_567be1281e2f", + "doc_745f751f1344", + "doc_9e9118ed2141", + "doc_a24b92541657", + "doc_d6ad61a79371", + "doc_e67e762fb6ab" + ], + "deny_shell_operators": [ + "doc_a24b92541657" + ], + "depend": [ + "doc_3ae3aaf0c12c", + "doc_618e34441fa7" + ], + "dependencies": [ + "doc_2a18de009d56", + "doc_4e1ebaf40267", + "doc_618e34441fa7" + ], + "dependent": [ + "doc_618e34441fa7" + ], + "depending": [ + "doc_4a1cf8a90e83", + "doc_618e34441fa7" + ], + "depends": [ + "doc_0ac01bce8c79", + "doc_f12bbbc027a7" + ], + "deploying": [ + "doc_067033cfc945", + "doc_136d7a909d51" + ], + "deployment": [ + "doc_067033cfc945", + "doc_1c5f37ea0ad3", + "doc_3aa0b3792d22", + "doc_4462f67b1c03", + "doc_b6088784caab", + "doc_deb460ffa5ee", + "doc_e67e762fb6ab", + "doc_f2ae50c638a4", + "doc_f5f256549d84" + ], + "deployments": [ + "doc_0383dbf277ba", + "doc_067033cfc945", + "doc_4462f67b1c03", + "doc_618e34441fa7", + "doc_78a8125d2c7e", + "doc_78b1717fafe3", + "doc_f1e32ed2ffce", + "doc_f5f256549d84" + ], + "depth": [ + "doc_067033cfc945", + "doc_a24b92541657", + "doc_e7d508371e5c" + ], + "dequeue": [ + "doc_2a18de009d56" + ], + "derive_auto_prompt_filename": [ + "doc_64090a20f830" + ], + "derived": [ + "doc_4a1cf8a90e83", + "doc_64090a20f830", + "doc_7320ab982fab", + "doc_75887f91c7df" + ], + "describe": [ + "doc_43ad71a4ad0c", + "doc_618e34441fa7", + "doc_f18f523b6ecc", + "doc_f54217a0f2cf" + ], + "described": [ + "doc_5ee8bbdbd8a8" + ], + "describes": [ + "doc_e67e762fb6ab", + "doc_f54217a0f2cf" + ], + "description": [ + "doc_07174a8633ac", + "doc_0ac01bce8c79", + "doc_1e46cbc6bfae", + "doc_43ad71a4ad0c", + "doc_4462f67b1c03", + "doc_464a2b4a8d35", + "doc_4a1cf8a90e83", + "doc_618e34441fa7", + "doc_61fd9f682847", + "doc_64090a20f830", + "doc_7320ab982fab", + "doc_745f751f1344", + "doc_78a8125d2c7e", + "doc_a24b92541657", + "doc_a2e84472f1f9", + "doc_b6088784caab", + "doc_e7d508371e5c", + "doc_f54217a0f2cf" + ], + "descriptions": [ + "doc_136d7a909d51" + ], + "descriptive": [ + "doc_618e34441fa7" + ], + "deserialized": [ + "doc_4a1cf8a90e83" + ], + "design": [ + "doc_0383dbf277ba", + "doc_2798948c3080", + "doc_3ae3aaf0c12c", + "doc_4e1ebaf40267", + "doc_50ac650fd8f6", + "doc_57e2139f0873", + "doc_5a65ddab1445", + "doc_64090a20f830", + "doc_b6088784caab", + "doc_c346f13ce597", + "doc_e67e762fb6ab", + "doc_f18f523b6ecc" + ], + "designed": [ + "doc_2a18de009d56", + "doc_464a2b4a8d35", + "doc_745f751f1344", + "doc_d6ad61a79371", + "doc_f18f523b6ecc", + "doc_f2ae50c638a4" + ], + "destructive": [ + "doc_43ad71a4ad0c", + "doc_567be1281e2f", + "doc_a5120de838fb", + "doc_d6ad61a79371" + ], + "detail": [ + "doc_3f1383f72c5f", + "doc_464a2b4a8d35", + "doc_4e1ebaf40267", + "doc_5ee8bbdbd8a8", + "doc_bbc97cbff254" + ], + "detailed": [ + "doc_136d7a909d51", + "doc_3f1383f72c5f", + "doc_c346f13ce597" + ], + "details": [ + "doc_136d7a909d51", + "doc_2a18de009d56", + "doc_618e34441fa7", + "doc_61fd9f682847", + "doc_b6088784caab", + "doc_c93c115aeb85", + "doc_deb460ffa5ee", + "doc_f92685a34f7e" + ], + "detected": [ + "doc_64090a20f830" + ], + "detection": [ + "doc_18d38fdacc9e" + ], + "detects": [ + "doc_3f1383f72c5f" + ], + "determine": [ + "doc_136d7a909d51" + ], + "determines": [ + "doc_0ac01bce8c79", + "doc_5ee8bbdbd8a8", + "doc_d1f7d83e0824" + ], + "deterministic": [ + "doc_0ac01bce8c79", + "doc_2a18de009d56", + "doc_618e34441fa7", + "doc_64090a20f830", + "doc_75887f91c7df", + "doc_a24b92541657", + "doc_c93c115aeb85" + ], + "dev": [ + "doc_0383dbf277ba" + ], + "developer": [ + "doc_f18f523b6ecc" + ], + "developer-guide": [ + "doc_3aa0b3792d22" + ], + "developers": [ + "doc_bbc97cbff254" + ], + "development": [ + "doc_0383dbf277ba", + "doc_067033cfc945", + "doc_1e46cbc6bfae", + "doc_2a18de009d56", + "doc_3f60413b3a06", + "doc_64090a20f830", + "doc_7320ab982fab", + "doc_78a8125d2c7e", + "doc_78b1717fafe3", + "doc_b6088784caab" + ], + "diagnosable": [ + "doc_3ae3aaf0c12c" + ], + "diagnosing": [ + "doc_5ee8bbdbd8a8" + ], + "diagnostics": [ + "doc_3f1383f72c5f" + ], + "diagram": [ + "doc_5ee8bbdbd8a8" + ], + "dict": [ + "doc_07174a8633ac", + "doc_0ac01bce8c79", + "doc_2798948c3080", + "doc_43ad71a4ad0c", + "doc_4a1cf8a90e83", + "doc_5ee8bbdbd8a8", + "doc_64090a20f830", + "doc_745f751f1344", + "doc_75887f91c7df", + "doc_9e9118ed2141", + "doc_a24b92541657", + "doc_e67e762fb6ab", + "doc_e7d508371e5c", + "doc_f54217a0f2cf" + ], + "dictionary": [ + "doc_3f60413b3a06", + "doc_464a2b4a8d35", + "doc_64090a20f830", + "doc_c346f13ce597" + ], + "difference": [ + "doc_5a65ddab1445" + ], + "differences": [ + "doc_43ad71a4ad0c", + "doc_5a65ddab1445", + "doc_e8794369a1a4" + ], + "different": [ + "doc_0ac01bce8c79", + "doc_136d7a909d51", + "doc_1c5f37ea0ad3", + "doc_3ae3aaf0c12c", + "doc_43ad71a4ad0c", + "doc_4a1cf8a90e83", + "doc_57e2139f0873", + "doc_618e34441fa7", + "doc_64090a20f830", + "doc_a2e84472f1f9", + "doc_c346f13ce597", + "doc_d1f7d83e0824", + "doc_e67e762fb6ab", + "doc_f12bbbc027a7" + ], + "differently": [ + "doc_5ee8bbdbd8a8" + ], + "diffs": [ + "doc_2798948c3080" + ], + "dimension": [ + "doc_07174a8633ac", + "doc_61fd9f682847" + ], + "dimensions": [ + "doc_07174a8633ac" + ], + "direct": [ + "doc_a5120de838fb", + "doc_f18f523b6ecc", + "doc_f1e32ed2ffce" + ], + "direct_llm_structured_output": [ + "doc_3aa0b3792d22" + ], + "directed": [ + "doc_f12bbbc027a7" + ], + "directly": [ + "doc_2798948c3080", + "doc_3f1383f72c5f", + "doc_3f60413b3a06", + "doc_40e30666bfa8", + "doc_4a1cf8a90e83", + "doc_61fd9f682847", + "doc_64090a20f830", + "doc_78a8125d2c7e", + "doc_9e9118ed2141", + "doc_a24b92541657", + "doc_a5120de838fb", + "doc_f12bbbc027a7", + "doc_f92685a34f7e" + ], + "directories": [ + "doc_464a2b4a8d35", + "doc_61fd9f682847", + "doc_b6088784caab" + ], + "directory": [ + "doc_2798948c3080", + "doc_2a18de009d56", + "doc_464a2b4a8d35", + "doc_61fd9f682847", + "doc_64090a20f830", + "doc_9e9118ed2141", + "doc_a24b92541657", + "doc_a5120de838fb", + "doc_b6088784caab", + "doc_e67e762fb6ab" + ], + "directory-scoped": [ + "doc_9e9118ed2141" + ], + "disable": [ + "doc_b6088784caab" + ], + "discipline": [ + "doc_3ae3aaf0c12c" + ], + "disconnects": [ + "doc_136d7a909d51" + ], + "discord": [ + "doc_2a18de009d56" + ], + "discover": [ + "doc_40e30666bfa8", + "doc_a5120de838fb", + "doc_b6088784caab", + "doc_e34fe24f9585" + ], + "discovery": [ + "doc_1c5f37ea0ad3", + "doc_e67e762fb6ab" + ], + "disk": [ + "doc_64090a20f830" + ], + "dispatch": [ + "doc_2a18de009d56", + "doc_3f60413b3a06", + "doc_745f751f1344", + "doc_78b1717fafe3", + "doc_f18f523b6ecc" + ], + "dispatches": [ + "doc_618e34441fa7", + "doc_a2e84472f1f9" + ], + "dispatching": [ + "doc_a2e84472f1f9", + "doc_f54217a0f2cf" + ], + "distinct": [ + "doc_2a18de009d56", + "doc_a2e84472f1f9", + "doc_c346f13ce597" + ], + "distinction": [ + "doc_43ad71a4ad0c" + ], + "distinguish": [ + "doc_43ad71a4ad0c", + "doc_5ee8bbdbd8a8", + "doc_d147a81bac29" + ], + "distributed": [ + "doc_067033cfc945", + "doc_3ae3aaf0c12c" + ], + "distribution": [ + "doc_d147a81bac29", + "doc_deb460ffa5ee" + ], + "dive": [ + "doc_a24b92541657" + ], + "diverse": [ + "doc_18d38fdacc9e" + ], + "diversification": [ + "doc_d1f7d83e0824" + ], + "dlq": [ + "doc_57e2139f0873", + "doc_75887f91c7df", + "doc_78b1717fafe3", + "doc_d147a81bac29" + ], + "do": [ + "doc_109157c0d2d7", + "doc_2798948c3080", + "doc_2a18de009d56", + "doc_3eeb75d383b8", + "doc_4e1ebaf40267", + "doc_618e34441fa7", + "doc_7320ab982fab", + "doc_745f751f1344", + "doc_b6088784caab", + "doc_bbc97cbff254", + "doc_e67e762fb6ab", + "doc_f12bbbc027a7" + ], + "doc": [ + "doc_618e34441fa7" + ], + "docker": [ + "doc_067033cfc945", + "doc_4462f67b1c03" + ], + "docker-compose": [ + "doc_067033cfc945" + ], + "dockerfile": [ + "doc_067033cfc945" + ], + "docs": [ + "doc_3ae3aaf0c12c", + "doc_3eeb75d383b8", + "doc_3f1383f72c5f", + "doc_618e34441fa7", + "doc_9e9118ed2141", + "doc_b6088784caab", + "doc_bbc97cbff254", + "doc_d147a81bac29", + "doc_f18f523b6ecc" + ], + "docstring": [ + "doc_745f751f1344", + "doc_a24b92541657" + ], + "document": [ + "doc_3ae3aaf0c12c" + ], + "documentation": [ + "doc_136d7a909d51", + "doc_5a65ddab1445", + "doc_d147a81bac29" + ], + "documents": [ + "doc_f54217a0f2cf" + ], + "does": [ + "doc_1c5f37ea0ad3", + "doc_3f60413b3a06", + "doc_4055659fe573", + "doc_4a1cf8a90e83", + "doc_618e34441fa7", + "doc_61fd9f682847", + "doc_f12bbbc027a7", + "doc_f18f523b6ecc" + ], + "doesn": [ + "doc_136d7a909d51", + "doc_2798948c3080", + "doc_40e30666bfa8", + "doc_4e1ebaf40267", + "doc_e34fe24f9585", + "doc_e67e762fb6ab" + ], + "don": [ + "doc_0383dbf277ba", + "doc_136d7a909d51", + "doc_18d38fdacc9e", + "doc_2798948c3080", + "doc_4e1ebaf40267", + "doc_57e2139f0873", + "doc_b6088784caab", + "doc_e67e762fb6ab", + "doc_f12bbbc027a7" + ], + "done": [ + "doc_50ac650fd8f6" + ], + "down": [ + "doc_a24b92541657", + "doc_a5120de838fb" + ], + "downstream": [ + "doc_3eeb75d383b8", + "doc_618e34441fa7", + "doc_bbc97cbff254" + ], + "drain": [ + "doc_c93c115aeb85" + ], + "dramatically": [ + "doc_2798948c3080" + ], + "drift": [ + "doc_18d38fdacc9e", + "doc_50ac650fd8f6" + ], + "drivers": [ + "doc_4e1ebaf40267" + ], + "drives": [ + "doc_0ac01bce8c79" + ], + "drop_": [ + "doc_43ad71a4ad0c" + ], + "drop_table": [ + "doc_d6ad61a79371" + ], + "dropped": [ + "doc_4e1ebaf40267" + ], + "dsn": [ + "doc_61fd9f682847" + ], + "due": [ + "doc_f54217a0f2cf" + ], + "duplicate": [ + "doc_0383dbf277ba", + "doc_1c5f37ea0ad3", + "doc_4a1cf8a90e83", + "doc_75887f91c7df" + ], + "duplicates": [ + "doc_75887f91c7df" + ], + "durability": [ + "doc_4a1cf8a90e83" + ], + "durable": [ + "doc_75887f91c7df", + "doc_78b1717fafe3", + "doc_f5f256549d84" + ], + "durable-enough": [ + "doc_0383dbf277ba" + ], + "duration": [ + "doc_067033cfc945", + "doc_0ac01bce8c79", + "doc_78a8125d2c7e", + "doc_a24b92541657" + ], + "during": [ + "doc_0383dbf277ba", + "doc_1e46cbc6bfae", + "doc_4a1cf8a90e83", + "doc_64090a20f830", + "doc_78a8125d2c7e", + "doc_b6088784caab", + "doc_c93c115aeb85", + "doc_e67e762fb6ab", + "doc_f54217a0f2cf", + "doc_fdf48d427fc0" + ], + "dynamic": [ + "doc_2798948c3080", + "doc_64090a20f830", + "doc_a24b92541657", + "doc_bbc97cbff254" + ], + "dynamodb": [ + "doc_2a18de009d56" + ], + "each": [ + "doc_0383dbf277ba", + "doc_0ac01bce8c79", + "doc_18d38fdacc9e", + "doc_1c5f37ea0ad3", + "doc_2a18de009d56", + "doc_3f60413b3a06", + "doc_4a1cf8a90e83", + "doc_4e1ebaf40267", + "doc_50ac650fd8f6", + "doc_567be1281e2f", + "doc_57e2139f0873", + "doc_5ee8bbdbd8a8", + "doc_618e34441fa7", + "doc_7320ab982fab", + "doc_745f751f1344", + "doc_78a8125d2c7e", + "doc_9e9118ed2141", + "doc_a24b92541657", + "doc_a2e84472f1f9", + "doc_a5120de838fb", + "doc_b6088784caab", + "doc_c346f13ce597", + "doc_c93c115aeb85", + "doc_e67e762fb6ab", + "doc_e7d508371e5c", + "doc_eeed52120ccf", + "doc_f12bbbc027a7", + "doc_f2ae50c638a4", + "doc_f54217a0f2cf" + ], + "early": [ + "doc_136d7a909d51", + "doc_e67e762fb6ab" + ], + "easier": [ + "doc_e67e762fb6ab" + ], + "easy": [ + "doc_43ad71a4ad0c", + "doc_f54217a0f2cf" + ], + "echo": [ + "doc_a24b92541657" + ], + "edge": [ + "doc_618e34441fa7", + "doc_c93c115aeb85" + ], + "edge-based": [ + "doc_618e34441fa7" + ], + "edit": [ + "doc_2798948c3080", + "doc_64090a20f830" + ], + "editable": [ + "doc_64090a20f830" + ], + "editing": [ + "doc_d147a81bac29" + ], + "effect": [ + "doc_0383dbf277ba", + "doc_2a18de009d56", + "doc_4a1cf8a90e83", + "doc_d6ad61a79371" + ], + "effective": [ + "doc_bbc97cbff254" + ], + "effects": [ + "doc_0383dbf277ba", + "doc_4a1cf8a90e83", + "doc_b6088784caab" + ], + "efficient": [ + "doc_0383dbf277ba" + ], + "efficiently": [ + "doc_78b1717fafe3" + ], + "effort": [ + "doc_a24b92541657", + "doc_e67e762fb6ab" + ], + "either": [ + "doc_0ac01bce8c79" + ], + "elevated": [ + "doc_567be1281e2f" + ], + "elif": [ + "doc_f54217a0f2cf" + ], + "elk": [ + "doc_78a8125d2c7e" + ], + "elsewhere": [ + "doc_64090a20f830" + ], + "embed": [ + "doc_c346f13ce597" + ], + "embed_timeout_s": [ + "doc_4462f67b1c03" + ], + "embedding": [ + "doc_3f60413b3a06", + "doc_61fd9f682847" + ], + "embedding-based": [ + "doc_2a18de009d56" + ], + "embeddingrequest": [ + "doc_c346f13ce597", + "doc_f92685a34f7e" + ], + "embeddingresponse": [ + "doc_c346f13ce597", + "doc_f92685a34f7e" + ], + "embeddings": [ + "doc_0383dbf277ba", + "doc_4462f67b1c03", + "doc_c346f13ce597", + "doc_f92685a34f7e" + ], + "emission": [ + "doc_618e34441fa7" + ], + "emit": [ + "doc_745f751f1344" + ], + "emits": [ + "doc_0ac01bce8c79", + "doc_109157c0d2d7", + "doc_3f1383f72c5f", + "doc_745f751f1344", + "doc_9e9118ed2141", + "doc_a24b92541657", + "doc_f54217a0f2cf" + ], + "emitted": [ + "doc_0ac01bce8c79", + "doc_43ad71a4ad0c", + "doc_618e34441fa7", + "doc_745f751f1344", + "doc_a24b92541657", + "doc_d6ad61a79371", + "doc_f54217a0f2cf" + ], + "empty": [ + "doc_5ee8bbdbd8a8", + "doc_a24b92541657", + "doc_e7d508371e5c" + ], + "enable": [ + "doc_067033cfc945", + "doc_136d7a909d51", + "doc_3f1383f72c5f", + "doc_4462f67b1c03", + "doc_567be1281e2f", + "doc_61fd9f682847", + "doc_7320ab982fab", + "doc_a24b92541657", + "doc_e67e762fb6ab" + ], + "enable_mcp_tools": [ + "doc_a24b92541657", + "doc_e67e762fb6ab" + ], + "enable_skill_tools": [ + "doc_a24b92541657", + "doc_e67e762fb6ab" + ], + "enabled": [ + "doc_5ee8bbdbd8a8", + "doc_9e9118ed2141", + "doc_a24b92541657" + ], + "enables": [ + "doc_1c5f37ea0ad3" + ], + "encountered": [ + "doc_136d7a909d51" + ], + "end": [ + "doc_3f60413b3a06" + ], + "end-to-end": [ + "doc_0ac01bce8c79", + "doc_745f751f1344", + "doc_e8794369a1a4" + ], + "endpoint": [ + "doc_61fd9f682847", + "doc_e34fe24f9585" + ], + "endpoints": [ + "doc_067033cfc945", + "doc_1c5f37ea0ad3", + "doc_57e2139f0873", + "doc_f1e32ed2ffce" + ], + "ends": [ + "doc_07174a8633ac", + "doc_136d7a909d51", + "doc_f54217a0f2cf" + ], + "enforce": [ + "doc_3ae3aaf0c12c", + "doc_9e9118ed2141", + "doc_a5120de838fb", + "doc_f5f256549d84" + ], + "enforcement": [ + "doc_5ee8bbdbd8a8", + "doc_618e34441fa7", + "doc_745f751f1344", + "doc_9e9118ed2141", + "doc_c346f13ce597" + ], + "enforces": [ + "doc_567be1281e2f", + "doc_64090a20f830", + "doc_745f751f1344" + ], + "enforcing": [ + "doc_50ac650fd8f6" + ], + "engine": [ + "doc_0ac01bce8c79", + "doc_1e46cbc6bfae", + "doc_2a18de009d56", + "doc_464a2b4a8d35", + "doc_567be1281e2f", + "doc_57e2139f0873", + "doc_5ee8bbdbd8a8", + "doc_618e34441fa7", + "doc_a24b92541657", + "doc_a5120de838fb", + "doc_d6ad61a79371", + "doc_e67e762fb6ab", + "doc_e7d508371e5c", + "doc_eeed52120ccf", + "doc_f54217a0f2cf" + ], + "engineering": [ + "doc_18d38fdacc9e" + ], + "engines": [ + "doc_a5120de838fb" + ], + "enough": [ + "doc_4a1cf8a90e83", + "doc_50ac650fd8f6", + "doc_57e2139f0873", + "doc_eeed52120ccf", + "doc_f12bbbc027a7", + "doc_f2ae50c638a4" + ], + "enqueue": [ + "doc_2a18de009d56" + ], + "enqueued": [ + "doc_f12bbbc027a7" + ], + "enrichment": [ + "doc_4055659fe573", + "doc_c346f13ce597" + ], + "ensure": [ + "doc_618e34441fa7" + ], + "ensures": [ + "doc_464a2b4a8d35", + "doc_618e34441fa7", + "doc_f54217a0f2cf" + ], + "enterprise": [ + "doc_d6ad61a79371" + ], + "enters": [ + "doc_136d7a909d51", + "doc_5ee8bbdbd8a8" + ], + "entire": [ + "doc_0ac01bce8c79", + "doc_50ac650fd8f6", + "doc_745f751f1344", + "doc_9e9118ed2141", + "doc_a24b92541657", + "doc_c346f13ce597", + "doc_f12bbbc027a7", + "doc_fdf48d427fc0" + ], + "entirely": [ + "doc_a24b92541657", + "doc_b6088784caab", + "doc_c346f13ce597" + ], + "entries": [ + "doc_07174a8633ac", + "doc_464a2b4a8d35", + "doc_4a1cf8a90e83", + "doc_e7d508371e5c" + ], + "entry": [ + "doc_464a2b4a8d35", + "doc_64090a20f830", + "doc_745f751f1344", + "doc_a2e84472f1f9" + ], + "entrypoints": [ + "doc_2a18de009d56" + ], + "env": [ + "doc_a24b92541657", + "doc_d147a81bac29" + ], + "envelope": [ + "doc_618e34441fa7", + "doc_745f751f1344", + "doc_75887f91c7df" + ], + "envelopes": [ + "doc_1c5f37ea0ad3", + "doc_2a18de009d56", + "doc_75887f91c7df" + ], + "environment": [ + "doc_0383dbf277ba", + "doc_067033cfc945", + "doc_18d38fdacc9e", + "doc_1e46cbc6bfae", + "doc_567be1281e2f", + "doc_61fd9f682847", + "doc_64090a20f830", + "doc_78b1717fafe3", + "doc_a24b92541657", + "doc_b6088784caab", + "doc_f5f256549d84" + ], + "environment-based": [ + "doc_0383dbf277ba", + "doc_2a18de009d56" + ], + "environment-variables": [ + "doc_3aa0b3792d22" + ], + "environments": [ + "doc_067033cfc945", + "doc_1c5f37ea0ad3", + "doc_43ad71a4ad0c", + "doc_464a2b4a8d35" + ], + "ephemeral": [ + "doc_0383dbf277ba" + ], + "equivalent": [ + "doc_5a65ddab1445" + ], + "equivalents": [ + "doc_5a65ddab1445" + ], + "error": [ + "doc_067033cfc945", + "doc_07174a8633ac", + "doc_0ac01bce8c79", + "doc_109157c0d2d7", + "doc_136d7a909d51", + "doc_18d38fdacc9e", + "doc_1e46cbc6bfae", + "doc_2798948c3080", + "doc_2a18de009d56", + "doc_3eeb75d383b8", + "doc_4055659fe573", + "doc_4e1ebaf40267", + "doc_50ac650fd8f6", + "doc_567be1281e2f", + "doc_5ee8bbdbd8a8", + "doc_618e34441fa7", + "doc_745f751f1344", + "doc_75887f91c7df", + "doc_78a8125d2c7e", + "doc_78b1717fafe3", + "doc_9e9118ed2141", + "doc_a24b92541657", + "doc_a2e84472f1f9", + "doc_c346f13ce597", + "doc_e7d508371e5c", + "doc_eeed52120ccf", + "doc_f54217a0f2cf" + ], + "error_count": [ + "doc_78a8125d2c7e" + ], + "error_message": [ + "doc_745f751f1344" + ], + "errors": [ + "doc_067033cfc945", + "doc_136d7a909d51", + "doc_18d38fdacc9e", + "doc_2a18de009d56", + "doc_3ae3aaf0c12c", + "doc_4e1ebaf40267", + "doc_50ac650fd8f6", + "doc_567be1281e2f", + "doc_5ee8bbdbd8a8", + "doc_7320ab982fab", + "doc_745f751f1344", + "doc_78a8125d2c7e", + "doc_a24b92541657", + "doc_c93c115aeb85", + "doc_eeed52120ccf", + "doc_f2ae50c638a4" + ], + "escapes": [ + "doc_464a2b4a8d35" + ], + "especially": [ + "doc_43ad71a4ad0c" + ], + "essential": [ + "doc_57e2139f0873", + "doc_f12bbbc027a7", + "doc_f54217a0f2cf" + ], + "establishment": [ + "doc_4462f67b1c03" + ], + "estimated": [ + "doc_4055659fe573", + "doc_4a1cf8a90e83", + "doc_50ac650fd8f6", + "doc_78a8125d2c7e", + "doc_bbc97cbff254", + "doc_bf91f8d5da74", + "doc_fdf48d427fc0" + ], + "estimates": [ + "doc_1e46cbc6bfae", + "doc_fdf48d427fc0" + ], + "estimation": [ + "doc_5ee8bbdbd8a8" + ], + "etc": [ + "doc_2a18de009d56", + "doc_3f60413b3a06", + "doc_4a1cf8a90e83", + "doc_5ee8bbdbd8a8", + "doc_78a8125d2c7e", + "doc_a24b92541657", + "doc_c93c115aeb85", + "doc_f12bbbc027a7", + "doc_f1e32ed2ffce", + "doc_f54217a0f2cf" + ], + "eval": [ + "doc_18d38fdacc9e", + "doc_2798948c3080", + "doc_2a18de009d56", + "doc_4e1ebaf40267", + "doc_618e34441fa7", + "doc_bbc97cbff254", + "doc_c93c115aeb85", + "doc_f54217a0f2cf" + ], + "eval-driven": [ + "doc_3f60413b3a06" + ], + "evalassertion": [ + "doc_2a18de009d56", + "doc_c93c115aeb85" + ], + "evalassertionresult": [ + "doc_2a18de009d56" + ], + "evalbudget": [ + "doc_2a18de009d56", + "doc_bbc97cbff254", + "doc_c93c115aeb85" + ], + "evalcase": [ + "doc_2a18de009d56", + "doc_bbc97cbff254" + ], + "evalcaseresult": [ + "doc_2a18de009d56" + ], + "evals": [ + "doc_18d38fdacc9e", + "doc_2798948c3080", + "doc_2a18de009d56", + "doc_3aa0b3792d22", + "doc_3ae3aaf0c12c", + "doc_3eeb75d383b8", + "doc_3f60413b3a06", + "doc_4e1ebaf40267", + "doc_50ac650fd8f6", + "doc_57e2139f0873", + "doc_5a65ddab1445", + "doc_618e34441fa7", + "doc_bbc97cbff254", + "doc_c93c115aeb85", + "doc_d147a81bac29", + "doc_deb460ffa5ee", + "doc_f18f523b6ecc", + "doc_f2ae50c638a4" + ], + "evalscorer": [ + "doc_2a18de009d56" + ], + "evalsuiteconfig": [ + "doc_2a18de009d56" + ], + "evalsuiteresult": [ + "doc_2a18de009d56" + ], + "evaluate_budget": [ + "doc_2a18de009d56" + ], + "evaluated": [ + "doc_0ac01bce8c79", + "doc_5ee8bbdbd8a8" + ], + "evaluates": [ + "doc_0ac01bce8c79", + "doc_745f751f1344", + "doc_d6ad61a79371", + "doc_f54217a0f2cf" + ], + "evaluation": [ + "doc_0ac01bce8c79", + "doc_618e34441fa7" + ], + "even": [ + "doc_464a2b4a8d35", + "doc_50ac650fd8f6", + "doc_64090a20f830", + "doc_78a8125d2c7e", + "doc_f2ae50c638a4", + "doc_fdf48d427fc0" + ], + "event": [ + "doc_0383dbf277ba", + "doc_07174a8633ac", + "doc_0ac01bce8c79", + "doc_109157c0d2d7", + "doc_1e46cbc6bfae", + "doc_2a18de009d56", + "doc_3f60413b3a06", + "doc_43ad71a4ad0c", + "doc_4e1ebaf40267", + "doc_5ee8bbdbd8a8", + "doc_618e34441fa7", + "doc_745f751f1344", + "doc_75887f91c7df", + "doc_78a8125d2c7e", + "doc_7b26cbd9a7ae", + "doc_a24b92541657", + "doc_bbc97cbff254", + "doc_d6ad61a79371", + "doc_e7d508371e5c", + "doc_eeed52120ccf", + "doc_f18f523b6ecc", + "doc_f2ae50c638a4", + "doc_f54217a0f2cf" + ], + "event-level": [ + "doc_0ac01bce8c79" + ], + "event-specific": [ + "doc_0ac01bce8c79", + "doc_f54217a0f2cf" + ], + "event-type": [ + "doc_f54217a0f2cf" + ], + "event_type": [ + "doc_0ac01bce8c79", + "doc_f54217a0f2cf" + ], + "events": [ + "doc_0383dbf277ba", + "doc_07174a8633ac", + "doc_0ac01bce8c79", + "doc_109157c0d2d7", + "doc_136d7a909d51", + "doc_43ad71a4ad0c", + "doc_4e1ebaf40267", + "doc_5ee8bbdbd8a8", + "doc_618e34441fa7", + "doc_61fd9f682847", + "doc_745f751f1344", + "doc_a24b92541657", + "doc_a5120de838fb", + "doc_bbc97cbff254", + "doc_d147a81bac29", + "doc_e7d508371e5c", + "doc_eeed52120ccf", + "doc_f2ae50c638a4", + "doc_f54217a0f2cf", + "doc_fdf48d427fc0" + ], + "ever": [ + "doc_0ac01bce8c79", + "doc_5ee8bbdbd8a8" + ], + "every": [ + "doc_0ac01bce8c79", + "doc_18d38fdacc9e", + "doc_1e46cbc6bfae", + "doc_3ae3aaf0c12c", + "doc_3f60413b3a06", + "doc_4055659fe573", + "doc_43ad71a4ad0c", + "doc_464a2b4a8d35", + "doc_4a1cf8a90e83", + "doc_4e1ebaf40267", + "doc_50ac650fd8f6", + "doc_567be1281e2f", + "doc_5a65ddab1445", + "doc_618e34441fa7", + "doc_64090a20f830", + "doc_7320ab982fab", + "doc_745f751f1344", + "doc_75887f91c7df", + "doc_78a8125d2c7e", + "doc_78b1717fafe3", + "doc_9e9118ed2141", + "doc_a24b92541657", + "doc_a5120de838fb", + "doc_c346f13ce597", + "doc_e67e762fb6ab", + "doc_e7d508371e5c", + "doc_e8794369a1a4", + "doc_f12bbbc027a7", + "doc_f54217a0f2cf", + "doc_f5f256549d84", + "doc_fdf48d427fc0" + ], + "everything": [ + "doc_43ad71a4ad0c", + "doc_4a1cf8a90e83", + "doc_e67e762fb6ab" + ], + "evidence": [ + "doc_18d38fdacc9e", + "doc_50ac650fd8f6", + "doc_e67e762fb6ab" + ], + "exact": [ + "doc_07174a8633ac", + "doc_3f60413b3a06" + ], + "exactly": [ + "doc_0ac01bce8c79", + "doc_50ac650fd8f6", + "doc_75887f91c7df", + "doc_e34fe24f9585", + "doc_e67e762fb6ab", + "doc_f54217a0f2cf" + ], + "example": [ + "doc_109157c0d2d7", + "doc_1e46cbc6bfae", + "doc_2798948c3080", + "doc_2a18de009d56", + "doc_3f60413b3a06", + "doc_4055659fe573", + "doc_4462f67b1c03", + "doc_4a1cf8a90e83", + "doc_61fd9f682847", + "doc_745f751f1344", + "doc_78b1717fafe3", + "doc_7b26cbd9a7ae", + "doc_9e9118ed2141", + "doc_a2e84472f1f9", + "doc_a5120de838fb", + "doc_d6ad61a79371", + "doc_e7d508371e5c", + "doc_eeed52120ccf", + "doc_f12bbbc027a7", + "doc_f92685a34f7e" + ], + "examples": [ + "doc_3aa0b3792d22", + "doc_3ae3aaf0c12c", + "doc_3eeb75d383b8", + "doc_5a65ddab1445", + "doc_a5120de838fb", + "doc_b6088784caab", + "doc_bbc97cbff254", + "doc_d147a81bac29", + "doc_deb460ffa5ee", + "doc_e67e762fb6ab", + "doc_f18f523b6ecc" + ], + "exceed": [ + "doc_07174a8633ac" + ], + "exceeded": [ + "doc_4055659fe573" + ], + "exceeding": [ + "doc_7320ab982fab" + ], + "exceeds": [ + "doc_0383dbf277ba", + "doc_2798948c3080", + "doc_50ac650fd8f6", + "doc_745f751f1344", + "doc_fdf48d427fc0" + ], + "exception": [ + "doc_4055659fe573", + "doc_618e34441fa7", + "doc_745f751f1344", + "doc_a24b92541657" + ], + "exceptions": [ + "doc_618e34441fa7", + "doc_745f751f1344", + "doc_a24b92541657" + ], + "excessive": [ + "doc_464a2b4a8d35", + "doc_50ac650fd8f6" + ], + "exchange": [ + "doc_75887f91c7df" + ], + "execute": [ + "doc_0ac01bce8c79", + "doc_109157c0d2d7", + "doc_1e46cbc6bfae", + "doc_43ad71a4ad0c", + "doc_50ac650fd8f6", + "doc_57e2139f0873", + "doc_745f751f1344", + "doc_9e9118ed2141", + "doc_a24b92541657", + "doc_a2e84472f1f9", + "doc_b6088784caab", + "doc_d147a81bac29", + "doc_e67e762fb6ab", + "doc_f12bbbc027a7", + "doc_f18f523b6ecc" + ], + "executed": [ + "doc_4a1cf8a90e83", + "doc_5ee8bbdbd8a8", + "doc_745f751f1344", + "doc_e67e762fb6ab", + "doc_e7d508371e5c", + "doc_f54217a0f2cf" + ], + "executes": [ + "doc_0ac01bce8c79", + "doc_1c5f37ea0ad3", + "doc_1e46cbc6bfae", + "doc_43ad71a4ad0c", + "doc_618e34441fa7", + "doc_745f751f1344", + "doc_b6088784caab", + "doc_bf91f8d5da74", + "doc_c346f13ce597", + "doc_d6ad61a79371" + ], + "executing": [ + "doc_78b1717fafe3", + "doc_d6ad61a79371", + "doc_f54217a0f2cf" + ], + "execution": [ + "doc_07174a8633ac", + "doc_0ac01bce8c79", + "doc_18d38fdacc9e", + "doc_1e46cbc6bfae", + "doc_2a18de009d56", + "doc_3f1383f72c5f", + "doc_3f60413b3a06", + "doc_43ad71a4ad0c", + "doc_464a2b4a8d35", + "doc_4a1cf8a90e83", + "doc_4e1ebaf40267", + "doc_50ac650fd8f6", + "doc_567be1281e2f", + "doc_57e2139f0873", + "doc_5a65ddab1445", + "doc_5ee8bbdbd8a8", + "doc_618e34441fa7", + "doc_61fd9f682847", + "doc_745f751f1344", + "doc_78a8125d2c7e", + "doc_78b1717fafe3", + "doc_9e9118ed2141", + "doc_a24b92541657", + "doc_a2e84472f1f9", + "doc_a5120de838fb", + "doc_b6088784caab", + "doc_bbc97cbff254", + "doc_bf91f8d5da74", + "doc_c346f13ce597", + "doc_d147a81bac29", + "doc_d6ad61a79371", + "doc_deb460ffa5ee", + "doc_e67e762fb6ab", + "doc_e7d508371e5c", + "doc_e8794369a1a4", + "doc_eeed52120ccf", + "doc_f12bbbc027a7", + "doc_f18f523b6ecc", + "doc_f2ae50c638a4", + "doc_f54217a0f2cf", + "doc_f5f256549d84", + "doc_fdf48d427fc0" + ], + "executions": [ + "doc_07174a8633ac", + "doc_5ee8bbdbd8a8", + "doc_745f751f1344", + "doc_78a8125d2c7e", + "doc_c346f13ce597", + "doc_e67e762fb6ab", + "doc_f12bbbc027a7" + ], + "executor": [ + "doc_2a18de009d56" + ], + "exercise": [ + "doc_3eeb75d383b8", + "doc_bbc97cbff254" + ], + "exhaust": [ + "doc_d1f7d83e0824" + ], + "exhausted": [ + "doc_75887f91c7df", + "doc_78b1717fafe3", + "doc_eeed52120ccf" + ], + "exhaustion": [ + "doc_f12bbbc027a7" + ], + "exhausts": [ + "doc_78b1717fafe3" + ], + "exhibits": [ + "doc_50ac650fd8f6" + ], + "exist": [ + "doc_2798948c3080", + "doc_4055659fe573", + "doc_4a1cf8a90e83", + "doc_f12bbbc027a7" + ], + "existing": [ + "doc_5a65ddab1445", + "doc_618e34441fa7", + "doc_78a8125d2c7e", + "doc_bbc97cbff254", + "doc_d147a81bac29", + "doc_f54217a0f2cf" + ], + "exists": [ + "doc_07174a8633ac", + "doc_2798948c3080", + "doc_4a1cf8a90e83" + ], + "exits": [ + "doc_0ac01bce8c79", + "doc_a24b92541657" + ], + "exotic": [ + "doc_2a18de009d56" + ], + "expands": [ + "doc_f2ae50c638a4" + ], + "expectations": [ + "doc_2798948c3080" + ], + "expected": [ + "doc_136d7a909d51", + "doc_1e46cbc6bfae", + "doc_3ae3aaf0c12c", + "doc_618e34441fa7" + ], + "expensive": [ + "doc_136d7a909d51", + "doc_d1f7d83e0824", + "doc_fdf48d427fc0" + ], + "experiments": [ + "doc_f5f256549d84" + ], + "expertise": [ + "doc_57e2139f0873", + "doc_a2e84472f1f9", + "doc_e67e762fb6ab", + "doc_f12bbbc027a7" + ], + "expire": [ + "doc_4a1cf8a90e83" + ], + "expired": [ + "doc_109157c0d2d7", + "doc_f54217a0f2cf" + ], + "expires": [ + "doc_5ee8bbdbd8a8", + "doc_a24b92541657", + "doc_d6ad61a79371" + ], + "explain": [ + "doc_618e34441fa7", + "doc_d147a81bac29", + "doc_f18f523b6ecc" + ], + "explains": [ + "doc_3eeb75d383b8", + "doc_5ee8bbdbd8a8", + "doc_eeed52120ccf", + "doc_f54217a0f2cf" + ], + "explanation": [ + "doc_1e46cbc6bfae" + ], + "explicit": [ + "doc_0383dbf277ba", + "doc_3eeb75d383b8", + "doc_464a2b4a8d35", + "doc_567be1281e2f", + "doc_5a65ddab1445", + "doc_61fd9f682847", + "doc_64090a20f830", + "doc_b6088784caab", + "doc_f18f523b6ecc", + "doc_f2ae50c638a4", + "doc_f54217a0f2cf", + "doc_f5f256549d84" + ], + "explicitly": [ + "doc_18d38fdacc9e", + "doc_43ad71a4ad0c", + "doc_567be1281e2f", + "doc_a24b92541657", + "doc_b6088784caab" + ], + "exploration": [ + "doc_9e9118ed2141" + ], + "explosion": [ + "doc_567be1281e2f" + ], + "exponential": [ + "doc_4055659fe573", + "doc_5ee8bbdbd8a8", + "doc_61fd9f682847", + "doc_7320ab982fab", + "doc_75887f91c7df" + ], + "export": [ + "doc_067033cfc945", + "doc_2a18de009d56", + "doc_3ae3aaf0c12c", + "doc_3eeb75d383b8", + "doc_4055659fe573", + "doc_745f751f1344", + "doc_78a8125d2c7e", + "doc_78b1717fafe3", + "doc_f18f523b6ecc", + "doc_f5f256549d84", + "doc_fdf48d427fc0" + ], + "exported": [ + "doc_3eeb75d383b8", + "doc_4e1ebaf40267", + "doc_5ee8bbdbd8a8" + ], + "exporter": [ + "doc_18d38fdacc9e", + "doc_4055659fe573", + "doc_4e1ebaf40267", + "doc_50ac650fd8f6", + "doc_567be1281e2f", + "doc_78a8125d2c7e" + ], + "exporters": [ + "doc_2a18de009d56", + "doc_4e1ebaf40267", + "doc_d147a81bac29" + ], + "exports": [ + "doc_2a18de009d56", + "doc_3eeb75d383b8", + "doc_4e1ebaf40267", + "doc_bbc97cbff254", + "doc_d147a81bac29", + "doc_f18f523b6ecc" + ], + "expose": [ + "doc_1c5f37ea0ad3", + "doc_40e30666bfa8", + "doc_57e2139f0873", + "doc_618e34441fa7", + "doc_745f751f1344", + "doc_78b1717fafe3", + "doc_a24b92541657", + "doc_bbc97cbff254", + "doc_e34fe24f9585", + "doc_e67e762fb6ab", + "doc_f92685a34f7e" + ], + "exposed": [ + "doc_745f751f1344", + "doc_a24b92541657" + ], + "exposes": [ + "doc_745f751f1344", + "doc_e34fe24f9585" + ], + "exposing": [ + "doc_e34fe24f9585" + ], + "express": [ + "doc_9e9118ed2141" + ], + "extend": [ + "doc_b6088784caab", + "doc_f54217a0f2cf" + ], + "extended": [ + "doc_2a18de009d56", + "doc_e67e762fb6ab" + ], + "extension": [ + "doc_2a18de009d56", + "doc_745f751f1344", + "doc_9e9118ed2141" + ], + "extensive": [ + "doc_07174a8633ac" + ], + "external": [ + "doc_0ac01bce8c79", + "doc_1c5f37ea0ad3", + "doc_1e46cbc6bfae", + "doc_3f1383f72c5f", + "doc_40e30666bfa8", + "doc_43ad71a4ad0c", + "doc_4e1ebaf40267", + "doc_567be1281e2f", + "doc_57e2139f0873", + "doc_9e9118ed2141", + "doc_a24b92541657", + "doc_a5120de838fb", + "doc_d6ad61a79371", + "doc_e34fe24f9585", + "doc_e67e762fb6ab", + "doc_e7d508371e5c", + "doc_f18f523b6ecc", + "doc_f5f256549d84" + ], + "extraction": [ + "doc_3f60413b3a06" + ], + "extracts": [ + "doc_1c5f37ea0ad3", + "doc_5ee8bbdbd8a8", + "doc_745f751f1344" + ], + "extras": [ + "doc_745f751f1344" + ], + "fact-checking": [ + "doc_f12bbbc027a7" + ], + "factory": [ + "doc_2a18de009d56", + "doc_464a2b4a8d35" + ], + "fail": [ + "doc_0ac01bce8c79", + "doc_109157c0d2d7", + "doc_4055659fe573", + "doc_4e1ebaf40267", + "doc_57e2139f0873", + "doc_5ee8bbdbd8a8", + "doc_745f751f1344", + "doc_a24b92541657", + "doc_c93c115aeb85", + "doc_d1f7d83e0824", + "doc_eeed52120ccf", + "doc_f12bbbc027a7" + ], + "fail-safe": [ + "doc_4e1ebaf40267", + "doc_bbc97cbff254", + "doc_f18f523b6ecc", + "doc_f5f256549d84" + ], + "fail_fast": [ + "doc_a24b92541657" + ], + "fail_run": [ + "doc_a24b92541657" + ], + "fail_safe": [ + "doc_0ac01bce8c79", + "doc_745f751f1344", + "doc_a24b92541657", + "doc_e67e762fb6ab" + ], + "failed": [ + "doc_067033cfc945", + "doc_0ac01bce8c79", + "doc_109157c0d2d7", + "doc_1e46cbc6bfae", + "doc_4055659fe573", + "doc_5ee8bbdbd8a8", + "doc_745f751f1344", + "doc_78b1717fafe3", + "doc_a24b92541657", + "doc_a2e84472f1f9", + "doc_bf91f8d5da74", + "doc_e7d508371e5c", + "doc_f54217a0f2cf" + ], + "failing": [ + "doc_4055659fe573", + "doc_7320ab982fab" + ], + "fails": [ + "doc_0383dbf277ba", + "doc_4055659fe573", + "doc_4a1cf8a90e83", + "doc_4e1ebaf40267", + "doc_5ee8bbdbd8a8", + "doc_618e34441fa7", + "doc_7320ab982fab", + "doc_745f751f1344", + "doc_75887f91c7df", + "doc_a24b92541657", + "doc_c346f13ce597", + "doc_d1f7d83e0824", + "doc_f12bbbc027a7" + ], + "failsafeconfig": [ + "doc_067033cfc945", + "doc_18d38fdacc9e", + "doc_1c5f37ea0ad3", + "doc_2a18de009d56", + "doc_3ae3aaf0c12c", + "doc_43ad71a4ad0c", + "doc_50ac650fd8f6", + "doc_57e2139f0873", + "doc_5ee8bbdbd8a8", + "doc_a24b92541657", + "doc_a2e84472f1f9", + "doc_a5120de838fb", + "doc_bbc97cbff254", + "doc_d147a81bac29", + "doc_d1f7d83e0824", + "doc_e67e762fb6ab", + "doc_fdf48d427fc0" + ], + "failure": [ + "doc_0383dbf277ba", + "doc_0ac01bce8c79", + "doc_2a18de009d56", + "doc_3ae3aaf0c12c", + "doc_4055659fe573", + "doc_4a1cf8a90e83", + "doc_4e1ebaf40267", + "doc_50ac650fd8f6", + "doc_5ee8bbdbd8a8", + "doc_618e34441fa7", + "doc_745f751f1344", + "doc_78a8125d2c7e", + "doc_78b1717fafe3", + "doc_a24b92541657", + "doc_a2e84472f1f9", + "doc_d147a81bac29", + "doc_d1f7d83e0824", + "doc_e67e762fb6ab", + "doc_f12bbbc027a7", + "doc_f54217a0f2cf", + "doc_fdf48d427fc0" + ], + "failure-policy-matrix": [ + "doc_3aa0b3792d22" + ], + "failure_count": [ + "doc_0ac01bce8c79" + ], + "failurepolicy": [ + "doc_a24b92541657" + ], + "failures": [ + "doc_067033cfc945", + "doc_07174a8633ac", + "doc_109157c0d2d7", + "doc_18d38fdacc9e", + "doc_3ae3aaf0c12c", + "doc_4055659fe573", + "doc_4e1ebaf40267", + "doc_50ac650fd8f6", + "doc_5ee8bbdbd8a8", + "doc_618e34441fa7", + "doc_61fd9f682847", + "doc_7320ab982fab", + "doc_745f751f1344", + "doc_d1f7d83e0824", + "doc_e7d508371e5c", + "doc_f12bbbc027a7", + "doc_f5f256549d84" + ], + "fallback": [ + "doc_109157c0d2d7", + "doc_2798948c3080", + "doc_3f60413b3a06", + "doc_4055659fe573", + "doc_43ad71a4ad0c", + "doc_4a1cf8a90e83", + "doc_5ee8bbdbd8a8", + "doc_61fd9f682847", + "doc_7320ab982fab", + "doc_a24b92541657", + "doc_a5120de838fb", + "doc_d1f7d83e0824", + "doc_deb460ffa5ee", + "doc_eeed52120ccf", + "doc_f54217a0f2cf" + ], + "fallback_model_chain": [ + "doc_5ee8bbdbd8a8", + "doc_a24b92541657", + "doc_d1f7d83e0824" + ], + "falls": [ + "doc_0383dbf277ba", + "doc_61fd9f682847", + "doc_a24b92541657", + "doc_d1f7d83e0824" + ], + "false": [ + "doc_0ac01bce8c79", + "doc_109157c0d2d7", + "doc_3aa0b3792d22", + "doc_4462f67b1c03", + "doc_618e34441fa7", + "doc_61fd9f682847", + "doc_745f751f1344", + "doc_a24b92541657" + ], + "families": [ + "doc_f92685a34f7e" + ], + "fan-out": [ + "doc_57e2139f0873", + "doc_f12bbbc027a7" + ], + "fanout": [ + "doc_a24b92541657" + ], + "fans": [ + "doc_f12bbbc027a7" + ], + "far": [ + "doc_4a1cf8a90e83" + ], + "fast": [ + "doc_0383dbf277ba", + "doc_18d38fdacc9e", + "doc_5ee8bbdbd8a8", + "doc_75887f91c7df", + "doc_a24b92541657", + "doc_eeed52120ccf" + ], + "faster": [ + "doc_3f60413b3a06", + "doc_7320ab982fab", + "doc_bbc97cbff254" + ], + "fastest": [ + "doc_1e46cbc6bfae" + ], + "fault": [ + "doc_4a1cf8a90e83" + ], + "favicon": [ + "doc_3aa0b3792d22" + ], + "fb67e": [ + "doc_3aa0b3792d22" + ], + "feature": [ + "doc_5a65ddab1445", + "doc_618e34441fa7", + "doc_b6088784caab", + "doc_e34fe24f9585", + "doc_f1e32ed2ffce" + ], + "features": [ + "doc_0383dbf277ba", + "doc_18d38fdacc9e", + "doc_57e2139f0873" + ], + "fed": [ + "doc_0ac01bce8c79", + "doc_745f751f1344", + "doc_a24b92541657", + "doc_e7d508371e5c" + ], + "federated": [ + "doc_57e2139f0873" + ], + "feedback": [ + "doc_0ac01bce8c79" + ], + "feeding": [ + "doc_a2e84472f1f9" + ], + "feeds": [ + "doc_618e34441fa7", + "doc_745f751f1344", + "doc_bf91f8d5da74" + ], + "fetch": [ + "doc_4a1cf8a90e83" + ], + "few": [ + "doc_2a18de009d56", + "doc_a24b92541657" + ], + "fewer": [ + "doc_f12bbbc027a7" + ], + "field": [ + "doc_0ac01bce8c79", + "doc_1e46cbc6bfae", + "doc_4a1cf8a90e83", + "doc_5ee8bbdbd8a8", + "doc_618e34441fa7", + "doc_64090a20f830", + "doc_745f751f1344", + "doc_75887f91c7df", + "doc_a24b92541657", + "doc_a2e84472f1f9", + "doc_bbc97cbff254", + "doc_bf91f8d5da74", + "doc_d147a81bac29", + "doc_e67e762fb6ab", + "doc_e7d508371e5c", + "doc_e8794369a1a4", + "doc_f54217a0f2cf", + "doc_fdf48d427fc0" + ], + "field-by-field": [ + "doc_bbc97cbff254" + ], + "field-level": [ + "doc_2a18de009d56" + ], + "fields": [ + "doc_0ac01bce8c79", + "doc_109157c0d2d7", + "doc_3ae3aaf0c12c", + "doc_3f60413b3a06", + "doc_464a2b4a8d35", + "doc_4a1cf8a90e83", + "doc_5ee8bbdbd8a8", + "doc_618e34441fa7", + "doc_61fd9f682847", + "doc_64090a20f830", + "doc_a24b92541657", + "doc_bbc97cbff254", + "doc_bf91f8d5da74", + "doc_d147a81bac29", + "doc_d1f7d83e0824", + "doc_deb460ffa5ee", + "doc_e67e762fb6ab", + "doc_e7d508371e5c", + "doc_f2ae50c638a4", + "doc_f54217a0f2cf" + ], + "file": [ + "doc_067033cfc945", + "doc_2798948c3080", + "doc_2a18de009d56", + "doc_464a2b4a8d35", + "doc_5a65ddab1445", + "doc_61fd9f682847", + "doc_64090a20f830", + "doc_a24b92541657", + "doc_a5120de838fb", + "doc_b6088784caab", + "doc_e67e762fb6ab", + "doc_f2ae50c638a4" + ], + "file-based": [ + "doc_64090a20f830" + ], + "fileaccesserror": [ + "doc_464a2b4a8d35" + ], + "filename": [ + "doc_64090a20f830" + ], + "files": [ + "doc_2798948c3080", + "doc_3ae3aaf0c12c", + "doc_3eeb75d383b8", + "doc_464a2b4a8d35", + "doc_618e34441fa7", + "doc_61fd9f682847", + "doc_64090a20f830", + "doc_9e9118ed2141", + "doc_a24b92541657", + "doc_a5120de838fb", + "doc_b6088784caab", + "doc_d147a81bac29", + "doc_e67e762fb6ab" + ], + "filesystem": [ + "doc_43ad71a4ad0c", + "doc_464a2b4a8d35", + "doc_9e9118ed2141" + ], + "fill": [ + "doc_0383dbf277ba" + ], + "filter": [ + "doc_75887f91c7df" + ], + "final": [ + "doc_109157c0d2d7", + "doc_1e46cbc6bfae", + "doc_3f60413b3a06", + "doc_4a1cf8a90e83", + "doc_5ee8bbdbd8a8", + "doc_618e34441fa7", + "doc_a2e84472f1f9", + "doc_bbc97cbff254", + "doc_bf91f8d5da74", + "doc_deb460ffa5ee", + "doc_e7d508371e5c", + "doc_eeed52120ccf", + "doc_f12bbbc027a7", + "doc_f18f523b6ecc", + "doc_f2ae50c638a4", + "doc_f5f256549d84" + ], + "final_structured": [ + "doc_4a1cf8a90e83" + ], + "final_text": [ + "doc_1e46cbc6bfae", + "doc_4a1cf8a90e83", + "doc_5ee8bbdbd8a8", + "doc_a2e84472f1f9", + "doc_a5120de838fb", + "doc_bbc97cbff254", + "doc_bf91f8d5da74", + "doc_e67e762fb6ab", + "doc_e7d508371e5c" + ], + "finalize": [ + "doc_3f1383f72c5f" + ], + "find": [ + "doc_0383dbf277ba", + "doc_136d7a909d51", + "doc_2a18de009d56", + "doc_a24b92541657", + "doc_bf91f8d5da74", + "doc_deb460ffa5ee" + ], + "finds": [ + "doc_3eeb75d383b8" + ], + "finish_reason": [ + "doc_4a1cf8a90e83", + "doc_5ee8bbdbd8a8", + "doc_e8794369a1a4", + "doc_f54217a0f2cf" + ], + "finished": [ + "doc_109157c0d2d7", + "doc_78b1717fafe3", + "doc_e7d508371e5c" + ], + "finishes": [ + "doc_f54217a0f2cf" + ], + "fire": [ + "doc_618e34441fa7" + ], + "fires": [ + "doc_0ac01bce8c79", + "doc_109157c0d2d7", + "doc_f54217a0f2cf" + ], + "first": [ + "doc_0383dbf277ba", + "doc_067033cfc945", + "doc_2798948c3080", + "doc_4a1cf8a90e83", + "doc_4e1ebaf40267", + "doc_5a65ddab1445", + "doc_618e34441fa7", + "doc_61fd9f682847", + "doc_745f751f1344", + "doc_78a8125d2c7e", + "doc_9e9118ed2141", + "doc_a5120de838fb", + "doc_c346f13ce597", + "doc_c93c115aeb85", + "doc_deb460ffa5ee", + "doc_e67e762fb6ab", + "doc_f12bbbc027a7", + "doc_f5f256549d84" + ], + "first_success": [ + "doc_f12bbbc027a7" + ], + "first_token_ms": [ + "doc_78a8125d2c7e" + ], + "fit": [ + "doc_f18f523b6ecc" + ], + "fits": [ + "doc_deb460ffa5ee" + ], + "five": [ + "doc_57e2139f0873" + ], + "fix": [ + "doc_18d38fdacc9e", + "doc_3f1383f72c5f", + "doc_618e34441fa7", + "doc_a24b92541657" + ], + "fixing": [ + "doc_618e34441fa7" + ], + "flag": [ + "doc_745f751f1344", + "doc_a24b92541657" + ], + "flags": [ + "doc_464a2b4a8d35" + ], + "float": [ + "doc_4a1cf8a90e83", + "doc_5ee8bbdbd8a8", + "doc_745f751f1344", + "doc_78a8125d2c7e", + "doc_a24b92541657", + "doc_a2e84472f1f9", + "doc_e7d508371e5c", + "doc_e8794369a1a4" + ], + "floor": [ + "doc_a24b92541657" + ], + "flow": [ + "doc_0ac01bce8c79", + "doc_1c5f37ea0ad3", + "doc_3f1383f72c5f", + "doc_4055659fe573", + "doc_50ac650fd8f6", + "doc_567be1281e2f", + "doc_5ee8bbdbd8a8", + "doc_618e34441fa7", + "doc_a2e84472f1f9", + "doc_c346f13ce597", + "doc_d147a81bac29", + "doc_d1f7d83e0824", + "doc_d6ad61a79371", + "doc_e8794369a1a4", + "doc_fdf48d427fc0" + ], + "flows": [ + "doc_0ac01bce8c79", + "doc_3ae3aaf0c12c", + "doc_50ac650fd8f6", + "doc_745f751f1344", + "doc_a5120de838fb", + "doc_e8794369a1a4", + "doc_f92685a34f7e" + ], + "fluent": [ + "doc_2a18de009d56", + "doc_3f60413b3a06" + ], + "flush": [ + "doc_4a1cf8a90e83", + "doc_a24b92541657" + ], + "flushed": [ + "doc_4a1cf8a90e83" + ], + "focused": [ + "doc_18d38fdacc9e", + "doc_2a18de009d56", + "doc_618e34441fa7", + "doc_b6088784caab", + "doc_d147a81bac29", + "doc_e67e762fb6ab" + ], + "follow": [ + "doc_43ad71a4ad0c", + "doc_50ac650fd8f6", + "doc_618e34441fa7", + "doc_c346f13ce597" + ], + "following": [ + "doc_64090a20f830", + "doc_f54217a0f2cf" + ], + "follows": [ + "doc_07174a8633ac", + "doc_4a1cf8a90e83", + "doc_e7d508371e5c" + ], + "footer": [ + "doc_3aa0b3792d22" + ], + "forge": [ + "doc_2a18de009d56" + ], + "form": [ + "doc_f54217a0f2cf" + ], + "format": [ + "doc_18d38fdacc9e", + "doc_2798948c3080", + "doc_745f751f1344", + "doc_b6088784caab", + "doc_e8794369a1a4" + ], + "formats": [ + "doc_4a1cf8a90e83" + ], + "formatted": [ + "doc_9e9118ed2141" + ], + "formatters": [ + "doc_2a18de009d56" + ], + "forward": [ + "doc_f54217a0f2cf" + ], + "forwarded": [ + "doc_a24b92541657" + ], + "forwards": [ + "doc_109157c0d2d7" + ], + "found": [ + "doc_07174a8633ac", + "doc_136d7a909d51" + ], + "foundation": [ + "doc_1e46cbc6bfae" + ], + "four": [ + "doc_0383dbf277ba", + "doc_567be1281e2f", + "doc_9e9118ed2141", + "doc_f5f256549d84" + ], + "framework": [ + "doc_0383dbf277ba", + "doc_2a18de009d56", + "doc_d147a81bac29" + ], + "frameworks": [ + "doc_5a65ddab1445" + ], + "freeform": [ + "doc_43ad71a4ad0c", + "doc_d6ad61a79371" + ], + "fresh": [ + "doc_0383dbf277ba", + "doc_4a1cf8a90e83" + ], + "from_env": [ + "doc_61fd9f682847" + ], + "frontmatter": [ + "doc_b6088784caab" + ], + "full": [ + "doc_0383dbf277ba", + "doc_07174a8633ac", + "doc_109157c0d2d7", + "doc_136d7a909d51", + "doc_1e46cbc6bfae", + "doc_2a18de009d56", + "doc_3f60413b3a06", + "doc_40e30666bfa8", + "doc_4462f67b1c03", + "doc_464a2b4a8d35", + "doc_4a1cf8a90e83", + "doc_5a65ddab1445", + "doc_618e34441fa7", + "doc_61fd9f682847", + "doc_64090a20f830", + "doc_745f751f1344", + "doc_75887f91c7df", + "doc_7b26cbd9a7ae", + "doc_bbc97cbff254", + "doc_c346f13ce597", + "doc_d1f7d83e0824", + "doc_e34fe24f9585", + "doc_e67e762fb6ab", + "doc_eeed52120ccf", + "doc_f12bbbc027a7", + "doc_fdf48d427fc0" + ], + "full-module-reference": [ + "doc_3aa0b3792d22" + ], + "function": [ + "doc_136d7a909d51", + "doc_2a18de009d56", + "doc_5a65ddab1445", + "doc_5ee8bbdbd8a8", + "doc_745f751f1344", + "doc_78b1717fafe3", + "doc_9e9118ed2141", + "doc_a24b92541657", + "doc_b6088784caab", + "doc_bf91f8d5da74", + "doc_c346f13ce597", + "doc_e67e762fb6ab", + "doc_eeed52120ccf", + "doc_f2ae50c638a4" + ], + "function-calling": [ + "doc_745f751f1344" + ], + "functions": [ + "doc_18d38fdacc9e", + "doc_2a18de009d56", + "doc_57e2139f0873", + "doc_5ee8bbdbd8a8", + "doc_a24b92541657", + "doc_b6088784caab", + "doc_bbc97cbff254", + "doc_bf91f8d5da74", + "doc_e67e762fb6ab" + ], + "fundamentally": [ + "doc_43ad71a4ad0c", + "doc_a24b92541657" + ], + "further": [ + "doc_464a2b4a8d35" + ], + "future": [ + "doc_f18f523b6ecc", + "doc_f54217a0f2cf" + ], + "gate": [ + "doc_0ac01bce8c79", + "doc_18d38fdacc9e", + "doc_43ad71a4ad0c", + "doc_50ac650fd8f6", + "doc_567be1281e2f", + "doc_745f751f1344", + "doc_9e9118ed2141", + "doc_a5120de838fb", + "doc_b6088784caab", + "doc_c93c115aeb85" + ], + "gated": [ + "doc_3ae3aaf0c12c", + "doc_9e9118ed2141", + "doc_b6088784caab" + ], + "gates": [ + "doc_3ae3aaf0c12c", + "doc_40e30666bfa8", + "doc_43ad71a4ad0c", + "doc_464a2b4a8d35", + "doc_50ac650fd8f6", + "doc_745f751f1344", + "doc_9e9118ed2141", + "doc_a5120de838fb", + "doc_b6088784caab", + "doc_e34fe24f9585", + "doc_e67e762fb6ab" + ], + "gating": [ + "doc_c93c115aeb85", + "doc_e67e762fb6ab" + ], + "gemini": [ + "doc_f1e32ed2ffce" + ], + "general": [ + "doc_d1f7d83e0824", + "doc_eeed52120ccf", + "doc_f92685a34f7e" + ], + "generally": [ + "doc_43ad71a4ad0c" + ], + "generate": [ + "doc_18d38fdacc9e", + "doc_9e9118ed2141" + ], + "generated": [ + "doc_3eeb75d383b8", + "doc_4a1cf8a90e83", + "doc_75887f91c7df", + "doc_7b26cbd9a7ae", + "doc_bbc97cbff254", + "doc_d147a81bac29", + "doc_e8794369a1a4", + "doc_eeed52120ccf", + "doc_f18f523b6ecc" + ], + "generates": [ + "doc_9e9118ed2141", + "doc_e67e762fb6ab" + ], + "generating": [ + "doc_5ee8bbdbd8a8" + ], + "generation": [ + "doc_57e2139f0873", + "doc_745f751f1344", + "doc_9e9118ed2141", + "doc_d1f7d83e0824", + "doc_e8794369a1a4", + "doc_eeed52120ccf", + "doc_f18f523b6ecc", + "doc_f1e32ed2ffce" + ], + "generic": [ + "doc_78b1717fafe3" + ], + "generous": [ + "doc_fdf48d427fc0" + ], + "genuinely": [ + "doc_3ae3aaf0c12c" + ], + "get": [ + "doc_1e46cbc6bfae", + "doc_3f60413b3a06", + "doc_4e1ebaf40267", + "doc_a5120de838fb", + "doc_e7d508371e5c", + "doc_e8794369a1a4" + ], + "get_redis_pool": [ + "doc_78b1717fafe3" + ], + "get_resource": [ + "doc_43ad71a4ad0c" + ], + "get_state": [ + "doc_4a1cf8a90e83" + ], + "gets": [ + "doc_0383dbf277ba", + "doc_18d38fdacc9e", + "doc_4a1cf8a90e83", + "doc_745f751f1344" + ], + "getting": [ + "doc_136d7a909d51" + ], + "giant": [ + "doc_18d38fdacc9e", + "doc_b6088784caab" + ], + "git": [ + "doc_2798948c3080", + "doc_b6088784caab" + ], + "github": [ + "doc_136d7a909d51", + "doc_3aa0b3792d22" + ], + "give": [ + "doc_18d38fdacc9e" + ], + "given": [ + "doc_07174a8633ac", + "doc_4a1cf8a90e83" + ], + "gives": [ + "doc_2798948c3080", + "doc_78a8125d2c7e" + ], + "glitches": [ + "doc_a24b92541657" + ], + "global": [ + "doc_9e9118ed2141", + "doc_a24b92541657" + ], + "go": [ + "doc_40e30666bfa8", + "doc_a5120de838fb" + ], + "goal": [ + "doc_7320ab982fab" + ], + "goes": [ + "doc_5ee8bbdbd8a8" + ], + "going": [ + "doc_bf91f8d5da74" + ], + "golden": [ + "doc_18d38fdacc9e", + "doc_2a18de009d56", + "doc_3ae3aaf0c12c" + ], + "good": [ + "doc_0383dbf277ba", + "doc_57e2139f0873", + "doc_b6088784caab", + "doc_eeed52120ccf", + "doc_f18f523b6ecc" + ], + "google": [ + "doc_1c5f37ea0ad3", + "doc_2a18de009d56" + ], + "gpt-4": [ + "doc_1e46cbc6bfae", + "doc_5ee8bbdbd8a8", + "doc_61fd9f682847", + "doc_d1f7d83e0824", + "doc_eeed52120ccf", + "doc_f1e32ed2ffce", + "doc_f92685a34f7e" + ], + "grace": [ + "doc_a24b92541657" + ], + "graceful": [ + "doc_a5120de838fb", + "doc_c93c115aeb85" + ], + "gracefully": [ + "doc_f54217a0f2cf" + ], + "gradient": [ + "doc_3aa0b3792d22" + ], + "grafana": [ + "doc_78a8125d2c7e" + ], + "graph": [ + "doc_2a18de009d56" + ], + "graphs": [ + "doc_f12bbbc027a7" + ], + "group": [ + "doc_3aa0b3792d22", + "doc_75887f91c7df" + ], + "groups": [ + "doc_3aa0b3792d22", + "doc_75887f91c7df" + ], + "grow": [ + "doc_0383dbf277ba", + "doc_f5f256549d84" + ], + "grown": [ + "doc_07174a8633ac" + ], + "growth": [ + "doc_618e34441fa7", + "doc_a5120de838fb" + ], + "grpc": [ + "doc_1c5f37ea0ad3" + ], + "guarantee": [ + "doc_618e34441fa7" + ], + "guaranteed": [ + "doc_3f60413b3a06", + "doc_618e34441fa7" + ], + "guarantees": [ + "doc_4a1cf8a90e83", + "doc_d147a81bac29" + ], + "guards": [ + "doc_464a2b4a8d35" + ], + "guidance": [ + "doc_618e34441fa7", + "doc_d147a81bac29", + "doc_deb460ffa5ee", + "doc_f5f256549d84" + ], + "guide": [ + "doc_067033cfc945", + "doc_136d7a909d51", + "doc_18d38fdacc9e", + "doc_3aa0b3792d22", + "doc_4462f67b1c03", + "doc_57e2139f0873", + "doc_5a65ddab1445", + "doc_eeed52120ccf", + "doc_f18f523b6ecc" + ], + "guided": [ + "doc_a24b92541657", + "doc_f18f523b6ecc" + ], + "guidelines": [ + "doc_0383dbf277ba", + "doc_2798948c3080", + "doc_64090a20f830", + "doc_b6088784caab", + "doc_c346f13ce597", + "doc_e67e762fb6ab" + ], + "guides": [ + "doc_1e46cbc6bfae", + "doc_b6088784caab" + ], + "hammering": [ + "doc_a24b92541657" + ], + "hand": [ + "doc_e7d508371e5c" + ], + "handle": [ + "doc_0ac01bce8c79", + "doc_18d38fdacc9e", + "doc_3f1383f72c5f", + "doc_4a1cf8a90e83", + "doc_4e1ebaf40267", + "doc_7320ab982fab", + "doc_745f751f1344", + "doc_7b26cbd9a7ae", + "doc_9e9118ed2141", + "doc_d147a81bac29", + "doc_f54217a0f2cf", + "doc_f5f256549d84", + "doc_f92685a34f7e" + ], + "handled": [ + "doc_0ac01bce8c79", + "doc_5ee8bbdbd8a8", + "doc_64090a20f830", + "doc_eeed52120ccf" + ], + "handler": [ + "doc_0ac01bce8c79", + "doc_2a18de009d56", + "doc_4055659fe573", + "doc_50ac650fd8f6", + "doc_745f751f1344", + "doc_78b1717fafe3", + "doc_7b26cbd9a7ae", + "doc_9e9118ed2141" + ], + "handlers": [ + "doc_18d38fdacc9e", + "doc_9e9118ed2141", + "doc_a24b92541657" + ], + "handles": [ + "doc_50ac650fd8f6", + "doc_9e9118ed2141", + "doc_a24b92541657", + "doc_a2e84472f1f9", + "doc_d147a81bac29", + "doc_deb460ffa5ee", + "doc_e67e762fb6ab", + "doc_e7d508371e5c", + "doc_f12bbbc027a7", + "doc_f2ae50c638a4" + ], + "handling": [ + "doc_07174a8633ac", + "doc_109157c0d2d7", + "doc_2798948c3080", + "doc_4462f67b1c03", + "doc_57e2139f0873", + "doc_5ee8bbdbd8a8", + "doc_618e34441fa7", + "doc_7320ab982fab", + "doc_745f751f1344", + "doc_78b1717fafe3", + "doc_a2e84472f1f9", + "doc_a5120de838fb", + "doc_c346f13ce597", + "doc_c93c115aeb85", + "doc_eeed52120ccf", + "doc_f12bbbc027a7", + "doc_f92685a34f7e" + ], + "hang": [ + "doc_136d7a909d51" + ], + "happen": [ + "doc_109157c0d2d7", + "doc_4a1cf8a90e83", + "doc_9e9118ed2141", + "doc_c346f13ce597" + ], + "happened": [ + "doc_0ac01bce8c79", + "doc_4055659fe573", + "doc_f54217a0f2cf" + ], + "happens": [ + "doc_4e1ebaf40267", + "doc_5ee8bbdbd8a8" + ], + "hard": [ + "doc_3ae3aaf0c12c", + "doc_50ac650fd8f6", + "doc_567be1281e2f", + "doc_7320ab982fab", + "doc_f2ae50c638a4", + "doc_fdf48d427fc0" + ], + "hard-coded": [ + "doc_b6088784caab" + ], + "hard-to-debug": [ + "doc_18d38fdacc9e" + ], + "hardcoded": [ + "doc_61fd9f682847" + ], + "hardcoding": [ + "doc_64090a20f830" + ], + "hardening": [ + "doc_067033cfc945", + "doc_4055659fe573", + "doc_567be1281e2f", + "doc_78a8125d2c7e", + "doc_c93c115aeb85" + ], + "has": [ + "doc_07174a8633ac", + "doc_0ac01bce8c79", + "doc_1c5f37ea0ad3", + "doc_43ad71a4ad0c", + "doc_4a1cf8a90e83", + "doc_50ac650fd8f6", + "doc_5ee8bbdbd8a8", + "doc_64090a20f830", + "doc_745f751f1344", + "doc_78b1717fafe3", + "doc_9e9118ed2141", + "doc_b6088784caab", + "doc_e67e762fb6ab" + ], + "hash": [ + "doc_4a1cf8a90e83", + "doc_64090a20f830" + ], + "hashicorp": [ + "doc_067033cfc945" + ], + "hashing": [ + "doc_1c5f37ea0ad3" + ], + "have": [ + "doc_07174a8633ac", + "doc_0ac01bce8c79", + "doc_18d38fdacc9e", + "doc_3ae3aaf0c12c", + "doc_43ad71a4ad0c", + "doc_4a1cf8a90e83", + "doc_4e1ebaf40267", + "doc_50ac650fd8f6", + "doc_57e2139f0873", + "doc_618e34441fa7", + "doc_78a8125d2c7e", + "doc_e67e762fb6ab", + "doc_f54217a0f2cf" + ], + "having": [ + "doc_618e34441fa7" + ], + "head": [ + "doc_a24b92541657" + ], + "headless": [ + "doc_1e46cbc6bfae", + "doc_43ad71a4ad0c", + "doc_a24b92541657", + "doc_d6ad61a79371" + ], + "headlessinteractionprovider": [ + "doc_0ac01bce8c79", + "doc_2a18de009d56" + ], + "health": [ + "doc_067033cfc945", + "doc_4462f67b1c03", + "doc_61fd9f682847", + "doc_78b1717fafe3" + ], + "health_check_interval_s": [ + "doc_4462f67b1c03" + ], + "hedgingpolicy": [ + "doc_2a18de009d56" + ], + "help": [ + "doc_136d7a909d51", + "doc_5ee8bbdbd8a8" + ], + "helpers": [ + "doc_2a18de009d56", + "doc_61fd9f682847", + "doc_9e9118ed2141", + "doc_d147a81bac29" + ], + "helps": [ + "doc_4055659fe573", + "doc_43ad71a4ad0c", + "doc_50ac650fd8f6", + "doc_5a65ddab1445", + "doc_b6088784caab" + ], + "here": [ + "doc_1e46cbc6bfae", + "doc_3aa0b3792d22", + "doc_5ee8bbdbd8a8", + "doc_a5120de838fb", + "doc_deb460ffa5ee" + ], + "hide": [ + "doc_618e34441fa7" + ], + "hiding": [ + "doc_3ae3aaf0c12c" + ], + "hierarchy": [ + "doc_2a18de009d56", + "doc_64090a20f830", + "doc_a24b92541657" + ], + "high": [ + "doc_136d7a909d51", + "doc_7320ab982fab", + "doc_78a8125d2c7e", + "doc_e67e762fb6ab" + ], + "high-risk": [ + "doc_d147a81bac29" + ], + "high-throughput": [ + "doc_57e2139f0873", + "doc_78b1717fafe3" + ], + "higher": [ + "doc_136d7a909d51", + "doc_18d38fdacc9e", + "doc_eeed52120ccf" + ], + "highest": [ + "doc_3f1383f72c5f", + "doc_61fd9f682847", + "doc_eeed52120ccf" + ], + "histogram": [ + "doc_0ac01bce8c79" + ], + "histograms": [ + "doc_745f751f1344" + ], + "histories": [ + "doc_07174a8633ac" + ], + "history": [ + "doc_0383dbf277ba", + "doc_07174a8633ac", + "doc_2798948c3080", + "doc_4a1cf8a90e83", + "doc_50ac650fd8f6", + "doc_5ee8bbdbd8a8", + "doc_e7d508371e5c", + "doc_e8794369a1a4", + "doc_eeed52120ccf" + ], + "hit": [ + "doc_0383dbf277ba", + "doc_4055659fe573", + "doc_78b1717fafe3", + "doc_e7d508371e5c" + ], + "hitl": [ + "doc_a5120de838fb", + "doc_d6ad61a79371", + "doc_f18f523b6ecc" + ], + "hits": [ + "doc_a24b92541657" + ], + "hmac-sha256": [ + "doc_1c5f37ea0ad3" + ], + "honeycomb": [ + "doc_2a18de009d56", + "doc_78a8125d2c7e" + ], + "hood": [ + "doc_1e46cbc6bfae" + ], + "hook": [ + "doc_618e34441fa7", + "doc_745f751f1344", + "doc_c346f13ce597" + ], + "hooks": [ + "doc_2a18de009d56", + "doc_4e1ebaf40267", + "doc_50ac650fd8f6", + "doc_5a65ddab1445", + "doc_9e9118ed2141", + "doc_a24b92541657", + "doc_a5120de838fb", + "doc_bbc97cbff254", + "doc_c346f13ce597", + "doc_deb460ffa5ee", + "doc_f2ae50c638a4" + ], + "horizontal": [ + "doc_067033cfc945" + ], + "host": [ + "doc_4e1ebaf40267", + "doc_567be1281e2f", + "doc_61fd9f682847" + ], + "hosting": [ + "doc_1c5f37ea0ad3", + "doc_2a18de009d56" + ], + "hosts": [ + "doc_2a18de009d56" + ], + "hot-reload": [ + "doc_64090a20f830" + ], + "hourly": [ + "doc_07174a8633ac" + ], + "hours": [ + "doc_4a1cf8a90e83" + ], + "how": [ + "doc_067033cfc945", + "doc_07174a8633ac", + "doc_0ac01bce8c79", + "doc_136d7a909d51", + "doc_2798948c3080", + "doc_3eeb75d383b8", + "doc_40e30666bfa8", + "doc_43ad71a4ad0c", + "doc_4462f67b1c03", + "doc_464a2b4a8d35", + "doc_4a1cf8a90e83", + "doc_50ac650fd8f6", + "doc_567be1281e2f", + "doc_57e2139f0873", + "doc_5ee8bbdbd8a8", + "doc_64090a20f830", + "doc_7320ab982fab", + "doc_7b26cbd9a7ae", + "doc_9e9118ed2141", + "doc_a24b92541657", + "doc_a2e84472f1f9", + "doc_a5120de838fb", + "doc_b6088784caab", + "doc_d1f7d83e0824", + "doc_d6ad61a79371", + "doc_deb460ffa5ee", + "doc_e67e762fb6ab", + "doc_eeed52120ccf", + "doc_f12bbbc027a7", + "doc_f1e32ed2ffce", + "doc_f54217a0f2cf", + "doc_f92685a34f7e", + "doc_fdf48d427fc0" + ], + "how-to": [ + "doc_b6088784caab" + ], + "how-to-use-afk": [ + "doc_3aa0b3792d22" + ], + "hpa": [ + "doc_067033cfc945" + ], + "href": [ + "doc_3aa0b3792d22" + ], + "http": [ + "doc_1c5f37ea0ad3", + "doc_61fd9f682847", + "doc_f5f256549d84" + ], + "https": [ + "doc_3aa0b3792d22" + ], + "human": [ + "doc_07174a8633ac", + "doc_0ac01bce8c79", + "doc_43ad71a4ad0c", + "doc_50ac650fd8f6", + "doc_745f751f1344", + "doc_a24b92541657", + "doc_a5120de838fb", + "doc_b6088784caab", + "doc_d6ad61a79371" + ], + "human-in-the-loop": [ + "doc_0ac01bce8c79", + "doc_2a18de009d56", + "doc_4a1cf8a90e83", + "doc_a24b92541657", + "doc_d6ad61a79371", + "doc_e67e762fb6ab" + ], + "human-readable": [ + "doc_78a8125d2c7e", + "doc_f54217a0f2cf" + ], + "humans": [ + "doc_a24b92541657" + ], + "hundreds": [ + "doc_07174a8633ac" + ], + "hybrid": [ + "doc_a5120de838fb" + ], + "hyphens": [ + "doc_64090a20f830" + ], + "id": [ + "doc_3f60413b3a06", + "doc_4a1cf8a90e83", + "doc_5ee8bbdbd8a8", + "doc_61fd9f682847", + "doc_745f751f1344", + "doc_7b26cbd9a7ae", + "doc_a24b92541657", + "doc_bf91f8d5da74", + "doc_e7d508371e5c" + ], + "idea": [ + "doc_f18f523b6ecc" + ], + "idempotency": [ + "doc_1c5f37ea0ad3", + "doc_4a1cf8a90e83", + "doc_7320ab982fab", + "doc_75887f91c7df" + ], + "idempotency_key": [ + "doc_1c5f37ea0ad3", + "doc_5ee8bbdbd8a8", + "doc_75887f91c7df" + ], + "idempotent": [ + "doc_4a1cf8a90e83", + "doc_75887f91c7df" + ], + "identical": [ + "doc_7320ab982fab" + ], + "identifier": [ + "doc_07174a8633ac", + "doc_1e46cbc6bfae", + "doc_3f60413b3a06", + "doc_4a1cf8a90e83", + "doc_5ee8bbdbd8a8", + "doc_745f751f1344", + "doc_75887f91c7df", + "doc_a24b92541657", + "doc_e7d508371e5c", + "doc_e8794369a1a4", + "doc_f54217a0f2cf" + ], + "identifies": [ + "doc_0ac01bce8c79" + ], + "identify": [ + "doc_43ad71a4ad0c", + "doc_618e34441fa7", + "doc_d147a81bac29", + "doc_f18f523b6ecc" + ], + "identity": [ + "doc_1c5f37ea0ad3", + "doc_1e46cbc6bfae", + "doc_a24b92541657", + "doc_e67e762fb6ab", + "doc_f18f523b6ecc" + ], + "idle": [ + "doc_4462f67b1c03", + "doc_61fd9f682847", + "doc_78b1717fafe3" + ], + "ids": [ + "doc_7320ab982fab", + "doc_75887f91c7df", + "doc_f18f523b6ecc", + "doc_f5f256549d84" + ], + "ignored": [ + "doc_4055659fe573" + ], + "ignores": [ + "doc_136d7a909d51", + "doc_f54217a0f2cf" + ], + "ignoring": [ + "doc_a24b92541657" + ], + "image": [ + "doc_f1e32ed2ffce" + ], + "immediate": [ + "doc_78a8125d2c7e" + ], + "immediately": [ + "doc_07174a8633ac", + "doc_0ac01bce8c79", + "doc_4055659fe573", + "doc_464a2b4a8d35", + "doc_618e34441fa7", + "doc_64090a20f830", + "doc_745f751f1344", + "doc_a24b92541657", + "doc_d6ad61a79371" + ], + "impact": [ + "doc_0ac01bce8c79", + "doc_4e1ebaf40267" + ], + "implement": [ + "doc_0383dbf277ba", + "doc_067033cfc945", + "doc_2a18de009d56", + "doc_4e1ebaf40267", + "doc_618e34441fa7", + "doc_75887f91c7df", + "doc_bbc97cbff254", + "doc_c93c115aeb85" + ], + "implementation": [ + "doc_2a18de009d56", + "doc_3eeb75d383b8", + "doc_50ac650fd8f6", + "doc_f5f256549d84" + ], + "implementations": [ + "doc_2a18de009d56" + ], + "implementing": [ + "doc_5ee8bbdbd8a8" + ], + "implements": [ + "doc_567be1281e2f", + "doc_d6ad61a79371" + ], + "import": [ + "doc_3eeb75d383b8", + "doc_4e1ebaf40267", + "doc_a24b92541657", + "doc_bbc97cbff254", + "doc_c93c115aeb85", + "doc_d147a81bac29", + "doc_deb460ffa5ee", + "doc_f18f523b6ecc" + ], + "important": [ + "doc_0ac01bce8c79", + "doc_43ad71a4ad0c", + "doc_4a1cf8a90e83", + "doc_5ee8bbdbd8a8", + "doc_bbc97cbff254" + ], + "imported": [ + "doc_f18f523b6ecc" + ], + "imports": [ + "doc_18d38fdacc9e", + "doc_2a18de009d56", + "doc_3eeb75d383b8", + "doc_4e1ebaf40267", + "doc_618e34441fa7", + "doc_bbc97cbff254", + "doc_d147a81bac29", + "doc_deb460ffa5ee" + ], + "improves": [ + "doc_4a1cf8a90e83" + ], + "in-memory": [ + "doc_0383dbf277ba", + "doc_136d7a909d51", + "doc_1e46cbc6bfae", + "doc_2a18de009d56", + "doc_618e34441fa7", + "doc_7320ab982fab", + "doc_f5f256549d84" + ], + "incident": [ + "doc_a2e84472f1f9", + "doc_f18f523b6ecc" + ], + "include": [ + "doc_07174a8633ac", + "doc_136d7a909d51", + "doc_2798948c3080", + "doc_43ad71a4ad0c", + "doc_4a1cf8a90e83", + "doc_4e1ebaf40267", + "doc_5ee8bbdbd8a8", + "doc_618e34441fa7", + "doc_b6088784caab", + "doc_d147a81bac29", + "doc_e7d508371e5c" + ], + "included": [ + "doc_5ee8bbdbd8a8" + ], + "includes": [ + "doc_07174a8633ac", + "doc_1e46cbc6bfae", + "doc_3f1383f72c5f", + "doc_5ee8bbdbd8a8", + "doc_618e34441fa7", + "doc_64090a20f830", + "doc_7320ab982fab", + "doc_78a8125d2c7e", + "doc_a2e84472f1f9", + "doc_b6088784caab", + "doc_fdf48d427fc0" + ], + "including": [ + "doc_464a2b4a8d35", + "doc_64090a20f830", + "doc_c346f13ce597", + "doc_d1f7d83e0824" + ], + "inconsistent": [ + "doc_136d7a909d51", + "doc_18d38fdacc9e" + ], + "incorrect": [ + "doc_618e34441fa7" + ], + "incorrectly": [ + "doc_618e34441fa7" + ], + "increase": [ + "doc_7320ab982fab", + "doc_f5f256549d84" + ], + "incremental": [ + "doc_109157c0d2d7", + "doc_5ee8bbdbd8a8", + "doc_f54217a0f2cf" + ], + "incrementally": [ + "doc_3ae3aaf0c12c" + ], + "independently": [ + "doc_0ac01bce8c79", + "doc_7320ab982fab", + "doc_78b1717fafe3", + "doc_a2e84472f1f9" + ], + "index": [ + "doc_3aa0b3792d22" + ], + "indexes": [ + "doc_d147a81bac29", + "doc_f18f523b6ecc" + ], + "indicate": [ + "doc_109157c0d2d7" + ], + "indicates": [ + "doc_067033cfc945" + ], + "indicating": [ + "doc_464a2b4a8d35" + ], + "individual": [ + "doc_4a1cf8a90e83", + "doc_7320ab982fab", + "doc_c346f13ce597", + "doc_c93c115aeb85", + "doc_e34fe24f9585", + "doc_f54217a0f2cf" + ], + "inference": [ + "doc_2a18de009d56", + "doc_f1e32ed2ffce" + ], + "infinite": [ + "doc_618e34441fa7" + ], + "information": [ + "doc_136d7a909d51", + "doc_a2e84472f1f9", + "doc_f5f256549d84" + ], + "infrastructure": [ + "doc_067033cfc945", + "doc_07174a8633ac", + "doc_4055659fe573", + "doc_78a8125d2c7e" + ], + "ingested": [ + "doc_a24b92541657" + ], + "inherit_context_keys": [ + "doc_a24b92541657", + "doc_e67e762fb6ab" + ], + "inherited": [ + "doc_e67e762fb6ab" + ], + "init__": [ + "doc_3eeb75d383b8" + ], + "initial": [ + "doc_1e46cbc6bfae", + "doc_5ee8bbdbd8a8", + "doc_7320ab982fab" + ], + "initialize": [ + "doc_0383dbf277ba" + ], + "inject": [ + "doc_2798948c3080", + "doc_a24b92541657" + ], + "injected": [ + "doc_0ac01bce8c79", + "doc_9e9118ed2141" + ], + "injection": [ + "doc_50ac650fd8f6", + "doc_567be1281e2f", + "doc_64090a20f830", + "doc_745f751f1344", + "doc_9e9118ed2141" + ], + "injects": [ + "doc_3f1383f72c5f" + ], + "inline": [ + "doc_2798948c3080", + "doc_64090a20f830" + ], + "inmemory": [ + "doc_61fd9f682847" + ], + "inmemoryinteractiveprovider": [ + "doc_2a18de009d56" + ], + "inmemorymemorystore": [ + "doc_2a18de009d56", + "doc_bbc97cbff254" + ], + "inmemorytaskqueue": [ + "doc_bbc97cbff254" + ], + "inode": [ + "doc_64090a20f830" + ], + "input": [ + "doc_0ac01bce8c79", + "doc_136d7a909d51", + "doc_18d38fdacc9e", + "doc_1c5f37ea0ad3", + "doc_4a1cf8a90e83", + "doc_567be1281e2f", + "doc_618e34441fa7", + "doc_61fd9f682847", + "doc_a24b92541657", + "doc_bbc97cbff254", + "doc_c346f13ce597", + "doc_c93c115aeb85", + "doc_d6ad61a79371", + "doc_e8794369a1a4", + "doc_f1e32ed2ffce" + ], + "input_fallback": [ + "doc_a24b92541657" + ], + "input_hash": [ + "doc_4a1cf8a90e83" + ], + "input_timeout_s": [ + "doc_a24b92541657" + ], + "input_tokens": [ + "doc_4a1cf8a90e83", + "doc_5ee8bbdbd8a8" + ], + "inputs": [ + "doc_136d7a909d51", + "doc_18d38fdacc9e", + "doc_c93c115aeb85", + "doc_f5f256549d84" + ], + "inside": [ + "doc_64090a20f830", + "doc_bbc97cbff254", + "doc_c346f13ce597" + ], + "inspect": [ + "doc_3ae3aaf0c12c", + "doc_618e34441fa7", + "doc_a2e84472f1f9", + "doc_d147a81bac29" + ], + "inspecting": [ + "doc_745f751f1344", + "doc_d1f7d83e0824" + ], + "inspection": [ + "doc_745f751f1344" + ], + "install": [ + "doc_b6088784caab", + "doc_bf91f8d5da74", + "doc_d147a81bac29", + "doc_deb460ffa5ee" + ], + "installed": [ + "doc_bf91f8d5da74" + ], + "installing": [ + "doc_b6088784caab" + ], + "installs": [ + "doc_b6088784caab" + ], + "instance": [ + "doc_3f60413b3a06", + "doc_a24b92541657", + "doc_e67e762fb6ab" + ], + "instances": [ + "doc_2a18de009d56", + "doc_5ee8bbdbd8a8", + "doc_f54217a0f2cf" + ], + "instantly_": [ + "doc_a24b92541657" + ], + "instead": [ + "doc_0ac01bce8c79", + "doc_109157c0d2d7", + "doc_2a18de009d56", + "doc_4055659fe573", + "doc_4462f67b1c03", + "doc_4a1cf8a90e83", + "doc_9e9118ed2141", + "doc_a24b92541657", + "doc_bf91f8d5da74", + "doc_f5f256549d84" + ], + "instruct": [ + "doc_18d38fdacc9e" + ], + "instruction": [ + "doc_18d38fdacc9e", + "doc_2798948c3080", + "doc_a24b92541657", + "doc_e67e762fb6ab" + ], + "instruction_file": [ + "doc_2798948c3080", + "doc_61fd9f682847", + "doc_64090a20f830", + "doc_a24b92541657", + "doc_e67e762fb6ab" + ], + "instruction_roles": [ + "doc_a24b92541657", + "doc_e67e762fb6ab" + ], + "instructionrole": [ + "doc_e67e762fb6ab" + ], + "instructions": [ + "doc_136d7a909d51", + "doc_18d38fdacc9e", + "doc_1e46cbc6bfae", + "doc_2798948c3080", + "doc_50ac650fd8f6", + "doc_5ee8bbdbd8a8", + "doc_61fd9f682847", + "doc_64090a20f830", + "doc_a24b92541657", + "doc_a2e84472f1f9", + "doc_b6088784caab", + "doc_bbc97cbff254", + "doc_deb460ffa5ee", + "doc_e67e762fb6ab", + "doc_e8794369a1a4", + "doc_eeed52120ccf", + "doc_f12bbbc027a7", + "doc_f18f523b6ecc" + ], + "instructs": [ + "doc_3f60413b3a06" + ], + "int": [ + "doc_464a2b4a8d35", + "doc_4a1cf8a90e83", + "doc_5ee8bbdbd8a8", + "doc_75887f91c7df", + "doc_78a8125d2c7e", + "doc_a24b92541657", + "doc_e67e762fb6ab", + "doc_e7d508371e5c", + "doc_e8794369a1a4", + "doc_f54217a0f2cf" + ], + "intact": [ + "doc_d147a81bac29" + ], + "integrate": [ + "doc_2a18de009d56" + ], + "integration": [ + "doc_1c5f37ea0ad3", + "doc_3eeb75d383b8", + "doc_618e34441fa7", + "doc_c93c115aeb85", + "doc_d147a81bac29", + "doc_d1f7d83e0824", + "doc_eeed52120ccf", + "doc_f1e32ed2ffce" + ], + "integrations": [ + "doc_3aa0b3792d22", + "doc_57e2139f0873", + "doc_e7d508371e5c" + ], + "intended": [ + "doc_618e34441fa7" + ], + "intent": [ + "doc_43ad71a4ad0c" + ], + "intentional": [ + "doc_07174a8633ac", + "doc_4a1cf8a90e83" + ], + "intentionally": [ + "doc_3eeb75d383b8", + "doc_bbc97cbff254" + ], + "interact": [ + "doc_43ad71a4ad0c" + ], + "interaction": [ + "doc_0ac01bce8c79", + "doc_1e46cbc6bfae", + "doc_2a18de009d56", + "doc_3f60413b3a06", + "doc_50ac650fd8f6", + "doc_5ee8bbdbd8a8", + "doc_a24b92541657", + "doc_a5120de838fb", + "doc_d147a81bac29", + "doc_d6ad61a79371", + "doc_e8794369a1a4" + ], + "interaction_mode": [ + "doc_a24b92541657", + "doc_d6ad61a79371" + ], + "interaction_provider": [ + "doc_a24b92541657" + ], + "interactionprovider": [ + "doc_0ac01bce8c79", + "doc_2a18de009d56", + "doc_a24b92541657", + "doc_d6ad61a79371" + ], + "interactive": [ + "doc_43ad71a4ad0c", + "doc_a24b92541657", + "doc_d6ad61a79371" + ], + "intercept": [ + "doc_c346f13ce597" + ], + "intercepting": [ + "doc_c346f13ce597", + "doc_f92685a34f7e" + ], + "interception": [ + "doc_5a65ddab1445" + ], + "intercepts": [ + "doc_a2e84472f1f9" + ], + "interface": [ + "doc_4e1ebaf40267", + "doc_f92685a34f7e" + ], + "interfaces": [ + "doc_2a18de009d56" + ], + "intermediate": [ + "doc_a5120de838fb" + ], + "internal": [ + "doc_0383dbf277ba", + "doc_1c5f37ea0ad3", + "doc_2a18de009d56", + "doc_3aa0b3792d22", + "doc_3eeb75d383b8", + "doc_4055659fe573", + "doc_464a2b4a8d35", + "doc_75887f91c7df", + "doc_bbc97cbff254", + "doc_d147a81bac29", + "doc_f18f523b6ecc", + "doc_f1e32ed2ffce" + ], + "internala2aenvelope": [ + "doc_75887f91c7df" + ], + "internala2aprotocol": [ + "doc_2a18de009d56", + "doc_57e2139f0873", + "doc_bbc97cbff254" + ], + "internally": [ + "doc_07174a8633ac", + "doc_64090a20f830" + ], + "internals": [ + "doc_3ae3aaf0c12c", + "doc_d147a81bac29", + "doc_deb460ffa5ee", + "doc_f18f523b6ecc" + ], + "internals-first": [ + "doc_3ae3aaf0c12c" + ], + "interop": [ + "doc_57e2139f0873", + "doc_e34fe24f9585" + ], + "interoperability": [ + "doc_1c5f37ea0ad3" + ], + "interpreted": [ + "doc_5ee8bbdbd8a8" + ], + "interpreting": [ + "doc_618e34441fa7" + ], + "interprets": [ + "doc_5ee8bbdbd8a8" + ], + "interrupt": [ + "doc_109157c0d2d7", + "doc_e7d508371e5c" + ], + "interrupted": [ + "doc_0383dbf277ba", + "doc_07174a8633ac", + "doc_4055659fe573", + "doc_4a1cf8a90e83", + "doc_e7d508371e5c", + "doc_f54217a0f2cf" + ], + "interruption": [ + "doc_07174a8633ac", + "doc_f54217a0f2cf" + ], + "interrupts": [ + "doc_0383dbf277ba" + ], + "interval": [ + "doc_4462f67b1c03", + "doc_a24b92541657" + ], + "intervals": [ + "doc_618e34441fa7" + ], + "intervention": [ + "doc_d6ad61a79371" + ], + "introduce": [ + "doc_618e34441fa7" + ], + "introduced": [ + "doc_a5120de838fb" + ], + "introduces": [ + "doc_f2ae50c638a4" + ], + "invalid": [ + "doc_4055659fe573", + "doc_5ee8bbdbd8a8", + "doc_78b1717fafe3", + "doc_9e9118ed2141", + "doc_eeed52120ccf" + ], + "invalidated": [ + "doc_64090a20f830" + ], + "invalidation": [ + "doc_64090a20f830" + ], + "invalidrequesterror": [ + "doc_136d7a909d51" + ], + "invariant": [ + "doc_3eeb75d383b8", + "doc_d147a81bac29" + ], + "investigation": [ + "doc_618e34441fa7" + ], + "invocation": [ + "doc_0ac01bce8c79", + "doc_1c5f37ea0ad3", + "doc_a2e84472f1f9", + "doc_d147a81bac29", + "doc_f54217a0f2cf" + ], + "invocations": [ + "doc_1e46cbc6bfae", + "doc_5ee8bbdbd8a8", + "doc_a24b92541657", + "doc_e7d508371e5c" + ], + "invoke": [ + "doc_1c5f37ea0ad3", + "doc_57e2139f0873", + "doc_a2e84472f1f9" + ], + "invoked": [ + "doc_0ac01bce8c79", + "doc_745f751f1344", + "doc_a2e84472f1f9" + ], + "involved": [ + "doc_d6ad61a79371" + ], + "irreversibility": [ + "doc_43ad71a4ad0c" + ], + "irreversible": [ + "doc_43ad71a4ad0c", + "doc_d6ad61a79371" + ], + "is_dir": [ + "doc_464a2b4a8d35" + ], + "is_file": [ + "doc_464a2b4a8d35" + ], + "isn": [ + "doc_50ac650fd8f6", + "doc_57e2139f0873", + "doc_f12bbbc027a7" + ], + "isolated": [ + "doc_618e34441fa7" + ], + "isolating": [ + "doc_567be1281e2f" + ], + "isolation": [ + "doc_1c5f37ea0ad3", + "doc_3ae3aaf0c12c", + "doc_4e1ebaf40267", + "doc_567be1281e2f", + "doc_618e34441fa7" + ], + "issue": [ + "doc_136d7a909d51", + "doc_f54217a0f2cf" + ], + "issuer": [ + "doc_1c5f37ea0ad3" + ], + "issues": [ + "doc_0ac01bce8c79", + "doc_136d7a909d51", + "doc_50ac650fd8f6", + "doc_5ee8bbdbd8a8" + ], + "item": [ + "doc_fdf48d427fc0" + ], + "items": [ + "doc_067033cfc945", + "doc_3aa0b3792d22" + ], + "iterate": [ + "doc_18d38fdacc9e" + ], + "iteration": [ + "doc_e7d508371e5c", + "doc_f54217a0f2cf" + ], + "iterations": [ + "doc_50ac650fd8f6", + "doc_78a8125d2c7e", + "doc_a24b92541657", + "doc_e67e762fb6ab" + ], + "iterator": [ + "doc_f54217a0f2cf" + ], + "its": [ + "doc_0ac01bce8c79", + "doc_1c5f37ea0ad3", + "doc_4a1cf8a90e83", + "doc_618e34441fa7", + "doc_64090a20f830", + "doc_745f751f1344", + "doc_9e9118ed2141", + "doc_a24b92541657", + "doc_c346f13ce597", + "doc_e67e762fb6ab", + "doc_e7d508371e5c", + "doc_f12bbbc027a7" + ], + "itself": [ + "doc_618e34441fa7", + "doc_b6088784caab", + "doc_d147a81bac29", + "doc_deb460ffa5ee", + "doc_f18f523b6ecc" + ], + "jaeger": [ + "doc_78a8125d2c7e" + ], + "jinja2": [ + "doc_2798948c3080", + "doc_64090a20f830" + ], + "jitter": [ + "doc_61fd9f682847", + "doc_7320ab982fab" + ], + "job": [ + "doc_1c5f37ea0ad3", + "doc_75887f91c7df", + "doc_78b1717fafe3" + ], + "jobs": [ + "doc_57e2139f0873", + "doc_78b1717fafe3", + "doc_a24b92541657" + ], + "join": [ + "doc_4055659fe573", + "doc_57e2139f0873", + "doc_f12bbbc027a7" + ], + "join_policy": [ + "doc_18d38fdacc9e" + ], + "joinpolicy": [ + "doc_2a18de009d56" + ], + "journal": [ + "doc_0383dbf277ba", + "doc_2a18de009d56" + ], + "json": [ + "doc_0383dbf277ba", + "doc_3aa0b3792d22", + "doc_3f60413b3a06", + "doc_4a1cf8a90e83", + "doc_745f751f1344", + "doc_78a8125d2c7e", + "doc_9e9118ed2141", + "doc_a24b92541657", + "doc_d147a81bac29", + "doc_e67e762fb6ab", + "doc_e8794369a1a4" + ], + "json-safe": [ + "doc_745f751f1344", + "doc_75887f91c7df", + "doc_f5f256549d84" + ], + "json-serializable": [ + "doc_75887f91c7df" + ], + "jsonvalue": [ + "doc_2a18de009d56", + "doc_745f751f1344", + "doc_e7d508371e5c" + ], + "junit": [ + "doc_618e34441fa7" + ], + "just": [ + "doc_40e30666bfa8", + "doc_50ac650fd8f6", + "doc_c346f13ce597" + ], + "jwt": [ + "doc_1c5f37ea0ad3" + ], + "jwta2aauthprovider": [ + "doc_2a18de009d56" + ], + "keep": [ + "doc_0383dbf277ba", + "doc_18d38fdacc9e", + "doc_2798948c3080", + "doc_3ae3aaf0c12c", + "doc_3eeb75d383b8", + "doc_618e34441fa7", + "doc_b6088784caab", + "doc_bf91f8d5da74", + "doc_d147a81bac29", + "doc_e67e762fb6ab", + "doc_f2ae50c638a4", + "doc_f5f256549d84" + ], + "keepalive": [ + "doc_4462f67b1c03" + ], + "keeping": [ + "doc_07174a8633ac", + "doc_f5f256549d84" + ], + "keeps": [ + "doc_136d7a909d51", + "doc_64090a20f830", + "doc_f2ae50c638a4" + ], + "key": [ + "doc_0383dbf277ba", + "doc_067033cfc945", + "doc_0ac01bce8c79", + "doc_109157c0d2d7", + "doc_2a18de009d56", + "doc_4055659fe573", + "doc_4a1cf8a90e83", + "doc_4e1ebaf40267", + "doc_5a65ddab1445", + "doc_5ee8bbdbd8a8", + "doc_618e34441fa7", + "doc_61fd9f682847", + "doc_75887f91c7df", + "doc_78a8125d2c7e", + "doc_7b26cbd9a7ae", + "doc_9e9118ed2141", + "doc_bf91f8d5da74", + "doc_deb460ffa5ee", + "doc_f2ae50c638a4", + "doc_f54217a0f2cf" + ], + "key-value": [ + "doc_07174a8633ac", + "doc_2798948c3080" + ], + "keyed": [ + "doc_64090a20f830" + ], + "keys": [ + "doc_067033cfc945", + "doc_1c5f37ea0ad3", + "doc_1e46cbc6bfae", + "doc_3f1383f72c5f", + "doc_4a1cf8a90e83", + "doc_567be1281e2f", + "doc_618e34441fa7", + "doc_61fd9f682847", + "doc_64090a20f830", + "doc_7320ab982fab", + "doc_9e9118ed2141", + "doc_a24b92541657", + "doc_d147a81bac29", + "doc_e67e762fb6ab" + ], + "kill": [ + "doc_a24b92541657", + "doc_fdf48d427fc0" + ], + "kind": [ + "doc_78b1717fafe3", + "doc_d6ad61a79371" + ], + "kit": [ + "doc_2a18de009d56" + ], + "knob": [ + "doc_a24b92541657" + ], + "know": [ + "doc_07174a8633ac", + "doc_40e30666bfa8", + "doc_4e1ebaf40267", + "doc_b6088784caab", + "doc_e34fe24f9585" + ], + "knowledge": [ + "doc_0383dbf277ba", + "doc_2798948c3080", + "doc_b6088784caab" + ], + "known": [ + "doc_136d7a909d51", + "doc_618e34441fa7", + "doc_a5120de838fb" + ], + "knows": [ + "doc_2798948c3080", + "doc_50ac650fd8f6", + "doc_745f751f1344", + "doc_e67e762fb6ab" + ], + "kubernetes": [ + "doc_067033cfc945", + "doc_4462f67b1c03" + ], + "kv": [ + "doc_0383dbf277ba" + ], + "l1": [ + "doc_57e2139f0873" + ], + "l2": [ + "doc_57e2139f0873" + ], + "l3": [ + "doc_57e2139f0873" + ], + "l4": [ + "doc_57e2139f0873" + ], + "l5": [ + "doc_57e2139f0873" + ], + "label": [ + "doc_a24b92541657" + ], + "labels": [ + "doc_18d38fdacc9e" + ], + "langchain": [ + "doc_5a65ddab1445" + ], + "langsmith": [ + "doc_5a65ddab1445" + ], + "language": [ + "doc_f92685a34f7e" + ], + "large": [ + "doc_07174a8633ac", + "doc_f5f256549d84" + ], + "larger": [ + "doc_f5f256549d84", + "doc_fdf48d427fc0" + ], + "last": [ + "doc_0383dbf277ba", + "doc_3eeb75d383b8", + "doc_a5120de838fb", + "doc_c346f13ce597" + ], + "latency": [ + "doc_067033cfc945", + "doc_0ac01bce8c79", + "doc_18d38fdacc9e", + "doc_3ae3aaf0c12c", + "doc_4462f67b1c03", + "doc_5ee8bbdbd8a8", + "doc_7320ab982fab", + "doc_745f751f1344", + "doc_78a8125d2c7e", + "doc_c93c115aeb85", + "doc_e7d508371e5c", + "doc_e8794369a1a4", + "doc_f12bbbc027a7", + "doc_f5f256549d84" + ], + "latency_ms": [ + "doc_0ac01bce8c79", + "doc_745f751f1344", + "doc_a2e84472f1f9", + "doc_e7d508371e5c", + "doc_e8794369a1a4", + "doc_f54217a0f2cf" + ], + "later": [ + "doc_3f1383f72c5f", + "doc_4a1cf8a90e83", + "doc_a5120de838fb" + ], + "latest": [ + "doc_07174a8633ac", + "doc_4a1cf8a90e83", + "doc_9e9118ed2141" + ], + "layer": [ + "doc_18d38fdacc9e", + "doc_1c5f37ea0ad3", + "doc_3ae3aaf0c12c", + "doc_4e1ebaf40267", + "doc_745f751f1344", + "doc_9e9118ed2141", + "doc_a24b92541657", + "doc_c346f13ce597", + "doc_eeed52120ccf", + "doc_f92685a34f7e", + "doc_fdf48d427fc0" + ], + "layered": [ + "doc_4e1ebaf40267" + ], + "layers": [ + "doc_1c5f37ea0ad3", + "doc_43ad71a4ad0c", + "doc_464a2b4a8d35", + "doc_64090a20f830", + "doc_c346f13ce597", + "doc_d147a81bac29", + "doc_fdf48d427fc0" + ], + "layout": [ + "doc_3aa0b3792d22" + ], + "lead": [ + "doc_a2e84472f1f9" + ], + "leakage": [ + "doc_567be1281e2f" + ], + "leaks": [ + "doc_18d38fdacc9e" + ], + "learn": [ + "doc_a5120de838fb", + "doc_f18f523b6ecc" + ], + "learn-in-15-minutes": [ + "doc_3aa0b3792d22" + ], + "least": [ + "doc_4e1ebaf40267", + "doc_567be1281e2f" + ], + "least-privilege": [ + "doc_567be1281e2f" + ], + "left": [ + "doc_07174a8633ac", + "doc_4a1cf8a90e83" + ], + "legacy": [ + "doc_4a1cf8a90e83" + ], + "length": [ + "doc_e8794369a1a4" + ], + "less": [ + "doc_eeed52120ccf" + ], + "let": [ + "doc_0383dbf277ba", + "doc_18d38fdacc9e", + "doc_9e9118ed2141", + "doc_fdf48d427fc0" + ], + "lets": [ + "doc_4a1cf8a90e83", + "doc_75887f91c7df", + "doc_d1f7d83e0824", + "doc_f12bbbc027a7" + ], + "letters": [ + "doc_f54217a0f2cf" + ], + "level": [ + "doc_50ac650fd8f6", + "doc_567be1281e2f", + "doc_57e2139f0873", + "doc_a24b92541657", + "doc_a5120de838fb", + "doc_e67e762fb6ab" + ], + "levels": [ + "doc_57e2139f0873", + "doc_64090a20f830" + ], + "levers": [ + "doc_f5f256549d84" + ], + "libraries": [ + "doc_61fd9f682847" + ], + "library": [ + "doc_3aa0b3792d22" + ], + "lifecycle": [ + "doc_0383dbf277ba", + "doc_07174a8633ac", + "doc_0ac01bce8c79", + "doc_109157c0d2d7", + "doc_1e46cbc6bfae", + "doc_2a18de009d56", + "doc_3f60413b3a06", + "doc_4055659fe573", + "doc_5ee8bbdbd8a8", + "doc_618e34441fa7", + "doc_61fd9f682847", + "doc_745f751f1344", + "doc_75887f91c7df", + "doc_78b1717fafe3", + "doc_c346f13ce597", + "doc_c93c115aeb85", + "doc_d147a81bac29", + "doc_deb460ffa5ee", + "doc_e67e762fb6ab", + "doc_e7d508371e5c", + "doc_f2ae50c638a4" + ], + "lifetime": [ + "doc_07174a8633ac" + ], + "light": [ + "doc_3aa0b3792d22" + ], + "lighter": [ + "doc_4a1cf8a90e83" + ], + "lightweight": [ + "doc_3f1383f72c5f" + ], + "like": [ + "doc_0383dbf277ba", + "doc_1e46cbc6bfae", + "doc_40e30666bfa8", + "doc_464a2b4a8d35", + "doc_50ac650fd8f6", + "doc_5ee8bbdbd8a8", + "doc_618e34441fa7", + "doc_745f751f1344", + "doc_a24b92541657", + "doc_b6088784caab", + "doc_c346f13ce597", + "doc_e34fe24f9585", + "doc_e67e762fb6ab" + ], + "limit": [ + "doc_136d7a909d51", + "doc_4055659fe573", + "doc_464a2b4a8d35", + "doc_50ac650fd8f6", + "doc_5ee8bbdbd8a8", + "doc_7320ab982fab", + "doc_745f751f1344", + "doc_78b1717fafe3", + "doc_a24b92541657", + "doc_c93c115aeb85", + "doc_d1f7d83e0824", + "doc_e34fe24f9585", + "doc_e7d508371e5c", + "doc_eeed52120ccf", + "doc_f12bbbc027a7", + "doc_fdf48d427fc0" + ], + "limitations": [ + "doc_618e34441fa7" + ], + "limiting": [ + "doc_1c5f37ea0ad3", + "doc_40e30666bfa8", + "doc_567be1281e2f", + "doc_9e9118ed2141", + "doc_c346f13ce597", + "doc_d147a81bac29", + "doc_e7d508371e5c", + "doc_e8794369a1a4", + "doc_f1e32ed2ffce" + ], + "limits": [ + "doc_0383dbf277ba", + "doc_067033cfc945", + "doc_136d7a909d51", + "doc_18d38fdacc9e", + "doc_1c5f37ea0ad3", + "doc_3ae3aaf0c12c", + "doc_4055659fe573", + "doc_43ad71a4ad0c", + "doc_50ac650fd8f6", + "doc_567be1281e2f", + "doc_5ee8bbdbd8a8", + "doc_618e34441fa7", + "doc_7320ab982fab", + "doc_9e9118ed2141", + "doc_a24b92541657", + "doc_a5120de838fb", + "doc_bbc97cbff254", + "doc_c93c115aeb85", + "doc_d147a81bac29", + "doc_d1f7d83e0824", + "doc_deb460ffa5ee", + "doc_e67e762fb6ab", + "doc_eeed52120ccf", + "doc_f12bbbc027a7", + "doc_f18f523b6ecc", + "doc_f2ae50c638a4", + "doc_f5f256549d84", + "doc_f92685a34f7e", + "doc_fdf48d427fc0" + ], + "line": [ + "doc_78a8125d2c7e" + ], + "line-by-line": [ + "doc_1e46cbc6bfae" + ], + "lineage": [ + "doc_e7d508371e5c" + ], + "lines": [ + "doc_2798948c3080", + "doc_a5120de838fb" + ], + "link": [ + "doc_136d7a909d51", + "doc_3aa0b3792d22" + ], + "linked": [ + "doc_2a18de009d56" + ], + "links": [ + "doc_0383dbf277ba" + ], + "list": [ + "doc_0ac01bce8c79", + "doc_1e46cbc6bfae", + "doc_2a18de009d56", + "doc_464a2b4a8d35", + "doc_4a1cf8a90e83", + "doc_5ee8bbdbd8a8", + "doc_745f751f1344", + "doc_9e9118ed2141", + "doc_a24b92541657", + "doc_a2e84472f1f9", + "doc_b6088784caab", + "doc_c346f13ce597", + "doc_d1f7d83e0824", + "doc_e67e762fb6ab", + "doc_e7d508371e5c", + "doc_e8794369a1a4" + ], + "list_directory": [ + "doc_464a2b4a8d35" + ], + "list_skills": [ + "doc_9e9118ed2141", + "doc_b6088784caab" + ], + "listed": [ + "doc_61fd9f682847", + "doc_c346f13ce597" + ], + "listing": [ + "doc_464a2b4a8d35", + "doc_a5120de838fb" + ], + "listings": [ + "doc_464a2b4a8d35" + ], + "lists": [ + "doc_464a2b4a8d35", + "doc_567be1281e2f", + "doc_a24b92541657" + ], + "litellm": [ + "doc_3f60413b3a06", + "doc_4a1cf8a90e83", + "doc_61fd9f682847", + "doc_f1e32ed2ffce", + "doc_f92685a34f7e" + ], + "litellmprovider": [ + "doc_2a18de009d56" + ], + "lives": [ + "doc_0383dbf277ba", + "doc_78b1717fafe3" + ], + "llm": [ + "doc_0383dbf277ba", + "doc_067033cfc945", + "doc_07174a8633ac", + "doc_0ac01bce8c79", + "doc_136d7a909d51", + "doc_1c5f37ea0ad3", + "doc_1e46cbc6bfae", + "doc_2a18de009d56", + "doc_3aa0b3792d22", + "doc_3f60413b3a06", + "doc_4055659fe573", + "doc_4462f67b1c03", + "doc_464a2b4a8d35", + "doc_4a1cf8a90e83", + "doc_4e1ebaf40267", + "doc_50ac650fd8f6", + "doc_5a65ddab1445", + "doc_5ee8bbdbd8a8", + "doc_618e34441fa7", + "doc_61fd9f682847", + "doc_64090a20f830", + "doc_7320ab982fab", + "doc_745f751f1344", + "doc_78a8125d2c7e", + "doc_78b1717fafe3", + "doc_9e9118ed2141", + "doc_a24b92541657", + "doc_a5120de838fb", + "doc_bbc97cbff254", + "doc_bf91f8d5da74", + "doc_c346f13ce597", + "doc_d147a81bac29", + "doc_d1f7d83e0824", + "doc_deb460ffa5ee", + "doc_e67e762fb6ab", + "doc_e7d508371e5c", + "doc_e8794369a1a4", + "doc_eeed52120ccf", + "doc_f12bbbc027a7", + "doc_f18f523b6ecc", + "doc_f1e32ed2ffce", + "doc_f54217a0f2cf", + "doc_f5f256549d84", + "doc_f92685a34f7e" + ], + "llm-interaction": [ + "doc_3aa0b3792d22" + ], + "llm_called": [ + "doc_f54217a0f2cf" + ], + "llm_calls": [ + "doc_4a1cf8a90e83" + ], + "llm_completed": [ + "doc_f54217a0f2cf" + ], + "llm_failure_policy": [ + "doc_5ee8bbdbd8a8", + "doc_a24b92541657", + "doc_d1f7d83e0824" + ], + "llmbuilder": [ + "doc_2a18de009d56", + "doc_3eeb75d383b8", + "doc_3f60413b3a06", + "doc_5a65ddab1445", + "doc_a5120de838fb", + "doc_bbc97cbff254", + "doc_f92685a34f7e" + ], + "llmchatmiddleware": [ + "doc_c346f13ce597", + "doc_f92685a34f7e" + ], + "llmclient": [ + "doc_2a18de009d56", + "doc_3f60413b3a06", + "doc_eeed52120ccf", + "doc_f92685a34f7e" + ], + "llmconfig": [ + "doc_2a18de009d56" + ], + "llmembedmiddleware": [ + "doc_c346f13ce597", + "doc_f92685a34f7e" + ], + "llmerror": [ + "doc_2a18de009d56" + ], + "llminvalidresponseerror": [ + "doc_3f60413b3a06" + ], + "llmprovider": [ + "doc_2a18de009d56" + ], + "llmrequest": [ + "doc_2a18de009d56", + "doc_3f60413b3a06", + "doc_50ac650fd8f6", + "doc_5ee8bbdbd8a8", + "doc_745f751f1344", + "doc_c346f13ce597", + "doc_e7d508371e5c", + "doc_e8794369a1a4", + "doc_eeed52120ccf", + "doc_f1e32ed2ffce", + "doc_f92685a34f7e" + ], + "llmresponse": [ + "doc_2a18de009d56", + "doc_50ac650fd8f6", + "doc_5ee8bbdbd8a8", + "doc_745f751f1344", + "doc_c346f13ce597", + "doc_e8794369a1a4", + "doc_f1e32ed2ffce", + "doc_f92685a34f7e" + ], + "llmretryableerror": [ + "doc_2a18de009d56" + ], + "llms": [ + "doc_2a18de009d56", + "doc_3aa0b3792d22", + "doc_3eeb75d383b8", + "doc_4e1ebaf40267", + "doc_618e34441fa7", + "doc_a5120de838fb", + "doc_bbc97cbff254", + "doc_c346f13ce597", + "doc_d147a81bac29" + ], + "llmsettings": [ + "doc_3f60413b3a06", + "doc_61fd9f682847" + ], + "llmstreamevent": [ + "doc_c346f13ce597", + "doc_f92685a34f7e" + ], + "llmstreammiddleware": [ + "doc_c346f13ce597", + "doc_f92685a34f7e" + ], + "llmtimeouterror": [ + "doc_2a18de009d56" + ], + "load": [ + "doc_07174a8633ac", + "doc_a5120de838fb" + ], + "loaded": [ + "doc_2798948c3080", + "doc_3f60413b3a06", + "doc_64090a20f830" + ], + "loader": [ + "doc_64090a20f830", + "doc_a5120de838fb" + ], + "loading": [ + "doc_2a18de009d56", + "doc_64090a20f830" + ], + "loads": [ + "doc_07174a8633ac", + "doc_4a1cf8a90e83", + "doc_64090a20f830" + ], + "local": [ + "doc_0383dbf277ba", + "doc_2a18de009d56", + "doc_40e30666bfa8", + "doc_5a65ddab1445", + "doc_a24b92541657", + "doc_a5120de838fb", + "doc_d147a81bac29", + "doc_e34fe24f9585", + "doc_f18f523b6ecc", + "doc_f1e32ed2ffce", + "doc_f5f256549d84" + ], + "localhost": [ + "doc_61fd9f682847" + ], + "locally": [ + "doc_1c5f37ea0ad3", + "doc_5a65ddab1445" + ], + "locking": [ + "doc_a5120de838fb" + ], + "log": [ + "doc_07174a8633ac", + "doc_4055659fe573", + "doc_78a8125d2c7e" + ], + "logged": [ + "doc_4055659fe573", + "doc_4e1ebaf40267" + ], + "logging": [ + "doc_067033cfc945", + "doc_136d7a909d51", + "doc_1e46cbc6bfae", + "doc_745f751f1344", + "doc_9e9118ed2141", + "doc_a24b92541657", + "doc_a5120de838fb", + "doc_c346f13ce597", + "doc_f54217a0f2cf", + "doc_f92685a34f7e" + ], + "logic": [ + "doc_18d38fdacc9e", + "doc_5a65ddab1445", + "doc_e67e762fb6ab" + ], + "logo": [ + "doc_3aa0b3792d22" + ], + "logo-dark": [ + "doc_3aa0b3792d22" + ], + "logo-light": [ + "doc_3aa0b3792d22" + ], + "logs": [ + "doc_07174a8633ac", + "doc_0ac01bce8c79", + "doc_a24b92541657", + "doc_e67e762fb6ab", + "doc_f54217a0f2cf" + ], + "long": [ + "doc_0383dbf277ba", + "doc_067033cfc945", + "doc_07174a8633ac", + "doc_2798948c3080", + "doc_57e2139f0873", + "doc_9e9118ed2141", + "doc_a24b92541657", + "doc_e7d508371e5c", + "doc_f5f256549d84" + ], + "long-running": [ + "doc_0383dbf277ba", + "doc_07174a8633ac", + "doc_1c5f37ea0ad3", + "doc_57e2139f0873", + "doc_78b1717fafe3", + "doc_9e9118ed2141", + "doc_a5120de838fb", + "doc_f5f256549d84", + "doc_fdf48d427fc0" + ], + "long-term": [ + "doc_0383dbf277ba", + "doc_2a18de009d56", + "doc_5a65ddab1445" + ], + "longer": [ + "doc_7320ab982fab" + ], + "longtermmemory": [ + "doc_2a18de009d56" + ], + "looks": [ + "doc_2798948c3080", + "doc_50ac650fd8f6", + "doc_b6088784caab" + ], + "lookup": [ + "doc_07174a8633ac", + "doc_4a1cf8a90e83", + "doc_57e2139f0873", + "doc_745f751f1344" + ], + "loop": [ + "doc_07174a8633ac", + "doc_0ac01bce8c79", + "doc_109157c0d2d7", + "doc_136d7a909d51", + "doc_1e46cbc6bfae", + "doc_3f60413b3a06", + "doc_4a1cf8a90e83", + "doc_4e1ebaf40267", + "doc_50ac650fd8f6", + "doc_5a65ddab1445", + "doc_5ee8bbdbd8a8", + "doc_745f751f1344", + "doc_78a8125d2c7e", + "doc_a24b92541657", + "doc_a5120de838fb", + "doc_b6088784caab", + "doc_bbc97cbff254", + "doc_bf91f8d5da74", + "doc_d147a81bac29", + "doc_deb460ffa5ee", + "doc_e67e762fb6ab", + "doc_e7d508371e5c", + "doc_eeed52120ccf", + "doc_f12bbbc027a7", + "doc_f18f523b6ecc", + "doc_f5f256549d84" + ], + "loops": [ + "doc_18d38fdacc9e", + "doc_50ac650fd8f6", + "doc_5ee8bbdbd8a8", + "doc_618e34441fa7", + "doc_f18f523b6ecc", + "doc_fdf48d427fc0" + ], + "loses": [ + "doc_136d7a909d51" + ], + "losing": [ + "doc_07174a8633ac" + ], + "lost": [ + "doc_0383dbf277ba", + "doc_75887f91c7df" + ], + "low": [ + "doc_e67e762fb6ab", + "doc_eeed52120ccf" + ], + "lower": [ + "doc_7320ab982fab" + ], + "lower-level": [ + "doc_a24b92541657" + ], + "lowercase": [ + "doc_64090a20f830" + ], + "lowest": [ + "doc_eeed52120ccf" + ], + "ls": [ + "doc_a24b92541657" + ], + "machine": [ + "doc_07174a8633ac", + "doc_e7d508371e5c" + ], + "made": [ + "doc_1e46cbc6bfae", + "doc_4a1cf8a90e83", + "doc_5ee8bbdbd8a8", + "doc_f54217a0f2cf" + ], + "main": [ + "doc_745f751f1344", + "doc_c346f13ce597", + "doc_f18f523b6ecc" + ], + "maintain": [ + "doc_3aa0b3792d22", + "doc_5ee8bbdbd8a8", + "doc_7b26cbd9a7ae", + "doc_e7d508371e5c" + ], + "maintainer": [ + "doc_3ae3aaf0c12c", + "doc_3eeb75d383b8", + "doc_d147a81bac29", + "doc_f18f523b6ecc" + ], + "maintainers": [ + "doc_3eeb75d383b8" + ], + "maintains": [ + "doc_5ee8bbdbd8a8" + ], + "major": [ + "doc_a5120de838fb" + ], + "majority": [ + "doc_f12bbbc027a7" + ], + "make": [ + "doc_3ae3aaf0c12c", + "doc_5ee8bbdbd8a8", + "doc_618e34441fa7", + "doc_a24b92541657", + "doc_d147a81bac29", + "doc_e34fe24f9585", + "doc_e67e762fb6ab", + "doc_f5f256549d84" + ], + "makes": [ + "doc_1e46cbc6bfae", + "doc_43ad71a4ad0c", + "doc_f12bbbc027a7" + ], + "making": [ + "doc_136d7a909d51", + "doc_3eeb75d383b8", + "doc_4a1cf8a90e83" + ], + "malformed": [ + "doc_4a1cf8a90e83", + "doc_4e1ebaf40267", + "doc_50ac650fd8f6" + ], + "manage": [ + "doc_50ac650fd8f6", + "doc_78b1717fafe3" + ], + "managed": [ + "doc_d6ad61a79371", + "doc_e67e762fb6ab" + ], + "management": [ + "doc_2a18de009d56", + "doc_4055659fe573", + "doc_4e1ebaf40267", + "doc_78b1717fafe3", + "doc_7b26cbd9a7ae", + "doc_a5120de838fb", + "doc_e67e762fb6ab" + ], + "manager": [ + "doc_067033cfc945" + ], + "managers": [ + "doc_067033cfc945" + ], + "manages": [ + "doc_4e1ebaf40267", + "doc_a2e84472f1f9", + "doc_e7d508371e5c" + ], + "managing": [ + "doc_7320ab982fab" + ], + "manifests": [ + "doc_5ee8bbdbd8a8" + ], + "manual": [ + "doc_75887f91c7df", + "doc_78b1717fafe3" + ], + "many": [ + "doc_136d7a909d51", + "doc_18d38fdacc9e", + "doc_4a1cf8a90e83", + "doc_d1f7d83e0824" + ], + "map": [ + "doc_2a18de009d56", + "doc_618e34441fa7", + "doc_745f751f1344", + "doc_78a8125d2c7e", + "doc_bbc97cbff254", + "doc_d147a81bac29" + ], + "mapping": [ + "doc_5ee8bbdbd8a8" + ], + "maps": [ + "doc_5ee8bbdbd8a8", + "doc_618e34441fa7" + ], + "markdown": [ + "doc_b6088784caab" + ], + "marked": [ + "doc_618e34441fa7", + "doc_a24b92541657" + ], + "markers": [ + "doc_2a18de009d56", + "doc_3f1383f72c5f", + "doc_5ee8bbdbd8a8" + ], + "marks": [ + "doc_75887f91c7df", + "doc_a24b92541657" + ], + "masked": [ + "doc_3f1383f72c5f" + ], + "match": [ + "doc_07174a8633ac", + "doc_0ac01bce8c79", + "doc_3f60413b3a06", + "doc_618e34441fa7", + "doc_745f751f1344" + ], + "matched_rules": [ + "doc_f54217a0f2cf" + ], + "matches": [ + "doc_50ac650fd8f6", + "doc_618e34441fa7", + "doc_d6ad61a79371" + ], + "matching": [ + "doc_07174a8633ac", + "doc_4a1cf8a90e83", + "doc_618e34441fa7", + "doc_eeed52120ccf" + ], + "matrix": [ + "doc_4055659fe573", + "doc_5ee8bbdbd8a8", + "doc_745f751f1344", + "doc_d1f7d83e0824", + "doc_fdf48d427fc0" + ], + "matter": [ + "doc_4a1cf8a90e83" + ], + "matters": [ + "doc_618e34441fa7", + "doc_deb460ffa5ee" + ], + "mature": [ + "doc_50ac650fd8f6" + ], + "maturity": [ + "doc_f12bbbc027a7" + ], + "max": [ + "doc_61fd9f682847", + "doc_78b1717fafe3", + "doc_a24b92541657", + "doc_e67e762fb6ab", + "doc_e8794369a1a4" + ], + "max_age_ms": [ + "doc_07174a8633ac" + ], + "max_chars": [ + "doc_464a2b4a8d35" + ], + "max_connections": [ + "doc_4462f67b1c03" + ], + "max_entries": [ + "doc_07174a8633ac", + "doc_464a2b4a8d35" + ], + "max_idle_connections": [ + "doc_4462f67b1c03" + ], + "max_llm_calls": [ + "doc_a24b92541657", + "doc_f5f256549d84", + "doc_fdf48d427fc0" + ], + "max_output_chars": [ + "doc_745f751f1344", + "doc_a24b92541657" + ], + "max_parallel_subagents_global": [ + "doc_a24b92541657" + ], + "max_parallel_subagents_per_parent": [ + "doc_a24b92541657" + ], + "max_parallel_subagents_per_target_agent": [ + "doc_a24b92541657" + ], + "max_parallel_tools": [ + "doc_a24b92541657" + ], + "max_retries": [ + "doc_7320ab982fab" + ], + "max_steps": [ + "doc_4055659fe573", + "doc_567be1281e2f", + "doc_a24b92541657", + "doc_e67e762fb6ab", + "doc_f5f256549d84", + "doc_fdf48d427fc0" + ], + "max_subagent_depth": [ + "doc_a24b92541657" + ], + "max_subagent_fanout_per_step": [ + "doc_a24b92541657" + ], + "max_thinking_tokens": [ + "doc_a24b92541657" + ], + "max_tokens": [ + "doc_3f60413b3a06", + "doc_5ee8bbdbd8a8", + "doc_e8794369a1a4", + "doc_eeed52120ccf" + ], + "max_tool_calls": [ + "doc_4055659fe573", + "doc_43ad71a4ad0c", + "doc_567be1281e2f", + "doc_a24b92541657" + ], + "max_total_cost_usd": [ + "doc_067033cfc945", + "doc_18d38fdacc9e", + "doc_4055659fe573", + "doc_43ad71a4ad0c", + "doc_50ac650fd8f6", + "doc_567be1281e2f", + "doc_a24b92541657", + "doc_bbc97cbff254", + "doc_c93c115aeb85", + "doc_e67e762fb6ab", + "doc_fdf48d427fc0" + ], + "max_wall_time_s": [ + "doc_4055659fe573", + "doc_a24b92541657", + "doc_f5f256549d84", + "doc_fdf48d427fc0" + ], + "maximum": [ + "doc_4462f67b1c03", + "doc_464a2b4a8d35", + "doc_5ee8bbdbd8a8", + "doc_61fd9f682847", + "doc_a24b92541657", + "doc_e67e762fb6ab" + ], + "may": [ + "doc_0383dbf277ba", + "doc_0ac01bce8c79", + "doc_3eeb75d383b8", + "doc_4055659fe573", + "doc_4a1cf8a90e83", + "doc_618e34441fa7", + "doc_745f751f1344", + "doc_78b1717fafe3", + "doc_bbc97cbff254", + "doc_d147a81bac29", + "doc_f18f523b6ecc", + "doc_f54217a0f2cf" + ], + "mcp": [ + "doc_2a18de009d56", + "doc_3eeb75d383b8", + "doc_40e30666bfa8", + "doc_567be1281e2f", + "doc_57e2139f0873", + "doc_61fd9f682847", + "doc_745f751f1344", + "doc_a24b92541657", + "doc_a5120de838fb", + "doc_bbc97cbff254", + "doc_d147a81bac29", + "doc_deb460ffa5ee", + "doc_e34fe24f9585", + "doc_e67e762fb6ab", + "doc_f18f523b6ecc", + "doc_f2ae50c638a4" + ], + "mcp-compatible": [ + "doc_e34fe24f9585" + ], + "mcp-discovered": [ + "doc_e67e762fb6ab" + ], + "mcp-exposed": [ + "doc_e34fe24f9585" + ], + "mcp-server": [ + "doc_3aa0b3792d22" + ], + "mcp-sourced": [ + "doc_40e30666bfa8" + ], + "mcp_client_integration": [ + "doc_3aa0b3792d22" + ], + "mcp_servers": [ + "doc_a24b92541657", + "doc_e67e762fb6ab" + ], + "mcpserver": [ + "doc_bbc97cbff254" + ], + "mcpserverlike": [ + "doc_e67e762fb6ab" + ], + "md": [ + "doc_2798948c3080", + "doc_2a18de009d56", + "doc_64090a20f830", + "doc_9e9118ed2141", + "doc_b6088784caab", + "doc_e67e762fb6ab" + ], + "meaning": [ + "doc_4055659fe573", + "doc_567be1281e2f", + "doc_78b1717fafe3", + "doc_bbc97cbff254", + "doc_bf91f8d5da74", + "doc_e7d508371e5c" + ], + "means": [ + "doc_4e1ebaf40267", + "doc_50ac650fd8f6", + "doc_618e34441fa7", + "doc_64090a20f830" + ], + "measure": [ + "doc_f5f256549d84" + ], + "measurement": [ + "doc_f5f256549d84" + ], + "mechanism": [ + "doc_1c5f37ea0ad3", + "doc_4a1cf8a90e83", + "doc_d6ad61a79371" + ], + "median": [ + "doc_78a8125d2c7e" + ], + "medium": [ + "doc_78a8125d2c7e", + "doc_e67e762fb6ab" + ], + "memories": [ + "doc_0383dbf277ba" + ], + "memory": [ + "doc_0383dbf277ba", + "doc_07174a8633ac", + "doc_136d7a909d51", + "doc_18d38fdacc9e", + "doc_1e46cbc6bfae", + "doc_2a18de009d56", + "doc_3aa0b3792d22", + "doc_3ae3aaf0c12c", + "doc_3eeb75d383b8", + "doc_3f1383f72c5f", + "doc_3f60413b3a06", + "doc_4055659fe573", + "doc_4462f67b1c03", + "doc_4a1cf8a90e83", + "doc_4e1ebaf40267", + "doc_50ac650fd8f6", + "doc_5a65ddab1445", + "doc_618e34441fa7", + "doc_61fd9f682847", + "doc_64090a20f830", + "doc_75887f91c7df", + "doc_78b1717fafe3", + "doc_7b26cbd9a7ae", + "doc_a24b92541657", + "doc_a5120de838fb", + "doc_bbc97cbff254", + "doc_bf91f8d5da74", + "doc_d147a81bac29", + "doc_deb460ffa5ee", + "doc_e7d508371e5c", + "doc_f18f523b6ecc", + "doc_f2ae50c638a4", + "doc_f54217a0f2cf", + "doc_f5f256549d84" + ], + "memory_store": [ + "doc_61fd9f682847", + "doc_a24b92541657", + "doc_f5f256549d84" + ], + "memorycapabilities": [ + "doc_2a18de009d56" + ], + "memorycompactionresult": [ + "doc_07174a8633ac", + "doc_2a18de009d56", + "doc_bbc97cbff254" + ], + "memoryevent": [ + "doc_2a18de009d56", + "doc_5a65ddab1445" + ], + "memorystore": [ + "doc_0383dbf277ba", + "doc_2a18de009d56", + "doc_5a65ddab1445", + "doc_a24b92541657", + "doc_f5f256549d84" + ], + "mental-model": [ + "doc_3aa0b3792d22" + ], + "mention": [ + "doc_136d7a909d51" + ], + "merge": [ + "doc_a5120de838fb" + ], + "merged": [ + "doc_07174a8633ac", + "doc_4a1cf8a90e83", + "doc_a24b92541657", + "doc_e67e762fb6ab" + ], + "merging": [ + "doc_3eeb75d383b8", + "doc_618e34441fa7" + ], + "meshes": [ + "doc_57e2139f0873" + ], + "message": [ + "doc_07174a8633ac", + "doc_0ac01bce8c79", + "doc_1e46cbc6bfae", + "doc_2a18de009d56", + "doc_3f1383f72c5f", + "doc_5a65ddab1445", + "doc_5ee8bbdbd8a8", + "doc_745f751f1344", + "doc_75887f91c7df", + "doc_78b1717fafe3", + "doc_9e9118ed2141", + "doc_a24b92541657", + "doc_a2e84472f1f9", + "doc_c93c115aeb85", + "doc_d6ad61a79371", + "doc_e7d508371e5c", + "doc_e8794369a1a4", + "doc_eeed52120ccf", + "doc_f54217a0f2cf" + ], + "message_count": [ + "doc_4a1cf8a90e83" + ], + "message_type": [ + "doc_75887f91c7df" + ], + "messages": [ + "doc_0383dbf277ba", + "doc_136d7a909d51", + "doc_4a1cf8a90e83", + "doc_50ac650fd8f6", + "doc_5ee8bbdbd8a8", + "doc_75887f91c7df", + "doc_e8794369a1a4", + "doc_eeed52120ccf" + ], + "messaging": [ + "doc_1c5f37ea0ad3", + "doc_2a18de009d56", + "doc_3aa0b3792d22", + "doc_3eeb75d383b8", + "doc_4e1ebaf40267", + "doc_75887f91c7df", + "doc_bbc97cbff254", + "doc_d147a81bac29" + ], + "metadata": [ + "doc_07174a8633ac", + "doc_2a18de009d56", + "doc_3eeb75d383b8", + "doc_3f1383f72c5f", + "doc_567be1281e2f", + "doc_5ee8bbdbd8a8", + "doc_64090a20f830", + "doc_75887f91c7df", + "doc_78a8125d2c7e", + "doc_9e9118ed2141", + "doc_a24b92541657", + "doc_c346f13ce597", + "doc_d147a81bac29", + "doc_d1f7d83e0824", + "doc_e7d508371e5c" + ], + "method": [ + "doc_07174a8633ac", + "doc_3f60413b3a06", + "doc_bbc97cbff254" + ], + "method-chaining": [ + "doc_3f60413b3a06" + ], + "methods": [ + "doc_07174a8633ac", + "doc_2798948c3080", + "doc_3f60413b3a06", + "doc_bbc97cbff254" + ], + "metric": [ + "doc_067033cfc945", + "doc_0ac01bce8c79", + "doc_2a18de009d56", + "doc_618e34441fa7", + "doc_78a8125d2c7e" + ], + "metric_agent_llm_calls_total": [ + "doc_2a18de009d56" + ], + "metrics": [ + "doc_067033cfc945", + "doc_2a18de009d56", + "doc_3ae3aaf0c12c", + "doc_4e1ebaf40267", + "doc_50ac650fd8f6", + "doc_618e34441fa7", + "doc_78a8125d2c7e", + "doc_c93c115aeb85", + "doc_fdf48d427fc0" + ], + "microservice": [ + "doc_57e2139f0873" + ], + "mid-run": [ + "doc_4a1cf8a90e83" + ], + "mid-stream": [ + "doc_7b26cbd9a7ae" + ], + "middleware": [ + "doc_2a18de009d56", + "doc_3f60413b3a06", + "doc_4462f67b1c03", + "doc_50ac650fd8f6", + "doc_5a65ddab1445", + "doc_5ee8bbdbd8a8", + "doc_618e34441fa7", + "doc_745f751f1344", + "doc_9e9118ed2141", + "doc_a24b92541657", + "doc_a5120de838fb", + "doc_c346f13ce597", + "doc_deb460ffa5ee", + "doc_f2ae50c638a4", + "doc_f92685a34f7e" + ], + "middlewares": [ + "doc_a24b92541657" + ], + "middlewarestack": [ + "doc_2a18de009d56" + ], + "might": [ + "doc_0383dbf277ba" + ], + "migrate": [ + "doc_5a65ddab1445" + ], + "migration": [ + "doc_2a18de009d56", + "doc_3aa0b3792d22", + "doc_3eeb75d383b8", + "doc_4a1cf8a90e83", + "doc_5a65ddab1445" + ], + "milliseconds": [ + "doc_0ac01bce8c79", + "doc_4a1cf8a90e83", + "doc_745f751f1344", + "doc_75887f91c7df", + "doc_a2e84472f1f9" + ], + "min": [ + "doc_7320ab982fab", + "doc_78a8125d2c7e" + ], + "mini": [ + "doc_1e46cbc6bfae", + "doc_5ee8bbdbd8a8", + "doc_61fd9f682847", + "doc_d1f7d83e0824", + "doc_eeed52120ccf", + "doc_f92685a34f7e" + ], + "minimal": [ + "doc_136d7a909d51", + "doc_a5120de838fb" + ], + "minimal_chat_agent": [ + "doc_3aa0b3792d22" + ], + "minimum": [ + "doc_61fd9f682847" + ], + "mint": [ + "doc_3aa0b3792d22" + ], + "mintlify": [ + "doc_3aa0b3792d22" + ], + "minutes": [ + "doc_0383dbf277ba", + "doc_18d38fdacc9e", + "doc_57e2139f0873", + "doc_5a65ddab1445", + "doc_e67e762fb6ab", + "doc_f18f523b6ecc" + ], + "misplaced": [ + "doc_618e34441fa7" + ], + "missing": [ + "doc_136d7a909d51", + "doc_18d38fdacc9e", + "doc_2798948c3080", + "doc_4a1cf8a90e83", + "doc_78b1717fafe3" + ], + "mistake": [ + "doc_18d38fdacc9e", + "doc_3ae3aaf0c12c", + "doc_57e2139f0873", + "doc_a24b92541657" + ], + "mistakes": [ + "doc_18d38fdacc9e", + "doc_3ae3aaf0c12c" + ], + "mistral": [ + "doc_f1e32ed2ffce" + ], + "mitigation": [ + "doc_567be1281e2f" + ], + "mixing": [ + "doc_18d38fdacc9e", + "doc_40e30666bfa8" + ], + "mocked": [ + "doc_618e34441fa7" + ], + "mocking": [ + "doc_618e34441fa7" + ], + "mode": [ + "doc_0383dbf277ba", + "doc_136d7a909d51", + "doc_1e46cbc6bfae", + "doc_3f1383f72c5f", + "doc_43ad71a4ad0c", + "doc_4e1ebaf40267", + "doc_78a8125d2c7e", + "doc_d6ad61a79371" + ], + "model": [ + "doc_0ac01bce8c79", + "doc_109157c0d2d7", + "doc_136d7a909d51", + "doc_18d38fdacc9e", + "doc_1c5f37ea0ad3", + "doc_1e46cbc6bfae", + "doc_2a18de009d56", + "doc_3ae3aaf0c12c", + "doc_3f60413b3a06", + "doc_4055659fe573", + "doc_40e30666bfa8", + "doc_43ad71a4ad0c", + "doc_464a2b4a8d35", + "doc_4a1cf8a90e83", + "doc_4e1ebaf40267", + "doc_50ac650fd8f6", + "doc_567be1281e2f", + "doc_57e2139f0873", + "doc_5ee8bbdbd8a8", + "doc_61fd9f682847", + "doc_64090a20f830", + "doc_7320ab982fab", + "doc_745f751f1344", + "doc_78b1717fafe3", + "doc_9e9118ed2141", + "doc_a24b92541657", + "doc_a2e84472f1f9", + "doc_a5120de838fb", + "doc_b6088784caab", + "doc_bbc97cbff254", + "doc_bf91f8d5da74", + "doc_c346f13ce597", + "doc_d1f7d83e0824", + "doc_d6ad61a79371", + "doc_deb460ffa5ee", + "doc_e34fe24f9585", + "doc_e67e762fb6ab", + "doc_e7d508371e5c", + "doc_e8794369a1a4", + "doc_eeed52120ccf", + "doc_f12bbbc027a7", + "doc_f18f523b6ecc", + "doc_f54217a0f2cf", + "doc_f5f256549d84", + "doc_f92685a34f7e" + ], + "model-generated": [ + "doc_5ee8bbdbd8a8" + ], + "model-provided": [ + "doc_bf91f8d5da74" + ], + "model-visible": [ + "doc_a24b92541657" + ], + "model_name": [ + "doc_2798948c3080" + ], + "model_resolver": [ + "doc_5ee8bbdbd8a8", + "doc_a24b92541657", + "doc_e67e762fb6ab", + "doc_eeed52120ccf" + ], + "modelnotfounderror": [ + "doc_136d7a909d51" + ], + "models": [ + "doc_0ac01bce8c79", + "doc_18d38fdacc9e", + "doc_2a18de009d56", + "doc_3ae3aaf0c12c", + "doc_3eeb75d383b8", + "doc_43ad71a4ad0c", + "doc_4e1ebaf40267", + "doc_50ac650fd8f6", + "doc_57e2139f0873", + "doc_618e34441fa7", + "doc_7320ab982fab", + "doc_a24b92541657", + "doc_a5120de838fb", + "doc_bf91f8d5da74", + "doc_c346f13ce597", + "doc_d1f7d83e0824", + "doc_e67e762fb6ab", + "doc_f1e32ed2ffce", + "doc_f5f256549d84", + "doc_f92685a34f7e", + "doc_fdf48d427fc0" + ], + "modes": [ + "doc_109157c0d2d7", + "doc_3f1383f72c5f", + "doc_5a65ddab1445", + "doc_a5120de838fb", + "doc_d6ad61a79371", + "doc_e67e762fb6ab", + "doc_e7d508371e5c" + ], + "modify": [ + "doc_c346f13ce597" + ], + "modify_": [ + "doc_43ad71a4ad0c" + ], + "modifying": [ + "doc_d6ad61a79371" + ], + "module": [ + "doc_2a18de009d56", + "doc_4e1ebaf40267", + "doc_9e9118ed2141", + "doc_bbc97cbff254", + "doc_d147a81bac29" + ], + "modules": [ + "doc_2a18de009d56", + "doc_3eeb75d383b8", + "doc_4e1ebaf40267", + "doc_618e34441fa7", + "doc_bbc97cbff254", + "doc_f18f523b6ecc" + ], + "moment": [ + "doc_0ac01bce8c79" + ], + "money": [ + "doc_d6ad61a79371" + ], + "mongo": [ + "doc_2a18de009d56" + ], + "monitor": [ + "doc_3ae3aaf0c12c", + "doc_7320ab982fab", + "doc_78b1717fafe3", + "doc_f5f256549d84", + "doc_fdf48d427fc0" + ], + "monitoring": [ + "doc_067033cfc945", + "doc_18d38fdacc9e", + "doc_2a18de009d56", + "doc_4e1ebaf40267", + "doc_567be1281e2f", + "doc_78a8125d2c7e", + "doc_a5120de838fb", + "doc_b6088784caab", + "doc_d1f7d83e0824", + "doc_fdf48d427fc0" + ], + "more": [ + "doc_0ac01bce8c79", + "doc_18d38fdacc9e", + "doc_61fd9f682847", + "doc_745f751f1344", + "doc_a24b92541657", + "doc_a2e84472f1f9", + "doc_eeed52120ccf", + "doc_f12bbbc027a7", + "doc_f18f523b6ecc" + ], + "most": [ + "doc_0383dbf277ba", + "doc_07174a8633ac", + "doc_18d38fdacc9e", + "doc_43ad71a4ad0c", + "doc_4a1cf8a90e83", + "doc_57e2139f0873", + "doc_618e34441fa7", + "doc_7b26cbd9a7ae", + "doc_bbc97cbff254", + "doc_e67e762fb6ab", + "doc_f2ae50c638a4", + "doc_fdf48d427fc0" + ], + "move": [ + "doc_3ae3aaf0c12c", + "doc_50ac650fd8f6", + "doc_57e2139f0873", + "doc_f5f256549d84" + ], + "moved": [ + "doc_f54217a0f2cf" + ], + "moves": [ + "doc_75887f91c7df", + "doc_78b1717fafe3" + ], + "moving": [ + "doc_f5f256549d84" + ], + "mtime": [ + "doc_64090a20f830" + ], + "much": [ + "doc_136d7a909d51" + ], + "multi-agent": [ + "doc_3ae3aaf0c12c", + "doc_57e2139f0873", + "doc_a5120de838fb", + "doc_d1f7d83e0824", + "doc_e67e762fb6ab", + "doc_f12bbbc027a7" + ], + "multi-model": [ + "doc_a5120de838fb" + ], + "multi-process": [ + "doc_0383dbf277ba", + "doc_78b1717fafe3" + ], + "multi-stage": [ + "doc_067033cfc945" + ], + "multi-step": [ + "doc_eeed52120ccf" + ], + "multi-turn": [ + "doc_0383dbf277ba", + "doc_18d38fdacc9e", + "doc_3f60413b3a06", + "doc_7b26cbd9a7ae", + "doc_a5120de838fb" + ], + "multi-worker": [ + "doc_067033cfc945", + "doc_78b1717fafe3" + ], + "multi_model_fallback": [ + "doc_3aa0b3792d22" + ], + "multiple": [ + "doc_0ac01bce8c79", + "doc_136d7a909d51", + "doc_2798948c3080", + "doc_43ad71a4ad0c", + "doc_4a1cf8a90e83", + "doc_5a65ddab1445", + "doc_64090a20f830", + "doc_eeed52120ccf", + "doc_f12bbbc027a7", + "doc_fdf48d427fc0" + ], + "must": [ + "doc_07174a8633ac", + "doc_3ae3aaf0c12c", + "doc_4a1cf8a90e83", + "doc_567be1281e2f", + "doc_64090a20f830", + "doc_745f751f1344", + "doc_75887f91c7df", + "doc_9e9118ed2141", + "doc_a24b92541657", + "doc_c346f13ce597", + "doc_d147a81bac29", + "doc_f12bbbc027a7", + "doc_f18f523b6ecc" + ], + "mutating": [ + "doc_18d38fdacc9e", + "doc_3ae3aaf0c12c", + "doc_43ad71a4ad0c", + "doc_567be1281e2f", + "doc_9e9118ed2141" + ], + "mutations": [ + "doc_50ac650fd8f6", + "doc_b6088784caab" + ], + "mw": [ + "doc_9e9118ed2141" + ], + "my": [ + "doc_50ac650fd8f6" + ], + "name": [ + "doc_18d38fdacc9e", + "doc_1e46cbc6bfae", + "doc_2798948c3080", + "doc_2a18de009d56", + "doc_3aa0b3792d22", + "doc_3f60413b3a06", + "doc_43ad71a4ad0c", + "doc_464a2b4a8d35", + "doc_4a1cf8a90e83", + "doc_5ee8bbdbd8a8", + "doc_61fd9f682847", + "doc_64090a20f830", + "doc_745f751f1344", + "doc_75887f91c7df", + "doc_9e9118ed2141", + "doc_a24b92541657", + "doc_a2e84472f1f9", + "doc_b6088784caab", + "doc_bbc97cbff254", + "doc_e67e762fb6ab", + "doc_e7d508371e5c", + "doc_eeed52120ccf", + "doc_f18f523b6ecc" + ], + "name-to-filename": [ + "doc_64090a20f830" + ], + "named": [ + "doc_3f60413b3a06", + "doc_9e9118ed2141", + "doc_c93c115aeb85" + ], + "names": [ + "doc_3eeb75d383b8", + "doc_464a2b4a8d35", + "doc_618e34441fa7", + "doc_64090a20f830", + "doc_a24b92541657", + "doc_e67e762fb6ab", + "doc_eeed52120ccf" + ], + "namespace": [ + "doc_7320ab982fab" + ], + "nano": [ + "doc_d1f7d83e0824", + "doc_eeed52120ccf", + "doc_f92685a34f7e" + ], + "narrow": [ + "doc_18d38fdacc9e", + "doc_3ae3aaf0c12c", + "doc_618e34441fa7", + "doc_f5f256549d84" + ], + "native": [ + "doc_3f60413b3a06", + "doc_e8794369a1a4" + ], + "natural": [ + "doc_07174a8633ac" + ], + "navigates": [ + "doc_7b26cbd9a7ae" + ], + "navigation": [ + "doc_3aa0b3792d22", + "doc_3eeb75d383b8", + "doc_d147a81bac29", + "doc_f18f523b6ecc" + ], + "nbsp": [ + "doc_57e2139f0873" + ], + "necessarily": [ + "doc_109157c0d2d7" + ], + "necessary": [ + "doc_067033cfc945", + "doc_618e34441fa7" + ], + "need": [ + "doc_1c5f37ea0ad3", + "doc_1e46cbc6bfae", + "doc_2a18de009d56", + "doc_3f60413b3a06", + "doc_4e1ebaf40267", + "doc_50ac650fd8f6", + "doc_57e2139f0873", + "doc_618e34441fa7", + "doc_61fd9f682847", + "doc_64090a20f830", + "doc_75887f91c7df", + "doc_7b26cbd9a7ae", + "doc_a5120de838fb", + "doc_b6088784caab", + "doc_deb460ffa5ee", + "doc_e67e762fb6ab", + "doc_f12bbbc027a7", + "doc_f18f523b6ecc", + "doc_f2ae50c638a4", + "doc_f5f256549d84", + "doc_fdf48d427fc0" + ], + "needed": [ + "doc_07174a8633ac", + "doc_18d38fdacc9e", + "doc_4a1cf8a90e83", + "doc_5a65ddab1445", + "doc_d1f7d83e0824", + "doc_d6ad61a79371", + "doc_f12bbbc027a7" + ], + "needs": [ + "doc_07174a8633ac", + "doc_136d7a909d51", + "doc_3ae3aaf0c12c", + "doc_3f60413b3a06", + "doc_57e2139f0873", + "doc_78b1717fafe3", + "doc_b6088784caab", + "doc_e67e762fb6ab", + "doc_f12bbbc027a7", + "doc_f18f523b6ecc" + ], + "negligible": [ + "doc_64090a20f830" + ], + "neither": [ + "doc_64090a20f830" + ], + "nested": [ + "doc_e7d508371e5c" + ], + "nesting": [ + "doc_e7d508371e5c" + ], + "network": [ + "doc_1e46cbc6bfae", + "doc_78b1717fafe3", + "doc_a24b92541657", + "doc_f18f523b6ecc" + ], + "never": [ + "doc_067033cfc945", + "doc_0ac01bce8c79", + "doc_1c5f37ea0ad3", + "doc_43ad71a4ad0c", + "doc_4a1cf8a90e83", + "doc_4e1ebaf40267", + "doc_745f751f1344", + "doc_a24b92541657", + "doc_e8794369a1a4", + "doc_f18f523b6ecc", + "doc_f92685a34f7e" + ], + "new": [ + "doc_109157c0d2d7", + "doc_1e46cbc6bfae", + "doc_2a18de009d56", + "doc_4462f67b1c03", + "doc_5ee8bbdbd8a8", + "doc_618e34441fa7", + "doc_a5120de838fb", + "doc_d147a81bac29", + "doc_f54217a0f2cf" + ], + "next": [ + "doc_0383dbf277ba", + "doc_067033cfc945", + "doc_07174a8633ac", + "doc_109157c0d2d7", + "doc_136d7a909d51", + "doc_18d38fdacc9e", + "doc_1c5f37ea0ad3", + "doc_2798948c3080", + "doc_3ae3aaf0c12c", + "doc_3f1383f72c5f", + "doc_4055659fe573", + "doc_40e30666bfa8", + "doc_4462f67b1c03", + "doc_464a2b4a8d35", + "doc_4e1ebaf40267", + "doc_567be1281e2f", + "doc_57e2139f0873", + "doc_5a65ddab1445", + "doc_5ee8bbdbd8a8", + "doc_61fd9f682847", + "doc_64090a20f830", + "doc_7320ab982fab", + "doc_745f751f1344", + "doc_75887f91c7df", + "doc_78a8125d2c7e", + "doc_78b1717fafe3", + "doc_7b26cbd9a7ae", + "doc_9e9118ed2141", + "doc_a24b92541657", + "doc_b6088784caab", + "doc_c346f13ce597", + "doc_c93c115aeb85", + "doc_d147a81bac29", + "doc_d1f7d83e0824", + "doc_deb460ffa5ee", + "doc_e34fe24f9585", + "doc_e67e762fb6ab", + "doc_e7d508371e5c", + "doc_e8794369a1a4", + "doc_eeed52120ccf", + "doc_f12bbbc027a7", + "doc_f18f523b6ecc", + "doc_f1e32ed2ffce", + "doc_f2ae50c638a4", + "doc_f92685a34f7e", + "doc_fdf48d427fc0" + ], + "nice-to-have": [ + "doc_f12bbbc027a7" + ], + "no": [ + "doc_0383dbf277ba", + "doc_067033cfc945", + "doc_07174a8633ac", + "doc_0ac01bce8c79", + "doc_136d7a909d51", + "doc_18d38fdacc9e", + "doc_1e46cbc6bfae", + "doc_3f60413b3a06", + "doc_4055659fe573", + "doc_4a1cf8a90e83", + "doc_4e1ebaf40267", + "doc_50ac650fd8f6", + "doc_567be1281e2f", + "doc_57e2139f0873", + "doc_5a65ddab1445", + "doc_5ee8bbdbd8a8", + "doc_61fd9f682847", + "doc_64090a20f830", + "doc_75887f91c7df", + "doc_78b1717fafe3", + "doc_a24b92541657", + "doc_d6ad61a79371" + ], + "node": [ + "doc_618e34441fa7" + ], + "nodes": [ + "doc_618e34441fa7", + "doc_a24b92541657" + ], + "non-alphanumeric": [ + "doc_64090a20f830" + ], + "non-critical": [ + "doc_18d38fdacc9e" + ], + "non-deterministic": [ + "doc_136d7a909d51", + "doc_618e34441fa7" + ], + "non-developers": [ + "doc_64090a20f830" + ], + "non-dict": [ + "doc_745f751f1344" + ], + "non-empty": [ + "doc_2798948c3080", + "doc_64090a20f830" + ], + "non-essential": [ + "doc_4055659fe573" + ], + "non-fatal": [ + "doc_0ac01bce8c79", + "doc_4055659fe573", + "doc_4e1ebaf40267", + "doc_50ac650fd8f6", + "doc_78b1717fafe3", + "doc_f54217a0f2cf" + ], + "non-headless": [ + "doc_a24b92541657" + ], + "non-openai": [ + "doc_f92685a34f7e" + ], + "non-streaming": [ + "doc_109157c0d2d7", + "doc_5ee8bbdbd8a8", + "doc_c346f13ce597", + "doc_eeed52120ccf", + "doc_f92685a34f7e" + ], + "non-terminal": [ + "doc_4a1cf8a90e83" + ], + "non-throwing": [ + "doc_745f751f1344" + ], + "none": [ + "doc_07174a8633ac", + "doc_4462f67b1c03", + "doc_4a1cf8a90e83", + "doc_567be1281e2f", + "doc_5ee8bbdbd8a8", + "doc_61fd9f682847", + "doc_7320ab982fab", + "doc_745f751f1344", + "doc_a24b92541657", + "doc_a2e84472f1f9", + "doc_d1f7d83e0824", + "doc_e67e762fb6ab", + "doc_e7d508371e5c", + "doc_e8794369a1a4", + "doc_f54217a0f2cf" + ], + "normal": [ + "doc_4a1cf8a90e83", + "doc_c93c115aeb85" + ], + "normalization": [ + "doc_4a1cf8a90e83" + ], + "normalize": [ + "doc_e8794369a1a4" + ], + "normalize_checkpoint_record": [ + "doc_4a1cf8a90e83" + ], + "normalized": [ + "doc_5ee8bbdbd8a8", + "doc_bbc97cbff254", + "doc_e8794369a1a4", + "doc_f1e32ed2ffce" + ], + "normalized_model": [ + "doc_4a1cf8a90e83", + "doc_bbc97cbff254" + ], + "normalizes": [ + "doc_4a1cf8a90e83", + "doc_5ee8bbdbd8a8", + "doc_64090a20f830", + "doc_f92685a34f7e" + ], + "normally": [ + "doc_0ac01bce8c79", + "doc_d6ad61a79371" + ], + "not": [ + "doc_0383dbf277ba", + "doc_07174a8633ac", + "doc_109157c0d2d7", + "doc_136d7a909d51", + "doc_18d38fdacc9e", + "doc_1e46cbc6bfae", + "doc_2798948c3080", + "doc_2a18de009d56", + "doc_3eeb75d383b8", + "doc_3f60413b3a06", + "doc_4055659fe573", + "doc_4a1cf8a90e83", + "doc_4e1ebaf40267", + "doc_50ac650fd8f6", + "doc_567be1281e2f", + "doc_57e2139f0873", + "doc_5a65ddab1445", + "doc_5ee8bbdbd8a8", + "doc_618e34441fa7", + "doc_61fd9f682847", + "doc_64090a20f830", + "doc_7320ab982fab", + "doc_745f751f1344", + "doc_75887f91c7df", + "doc_b6088784caab", + "doc_bbc97cbff254", + "doc_c346f13ce597", + "doc_e67e762fb6ab", + "doc_f12bbbc027a7", + "doc_f18f523b6ecc", + "doc_f54217a0f2cf" + ], + "nothing": [ + "doc_4055659fe573", + "doc_78a8125d2c7e" + ], + "notice": [ + "doc_43ad71a4ad0c" + ], + "notification": [ + "doc_5ee8bbdbd8a8" + ], + "nucleus": [ + "doc_5ee8bbdbd8a8", + "doc_e8794369a1a4" + ], + "number": [ + "doc_4a1cf8a90e83", + "doc_78a8125d2c7e", + "doc_a24b92541657", + "doc_f54217a0f2cf" + ], + "o-bound": [ + "doc_f5f256549d84" + ], + "o-heavy": [ + "doc_9e9118ed2141" + ], + "o-series": [ + "doc_f1e32ed2ffce", + "doc_f92685a34f7e" + ], + "oauth": [ + "doc_1c5f37ea0ad3" + ], + "object": [ + "doc_4e1ebaf40267", + "doc_5a65ddab1445", + "doc_78a8125d2c7e", + "doc_bf91f8d5da74", + "doc_c346f13ce597", + "doc_e67e762fb6ab", + "doc_f18f523b6ecc" + ], + "objects": [ + "doc_3f60413b3a06", + "doc_5ee8bbdbd8a8", + "doc_61fd9f682847", + "doc_745f751f1344", + "doc_f18f523b6ecc" + ], + "observability": [ + "doc_0ac01bce8c79", + "doc_18d38fdacc9e", + "doc_2a18de009d56", + "doc_3aa0b3792d22", + "doc_3eeb75d383b8", + "doc_4e1ebaf40267", + "doc_50ac650fd8f6", + "doc_57e2139f0873", + "doc_618e34441fa7", + "doc_78a8125d2c7e", + "doc_bbc97cbff254", + "doc_d147a81bac29", + "doc_deb460ffa5ee", + "doc_f18f523b6ecc", + "doc_f5f256549d84", + "doc_fdf48d427fc0" + ], + "observable": [ + "doc_f18f523b6ecc" + ], + "observe": [ + "doc_43ad71a4ad0c", + "doc_fdf48d427fc0" + ], + "observers": [ + "doc_3f60413b3a06" + ], + "occur": [ + "doc_0ac01bce8c79", + "doc_f54217a0f2cf" + ], + "occurred": [ + "doc_e7d508371e5c" + ], + "occurs": [ + "doc_464a2b4a8d35", + "doc_4a1cf8a90e83", + "doc_64090a20f830", + "doc_f54217a0f2cf" + ], + "off": [ + "doc_07174a8633ac", + "doc_4a1cf8a90e83" + ], + "often": [ + "doc_f5f256549d84" + ], + "old": [ + "doc_0383dbf277ba", + "doc_07174a8633ac" + ], + "older": [ + "doc_07174a8633ac" + ], + "omit": [ + "doc_07174a8633ac" + ], + "on-call": [ + "doc_3ae3aaf0c12c" + ], + "once": [ + "doc_136d7a909d51", + "doc_3ae3aaf0c12c", + "doc_40e30666bfa8", + "doc_75887f91c7df", + "doc_9e9118ed2141", + "doc_e34fe24f9585" + ], + "one": [ + "doc_0ac01bce8c79", + "doc_1e46cbc6bfae", + "doc_3ae3aaf0c12c", + "doc_4055659fe573", + "doc_4e1ebaf40267", + "doc_50ac650fd8f6", + "doc_57e2139f0873", + "doc_618e34441fa7", + "doc_64090a20f830", + "doc_745f751f1344", + "doc_78a8125d2c7e", + "doc_b6088784caab", + "doc_bf91f8d5da74", + "doc_c346f13ce597", + "doc_deb460ffa5ee", + "doc_e67e762fb6ab", + "doc_e7d508371e5c", + "doc_f12bbbc027a7", + "doc_f18f523b6ecc", + "doc_f2ae50c638a4", + "doc_f54217a0f2cf", + "doc_fdf48d427fc0" + ], + "one-off": [ + "doc_f18f523b6ecc" + ], + "ones": [ + "doc_07174a8633ac", + "doc_a5120de838fb" + ], + "only": [ + "doc_0383dbf277ba", + "doc_067033cfc945", + "doc_07174a8633ac", + "doc_0ac01bce8c79", + "doc_18d38fdacc9e", + "doc_1e46cbc6bfae", + "doc_3ae3aaf0c12c", + "doc_3eeb75d383b8", + "doc_3f60413b3a06", + "doc_4e1ebaf40267", + "doc_50ac650fd8f6", + "doc_57e2139f0873", + "doc_61fd9f682847", + "doc_64090a20f830", + "doc_9e9118ed2141", + "doc_b6088784caab", + "doc_bbc97cbff254", + "doc_d1f7d83e0824", + "doc_e67e762fb6ab", + "doc_e7d508371e5c" + ], + "open": [ + "doc_4055659fe573", + "doc_7320ab982fab", + "doc_78a8125d2c7e", + "doc_a24b92541657", + "doc_e34fe24f9585", + "doc_eeed52120ccf", + "doc_f5f256549d84" + ], + "openai": [ + "doc_3f60413b3a06", + "doc_4a1cf8a90e83", + "doc_5a65ddab1445", + "doc_61fd9f682847", + "doc_745f751f1344", + "doc_eeed52120ccf", + "doc_f1e32ed2ffce", + "doc_f92685a34f7e" + ], + "openai-compatible": [ + "doc_5ee8bbdbd8a8" + ], + "openai-format": [ + "doc_5ee8bbdbd8a8" + ], + "openai_api_key": [ + "doc_1e46cbc6bfae", + "doc_61fd9f682847", + "doc_bf91f8d5da74" + ], + "openaiprovider": [ + "doc_2a18de009d56" + ], + "opens": [ + "doc_5ee8bbdbd8a8", + "doc_a24b92541657", + "doc_d1f7d83e0824" + ], + "opentelemetry": [ + "doc_78a8125d2c7e" + ], + "operate": [ + "doc_c346f13ce597" + ], + "operates": [ + "doc_07174a8633ac", + "doc_c346f13ce597" + ], + "operating": [ + "doc_fdf48d427fc0" + ], + "operation": [ + "doc_464a2b4a8d35", + "doc_c346f13ce597", + "doc_f92685a34f7e" + ], + "operational": [ + "doc_2a18de009d56", + "doc_3ae3aaf0c12c", + "doc_f18f523b6ecc" + ], + "operations": [ + "doc_3ae3aaf0c12c", + "doc_4055659fe573", + "doc_43ad71a4ad0c", + "doc_4462f67b1c03", + "doc_464a2b4a8d35", + "doc_567be1281e2f", + "doc_61fd9f682847", + "doc_78a8125d2c7e", + "doc_9e9118ed2141", + "doc_c346f13ce597" + ], + "operator": [ + "doc_3ae3aaf0c12c", + "doc_9e9118ed2141", + "doc_a5120de838fb", + "doc_d6ad61a79371" + ], + "operators": [ + "doc_3ae3aaf0c12c", + "doc_d6ad61a79371" + ], + "opt-in": [ + "doc_567be1281e2f" + ], + "optimization": [ + "doc_a5120de838fb", + "doc_d1f7d83e0824" + ], + "optimize": [ + "doc_4462f67b1c03" + ], + "optional": [ + "doc_0383dbf277ba", + "doc_07174a8633ac", + "doc_1e46cbc6bfae", + "doc_4055659fe573", + "doc_4e1ebaf40267", + "doc_618e34441fa7", + "doc_61fd9f682847", + "doc_a24b92541657", + "doc_a2e84472f1f9", + "doc_d147a81bac29" + ], + "optionally": [ + "doc_9e9118ed2141" + ], + "options": [ + "doc_61fd9f682847" + ], + "opus": [ + "doc_f1e32ed2ffce", + "doc_f92685a34f7e" + ], + "orchestrate": [ + "doc_f12bbbc027a7" + ], + "orchestration": [ + "doc_18d38fdacc9e", + "doc_4e1ebaf40267", + "doc_57e2139f0873", + "doc_a24b92541657", + "doc_d6ad61a79371", + "doc_f12bbbc027a7" + ], + "order": [ + "doc_2798948c3080", + "doc_618e34441fa7", + "doc_61fd9f682847", + "doc_745f751f1344", + "doc_9e9118ed2141", + "doc_a24b92541657", + "doc_c346f13ce597", + "doc_eeed52120ccf", + "doc_f12bbbc027a7", + "doc_f18f523b6ecc", + "doc_f54217a0f2cf" + ], + "ordered": [ + "doc_a24b92541657", + "doc_a5120de838fb", + "doc_bbc97cbff254", + "doc_bf91f8d5da74", + "doc_d1f7d83e0824" + ], + "ordering": [ + "doc_4a1cf8a90e83", + "doc_618e34441fa7", + "doc_e67e762fb6ab" + ], + "organizations": [ + "doc_1c5f37ea0ad3" + ], + "organize": [ + "doc_0383dbf277ba" + ], + "organized": [ + "doc_18d38fdacc9e", + "doc_2a18de009d56" + ], + "original": [ + "doc_07174a8633ac", + "doc_4a1cf8a90e83" + ], + "originally": [ + "doc_4a1cf8a90e83" + ], + "origins": [ + "doc_61fd9f682847", + "doc_e34fe24f9585" + ], + "os": [ + "doc_64090a20f830" + ], + "otel": [ + "doc_18d38fdacc9e", + "doc_4055659fe573", + "doc_4e1ebaf40267", + "doc_567be1281e2f", + "doc_5a65ddab1445", + "doc_78a8125d2c7e" + ], + "otel-compatible": [ + "doc_78a8125d2c7e" + ], + "other": [ + "doc_1e46cbc6bfae", + "doc_2a18de009d56", + "doc_3f1383f72c5f", + "doc_4e1ebaf40267", + "doc_50ac650fd8f6", + "doc_57e2139f0873", + "doc_5a65ddab1445", + "doc_9e9118ed2141", + "doc_e67e762fb6ab", + "doc_f12bbbc027a7", + "doc_f5f256549d84" + ], + "others": [ + "doc_4e1ebaf40267", + "doc_f12bbbc027a7" + ], + "out": [ + "doc_0ac01bce8c79", + "doc_4055659fe573", + "doc_a24b92541657", + "doc_f12bbbc027a7" + ], + "out-of-process": [ + "doc_3f1383f72c5f" + ], + "outage": [ + "doc_d1f7d83e0824" + ], + "outages": [ + "doc_7320ab982fab", + "doc_d1f7d83e0824" + ], + "outcome": [ + "doc_0ac01bce8c79", + "doc_d1f7d83e0824" + ], + "outermost": [ + "doc_745f751f1344", + "doc_c346f13ce597" + ], + "output": [ + "doc_0ac01bce8c79", + "doc_136d7a909d51", + "doc_18d38fdacc9e", + "doc_1e46cbc6bfae", + "doc_2798948c3080", + "doc_2a18de009d56", + "doc_3ae3aaf0c12c", + "doc_3f1383f72c5f", + "doc_3f60413b3a06", + "doc_4a1cf8a90e83", + "doc_50ac650fd8f6", + "doc_567be1281e2f", + "doc_5ee8bbdbd8a8", + "doc_618e34441fa7", + "doc_61fd9f682847", + "doc_7320ab982fab", + "doc_745f751f1344", + "doc_78a8125d2c7e", + "doc_9e9118ed2141", + "doc_a24b92541657", + "doc_a5120de838fb", + "doc_b6088784caab", + "doc_bbc97cbff254", + "doc_c346f13ce597", + "doc_d147a81bac29", + "doc_deb460ffa5ee", + "doc_e7d508371e5c", + "doc_e8794369a1a4", + "doc_f12bbbc027a7", + "doc_f1e32ed2ffce", + "doc_f2ae50c638a4", + "doc_f54217a0f2cf" + ], + "output_hash": [ + "doc_4a1cf8a90e83" + ], + "output_text": [ + "doc_1e46cbc6bfae", + "doc_a2e84472f1f9" + ], + "output_tokens": [ + "doc_4a1cf8a90e83", + "doc_5ee8bbdbd8a8" + ], + "outputs": [ + "doc_136d7a909d51", + "doc_a2e84472f1f9", + "doc_a5120de838fb", + "doc_f5f256549d84" + ], + "outside": [ + "doc_464a2b4a8d35", + "doc_64090a20f830", + "doc_9e9118ed2141", + "doc_a24b92541657", + "doc_d6ad61a79371", + "doc_deb460ffa5ee" + ], + "over": [ + "doc_0383dbf277ba", + "doc_07174a8633ac", + "doc_3f60413b3a06", + "doc_4e1ebaf40267", + "doc_5a65ddab1445", + "doc_78a8125d2c7e", + "doc_eeed52120ccf", + "doc_fdf48d427fc0" + ], + "overflow": [ + "doc_18d38fdacc9e" + ], + "overhead": [ + "doc_3f60413b3a06", + "doc_64090a20f830" + ], + "overkill": [ + "doc_f12bbbc027a7" + ], + "overlay": [ + "doc_07174a8633ac" + ], + "override": [ + "doc_4462f67b1c03", + "doc_5ee8bbdbd8a8", + "doc_a24b92541657" + ], + "overview": [ + "doc_3aa0b3792d22", + "doc_567be1281e2f", + "doc_c346f13ce597" + ], + "own": [ + "doc_1c5f37ea0ad3", + "doc_40e30666bfa8", + "doc_464a2b4a8d35", + "doc_64090a20f830", + "doc_78b1717fafe3", + "doc_9e9118ed2141", + "doc_a2e84472f1f9", + "doc_c346f13ce597", + "doc_e67e762fb6ab", + "doc_f18f523b6ecc", + "doc_f1e32ed2ffce" + ], + "owning": [ + "doc_2a18de009d56" + ], + "owns": [ + "doc_2a18de009d56", + "doc_f18f523b6ecc", + "doc_f2ae50c638a4" + ], + "p95": [ + "doc_067033cfc945" + ], + "p99": [ + "doc_78a8125d2c7e" + ], + "package": [ + "doc_2a18de009d56", + "doc_3eeb75d383b8", + "doc_bbc97cbff254", + "doc_bf91f8d5da74", + "doc_d147a81bac29", + "doc_deb460ffa5ee", + "doc_f18f523b6ecc" + ], + "package-level": [ + "doc_3eeb75d383b8", + "doc_d147a81bac29" + ], + "packages": [ + "doc_2a18de009d56" + ], + "packs": [ + "doc_e67e762fb6ab" + ], + "page": [ + "doc_0ac01bce8c79", + "doc_2a18de009d56", + "doc_3eeb75d383b8", + "doc_43ad71a4ad0c", + "doc_5ee8bbdbd8a8", + "doc_618e34441fa7", + "doc_61fd9f682847", + "doc_745f751f1344", + "doc_a24b92541657", + "doc_bbc97cbff254", + "doc_bf91f8d5da74", + "doc_d147a81bac29", + "doc_eeed52120ccf", + "doc_f54217a0f2cf" + ], + "pages": [ + "doc_2a18de009d56", + "doc_3aa0b3792d22", + "doc_3ae3aaf0c12c", + "doc_d147a81bac29" + ], + "pair": [ + "doc_464a2b4a8d35" + ], + "pairs": [ + "doc_2798948c3080" + ], + "parallel": [ + "doc_57e2139f0873", + "doc_5ee8bbdbd8a8", + "doc_618e34441fa7", + "doc_a24b92541657", + "doc_c93c115aeb85", + "doc_f12bbbc027a7" + ], + "parallelize": [ + "doc_f12bbbc027a7" + ], + "parameter": [ + "doc_07174a8633ac", + "doc_0ac01bce8c79", + "doc_3f60413b3a06", + "doc_4462f67b1c03", + "doc_464a2b4a8d35", + "doc_5ee8bbdbd8a8", + "doc_7320ab982fab", + "doc_9e9118ed2141", + "doc_e8794369a1a4" + ], + "parameters_schema": [ + "doc_745f751f1344" + ], + "parent": [ + "doc_618e34441fa7", + "doc_a24b92541657", + "doc_e67e762fb6ab", + "doc_e7d508371e5c" + ], + "parse": [ + "doc_618e34441fa7" + ], + "parsed": [ + "doc_3f60413b3a06", + "doc_5ee8bbdbd8a8" + ], + "parses": [ + "doc_9e9118ed2141" + ], + "parsing": [ + "doc_2a18de009d56" + ], + "part": [ + "doc_f2ae50c638a4", + "doc_f5f256549d84" + ], + "partial": [ + "doc_4055659fe573", + "doc_4a1cf8a90e83", + "doc_618e34441fa7", + "doc_a24b92541657", + "doc_e7d508371e5c", + "doc_fdf48d427fc0" + ], + "participates": [ + "doc_745f751f1344" + ], + "parts": [ + "doc_57e2139f0873", + "doc_e67e762fb6ab" + ], + "pass": [ + "doc_3f60413b3a06", + "doc_40e30666bfa8", + "doc_618e34441fa7", + "doc_7b26cbd9a7ae", + "doc_9e9118ed2141", + "doc_c346f13ce597", + "doc_c93c115aeb85", + "doc_e7d508371e5c", + "doc_eeed52120ccf", + "doc_f2ae50c638a4", + "doc_f5f256549d84" + ], + "passed": [ + "doc_4a1cf8a90e83", + "doc_61fd9f682847", + "doc_64090a20f830", + "doc_745f751f1344", + "doc_a24b92541657" + ], + "passes": [ + "doc_4a1cf8a90e83", + "doc_567be1281e2f", + "doc_5ee8bbdbd8a8", + "doc_7320ab982fab", + "doc_745f751f1344", + "doc_a24b92541657", + "doc_c346f13ce597", + "doc_eeed52120ccf" + ], + "passing": [ + "doc_618e34441fa7", + "doc_a24b92541657" + ], + "password": [ + "doc_3f1383f72c5f", + "doc_61fd9f682847" + ], + "path": [ + "doc_109157c0d2d7", + "doc_2798948c3080", + "doc_464a2b4a8d35", + "doc_50ac650fd8f6", + "doc_5ee8bbdbd8a8", + "doc_61fd9f682847", + "doc_64090a20f830", + "doc_a24b92541657", + "doc_b6088784caab", + "doc_bf91f8d5da74", + "doc_c93c115aeb85", + "doc_deb460ffa5ee", + "doc_e67e762fb6ab", + "doc_e7d508371e5c", + "doc_f18f523b6ecc" + ], + "paths": [ + "doc_464a2b4a8d35", + "doc_61fd9f682847", + "doc_a24b92541657", + "doc_d147a81bac29", + "doc_eeed52120ccf" + ], + "pattern": [ + "doc_3f60413b3a06", + "doc_618e34441fa7", + "doc_9e9118ed2141", + "doc_a2e84472f1f9", + "doc_a5120de838fb", + "doc_c346f13ce597", + "doc_d6ad61a79371", + "doc_f54217a0f2cf", + "doc_f92685a34f7e" + ], + "patterns": [ + "doc_067033cfc945", + "doc_136d7a909d51", + "doc_18d38fdacc9e", + "doc_3ae3aaf0c12c", + "doc_4e1ebaf40267", + "doc_5a65ddab1445", + "doc_64090a20f830", + "doc_7b26cbd9a7ae", + "doc_9e9118ed2141", + "doc_a5120de838fb", + "doc_c346f13ce597", + "doc_f2ae50c638a4", + "doc_f54217a0f2cf" + ], + "pause": [ + "doc_0383dbf277ba", + "doc_109157c0d2d7", + "doc_d6ad61a79371" + ], + "paused": [ + "doc_a24b92541657" + ], + "pauses": [ + "doc_07174a8633ac", + "doc_0ac01bce8c79", + "doc_4a1cf8a90e83", + "doc_a24b92541657", + "doc_d6ad61a79371" + ], + "payload": [ + "doc_0ac01bce8c79", + "doc_3f1383f72c5f", + "doc_4a1cf8a90e83", + "doc_745f751f1344", + "doc_75887f91c7df", + "doc_9e9118ed2141", + "doc_bbc97cbff254", + "doc_c346f13ce597", + "doc_e7d508371e5c", + "doc_f54217a0f2cf" + ], + "payloads": [ + "doc_4a1cf8a90e83", + "doc_5ee8bbdbd8a8", + "doc_7320ab982fab", + "doc_f5f256549d84" + ], + "pending": [ + "doc_067033cfc945", + "doc_07174a8633ac", + "doc_4a1cf8a90e83", + "doc_a24b92541657" + ], + "pending_llm_response": [ + "doc_4a1cf8a90e83" + ], + "per": [ + "doc_0383dbf277ba", + "doc_067033cfc945", + "doc_18d38fdacc9e", + "doc_3ae3aaf0c12c", + "doc_4462f67b1c03", + "doc_567be1281e2f", + "doc_61fd9f682847", + "doc_64090a20f830", + "doc_745f751f1344", + "doc_a24b92541657", + "doc_b6088784caab", + "doc_e34fe24f9585", + "doc_e67e762fb6ab", + "doc_eeed52120ccf", + "doc_f5f256549d84" + ], + "per-agent": [ + "doc_1c5f37ea0ad3" + ], + "per-call": [ + "doc_43ad71a4ad0c" + ], + "per-caller": [ + "doc_1c5f37ea0ad3", + "doc_567be1281e2f" + ], + "per-case": [ + "doc_618e34441fa7" + ], + "per-item": [ + "doc_fdf48d427fc0" + ], + "per-parent-run": [ + "doc_a24b92541657" + ], + "per-request": [ + "doc_4462f67b1c03", + "doc_c346f13ce597" + ], + "per-target-agent": [ + "doc_a24b92541657" + ], + "per-tool": [ + "doc_567be1281e2f" + ], + "percentile": [ + "doc_78a8125d2c7e" + ], + "perform": [ + "doc_4a1cf8a90e83" + ], + "performance": [ + "doc_0383dbf277ba", + "doc_3aa0b3792d22", + "doc_4462f67b1c03", + "doc_78a8125d2c7e", + "doc_78b1717fafe3", + "doc_f5f256549d84" + ], + "permanent": [ + "doc_4055659fe573", + "doc_43ad71a4ad0c" + ], + "permissions": [ + "doc_567be1281e2f" + ], + "permits": [ + "doc_1c5f37ea0ad3" + ], + "persist": [ + "doc_109157c0d2d7", + "doc_136d7a909d51", + "doc_4055659fe573", + "doc_43ad71a4ad0c", + "doc_61fd9f682847", + "doc_a5120de838fb", + "doc_f18f523b6ecc" + ], + "persisted": [ + "doc_0383dbf277ba", + "doc_4a1cf8a90e83", + "doc_4e1ebaf40267", + "doc_a24b92541657", + "doc_bbc97cbff254" + ], + "persistence": [ + "doc_0383dbf277ba", + "doc_2a18de009d56", + "doc_4a1cf8a90e83", + "doc_4e1ebaf40267", + "doc_78b1717fafe3", + "doc_7b26cbd9a7ae", + "doc_a5120de838fb", + "doc_d147a81bac29", + "doc_e7d508371e5c", + "doc_f18f523b6ecc" + ], + "persistent": [ + "doc_0383dbf277ba", + "doc_18d38fdacc9e", + "doc_3ae3aaf0c12c", + "doc_61fd9f682847", + "doc_deb460ffa5ee", + "doc_f5f256549d84" + ], + "persists": [ + "doc_0383dbf277ba", + "doc_07174a8633ac" + ], + "pgvector": [ + "doc_0383dbf277ba" + ], + "phase": [ + "doc_3ae3aaf0c12c", + "doc_4a1cf8a90e83", + "doc_5ee8bbdbd8a8" + ], + "phase-specific": [ + "doc_4a1cf8a90e83" + ], + "phases": [ + "doc_18d38fdacc9e", + "doc_4a1cf8a90e83" + ], + "philosophy": [ + "doc_5a65ddab1445" + ], + "picked": [ + "doc_64090a20f830", + "doc_78b1717fafe3" + ], + "picks": [ + "doc_07174a8633ac", + "doc_78b1717fafe3" + ], + "pip": [ + "doc_136d7a909d51" + ], + "pipeline": [ + "doc_0ac01bce8c79", + "doc_464a2b4a8d35", + "doc_4e1ebaf40267", + "doc_618e34441fa7", + "doc_64090a20f830", + "doc_7320ab982fab", + "doc_745f751f1344", + "doc_78a8125d2c7e", + "doc_c346f13ce597", + "doc_c93c115aeb85", + "doc_f12bbbc027a7", + "doc_fdf48d427fc0" + ], + "pipelines": [ + "doc_2a18de009d56", + "doc_57e2139f0873", + "doc_618e34441fa7", + "doc_a2e84472f1f9", + "doc_d6ad61a79371", + "doc_f54217a0f2cf" + ], + "pipes": [ + "doc_a24b92541657" + ], + "places": [ + "doc_0ac01bce8c79" + ], + "plain-english": [ + "doc_618e34441fa7" + ], + "plans": [ + "doc_618e34441fa7" + ], + "platforms": [ + "doc_d6ad61a79371" + ], + "playbook": [ + "doc_567be1281e2f", + "doc_c93c115aeb85" + ], + "playbooks": [ + "doc_b6088784caab" + ], + "pluggable": [ + "doc_2a18de009d56" + ], + "plus": [ + "doc_40e30666bfa8", + "doc_745f751f1344" + ], + "point": [ + "doc_0383dbf277ba", + "doc_07174a8633ac", + "doc_c346f13ce597", + "doc_f2ae50c638a4", + "doc_f92685a34f7e" + ], + "pointers": [ + "doc_0383dbf277ba" + ], + "pointing": [ + "doc_64090a20f830" + ], + "points": [ + "doc_2a18de009d56", + "doc_745f751f1344", + "doc_9e9118ed2141", + "doc_a24b92541657", + "doc_eeed52120ccf", + "doc_f92685a34f7e" + ], + "policies": [ + "doc_07174a8633ac", + "doc_2a18de009d56", + "doc_4055659fe573", + "doc_4462f67b1c03", + "doc_4e1ebaf40267", + "doc_57e2139f0873", + "doc_7320ab982fab", + "doc_745f751f1344", + "doc_a24b92541657", + "doc_d147a81bac29", + "doc_deb460ffa5ee", + "doc_e67e762fb6ab", + "doc_e7d508371e5c", + "doc_f12bbbc027a7" + ], + "policy": [ + "doc_07174a8633ac", + "doc_0ac01bce8c79", + "doc_18d38fdacc9e", + "doc_1e46cbc6bfae", + "doc_2a18de009d56", + "doc_3ae3aaf0c12c", + "doc_3f60413b3a06", + "doc_4055659fe573", + "doc_40e30666bfa8", + "doc_43ad71a4ad0c", + "doc_464a2b4a8d35", + "doc_50ac650fd8f6", + "doc_567be1281e2f", + "doc_57e2139f0873", + "doc_5ee8bbdbd8a8", + "doc_618e34441fa7", + "doc_61fd9f682847", + "doc_7320ab982fab", + "doc_745f751f1344", + "doc_9e9118ed2141", + "doc_a24b92541657", + "doc_a2e84472f1f9", + "doc_a5120de838fb", + "doc_b6088784caab", + "doc_bbc97cbff254", + "doc_c346f13ce597", + "doc_d147a81bac29", + "doc_d1f7d83e0824", + "doc_d6ad61a79371", + "doc_e34fe24f9585", + "doc_e67e762fb6ab", + "doc_f12bbbc027a7", + "doc_f18f523b6ecc", + "doc_f54217a0f2cf", + "doc_fdf48d427fc0" + ], + "policy-aware": [ + "doc_e67e762fb6ab" + ], + "policy-checked": [ + "doc_e7d508371e5c" + ], + "policy-driven": [ + "doc_4055659fe573", + "doc_745f751f1344" + ], + "policy-gated": [ + "doc_43ad71a4ad0c", + "doc_9e9118ed2141" + ], + "policy_decision": [ + "doc_0ac01bce8c79", + "doc_43ad71a4ad0c", + "doc_d6ad61a79371", + "doc_f54217a0f2cf" + ], + "policy_engine": [ + "doc_a24b92541657", + "doc_e67e762fb6ab" + ], + "policy_id": [ + "doc_f54217a0f2cf" + ], + "policy_roles": [ + "doc_a24b92541657", + "doc_e67e762fb6ab" + ], + "policy_with_hitl": [ + "doc_3aa0b3792d22" + ], + "policydecision": [ + "doc_2a18de009d56", + "doc_d6ad61a79371" + ], + "policyengine": [ + "doc_0ac01bce8c79", + "doc_2a18de009d56", + "doc_43ad71a4ad0c", + "doc_745f751f1344", + "doc_9e9118ed2141", + "doc_a24b92541657", + "doc_bbc97cbff254", + "doc_d6ad61a79371", + "doc_e67e762fb6ab" + ], + "policyevaluation": [ + "doc_2a18de009d56" + ], + "policyevent": [ + "doc_0ac01bce8c79", + "doc_2a18de009d56", + "doc_d6ad61a79371" + ], + "policyrole": [ + "doc_bbc97cbff254", + "doc_e67e762fb6ab" + ], + "policyrule": [ + "doc_2a18de009d56", + "doc_bbc97cbff254" + ], + "poll": [ + "doc_a24b92541657" + ], + "poller": [ + "doc_3f1383f72c5f" + ], + "pool": [ + "doc_61fd9f682847", + "doc_64090a20f830", + "doc_78b1717fafe3" + ], + "poolconfig": [ + "doc_4462f67b1c03" + ], + "pooling": [ + "doc_0383dbf277ba", + "doc_4462f67b1c03", + "doc_78b1717fafe3", + "doc_a5120de838fb" + ], + "pools": [ + "doc_4462f67b1c03" + ], + "port": [ + "doc_61fd9f682847" + ], + "portable": [ + "doc_5a65ddab1445" + ], + "possible": [ + "doc_1e46cbc6bfae", + "doc_57e2139f0873", + "doc_a5120de838fb" + ], + "post": [ + "doc_9e9118ed2141", + "doc_a5120de838fb" + ], + "post-execution": [ + "doc_a5120de838fb" + ], + "post-hook": [ + "doc_c346f13ce597" + ], + "post-hooks": [ + "doc_c346f13ce597" + ], + "post-tool": [ + "doc_0383dbf277ba", + "doc_4a1cf8a90e83" + ], + "post_llm": [ + "doc_4a1cf8a90e83" + ], + "post_subagent_batch": [ + "doc_4a1cf8a90e83" + ], + "post_tool_batch": [ + "doc_4a1cf8a90e83" + ], + "postgres": [ + "doc_0383dbf277ba", + "doc_618e34441fa7", + "doc_61fd9f682847", + "doc_f5f256549d84" + ], + "postgresmemorystore": [ + "doc_2a18de009d56" + ], + "postgresql": [ + "doc_067033cfc945", + "doc_61fd9f682847" + ], + "posthook": [ + "doc_745f751f1344", + "doc_9e9118ed2141", + "doc_a24b92541657" + ], + "posthooks": [ + "doc_745f751f1344", + "doc_9e9118ed2141", + "doc_a24b92541657" + ], + "posture": [ + "doc_567be1281e2f", + "doc_61fd9f682847" + ], + "potential": [ + "doc_9e9118ed2141" + ], + "powered": [ + "doc_3f60413b3a06" + ], + "pr": [ + "doc_18d38fdacc9e" + ], + "practice": [ + "doc_0ac01bce8c79", + "doc_9e9118ed2141" + ], + "practices": [ + "doc_067033cfc945" + ], + "pre": [ + "doc_9e9118ed2141", + "doc_a5120de838fb" + ], + "pre-bound": [ + "doc_e67e762fb6ab" + ], + "pre-built": [ + "doc_a24b92541657", + "doc_c346f13ce597", + "doc_e67e762fb6ab" + ], + "pre-configured": [ + "doc_eeed52120ccf" + ], + "pre-execution": [ + "doc_a5120de838fb" + ], + "pre-hook": [ + "doc_c346f13ce597" + ], + "pre-hooks": [ + "doc_c346f13ce597" + ], + "pre-llm": [ + "doc_0383dbf277ba", + "doc_4a1cf8a90e83" + ], + "pre-resolved": [ + "doc_4a1cf8a90e83" + ], + "pre-shared": [ + "doc_1c5f37ea0ad3" + ], + "pre_llm": [ + "doc_4a1cf8a90e83" + ], + "pre_subagent_batch": [ + "doc_4a1cf8a90e83" + ], + "pre_tool_batch": [ + "doc_4a1cf8a90e83" + ], + "preamble": [ + "doc_5ee8bbdbd8a8", + "doc_a24b92541657" + ], + "prebuilt": [ + "doc_2a18de009d56", + "doc_464a2b4a8d35", + "doc_9e9118ed2141" + ], + "prebuilt_runtime_tools": [ + "doc_3aa0b3792d22" + ], + "prebuilts": [ + "doc_2a18de009d56", + "doc_9e9118ed2141" + ], + "precedence": [ + "doc_2798948c3080", + "doc_61fd9f682847", + "doc_64090a20f830", + "doc_a24b92541657" + ], + "precision": [ + "doc_3f60413b3a06" + ], + "predefined": [ + "doc_18d38fdacc9e" + ], + "predictable": [ + "doc_bbc97cbff254" + ], + "predictably": [ + "doc_50ac650fd8f6" + ], + "prefer": [ + "doc_3eeb75d383b8" + ], + "preference": [ + "doc_61fd9f682847" + ], + "preferences": [ + "doc_0383dbf277ba" + ], + "preferred": [ + "doc_3eeb75d383b8", + "doc_61fd9f682847" + ], + "prefix": [ + "doc_61fd9f682847", + "doc_eeed52120ccf" + ], + "prefixes": [ + "doc_43ad71a4ad0c", + "doc_a24b92541657" + ], + "prehook": [ + "doc_745f751f1344", + "doc_9e9118ed2141", + "doc_a24b92541657" + ], + "prehooks": [ + "doc_745f751f1344", + "doc_9e9118ed2141", + "doc_a24b92541657" + ], + "premature": [ + "doc_57e2139f0873", + "doc_618e34441fa7" + ], + "prerequisites": [ + "doc_bf91f8d5da74" + ], + "present": [ + "doc_618e34441fa7", + "doc_f5f256549d84" + ], + "preserve": [ + "doc_618e34441fa7" + ], + "preserved": [ + "doc_0383dbf277ba" + ], + "preserves": [ + "doc_4a1cf8a90e83", + "doc_d147a81bac29" + ], + "preserving": [ + "doc_4a1cf8a90e83" + ], + "presets": [ + "doc_2a18de009d56" + ], + "prevent": [ + "doc_067033cfc945", + "doc_18d38fdacc9e", + "doc_43ad71a4ad0c", + "doc_4462f67b1c03", + "doc_464a2b4a8d35", + "doc_567be1281e2f", + "doc_618e34441fa7", + "doc_7320ab982fab", + "doc_745f751f1344", + "doc_c346f13ce597", + "doc_f12bbbc027a7", + "doc_fdf48d427fc0" + ], + "prevention": [ + "doc_464a2b4a8d35" + ], + "prevents": [ + "doc_1c5f37ea0ad3", + "doc_464a2b4a8d35", + "doc_50ac650fd8f6", + "doc_5ee8bbdbd8a8" + ], + "preview": [ + "doc_d147a81bac29" + ], + "previews": [ + "doc_3f1383f72c5f" + ], + "previous": [ + "doc_136d7a909d51", + "doc_5ee8bbdbd8a8", + "doc_7b26cbd9a7ae", + "doc_a5120de838fb", + "doc_eeed52120ccf" + ], + "previously": [ + "doc_4a1cf8a90e83" + ], + "prices": [ + "doc_eeed52120ccf" + ], + "pricing": [ + "doc_f92685a34f7e" + ], + "primarily": [ + "doc_61fd9f682847" + ], + "primary": [ + "doc_1e46cbc6bfae", + "doc_3aa0b3792d22", + "doc_43ad71a4ad0c", + "doc_4a1cf8a90e83", + "doc_5ee8bbdbd8a8", + "doc_7320ab982fab", + "doc_d1f7d83e0824", + "doc_f54217a0f2cf" + ], + "principle": [ + "doc_4e1ebaf40267" + ], + "principles": [ + "doc_43ad71a4ad0c", + "doc_4e1ebaf40267" + ], + "prints": [ + "doc_a24b92541657" + ], + "priority": [ + "doc_2798948c3080", + "doc_61fd9f682847", + "doc_64090a20f830", + "doc_eeed52120ccf" + ], + "privilege": [ + "doc_4e1ebaf40267", + "doc_567be1281e2f" + ], + "proactively": [ + "doc_0383dbf277ba" + ], + "problem": [ + "doc_18d38fdacc9e" + ], + "proceed": [ + "doc_745f751f1344", + "doc_d6ad61a79371", + "doc_e7d508371e5c" + ], + "proceeding": [ + "doc_d6ad61a79371" + ], + "proceeds": [ + "doc_0ac01bce8c79", + "doc_4a1cf8a90e83", + "doc_f12bbbc027a7" + ], + "process": [ + "doc_0383dbf277ba", + "doc_3ae3aaf0c12c", + "doc_4a1cf8a90e83", + "doc_567be1281e2f", + "doc_64090a20f830", + "doc_78b1717fafe3" + ], + "process-wide": [ + "doc_2a18de009d56", + "doc_61fd9f682847", + "doc_64090a20f830" + ], + "processed": [ + "doc_75887f91c7df" + ], + "processes": [ + "doc_0383dbf277ba", + "doc_4a1cf8a90e83", + "doc_745f751f1344", + "doc_75887f91c7df", + "doc_f5f256549d84" + ], + "processing": [ + "doc_1c5f37ea0ad3", + "doc_57e2139f0873", + "doc_75887f91c7df", + "doc_78b1717fafe3", + "doc_d6ad61a79371", + "doc_f54217a0f2cf", + "doc_fdf48d427fc0" + ], + "produce": [ + "doc_618e34441fa7", + "doc_745f751f1344", + "doc_a2e84472f1f9", + "doc_e67e762fb6ab", + "doc_f12bbbc027a7" + ], + "producers": [ + "doc_57e2139f0873", + "doc_78b1717fafe3" + ], + "produces": [ + "doc_0ac01bce8c79", + "doc_136d7a909d51", + "doc_18d38fdacc9e", + "doc_464a2b4a8d35", + "doc_5ee8bbdbd8a8", + "doc_618e34441fa7", + "doc_745f751f1344", + "doc_78a8125d2c7e", + "doc_a2e84472f1f9", + "doc_e7d508371e5c", + "doc_f54217a0f2cf" + ], + "production": [ + "doc_0383dbf277ba", + "doc_067033cfc945", + "doc_18d38fdacc9e", + "doc_1c5f37ea0ad3", + "doc_2798948c3080", + "doc_2a18de009d56", + "doc_3aa0b3792d22", + "doc_3ae3aaf0c12c", + "doc_3f60413b3a06", + "doc_4462f67b1c03", + "doc_464a2b4a8d35", + "doc_567be1281e2f", + "doc_61fd9f682847", + "doc_64090a20f830", + "doc_7320ab982fab", + "doc_78a8125d2c7e", + "doc_78b1717fafe3", + "doc_a5120de838fb", + "doc_bbc97cbff254", + "doc_c93c115aeb85", + "doc_d6ad61a79371", + "doc_deb460ffa5ee", + "doc_e34fe24f9585", + "doc_e67e762fb6ab", + "doc_f18f523b6ecc", + "doc_f2ae50c638a4", + "doc_f5f256549d84" + ], + "production-grade": [ + "doc_0383dbf277ba" + ], + "production-quality": [ + "doc_18d38fdacc9e" + ], + "production-ready": [ + "doc_a5120de838fb" + ], + "production_client": [ + "doc_3aa0b3792d22" + ], + "profile": [ + "doc_2a18de009d56", + "doc_3f60413b3a06", + "doc_464a2b4a8d35", + "doc_618e34441fa7", + "doc_7320ab982fab", + "doc_a24b92541657" + ], + "profile_id": [ + "doc_a24b92541657" + ], + "profiles": [ + "doc_18d38fdacc9e", + "doc_2a18de009d56", + "doc_40e30666bfa8", + "doc_43ad71a4ad0c", + "doc_464a2b4a8d35", + "doc_50ac650fd8f6", + "doc_567be1281e2f", + "doc_7320ab982fab", + "doc_9e9118ed2141", + "doc_a5120de838fb", + "doc_c346f13ce597" + ], + "progress": [ + "doc_136d7a909d51", + "doc_57e2139f0873", + "doc_bbc97cbff254", + "doc_f18f523b6ecc", + "doc_f2ae50c638a4" + ], + "progression": [ + "doc_a5120de838fb" + ], + "projected": [ + "doc_618e34441fa7" + ], + "projection": [ + "doc_2a18de009d56", + "doc_618e34441fa7" + ], + "projector": [ + "doc_618e34441fa7" + ], + "projectors": [ + "doc_2a18de009d56", + "doc_618e34441fa7", + "doc_d147a81bac29" + ], + "prompt": [ + "doc_136d7a909d51", + "doc_18d38fdacc9e", + "doc_1e46cbc6bfae", + "doc_2798948c3080", + "doc_2a18de009d56", + "doc_3ae3aaf0c12c", + "doc_3f60413b3a06", + "doc_50ac650fd8f6", + "doc_567be1281e2f", + "doc_57e2139f0873", + "doc_61fd9f682847", + "doc_64090a20f830", + "doc_745f751f1344", + "doc_78a8125d2c7e", + "doc_9e9118ed2141", + "doc_a24b92541657", + "doc_a5120de838fb", + "doc_b6088784caab", + "doc_c93c115aeb85", + "doc_e67e762fb6ab", + "doc_e8794369a1a4", + "doc_f2ae50c638a4", + "doc_f5f256549d84" + ], + "prompt-based": [ + "doc_3f60413b3a06" + ], + "prompt-injection": [ + "doc_2a18de009d56" + ], + "promptaccesserror": [ + "doc_64090a20f830" + ], + "promptresolutionerror": [ + "doc_2798948c3080" + ], + "prompts": [ + "doc_0383dbf277ba", + "doc_18d38fdacc9e", + "doc_2798948c3080", + "doc_2a18de009d56", + "doc_3ae3aaf0c12c", + "doc_5a65ddab1445", + "doc_61fd9f682847", + "doc_64090a20f830", + "doc_a5120de838fb", + "doc_d147a81bac29", + "doc_f2ae50c638a4", + "doc_f5f256549d84" + ], + "prompts_dir": [ + "doc_2798948c3080", + "doc_64090a20f830", + "doc_a24b92541657", + "doc_e67e762fb6ab" + ], + "promptstore": [ + "doc_2a18de009d56", + "doc_64090a20f830" + ], + "prompttemplateerror": [ + "doc_2798948c3080" + ], + "properties": [ + "doc_50ac650fd8f6", + "doc_618e34441fa7" + ], + "property": [ + "doc_618e34441fa7" + ], + "proposal": [ + "doc_c346f13ce597" + ], + "proposed": [ + "doc_0ac01bce8c79" + ], + "proposes": [ + "doc_745f751f1344" + ], + "protect": [ + "doc_43ad71a4ad0c", + "doc_50ac650fd8f6", + "doc_d147a81bac29" + ], + "protected": [ + "doc_0383dbf277ba", + "doc_d147a81bac29" + ], + "protecting": [ + "doc_7320ab982fab" + ], + "protection": [ + "doc_5ee8bbdbd8a8" + ], + "protects": [ + "doc_a24b92541657" + ], + "protocol": [ + "doc_0383dbf277ba", + "doc_1c5f37ea0ad3", + "doc_2a18de009d56", + "doc_40e30666bfa8", + "doc_4e1ebaf40267", + "doc_57e2139f0873", + "doc_75887f91c7df", + "doc_78b1717fafe3", + "doc_c346f13ce597", + "doc_c93c115aeb85", + "doc_d147a81bac29", + "doc_e34fe24f9585", + "doc_f92685a34f7e" + ], + "protocol-first": [ + "doc_2a18de009d56" + ], + "protocols": [ + "doc_2a18de009d56", + "doc_3eeb75d383b8", + "doc_c346f13ce597", + "doc_f92685a34f7e" + ], + "prototyping": [ + "doc_2798948c3080", + "doc_78b1717fafe3" + ], + "proven": [ + "doc_18d38fdacc9e" + ], + "provide": [ + "doc_136d7a909d51", + "doc_2798948c3080", + "doc_61fd9f682847", + "doc_d147a81bac29", + "doc_f12bbbc027a7", + "doc_f18f523b6ecc" + ], + "provided": [ + "doc_4a1cf8a90e83", + "doc_c346f13ce597" + ], + "provider": [ + "doc_0383dbf277ba", + "doc_0ac01bce8c79", + "doc_109157c0d2d7", + "doc_136d7a909d51", + "doc_1c5f37ea0ad3", + "doc_1e46cbc6bfae", + "doc_2a18de009d56", + "doc_3f60413b3a06", + "doc_4055659fe573", + "doc_4462f67b1c03", + "doc_4a1cf8a90e83", + "doc_4e1ebaf40267", + "doc_5ee8bbdbd8a8", + "doc_618e34441fa7", + "doc_61fd9f682847", + "doc_7320ab982fab", + "doc_745f751f1344", + "doc_a24b92541657", + "doc_bbc97cbff254", + "doc_bf91f8d5da74", + "doc_c346f13ce597", + "doc_d1f7d83e0824", + "doc_d6ad61a79371", + "doc_deb460ffa5ee", + "doc_e7d508371e5c", + "doc_e8794369a1a4", + "doc_eeed52120ccf", + "doc_f18f523b6ecc", + "doc_f1e32ed2ffce", + "doc_f54217a0f2cf", + "doc_f92685a34f7e" + ], + "provider-agnostic": [ + "doc_f92685a34f7e" + ], + "provider-dependent": [ + "doc_f1e32ed2ffce" + ], + "provider-portable": [ + "doc_4e1ebaf40267", + "doc_5a65ddab1445", + "doc_d147a81bac29", + "doc_deb460ffa5ee" + ], + "provider-specific": [ + "doc_5ee8bbdbd8a8", + "doc_e8794369a1a4", + "doc_f1e32ed2ffce", + "doc_f92685a34f7e" + ], + "provider-supported": [ + "doc_e67e762fb6ab" + ], + "provider_adapter": [ + "doc_4a1cf8a90e83", + "doc_bbc97cbff254" + ], + "providers": [ + "doc_067033cfc945", + "doc_1c5f37ea0ad3", + "doc_2a18de009d56", + "doc_4a1cf8a90e83", + "doc_567be1281e2f", + "doc_57e2139f0873", + "doc_5a65ddab1445", + "doc_5ee8bbdbd8a8", + "doc_618e34441fa7", + "doc_61fd9f682847", + "doc_c93c115aeb85", + "doc_d147a81bac29", + "doc_e8794369a1a4", + "doc_eeed52120ccf", + "doc_f1e32ed2ffce", + "doc_f92685a34f7e" + ], + "provides": [ + "doc_3f60413b3a06", + "doc_43ad71a4ad0c", + "doc_4a1cf8a90e83", + "doc_64090a20f830", + "doc_78b1717fafe3", + "doc_9e9118ed2141", + "doc_a5120de838fb", + "doc_b6088784caab", + "doc_c346f13ce597", + "doc_f54217a0f2cf", + "doc_fdf48d427fc0" + ], + "providing": [ + "doc_0ac01bce8c79" + ], + "proxy": [ + "doc_f1e32ed2ffce", + "doc_f92685a34f7e" + ], + "prunes": [ + "doc_07174a8633ac" + ], + "public": [ + "doc_067033cfc945", + "doc_2a18de009d56", + "doc_3ae3aaf0c12c", + "doc_3eeb75d383b8", + "doc_4e1ebaf40267", + "doc_618e34441fa7", + "doc_bbc97cbff254", + "doc_d147a81bac29", + "doc_deb460ffa5ee", + "doc_f18f523b6ecc", + "doc_f5f256549d84" + ], + "public-import": [ + "doc_d147a81bac29" + ], + "public-imports-and-function-improvement": [ + "doc_3aa0b3792d22" + ], + "published": [ + "doc_0ac01bce8c79" + ], + "pure": [ + "doc_18d38fdacc9e", + "doc_745f751f1344" + ], + "purpose": [ + "doc_0383dbf277ba", + "doc_3f60413b3a06", + "doc_5ee8bbdbd8a8", + "doc_745f751f1344", + "doc_75887f91c7df", + "doc_9e9118ed2141", + "doc_a24b92541657", + "doc_b6088784caab", + "doc_e67e762fb6ab", + "doc_e8794369a1a4", + "doc_eeed52120ccf", + "doc_f92685a34f7e" + ], + "purposes": [ + "doc_d6ad61a79371" + ], + "push": [ + "doc_4055659fe573", + "doc_78b1717fafe3" + ], + "pwd": [ + "doc_a24b92541657" + ], + "py": [ + "doc_3eeb75d383b8", + "doc_d147a81bac29" + ], + "pydantic": [ + "doc_18d38fdacc9e", + "doc_3ae3aaf0c12c", + "doc_3eeb75d383b8", + "doc_3f60413b3a06", + "doc_43ad71a4ad0c", + "doc_4e1ebaf40267", + "doc_50ac650fd8f6", + "doc_57e2139f0873", + "doc_5a65ddab1445", + "doc_618e34441fa7", + "doc_745f751f1344", + "doc_9e9118ed2141", + "doc_a24b92541657", + "doc_a5120de838fb", + "doc_b6088784caab", + "doc_bf91f8d5da74", + "doc_c346f13ce597", + "doc_deb460ffa5ee", + "doc_e67e762fb6ab", + "doc_e8794369a1a4", + "doc_f5f256549d84" + ], + "pydantic-based": [ + "doc_5a65ddab1445" + ], + "pydantic-validated": [ + "doc_3f60413b3a06" + ], + "pytest": [ + "doc_3eeb75d383b8" + ], + "python": [ + "doc_136d7a909d51", + "doc_2a18de009d56", + "doc_464a2b4a8d35", + "doc_64090a20f830", + "doc_745f751f1344", + "doc_9e9118ed2141", + "doc_b6088784caab", + "doc_bf91f8d5da74", + "doc_d147a81bac29", + "doc_deb460ffa5ee", + "doc_f18f523b6ecc", + "doc_f1e32ed2ffce" + ], + "pythonpath": [ + "doc_3eeb75d383b8" + ], + "qa": [ + "doc_64090a20f830" + ], + "qa_bot_v2": [ + "doc_64090a20f830" + ], + "quality": [ + "doc_067033cfc945", + "doc_78a8125d2c7e", + "doc_d147a81bac29", + "doc_eeed52120ccf" + ], + "queries": [ + "doc_0383dbf277ba", + "doc_07174a8633ac" + ], + "query": [ + "doc_9e9118ed2141" + ], + "question": [ + "doc_b6088784caab" + ], + "queue": [ + "doc_067033cfc945", + "doc_2a18de009d56", + "doc_4055659fe573", + "doc_57e2139f0873", + "doc_618e34441fa7", + "doc_61fd9f682847", + "doc_75887f91c7df", + "doc_78b1717fafe3" + ], + "queue-based": [ + "doc_57e2139f0873" + ], + "queue-level": [ + "doc_2a18de009d56" + ], + "queued": [ + "doc_4a1cf8a90e83", + "doc_78b1717fafe3", + "doc_a24b92541657", + "doc_f12bbbc027a7" + ], + "queues": [ + "doc_2a18de009d56", + "doc_3ae3aaf0c12c", + "doc_3eeb75d383b8", + "doc_57e2139f0873", + "doc_618e34441fa7", + "doc_61fd9f682847", + "doc_78b1717fafe3", + "doc_bbc97cbff254", + "doc_d147a81bac29", + "doc_deb460ffa5ee", + "doc_f18f523b6ecc", + "doc_f2ae50c638a4", + "doc_f5f256549d84" + ], + "quick": [ + "doc_0383dbf277ba", + "doc_109157c0d2d7", + "doc_3f1383f72c5f", + "doc_78b1717fafe3", + "doc_a24b92541657", + "doc_e7d508371e5c", + "doc_f12bbbc027a7" + ], + "quickstart": [ + "doc_3aa0b3792d22", + "doc_d147a81bac29", + "doc_f18f523b6ecc", + "doc_f2ae50c638a4" + ], + "quorum": [ + "doc_f12bbbc027a7" + ], + "rag": [ + "doc_57e2139f0873", + "doc_5a65ddab1445" + ], + "raise": [ + "doc_a24b92541657" + ], + "raise_on_error": [ + "doc_745f751f1344", + "doc_a24b92541657" + ], + "raised": [ + "doc_3f60413b3a06", + "doc_4055659fe573", + "doc_4a1cf8a90e83" + ], + "raises": [ + "doc_07174a8633ac", + "doc_0ac01bce8c79", + "doc_464a2b4a8d35", + "doc_4a1cf8a90e83", + "doc_64090a20f830", + "doc_a24b92541657" + ], + "ran": [ + "doc_0ac01bce8c79", + "doc_e7d508371e5c" + ], + "random": [ + "doc_61fd9f682847" + ], + "rarely": [ + "doc_f92685a34f7e" + ], + "rate": [ + "doc_067033cfc945", + "doc_136d7a909d51", + "doc_18d38fdacc9e", + "doc_1c5f37ea0ad3", + "doc_3ae3aaf0c12c", + "doc_4055659fe573", + "doc_40e30666bfa8", + "doc_567be1281e2f", + "doc_5ee8bbdbd8a8", + "doc_7320ab982fab", + "doc_78a8125d2c7e", + "doc_78b1717fafe3", + "doc_9e9118ed2141", + "doc_a24b92541657", + "doc_c346f13ce597", + "doc_c93c115aeb85", + "doc_d147a81bac29", + "doc_d1f7d83e0824", + "doc_deb460ffa5ee", + "doc_e7d508371e5c", + "doc_e8794369a1a4", + "doc_eeed52120ccf", + "doc_f1e32ed2ffce" + ], + "ratelimiterror": [ + "doc_136d7a909d51" + ], + "ratelimitpolicy": [ + "doc_2a18de009d56" + ], + "rates": [ + "doc_e34fe24f9585" + ], + "rather": [ + "doc_07174a8633ac", + "doc_4a1cf8a90e83", + "doc_618e34441fa7", + "doc_64090a20f830", + "doc_b6088784caab" + ], + "raw": [ + "doc_0383dbf277ba", + "doc_07174a8633ac", + "doc_3f60413b3a06", + "doc_7320ab982fab", + "doc_745f751f1344", + "doc_9e9118ed2141" + ], + "re": [ + "doc_e67e762fb6ab" + ], + "re-enters": [ + "doc_4a1cf8a90e83" + ], + "re-executing": [ + "doc_07174a8633ac", + "doc_4a1cf8a90e83" + ], + "re-execution": [ + "doc_4a1cf8a90e83" + ], + "re-exports": [ + "doc_2a18de009d56" + ], + "re-rendering": [ + "doc_64090a20f830" + ], + "re-validation": [ + "doc_745f751f1344" + ], + "reach": [ + "doc_067033cfc945", + "doc_e34fe24f9585" + ], + "reached": [ + "doc_f12bbbc027a7" + ], + "reaches": [ + "doc_07174a8633ac", + "doc_745f751f1344", + "doc_f54217a0f2cf" + ], + "read": [ + "doc_07174a8633ac", + "doc_2a18de009d56", + "doc_3ae3aaf0c12c", + "doc_40e30666bfa8", + "doc_4462f67b1c03", + "doc_464a2b4a8d35", + "doc_618e34441fa7", + "doc_61fd9f682847", + "doc_64090a20f830", + "doc_7b26cbd9a7ae", + "doc_9e9118ed2141", + "doc_a5120de838fb", + "doc_b6088784caab", + "doc_bf91f8d5da74", + "doc_c346f13ce597", + "doc_d1f7d83e0824", + "doc_f18f523b6ecc", + "doc_f2ae50c638a4", + "doc_f5f256549d84", + "doc_fdf48d427fc0" + ], + "read-only": [ + "doc_109157c0d2d7", + "doc_43ad71a4ad0c", + "doc_9e9118ed2141", + "doc_b6088784caab" + ], + "read_file": [ + "doc_464a2b4a8d35" + ], + "read_skill_file": [ + "doc_9e9118ed2141", + "doc_b6088784caab" + ], + "read_skill_md": [ + "doc_9e9118ed2141", + "doc_b6088784caab" + ], + "readable": [ + "doc_0383dbf277ba", + "doc_f54217a0f2cf" + ], + "readiness": [ + "doc_18d38fdacc9e" + ], + "reading": [ + "doc_1e46cbc6bfae", + "doc_464a2b4a8d35", + "doc_a5120de838fb", + "doc_b6088784caab" + ], + "reads": [ + "doc_43ad71a4ad0c", + "doc_464a2b4a8d35", + "doc_a24b92541657", + "doc_b6088784caab" + ], + "ready": [ + "doc_f2ae50c638a4" + ], + "ready-to-use": [ + "doc_9e9118ed2141" + ], + "real": [ + "doc_18d38fdacc9e", + "doc_3ae3aaf0c12c", + "doc_618e34441fa7", + "doc_9e9118ed2141", + "doc_c93c115aeb85", + "doc_d1f7d83e0824", + "doc_d6ad61a79371", + "doc_fdf48d427fc0" + ], + "real-time": [ + "doc_109157c0d2d7", + "doc_5ee8bbdbd8a8", + "doc_7b26cbd9a7ae", + "doc_a5120de838fb", + "doc_e7d508371e5c", + "doc_eeed52120ccf", + "doc_f54217a0f2cf", + "doc_fdf48d427fc0" + ], + "reason": [ + "doc_0ac01bce8c79", + "doc_50ac650fd8f6", + "doc_618e34441fa7", + "doc_d6ad61a79371", + "doc_f54217a0f2cf" + ], + "reasonable": [ + "doc_c93c115aeb85" + ], + "reasoning": [ + "doc_a24b92541657", + "doc_e67e762fb6ab", + "doc_eeed52120ccf", + "doc_f5f256549d84", + "doc_f92685a34f7e" + ], + "reasoning_effort": [ + "doc_a24b92541657", + "doc_e67e762fb6ab" + ], + "reasoning_enabled": [ + "doc_a24b92541657", + "doc_e67e762fb6ab" + ], + "reasoning_max_tokens": [ + "doc_a24b92541657", + "doc_e67e762fb6ab" + ], + "reasons": [ + "doc_4a1cf8a90e83" + ], + "receive": [ + "doc_109157c0d2d7", + "doc_50ac650fd8f6", + "doc_9e9118ed2141", + "doc_c346f13ce597", + "doc_f2ae50c638a4" + ], + "received": [ + "doc_0ac01bce8c79", + "doc_4a1cf8a90e83", + "doc_a24b92541657" + ], + "receiver": [ + "doc_75887f91c7df" + ], + "receives": [ + "doc_0ac01bce8c79", + "doc_3f1383f72c5f", + "doc_9e9118ed2141", + "doc_a2e84472f1f9", + "doc_c346f13ce597", + "doc_d6ad61a79371", + "doc_f12bbbc027a7" + ], + "receiving": [ + "doc_75887f91c7df", + "doc_f54217a0f2cf" + ], + "recent": [ + "doc_0383dbf277ba", + "doc_07174a8633ac", + "doc_4a1cf8a90e83" + ], + "recommendations": [ + "doc_78a8125d2c7e", + "doc_d1f7d83e0824", + "doc_fdf48d427fc0" + ], + "recommended": [ + "doc_18d38fdacc9e", + "doc_57e2139f0873", + "doc_618e34441fa7", + "doc_f54217a0f2cf" + ], + "recommends": [ + "doc_567be1281e2f" + ], + "reconstruct": [ + "doc_4a1cf8a90e83" + ], + "record": [ + "doc_0383dbf277ba", + "doc_4a1cf8a90e83", + "doc_a2e84472f1f9", + "doc_deb460ffa5ee" + ], + "recorded": [ + "doc_0ac01bce8c79", + "doc_745f751f1344" + ], + "records": [ + "doc_0383dbf277ba", + "doc_07174a8633ac", + "doc_0ac01bce8c79", + "doc_1e46cbc6bfae", + "doc_2a18de009d56", + "doc_3ae3aaf0c12c", + "doc_50ac650fd8f6", + "doc_a2e84472f1f9", + "doc_bbc97cbff254", + "doc_bf91f8d5da74", + "doc_deb460ffa5ee", + "doc_f18f523b6ecc" + ], + "recover": [ + "doc_4055659fe573" + ], + "recovery": [ + "doc_4a1cf8a90e83", + "doc_618e34441fa7", + "doc_a24b92541657" + ], + "recursion": [ + "doc_a24b92541657" + ], + "redact_secrets": [ + "doc_3f1383f72c5f" + ], + "redaction": [ + "doc_2a18de009d56", + "doc_3f1383f72c5f", + "doc_a24b92541657", + "doc_c346f13ce597" + ], + "redelivered": [ + "doc_75887f91c7df" + ], + "redirects": [ + "doc_a24b92541657" + ], + "redis": [ + "doc_0383dbf277ba", + "doc_2a18de009d56", + "doc_4055659fe573", + "doc_4462f67b1c03", + "doc_618e34441fa7", + "doc_61fd9f682847", + "doc_a5120de838fb", + "doc_f5f256549d84" + ], + "redisconnectionpool": [ + "doc_78b1717fafe3" + ], + "redismemorystore": [ + "doc_2a18de009d56" + ], + "redistaskqueue": [ + "doc_bbc97cbff254" + ], + "reduce": [ + "doc_7320ab982fab" + ], + "reduces": [ + "doc_07174a8633ac" + ], + "reducing": [ + "doc_f5f256549d84" + ], + "reference": [ + "doc_0ac01bce8c79", + "doc_109157c0d2d7", + "doc_2a18de009d56", + "doc_3aa0b3792d22", + "doc_3eeb75d383b8", + "doc_4462f67b1c03", + "doc_4a1cf8a90e83", + "doc_61fd9f682847", + "doc_78a8125d2c7e", + "doc_7b26cbd9a7ae", + "doc_a24b92541657", + "doc_b6088784caab", + "doc_bbc97cbff254", + "doc_d147a81bac29", + "doc_d1f7d83e0824", + "doc_e67e762fb6ab", + "doc_e7d508371e5c", + "doc_f18f523b6ecc", + "doc_f54217a0f2cf", + "doc_fdf48d427fc0" + ], + "references": [ + "doc_3eeb75d383b8", + "doc_f18f523b6ecc" + ], + "reflect": [ + "doc_43ad71a4ad0c" + ], + "refreshed": [ + "doc_f18f523b6ecc" + ], + "refs": [ + "doc_40e30666bfa8", + "doc_a24b92541657" + ], + "refused": [ + "doc_4055659fe573" + ], + "regardless": [ + "doc_0ac01bce8c79" + ], + "regenerate": [ + "doc_3eeb75d383b8", + "doc_d147a81bac29" + ], + "register": [ + "doc_43ad71a4ad0c", + "doc_78b1717fafe3", + "doc_a5120de838fb", + "doc_b6088784caab", + "doc_f1e32ed2ffce" + ], + "register_llm_provider": [ + "doc_2a18de009d56" + ], + "register_llm_router": [ + "doc_2a18de009d56" + ], + "registered": [ + "doc_1e46cbc6bfae", + "doc_3f60413b3a06", + "doc_5ee8bbdbd8a8", + "doc_745f751f1344", + "doc_eeed52120ccf" + ], + "registering": [ + "doc_43ad71a4ad0c", + "doc_b6088784caab" + ], + "registers": [ + "doc_b6088784caab" + ], + "registration": [ + "doc_2a18de009d56", + "doc_745f751f1344", + "doc_a5120de838fb", + "doc_f92685a34f7e" + ], + "registries": [ + "doc_2a18de009d56" + ], + "registry": [ + "doc_0ac01bce8c79", + "doc_2a18de009d56", + "doc_464a2b4a8d35", + "doc_4e1ebaf40267", + "doc_745f751f1344", + "doc_9e9118ed2141", + "doc_c346f13ce597", + "doc_d147a81bac29" + ], + "registry-level": [ + "doc_9e9118ed2141", + "doc_c346f13ce597" + ], + "registry_middleware": [ + "doc_9e9118ed2141" + ], + "regression": [ + "doc_18d38fdacc9e", + "doc_50ac650fd8f6", + "doc_618e34441fa7", + "doc_c93c115aeb85" + ], + "regressions": [ + "doc_618e34441fa7" + ], + "reject": [ + "doc_4a1cf8a90e83" + ], + "rejected": [ + "doc_0ac01bce8c79" + ], + "rejects": [ + "doc_75887f91c7df" + ], + "related": [ + "doc_75887f91c7df", + "doc_f2ae50c638a4" + ], + "relative": [ + "doc_464a2b4a8d35", + "doc_61fd9f682847", + "doc_64090a20f830" + ], + "relative_to": [ + "doc_464a2b4a8d35" + ], + "release": [ + "doc_3ae3aaf0c12c", + "doc_618e34441fa7", + "doc_c93c115aeb85" + ], + "release-gating": [ + "doc_618e34441fa7" + ], + "releases": [ + "doc_067033cfc945", + "doc_3ae3aaf0c12c", + "doc_c93c115aeb85", + "doc_f54217a0f2cf" + ], + "relevant": [ + "doc_3eeb75d383b8", + "doc_618e34441fa7", + "doc_b6088784caab" + ], + "reliability": [ + "doc_57e2139f0873" + ], + "reliable": [ + "doc_3ae3aaf0c12c", + "doc_61fd9f682847", + "doc_78b1717fafe3" + ], + "reliably": [ + "doc_75887f91c7df", + "doc_deb460ffa5ee", + "doc_f5f256549d84" + ], + "remain": [ + "doc_61fd9f682847", + "doc_eeed52120ccf" + ], + "remaining": [ + "doc_0383dbf277ba", + "doc_0ac01bce8c79" + ], + "remember": [ + "doc_136d7a909d51" + ], + "remembers": [ + "doc_7b26cbd9a7ae" + ], + "remote": [ + "doc_40e30666bfa8", + "doc_a5120de838fb", + "doc_e34fe24f9585" + ], + "remove_": [ + "doc_43ad71a4ad0c" + ], + "removed": [ + "doc_07174a8633ac", + "doc_f54217a0f2cf" + ], + "removes": [ + "doc_07174a8633ac" + ], + "removing": [ + "doc_3eeb75d383b8" + ], + "renamed": [ + "doc_618e34441fa7" + ], + "renaming": [ + "doc_3eeb75d383b8" + ], + "render_untrusted_tool_message": [ + "doc_745f751f1344" + ], + "rendered": [ + "doc_f54217a0f2cf" + ], + "rendering": [ + "doc_2a18de009d56" + ], + "renders": [ + "doc_64090a20f830" + ], + "repair": [ + "doc_61fd9f682847" + ], + "repeated": [ + "doc_4a1cf8a90e83", + "doc_a24b92541657" + ], + "repeatedly": [ + "doc_136d7a909d51" + ], + "rephrasing": [ + "doc_0ac01bce8c79" + ], + "replace": [ + "doc_3f60413b3a06" + ], + "replaced": [ + "doc_64090a20f830" + ], + "replay": [ + "doc_0ac01bce8c79", + "doc_4a1cf8a90e83", + "doc_50ac650fd8f6" + ], + "replayed": [ + "doc_0383dbf277ba", + "doc_4a1cf8a90e83" + ], + "replayed_effect_count": [ + "doc_4a1cf8a90e83" + ], + "repo": [ + "doc_f18f523b6ecc" + ], + "report": [ + "doc_2a18de009d56", + "doc_4055659fe573", + "doc_618e34441fa7", + "doc_c93c115aeb85", + "doc_f12bbbc027a7" + ], + "reported": [ + "doc_109157c0d2d7", + "doc_d6ad61a79371" + ], + "reporting": [ + "doc_136d7a909d51", + "doc_2a18de009d56", + "doc_4e1ebaf40267", + "doc_618e34441fa7" + ], + "reports": [ + "doc_618e34441fa7" + ], + "repository": [ + "doc_618e34441fa7", + "doc_b6088784caab", + "doc_bf91f8d5da74", + "doc_d147a81bac29" + ], + "representing": [ + "doc_5ee8bbdbd8a8" + ], + "represents": [ + "doc_78b1717fafe3" + ], + "reproducible": [ + "doc_618e34441fa7" + ], + "reproduction": [ + "doc_136d7a909d51" + ], + "req": [ + "doc_7320ab982fab", + "doc_c346f13ce597", + "doc_f92685a34f7e" + ], + "request": [ + "doc_0ac01bce8c79", + "doc_1c5f37ea0ad3", + "doc_2a18de009d56", + "doc_3f60413b3a06", + "doc_4055659fe573", + "doc_4462f67b1c03", + "doc_50ac650fd8f6", + "doc_5a65ddab1445", + "doc_5ee8bbdbd8a8", + "doc_61fd9f682847", + "doc_7320ab982fab", + "doc_75887f91c7df", + "doc_a24b92541657", + "doc_c346f13ce597", + "doc_d6ad61a79371", + "doc_e34fe24f9585", + "doc_e7d508371e5c", + "doc_e8794369a1a4", + "doc_eeed52120ccf", + "doc_f12bbbc027a7", + "doc_f54217a0f2cf", + "doc_f92685a34f7e" + ], + "request-level": [ + "doc_5ee8bbdbd8a8" + ], + "request_approval": [ + "doc_0ac01bce8c79", + "doc_43ad71a4ad0c", + "doc_567be1281e2f", + "doc_9e9118ed2141", + "doc_d6ad61a79371", + "doc_e67e762fb6ab" + ], + "request_id": [ + "doc_5ee8bbdbd8a8" + ], + "request_payload": [ + "doc_0ac01bce8c79" + ], + "request_user_input": [ + "doc_0ac01bce8c79", + "doc_567be1281e2f", + "doc_745f751f1344", + "doc_a24b92541657", + "doc_d6ad61a79371", + "doc_e67e762fb6ab" + ], + "requested": [ + "doc_07174a8633ac", + "doc_4a1cf8a90e83", + "doc_50ac650fd8f6", + "doc_5ee8bbdbd8a8", + "doc_bbc97cbff254", + "doc_e8794369a1a4" + ], + "requested_model": [ + "doc_4a1cf8a90e83", + "doc_bbc97cbff254" + ], + "requests": [ + "doc_136d7a909d51", + "doc_1c5f37ea0ad3", + "doc_4462f67b1c03", + "doc_5ee8bbdbd8a8", + "doc_61fd9f682847", + "doc_7320ab982fab", + "doc_a24b92541657", + "doc_a5120de838fb", + "doc_c346f13ce597", + "doc_d6ad61a79371", + "doc_f5f256549d84", + "doc_f92685a34f7e", + "doc_fdf48d427fc0" + ], + "require": [ + "doc_3f60413b3a06", + "doc_43ad71a4ad0c", + "doc_50ac650fd8f6", + "doc_7320ab982fab", + "doc_745f751f1344", + "doc_a2e84472f1f9", + "doc_d6ad61a79371", + "doc_e34fe24f9585", + "doc_f18f523b6ecc" + ], + "require_idempotency_key": [ + "doc_7320ab982fab" + ], + "required": [ + "doc_067033cfc945", + "doc_0ac01bce8c79", + "doc_1e46cbc6bfae", + "doc_4055659fe573", + "doc_464a2b4a8d35", + "doc_4a1cf8a90e83", + "doc_567be1281e2f", + "doc_61fd9f682847", + "doc_78b1717fafe3", + "doc_a24b92541657", + "doc_bbc97cbff254", + "doc_e67e762fb6ab" + ], + "requirement": [ + "doc_18d38fdacc9e" + ], + "requires": [ + "doc_067033cfc945", + "doc_0ac01bce8c79", + "doc_567be1281e2f" + ], + "requiring": [ + "doc_f54217a0f2cf" + ], + "research": [ + "doc_a2e84472f1f9" + ], + "research-assistant": [ + "doc_64090a20f830" + ], + "research_assistant": [ + "doc_64090a20f830" + ], + "researcher": [ + "doc_4055659fe573" + ], + "reserve": [ + "doc_f5f256549d84", + "doc_fdf48d427fc0" + ], + "reserved": [ + "doc_64090a20f830" + ], + "resilience": [ + "doc_a24b92541657", + "doc_a5120de838fb", + "doc_d1f7d83e0824" + ], + "resilient": [ + "doc_a2e84472f1f9" + ], + "resolution": [ + "doc_2798948c3080", + "doc_2a18de009d56", + "doc_4a1cf8a90e83", + "doc_5ee8bbdbd8a8", + "doc_64090a20f830", + "doc_a24b92541657", + "doc_eeed52120ccf", + "doc_f2ae50c638a4" + ], + "resolve": [ + "doc_136d7a909d51", + "doc_61fd9f682847", + "doc_64090a20f830", + "doc_7320ab982fab", + "doc_9e9118ed2141", + "doc_a24b92541657", + "doc_e67e762fb6ab", + "doc_f1e32ed2ffce", + "doc_f92685a34f7e" + ], + "resolved": [ + "doc_3f1383f72c5f", + "doc_464a2b4a8d35", + "doc_5ee8bbdbd8a8", + "doc_618e34441fa7", + "doc_61fd9f682847", + "doc_64090a20f830", + "doc_9e9118ed2141", + "doc_a24b92541657", + "doc_e67e762fb6ab" + ], + "resolver": [ + "doc_a24b92541657" + ], + "resolves": [ + "doc_0ac01bce8c79", + "doc_618e34441fa7", + "doc_64090a20f830", + "doc_eeed52120ccf" + ], + "resource": [ + "doc_f12bbbc027a7" + ], + "resources": [ + "doc_43ad71a4ad0c", + "doc_b6088784caab" + ], + "resp": [ + "doc_3f60413b3a06", + "doc_5ee8bbdbd8a8" + ], + "respect": [ + "doc_618e34441fa7" + ], + "respected": [ + "doc_618e34441fa7" + ], + "respecting": [ + "doc_c93c115aeb85" + ], + "respects": [ + "doc_c346f13ce597" + ], + "respond": [ + "doc_0ac01bce8c79", + "doc_50ac650fd8f6" + ], + "responds": [ + "doc_136d7a909d51", + "doc_1e46cbc6bfae" + ], + "response": [ + "doc_067033cfc945", + "doc_0ac01bce8c79", + "doc_109157c0d2d7", + "doc_1e46cbc6bfae", + "doc_2a18de009d56", + "doc_3f60413b3a06", + "doc_4055659fe573", + "doc_4a1cf8a90e83", + "doc_4e1ebaf40267", + "doc_5a65ddab1445", + "doc_5ee8bbdbd8a8", + "doc_7320ab982fab", + "doc_745f751f1344", + "doc_75887f91c7df", + "doc_9e9118ed2141", + "doc_a2e84472f1f9", + "doc_a5120de838fb", + "doc_bf91f8d5da74", + "doc_c346f13ce597", + "doc_e7d508371e5c", + "doc_e8794369a1a4", + "doc_f12bbbc027a7", + "doc_f54217a0f2cf", + "doc_f92685a34f7e" + ], + "response_format": [ + "doc_3f60413b3a06", + "doc_e8794369a1a4" + ], + "response_model": [ + "doc_3f60413b3a06", + "doc_5ee8bbdbd8a8" + ], + "responseformat": [ + "doc_e8794369a1a4" + ], + "responses": [ + "doc_0383dbf277ba", + "doc_136d7a909d51", + "doc_50ac650fd8f6", + "doc_5ee8bbdbd8a8", + "doc_a24b92541657", + "doc_a5120de838fb", + "doc_c346f13ce597", + "doc_e34fe24f9585", + "doc_e8794369a1a4", + "doc_f5f256549d84", + "doc_f92685a34f7e" + ], + "responsibility": [ + "doc_2a18de009d56", + "doc_4e1ebaf40267", + "doc_d147a81bac29" + ], + "rest": [ + "doc_0ac01bce8c79", + "doc_f12bbbc027a7" + ], + "restart": [ + "doc_0383dbf277ba", + "doc_136d7a909d51", + "doc_75887f91c7df" + ], + "restarting": [ + "doc_64090a20f830" + ], + "restarts": [ + "doc_3ae3aaf0c12c", + "doc_4a1cf8a90e83", + "doc_61fd9f682847" + ], + "restoration": [ + "doc_07174a8633ac" + ], + "restored": [ + "doc_07174a8633ac", + "doc_4a1cf8a90e83" + ], + "restoring": [ + "doc_07174a8633ac" + ], + "restrict": [ + "doc_067033cfc945", + "doc_43ad71a4ad0c", + "doc_464a2b4a8d35", + "doc_a24b92541657" + ], + "restrictions": [ + "doc_9e9118ed2141", + "doc_a24b92541657" + ], + "result": [ + "doc_07174a8633ac", + "doc_0ac01bce8c79", + "doc_109157c0d2d7", + "doc_1e46cbc6bfae", + "doc_4a1cf8a90e83", + "doc_4e1ebaf40267", + "doc_50ac650fd8f6", + "doc_618e34441fa7", + "doc_64090a20f830", + "doc_745f751f1344", + "doc_75887f91c7df", + "doc_78b1717fafe3", + "doc_7b26cbd9a7ae", + "doc_9e9118ed2141", + "doc_a24b92541657", + "doc_bf91f8d5da74", + "doc_c346f13ce597", + "doc_c93c115aeb85", + "doc_d1f7d83e0824", + "doc_d6ad61a79371", + "doc_deb460ffa5ee", + "doc_e7d508371e5c", + "doc_f12bbbc027a7", + "doc_f2ae50c638a4", + "doc_f5f256549d84", + "doc_fdf48d427fc0" + ], + "resulting": [ + "doc_c93c115aeb85" + ], + "results": [ + "doc_0383dbf277ba", + "doc_2a18de009d56", + "doc_4055659fe573", + "doc_4a1cf8a90e83", + "doc_4e1ebaf40267", + "doc_57e2139f0873", + "doc_5ee8bbdbd8a8", + "doc_618e34441fa7", + "doc_9e9118ed2141", + "doc_a24b92541657", + "doc_a2e84472f1f9", + "doc_a5120de838fb", + "doc_c93c115aeb85", + "doc_e34fe24f9585", + "doc_e67e762fb6ab", + "doc_e8794369a1a4", + "doc_f12bbbc027a7", + "doc_fdf48d427fc0" + ], + "resume": [ + "doc_0383dbf277ba", + "doc_07174a8633ac", + "doc_109157c0d2d7", + "doc_136d7a909d51", + "doc_2a18de009d56", + "doc_3ae3aaf0c12c", + "doc_4a1cf8a90e83", + "doc_50ac650fd8f6", + "doc_7b26cbd9a7ae", + "doc_a5120de838fb", + "doc_bbc97cbff254", + "doc_d147a81bac29", + "doc_e7d508371e5c", + "doc_f18f523b6ecc" + ], + "resume_and_compact": [ + "doc_3aa0b3792d22" + ], + "resume_hint": [ + "doc_109157c0d2d7", + "doc_f54217a0f2cf" + ], + "resume_with_input": [ + "doc_a24b92541657" + ], + "resumed": [ + "doc_4a1cf8a90e83", + "doc_f54217a0f2cf" + ], + "resumes": [ + "doc_0ac01bce8c79", + "doc_4a1cf8a90e83" + ], + "resuming": [ + "doc_07174a8633ac", + "doc_4a1cf8a90e83" + ], + "resumption": [ + "doc_0383dbf277ba", + "doc_7b26cbd9a7ae", + "doc_a5120de838fb" + ], + "retain": [ + "doc_7320ab982fab" + ], + "retained": [ + "doc_f5f256549d84" + ], + "retention": [ + "doc_0383dbf277ba", + "doc_07174a8633ac", + "doc_2a18de009d56", + "doc_bbc97cbff254", + "doc_d147a81bac29", + "doc_deb460ffa5ee" + ], + "retentionpolicy": [ + "doc_07174a8633ac" + ], + "retried": [ + "doc_57e2139f0873", + "doc_5ee8bbdbd8a8", + "doc_7320ab982fab", + "doc_78b1717fafe3", + "doc_a24b92541657" + ], + "retries": [ + "doc_1c5f37ea0ad3", + "doc_5ee8bbdbd8a8", + "doc_78b1717fafe3", + "doc_a24b92541657", + "doc_c346f13ce597", + "doc_d147a81bac29", + "doc_d1f7d83e0824", + "doc_deb460ffa5ee", + "doc_eeed52120ccf" + ], + "retrieval": [ + "doc_5a65ddab1445" + ], + "retrieve": [ + "doc_18d38fdacc9e" + ], + "retry": [ + "doc_4055659fe573", + "doc_4462f67b1c03", + "doc_4a1cf8a90e83", + "doc_4e1ebaf40267", + "doc_50ac650fd8f6", + "doc_5a65ddab1445", + "doc_618e34441fa7", + "doc_61fd9f682847", + "doc_7320ab982fab", + "doc_75887f91c7df", + "doc_78b1717fafe3", + "doc_a24b92541657", + "doc_d147a81bac29", + "doc_d1f7d83e0824", + "doc_e7d508371e5c", + "doc_e8794369a1a4", + "doc_eeed52120ccf", + "doc_f1e32ed2ffce" + ], + "retry_then_continue": [ + "doc_a24b92541657" + ], + "retry_then_degrade": [ + "doc_a24b92541657", + "doc_a2e84472f1f9" + ], + "retry_then_fail": [ + "doc_a24b92541657" + ], + "retryable": [ + "doc_18d38fdacc9e", + "doc_4055659fe573", + "doc_4e1ebaf40267", + "doc_50ac650fd8f6", + "doc_57e2139f0873", + "doc_5ee8bbdbd8a8", + "doc_618e34441fa7", + "doc_78b1717fafe3", + "doc_eeed52120ccf" + ], + "retrypolicy": [ + "doc_2a18de009d56" + ], + "return": [ + "doc_136d7a909d51", + "doc_18d38fdacc9e", + "doc_3f60413b3a06", + "doc_464a2b4a8d35", + "doc_4e1ebaf40267", + "doc_50ac650fd8f6", + "doc_745f751f1344", + "doc_9e9118ed2141", + "doc_c346f13ce597", + "doc_f12bbbc027a7", + "doc_f5f256549d84" + ], + "returned": [ + "doc_1e46cbc6bfae", + "doc_4055659fe573", + "doc_4a1cf8a90e83", + "doc_745f751f1344", + "doc_a2e84472f1f9", + "doc_c346f13ce597", + "doc_d6ad61a79371" + ], + "returning": [ + "doc_4a1cf8a90e83", + "doc_a24b92541657", + "doc_c346f13ce597", + "doc_f5f256549d84" + ], + "returns": [ + "doc_07174a8633ac", + "doc_0ac01bce8c79", + "doc_136d7a909d51", + "doc_1e46cbc6bfae", + "doc_3f60413b3a06", + "doc_464a2b4a8d35", + "doc_4a1cf8a90e83", + "doc_745f751f1344", + "doc_9e9118ed2141", + "doc_a24b92541657", + "doc_b6088784caab", + "doc_bbc97cbff254", + "doc_c346f13ce597", + "doc_d6ad61a79371", + "doc_e7d508371e5c", + "doc_f12bbbc027a7", + "doc_f2ae50c638a4", + "doc_fdf48d427fc0" + ], + "reusable": [ + "doc_2798948c3080", + "doc_64090a20f830", + "doc_b6088784caab" + ], + "reuses": [ + "doc_64090a20f830" + ], + "review": [ + "doc_18d38fdacc9e", + "doc_3eeb75d383b8", + "doc_618e34441fa7", + "doc_75887f91c7df", + "doc_78b1717fafe3", + "doc_d6ad61a79371" + ], + "reviewers": [ + "doc_43ad71a4ad0c" + ], + "reviewing": [ + "doc_b6088784caab", + "doc_d147a81bac29", + "doc_deb460ffa5ee" + ], + "rg": [ + "doc_a24b92541657" + ], + "right": [ + "doc_0383dbf277ba", + "doc_4055659fe573", + "doc_618e34441fa7", + "doc_f5f256549d84" + ], + "risk": [ + "doc_43ad71a4ad0c", + "doc_d147a81bac29" + ], + "role": [ + "doc_745f751f1344", + "doc_e8794369a1a4" + ], + "role-based": [ + "doc_e67e762fb6ab" + ], + "roles": [ + "doc_3ae3aaf0c12c", + "doc_a24b92541657" + ], + "rollback": [ + "doc_3ae3aaf0c12c" + ], + "rolling": [ + "doc_78a8125d2c7e" + ], + "root": [ + "doc_464a2b4a8d35", + "doc_618e34441fa7", + "doc_61fd9f682847", + "doc_64090a20f830", + "doc_a24b92541657" + ], + "root_dir": [ + "doc_9e9118ed2141" + ], + "rounded": [ + "doc_3aa0b3792d22" + ], + "route": [ + "doc_18d38fdacc9e", + "doc_a5120de838fb" + ], + "routed": [ + "doc_d6ad61a79371" + ], + "router": [ + "doc_2a18de009d56", + "doc_3f60413b3a06", + "doc_a24b92541657", + "doc_a5120de838fb" + ], + "routes": [ + "doc_d6ad61a79371" + ], + "routine": [ + "doc_fdf48d427fc0" + ], + "routing": [ + "doc_18d38fdacc9e", + "doc_2a18de009d56", + "doc_618e34441fa7", + "doc_61fd9f682847", + "doc_75887f91c7df", + "doc_a24b92541657", + "doc_c93c115aeb85", + "doc_d147a81bac29", + "doc_e67e762fb6ab" + ], + "row": [ + "doc_a24b92541657" + ], + "rows": [ + "doc_7320ab982fab" + ], + "rule": [ + "doc_0383dbf277ba", + "doc_43ad71a4ad0c", + "doc_4e1ebaf40267", + "doc_618e34441fa7", + "doc_64090a20f830", + "doc_a24b92541657", + "doc_b6088784caab", + "doc_d6ad61a79371", + "doc_f18f523b6ecc" + ], + "rules": [ + "doc_0383dbf277ba", + "doc_0ac01bce8c79", + "doc_18d38fdacc9e", + "doc_1c5f37ea0ad3", + "doc_3eeb75d383b8", + "doc_40e30666bfa8", + "doc_43ad71a4ad0c", + "doc_50ac650fd8f6", + "doc_567be1281e2f", + "doc_618e34441fa7", + "doc_64090a20f830", + "doc_9e9118ed2141", + "doc_b6088784caab", + "doc_bbc97cbff254", + "doc_d147a81bac29", + "doc_d6ad61a79371", + "doc_deb460ffa5ee", + "doc_e34fe24f9585", + "doc_e67e762fb6ab", + "doc_f18f523b6ecc" + ], + "run": [ + "doc_0383dbf277ba", + "doc_067033cfc945", + "doc_07174a8633ac", + "doc_0ac01bce8c79", + "doc_109157c0d2d7", + "doc_136d7a909d51", + "doc_18d38fdacc9e", + "doc_1e46cbc6bfae", + "doc_2798948c3080", + "doc_2a18de009d56", + "doc_3ae3aaf0c12c", + "doc_3eeb75d383b8", + "doc_3f1383f72c5f", + "doc_3f60413b3a06", + "doc_4055659fe573", + "doc_43ad71a4ad0c", + "doc_4a1cf8a90e83", + "doc_4e1ebaf40267", + "doc_50ac650fd8f6", + "doc_567be1281e2f", + "doc_5a65ddab1445", + "doc_5ee8bbdbd8a8", + "doc_618e34441fa7", + "doc_64090a20f830", + "doc_745f751f1344", + "doc_75887f91c7df", + "doc_78a8125d2c7e", + "doc_9e9118ed2141", + "doc_a24b92541657", + "doc_a2e84472f1f9", + "doc_a5120de838fb", + "doc_bbc97cbff254", + "doc_bf91f8d5da74", + "doc_c346f13ce597", + "doc_c93c115aeb85", + "doc_d1f7d83e0824", + "doc_d6ad61a79371", + "doc_deb460ffa5ee", + "doc_e67e762fb6ab", + "doc_e7d508371e5c", + "doc_eeed52120ccf", + "doc_f12bbbc027a7", + "doc_f18f523b6ecc", + "doc_f54217a0f2cf", + "doc_f5f256549d84", + "doc_fdf48d427fc0" + ], + "run-event-contract": [ + "doc_3aa0b3792d22" + ], + "run-level": [ + "doc_0ac01bce8c79" + ], + "run_cancelled": [ + "doc_f54217a0f2cf" + ], + "run_case": [ + "doc_2a18de009d56" + ], + "run_completed": [ + "doc_f54217a0f2cf" + ], + "run_failed": [ + "doc_f54217a0f2cf" + ], + "run_handle": [ + "doc_07174a8633ac", + "doc_109157c0d2d7", + "doc_2a18de009d56", + "doc_4a1cf8a90e83", + "doc_e7d508371e5c" + ], + "run_id": [ + "doc_07174a8633ac", + "doc_1e46cbc6bfae", + "doc_2798948c3080", + "doc_4a1cf8a90e83", + "doc_5ee8bbdbd8a8", + "doc_75887f91c7df", + "doc_9e9118ed2141", + "doc_bbc97cbff254", + "doc_bf91f8d5da74", + "doc_e7d508371e5c", + "doc_f54217a0f2cf" + ], + "run_interrupted": [ + "doc_f54217a0f2cf" + ], + "run_paused": [ + "doc_0ac01bce8c79", + "doc_a24b92541657" + ], + "run_resumed": [ + "doc_0ac01bce8c79" + ], + "run_skill_command": [ + "doc_9e9118ed2141", + "doc_b6088784caab" + ], + "run_started": [ + "doc_4a1cf8a90e83", + "doc_f54217a0f2cf" + ], + "run_stream": [ + "doc_109157c0d2d7", + "doc_136d7a909d51", + "doc_2a18de009d56", + "doc_3f60413b3a06", + "doc_5ee8bbdbd8a8", + "doc_618e34441fa7", + "doc_7b26cbd9a7ae", + "doc_bbc97cbff254", + "doc_eeed52120ccf", + "doc_f2ae50c638a4", + "doc_f5f256549d84" + ], + "run_suite": [ + "doc_2a18de009d56", + "doc_bbc97cbff254" + ], + "run_sync": [ + "doc_1e46cbc6bfae", + "doc_2a18de009d56", + "doc_57e2139f0873", + "doc_bbc97cbff254" + ], + "run_terminal": [ + "doc_4a1cf8a90e83" + ], + "runaway": [ + "doc_18d38fdacc9e", + "doc_43ad71a4ad0c", + "doc_4462f67b1c03", + "doc_567be1281e2f", + "doc_c346f13ce597", + "doc_e67e762fb6ab", + "doc_fdf48d427fc0" + ], + "runmetrics": [ + "doc_2a18de009d56", + "doc_50ac650fd8f6", + "doc_618e34441fa7", + "doc_78a8125d2c7e" + ], + "runnable": [ + "doc_3aa0b3792d22", + "doc_5a65ddab1445", + "doc_a5120de838fb", + "doc_deb460ffa5ee" + ], + "runner": [ + "doc_0383dbf277ba", + "doc_07174a8633ac", + "doc_0ac01bce8c79", + "doc_109157c0d2d7", + "doc_136d7a909d51", + "doc_18d38fdacc9e", + "doc_1c5f37ea0ad3", + "doc_1e46cbc6bfae", + "doc_2a18de009d56", + "doc_3eeb75d383b8", + "doc_3f1383f72c5f", + "doc_3f60413b3a06", + "doc_4a1cf8a90e83", + "doc_4e1ebaf40267", + "doc_50ac650fd8f6", + "doc_567be1281e2f", + "doc_57e2139f0873", + "doc_5a65ddab1445", + "doc_5ee8bbdbd8a8", + "doc_618e34441fa7", + "doc_61fd9f682847", + "doc_64090a20f830", + "doc_745f751f1344", + "doc_9e9118ed2141", + "doc_a24b92541657", + "doc_a2e84472f1f9", + "doc_a5120de838fb", + "doc_bbc97cbff254", + "doc_bf91f8d5da74", + "doc_c346f13ce597", + "doc_c93c115aeb85", + "doc_d147a81bac29", + "doc_d6ad61a79371", + "doc_deb460ffa5ee", + "doc_e67e762fb6ab", + "doc_e7d508371e5c", + "doc_eeed52120ccf", + "doc_f18f523b6ecc", + "doc_f2ae50c638a4", + "doc_f54217a0f2cf", + "doc_f5f256549d84", + "doc_fdf48d427fc0" + ], + "runnerapimixin": [ + "doc_3eeb75d383b8" + ], + "runnerconfig": [ + "doc_2a18de009d56", + "doc_464a2b4a8d35", + "doc_4a1cf8a90e83", + "doc_61fd9f682847", + "doc_a24b92541657", + "doc_bbc97cbff254", + "doc_d147a81bac29", + "doc_d6ad61a79371", + "doc_eeed52120ccf", + "doc_f5f256549d84" + ], + "runnerdebugconfig": [ + "doc_a24b92541657" + ], + "runners": [ + "doc_4462f67b1c03", + "doc_d147a81bac29", + "doc_f18f523b6ecc" + ], + "running": [ + "doc_18d38fdacc9e", + "doc_4a1cf8a90e83", + "doc_618e34441fa7", + "doc_78b1717fafe3", + "doc_deb460ffa5ee", + "doc_fdf48d427fc0" + ], + "runs": [ + "doc_0383dbf277ba", + "doc_067033cfc945", + "doc_07174a8633ac", + "doc_0ac01bce8c79", + "doc_109157c0d2d7", + "doc_136d7a909d51", + "doc_1e46cbc6bfae", + "doc_3ae3aaf0c12c", + "doc_4a1cf8a90e83", + "doc_50ac650fd8f6", + "doc_745f751f1344", + "doc_78b1717fafe3", + "doc_9e9118ed2141", + "doc_a24b92541657", + "doc_a5120de838fb", + "doc_b6088784caab", + "doc_c346f13ce597", + "doc_c93c115aeb85", + "doc_e67e762fb6ab", + "doc_e7d508371e5c", + "doc_f12bbbc027a7", + "doc_f2ae50c638a4", + "doc_f54217a0f2cf", + "doc_f5f256549d84", + "doc_fdf48d427fc0" + ], + "runtime": [ + "doc_07174a8633ac", + "doc_109157c0d2d7", + "doc_2798948c3080", + "doc_2a18de009d56", + "doc_3aa0b3792d22", + "doc_3f1383f72c5f", + "doc_464a2b4a8d35", + "doc_4a1cf8a90e83", + "doc_4e1ebaf40267", + "doc_50ac650fd8f6", + "doc_567be1281e2f", + "doc_5a65ddab1445", + "doc_5ee8bbdbd8a8", + "doc_618e34441fa7", + "doc_61fd9f682847", + "doc_7320ab982fab", + "doc_9e9118ed2141", + "doc_a24b92541657", + "doc_a5120de838fb", + "doc_b6088784caab", + "doc_bbc97cbff254", + "doc_c346f13ce597", + "doc_d147a81bac29", + "doc_e7d508371e5c", + "doc_f18f523b6ecc" + ], + "runtime-injected": [ + "doc_745f751f1344" + ], + "runtime_state": [ + "doc_4a1cf8a90e83", + "doc_a24b92541657" + ], + "safe": [ + "doc_43ad71a4ad0c", + "doc_9e9118ed2141" + ], + "safely": [ + "doc_18d38fdacc9e", + "doc_43ad71a4ad0c", + "doc_f54217a0f2cf" + ], + "safety": [ + "doc_18d38fdacc9e", + "doc_2a18de009d56", + "doc_3ae3aaf0c12c", + "doc_bf91f8d5da74", + "doc_deb460ffa5ee", + "doc_e67e762fb6ab", + "doc_f2ae50c638a4" + ], + "same": [ + "doc_136d7a909d51", + "doc_1c5f37ea0ad3", + "doc_3eeb75d383b8", + "doc_40e30666bfa8", + "doc_4a1cf8a90e83", + "doc_5ee8bbdbd8a8", + "doc_618e34441fa7", + "doc_64090a20f830", + "doc_75887f91c7df", + "doc_7b26cbd9a7ae", + "doc_9e9118ed2141", + "doc_a24b92541657", + "doc_b6088784caab", + "doc_c346f13ce597", + "doc_c93c115aeb85", + "doc_e34fe24f9585", + "doc_f12bbbc027a7", + "doc_f2ae50c638a4", + "doc_f92685a34f7e" + ], + "sampling": [ + "doc_3f60413b3a06", + "doc_5ee8bbdbd8a8", + "doc_e8794369a1a4" + ], + "sandbox": [ + "doc_0ac01bce8c79", + "doc_18d38fdacc9e", + "doc_2a18de009d56", + "doc_40e30666bfa8", + "doc_43ad71a4ad0c", + "doc_464a2b4a8d35", + "doc_50ac650fd8f6", + "doc_567be1281e2f", + "doc_618e34441fa7", + "doc_745f751f1344", + "doc_9e9118ed2141", + "doc_a24b92541657", + "doc_a5120de838fb", + "doc_d147a81bac29" + ], + "sandbox_profile_provider": [ + "doc_a24b92541657" + ], + "sandboxing": [ + "doc_3ae3aaf0c12c", + "doc_d147a81bac29", + "doc_deb460ffa5ee", + "doc_f2ae50c638a4" + ], + "sandboxprofile": [ + "doc_2a18de009d56", + "doc_43ad71a4ad0c", + "doc_a24b92541657" + ], + "sandboxprofileprovider": [ + "doc_2a18de009d56" + ], + "sanitization": [ + "doc_2a18de009d56", + "doc_40e30666bfa8", + "doc_567be1281e2f", + "doc_9e9118ed2141", + "doc_c346f13ce597" + ], + "sanitize": [ + "doc_50ac650fd8f6", + "doc_745f751f1344", + "doc_a24b92541657" + ], + "sanitize_tool_output": [ + "doc_567be1281e2f", + "doc_9e9118ed2141", + "doc_a24b92541657" + ], + "sanitized": [ + "doc_567be1281e2f", + "doc_745f751f1344", + "doc_9e9118ed2141", + "doc_e7d508371e5c" + ], + "sanitizes": [ + "doc_745f751f1344" + ], + "satisfied": [ + "doc_4a1cf8a90e83" + ], + "save": [ + "doc_18d38fdacc9e" + ], + "says": [ + "doc_43ad71a4ad0c" + ], + "scale": [ + "doc_50ac650fd8f6" + ], + "scaling": [ + "doc_067033cfc945", + "doc_18d38fdacc9e" + ], + "scans": [ + "doc_b6088784caab" + ], + "scenario": [ + "doc_3f1383f72c5f", + "doc_3f60413b3a06", + "doc_a2e84472f1f9", + "doc_a5120de838fb", + "doc_b6088784caab", + "doc_d1f7d83e0824", + "doc_deb460ffa5ee", + "doc_f12bbbc027a7", + "doc_f92685a34f7e" + ], + "scenarios": [ + "doc_4a1cf8a90e83", + "doc_bf91f8d5da74" + ], + "schedule": [ + "doc_07174a8633ac" + ], + "scheduler": [ + "doc_c93c115aeb85" + ], + "schedules": [ + "doc_75887f91c7df" + ], + "scheduling": [ + "doc_2a18de009d56", + "doc_618e34441fa7" + ], + "schema": [ + "doc_067033cfc945", + "doc_07174a8633ac", + "doc_1c5f37ea0ad3", + "doc_2a18de009d56", + "doc_3aa0b3792d22", + "doc_3f60413b3a06", + "doc_4a1cf8a90e83", + "doc_50ac650fd8f6", + "doc_618e34441fa7", + "doc_745f751f1344", + "doc_9e9118ed2141", + "doc_a5120de838fb", + "doc_b6088784caab", + "doc_bf91f8d5da74" + ], + "schema-validated": [ + "doc_3f60413b3a06", + "doc_4a1cf8a90e83", + "doc_a5120de838fb" + ], + "schema_version": [ + "doc_4a1cf8a90e83", + "doc_618e34441fa7" + ], + "schemas": [ + "doc_50ac650fd8f6", + "doc_9e9118ed2141", + "doc_e34fe24f9585", + "doc_e7d508371e5c", + "doc_e8794369a1a4", + "doc_eeed52120ccf", + "doc_f2ae50c638a4" + ], + "scope": [ + "doc_3ae3aaf0c12c", + "doc_567be1281e2f", + "doc_618e34441fa7", + "doc_9e9118ed2141", + "doc_c346f13ce597", + "doc_d147a81bac29" + ], + "scoped": [ + "doc_0383dbf277ba", + "doc_43ad71a4ad0c", + "doc_464a2b4a8d35", + "doc_9e9118ed2141", + "doc_a5120de838fb" + ], + "scopes": [ + "doc_0383dbf277ba", + "doc_567be1281e2f" + ], + "scoping": [ + "doc_43ad71a4ad0c", + "doc_464a2b4a8d35", + "doc_a5120de838fb" + ], + "scoring": [ + "doc_2a18de009d56" + ], + "scratch": [ + "doc_07174a8633ac" + ], + "scripts": [ + "doc_0383dbf277ba", + "doc_3eeb75d383b8", + "doc_618e34441fa7", + "doc_a24b92541657", + "doc_b6088784caab", + "doc_bbc97cbff254", + "doc_e7d508371e5c" + ], + "sdk": [ + "doc_1c5f37ea0ad3", + "doc_2a18de009d56", + "doc_4e1ebaf40267", + "doc_5ee8bbdbd8a8", + "doc_deb460ffa5ee", + "doc_f18f523b6ecc", + "doc_f1e32ed2ffce" + ], + "sdks": [ + "doc_4e1ebaf40267" + ], + "search": [ + "doc_0383dbf277ba", + "doc_18d38fdacc9e", + "doc_2a18de009d56", + "doc_3eeb75d383b8", + "doc_5a65ddab1445", + "doc_61fd9f682847", + "doc_a5120de838fb", + "doc_d147a81bac29", + "doc_f5f256549d84" + ], + "second": [ + "doc_9e9118ed2141" + ], + "seconds": [ + "doc_5ee8bbdbd8a8", + "doc_61fd9f682847", + "doc_7320ab982fab", + "doc_745f751f1344", + "doc_78a8125d2c7e", + "doc_a24b92541657" + ], + "secret": [ + "doc_3ae3aaf0c12c", + "doc_3f1383f72c5f", + "doc_567be1281e2f", + "doc_618e34441fa7", + "doc_d147a81bac29" + ], + "secret-scope": [ + "doc_a24b92541657" + ], + "secret_scope_provider": [ + "doc_a24b92541657" + ], + "secrets": [ + "doc_0383dbf277ba", + "doc_067033cfc945", + "doc_18d38fdacc9e", + "doc_567be1281e2f" + ], + "secretscopeprovider": [ + "doc_2a18de009d56" + ], + "section": [ + "doc_4a1cf8a90e83", + "doc_a24b92541657", + "doc_f2ae50c638a4" + ], + "sections": [ + "doc_e67e762fb6ab" + ], + "security": [ + "doc_067033cfc945", + "doc_18d38fdacc9e", + "doc_1c5f37ea0ad3", + "doc_2a18de009d56", + "doc_3ae3aaf0c12c", + "doc_4055659fe573", + "doc_40e30666bfa8", + "doc_43ad71a4ad0c", + "doc_464a2b4a8d35", + "doc_567be1281e2f", + "doc_618e34441fa7", + "doc_61fd9f682847", + "doc_64090a20f830", + "doc_78a8125d2c7e", + "doc_a5120de838fb", + "doc_b6088784caab", + "doc_c93c115aeb85", + "doc_d147a81bac29", + "doc_deb460ffa5ee", + "doc_e34fe24f9585", + "doc_e67e762fb6ab", + "doc_f2ae50c638a4" + ], + "security-first": [ + "doc_464a2b4a8d35" + ], + "security-model": [ + "doc_3aa0b3792d22" + ], + "see": [ + "doc_136d7a909d51", + "doc_3eeb75d383b8", + "doc_4a1cf8a90e83", + "doc_4e1ebaf40267", + "doc_50ac650fd8f6", + "doc_61fd9f682847", + "doc_745f751f1344", + "doc_75887f91c7df", + "doc_7b26cbd9a7ae", + "doc_a5120de838fb", + "doc_b6088784caab", + "doc_bbc97cbff254", + "doc_deb460ffa5ee", + "doc_f54217a0f2cf", + "doc_f5f256549d84" + ], + "sees": [ + "doc_0ac01bce8c79", + "doc_e34fe24f9585", + "doc_e67e762fb6ab" + ], + "selected": [ + "doc_a24b92541657", + "doc_b6088784caab" + ], + "selection": [ + "doc_0383dbf277ba", + "doc_2a18de009d56", + "doc_5ee8bbdbd8a8", + "doc_a24b92541657", + "doc_c346f13ce597", + "doc_eeed52120ccf" + ], + "self-contained": [ + "doc_a5120de838fb" + ], + "self-correct": [ + "doc_0ac01bce8c79", + "doc_745f751f1344" + ], + "self-correction": [ + "doc_4055659fe573", + "doc_4e1ebaf40267", + "doc_9e9118ed2141" + ], + "self-hosted": [ + "doc_f92685a34f7e" + ], + "semantic": [ + "doc_0383dbf277ba" + ], + "semantically": [ + "doc_0383dbf277ba" + ], + "semantics": [ + "doc_d147a81bac29" + ], + "semicolons": [ + "doc_a24b92541657" + ], + "send": [ + "doc_e8794369a1a4" + ], + "sender": [ + "doc_75887f91c7df" + ], + "sending": [ + "doc_75887f91c7df", + "doc_d6ad61a79371", + "doc_f54217a0f2cf" + ], + "sends": [ + "doc_50ac650fd8f6", + "doc_5ee8bbdbd8a8", + "doc_d6ad61a79371" + ], + "sensitive": [ + "doc_9e9118ed2141", + "doc_a5120de838fb", + "doc_d6ad61a79371" + ], + "sent": [ + "doc_1e46cbc6bfae", + "doc_4e1ebaf40267", + "doc_78b1717fafe3", + "doc_9e9118ed2141", + "doc_e7d508371e5c" + ], + "sentiment": [ + "doc_f12bbbc027a7" + ], + "separate": [ + "doc_2798948c3080", + "doc_3eeb75d383b8", + "doc_4e1ebaf40267", + "doc_567be1281e2f", + "doc_a2e84472f1f9" + ], + "separated": [ + "doc_f12bbbc027a7" + ], + "separates": [ + "doc_57e2139f0873" + ], + "separation": [ + "doc_f18f523b6ecc" + ], + "sequence": [ + "doc_07174a8633ac", + "doc_0ac01bce8c79", + "doc_4e1ebaf40267" + ], + "sequences": [ + "doc_5ee8bbdbd8a8", + "doc_618e34441fa7", + "doc_e8794369a1a4" + ], + "sequentially": [ + "doc_745f751f1344", + "doc_c93c115aeb85" + ], + "serializable": [ + "doc_4e1ebaf40267" + ], + "serialization": [ + "doc_0383dbf277ba", + "doc_2a18de009d56", + "doc_618e34441fa7" + ], + "serialized": [ + "doc_4a1cf8a90e83" + ], + "serializer": [ + "doc_618e34441fa7" + ], + "server": [ + "doc_067033cfc945", + "doc_1c5f37ea0ad3", + "doc_2a18de009d56", + "doc_4055659fe573", + "doc_40e30666bfa8", + "doc_57e2139f0873", + "doc_5ee8bbdbd8a8", + "doc_61fd9f682847", + "doc_a24b92541657", + "doc_e34fe24f9585", + "doc_e67e762fb6ab", + "doc_eeed52120ccf" + ], + "servers": [ + "doc_40e30666bfa8", + "doc_a24b92541657", + "doc_a5120de838fb", + "doc_deb460ffa5ee", + "doc_e34fe24f9585", + "doc_e7d508371e5c", + "doc_f18f523b6ecc", + "doc_f1e32ed2ffce", + "doc_f5f256549d84" + ], + "service": [ + "doc_1c5f37ea0ad3", + "doc_618e34441fa7", + "doc_a24b92541657" + ], + "services": [ + "doc_067033cfc945", + "doc_1c5f37ea0ad3", + "doc_1e46cbc6bfae", + "doc_bbc97cbff254", + "doc_f5f256549d84" + ], + "session": [ + "doc_4462f67b1c03", + "doc_4a1cf8a90e83", + "doc_5ee8bbdbd8a8", + "doc_7320ab982fab" + ], + "session-aware": [ + "doc_4a1cf8a90e83" + ], + "session_token": [ + "doc_4a1cf8a90e83", + "doc_5ee8bbdbd8a8" + ], + "sessions": [ + "doc_0383dbf277ba", + "doc_5ee8bbdbd8a8" + ], + "set": [ + "doc_0383dbf277ba", + "doc_067033cfc945", + "doc_136d7a909d51", + "doc_18d38fdacc9e", + "doc_2798948c3080", + "doc_3ae3aaf0c12c", + "doc_3eeb75d383b8", + "doc_3f60413b3a06", + "doc_43ad71a4ad0c", + "doc_464a2b4a8d35", + "doc_4a1cf8a90e83", + "doc_50ac650fd8f6", + "doc_567be1281e2f", + "doc_5ee8bbdbd8a8", + "doc_618e34441fa7", + "doc_61fd9f682847", + "doc_64090a20f830", + "doc_7320ab982fab", + "doc_745f751f1344", + "doc_75887f91c7df", + "doc_78b1717fafe3", + "doc_a24b92541657", + "doc_bbc97cbff254", + "doc_c93c115aeb85", + "doc_deb460ffa5ee", + "doc_e67e762fb6ab", + "doc_f18f523b6ecc", + "doc_f2ae50c638a4", + "doc_f5f256549d84", + "doc_fdf48d427fc0" + ], + "setting": [ + "doc_567be1281e2f", + "doc_7320ab982fab", + "doc_a24b92541657", + "doc_fdf48d427fc0" + ], + "settings": [ + "doc_3f60413b3a06", + "doc_61fd9f682847", + "doc_7320ab982fab", + "doc_d1f7d83e0824", + "doc_eeed52120ccf", + "doc_f1e32ed2ffce" + ], + "setup": [ + "doc_0383dbf277ba", + "doc_067033cfc945", + "doc_43ad71a4ad0c", + "doc_57e2139f0873", + "doc_75887f91c7df", + "doc_78a8125d2c7e", + "doc_78b1717fafe3", + "doc_d147a81bac29", + "doc_f18f523b6ecc" + ], + "setups": [ + "doc_067033cfc945", + "doc_40e30666bfa8" + ], + "severity": [ + "doc_43ad71a4ad0c", + "doc_78a8125d2c7e" + ], + "sh": [ + "doc_3eeb75d383b8", + "doc_b6088784caab" + ], + "sha-256": [ + "doc_64090a20f830" + ], + "shape": [ + "doc_9e9118ed2141", + "doc_b6088784caab", + "doc_c346f13ce597", + "doc_f5f256549d84" + ], + "shapes": [ + "doc_4e1ebaf40267" + ], + "share": [ + "doc_e34fe24f9585" + ], + "shared": [ + "doc_0383dbf277ba", + "doc_2a18de009d56", + "doc_4462f67b1c03", + "doc_61fd9f682847", + "doc_a24b92541657", + "doc_f5f256549d84" + ], + "shares": [ + "doc_e34fe24f9585" + ], + "sharing": [ + "doc_e34fe24f9585" + ], + "sheet": [ + "doc_7320ab982fab" + ], + "shell": [ + "doc_9e9118ed2141", + "doc_a24b92541657" + ], + "shift": [ + "doc_2798948c3080" + ], + "shipping": [ + "doc_136d7a909d51", + "doc_3ae3aaf0c12c", + "doc_f2ae50c638a4" + ], + "ships": [ + "doc_0383dbf277ba", + "doc_1c5f37ea0ad3", + "doc_464a2b4a8d35", + "doc_9e9118ed2141", + "doc_c346f13ce597", + "doc_f1e32ed2ffce" + ], + "short": [ + "doc_f5f256549d84" + ], + "short-circuit": [ + "doc_745f751f1344", + "doc_c346f13ce597" + ], + "short-lived": [ + "doc_0383dbf277ba" + ], + "shortest": [ + "doc_bf91f8d5da74" + ], + "shorthand": [ + "doc_64090a20f830" + ], + "should": [ + "doc_067033cfc945", + "doc_18d38fdacc9e", + "doc_3eeb75d383b8", + "doc_43ad71a4ad0c", + "doc_4e1ebaf40267", + "doc_50ac650fd8f6", + "doc_618e34441fa7", + "doc_9e9118ed2141", + "doc_a5120de838fb", + "doc_bbc97cbff254", + "doc_c346f13ce597", + "doc_d147a81bac29", + "doc_e67e762fb6ab", + "doc_f18f523b6ecc", + "doc_f2ae50c638a4", + "doc_f54217a0f2cf" + ], + "show": [ + "doc_136d7a909d51", + "doc_b6088784caab", + "doc_f2ae50c638a4" + ], + "shows": [ + "doc_40e30666bfa8", + "doc_4462f67b1c03", + "doc_464a2b4a8d35", + "doc_7b26cbd9a7ae", + "doc_a5120de838fb", + "doc_d1f7d83e0824", + "doc_fdf48d427fc0" + ], + "shutdown": [ + "doc_4462f67b1c03", + "doc_a5120de838fb" + ], + "side": [ + "doc_0383dbf277ba", + "doc_4a1cf8a90e83", + "doc_78b1717fafe3", + "doc_b6088784caab" + ], + "sidebar": [ + "doc_2a18de009d56", + "doc_3aa0b3792d22" + ], + "sidenav": [ + "doc_3aa0b3792d22" + ], + "signal": [ + "doc_43ad71a4ad0c", + "doc_57e2139f0873" + ], + "signals": [ + "doc_0ac01bce8c79", + "doc_43ad71a4ad0c", + "doc_57e2139f0873", + "doc_5ee8bbdbd8a8" + ], + "signature": [ + "doc_07174a8633ac", + "doc_64090a20f830", + "doc_745f751f1344", + "doc_c346f13ce597", + "doc_f92685a34f7e" + ], + "signatures": [ + "doc_deb460ffa5ee" + ], + "significant": [ + "doc_18d38fdacc9e", + "doc_e67e762fb6ab" + ], + "silent": [ + "doc_4055659fe573", + "doc_4e1ebaf40267", + "doc_50ac650fd8f6" + ], + "silently": [ + "doc_18d38fdacc9e", + "doc_4e1ebaf40267", + "doc_618e34441fa7", + "doc_a24b92541657" + ], + "similar": [ + "doc_0383dbf277ba", + "doc_0ac01bce8c79", + "doc_136d7a909d51", + "doc_d6ad61a79371" + ], + "similarity": [ + "doc_0383dbf277ba" + ], + "similarly": [ + "doc_745f751f1344" + ], + "simple": [ + "doc_2798948c3080", + "doc_57e2139f0873", + "doc_5a65ddab1445", + "doc_e7d508371e5c", + "doc_eeed52120ccf", + "doc_f12bbbc027a7", + "doc_f18f523b6ecc", + "doc_f54217a0f2cf" + ], + "simpler": [ + "doc_3f60413b3a06", + "doc_40e30666bfa8", + "doc_f18f523b6ecc" + ], + "simplest": [ + "doc_18d38fdacc9e", + "doc_1e46cbc6bfae", + "doc_57e2139f0873", + "doc_a5120de838fb", + "doc_f54217a0f2cf", + "doc_fdf48d427fc0" + ], + "simultaneously": [ + "doc_7b26cbd9a7ae" + ], + "since": [ + "doc_0ac01bce8c79", + "doc_1e46cbc6bfae" + ], + "single": [ + "doc_0ac01bce8c79", + "doc_1e46cbc6bfae", + "doc_3f60413b3a06", + "doc_464a2b4a8d35", + "doc_61fd9f682847", + "doc_64090a20f830", + "doc_745f751f1344", + "doc_9e9118ed2141", + "doc_a24b92541657", + "doc_a2e84472f1f9", + "doc_c346f13ce597", + "doc_e67e762fb6ab", + "doc_f12bbbc027a7", + "doc_f2ae50c638a4" + ], + "single-agent": [ + "doc_18d38fdacc9e" + ], + "single-case": [ + "doc_2a18de009d56" + ], + "single-container": [ + "doc_067033cfc945" + ], + "single-process": [ + "doc_0383dbf277ba", + "doc_f5f256549d84" + ], + "single-source": [ + "doc_a24b92541657" + ], + "single-turn": [ + "doc_f18f523b6ecc" + ], + "singleton": [ + "doc_64090a20f830", + "doc_78b1717fafe3" + ], + "sink": [ + "doc_1e46cbc6bfae", + "doc_2a18de009d56", + "doc_a24b92541657" + ], + "size": [ + "doc_61fd9f682847", + "doc_64090a20f830", + "doc_f5f256549d84" + ], + "skill": [ + "doc_2a18de009d56", + "doc_3eeb75d383b8", + "doc_5ee8bbdbd8a8", + "doc_745f751f1344", + "doc_9e9118ed2141", + "doc_a24b92541657", + "doc_b6088784caab", + "doc_d147a81bac29", + "doc_e67e762fb6ab", + "doc_f18f523b6ecc" + ], + "skill-provided": [ + "doc_e67e762fb6ab" + ], + "skill_tool_policy": [ + "doc_e67e762fb6ab" + ], + "skilldoc": [ + "doc_2a18de009d56" + ], + "skills": [ + "doc_18d38fdacc9e", + "doc_2a18de009d56", + "doc_3aa0b3792d22", + "doc_9e9118ed2141", + "doc_a24b92541657", + "doc_b6088784caab", + "doc_d147a81bac29", + "doc_deb460ffa5ee", + "doc_e67e762fb6ab", + "doc_f18f523b6ecc", + "doc_f2ae50c638a4" + ], + "skills_dir": [ + "doc_a24b92541657", + "doc_b6088784caab", + "doc_e67e762fb6ab" + ], + "skillstore": [ + "doc_2a18de009d56" + ], + "skilltoolpolicy": [ + "doc_9e9118ed2141", + "doc_e67e762fb6ab" + ], + "skip": [ + "doc_4e1ebaf40267", + "doc_a24b92541657" + ], + "skip_action": [ + "doc_a24b92541657" + ], + "skippable": [ + "doc_618e34441fa7" + ], + "skipped": [ + "doc_0ac01bce8c79", + "doc_d6ad61a79371" + ], + "skipping": [ + "doc_18d38fdacc9e" + ], + "skips": [ + "doc_4a1cf8a90e83", + "doc_d1f7d83e0824" + ], + "slack": [ + "doc_2a18de009d56", + "doc_a24b92541657" + ], + "slack-based": [ + "doc_d6ad61a79371" + ], + "slots": [ + "doc_f12bbbc027a7" + ], + "slow": [ + "doc_4462f67b1c03" + ], + "slowest": [ + "doc_f5f256549d84" + ], + "small": [ + "doc_2798948c3080", + "doc_f2ae50c638a4", + "doc_f5f256549d84" + ], + "smaller": [ + "doc_fdf48d427fc0" + ], + "smallest": [ + "doc_d147a81bac29", + "doc_f18f523b6ecc", + "doc_f5f256549d84" + ], + "snapshot": [ + "doc_07174a8633ac", + "doc_4a1cf8a90e83", + "doc_bbc97cbff254", + "doc_e7d508371e5c" + ], + "snapshots": [ + "doc_07174a8633ac", + "doc_2a18de009d56" + ], + "snippet": [ + "doc_07174a8633ac", + "doc_40e30666bfa8", + "doc_4462f67b1c03", + "doc_464a2b4a8d35", + "doc_64090a20f830", + "doc_7b26cbd9a7ae", + "doc_c346f13ce597", + "doc_d1f7d83e0824", + "doc_fdf48d427fc0" + ], + "snippets": [ + "doc_3aa0b3792d22", + "doc_bf91f8d5da74", + "doc_d147a81bac29", + "doc_deb460ffa5ee" + ], + "so": [ + "doc_07174a8633ac", + "doc_0ac01bce8c79", + "doc_109157c0d2d7", + "doc_3f60413b3a06", + "doc_4462f67b1c03", + "doc_4a1cf8a90e83", + "doc_5ee8bbdbd8a8", + "doc_618e34441fa7", + "doc_64090a20f830", + "doc_745f751f1344", + "doc_7b26cbd9a7ae", + "doc_9e9118ed2141", + "doc_b6088784caab", + "doc_e8794369a1a4" + ], + "socials": [ + "doc_3aa0b3792d22" + ], + "socket": [ + "doc_4462f67b1c03" + ], + "socket_connect_timeout": [ + "doc_4462f67b1c03" + ], + "socket_keepalive": [ + "doc_4462f67b1c03" + ], + "socket_timeout": [ + "doc_4462f67b1c03" + ], + "solution": [ + "doc_136d7a909d51" + ], + "solutions": [ + "doc_136d7a909d51" + ], + "solves": [ + "doc_18d38fdacc9e", + "doc_3ae3aaf0c12c" + ], + "some": [ + "doc_18d38fdacc9e", + "doc_3eeb75d383b8", + "doc_4055659fe573", + "doc_618e34441fa7", + "doc_e7d508371e5c", + "doc_f12bbbc027a7" + ], + "something": [ + "doc_4055659fe573", + "doc_b6088784caab" + ], + "sometimes": [ + "doc_3f60413b3a06" + ], + "sonnet": [ + "doc_f92685a34f7e" + ], + "soon": [ + "doc_f12bbbc027a7" + ], + "sorted": [ + "doc_618e34441fa7" + ], + "source": [ + "doc_0ac01bce8c79", + "doc_2798948c3080", + "doc_4055659fe573", + "doc_61fd9f682847", + "doc_745f751f1344", + "doc_e8794369a1a4", + "doc_eeed52120ccf", + "doc_fdf48d427fc0" + ], + "source-level": [ + "doc_d147a81bac29" + ], + "source-of-truth": [ + "doc_f18f523b6ecc" + ], + "source_agent": [ + "doc_75887f91c7df" + ], + "sources": [ + "doc_18d38fdacc9e", + "doc_2798948c3080", + "doc_eeed52120ccf" + ], + "spaces": [ + "doc_64090a20f830" + ], + "span": [ + "doc_2a18de009d56" + ], + "span_agent_run": [ + "doc_2a18de009d56" + ], + "spans": [ + "doc_50ac650fd8f6", + "doc_78a8125d2c7e" + ], + "special-casing": [ + "doc_618e34441fa7" + ], + "specialist": [ + "doc_18d38fdacc9e", + "doc_57e2139f0873", + "doc_a24b92541657", + "doc_a2e84472f1f9", + "doc_a5120de838fb", + "doc_e67e762fb6ab", + "doc_f12bbbc027a7", + "doc_f18f523b6ecc" + ], + "specialists": [ + "doc_18d38fdacc9e", + "doc_57e2139f0873", + "doc_d1f7d83e0824" + ], + "specialized": [ + "doc_78b1717fafe3", + "doc_e67e762fb6ab" + ], + "specific": [ + "doc_18d38fdacc9e", + "doc_2798948c3080", + "doc_2a18de009d56", + "doc_3f60413b3a06", + "doc_4462f67b1c03", + "doc_464a2b4a8d35", + "doc_5ee8bbdbd8a8", + "doc_618e34441fa7", + "doc_61fd9f682847", + "doc_a5120de838fb", + "doc_b6088784caab", + "doc_f5f256549d84" + ], + "specifically": [ + "doc_3eeb75d383b8" + ], + "specified": [ + "doc_1e46cbc6bfae", + "doc_3f60413b3a06" + ], + "specifies": [ + "doc_1e46cbc6bfae", + "doc_c93c115aeb85" + ], + "specify": [ + "doc_0ac01bce8c79", + "doc_eeed52120ccf" + ], + "spend": [ + "doc_067033cfc945", + "doc_18d38fdacc9e", + "doc_e67e762fb6ab" + ], + "spending": [ + "doc_567be1281e2f", + "doc_d6ad61a79371", + "doc_fdf48d427fc0" + ], + "spike": [ + "doc_78a8125d2c7e" + ], + "split": [ + "doc_18d38fdacc9e", + "doc_3ae3aaf0c12c", + "doc_57e2139f0873", + "doc_64090a20f830" + ], + "splits": [ + "doc_64090a20f830" + ], + "sqlite": [ + "doc_0383dbf277ba", + "doc_067033cfc945", + "doc_618e34441fa7", + "doc_61fd9f682847", + "doc_f5f256549d84" + ], + "sqlite3": [ + "doc_61fd9f682847" + ], + "sqlitememorystore": [ + "doc_2a18de009d56", + "doc_bbc97cbff254" + ], + "src": [ + "doc_3eeb75d383b8", + "doc_bbc97cbff254", + "doc_d147a81bac29", + "doc_f18f523b6ecc" + ], + "sre": [ + "doc_1e46cbc6bfae" + ], + "sse": [ + "doc_61fd9f682847" + ], + "ssl": [ + "doc_61fd9f682847" + ], + "stability": [ + "doc_618e34441fa7", + "doc_bbc97cbff254", + "doc_deb460ffa5ee" + ], + "stable": [ + "doc_2a18de009d56", + "doc_4e1ebaf40267", + "doc_618e34441fa7", + "doc_d147a81bac29" + ], + "stack": [ + "doc_3f60413b3a06", + "doc_78a8125d2c7e" + ], + "stage": [ + "doc_745f751f1344" + ], + "staging": [ + "doc_78a8125d2c7e" + ], + "stale": [ + "doc_4a1cf8a90e83" + ], + "standard": [ + "doc_78b1717fafe3", + "doc_e34fe24f9585" + ], + "start": [ + "doc_0383dbf277ba", + "doc_0ac01bce8c79", + "doc_18d38fdacc9e", + "doc_1e46cbc6bfae", + "doc_3aa0b3792d22", + "doc_3ae3aaf0c12c", + "doc_3f1383f72c5f", + "doc_4a1cf8a90e83", + "doc_50ac650fd8f6", + "doc_57e2139f0873", + "doc_78a8125d2c7e", + "doc_78b1717fafe3", + "doc_9e9118ed2141", + "doc_a5120de838fb", + "doc_d147a81bac29", + "doc_deb460ffa5ee", + "doc_e67e762fb6ab", + "doc_f54217a0f2cf" + ], + "started": [ + "doc_07174a8633ac", + "doc_1e46cbc6bfae", + "doc_4a1cf8a90e83", + "doc_f54217a0f2cf" + ], + "started_at_s": [ + "doc_4a1cf8a90e83" + ], + "starting": [ + "doc_07174a8633ac", + "doc_18d38fdacc9e", + "doc_3ae3aaf0c12c", + "doc_eeed52120ccf", + "doc_f92685a34f7e" + ], + "starts": [ + "doc_0383dbf277ba", + "doc_618e34441fa7", + "doc_745f751f1344" + ], + "startup": [ + "doc_61fd9f682847" + ], + "stat": [ + "doc_64090a20f830" + ], + "stat-based": [ + "doc_64090a20f830" + ], + "state": [ + "doc_0383dbf277ba", + "doc_07174a8633ac", + "doc_0ac01bce8c79", + "doc_109157c0d2d7", + "doc_136d7a909d51", + "doc_1e46cbc6bfae", + "doc_2a18de009d56", + "doc_3f1383f72c5f", + "doc_4055659fe573", + "doc_4a1cf8a90e83", + "doc_4e1ebaf40267", + "doc_50ac650fd8f6", + "doc_5ee8bbdbd8a8", + "doc_75887f91c7df", + "doc_78a8125d2c7e", + "doc_78b1717fafe3", + "doc_9e9118ed2141", + "doc_a24b92541657", + "doc_a5120de838fb", + "doc_bbc97cbff254", + "doc_bf91f8d5da74", + "doc_c93c115aeb85", + "doc_d147a81bac29", + "doc_deb460ffa5ee", + "doc_e67e762fb6ab", + "doc_e7d508371e5c", + "doc_f18f523b6ecc", + "doc_f54217a0f2cf", + "doc_f5f256549d84", + "doc_fdf48d427fc0" + ], + "state_snapshot": [ + "doc_bbc97cbff254", + "doc_e7d508371e5c" + ], + "stateful": [ + "doc_5ee8bbdbd8a8" + ], + "statement": [ + "doc_618e34441fa7" + ], + "statements": [ + "doc_618e34441fa7" + ], + "stateretentionpolicy": [ + "doc_07174a8633ac" + ], + "states": [ + "doc_4a1cf8a90e83", + "doc_618e34441fa7", + "doc_e7d508371e5c" + ], + "static": [ + "doc_2798948c3080", + "doc_a24b92541657" + ], + "statistics": [ + "doc_2a18de009d56", + "doc_618e34441fa7" + ], + "status": [ + "doc_109157c0d2d7", + "doc_18d38fdacc9e", + "doc_567be1281e2f", + "doc_9e9118ed2141", + "doc_f54217a0f2cf" + ], + "stays": [ + "doc_109157c0d2d7", + "doc_464a2b4a8d35", + "doc_c93c115aeb85" + ], + "stdin": [ + "doc_a24b92541657" + ], + "stdout": [ + "doc_78a8125d2c7e", + "doc_a24b92541657" + ], + "step": [ + "doc_0383dbf277ba", + "doc_07174a8633ac", + "doc_0ac01bce8c79", + "doc_109157c0d2d7", + "doc_18d38fdacc9e", + "doc_1e46cbc6bfae", + "doc_3f1383f72c5f", + "doc_4055659fe573", + "doc_4a1cf8a90e83", + "doc_4e1ebaf40267", + "doc_50ac650fd8f6", + "doc_567be1281e2f", + "doc_5a65ddab1445", + "doc_5ee8bbdbd8a8", + "doc_745f751f1344", + "doc_a24b92541657", + "doc_a2e84472f1f9", + "doc_c346f13ce597", + "doc_e67e762fb6ab", + "doc_e7d508371e5c", + "doc_eeed52120ccf", + "doc_f18f523b6ecc", + "doc_f54217a0f2cf" + ], + "step-by-step": [ + "doc_4a1cf8a90e83", + "doc_745f751f1344" + ], + "step_name": [ + "doc_75887f91c7df" + ], + "step_started": [ + "doc_109157c0d2d7", + "doc_4a1cf8a90e83", + "doc_5ee8bbdbd8a8", + "doc_f54217a0f2cf" + ], + "steps": [ + "doc_0383dbf277ba", + "doc_067033cfc945", + "doc_109157c0d2d7", + "doc_136d7a909d51", + "doc_18d38fdacc9e", + "doc_1c5f37ea0ad3", + "doc_2798948c3080", + "doc_3ae3aaf0c12c", + "doc_4055659fe573", + "doc_4e1ebaf40267", + "doc_567be1281e2f", + "doc_57e2139f0873", + "doc_5a65ddab1445", + "doc_61fd9f682847", + "doc_7320ab982fab", + "doc_75887f91c7df", + "doc_78a8125d2c7e", + "doc_78b1717fafe3", + "doc_9e9118ed2141", + "doc_a24b92541657", + "doc_b6088784caab", + "doc_c93c115aeb85", + "doc_d147a81bac29", + "doc_deb460ffa5ee", + "doc_e34fe24f9585", + "doc_e67e762fb6ab", + "doc_e7d508371e5c", + "doc_e8794369a1a4", + "doc_eeed52120ccf", + "doc_f12bbbc027a7", + "doc_f18f523b6ecc", + "doc_f1e32ed2ffce", + "doc_f92685a34f7e" + ], + "still": [ + "doc_07174a8633ac", + "doc_0ac01bce8c79", + "doc_4055659fe573", + "doc_618e34441fa7" + ], + "stop": [ + "doc_136d7a909d51", + "doc_4055659fe573", + "doc_5ee8bbdbd8a8", + "doc_618e34441fa7", + "doc_7320ab982fab", + "doc_7b26cbd9a7ae", + "doc_e8794369a1a4" + ], + "stopped": [ + "doc_136d7a909d51", + "doc_4055659fe573", + "doc_5ee8bbdbd8a8", + "doc_e8794369a1a4" + ], + "stops": [ + "doc_50ac650fd8f6", + "doc_745f751f1344", + "doc_a24b92541657" + ], + "storage": [ + "doc_0383dbf277ba", + "doc_07174a8633ac", + "doc_a5120de838fb", + "doc_f5f256549d84" + ], + "store": [ + "doc_0383dbf277ba", + "doc_067033cfc945", + "doc_07174a8633ac", + "doc_136d7a909d51", + "doc_2798948c3080", + "doc_2a18de009d56", + "doc_4462f67b1c03", + "doc_4a1cf8a90e83", + "doc_567be1281e2f", + "doc_618e34441fa7", + "doc_75887f91c7df", + "doc_b6088784caab", + "doc_e67e762fb6ab" + ], + "stored": [ + "doc_0383dbf277ba", + "doc_07174a8633ac", + "doc_4a1cf8a90e83", + "doc_4e1ebaf40267", + "doc_64090a20f830", + "doc_78b1717fafe3" + ], + "stores": [ + "doc_2a18de009d56", + "doc_5ee8bbdbd8a8", + "doc_bbc97cbff254", + "doc_bf91f8d5da74", + "doc_d147a81bac29", + "doc_deb460ffa5ee" + ], + "str": [ + "doc_07174a8633ac", + "doc_1e46cbc6bfae", + "doc_464a2b4a8d35", + "doc_4a1cf8a90e83", + "doc_5ee8bbdbd8a8", + "doc_64090a20f830", + "doc_745f751f1344", + "doc_75887f91c7df", + "doc_a24b92541657", + "doc_a2e84472f1f9", + "doc_e67e762fb6ab", + "doc_e7d508371e5c", + "doc_e8794369a1a4", + "doc_f54217a0f2cf" + ], + "straight": [ + "doc_d1f7d83e0824" + ], + "strategies": [ + "doc_a5120de838fb", + "doc_f12bbbc027a7" + ], + "strategy": [ + "doc_5ee8bbdbd8a8", + "doc_a24b92541657" + ], + "stream": [ + "doc_109157c0d2d7", + "doc_136d7a909d51", + "doc_2a18de009d56", + "doc_3f60413b3a06", + "doc_43ad71a4ad0c", + "doc_5ee8bbdbd8a8", + "doc_618e34441fa7", + "doc_61fd9f682847", + "doc_7b26cbd9a7ae", + "doc_c346f13ce597", + "doc_d147a81bac29", + "doc_d6ad61a79371", + "doc_eeed52120ccf", + "doc_f18f523b6ecc", + "doc_f2ae50c638a4", + "doc_f54217a0f2cf", + "doc_f5f256549d84" + ], + "stream_completed": [ + "doc_2a18de009d56" + ], + "stream_timeout_s": [ + "doc_4462f67b1c03" + ], + "streamed": [ + "doc_f54217a0f2cf" + ], + "streaming": [ + "doc_109157c0d2d7", + "doc_136d7a909d51", + "doc_2a18de009d56", + "doc_3aa0b3792d22", + "doc_3f60413b3a06", + "doc_4462f67b1c03", + "doc_5ee8bbdbd8a8", + "doc_618e34441fa7", + "doc_78a8125d2c7e", + "doc_7b26cbd9a7ae", + "doc_a5120de838fb", + "doc_bbc97cbff254", + "doc_bf91f8d5da74", + "doc_c346f13ce597", + "doc_d147a81bac29", + "doc_deb460ffa5ee", + "doc_e7d508371e5c", + "doc_eeed52120ccf", + "doc_f18f523b6ecc", + "doc_f1e32ed2ffce", + "doc_f2ae50c638a4", + "doc_f92685a34f7e", + "doc_fdf48d427fc0" + ], + "streaming_chat_with_memory": [ + "doc_3aa0b3792d22" + ], + "streams": [ + "doc_bbc97cbff254" + ], + "strict": [ + "doc_64090a20f830" + ], + "stricter": [ + "doc_a2e84472f1f9" + ], + "string": [ + "doc_2798948c3080", + "doc_4a1cf8a90e83", + "doc_61fd9f682847", + "doc_64090a20f830", + "doc_9e9118ed2141", + "doc_a24b92541657", + "doc_eeed52120ccf" + ], + "strings": [ + "doc_2798948c3080", + "doc_64090a20f830" + ], + "strip": [ + "doc_50ac650fd8f6" + ], + "stripped": [ + "doc_9e9118ed2141" + ], + "structure": [ + "doc_07174a8633ac", + "doc_618e34441fa7", + "doc_f18f523b6ecc", + "doc_f54217a0f2cf" + ], + "structured": [ + "doc_136d7a909d51", + "doc_2a18de009d56", + "doc_3f1383f72c5f", + "doc_3f60413b3a06", + "doc_4a1cf8a90e83", + "doc_5ee8bbdbd8a8", + "doc_618e34441fa7", + "doc_61fd9f682847", + "doc_7320ab982fab", + "doc_75887f91c7df", + "doc_78a8125d2c7e", + "doc_9e9118ed2141", + "doc_a5120de838fb", + "doc_e67e762fb6ab", + "doc_e8794369a1a4", + "doc_f1e32ed2ffce", + "doc_f54217a0f2cf" + ], + "structured_response": [ + "doc_3f60413b3a06", + "doc_5ee8bbdbd8a8" + ], + "structures": [ + "doc_50ac650fd8f6" + ], + "style": [ + "doc_3aa0b3792d22" + ], + "sub-expertise": [ + "doc_e67e762fb6ab" + ], + "sub-module": [ + "doc_2a18de009d56" + ], + "sub-modules": [ + "doc_2a18de009d56" + ], + "subagent": [ + "doc_07174a8633ac", + "doc_1e46cbc6bfae", + "doc_3f60413b3a06", + "doc_4055659fe573", + "doc_50ac650fd8f6", + "doc_618e34441fa7", + "doc_a24b92541657", + "doc_a2e84472f1f9", + "doc_a5120de838fb", + "doc_bbc97cbff254", + "doc_bf91f8d5da74", + "doc_deb460ffa5ee", + "doc_e67e762fb6ab", + "doc_e7d508371e5c", + "doc_f12bbbc027a7", + "doc_f18f523b6ecc", + "doc_f54217a0f2cf" + ], + "subagent_completed": [ + "doc_f54217a0f2cf" + ], + "subagent_executions": [ + "doc_1e46cbc6bfae", + "doc_4a1cf8a90e83", + "doc_a2e84472f1f9", + "doc_bbc97cbff254", + "doc_bf91f8d5da74", + "doc_e7d508371e5c" + ], + "subagent_failure_policy": [ + "doc_a24b92541657", + "doc_a2e84472f1f9" + ], + "subagent_name": [ + "doc_a2e84472f1f9", + "doc_f54217a0f2cf" + ], + "subagent_parallelism_mode": [ + "doc_a24b92541657", + "doc_e67e762fb6ab" + ], + "subagent_queue_backpressure_limit": [ + "doc_a24b92541657" + ], + "subagent_router": [ + "doc_a24b92541657", + "doc_e67e762fb6ab" + ], + "subagent_started": [ + "doc_f54217a0f2cf" + ], + "subagentexecutionrecord": [ + "doc_4a1cf8a90e83", + "doc_618e34441fa7", + "doc_a2e84472f1f9", + "doc_e7d508371e5c" + ], + "subagentrouter": [ + "doc_a24b92541657", + "doc_e67e762fb6ab" + ], + "subagentroutingerror": [ + "doc_2a18de009d56" + ], + "subagents": [ + "doc_18d38fdacc9e", + "doc_57e2139f0873", + "doc_618e34441fa7", + "doc_a24b92541657", + "doc_a2e84472f1f9", + "doc_a5120de838fb", + "doc_deb460ffa5ee", + "doc_e67e762fb6ab", + "doc_eeed52120ccf", + "doc_f12bbbc027a7", + "doc_f18f523b6ecc", + "doc_f2ae50c638a4" + ], + "subagents_with_router": [ + "doc_3aa0b3792d22" + ], + "subclass": [ + "doc_2a18de009d56" + ], + "subdirectories": [ + "doc_bbc97cbff254" + ], + "subdirectory": [ + "doc_618e34441fa7", + "doc_b6088784caab" + ], + "subject": [ + "doc_2a18de009d56" + ], + "submits": [ + "doc_75887f91c7df" + ], + "subsequent": [ + "doc_5ee8bbdbd8a8", + "doc_a24b92541657" + ], + "substitution": [ + "doc_a5120de838fb" + ], + "subsystem": [ + "doc_3eeb75d383b8" + ], + "subsystems": [ + "doc_f18f523b6ecc" + ], + "subtasks": [ + "doc_a2e84472f1f9" + ], + "succeed": [ + "doc_4055659fe573", + "doc_f12bbbc027a7" + ], + "succeeded": [ + "doc_745f751f1344" + ], + "succeeds": [ + "doc_f12bbbc027a7" + ], + "success": [ + "doc_0ac01bce8c79", + "doc_50ac650fd8f6", + "doc_618e34441fa7", + "doc_745f751f1344", + "doc_78a8125d2c7e", + "doc_a24b92541657", + "doc_a2e84472f1f9", + "doc_d147a81bac29", + "doc_e7d508371e5c", + "doc_f54217a0f2cf" + ], + "successful": [ + "doc_a2e84472f1f9", + "doc_f54217a0f2cf" + ], + "successfully": [ + "doc_109157c0d2d7", + "doc_78b1717fafe3", + "doc_a2e84472f1f9", + "doc_e7d508371e5c", + "doc_f12bbbc027a7", + "doc_f54217a0f2cf" + ], + "such": [ + "doc_07174a8633ac", + "doc_0ac01bce8c79", + "doc_3eeb75d383b8", + "doc_618e34441fa7", + "doc_bf91f8d5da74", + "doc_d147a81bac29" + ], + "suffix": [ + "doc_64090a20f830" + ], + "suite": [ + "doc_18d38fdacc9e", + "doc_2798948c3080", + "doc_2a18de009d56", + "doc_618e34441fa7", + "doc_c93c115aeb85" + ], + "suite-level": [ + "doc_c93c115aeb85" + ], + "suite_report_payload": [ + "doc_2a18de009d56", + "doc_618e34441fa7" + ], + "suites": [ + "doc_bbc97cbff254" + ], + "summaries": [ + "doc_07174a8633ac" + ], + "summarization": [ + "doc_3f60413b3a06", + "doc_57e2139f0873" + ], + "summarize": [ + "doc_f5f256549d84" + ], + "summarizes": [ + "doc_2a18de009d56" + ], + "summary": [ + "doc_618e34441fa7", + "doc_f54217a0f2cf" + ], + "support": [ + "doc_0383dbf277ba", + "doc_136d7a909d51", + "doc_2a18de009d56", + "doc_3f60413b3a06", + "doc_40e30666bfa8", + "doc_5a65ddab1445", + "doc_5ee8bbdbd8a8", + "doc_64090a20f830", + "doc_745f751f1344", + "doc_78b1717fafe3" + ], + "support_agent": [ + "doc_64090a20f830" + ], + "supported": [ + "doc_b6088784caab", + "doc_deb460ffa5ee", + "doc_f92685a34f7e" + ], + "supports": [ + "doc_0383dbf277ba", + "doc_109157c0d2d7", + "doc_2798948c3080", + "doc_3f60413b3a06", + "doc_5ee8bbdbd8a8", + "doc_64090a20f830", + "doc_a2e84472f1f9", + "doc_d6ad61a79371", + "doc_e34fe24f9585", + "doc_eeed52120ccf", + "doc_f1e32ed2ffce", + "doc_f92685a34f7e" + ], + "sure": [ + "doc_50ac650fd8f6" + ], + "surface": [ + "doc_3eeb75d383b8" + ], + "surfaced": [ + "doc_745f751f1344" + ], + "surfaces": [ + "doc_3eeb75d383b8", + "doc_d147a81bac29" + ], + "survive": [ + "doc_3ae3aaf0c12c", + "doc_4a1cf8a90e83" + ], + "suspended": [ + "doc_0ac01bce8c79" + ], + "suspends": [ + "doc_a24b92541657" + ], + "svg": [ + "doc_3aa0b3792d22" + ], + "swap": [ + "doc_4e1ebaf40267" + ], + "switch": [ + "doc_2798948c3080", + "doc_5a65ddab1445", + "doc_78a8125d2c7e" + ], + "symbol": [ + "doc_3eeb75d383b8", + "doc_d147a81bac29" + ], + "symptoms": [ + "doc_136d7a909d51" + ], + "sync": [ + "doc_bbc97cbff254", + "doc_bf91f8d5da74", + "doc_deb460ffa5ee" + ], + "synchronous": [ + "doc_bbc97cbff254" + ], + "synchronously": [ + "doc_1e46cbc6bfae", + "doc_a5120de838fb" + ], + "syntax": [ + "doc_2798948c3080", + "doc_64090a20f830" + ], + "synthesis": [ + "doc_a2e84472f1f9" + ], + "synthesizes": [ + "doc_a2e84472f1f9" + ], + "synthetic": [ + "doc_3f1383f72c5f" + ], + "system": [ + "doc_0383dbf277ba", + "doc_0ac01bce8c79", + "doc_136d7a909d51", + "doc_18d38fdacc9e", + "doc_1c5f37ea0ad3", + "doc_1e46cbc6bfae", + "doc_2798948c3080", + "doc_3ae3aaf0c12c", + "doc_40e30666bfa8", + "doc_464a2b4a8d35", + "doc_4a1cf8a90e83", + "doc_50ac650fd8f6", + "doc_567be1281e2f", + "doc_57e2139f0873", + "doc_5ee8bbdbd8a8", + "doc_618e34441fa7", + "doc_61fd9f682847", + "doc_64090a20f830", + "doc_745f751f1344", + "doc_75887f91c7df", + "doc_a24b92541657", + "doc_a5120de838fb", + "doc_b6088784caab", + "doc_c346f13ce597", + "doc_d1f7d83e0824", + "doc_e67e762fb6ab", + "doc_e8794369a1a4", + "doc_eeed52120ccf", + "doc_f5f256549d84", + "doc_fdf48d427fc0" + ], + "system-prompts": [ + "doc_3aa0b3792d22" + ], + "system_prompt_loader": [ + "doc_3aa0b3792d22" + ], + "systems": [ + "doc_1c5f37ea0ad3", + "doc_43ad71a4ad0c", + "doc_50ac650fd8f6", + "doc_57e2139f0873", + "doc_c346f13ce597", + "doc_d6ad61a79371", + "doc_e34fe24f9585", + "doc_f18f523b6ecc" + ], + "tab": [ + "doc_3aa0b3792d22" + ], + "table": [ + "doc_0ac01bce8c79", + "doc_3eeb75d383b8", + "doc_f54217a0f2cf" + ], + "tables": [ + "doc_067033cfc945" + ], + "tabs": [ + "doc_3aa0b3792d22" + ], + "tail": [ + "doc_a24b92541657" + ], + "take": [ + "doc_0383dbf277ba", + "doc_067033cfc945", + "doc_57e2139f0873", + "doc_61fd9f682847", + "doc_9e9118ed2141", + "doc_d6ad61a79371" + ], + "takes": [ + "doc_2798948c3080" + ], + "taking": [ + "doc_b6088784caab" + ], + "talk": [ + "doc_f12bbbc027a7" + ], + "target": [ + "doc_1c5f37ea0ad3" + ], + "target_agent": [ + "doc_75887f91c7df" + ], + "target_arg": [ + "doc_0ac01bce8c79" + ], + "targeted": [ + "doc_3eeb75d383b8", + "doc_d147a81bac29" + ], + "targets": [ + "doc_a24b92541657", + "doc_d147a81bac29" + ], + "task": [ + "doc_07174a8633ac", + "doc_2a18de009d56", + "doc_3ae3aaf0c12c", + "doc_50ac650fd8f6", + "doc_57e2139f0873", + "doc_618e34441fa7", + "doc_61fd9f682847", + "doc_75887f91c7df", + "doc_78b1717fafe3", + "doc_a2e84472f1f9", + "doc_bbc97cbff254", + "doc_d147a81bac29", + "doc_e67e762fb6ab", + "doc_eeed52120ccf", + "doc_f12bbbc027a7", + "doc_f5f256549d84", + "doc_fdf48d427fc0" + ], + "task-queues": [ + "doc_3aa0b3792d22" + ], + "task_id": [ + "doc_75887f91c7df" + ], + "taskitem": [ + "doc_57e2139f0873" + ], + "taskqueue": [ + "doc_57e2139f0873" + ], + "tasks": [ + "doc_067033cfc945", + "doc_18d38fdacc9e", + "doc_3f1383f72c5f", + "doc_57e2139f0873", + "doc_5a65ddab1445", + "doc_a24b92541657", + "doc_e67e762fb6ab", + "doc_f5f256549d84" + ], + "taskworker": [ + "doc_bbc97cbff254" + ], + "tcp": [ + "doc_4462f67b1c03" + ], + "teams": [ + "doc_f12bbbc027a7", + "doc_f18f523b6ecc" + ], + "telemetry": [ + "doc_067033cfc945", + "doc_07174a8633ac", + "doc_0ac01bce8c79", + "doc_18d38fdacc9e", + "doc_1e46cbc6bfae", + "doc_2a18de009d56", + "doc_3ae3aaf0c12c", + "doc_4055659fe573", + "doc_40e30666bfa8", + "doc_4e1ebaf40267", + "doc_50ac650fd8f6", + "doc_567be1281e2f", + "doc_5a65ddab1445", + "doc_618e34441fa7", + "doc_745f751f1344", + "doc_78a8125d2c7e", + "doc_78b1717fafe3", + "doc_a24b92541657", + "doc_a5120de838fb", + "doc_c93c115aeb85", + "doc_d147a81bac29", + "doc_deb460ffa5ee", + "doc_e34fe24f9585", + "doc_e67e762fb6ab", + "doc_e7d508371e5c", + "doc_f18f523b6ecc", + "doc_f2ae50c638a4", + "doc_f5f256549d84", + "doc_fdf48d427fc0" + ], + "telemetry_config": [ + "doc_a24b92541657" + ], + "telemetrysink": [ + "doc_2a18de009d56", + "doc_a24b92541657" + ], + "tell": [ + "doc_0383dbf277ba", + "doc_0ac01bce8c79", + "doc_2798948c3080", + "doc_64090a20f830", + "doc_b6088784caab", + "doc_e67e762fb6ab", + "doc_f54217a0f2cf" + ], + "telling": [ + "doc_50ac650fd8f6" + ], + "temperature": [ + "doc_136d7a909d51", + "doc_3f60413b3a06", + "doc_5ee8bbdbd8a8", + "doc_7320ab982fab", + "doc_e8794369a1a4", + "doc_eeed52120ccf" + ], + "template": [ + "doc_0383dbf277ba", + "doc_2798948c3080", + "doc_2a18de009d56", + "doc_64090a20f830", + "doc_a5120de838fb" + ], + "templates": [ + "doc_18d38fdacc9e", + "doc_2798948c3080", + "doc_64090a20f830" + ], + "templating": [ + "doc_2798948c3080", + "doc_64090a20f830" + ], + "temporaries": [ + "doc_0383dbf277ba" + ], + "terminal": [ + "doc_07174a8633ac", + "doc_0ac01bce8c79", + "doc_109157c0d2d7", + "doc_18d38fdacc9e", + "doc_1e46cbc6bfae", + "doc_4055659fe573", + "doc_4a1cf8a90e83", + "doc_4e1ebaf40267", + "doc_50ac650fd8f6", + "doc_5ee8bbdbd8a8", + "doc_618e34441fa7", + "doc_78b1717fafe3", + "doc_a24b92541657", + "doc_bbc97cbff254", + "doc_bf91f8d5da74", + "doc_deb460ffa5ee", + "doc_e7d508371e5c", + "doc_eeed52120ccf", + "doc_f18f523b6ecc", + "doc_f2ae50c638a4", + "doc_f54217a0f2cf", + "doc_f5f256549d84" + ], + "terminal_result": [ + "doc_4a1cf8a90e83" + ], + "terminated": [ + "doc_f54217a0f2cf" + ], + "terminates": [ + "doc_0ac01bce8c79", + "doc_5ee8bbdbd8a8", + "doc_618e34441fa7", + "doc_f54217a0f2cf", + "doc_fdf48d427fc0" + ], + "termination": [ + "doc_a24b92541657" + ], + "test": [ + "doc_136d7a909d51", + "doc_18d38fdacc9e", + "doc_2798948c3080", + "doc_3ae3aaf0c12c", + "doc_3eeb75d383b8", + "doc_50ac650fd8f6", + "doc_618e34441fa7", + "doc_c93c115aeb85" + ], + "testable": [ + "doc_5a65ddab1445", + "doc_f18f523b6ecc" + ], + "tested": [ + "doc_618e34441fa7", + "doc_f18f523b6ecc" + ], + "tested-behaviors": [ + "doc_3aa0b3792d22" + ], + "testing": [ + "doc_0383dbf277ba", + "doc_18d38fdacc9e", + "doc_78a8125d2c7e", + "doc_78b1717fafe3", + "doc_a24b92541657", + "doc_d6ad61a79371" + ], + "tests": [ + "doc_18d38fdacc9e", + "doc_3eeb75d383b8", + "doc_50ac650fd8f6", + "doc_618e34441fa7", + "doc_bbc97cbff254", + "doc_c93c115aeb85", + "doc_d147a81bac29", + "doc_e7d508371e5c", + "doc_f5f256549d84" + ], + "text": [ + "doc_0383dbf277ba", + "doc_109157c0d2d7", + "doc_136d7a909d51", + "doc_1e46cbc6bfae", + "doc_3f60413b3a06", + "doc_4a1cf8a90e83", + "doc_50ac650fd8f6", + "doc_57e2139f0873", + "doc_5ee8bbdbd8a8", + "doc_618e34441fa7", + "doc_64090a20f830", + "doc_745f751f1344", + "doc_7b26cbd9a7ae", + "doc_a24b92541657", + "doc_a2e84472f1f9", + "doc_bbc97cbff254", + "doc_bf91f8d5da74", + "doc_c93c115aeb85", + "doc_d6ad61a79371", + "doc_deb460ffa5ee", + "doc_e7d508371e5c", + "doc_e8794369a1a4", + "doc_eeed52120ccf", + "doc_f18f523b6ecc", + "doc_f1e32ed2ffce", + "doc_f2ae50c638a4", + "doc_f54217a0f2cf" + ], + "text-only": [ + "doc_0ac01bce8c79", + "doc_e7d508371e5c" + ], + "text_delta": [ + "doc_109157c0d2d7", + "doc_2a18de009d56", + "doc_5ee8bbdbd8a8", + "doc_eeed52120ccf", + "doc_f54217a0f2cf" + ], + "th": [ + "doc_78a8125d2c7e" + ], + "than": [ + "doc_07174a8633ac", + "doc_136d7a909d51", + "doc_4055659fe573", + "doc_4a1cf8a90e83", + "doc_618e34441fa7", + "doc_64090a20f830", + "doc_b6088784caab", + "doc_bbc97cbff254", + "doc_f18f523b6ecc" + ], + "their": [ + "doc_43ad71a4ad0c", + "doc_a24b92541657", + "doc_a2e84472f1f9", + "doc_d6ad61a79371", + "doc_eeed52120ccf", + "doc_f54217a0f2cf", + "doc_f92685a34f7e" + ], + "them": [ + "doc_136d7a909d51", + "doc_18d38fdacc9e", + "doc_3eeb75d383b8", + "doc_3f60413b3a06", + "doc_40e30666bfa8", + "doc_43ad71a4ad0c", + "doc_4a1cf8a90e83", + "doc_4e1ebaf40267", + "doc_7320ab982fab", + "doc_745f751f1344", + "doc_a2e84472f1f9", + "doc_b6088784caab", + "doc_c93c115aeb85", + "doc_f12bbbc027a7", + "doc_f2ae50c638a4", + "doc_f5f256549d84", + "doc_fdf48d427fc0" + ], + "theme": [ + "doc_3aa0b3792d22" + ], + "themselves": [ + "doc_e67e762fb6ab" + ], + "then": [ + "doc_0383dbf277ba", + "doc_18d38fdacc9e", + "doc_3ae3aaf0c12c", + "doc_4e1ebaf40267", + "doc_5ee8bbdbd8a8", + "doc_61fd9f682847", + "doc_745f751f1344", + "doc_9e9118ed2141", + "doc_a24b92541657", + "doc_f92685a34f7e" + ], + "they": [ + "doc_109157c0d2d7", + "doc_3eeb75d383b8", + "doc_40e30666bfa8", + "doc_4e1ebaf40267", + "doc_50ac650fd8f6", + "doc_5ee8bbdbd8a8", + "doc_61fd9f682847", + "doc_64090a20f830", + "doc_9e9118ed2141", + "doc_bbc97cbff254", + "doc_c93c115aeb85", + "doc_e67e762fb6ab", + "doc_f12bbbc027a7", + "doc_f54217a0f2cf" + ], + "things": [ + "doc_7b26cbd9a7ae" + ], + "think": [ + "doc_50ac650fd8f6" + ], + "thinking": [ + "doc_a24b92541657", + "doc_e67e762fb6ab" + ], + "thinking_effort": [ + "doc_a24b92541657" + ], + "third-party": [ + "doc_57e2139f0873" + ], + "thread": [ + "doc_0383dbf277ba", + "doc_07174a8633ac", + "doc_18d38fdacc9e", + "doc_1e46cbc6bfae", + "doc_2a18de009d56", + "doc_5a65ddab1445", + "doc_61fd9f682847", + "doc_75887f91c7df", + "doc_7b26cbd9a7ae", + "doc_a5120de838fb", + "doc_bf91f8d5da74", + "doc_deb460ffa5ee", + "doc_e7d508371e5c", + "doc_eeed52120ccf", + "doc_f18f523b6ecc", + "doc_f2ae50c638a4", + "doc_f54217a0f2cf", + "doc_f5f256549d84" + ], + "thread-based": [ + "doc_61fd9f682847", + "doc_a5120de838fb", + "doc_e7d508371e5c" + ], + "thread_id": [ + "doc_0383dbf277ba", + "doc_07174a8633ac", + "doc_136d7a909d51", + "doc_1e46cbc6bfae", + "doc_2798948c3080", + "doc_4a1cf8a90e83", + "doc_5a65ddab1445", + "doc_5ee8bbdbd8a8", + "doc_75887f91c7df", + "doc_7b26cbd9a7ae", + "doc_bbc97cbff254", + "doc_bf91f8d5da74", + "doc_e7d508371e5c", + "doc_f2ae50c638a4", + "doc_f54217a0f2cf" + ], + "threads": [ + "doc_0383dbf277ba", + "doc_07174a8633ac", + "doc_e7d508371e5c", + "doc_f5f256549d84" + ], + "threat": [ + "doc_464a2b4a8d35", + "doc_567be1281e2f", + "doc_64090a20f830" + ], + "three": [ + "doc_18d38fdacc9e", + "doc_1c5f37ea0ad3", + "doc_1e46cbc6bfae", + "doc_2798948c3080", + "doc_4055659fe573", + "doc_4a1cf8a90e83", + "doc_50ac650fd8f6", + "doc_64090a20f830", + "doc_745f751f1344", + "doc_c346f13ce597", + "doc_d6ad61a79371", + "doc_e7d508371e5c", + "doc_f18f523b6ecc", + "doc_f1e32ed2ffce" + ], + "threshold": [ + "doc_067033cfc945", + "doc_a24b92541657", + "doc_fdf48d427fc0" + ], + "through": [ + "doc_0ac01bce8c79", + "doc_109157c0d2d7", + "doc_1e46cbc6bfae", + "doc_3ae3aaf0c12c", + "doc_3eeb75d383b8", + "doc_40e30666bfa8", + "doc_4a1cf8a90e83", + "doc_50ac650fd8f6", + "doc_567be1281e2f", + "doc_57e2139f0873", + "doc_5a65ddab1445", + "doc_5ee8bbdbd8a8", + "doc_61fd9f682847", + "doc_64090a20f830", + "doc_7320ab982fab", + "doc_745f751f1344", + "doc_9e9118ed2141", + "doc_a2e84472f1f9", + "doc_a5120de838fb", + "doc_c346f13ce597", + "doc_c93c115aeb85", + "doc_d1f7d83e0824", + "doc_d6ad61a79371", + "doc_deb460ffa5ee", + "doc_e7d508371e5c", + "doc_e8794369a1a4", + "doc_eeed52120ccf", + "doc_f54217a0f2cf", + "doc_f5f256549d84", + "doc_fdf48d427fc0" + ], + "throughput": [ + "doc_4462f67b1c03", + "doc_4a1cf8a90e83", + "doc_7320ab982fab", + "doc_f5f256549d84" + ], + "thumb": [ + "doc_b6088784caab" + ], + "ticket": [ + "doc_3f1383f72c5f", + "doc_a24b92541657" + ], + "ticket_id": [ + "doc_9e9118ed2141", + "doc_f54217a0f2cf" + ], + "tickets": [ + "doc_9e9118ed2141", + "doc_a24b92541657" + ], + "tier": [ + "doc_a5120de838fb" + ], + "tiers": [ + "doc_d1f7d83e0824" + ], + "tight": [ + "doc_7320ab982fab", + "doc_a5120de838fb", + "doc_f12bbbc027a7", + "doc_f5f256549d84" + ], + "tightly": [ + "doc_43ad71a4ad0c", + "doc_745f751f1344" + ], + "time": [ + "doc_0383dbf277ba", + "doc_067033cfc945", + "doc_07174a8633ac", + "doc_18d38fdacc9e", + "doc_4055659fe573", + "doc_50ac650fd8f6", + "doc_567be1281e2f", + "doc_78a8125d2c7e", + "doc_9e9118ed2141", + "doc_a2e84472f1f9", + "doc_a5120de838fb", + "doc_b6088784caab", + "doc_d1f7d83e0824", + "doc_f18f523b6ecc", + "doc_fdf48d427fc0" + ], + "timed": [ + "doc_0ac01bce8c79", + "doc_4055659fe573" + ], + "timeline": [ + "doc_0ac01bce8c79" + ], + "timeout": [ + "doc_0383dbf277ba", + "doc_0ac01bce8c79", + "doc_136d7a909d51", + "doc_4055659fe573", + "doc_4462f67b1c03", + "doc_50ac650fd8f6", + "doc_5ee8bbdbd8a8", + "doc_618e34441fa7", + "doc_61fd9f682847", + "doc_7320ab982fab", + "doc_745f751f1344", + "doc_75887f91c7df", + "doc_78b1717fafe3", + "doc_9e9118ed2141", + "doc_a24b92541657", + "doc_a2e84472f1f9", + "doc_a5120de838fb", + "doc_c346f13ce597", + "doc_d147a81bac29", + "doc_d1f7d83e0824", + "doc_e67e762fb6ab", + "doc_e7d508371e5c", + "doc_eeed52120ccf", + "doc_f12bbbc027a7", + "doc_f54217a0f2cf" + ], + "timeout_s": [ + "doc_5ee8bbdbd8a8" + ], + "timeoutconfig": [ + "doc_4462f67b1c03" + ], + "timeouterror": [ + "doc_745f751f1344" + ], + "timeoutpolicy": [ + "doc_2a18de009d56", + "doc_c346f13ce597" + ], + "timeouts": [ + "doc_07174a8633ac", + "doc_4462f67b1c03", + "doc_5ee8bbdbd8a8", + "doc_c346f13ce597", + "doc_d1f7d83e0824", + "doc_deb460ffa5ee", + "doc_f5f256549d84" + ], + "times": [ + "doc_136d7a909d51", + "doc_618e34441fa7", + "doc_a24b92541657" + ], + "timestamp": [ + "doc_4a1cf8a90e83", + "doc_75887f91c7df" + ], + "timestamp_ms": [ + "doc_4a1cf8a90e83", + "doc_75887f91c7df" + ], + "timing": [ + "doc_0ac01bce8c79", + "doc_745f751f1344", + "doc_78a8125d2c7e", + "doc_c346f13ce597" + ], + "tip": [ + "doc_18d38fdacc9e", + "doc_50ac650fd8f6", + "doc_57e2139f0873" + ], + "tips": [ + "doc_136d7a909d51", + "doc_18d38fdacc9e" + ], + "to_dict": [ + "doc_618e34441fa7" + ], + "to_openai_function_tools": [ + "doc_745f751f1344" + ], + "together": [ + "doc_136d7a909d51", + "doc_4e1ebaf40267", + "doc_a5120de838fb", + "doc_deb460ffa5ee" + ], + "token": [ + "doc_0383dbf277ba", + "doc_067033cfc945", + "doc_136d7a909d51", + "doc_18d38fdacc9e", + "doc_1c5f37ea0ad3", + "doc_1e46cbc6bfae", + "doc_3f1383f72c5f", + "doc_464a2b4a8d35", + "doc_4a1cf8a90e83", + "doc_5ee8bbdbd8a8", + "doc_78a8125d2c7e", + "doc_bf91f8d5da74", + "doc_e67e762fb6ab", + "doc_e7d508371e5c", + "doc_e8794369a1a4", + "doc_eeed52120ccf", + "doc_f5f256549d84", + "doc_fdf48d427fc0" + ], + "token-based": [ + "doc_1c5f37ea0ad3" + ], + "tokens": [ + "doc_0383dbf277ba", + "doc_1c5f37ea0ad3", + "doc_4a1cf8a90e83", + "doc_567be1281e2f", + "doc_5ee8bbdbd8a8", + "doc_7320ab982fab", + "doc_78a8125d2c7e", + "doc_bbc97cbff254", + "doc_e34fe24f9585", + "doc_e8794369a1a4" + ], + "tolerance": [ + "doc_4a1cf8a90e83" + ], + "tolerated": [ + "doc_4055659fe573", + "doc_e7d508371e5c", + "doc_f12bbbc027a7" + ], + "too": [ + "doc_136d7a909d51", + "doc_18d38fdacc9e", + "doc_2798948c3080", + "doc_57e2139f0873", + "doc_a2e84472f1f9", + "doc_d1f7d83e0824" + ], + "tool": [ + "doc_0383dbf277ba", + "doc_07174a8633ac", + "doc_0ac01bce8c79", + "doc_109157c0d2d7", + "doc_136d7a909d51", + "doc_18d38fdacc9e", + "doc_1e46cbc6bfae", + "doc_2a18de009d56", + "doc_3ae3aaf0c12c", + "doc_3eeb75d383b8", + "doc_3f1383f72c5f", + "doc_3f60413b3a06", + "doc_4055659fe573", + "doc_40e30666bfa8", + "doc_43ad71a4ad0c", + "doc_464a2b4a8d35", + "doc_4a1cf8a90e83", + "doc_4e1ebaf40267", + "doc_50ac650fd8f6", + "doc_567be1281e2f", + "doc_57e2139f0873", + "doc_5a65ddab1445", + "doc_5ee8bbdbd8a8", + "doc_618e34441fa7", + "doc_61fd9f682847", + "doc_745f751f1344", + "doc_78a8125d2c7e", + "doc_9e9118ed2141", + "doc_a24b92541657", + "doc_a5120de838fb", + "doc_b6088784caab", + "doc_bbc97cbff254", + "doc_bf91f8d5da74", + "doc_c346f13ce597", + "doc_c93c115aeb85", + "doc_d147a81bac29", + "doc_d6ad61a79371", + "doc_deb460ffa5ee", + "doc_e34fe24f9585", + "doc_e67e762fb6ab", + "doc_e7d508371e5c", + "doc_e8794369a1a4", + "doc_eeed52120ccf", + "doc_f12bbbc027a7", + "doc_f18f523b6ecc", + "doc_f1e32ed2ffce", + "doc_f2ae50c638a4", + "doc_f54217a0f2cf", + "doc_f5f256549d84" + ], + "tool-call": [ + "doc_745f751f1344", + "doc_f18f523b6ecc" + ], + "tool-call-lifecycle": [ + "doc_3aa0b3792d22" + ], + "tool-level": [ + "doc_9e9118ed2141", + "doc_c346f13ce597" + ], + "tool-like": [ + "doc_a2e84472f1f9" + ], + "tool-output": [ + "doc_2a18de009d56" + ], + "tool_background_failed": [ + "doc_109157c0d2d7", + "doc_3f1383f72c5f", + "doc_9e9118ed2141", + "doc_f54217a0f2cf" + ], + "tool_background_resolved": [ + "doc_109157c0d2d7", + "doc_3f1383f72c5f", + "doc_9e9118ed2141", + "doc_f54217a0f2cf" + ], + "tool_batch": [ + "doc_0ac01bce8c79" + ], + "tool_batch_started": [ + "doc_0ac01bce8c79", + "doc_f54217a0f2cf" + ], + "tool_before_execute": [ + "doc_0ac01bce8c79", + "doc_745f751f1344" + ], + "tool_call": [ + "doc_0ac01bce8c79", + "doc_9e9118ed2141" + ], + "tool_call_count": [ + "doc_0ac01bce8c79", + "doc_4a1cf8a90e83", + "doc_f54217a0f2cf" + ], + "tool_call_id": [ + "doc_0ac01bce8c79", + "doc_109157c0d2d7", + "doc_745f751f1344", + "doc_e7d508371e5c", + "doc_f54217a0f2cf" + ], + "tool_call_ids": [ + "doc_f54217a0f2cf" + ], + "tool_calls": [ + "doc_4a1cf8a90e83", + "doc_5ee8bbdbd8a8", + "doc_78a8125d2c7e", + "doc_e8794369a1a4" + ], + "tool_calls_total": [ + "doc_4a1cf8a90e83" + ], + "tool_choice": [ + "doc_5ee8bbdbd8a8" + ], + "tool_completed": [ + "doc_0ac01bce8c79", + "doc_109157c0d2d7", + "doc_5ee8bbdbd8a8", + "doc_745f751f1344", + "doc_f54217a0f2cf" + ], + "tool_deferred": [ + "doc_109157c0d2d7", + "doc_3f1383f72c5f", + "doc_9e9118ed2141", + "doc_f54217a0f2cf" + ], + "tool_error": [ + "doc_109157c0d2d7" + ], + "tool_executions": [ + "doc_0ac01bce8c79", + "doc_1e46cbc6bfae", + "doc_4a1cf8a90e83", + "doc_745f751f1344", + "doc_bbc97cbff254", + "doc_bf91f8d5da74", + "doc_e7d508371e5c" + ], + "tool_failure_policy": [ + "doc_745f751f1344", + "doc_a24b92541657" + ], + "tool_failures": [ + "doc_4a1cf8a90e83", + "doc_78a8125d2c7e" + ], + "tool_hooks_and_middleware": [ + "doc_3aa0b3792d22" + ], + "tool_latency_p50_ms": [ + "doc_78a8125d2c7e" + ], + "tool_latency_p99_ms": [ + "doc_78a8125d2c7e" + ], + "tool_name": [ + "doc_0ac01bce8c79", + "doc_109157c0d2d7", + "doc_745f751f1344", + "doc_9e9118ed2141", + "doc_c346f13ce597", + "doc_e7d508371e5c", + "doc_f54217a0f2cf" + ], + "tool_names": [ + "doc_f54217a0f2cf" + ], + "tool_output": [ + "doc_109157c0d2d7" + ], + "tool_output_max_chars": [ + "doc_18d38fdacc9e", + "doc_567be1281e2f", + "doc_9e9118ed2141", + "doc_a24b92541657", + "doc_f5f256549d84" + ], + "tool_parallelism": [ + "doc_a24b92541657", + "doc_e67e762fb6ab" + ], + "tool_registry_security": [ + "doc_3aa0b3792d22" + ], + "tool_started": [ + "doc_109157c0d2d7", + "doc_2a18de009d56", + "doc_5ee8bbdbd8a8" + ], + "tool_success": [ + "doc_109157c0d2d7" + ], + "tool_ticket_id": [ + "doc_109157c0d2d7" + ], + "toolcall": [ + "doc_0ac01bce8c79", + "doc_2a18de009d56", + "doc_50ac650fd8f6", + "doc_5ee8bbdbd8a8", + "doc_745f751f1344", + "doc_e8794369a1a4" + ], + "toolcontext": [ + "doc_2a18de009d56", + "doc_567be1281e2f", + "doc_9e9118ed2141", + "doc_bbc97cbff254" + ], + "toolexecutionerror": [ + "doc_745f751f1344" + ], + "toolexecutionrecord": [ + "doc_0ac01bce8c79", + "doc_4a1cf8a90e83", + "doc_745f751f1344", + "doc_e7d508371e5c" + ], + "toolregistry": [ + "doc_2a18de009d56", + "doc_745f751f1344", + "doc_9e9118ed2141", + "doc_c346f13ce597" + ], + "toolresult": [ + "doc_2a18de009d56", + "doc_464a2b4a8d35", + "doc_50ac650fd8f6", + "doc_745f751f1344", + "doc_9e9118ed2141", + "doc_a24b92541657" + ], + "tools": [ + "doc_0ac01bce8c79", + "doc_109157c0d2d7", + "doc_136d7a909d51", + "doc_18d38fdacc9e", + "doc_1c5f37ea0ad3", + "doc_1e46cbc6bfae", + "doc_2a18de009d56", + "doc_3aa0b3792d22", + "doc_3ae3aaf0c12c", + "doc_3eeb75d383b8", + "doc_3f60413b3a06", + "doc_40e30666bfa8", + "doc_43ad71a4ad0c", + "doc_464a2b4a8d35", + "doc_4a1cf8a90e83", + "doc_4e1ebaf40267", + "doc_567be1281e2f", + "doc_57e2139f0873", + "doc_5a65ddab1445", + "doc_5ee8bbdbd8a8", + "doc_618e34441fa7", + "doc_61fd9f682847", + "doc_745f751f1344", + "doc_78b1717fafe3", + "doc_9e9118ed2141", + "doc_a24b92541657", + "doc_a5120de838fb", + "doc_b6088784caab", + "doc_bbc97cbff254", + "doc_bf91f8d5da74", + "doc_c346f13ce597", + "doc_c93c115aeb85", + "doc_d147a81bac29", + "doc_deb460ffa5ee", + "doc_e34fe24f9585", + "doc_e67e762fb6ab", + "doc_e7d508371e5c", + "doc_e8794369a1a4", + "doc_eeed52120ccf", + "doc_f18f523b6ecc", + "doc_f2ae50c638a4", + "doc_f54217a0f2cf", + "doc_f5f256549d84" + ], + "tools-system-walkthrough": [ + "doc_3aa0b3792d22" + ], + "toolschema": [ + "doc_e8794369a1a4" + ], + "toolspec": [ + "doc_2a18de009d56", + "doc_745f751f1344" + ], + "tooltimeouterror": [ + "doc_745f751f1344" + ], + "toolvalidationerror": [ + "doc_136d7a909d51", + "doc_745f751f1344", + "doc_c346f13ce597" + ], + "top": [ + "doc_50ac650fd8f6", + "doc_a5120de838fb", + "doc_deb460ffa5ee" + ], + "top-level": [ + "doc_2a18de009d56", + "doc_64090a20f830" + ], + "top_p": [ + "doc_5ee8bbdbd8a8", + "doc_e8794369a1a4" + ], + "topbar": [ + "doc_3aa0b3792d22" + ], + "topbarctabutton": [ + "doc_3aa0b3792d22" + ], + "topbarlinks": [ + "doc_3aa0b3792d22" + ], + "topic": [ + "doc_b6088784caab" + ], + "topologically": [ + "doc_618e34441fa7" + ], + "total": [ + "doc_4462f67b1c03", + "doc_78a8125d2c7e", + "doc_bbc97cbff254", + "doc_bf91f8d5da74", + "doc_c93c115aeb85", + "doc_e8794369a1a4" + ], + "total_cost_usd": [ + "doc_4a1cf8a90e83", + "doc_78a8125d2c7e", + "doc_bbc97cbff254", + "doc_bf91f8d5da74" + ], + "total_llm_calls": [ + "doc_78a8125d2c7e" + ], + "total_runs": [ + "doc_78a8125d2c7e" + ], + "total_steps": [ + "doc_78a8125d2c7e" + ], + "total_tokens": [ + "doc_4a1cf8a90e83", + "doc_5ee8bbdbd8a8", + "doc_78a8125d2c7e" + ], + "total_tool_calls": [ + "doc_78a8125d2c7e" + ], + "touched": [ + "doc_618e34441fa7" + ], + "touches": [ + "doc_e8794369a1a4", + "doc_f92685a34f7e" + ], + "touching": [ + "doc_4e1ebaf40267", + "doc_d147a81bac29" + ], + "trace": [ + "doc_2a18de009d56", + "doc_3f1383f72c5f" + ], + "traceback": [ + "doc_136d7a909d51" + ], + "traces": [ + "doc_18d38fdacc9e", + "doc_2a18de009d56", + "doc_3ae3aaf0c12c", + "doc_78a8125d2c7e", + "doc_bbc97cbff254", + "doc_e67e762fb6ab" + ], + "tracing": [ + "doc_5ee8bbdbd8a8", + "doc_75887f91c7df", + "doc_c346f13ce597", + "doc_f92685a34f7e" + ], + "track": [ + "doc_067033cfc945", + "doc_2798948c3080", + "doc_a5120de838fb", + "doc_d1f7d83e0824", + "doc_f5f256549d84", + "doc_fdf48d427fc0" + ], + "tracking": [ + "doc_0ac01bce8c79", + "doc_5ee8bbdbd8a8", + "doc_a5120de838fb" + ], + "tracks": [ + "doc_4a1cf8a90e83" + ], + "traffic": [ + "doc_067033cfc945", + "doc_a24b92541657" + ], + "trail": [ + "doc_50ac650fd8f6", + "doc_f54217a0f2cf" + ], + "transactional": [ + "doc_a24b92541657" + ], + "transfer": [ + "doc_e67e762fb6ab", + "doc_eeed52120ccf" + ], + "transfer_to_researcher": [ + "doc_e67e762fb6ab" + ], + "transfer_to_writer": [ + "doc_e67e762fb6ab" + ], + "transform": [ + "doc_2a18de009d56", + "doc_745f751f1344", + "doc_a24b92541657", + "doc_c346f13ce597" + ], + "transformation": [ + "doc_2a18de009d56", + "doc_a5120de838fb" + ], + "transformed": [ + "doc_9e9118ed2141", + "doc_c346f13ce597" + ], + "transforming": [ + "doc_f92685a34f7e" + ], + "transforms": [ + "doc_745f751f1344" + ], + "transient": [ + "doc_4055659fe573", + "doc_61fd9f682847", + "doc_7320ab982fab", + "doc_75887f91c7df", + "doc_78b1717fafe3", + "doc_a24b92541657" + ], + "transitions": [ + "doc_07174a8633ac", + "doc_0ac01bce8c79", + "doc_5ee8bbdbd8a8", + "doc_78a8125d2c7e" + ], + "translate": [ + "doc_f1e32ed2ffce" + ], + "translates": [ + "doc_e8794369a1a4" + ], + "translation": [ + "doc_57e2139f0873" + ], + "transparent": [ + "doc_40e30666bfa8", + "doc_e34fe24f9585" + ], + "transport": [ + "doc_1c5f37ea0ad3", + "doc_c346f13ce597", + "doc_f1e32ed2ffce" + ], + "traversal": [ + "doc_464a2b4a8d35", + "doc_64090a20f830", + "doc_b6088784caab" + ], + "treat": [ + "doc_618e34441fa7", + "doc_eeed52120ccf" + ], + "treated": [ + "doc_18d38fdacc9e" + ], + "treating": [ + "doc_3ae3aaf0c12c" + ], + "tree": [ + "doc_50ac650fd8f6" + ], + "trend": [ + "doc_618e34441fa7" + ], + "trends": [ + "doc_fdf48d427fc0" + ], + "tries": [ + "doc_5ee8bbdbd8a8", + "doc_a24b92541657", + "doc_d1f7d83e0824" + ], + "trigger": [ + "doc_618e34441fa7" + ], + "triggered": [ + "doc_4055659fe573" + ], + "triggers": [ + "doc_0ac01bce8c79", + "doc_618e34441fa7", + "doc_d1f7d83e0824" + ], + "trim": [ + "doc_0383dbf277ba" + ], + "trips": [ + "doc_78a8125d2c7e" + ], + "troubleshooting": [ + "doc_3aa0b3792d22", + "doc_3ae3aaf0c12c", + "doc_deb460ffa5ee" + ], + "true": [ + "doc_3f1383f72c5f", + "doc_4a1cf8a90e83", + "doc_567be1281e2f", + "doc_61fd9f682847", + "doc_7320ab982fab", + "doc_745f751f1344", + "doc_9e9118ed2141", + "doc_a24b92541657", + "doc_e67e762fb6ab" + ], + "truncate": [ + "doc_50ac650fd8f6", + "doc_a24b92541657", + "doc_f5f256549d84" + ], + "truncated": [ + "doc_464a2b4a8d35", + "doc_618e34441fa7", + "doc_9e9118ed2141" + ], + "truncation": [ + "doc_464a2b4a8d35", + "doc_61fd9f682847" + ], + "trusted": [ + "doc_2a18de009d56", + "doc_5ee8bbdbd8a8" + ], + "truth": [ + "doc_745f751f1344" + ], + "try": [ + "doc_0ac01bce8c79", + "doc_7320ab982fab", + "doc_a24b92541657", + "doc_d1f7d83e0824", + "doc_eeed52120ccf" + ], + "ttl": [ + "doc_7320ab982fab", + "doc_a24b92541657" + ], + "tuning": [ + "doc_7320ab982fab" + ], + "tuple": [ + "doc_a24b92541657" + ], + "turn": [ + "doc_0ac01bce8c79", + "doc_50ac650fd8f6", + "doc_745f751f1344", + "doc_e7d508371e5c" + ], + "turns": [ + "doc_07174a8633ac", + "doc_7b26cbd9a7ae", + "doc_bf91f8d5da74", + "doc_f2ae50c638a4" + ], + "tutorial": [ + "doc_f2ae50c638a4" + ], + "two": [ + "doc_07174a8633ac", + "doc_0ac01bce8c79", + "doc_464a2b4a8d35", + "doc_7b26cbd9a7ae", + "doc_b6088784caab", + "doc_c346f13ce597", + "doc_d6ad61a79371", + "doc_e8794369a1a4", + "doc_eeed52120ccf", + "doc_fdf48d427fc0" + ], + "txt": [ + "doc_2798948c3080", + "doc_e67e762fb6ab" + ], + "type": [ + "doc_0383dbf277ba", + "doc_07174a8633ac", + "doc_0ac01bce8c79", + "doc_109157c0d2d7", + "doc_1e46cbc6bfae", + "doc_2a18de009d56", + "doc_3aa0b3792d22", + "doc_43ad71a4ad0c", + "doc_464a2b4a8d35", + "doc_4a1cf8a90e83", + "doc_5ee8bbdbd8a8", + "doc_618e34441fa7", + "doc_61fd9f682847", + "doc_64090a20f830", + "doc_745f751f1344", + "doc_75887f91c7df", + "doc_78a8125d2c7e", + "doc_78b1717fafe3", + "doc_a24b92541657", + "doc_a2e84472f1f9", + "doc_e67e762fb6ab", + "doc_e7d508371e5c", + "doc_e8794369a1a4", + "doc_f54217a0f2cf" + ], + "typed": [ + "doc_1c5f37ea0ad3", + "doc_3ae3aaf0c12c", + "doc_43ad71a4ad0c", + "doc_4e1ebaf40267", + "doc_50ac650fd8f6", + "doc_57e2139f0873", + "doc_5a65ddab1445", + "doc_618e34441fa7", + "doc_75887f91c7df", + "doc_a24b92541657", + "doc_b6088784caab", + "doc_bf91f8d5da74", + "doc_c346f13ce597", + "doc_d147a81bac29", + "doc_deb460ffa5ee", + "doc_e67e762fb6ab", + "doc_e8794369a1a4", + "doc_f18f523b6ecc", + "doc_f2ae50c638a4", + "doc_f5f256549d84" + ], + "types": [ + "doc_0383dbf277ba", + "doc_109157c0d2d7", + "doc_18d38fdacc9e", + "doc_2a18de009d56", + "doc_618e34441fa7", + "doc_c93c115aeb85", + "doc_e8794369a1a4", + "doc_f2ae50c638a4", + "doc_f54217a0f2cf", + "doc_f92685a34f7e" + ], + "typically": [ + "doc_d6ad61a79371" + ], + "ui": [ + "doc_109157c0d2d7", + "doc_3f60413b3a06", + "doc_f18f523b6ecc", + "doc_f2ae50c638a4" + ], + "uis": [ + "doc_2a18de009d56", + "doc_5ee8bbdbd8a8", + "doc_618e34441fa7", + "doc_a24b92541657", + "doc_a5120de838fb", + "doc_bbc97cbff254", + "doc_d6ad61a79371", + "doc_e7d508371e5c", + "doc_f54217a0f2cf" + ], + "unacceptable": [ + "doc_a24b92541657" + ], + "unauthenticated": [ + "doc_1c5f37ea0ad3", + "doc_567be1281e2f", + "doc_e34fe24f9585" + ], + "unauthorized": [ + "doc_464a2b4a8d35", + "doc_567be1281e2f", + "doc_a24b92541657" + ], + "unavailable": [ + "doc_4055659fe573", + "doc_618e34441fa7" + ], + "unbounded": [ + "doc_464a2b4a8d35", + "doc_618e34441fa7" + ], + "unclear": [ + "doc_136d7a909d51" + ], + "unclosed": [ + "doc_2798948c3080" + ], + "undecorated": [ + "doc_3aa0b3792d22" + ], + "under": [ + "doc_0ac01bce8c79", + "doc_1e46cbc6bfae", + "doc_2798948c3080", + "doc_464a2b4a8d35", + "doc_618e34441fa7", + "doc_9e9118ed2141", + "doc_a24b92541657", + "doc_a5120de838fb", + "doc_bbc97cbff254", + "doc_c93c115aeb85" + ], + "underlying": [ + "doc_61fd9f682847" + ], + "underscores": [ + "doc_64090a20f830" + ], + "understand": [ + "doc_136d7a909d51", + "doc_3ae3aaf0c12c", + "doc_43ad71a4ad0c", + "doc_5a65ddab1445", + "doc_9e9118ed2141", + "doc_a2e84472f1f9", + "doc_bf91f8d5da74", + "doc_e67e762fb6ab", + "doc_f18f523b6ecc" + ], + "understanding": [ + "doc_0ac01bce8c79", + "doc_4055659fe573", + "doc_50ac650fd8f6", + "doc_5ee8bbdbd8a8", + "doc_f54217a0f2cf" + ], + "unexpected": [ + "doc_136d7a909d51", + "doc_fdf48d427fc0" + ], + "unhandled": [ + "doc_618e34441fa7", + "doc_745f751f1344" + ], + "unified": [ + "doc_a2e84472f1f9", + "doc_a5120de838fb" + ], + "uniformly": [ + "doc_c346f13ce597" + ], + "unintended": [ + "doc_43ad71a4ad0c" + ], + "unique": [ + "doc_07174a8633ac", + "doc_1e46cbc6bfae", + "doc_4a1cf8a90e83", + "doc_5ee8bbdbd8a8", + "doc_bf91f8d5da74", + "doc_e7d508371e5c", + "doc_f54217a0f2cf" + ], + "unit": [ + "doc_3eeb75d383b8" + ], + "unix": [ + "doc_4a1cf8a90e83" + ], + "unknown": [ + "doc_f54217a0f2cf" + ], + "unless": [ + "doc_567be1281e2f", + "doc_745f751f1344" + ], + "unnecessarily": [ + "doc_136d7a909d51" + ], + "unnecessary": [ + "doc_f5f256549d84" + ], + "unrecoverable": [ + "doc_e7d508371e5c", + "doc_f54217a0f2cf" + ], + "unrelated": [ + "doc_618e34441fa7" + ], + "unresolved": [ + "doc_a24b92541657" + ], + "unset": [ + "doc_61fd9f682847", + "doc_bbc97cbff254" + ], + "unstructured": [ + "doc_b6088784caab" + ], + "unsupported": [ + "doc_f1e32ed2ffce" + ], + "until": [ + "doc_0383dbf277ba", + "doc_1e46cbc6bfae", + "doc_4e1ebaf40267", + "doc_5ee8bbdbd8a8", + "doc_a24b92541657", + "doc_d6ad61a79371", + "doc_e7d508371e5c" + ], + "untrusted": [ + "doc_2a18de009d56", + "doc_5ee8bbdbd8a8" + ], + "untrusted-data": [ + "doc_5ee8bbdbd8a8", + "doc_745f751f1344", + "doc_a24b92541657" + ], + "untrusted_tool_preamble": [ + "doc_a24b92541657" + ], + "untyped": [ + "doc_18d38fdacc9e", + "doc_3ae3aaf0c12c", + "doc_50ac650fd8f6", + "doc_e67e762fb6ab" + ], + "up": [ + "doc_067033cfc945", + "doc_07174a8633ac", + "doc_18d38fdacc9e", + "doc_50ac650fd8f6", + "doc_567be1281e2f", + "doc_57e2139f0873", + "doc_5ee8bbdbd8a8", + "doc_64090a20f830", + "doc_78b1717fafe3", + "doc_f18f523b6ecc" + ], + "update": [ + "doc_3eeb75d383b8", + "doc_3f1383f72c5f", + "doc_618e34441fa7", + "doc_bbc97cbff254", + "doc_d147a81bac29", + "doc_f54217a0f2cf" + ], + "update_": [ + "doc_43ad71a4ad0c" + ], + "updated": [ + "doc_5ee8bbdbd8a8" + ], + "updates": [ + "doc_109157c0d2d7" + ], + "updating": [ + "doc_deb460ffa5ee" + ], + "upper_snake_case": [ + "doc_64090a20f830" + ], + "uppercases": [ + "doc_64090a20f830" + ], + "upsert": [ + "doc_0383dbf277ba" + ], + "upsert_long_term_memory": [ + "doc_0383dbf277ba" + ], + "url": [ + "doc_3aa0b3792d22", + "doc_61fd9f682847" + ], + "urls": [ + "doc_f1e32ed2ffce" + ], + "usage": [ + "doc_1e46cbc6bfae", + "doc_2a18de009d56", + "doc_3ae3aaf0c12c", + "doc_43ad71a4ad0c", + "doc_4a1cf8a90e83", + "doc_50ac650fd8f6", + "doc_5ee8bbdbd8a8", + "doc_64090a20f830", + "doc_a5120de838fb", + "doc_bf91f8d5da74", + "doc_c93c115aeb85", + "doc_d1f7d83e0824", + "doc_deb460ffa5ee", + "doc_e67e762fb6ab", + "doc_e8794369a1a4", + "doc_f18f523b6ecc", + "doc_f1e32ed2ffce", + "doc_f5f256549d84" + ], + "usage_aggregate": [ + "doc_bbc97cbff254", + "doc_bf91f8d5da74", + "doc_e7d508371e5c" + ], + "usageaggregate": [ + "doc_1e46cbc6bfae", + "doc_2a18de009d56", + "doc_5ee8bbdbd8a8", + "doc_e7d508371e5c" + ], + "usd": [ + "doc_4a1cf8a90e83", + "doc_78a8125d2c7e" + ], + "use": [ + "doc_0383dbf277ba", + "doc_067033cfc945", + "doc_109157c0d2d7", + "doc_136d7a909d51", + "doc_18d38fdacc9e", + "doc_1c5f37ea0ad3", + "doc_1e46cbc6bfae", + "doc_2798948c3080", + "doc_2a18de009d56", + "doc_3ae3aaf0c12c", + "doc_3eeb75d383b8", + "doc_3f1383f72c5f", + "doc_3f60413b3a06", + "doc_4055659fe573", + "doc_43ad71a4ad0c", + "doc_4462f67b1c03", + "doc_464a2b4a8d35", + "doc_4e1ebaf40267", + "doc_50ac650fd8f6", + "doc_567be1281e2f", + "doc_57e2139f0873", + "doc_5a65ddab1445", + "doc_618e34441fa7", + "doc_61fd9f682847", + "doc_64090a20f830", + "doc_7320ab982fab", + "doc_75887f91c7df", + "doc_78a8125d2c7e", + "doc_78b1717fafe3", + "doc_9e9118ed2141", + "doc_a24b92541657", + "doc_a5120de838fb", + "doc_b6088784caab", + "doc_bbc97cbff254", + "doc_c346f13ce597", + "doc_c93c115aeb85", + "doc_d147a81bac29", + "doc_d1f7d83e0824", + "doc_d6ad61a79371", + "doc_deb460ffa5ee", + "doc_e34fe24f9585", + "doc_e67e762fb6ab", + "doc_e7d508371e5c", + "doc_eeed52120ccf", + "doc_f12bbbc027a7", + "doc_f18f523b6ecc", + "doc_f1e32ed2ffce", + "doc_f2ae50c638a4", + "doc_f5f256549d84", + "doc_f92685a34f7e", + "doc_fdf48d427fc0" + ], + "used": [ + "doc_0383dbf277ba", + "doc_07174a8633ac", + "doc_0ac01bce8c79", + "doc_136d7a909d51", + "doc_1e46cbc6bfae", + "doc_4a1cf8a90e83", + "doc_57e2139f0873", + "doc_64090a20f830", + "doc_bbc97cbff254", + "doc_bf91f8d5da74", + "doc_d1f7d83e0824", + "doc_f2ae50c638a4" + ], + "useful": [ + "doc_3ae3aaf0c12c", + "doc_4a1cf8a90e83", + "doc_9e9118ed2141", + "doc_a24b92541657", + "doc_a2e84472f1f9", + "doc_bf91f8d5da74", + "doc_d147a81bac29", + "doc_f18f523b6ecc" + ], + "user": [ + "doc_0383dbf277ba", + "doc_0ac01bce8c79", + "doc_1e46cbc6bfae", + "doc_3eeb75d383b8", + "doc_4a1cf8a90e83", + "doc_5ee8bbdbd8a8", + "doc_61fd9f682847", + "doc_78b1717fafe3", + "doc_7b26cbd9a7ae", + "doc_9e9118ed2141", + "doc_a24b92541657", + "doc_a2e84472f1f9", + "doc_b6088784caab", + "doc_e8794369a1a4", + "doc_eeed52120ccf", + "doc_f12bbbc027a7" + ], + "user-facing": [ + "doc_3eeb75d383b8", + "doc_bbc97cbff254", + "doc_d147a81bac29", + "doc_f18f523b6ecc", + "doc_f5f256549d84" + ], + "user-input": [ + "doc_a24b92541657" + ], + "user-visible": [ + "doc_618e34441fa7" + ], + "user_message": [ + "doc_1e46cbc6bfae", + "doc_4e1ebaf40267", + "doc_bbc97cbff254", + "doc_e8794369a1a4", + "doc_eeed52120ccf" + ], + "user_role": [ + "doc_2798948c3080" + ], + "users": [ + "doc_3ae3aaf0c12c", + "doc_57e2139f0873", + "doc_7b26cbd9a7ae" + ], + "uses": [ + "doc_1e46cbc6bfae", + "doc_2798948c3080", + "doc_3f1383f72c5f", + "doc_3f60413b3a06", + "doc_464a2b4a8d35", + "doc_4e1ebaf40267", + "doc_61fd9f682847", + "doc_64090a20f830", + "doc_78b1717fafe3", + "doc_9e9118ed2141", + "doc_a24b92541657", + "doc_b6088784caab", + "doc_c93c115aeb85", + "doc_eeed52120ccf", + "doc_f92685a34f7e" + ], + "using": [ + "doc_136d7a909d51", + "doc_1c5f37ea0ad3", + "doc_40e30666bfa8", + "doc_4462f67b1c03", + "doc_464a2b4a8d35", + "doc_57e2139f0873", + "doc_5ee8bbdbd8a8", + "doc_64090a20f830", + "doc_a2e84472f1f9", + "doc_a5120de838fb", + "doc_b6088784caab", + "doc_d6ad61a79371", + "doc_eeed52120ccf", + "doc_f12bbbc027a7" + ], + "usually": [ + "doc_a24b92541657", + "doc_f5f256549d84" + ], + "utilities": [ + "doc_2a18de009d56" + ], + "utility": [ + "doc_2a18de009d56" + ], + "v2": [ + "doc_64090a20f830" + ], + "vague": [ + "doc_18d38fdacc9e", + "doc_e67e762fb6ab" + ], + "valid": [ + "doc_3eeb75d383b8", + "doc_3f60413b3a06", + "doc_4a1cf8a90e83", + "doc_567be1281e2f", + "doc_f12bbbc027a7" + ], + "validate": [ + "doc_50ac650fd8f6", + "doc_618e34441fa7", + "doc_745f751f1344", + "doc_f5f256549d84" + ], + "validated": [ + "doc_1c5f37ea0ad3", + "doc_3f60413b3a06", + "doc_464a2b4a8d35", + "doc_50ac650fd8f6", + "doc_745f751f1344", + "doc_9e9118ed2141", + "doc_c346f13ce597", + "doc_e7d508371e5c", + "doc_f2ae50c638a4" + ], + "validates": [ + "doc_1c5f37ea0ad3", + "doc_745f751f1344", + "doc_bf91f8d5da74", + "doc_f12bbbc027a7" + ], + "validation": [ + "doc_136d7a909d51", + "doc_18d38fdacc9e", + "doc_1c5f37ea0ad3", + "doc_2a18de009d56", + "doc_4055659fe573", + "doc_40e30666bfa8", + "doc_43ad71a4ad0c", + "doc_4a1cf8a90e83", + "doc_4e1ebaf40267", + "doc_50ac650fd8f6", + "doc_567be1281e2f", + "doc_5a65ddab1445", + "doc_618e34441fa7", + "doc_745f751f1344", + "doc_9e9118ed2141", + "doc_a24b92541657", + "doc_a5120de838fb", + "doc_c346f13ce597", + "doc_deb460ffa5ee", + "doc_e34fe24f9585", + "doc_e67e762fb6ab" + ], + "value": [ + "doc_0ac01bce8c79", + "doc_745f751f1344", + "doc_a24b92541657" + ], + "values": [ + "doc_109157c0d2d7", + "doc_2798948c3080", + "doc_4a1cf8a90e83", + "doc_618e34441fa7", + "doc_a24b92541657" + ], + "var": [ + "doc_2798948c3080" + ], + "variable": [ + "doc_2798948c3080", + "doc_61fd9f682847", + "doc_64090a20f830", + "doc_a24b92541657", + "doc_a5120de838fb" + ], + "variables": [ + "doc_0383dbf277ba", + "doc_067033cfc945", + "doc_18d38fdacc9e", + "doc_1e46cbc6bfae", + "doc_2798948c3080", + "doc_567be1281e2f", + "doc_61fd9f682847", + "doc_64090a20f830", + "doc_78b1717fafe3", + "doc_a5120de838fb", + "doc_f5f256549d84" + ], + "variant": [ + "doc_2a18de009d56" + ], + "variation": [ + "doc_eeed52120ccf" + ], + "varies": [ + "doc_f54217a0f2cf" + ], + "vars": [ + "doc_d147a81bac29" + ], + "vary": [ + "doc_4a1cf8a90e83", + "doc_bbc97cbff254" + ], + "vault": [ + "doc_067033cfc945" + ], + "vector": [ + "doc_0383dbf277ba", + "doc_2a18de009d56", + "doc_5a65ddab1445", + "doc_61fd9f682847", + "doc_d147a81bac29", + "doc_f5f256549d84" + ], + "vector-based": [ + "doc_0383dbf277ba" + ], + "vectors": [ + "doc_50ac650fd8f6", + "doc_9e9118ed2141" + ], + "verb": [ + "doc_43ad71a4ad0c" + ], + "verbose": [ + "doc_136d7a909d51", + "doc_618e34441fa7" + ], + "verbosity": [ + "doc_3f1383f72c5f", + "doc_a24b92541657" + ], + "vercel": [ + "doc_b6088784caab", + "doc_d147a81bac29", + "doc_deb460ffa5ee" + ], + "verification": [ + "doc_2a18de009d56", + "doc_f12bbbc027a7" + ], + "verify": [ + "doc_464a2b4a8d35", + "doc_618e34441fa7", + "doc_c93c115aeb85", + "doc_f92685a34f7e" + ], + "versa": [ + "doc_18d38fdacc9e" + ], + "version": [ + "doc_067033cfc945", + "doc_136d7a909d51", + "doc_18d38fdacc9e", + "doc_2798948c3080", + "doc_3ae3aaf0c12c", + "doc_4a1cf8a90e83", + "doc_61fd9f682847", + "doc_b6088784caab" + ], + "version-controlled": [ + "doc_2798948c3080", + "doc_64090a20f830" + ], + "versionable": [ + "doc_4e1ebaf40267" + ], + "versioning": [ + "doc_2a18de009d56" + ], + "versions": [ + "doc_2798948c3080", + "doc_618e34441fa7" + ], + "via": [ + "doc_0383dbf277ba", + "doc_109157c0d2d7", + "doc_1c5f37ea0ad3", + "doc_1e46cbc6bfae", + "doc_2a18de009d56", + "doc_40e30666bfa8", + "doc_5ee8bbdbd8a8", + "doc_64090a20f830", + "doc_7320ab982fab", + "doc_745f751f1344", + "doc_75887f91c7df", + "doc_78b1717fafe3", + "doc_9e9118ed2141", + "doc_a24b92541657", + "doc_a5120de838fb", + "doc_d6ad61a79371", + "doc_e34fe24f9585", + "doc_f1e32ed2ffce", + "doc_f92685a34f7e", + "doc_fdf48d427fc0" + ], + "vice": [ + "doc_18d38fdacc9e" + ], + "violated": [ + "doc_50ac650fd8f6" + ], + "violation": [ + "doc_4055659fe573" + ], + "violations": [ + "doc_0ac01bce8c79", + "doc_618e34441fa7" + ], + "visibility": [ + "doc_78a8125d2c7e" + ], + "visible": [ + "doc_78a8125d2c7e" + ], + "vision": [ + "doc_f1e32ed2ffce" + ], + "vs": [ + "doc_2a18de009d56", + "doc_3f60413b3a06", + "doc_43ad71a4ad0c", + "doc_b6088784caab", + "doc_d6ad61a79371", + "doc_e34fe24f9585", + "doc_e67e762fb6ab" + ], + "wait": [ + "doc_0383dbf277ba", + "doc_a24b92541657" + ], + "waiting": [ + "doc_07174a8633ac", + "doc_0ac01bce8c79", + "doc_109157c0d2d7", + "doc_57e2139f0873", + "doc_78b1717fafe3", + "doc_f12bbbc027a7" + ], + "waits": [ + "doc_0ac01bce8c79", + "doc_a24b92541657" + ], + "wal": [ + "doc_0383dbf277ba" + ], + "walk": [ + "doc_deb460ffa5ee" + ], + "walks": [ + "doc_745f751f1344" + ], + "walkthrough": [ + "doc_0ac01bce8c79" + ], + "wall": [ + "doc_4055659fe573", + "doc_50ac650fd8f6", + "doc_567be1281e2f" + ], + "wall-clock": [ + "doc_0ac01bce8c79", + "doc_a24b92541657", + "doc_a2e84472f1f9" + ], + "wall_time_s": [ + "doc_78a8125d2c7e" + ], + "wallet": [ + "doc_a24b92541657" + ], + "want": [ + "doc_3f60413b3a06", + "doc_61fd9f682847", + "doc_d6ad61a79371", + "doc_deb460ffa5ee" + ], + "warning": [ + "doc_0ac01bce8c79", + "doc_4055659fe573", + "doc_43ad71a4ad0c", + "doc_78b1717fafe3", + "doc_a24b92541657", + "doc_f54217a0f2cf" + ], + "was": [ + "doc_07174a8633ac", + "doc_0ac01bce8c79", + "doc_109157c0d2d7", + "doc_464a2b4a8d35", + "doc_4a1cf8a90e83", + "doc_50ac650fd8f6", + "doc_a2e84472f1f9", + "doc_d1f7d83e0824", + "doc_f54217a0f2cf" + ], + "waste": [ + "doc_18d38fdacc9e" + ], + "watch": [ + "doc_9e9118ed2141" + ], + "way": [ + "doc_0ac01bce8c79", + "doc_1e46cbc6bfae", + "doc_43ad71a4ad0c", + "doc_61fd9f682847", + "doc_a5120de838fb", + "doc_f54217a0f2cf" + ], + "ways": [ + "doc_2798948c3080", + "doc_eeed52120ccf" + ], + "web": [ + "doc_2a18de009d56", + "doc_a24b92541657" + ], + "well": [ + "doc_136d7a909d51", + "doc_f2ae50c638a4" + ], + "well-defined": [ + "doc_5ee8bbdbd8a8", + "doc_e67e762fb6ab" + ], + "went": [ + "doc_4055659fe573" + ], + "were": [ + "doc_4a1cf8a90e83", + "doc_618e34441fa7", + "doc_f54217a0f2cf" + ], + "what": [ + "doc_0383dbf277ba", + "doc_067033cfc945", + "doc_07174a8633ac", + "doc_0ac01bce8c79", + "doc_1c5f37ea0ad3", + "doc_1e46cbc6bfae", + "doc_2798948c3080", + "doc_40e30666bfa8", + "doc_4462f67b1c03", + "doc_464a2b4a8d35", + "doc_4a1cf8a90e83", + "doc_4e1ebaf40267", + "doc_50ac650fd8f6", + "doc_5ee8bbdbd8a8", + "doc_618e34441fa7", + "doc_64090a20f830", + "doc_745f751f1344", + "doc_78b1717fafe3", + "doc_7b26cbd9a7ae", + "doc_9e9118ed2141", + "doc_a24b92541657", + "doc_a2e84472f1f9", + "doc_a5120de838fb", + "doc_b6088784caab", + "doc_c346f13ce597", + "doc_d1f7d83e0824", + "doc_deb460ffa5ee", + "doc_e67e762fb6ab", + "doc_e8794369a1a4", + "doc_f12bbbc027a7", + "doc_f18f523b6ecc", + "doc_f2ae50c638a4", + "doc_f54217a0f2cf", + "doc_f92685a34f7e", + "doc_fdf48d427fc0" + ], + "what_": [ + "doc_e67e762fb6ab" + ], + "whatever": [ + "doc_f12bbbc027a7" + ], + "when": [ + "doc_0383dbf277ba", + "doc_07174a8633ac", + "doc_0ac01bce8c79", + "doc_109157c0d2d7", + "doc_136d7a909d51", + "doc_1c5f37ea0ad3", + "doc_1e46cbc6bfae", + "doc_2798948c3080", + "doc_2a18de009d56", + "doc_3ae3aaf0c12c", + "doc_3eeb75d383b8", + "doc_3f1383f72c5f", + "doc_3f60413b3a06", + "doc_4055659fe573", + "doc_43ad71a4ad0c", + "doc_4a1cf8a90e83", + "doc_4e1ebaf40267", + "doc_50ac650fd8f6", + "doc_57e2139f0873", + "doc_5ee8bbdbd8a8", + "doc_618e34441fa7", + "doc_61fd9f682847", + "doc_64090a20f830", + "doc_7320ab982fab", + "doc_745f751f1344", + "doc_75887f91c7df", + "doc_78a8125d2c7e", + "doc_78b1717fafe3", + "doc_9e9118ed2141", + "doc_a24b92541657", + "doc_a2e84472f1f9", + "doc_b6088784caab", + "doc_bbc97cbff254", + "doc_bf91f8d5da74", + "doc_c346f13ce597", + "doc_d147a81bac29", + "doc_d1f7d83e0824", + "doc_d6ad61a79371", + "doc_deb460ffa5ee", + "doc_e67e762fb6ab", + "doc_e7d508371e5c", + "doc_eeed52120ccf", + "doc_f12bbbc027a7", + "doc_f18f523b6ecc", + "doc_f2ae50c638a4", + "doc_f54217a0f2cf", + "doc_f5f256549d84", + "doc_fdf48d427fc0" + ], + "where": [ + "doc_07174a8633ac", + "doc_136d7a909d51", + "doc_3ae3aaf0c12c", + "doc_3f60413b3a06", + "doc_4a1cf8a90e83", + "doc_50ac650fd8f6", + "doc_618e34441fa7", + "doc_a24b92541657", + "doc_a2e84472f1f9", + "doc_d147a81bac29", + "doc_d6ad61a79371", + "doc_e7d508371e5c", + "doc_f12bbbc027a7" + ], + "whether": [ + "doc_0ac01bce8c79", + "doc_40e30666bfa8", + "doc_464a2b4a8d35", + "doc_50ac650fd8f6", + "doc_5ee8bbdbd8a8", + "doc_745f751f1344", + "doc_78b1717fafe3", + "doc_a24b92541657", + "doc_a2e84472f1f9", + "doc_e34fe24f9585", + "doc_e67e762fb6ab", + "doc_f12bbbc027a7" + ], + "which": [ + "doc_0383dbf277ba", + "doc_07174a8633ac", + "doc_1c5f37ea0ad3", + "doc_1e46cbc6bfae", + "doc_2a18de009d56", + "doc_4a1cf8a90e83", + "doc_567be1281e2f", + "doc_64090a20f830", + "doc_745f751f1344", + "doc_9e9118ed2141", + "doc_a5120de838fb", + "doc_b6088784caab", + "doc_d147a81bac29", + "doc_d1f7d83e0824", + "doc_d6ad61a79371", + "doc_f12bbbc027a7" + ], + "while": [ + "doc_0ac01bce8c79", + "doc_4a1cf8a90e83", + "doc_9e9118ed2141", + "doc_f92685a34f7e" + ], + "who": [ + "doc_e34fe24f9585" + ], + "whose": [ + "doc_4a1cf8a90e83", + "doc_a24b92541657" + ], + "why": [ + "doc_0ac01bce8c79", + "doc_5ee8bbdbd8a8", + "doc_618e34441fa7", + "doc_e8794369a1a4", + "doc_eeed52120ccf" + ], + "will": [ + "doc_18d38fdacc9e", + "doc_4055659fe573", + "doc_64090a20f830", + "doc_c346f13ce597", + "doc_f18f523b6ecc", + "doc_f54217a0f2cf" + ], + "window": [ + "doc_a24b92541657" + ], + "wins": [ + "doc_2798948c3080", + "doc_f12bbbc027a7" + ], + "wire": [ + "doc_4e1ebaf40267", + "doc_c346f13ce597" + ], + "wires": [ + "doc_4e1ebaf40267" + ], + "wiring": [ + "doc_2a18de009d56" + ], + "with_cache": [ + "doc_3f60413b3a06" + ], + "with_middlewares": [ + "doc_3f60413b3a06" + ], + "with_observers": [ + "doc_3f60413b3a06" + ], + "with_router": [ + "doc_3f60413b3a06" + ], + "within": [ + "doc_464a2b4a8d35", + "doc_b6088784caab", + "doc_c93c115aeb85" + ], + "without": [ + "doc_0383dbf277ba", + "doc_07174a8633ac", + "doc_136d7a909d51", + "doc_1c5f37ea0ad3", + "doc_3ae3aaf0c12c", + "doc_3eeb75d383b8", + "doc_3f1383f72c5f", + "doc_3f60413b3a06", + "doc_4a1cf8a90e83", + "doc_4e1ebaf40267", + "doc_5a65ddab1445", + "doc_5ee8bbdbd8a8", + "doc_618e34441fa7", + "doc_64090a20f830", + "doc_75887f91c7df", + "doc_9e9118ed2141", + "doc_a24b92541657", + "doc_a5120de838fb", + "doc_b6088784caab", + "doc_bbc97cbff254", + "doc_c346f13ce597", + "doc_c93c115aeb85", + "doc_f54217a0f2cf" + ], + "work": [ + "doc_136d7a909d51", + "doc_18d38fdacc9e", + "doc_1c5f37ea0ad3", + "doc_2798948c3080", + "doc_4a1cf8a90e83", + "doc_57e2139f0873", + "doc_618e34441fa7", + "doc_78b1717fafe3", + "doc_9e9118ed2141", + "doc_a24b92541657", + "doc_a2e84472f1f9", + "doc_a5120de838fb", + "doc_f12bbbc027a7", + "doc_f54217a0f2cf", + "doc_f5f256549d84", + "doc_fdf48d427fc0" + ], + "worker": [ + "doc_2a18de009d56", + "doc_3f1383f72c5f", + "doc_78b1717fafe3", + "doc_d147a81bac29" + ], + "workers": [ + "doc_0383dbf277ba", + "doc_067033cfc945", + "doc_3ae3aaf0c12c", + "doc_57e2139f0873", + "doc_78b1717fafe3", + "doc_9e9118ed2141", + "doc_a24b92541657", + "doc_bbc97cbff254", + "doc_d147a81bac29", + "doc_f5f256549d84" + ], + "workflow": [ + "doc_75887f91c7df", + "doc_c346f13ce597", + "doc_d147a81bac29", + "doc_deb460ffa5ee", + "doc_f18f523b6ecc", + "doc_f2ae50c638a4" + ], + "workflows": [ + "doc_a2e84472f1f9", + "doc_a5120de838fb", + "doc_d6ad61a79371" + ], + "working": [ + "doc_64090a20f830", + "doc_bf91f8d5da74" + ], + "workload": [ + "doc_a5120de838fb" + ], + "workloads": [ + "doc_61fd9f682847", + "doc_78b1717fafe3" + ], + "works": [ + "doc_07174a8633ac", + "doc_18d38fdacc9e", + "doc_57e2139f0873", + "doc_5ee8bbdbd8a8", + "doc_618e34441fa7", + "doc_64090a20f830", + "doc_9e9118ed2141", + "doc_a2e84472f1f9", + "doc_d1f7d83e0824", + "doc_d6ad61a79371", + "doc_e67e762fb6ab" + ], + "worse": [ + "doc_4055659fe573" + ], + "would": [ + "doc_136d7a909d51", + "doc_3eeb75d383b8", + "doc_618e34441fa7" + ], + "wrap": [ + "doc_745f751f1344", + "doc_c346f13ce597" + ], + "wrapped": [ + "doc_745f751f1344", + "doc_75887f91c7df" + ], + "wrapper": [ + "doc_bbc97cbff254", + "doc_c346f13ce597" + ], + "wrappers": [ + "doc_a24b92541657" + ], + "wraps": [ + "doc_1c5f37ea0ad3", + "doc_745f751f1344", + "doc_9e9118ed2141", + "doc_c346f13ce597" + ], + "write": [ + "doc_18d38fdacc9e", + "doc_43ad71a4ad0c", + "doc_4462f67b1c03", + "doc_618e34441fa7", + "doc_745f751f1344", + "doc_9e9118ed2141", + "doc_a24b92541657", + "doc_b6088784caab" + ], + "write-behind": [ + "doc_4a1cf8a90e83" + ], + "write_docs": [ + "doc_3f1383f72c5f" + ], + "write_file": [ + "doc_18d38fdacc9e" + ], + "write_golden_trace": [ + "doc_2a18de009d56" + ], + "write_suite_report_json": [ + "doc_2a18de009d56" + ], + "writer": [ + "doc_4a1cf8a90e83" + ], + "writes": [ + "doc_0383dbf277ba", + "doc_3f1383f72c5f", + "doc_4a1cf8a90e83", + "doc_a24b92541657" + ], + "writing": [ + "doc_3ae3aaf0c12c", + "doc_3f1383f72c5f", + "doc_9e9118ed2141" + ], + "written": [ + "doc_0383dbf277ba", + "doc_4a1cf8a90e83" + ], + "wrong": [ + "doc_4055659fe573" + ], + "xfail": [ + "doc_618e34441fa7" + ], + "xml": [ + "doc_618e34441fa7" + ], + "yaml": [ + "doc_067033cfc945", + "doc_b6088784caab" + ], + "yes": [ + "doc_0ac01bce8c79", + "doc_b6088784caab" + ], + "yet": [ + "doc_18d38fdacc9e", + "doc_4a1cf8a90e83", + "doc_f54217a0f2cf" + ], + "yields": [ + "doc_a24b92541657" + ], + "yml": [ + "doc_067033cfc945" + ], + "yourmodel": [ + "doc_3f60413b3a06" + ], + "zooms": [ + "doc_0ac01bce8c79" + ] +} diff --git a/skills/afk-coder/references/afk-docs/manifest.json b/skills/afk-coder/references/afk-docs/manifest.json new file mode 100644 index 0000000..7c7d420 --- /dev/null +++ b/skills/afk-coder/references/afk-docs/manifest.json @@ -0,0 +1,16 @@ +{ + "generated_at_utc": "2026-05-31T13:25:20.450882+00:00", + "document_count": 63, + "skill_count": 2, + "files": [ + "docs-index.json", + "docs-index.jsonl", + "inverted-index.json", + "path-to-id.json", + "id-to-path.json", + "examples.md", + "records/", + "text/", + "../skills/index.json" + ] +} diff --git a/skills/afk-coder/references/afk-docs/path-to-id.json b/skills/afk-coder/references/afk-docs/path-to-id.json new file mode 100644 index 0000000..2bee844 --- /dev/null +++ b/skills/afk-coder/references/afk-docs/path-to-id.json @@ -0,0 +1,65 @@ +{ + "docs/docs.json": "doc_3aa0b3792d22", + "docs/index.mdx": "doc_deb460ffa5ee", + "docs/library/a2a.mdx": "doc_1c5f37ea0ad3", + "docs/library/agent-skills.mdx": "doc_b6088784caab", + "docs/library/agentic-behavior.mdx": "doc_f12bbbc027a7", + "docs/library/agentic-levels.mdx": "doc_57e2139f0873", + "docs/library/agents.mdx": "doc_e67e762fb6ab", + "docs/library/api-reference.mdx": "doc_bbc97cbff254", + "docs/library/architecture.mdx": "doc_4e1ebaf40267", + "docs/library/building-with-ai.mdx": "doc_18d38fdacc9e", + "docs/library/checkpoint-schema.mdx": "doc_4a1cf8a90e83", + "docs/library/configuration-reference.mdx": "doc_a24b92541657", + "docs/library/core-runner.mdx": "doc_e7d508371e5c", + "docs/library/debugger.mdx": "doc_3f1383f72c5f", + "docs/library/deployment.mdx": "doc_067033cfc945", + "docs/library/developer-guide.mdx": "doc_d147a81bac29", + "docs/library/environment-variables.mdx": "doc_61fd9f682847", + "docs/library/evals.mdx": "doc_c93c115aeb85", + "docs/library/examples/index.mdx": "doc_a5120de838fb", + "docs/library/failure-policy-matrix.mdx": "doc_4055659fe573", + "docs/library/full-module-reference.mdx": "doc_2a18de009d56", + "docs/library/how-to-use-afk.mdx": "doc_3ae3aaf0c12c", + "docs/library/learn-in-15-minutes.mdx": "doc_f2ae50c638a4", + "docs/library/llm-interaction.mdx": "doc_5ee8bbdbd8a8", + "docs/library/mcp-server.mdx": "doc_e34fe24f9585", + "docs/library/memory.mdx": "doc_0383dbf277ba", + "docs/library/mental-model.mdx": "doc_50ac650fd8f6", + "docs/library/messaging.mdx": "doc_75887f91c7df", + "docs/library/migration.mdx": "doc_5a65ddab1445", + "docs/library/observability.mdx": "doc_78a8125d2c7e", + "docs/library/overview.mdx": "doc_f18f523b6ecc", + "docs/library/performance.mdx": "doc_f5f256549d84", + "docs/library/public-imports-and-function-improvement.mdx": "doc_3eeb75d383b8", + "docs/library/quickstart.mdx": "doc_bf91f8d5da74", + "docs/library/run-event-contract.mdx": "doc_f54217a0f2cf", + "docs/library/security-model.mdx": "doc_567be1281e2f", + "docs/library/snippets/01_minimal_chat_agent.mdx": "doc_1e46cbc6bfae", + "docs/library/snippets/02_policy_with_hitl.mdx": "doc_d6ad61a79371", + "docs/library/snippets/03_subagents_with_router.mdx": "doc_a2e84472f1f9", + "docs/library/snippets/04_resume_and_compact.mdx": "doc_07174a8633ac", + "docs/library/snippets/05_direct_llm_structured_output.mdx": "doc_3f60413b3a06", + "docs/library/snippets/06_tool_registry_security.mdx": "doc_43ad71a4ad0c", + "docs/library/snippets/07_tool_hooks_and_middleware.mdx": "doc_c346f13ce597", + "docs/library/snippets/08_prebuilt_runtime_tools.mdx": "doc_464a2b4a8d35", + "docs/library/snippets/09_system_prompt_loader.mdx": "doc_64090a20f830", + "docs/library/snippets/10_streaming_chat_with_memory.mdx": "doc_7b26cbd9a7ae", + "docs/library/snippets/11_cost_monitoring.mdx": "doc_fdf48d427fc0", + "docs/library/snippets/12_mcp_client_integration.mdx": "doc_40e30666bfa8", + "docs/library/snippets/13_multi_model_fallback.mdx": "doc_d1f7d83e0824", + "docs/library/snippets/14_production_client.mdx": "doc_4462f67b1c03", + "docs/library/streaming.mdx": "doc_109157c0d2d7", + "docs/library/system-prompts.mdx": "doc_2798948c3080", + "docs/library/task-queues.mdx": "doc_78b1717fafe3", + "docs/library/tested-behaviors.mdx": "doc_618e34441fa7", + "docs/library/tool-call-lifecycle.mdx": "doc_0ac01bce8c79", + "docs/library/tools-system-walkthrough.mdx": "doc_745f751f1344", + "docs/library/tools.mdx": "doc_9e9118ed2141", + "docs/library/troubleshooting.mdx": "doc_136d7a909d51", + "docs/llms/adapters.mdx": "doc_f1e32ed2ffce", + "docs/llms/agent-integration.mdx": "doc_eeed52120ccf", + "docs/llms/contracts.mdx": "doc_e8794369a1a4", + "docs/llms/control-and-session.mdx": "doc_7320ab982fab", + "docs/llms/index.mdx": "doc_f92685a34f7e" +} diff --git a/skills/afk-coder/references/afk-examples.md b/skills/afk-coder/references/afk-examples.md new file mode 100644 index 0000000..38db749 --- /dev/null +++ b/skills/afk-coder/references/afk-examples.md @@ -0,0 +1,2192 @@ +# AFK Examples + +Merged from `docs/library/snippets/*.mdx` for agent-friendly context loading. + +Generated at: 2026-05-31T13:25:20.451068+00:00 + +## 01_minimal_chat_agent + +Source: `docs/library/snippets/01_minimal_chat_agent.mdx` + +```python +--- +title: "01: Minimal Chat Agent" +description: Smallest synchronous AFK agent run. +--- + +This is the simplest possible AFK agent. It demonstrates the three core concepts you need to get started: defining an `Agent` with a model and instructions, creating a `Runner` to execute it, and reading the result from `final_text`. + +If you are new to AFK, start here. Every other example builds on this foundation. + +```python +from afk.agents import Agent +from afk.core import Runner + +agent = Agent(name="chat", model="gpt-4.1-mini", instructions="Answer directly with concrete detail.") +runner = Runner() +result = runner.run_sync(agent, user_message="Define error budget in SRE.") +print(result.final_text) +``` + +## Line-by-line explanation + +**`Agent(...)`** defines the agent's identity and behavior. The `name` is used for telemetry and logging. The `model` specifies which LLM to use. The `instructions` become the system prompt that guides the model's behavior. + +**`Runner()`** creates the execution engine. With no arguments, it uses in-memory defaults: headless interaction mode, no telemetry sink, and no policy engine. This is the fastest way to get started during development. + +**`runner.run_sync(...)`** executes the agent synchronously, blocking until the run completes. Under the hood, this creates an async event loop, runs the agent through the full lifecycle (LLM call, optional tool execution, optional subagent delegation), and returns the terminal `AgentResult`. The `user_message` is the initial prompt sent to the model. + +**`result.final_text`** contains the model's final text response. This is the primary output field on `AgentResult`. Always use `final_text` (not `output_text`) to access the agent's response. + +## What AgentResult contains + +The `AgentResult` dataclass returned by `run_sync` includes: + +| Field | Type | Description | +| --- | --- | --- | +| `final_text` | `str` | The agent's final text response. | +| `state` | `str` | Terminal state: `"completed"`, `"failed"`, `"cancelled"`, or `"degraded"`. | +| `run_id` | `str` | Unique identifier for this run. | +| `thread_id` | `str` | Thread identifier for memory continuity across runs. | +| `tool_executions` | `list` | Records of all tool calls made during the run. | +| `subagent_executions` | `list` | Records of all subagent invocations. | +| `usage` | `UsageAggregate` | Token usage and cost estimates across all LLM calls. | + +## Expected behavior + +When you run this example, the runner makes a single LLM call to `gpt-4.1-mini` with the system instructions and user message. Since no tools are registered, the model responds with text only. The run completes in one step with `state="completed"`, and `final_text` contains a concise definition of error budgets in SRE. + +No network calls, API keys, or external services are required beyond access to the specified LLM provider (configured via environment variables like `OPENAI_API_KEY`). +``` + +## 02_policy_with_hitl + +Source: `docs/library/snippets/02_policy_with_hitl.mdx` + +```python +--- +title: "02: Policy with Human Interaction" +description: Route sensitive actions through policy and human approval. +--- + +Human-in-the-loop (HITL) is the pattern where the agent pauses execution to request approval or input from a human operator before proceeding with a sensitive action. This is critical for any agent that can take destructive or irreversible actions -- deleting data, modifying production systems, sending communications, or spending money. + +AFK implements HITL through two components: a `PolicyEngine` that decides which actions require human intervention, and an `InteractionProvider` that routes the approval request to a human and returns their decision. + +## Basic example + +```python +from afk.agents import Agent, PolicyEngine, PolicyRule +from afk.core import Runner, RunnerConfig + +# Define a policy that gates destructive operations +policy = PolicyEngine(rules=[ + PolicyRule( + rule_id="gate-destructive-ops", + description="Require human approval for any destructive operation", + condition=lambda event: ( + event.tool_name is not None + and any(keyword in event.tool_name.lower() for keyword in ["delete", "drop", "remove"]) + ), + action="request_approval", + reason="Destructive operations require human approval before execution.", + ), +]) + +agent = Agent( + name="change-manager", + model="gpt-4.1-mini", + instructions="You manage infrastructure changes. Always use available tools.", +) + +# Headless mode: approval requests are auto-resolved using approval_fallback +runner = Runner( + policy_engine=policy, + config=RunnerConfig( + interaction_mode="headless", + approval_fallback="deny", # Auto-deny in headless mode + ), +) +result = runner.run_sync(agent, user_message="Drop old production tables") +print(f"State: {result.state}") +# In headless mode with approval_fallback="deny", the destructive action is blocked. +``` + +## Interactive mode with an InteractionProvider + +In production, you typically want a real human to review approval requests. Use `interaction_mode="interactive"` with a custom `InteractionProvider`: + +```python +from afk.core import Runner, RunnerConfig, InMemoryInteractiveProvider +from afk.agents import ApprovalDecision + +# In-memory provider for testing (simulates human approval) +provider = InMemoryInteractiveProvider() + +runner = Runner( + policy_engine=policy, + interaction_provider=provider, + config=RunnerConfig( + interaction_mode="interactive", + approval_timeout_s=300.0, # Wait up to 5 minutes for human response + ), +) + +# In a real application, a separate process or UI would call: +# provider.resolve_approval(request_id, ApprovalDecision(kind="allow")) +``` + +## Headless vs interactive modes + +AFK supports three interaction modes, configured via `RunnerConfig.interaction_mode`: + +| Mode | Behavior | When to Use | +| --- | --- | --- | +| `"headless"` | Approval requests are auto-resolved using `approval_fallback` (default: `"deny"`). No human is involved. | CI/CD pipelines, batch processing, testing, automated workflows where no human is available. | +| `"interactive"` | Approval requests are routed to the configured `InteractionProvider`. The runner pauses until a decision is returned or `approval_timeout_s` expires. | Production applications with human operators, chat UIs with approval buttons, Slack-based approval workflows. | +| `"external"` | Similar to interactive, but designed for use in external orchestration systems where the approval mechanism is managed outside AFK. | Enterprise systems with external approval platforms. | + +## How the policy flow works + +1. The agent calls a tool (e.g., `drop_table`). +2. Before executing, the runner sends a `PolicyEvent` to the `PolicyEngine`. +3. The engine evaluates all rules. If a rule matches, it returns a `PolicyDecision` with the configured action. +4. If the action is `request_approval`: + - In **headless** mode: the runner auto-resolves using `approval_fallback`. + - In **interactive** mode: the runner creates an `ApprovalRequest` and sends it to the `InteractionProvider`. Execution pauses until the provider returns an `ApprovalDecision`. +5. If approved (`kind="allow"`): the tool executes normally. +6. If denied (`kind="deny"`): the tool execution is skipped and the model receives a denial message as the tool result. +7. A `policy_decision` event is emitted in the run event stream for audit purposes. + +## Policy decision actions + +| Action | Effect | +| --- | --- | +| `"allow"` | Proceed with execution. No human interaction needed. | +| `"deny"` | Block execution immediately. The denial reason is reported to the model. | +| `"request_approval"` | Pause and request human approval through the `InteractionProvider`. | +| `"request_user_input"` | Pause and request freeform text input from a human operator. | +``` + +## 03_subagents_with_router + +Source: `docs/library/snippets/03_subagents_with_router.mdx` + +```python +--- +title: "03: Subagents with Router" +description: Delegate workload to specialist subagents and merge outputs. +--- + +When a task is too complex for a single agent, AFK supports delegating subtasks to specialist subagents. The coordinator (or "lead") agent decides what to delegate, and the runner handles dispatching work to subagents, collecting their results, and feeding those results back to the coordinator for synthesis. + +This pattern is useful for incident response, research workflows, content pipelines, and any scenario where different aspects of a task require distinct expertise or instructions. + +## Delegation flow + +```mermaid +flowchart TD + User[User message] --> Lead[Lead agent] + Lead --> Triage[Triage subagent] + Lead --> Analysis[Analysis subagent] + Lead --> Comms[Comms subagent] + Triage --> Lead + Analysis --> Lead + Comms --> Lead + Lead --> Result[Synthesized final_text] +``` + +## Example + +```python +from afk.agents import Agent +from afk.core import Runner + +# Define specialist subagents +triage = Agent( + name="triage", + model="gpt-4.1-mini", + instructions="Classify incident severity as SEV1, SEV2, SEV3, or SEV4 based on the description.", +) +analysis = Agent( + name="analysis", + model="gpt-4.1-mini", + instructions="Identify the most likely root causes for the described incident.", +) +comms = Agent( + name="comms", + model="gpt-4.1-mini", + instructions="Draft a concise stakeholder update email summarizing the incident and current status.", +) + +# Define the coordinator agent +lead = Agent( + name="lead", + model="gpt-4.1-mini", + instructions="Delegate to specialists and synthesize their outputs into a final response.", + subagents=[triage, analysis, comms], +) + +runner = Runner() +result = runner.run_sync(lead, user_message="Investigate API latency spike and draft update") +print(result.final_text) +``` + +## How the coordinator pattern works + +1. The **lead agent** receives the user message and decides how to delegate. It can invoke subagents through tool-like calls that the runner intercepts. + +2. The **runner** dispatches each subagent invocation as a separate run. Subagents execute independently with their own instructions and model configuration. The runner manages concurrency, timeout, and failure handling for each subagent. + +3. **Subagent results** are returned to the lead agent as execution records. Each record contains the subagent's `output_text` and optional error information. + +4. The **lead agent** receives all subagent outputs and synthesizes them into a unified response. This final synthesis step is what produces the coordinator's `final_text`. + +## What subagent_executions contains + +The `AgentResult` returned by the lead agent includes a `subagent_executions` list. Each entry is a `SubagentExecutionRecord` with: + +| Field | Type | Description | +| --- | --- | --- | +| `subagent_name` | `str` | Name of the subagent that was invoked. | +| `success` | `bool` | Whether the subagent completed successfully. | +| `output_text` | `str or None` | The subagent's response text, if it completed. | +| `latency_ms` | `float` | Wall-clock execution time in milliseconds. | +| `error` | `str or None` | Error message if the subagent failed. | + +You can inspect these records to understand what each subagent contributed: + +```python +result = runner.run_sync(lead, user_message="Investigate API latency spike") + +for execution in result.subagent_executions: + status = "OK" if execution.success else "FAILED" + text = execution.output_text or "" + print(f" [{status}] {execution.subagent_name}: {text[:80]}...") +``` + +## Subagent failure handling + +By default, subagent failure policy is `continue`. You can configure stricter or more resilient behavior using `FailSafeConfig`: + +```python +from afk.agents import FailSafeConfig + +lead = Agent( + name="lead", + model="gpt-4.1-mini", + instructions="Delegate to specialists. If any specialist fails, work with available results.", + subagents=[triage, analysis, comms], + fail_safe=FailSafeConfig( + subagent_failure_policy="retry_then_degrade", # Continue with partial results + max_subagent_depth=3, # Prevent deep recursion + ), +) +``` + +With `subagent_failure_policy="retry_then_degrade"`, the lead agent receives error information for failed subagents alongside successful results and can produce a best-effort synthesis. +``` + +## 04_resume_and_compact + +Source: `docs/library/snippets/04_resume_and_compact.mdx` + +```python +--- +title: "04: Resume and Compact" +description: Resume interrupted runs from their last checkpoint and compact retained thread memory to control storage growth. +--- + +## What this snippet demonstrates + +Agent runs can be interrupted by timeouts, cancellations, infrastructure failures, or intentional pauses (such as waiting for human approval). When a run is interrupted, the runner persists a checkpoint containing the run's state at the point of interruption. The `resume()` method picks up from that checkpoint, restoring the conversation history, tool execution records, and step counter so the agent continues where it left off rather than starting from scratch. + +Over time, long-running threads accumulate checkpoint records, event logs, and state entries. The `compact_thread()` method prunes old records according to retention policies, keeping storage bounded without losing the data needed for active runs. + +## Resuming an interrupted run + +```python +import asyncio +from afk.agents import Agent +from afk.core import Runner, RunnerConfig + +agent = Agent( + name="research-bot", + model="gpt-4.1-mini", + instructions="You help users research topics thoroughly.", +) + +runner = Runner(config=RunnerConfig(interaction_mode="headless")) + + +async def main(): + # Start a run that might be interrupted + result = await runner.run( + agent, + user_message="Research the history of distributed systems.", + thread_id="thread_research_001", + ) + + # Save these identifiers for later resume + run_id = result.run_id + thread_id = result.thread_id + print(f"Run completed: state={result.state}") + + # Later, resume from the checkpoint if the run was interrupted. + # The runner loads the latest checkpoint for this run_id + thread_id pair, + # restores the conversation state, and continues execution. + resumed_result = await runner.resume( + agent, + run_id=run_id, + thread_id=thread_id, + ) + print(f"Resumed run: state={resumed_result.state}") + print(resumed_result.final_text) + + +asyncio.run(main()) +``` + +### How resume works internally + +The runner follows this sequence when `resume()` is called: + +1. **Checkpoint lookup** -- The runner queries the memory store for the latest checkpoint matching the given `run_id` and `thread_id`. If no checkpoint exists, it raises `AgentCheckpointCorruptionError`. + +2. **Terminal check** -- If the checkpoint already contains a terminal result (the run completed before the resume was requested), the runner returns that result immediately without re-executing. + +3. **Snapshot restoration** -- The runner loads the runtime snapshot from the checkpoint, which includes the conversation message history, step counter, tool execution records, and any pending subagent state. + +4. **Continued execution** -- The runner calls `run_handle()` internally with the restored snapshot, continuing the step loop from where it was interrupted. + +### Resume method signature + +```python +await runner.resume( + agent, # Agent definition (must match the original run's agent) + run_id="run_123", # The run_id from the interrupted run + thread_id="th_abc", # The thread_id from the interrupted run + context=None, # Optional context overlay for resumed execution +) +``` + +| Parameter | Type | Description | +| --- | --- | --- | +| `agent` | `BaseAgent` | The agent definition used for continued execution. Must match the agent that started the original run. | +| `run_id` | `str` | The unique run identifier from the interrupted run. Found on `result.run_id`. | +| `thread_id` | `str` | The thread identifier from the interrupted run. Found on `result.thread_id`. | +| `context` | `dict` or `None` | Optional context overlay. Merged with the original run context. | + +## Compacting thread memory + +```python +import asyncio +from afk.core import Runner, RunnerConfig +from afk.memory import RetentionPolicy, StateRetentionPolicy + +runner = Runner(config=RunnerConfig(interaction_mode="headless")) + + +async def compact(): + compaction = await runner.compact_thread( + thread_id="thread_research_001", + event_policy=RetentionPolicy(max_age_ms=86_400_000), # Keep last 24 hours + state_policy=StateRetentionPolicy(max_entries=50), # Keep last 50 state entries + ) + print(f"Events removed: {compaction.events_removed}") + print(f"States removed: {compaction.states_removed}") + + +asyncio.run(compact()) +``` + +### How compaction works + +Compaction operates on two dimensions of stored data: + +- **Event retention** -- Controlled by `RetentionPolicy`. Removes event records older than `max_age_ms`. Events are the raw telemetry log entries (LLM calls, tool executions, state transitions) that accumulate over the lifetime of a thread. + +- **State retention** -- Controlled by `StateRetentionPolicy`. Removes state entries that exceed `max_entries`, keeping only the most recent ones. State entries include checkpoint snapshots, conversation summaries, and key-value metadata. + +Both policies are optional. If you omit a policy, that dimension is not compacted. The method returns a `MemoryCompactionResult` with counts of removed records so you can log or alert on compaction activity. + +### When to compact + +- **After long conversations** -- Threads with hundreds of turns accumulate large checkpoint histories. Compact after the conversation ends or reaches a natural break point. +- **On a schedule** -- Run compaction as a background task (e.g., hourly or daily) for threads that are still active but have grown large. +- **Before resume** -- If you know a thread has extensive history, compacting before resume reduces the data the runner needs to load. + +## Error handling + +```python +from afk.agents.errors import AgentCheckpointCorruptionError, AgentConfigurationError + +try: + result = await runner.resume(agent, run_id="invalid", thread_id="missing") +except AgentCheckpointCorruptionError: + # No checkpoint found for this run_id + thread_id combination. + # This means either the run_id is wrong, the checkpoint was compacted away, + # or the memory store was cleared. + print("No checkpoint found -- cannot resume.") +except AgentConfigurationError: + # run_id or thread_id is empty or invalid + print("Invalid run_id or thread_id.") +``` + +## What to read next + +- [Memory](/library/memory) -- Full memory architecture, checkpoint schema, and retention policies. +- [Core Runner](/library/core-runner) -- Step loop lifecycle, state machine, and all runner API methods. +- [Checkpoint Schema](/library/checkpoint-schema) -- Exact structure of checkpoint records stored in memory. +``` + +## 05_direct_llm_structured_output + +Source: `docs/library/snippets/05_direct_llm_structured_output.mdx` + +```python +--- +title: "05: Direct LLM Structured Output" +description: Use afk.llms with schema-validated responses. +--- + +Not every use case needs the full agent loop. Sometimes you want to call an LLM directly with a specific prompt and get back a structured, schema-validated response. AFK's `LLMBuilder` provides a fluent API for constructing LLM clients that can return Pydantic-validated objects directly, without the overhead of the agent run lifecycle. + +Use this pattern for classification, extraction, summarization, and any scenario where you want a single LLM call with a guaranteed output schema. + +## Example + +```python +from pydantic import BaseModel +from afk.llms import LLMBuilder +from afk.llms.types import LLMRequest, Message + +# Define the output schema as a Pydantic model +class Summary(BaseModel): + title: str + bullets: list[str] + +# Build an LLM client using the fluent builder +client = LLMBuilder().provider("openai").model("gpt-4.1-mini").profile("production").build() + +# Make a structured request +resp = await client.chat( + LLMRequest(messages=[Message(role="user", content="Summarize incident timeline")]), + response_model=Summary, +) +print(resp.structured_response) # {"title": "...", "bullets": ["...", "..."]} +print(resp.text) # The raw text response +``` + +## The builder pattern + +`LLMBuilder` uses a fluent (method-chaining) API to construct an LLM client with the exact configuration you need: + +```python +client = ( + LLMBuilder() + .provider("openai") # Which LLM provider to use + .model("gpt-4.1-mini") # Which model + .profile("production") # Apply a preset profile (retry, timeout, etc.) + .build() # Return the configured LLMClient +) +``` + +Each method returns the builder instance, so calls can be chained. The `.build()` call at the end constructs the final `LLMClient` with all specified settings. + +Available builder methods: + +| Method | Purpose | +| --- | --- | +| `.provider(name)` | Set the LLM provider (`"openai"`, `"litellm"`, `"anthropic_agent"`). | +| `.model(name)` | Set the model identifier. | +| `.profile(name)` | Apply a named configuration profile (`"production"`, `"development"`, etc.). | +| `.settings(settings)` | Replace the loaded `LLMSettings`. | +| `.with_middlewares(stack)` | Attach chat, stream, or embedding middleware. | +| `.with_observers(observers)` | Attach LLM lifecycle observers. | +| `.with_cache(cache_backend)` | Attach a cache backend instance or registered backend id. | +| `.with_router(router)` | Attach a router instance or registered router id. | +| `.build()` | Construct and return the `LLMClient`. | + +Sampling controls are request fields, not builder methods. Set them on `LLMRequest`, for example `LLMRequest(..., temperature=0.0, max_tokens=1000)`. + +## Structured output with Pydantic + +When you pass `response_model=YourModel` to `client.chat()`, the client instructs the LLM to return output that conforms to the model's JSON schema. The response is parsed and validated against the Pydantic model: + +- If the LLM returns valid structured output, `resp.structured_response` contains the parsed dictionary and `resp.text` contains the raw response. +- If the LLM returns output that does not match the schema, a `LLMInvalidResponseError` is raised. + +This is powered by the LLM provider's native structured output support (e.g., OpenAI's `response_format` parameter) when available, with a fallback to prompt-based JSON extraction. + +## When to use LLMBuilder vs Runner + +| Use Case | Approach | +| --- | --- | +| Single LLM call, no tools, no memory | `LLMBuilder` -- simpler, faster, no lifecycle overhead. | +| Structured extraction or classification | `LLMBuilder` with `response_model`. | +| Multi-turn conversation with tools | `Runner` -- provides the full agent loop with tool execution, policy, and memory. | +| Subagent delegation | `Runner` -- only the runner supports subagent dispatch. | +| Event streaming to a UI | `Runner` with `run_stream()`. | +| Eval-driven development | `Runner` -- evals require the full `AgentResult` lifecycle. | + +Use `LLMBuilder` when you want precision and control over a single LLM interaction. Use `Runner` when you need the full agentic lifecycle. +``` + +## 06_tool_registry_security + +Source: `docs/library/snippets/06_tool_registry_security.mdx` + +```python +--- +title: "06: Tool Registry Security" +description: Safe tool registration and guardrail practices. +--- + +Tools are the primary way agents interact with external systems. A tool that reads data is fundamentally different from a tool that deletes resources -- and your security model should reflect this. AFK provides multiple layers of defense for tool security: scoped tool definitions with typed arguments, sandbox profiles that restrict execution capabilities, and policy gates that require human approval for destructive operations. + +This page demonstrates how to register tools safely, distinguish between read-only and mutating tools, and configure policy gates to protect against unintended destructive actions. + +## Read-only vs mutating tools + +The most important security distinction is between tools that observe (read-only) and tools that act (mutating). Read-only tools are generally safe to allow broadly. Mutating tools should be tightly scoped and policy-gated. + +```python +from pydantic import BaseModel +from afk.tools import tool, ToolResult + +# --- Read-only tool: safe, broadly permitted --- +class LookupArgs(BaseModel): + resource_id: str + +@tool( + args_model=LookupArgs, + name="get_resource", + description="Look up a resource by ID. Returns resource metadata. Read-only.", +) +def get_resource(args: LookupArgs) -> dict: + return {"id": args.resource_id, "status": "active", "region": "us-east-1"} + + +# --- Mutating tool: destructive, requires policy gate --- +class DeleteArgs(BaseModel): + resource_id: str + +@tool( + args_model=DeleteArgs, + name="delete_resource", + description="Permanently delete a resource by ID. This action is irreversible.", +) +def delete_resource(args: DeleteArgs) -> dict: + # In production: call your API to delete the resource + return {"deleted": args.resource_id} +``` + +Notice the differences: +- The read-only tool (`get_resource`) has a description that explicitly says "Read-only." This signals to both the model and human reviewers that the tool is safe. +- The mutating tool (`delete_resource`) has a description warning about irreversibility. This helps the model understand the severity, and helps policy rules identify destructive operations. + +## Policy gate setup + +Use a `PolicyEngine` to require human approval before any mutating tool executes: + +```python +from afk.agents import Agent, PolicyEngine, PolicyRule, FailSafeConfig +from afk.core import Runner, RunnerConfig + +# Define policy rules that distinguish read vs write operations +policy = PolicyEngine(rules=[ + PolicyRule( + rule_id="gate-delete", + description="Require approval for delete operations", + condition=lambda event: event.tool_name == "delete_resource", + action="request_approval", + reason="Delete operations are irreversible and require human approval.", + ), + PolicyRule( + rule_id="deny-unknown-tools", + description="Deny any tool not explicitly registered", + condition=lambda event: ( + event.tool_name is not None + and event.tool_name not in {"get_resource", "delete_resource"} + ), + action="deny", + reason="Unregistered tools are not permitted.", + ), +]) + +agent = Agent( + name="resource-manager", + model="gpt-4.1-mini", + instructions="Manage resources using the available tools. Always look up a resource before modifying it.", + tools=[get_resource, delete_resource], + fail_safe=FailSafeConfig( + max_tool_calls=10, + max_total_cost_usd=0.10, + ), +) + +runner = Runner( + policy_engine=policy, + config=RunnerConfig( + interaction_mode="headless", + approval_fallback="deny", # Auto-deny destructive actions in headless mode + sanitize_tool_output=True, # Wrap tool output in untrusted-data markers + ), +) + +result = runner.run_sync(agent, user_message="Delete resource res-123") +print(f"State: {result.state}") +# In headless mode, the delete is auto-denied. The model sees the denial and responds accordingly. +``` + +## Sandbox profiles for filesystem tools + +For tools that interact with the filesystem or execute commands, use `SandboxProfile` to restrict their capabilities: + +```python +from afk.tools.security import SandboxProfile +from afk.core import RunnerConfig + +config = RunnerConfig( + default_sandbox_profile=SandboxProfile( + profile_id="restricted", + allow_network=False, # Block network access + allow_command_execution=True, # Allow shell commands + allowed_command_prefixes=["ls", "cat"], # Only safe read commands + deny_shell_operators=True, # Block pipes, redirects, semicolons + allowed_paths=["/app/data"], # Restrict file access to data directory + denied_paths=["/etc", "/root"], # Explicitly deny sensitive paths + command_timeout_s=10.0, # Kill commands after 10 seconds + max_output_chars=5_000, # Truncate large outputs + ), +) +``` + +## Scoping destructive tools + +Follow these principles when registering destructive tools: + +1. **Name them clearly.** Use verb prefixes that signal intent: `delete_`, `remove_`, `drop_`, `update_`, `modify_`. This makes policy rules easy to write and audit. + +2. **Type all arguments.** Use Pydantic models for argument validation. Never accept freeform `dict` arguments for mutating operations. + +3. **Describe irreversibility.** Include "irreversible", "destructive", or "permanent" in the tool description. This helps both the model and policy reviewers understand the risk. + +4. **Gate with policy rules.** Every mutating tool should have a corresponding policy rule. Use `request_approval` for interactive environments and `deny` as the fallback in headless mode. + +5. **Set cost limits.** Use `FailSafeConfig.max_tool_calls` and `max_total_cost_usd` to prevent runaway tool usage, especially when the agent has access to APIs with per-call costs. + +6. **Audit everything.** Policy decisions are emitted as `policy_decision` events in the run event stream. Persist these events for compliance and debugging. +``` + +## 07_tool_hooks_and_middleware + +Source: `docs/library/snippets/07_tool_hooks_and_middleware.mdx` + +```python +--- +title: "07: Tool Hooks and Middleware" +description: Add pre-execution validation, post-execution transformation, and cross-cutting middleware to tools and the LLM client pipeline. +--- + +## What this snippet demonstrates + +AFK provides two distinct hook/middleware systems that operate at different layers: + +1. **Tool hooks and middleware** -- Pre-hooks, post-hooks, and middleware that wrap individual tool executions. These use Pydantic models for typed arguments and run inside the tool execution pipeline. + +2. **LLM middleware** -- Middleware that wraps LLM client operations (chat, stream, embed). These intercept requests and responses at the provider transport layer. + +Both systems follow the same pattern: define a callable, wire it into the pipeline, and the runner executes it at the appropriate point in the lifecycle. + +## Tool pre-hooks + +A pre-hook runs before the main tool function executes. It receives the tool's arguments (validated against its own Pydantic model) and returns a dictionary of transformed arguments that the main tool will receive. Use pre-hooks for input sanitization, enrichment, or validation that should happen before execution. + +```python +from pydantic import BaseModel, Field +from afk.tools.core.decorator import tool, prehook + + +# Pre-hook argument model matches the main tool's argument shape +class SearchArgs(BaseModel): + query: str + max_results: int = Field(default=10, ge=1, le=100) + + +# Pre-hook: sanitize and normalize the query before the tool runs +@prehook(args_model=SearchArgs, name="normalize_query") +async def normalize_query(args: SearchArgs) -> dict: + """Strip extra whitespace and lowercase the query.""" + return { + "query": " ".join(args.query.lower().split()), + "max_results": min(args.max_results, 50), # Cap at 50 + } + + +# Main tool with the pre-hook attached +@tool( + args_model=SearchArgs, + name="search_docs", + description="Search the documentation index.", + prehooks=[normalize_query], +) +async def search_docs(args: SearchArgs) -> dict: + # args.query is already normalized by the pre-hook + return {"results": [f"Result for: {args.query}"], "count": args.max_results} +``` + +### Pre-hook execution flow + +```mermaid +flowchart LR + LLM[LLM proposes tool call] --> Validate[Validate raw args] + Validate --> PreHook[Pre-hook transforms args] + PreHook --> Execute[Tool function executes] + Execute --> Result[ToolResult returned] +``` + +The pre-hook receives validated arguments and must return a dictionary compatible with the main tool's `args_model`. If the returned dictionary fails validation against the tool's model, the tool call fails with a `ToolValidationError`. + +## Tool post-hooks + +A post-hook runs after the main tool function completes. It receives the tool output and can transform or annotate the result before it is returned to the LLM. Use post-hooks for output sanitization, logging, or enrichment. + +```python +from pydantic import BaseModel +from typing import Any +from afk.tools.core.decorator import posthook + + +class PostHookArgs(BaseModel): + output: Any + tool_name: str | None = None + + +@posthook(args_model=PostHookArgs, name="redact_sensitive") +async def redact_sensitive(args: PostHookArgs) -> dict: + """Remove sensitive fields from tool output before returning to LLM.""" + output = args.output + if isinstance(output, dict): + # Strip any fields that might contain secrets + sanitized = { + k: v for k, v in output.items() + if k not in ("api_key", "secret", "password", "token") + } + return {"output": sanitized, "tool_name": args.tool_name} + return {"output": output, "tool_name": args.tool_name} +``` + +AFK passes post-hooks a payload dictionary with the shape `{"output": , "tool_name": ""}`. The post-hook must return a dictionary with the same shape. + +## Tool-level middleware + +Tool-level middleware wraps around the entire tool execution, including pre-hooks and post-hooks. Middleware receives a `call_next` function and the tool arguments, and can modify behavior before, after, or around execution. + +```python +from afk.tools.core.decorator import middleware + + +@middleware(name="timing_middleware") +async def timing_middleware(call_next, args, ctx): + """Measure and log tool execution time.""" + import time + start = time.monotonic() + result = await call_next(args, ctx) + elapsed_ms = (time.monotonic() - start) * 1000 + print(f"Tool executed in {elapsed_ms:.1f}ms") + return result + + +@middleware(name="retry_on_transient") +async def retry_on_transient(call_next, args, ctx): + """Retry the tool once on transient errors.""" + try: + return await call_next(args, ctx) + except ConnectionError: + # One retry on connection errors + return await call_next(args, ctx) +``` + +### Attaching middleware to a tool + +```python +@tool( + args_model=SearchArgs, + name="search_docs", + description="Search the documentation index.", + prehooks=[normalize_query], + posthooks=[redact_sensitive], + middlewares=[timing_middleware, retry_on_transient], +) +async def search_docs(args: SearchArgs) -> dict: + return {"results": [...]} +``` + +Middleware executes in the order listed. The first middleware in the list is the outermost wrapper. Each middleware calls `call_next` to pass control to the next middleware (or the actual tool function if it is the last one). + +## Registry-level middleware + +Registry-level middleware applies to every tool in a `ToolRegistry`, not just a single tool. Use this for cross-cutting concerns like audit logging, rate limiting, or policy enforcement that should apply uniformly. + +```python +from afk.tools.core.decorator import registry_middleware + + +@registry_middleware(name="audit_log") +async def audit_log(call_next, tool, raw_args, ctx): + """Log every tool invocation for audit purposes.""" + print(f"AUDIT: tool={tool.spec.name} args={raw_args}") + result = await call_next(tool, raw_args, ctx) + print(f"AUDIT: tool={tool.spec.name} success={result.success}") + return result +``` + +## LLM client middleware + +LLM middleware operates at the provider transport layer, intercepting requests to and responses from the LLM API. AFK defines three middleware protocols for the three LLM operations: + +```python +from afk.llms import LLMBuilder, LLMRequest, LLMResponse +from afk.llms.middleware import MiddlewareStack + + +# Chat middleware: intercepts non-streaming chat requests +async def add_request_metadata(call_next, req: LLMRequest) -> LLMResponse: + """Add tracing metadata to every LLM request.""" + req.metadata = req.metadata or {} + req.metadata["trace_id"] = "trace_abc123" + return await call_next(req) + + +# Build client with middleware +client = ( + LLMBuilder() + .provider("openai") + .model("gpt-4.1-mini") + .profile("production") + .with_middlewares(MiddlewareStack( + chat=[add_request_metadata], + embed=[], + stream=[], + )) + .build() +) +``` + +### LLM middleware protocols + +| Protocol | Operation | Signature | +| --- | --- | --- | +| `LLMChatMiddleware` | Non-streaming chat | `async (call_next, req: LLMRequest) -> LLMResponse` | +| `LLMEmbedMiddleware` | Embeddings | `async (call_next, req: EmbeddingRequest) -> EmbeddingResponse` | +| `LLMStreamMiddleware` | Streaming chat | `(call_next, req: LLMRequest) -> AsyncIterator[LLMStreamEvent]` | + +Each middleware receives `call_next` (the next middleware or transport in the chain) and the request object. It can modify the request before calling `call_next`, modify the response after, or short-circuit entirely by returning a response without calling `call_next`. + +## Built-in LLM middleware + +AFK ships with pre-built middleware for common patterns: + +### Timeout middleware + +Apply per-request timeouts to prevent runaway calls: + +```python +from afk.llms.middleware.timeout import ( + TimeoutMiddleware, + EmbedTimeoutMiddleware, + StreamTimeoutMiddleware, + TimeoutConfig, +) +from afk.llms.middleware import MiddlewareStack + +# Configure timeouts +config = TimeoutConfig( + default_timeout_s=30.0, + chat_timeout_s=60.0, + embed_timeout_s=15.0, + stream_timeout_s=45.0, +) + +# Add to middleware stack +stack = MiddlewareStack( + chat=[TimeoutMiddleware(config)], + embed=[EmbedTimeoutMiddleware(config)], + stream=[StreamTimeoutMiddleware(config)], +) + +# Build client +client = ( + LLMBuilder() + .provider("openai") + .model("gpt-4.1-mini") + .with_middlewares(stack) + .build() +) +``` + +The timeout middleware respects `TimeoutPolicy` from the request if provided: +```python +req = LLMRequest( + model="gpt-4.1-mini", + messages=[...], + timeout_policy=TimeoutPolicy(request_timeout_s=45.0), # Override config +) +``` + +## When to use each layer + +| Layer | Scope | Use for | +| --- | --- | --- | +| **Tool pre-hook** | Single tool, before execution | Input sanitization, argument enrichment, validation | +| **Tool post-hook** | Single tool, after execution | Output sanitization, redaction, annotation | +| **Tool middleware** | Single tool, wraps execution | Timing, retries, caching, error handling | +| **Registry middleware** | All tools in registry | Audit logging, rate limiting, policy enforcement | +| **LLM middleware** | All LLM calls through client | Request metadata, response logging, tracing | + +## What to read next + +- [Tools](/library/tools) -- Full tool system architecture, the 6-step execution pipeline, and design guidelines. +- [Tool Call Lifecycle](/library/tool-call-lifecycle) -- Detailed lifecycle of a tool call from LLM proposal to result delivery. +- [LLMs Overview](/llms/index) -- Builder workflow, runtime profiles, and provider selection. +``` + +## 08_prebuilt_runtime_tools + +Source: `docs/library/snippets/08_prebuilt_runtime_tools.mdx` + +```python +--- +title: "08: Prebuilt Runtime Tools" +description: Use AFK's built-in filesystem tools with directory-scoped security constraints and compose them with policy checks. +--- + +## What this snippet demonstrates + +AFK ships prebuilt tools for common runtime operations like listing directories and reading files. These tools are designed with security-first defaults: every tool is scoped to an explicit root directory that prevents directory traversal attacks. This snippet shows how to create, configure, and compose prebuilt tools with agents and policy guards. + +## Building runtime tools + +The `build_runtime_tools()` factory creates a set of filesystem tools bound to a specific root directory. All path operations within these tools are resolved against this root, and any attempt to access files outside it raises a `FileAccessError`. + +```python +from pathlib import Path +from afk.agents import Agent +from afk.core import Runner, RunnerConfig +from afk.tools.prebuilts.runtime import build_runtime_tools + +# Create filesystem tools scoped to a specific directory +runtime_tools = build_runtime_tools(root_dir=Path("./workspace")) + +agent = Agent( + name="file-assistant", + model="gpt-4.1-mini", + instructions=( + "You help users explore and read files in the workspace directory. " + "Use list_directory to browse the directory structure and read_file " + "to read file contents. You cannot access files outside the workspace." + ), + tools=runtime_tools, +) + +runner = Runner(config=RunnerConfig(interaction_mode="headless")) +result = runner.run_sync(agent, user_message="What files are in the workspace?") +print(result.final_text) +``` + +## Available prebuilt tools + +The `build_runtime_tools()` factory produces two tools: + +### list_directory + +Lists entries in a directory under the configured root. Returns entry names, paths, and type flags (file or directory). + +| Parameter | Type | Default | Description | +| ------------- | ----- | ------- | ----------------------------------------------------------------- | +| `path` | `str` | `"."` | Relative path to list, resolved against the root directory. | +| `max_entries` | `int` | `200` | Maximum entries to return (1--5000). Prevents unbounded listings. | + +**Returns:** A dictionary with `root`, `path`, and `entries` (list of `{name, path, is_dir, is_file}`). + +### read_file + +Reads the contents of a file under the configured root, with configurable truncation to prevent excessive token consumption. + +| Parameter | Type | Default | Description | +| ----------- | ----- | ---------- | -------------------------------------------------------------------------------- | +| `path` | `str` | (required) | Relative path to the file, resolved against the root directory. | +| `max_chars` | `int` | `20_000` | Maximum characters to read (1--500,000). Content is truncated beyond this limit. | + +**Returns:** A dictionary with `root`, `path`, `content`, and `truncated` (boolean indicating whether content was truncated). + +## Security: directory traversal prevention + +Every path operation is validated with an internal containment check that uses Python's `Path.relative_to()` to verify that the resolved path stays within the configured root. This prevents attacks like: + +``` +../../etc/passwd # Blocked: escapes root +/absolute/path/to/secrets # Blocked: escapes root +./workspace/../../../etc # Blocked: resolved path escapes root +``` + +If a path escapes the root, the tool raises `FileAccessError` immediately, before any file I/O occurs. + +## Composing with policy checks + +For additional security, pair runtime tools with a policy engine that gates specific operations on approval: + +```python +from afk.agents import Agent, PolicyEngine, PolicyRule + +# Define a policy that requires approval for reading certain files +policy = PolicyEngine( + rules=[ + PolicyRule( + tool_name="read_file", + description="Require approval for reading config files", + condition=lambda event: ".env" in event.tool_args.get("path", "") + or "config" in event.tool_args.get("path", ""), + action="request_approval", + approval_message="Agent wants to read a config file: {path}", + ), + ] +) + +agent = Agent( + name="ops-assistant", + model="gpt-4.1-mini", + instructions="Use approved runtime tools only. Never read sensitive configuration without approval.", + tools=build_runtime_tools(root_dir=Path("./project")), +) + +runner = Runner( + policy_engine=policy, + config=RunnerConfig(interaction_mode="headless"), +) +``` + +## Composing with custom tools + +You can combine prebuilt tools with your own custom tools in a single agent: + +```python +from pydantic import BaseModel +from afk.tools import tool + + +class GrepArgs(BaseModel): + pattern: str + path: str = "." + + +@tool( + args_model=GrepArgs, + name="grep_files", + description="Search for a pattern in files within the workspace.", +) +async def grep_files(args: GrepArgs) -> dict: + # Your custom search implementation + return {"matches": [], "pattern": args.pattern} + + +# Combine prebuilt + custom tools +all_tools = build_runtime_tools(root_dir=Path("./workspace")) + [grep_files] + +agent = Agent( + name="dev-assistant", + model="gpt-4.1-mini", + instructions="Help developers explore and search the codebase.", + tools=all_tools, +) +``` + +## Command allowlists and sandbox profiles + +For production environments, restrict tool capabilities further using sandbox profiles: + +```python +from afk.tools.security import SandboxProfile + +# Create a read-only sandbox that restricts what operations tools can perform +read_only_profile = SandboxProfile( + name="read_only", + allowed_operations=["read", "list"], + denied_operations=["write", "delete", "execute"], + max_file_size_bytes=1_000_000, # 1 MB max read size + allowed_extensions=[".py", ".md", ".txt", ".json", ".yaml"], +) +``` + +This ensures that even if the LLM attempts to use tools for unauthorized operations, the sandbox profile blocks execution before any I/O occurs. + +## What to read next + +- [Tools](/library/tools) -- Full tool system architecture, including the `@tool` decorator, `ToolResult`, and execution pipeline. +- [Snippet 06: Tool Registry Security](/library/snippets/06_tool_registry_security) -- Security scoping, policy gates, and sandbox profiles in detail. +- [Security Model](/library/security-model) -- Threat model, defense layers, and RunnerConfig security fields. +``` + +## 09_system_prompt_loader + +Source: `docs/library/snippets/09_system_prompt_loader.mdx` + +```python +--- +title: "09: System Prompt Loader" +description: Resolve agent system prompts from a file hierarchy with deterministic precedence, Jinja templating, and stat-based caching. +--- + +## What this snippet demonstrates + +AFK agents need system prompts (instructions) that tell the LLM how to behave. Rather than hardcoding instructions as inline strings, AFK provides a file-based prompt resolution system that loads prompts from a directory hierarchy. This keeps prompts version-controlled, editable by non-developers, and reusable across agents. + +The prompt loader resolves instructions through a deterministic precedence chain, supports Jinja2 templating for dynamic prompts, and caches compiled templates using stat-based invalidation for hot-reload during development. + +## Resolution precedence + +The prompt system resolves agent instructions through this priority chain: + +```mermaid +flowchart TD + A[Agent has inline instructions?] -->|Yes| B[Use inline instructions] + A -->|No| C[Agent has instruction_file?] + C -->|Yes| D[Load from instruction_file path] + C -->|No| E[Auto-detect from agent name] + E --> F[Convert name to UPPER_SNAKE_CASE.md] + F --> G[Look in prompts_dir] +``` + +1. **Inline `instructions`** -- If the agent has a non-empty `instructions` string, it is used directly. No file loading occurs. +2. **Explicit `instruction_file`** -- If set, the file is loaded from the configured `prompts_dir`. The path must resolve to a file inside the prompts root (no directory traversal). +3. **Auto-detected file** -- If neither is set, the agent's name is converted to `UPPER_SNAKE_CASE.md` and loaded from `prompts_dir`. + +## Basic usage + +```python +from afk.agents import Agent + +# Option 1: Inline instructions (highest priority) +agent = Agent( + name="ChatAgent", + model="gpt-4.1-mini", + instructions="Answer customer questions concisely.", +) + +# Option 2: Explicit instruction file +agent = Agent( + name="ChatAgent", + model="gpt-4.1-mini", + instruction_file="chat_agent_system.md", # Loaded from prompts_dir + prompts_dir=".agents/prompt", +) + +# Option 3: Auto-detected file (uses agent name) +# Loads .agents/prompt/CHAT_AGENT.md automatically +agent = Agent( + name="ChatAgent", + model="gpt-4.1-mini", + prompts_dir=".agents/prompt", +) +``` + +## Name-to-filename conversion + +The auto-detection algorithm converts the agent name to a filename using these rules: + +| Agent Name | Derived Filename | Rule Applied | +| --- | --- | --- | +| `ChatAgent` | `CHAT_AGENT.md` | CamelCase split on boundaries | +| `chatagent` | `CHAT_AGENT.md` | Lowercase `agent` suffix detected and split | +| `research-assistant` | `RESEARCH_ASSISTANT.md` | Hyphens replaced with underscores | +| `QA Bot v2` | `QA_BOT_V2.md` | Spaces and non-alphanumeric chars become underscores | + +The conversion is handled by `derive_auto_prompt_filename()` internally. It splits camelCase boundaries, normalizes non-alphanumeric characters to underscores, collapses consecutive underscores, and uppercases the result. + +## Prompts directory resolution + +The prompts directory is resolved through its own priority chain: + +1. Explicit `prompts_dir` argument on the `Agent` constructor. +2. `AFK_AGENT_PROMPTS_DIR` environment variable. +3. Default: `.agents/prompt` relative to the current working directory. + +```python +# Explicit +agent = Agent(name="Bot", model="gpt-4.1-mini", prompts_dir="/opt/prompts") + +# Environment variable +# export AFK_AGENT_PROMPTS_DIR=/opt/prompts +agent = Agent(name="Bot", model="gpt-4.1-mini") + +# Default: .agents/prompt/ +agent = Agent(name="Bot", model="gpt-4.1-mini") +``` + +## Jinja2 templating + +Prompt files support Jinja2 template syntax. When the runner resolves a prompt, it renders the template with a context dictionary that includes agent metadata and any custom context passed to the run. + +**File: `.agents/prompt/SUPPORT_AGENT.md`** + +```markdown +You are {{ agent_name }}, a support agent for {{ ctx.company_name }}. + +Your responsibilities: +- Answer questions about {{ ctx.product_name }} +- Escalate billing issues to the billing team +- Never disclose internal pricing formulas + +{% if ctx.get("tone") == "formal" %} +Use formal language and address the customer by title. +{% else %} +Use friendly, conversational language. +{% endif %} +``` + +**Agent code:** + +```python +from afk.core import Runner, RunnerConfig + +agent = Agent( + name="SupportAgent", + model="gpt-4.1-mini", + prompts_dir=".agents/prompt", +) + +runner = Runner(config=RunnerConfig(interaction_mode="headless")) +result = runner.run_sync( + agent, + user_message="How do I reset my password?", + context={ + "company_name": "Acme Corp", + "product_name": "Acme Cloud", + "tone": "friendly", + }, +) +``` + +### Template context variables + +The following variables are available in every prompt template: + +| Variable | Type | Description | +| --- | --- | --- | +| `agent_name` | `str` | The agent's `name` field. | +| `agent_class` | `str` | The Python class name of the agent. | +| `context` | `dict` | The full context dictionary passed to the run. | +| `ctx` | `dict` | Alias for `context` (shorthand). | + +Any keys in the `context` dictionary that are not reserved names (`context`, `ctx`, `agent_name`, `agent_class`) are also available as top-level template variables. So `{{ company_name }}` works as a shorthand for `{{ ctx.company_name }}`. + +## Caching and hot-reload + +The prompt system uses a process-wide `PromptStore` singleton that caches at three levels: + +1. **File cache** -- Keyed by resolved file path. Uses `stat()` metadata (mtime, size, inode) as the cache signature. If the file changes on disk, the cache entry is invalidated automatically. + +2. **Text pool** -- Deduplicates prompt text by SHA-256 hash. If multiple agents use the same prompt content (even from different files), only one copy is stored in memory. + +3. **Template cache** -- Compiled Jinja2 templates are cached by content hash. Re-rendering with different context variables reuses the compiled template. + +This means that during development, you can edit prompt files and they will be picked up on the next run without restarting the process. In production, the stat-based check is a single `os.stat()` call per prompt resolution, which is negligible overhead. + +## Security: path containment + +The prompt loader enforces strict path containment. The resolved prompt file path must be inside the configured `prompts_dir`. If an `instruction_file` path resolves outside the prompts root (via `../` traversal or an absolute path pointing elsewhere), the loader raises `PromptAccessError` immediately. + +```python +# This would raise PromptAccessError: +agent = Agent( + name="Agent", + model="gpt-4.1-mini", + instruction_file="../../etc/passwd", # Escapes prompts root + prompts_dir=".agents/prompt", +) +``` + +## What to read next + +- [System Prompts](/library/system-prompts) -- Full system prompt architecture, resolution pipeline, and design guidelines. +- [Agents](/library/agents) -- Agent model, configuration fields, and composition patterns. +- [Security Model](/library/security-model) -- Threat model and defense layers including prompt injection considerations. +``` + +## 10_streaming_chat_with_memory + +Source: `docs/library/snippets/10_streaming_chat_with_memory.mdx` + +```python +--- +title: "10: Streaming Chat with Memory" +description: Combine real-time streaming with thread-based memory for multi-turn chat UIs. +--- + +## What this snippet demonstrates + +Most chat applications need two things simultaneously: **real-time streaming** (so users see text as it's generated) and **memory continuity** (so the agent remembers previous turns). This snippet shows how to combine `run_stream()` with `thread_id` to build a multi-turn streaming chat handler. + +## Full example + +```python +import asyncio +from afk.agents import Agent, FailSafeConfig +from afk.core import Runner, RunnerConfig + +agent = Agent( + name="chat-assistant", + model="gpt-4.1-mini", + instructions=""" + You are a helpful assistant. Remember context from earlier in the conversation. + Be concise but thorough. If the user refers to something from a previous message, + use that context in your response. + """, + fail_safe=FailSafeConfig( + max_steps=10, + max_total_cost_usd=0.25, + ), +) + + +async def stream_turn(runner: Runner, user_message: str, thread_id: str): + """Stream a single turn and return the result.""" + handle = await runner.run_stream( + agent, + user_message=user_message, + thread_id=thread_id, # ← Same thread_id = same conversation + ) + + async for event in handle: + match event.type: + case "text_delta": + print(event.text_delta, end="", flush=True) + case "tool_started": + print(f"\n[TOOL] {event.tool_name}...") + case "tool_completed": + status = "[OK]" if event.tool_success else "[ERR]" + print(f" {status} done") + case "error": + if event.error: + print(f"\n[WARN] {event.error}") + case "completed": + print(f"\n[DONE] ({event.result.state})") + + return handle.result + + +async def main(): + runner = Runner(config=RunnerConfig(interaction_mode="headless")) + thread = "session-demo-42" + + # Turn 1 + print("User: What is the GIL in Python?\n") + print("Assistant: ", end="") + r1 = await stream_turn(runner, "What is the GIL in Python?", thread) + + # Turn 2 — agent remembers Turn 1 + print("\n\nUser: How does it affect multithreading?\n") + print("Assistant: ", end="") + r2 = await stream_turn(runner, "How does it affect multithreading?", thread) + + # Turn 3 — agent still has full context + print("\n\nUser: What are the alternatives?\n") + print("Assistant: ", end="") + r3 = await stream_turn(runner, "What are the alternatives?", thread) + + # Print usage summary + print(f"\n\n--- Usage ---") + for i, r in enumerate([r1, r2, r3], 1): + print(f"Turn {i}: {r.usage.total_tokens} tokens") + + +asyncio.run(main()) +``` + +## Key patterns + +### Thread ID connects turns + +Pass the same `thread_id` across `run_stream()` calls to maintain conversation context: + +```python +# These two calls share memory +r1 = await runner.run_stream(agent, user_message="Hello", thread_id="t-42") +r2 = await runner.run_stream(agent, user_message="Follow up", thread_id="t-42") +``` + +### Access the result after streaming + +The `handle.result` is available after the stream completes: + +```python +async for event in handle: + ... # Process events + +result = handle.result # Full AgentResult with final_text, usage, etc. +``` + +### Cancel mid-stream + +If the user navigates away or clicks "stop": + +```python +await handle.cancel() +# The run transitions to "cancelled" state +``` + +## What to read next + +- [Streaming](/library/streaming) — Full event reference and stream control API. +- [Memory](/library/memory) — Thread persistence, compaction, and backend configuration. +- [Snippet 04: Resume + Compact](/library/snippets/04_resume_and_compact) — Checkpoint-based resumption and memory management. +``` + +## 11_cost_monitoring + +Source: `docs/library/snippets/11_cost_monitoring.mdx` + +```python +--- +title: "11: Cost Monitoring" +description: Track and control agent costs using FailSafeConfig budgets and telemetry events. +--- + +## What this snippet demonstrates + +Runaway agent loops are the most common source of unexpected API costs. AFK provides two defense layers: **cost budgets** that kill runs when spending exceeds a threshold, and **telemetry events** that let you observe cost in real time. This snippet shows how to configure both. + +## Setting cost budgets + +The simplest defense is a hard cost ceiling on every agent: + +```python +from afk.agents import Agent, FailSafeConfig + +agent = Agent( + name="budget-agent", + model="gpt-4.1-mini", + instructions="Be helpful and concise.", + fail_safe=FailSafeConfig( + max_total_cost_usd=0.50, # Hard cost ceiling + max_llm_calls=30, # Secondary defense: limit API calls + max_steps=15, # Tertiary defense: limit reasoning steps + max_wall_time_s=120.0, # Quaternary defense: wall-clock timeout + ), +) +``` + +When the estimated cost exceeds `max_total_cost_usd`, the runner terminates the run with a `degraded` state and returns the best partial result. + +## Monitoring cost from results + +Every `AgentResult` includes token counts and cost estimates: + +```python +from afk.core import Runner + +runner = Runner() +result = runner.run_sync(agent, user_message="Analyze this dataset...") + +# Access usage statistics +usage = result.usage_aggregate +print(f"Input tokens: {usage.input_tokens}") +print(f"Output tokens: {usage.output_tokens}") +print(f"Total tokens: {usage.total_tokens}") +print(f"Estimated cost: ${result.total_cost_usd or 0:.4f}") +print(f"Tool calls: {len(result.tool_executions)}") +``` + +## Real-time cost monitoring via streaming + +For long-running agents, monitor cost during execution: + +```python +import asyncio +from afk.agents import Agent, FailSafeConfig +from afk.core import Runner + +agent = Agent( + name="analyst", + model="gpt-4.1", + instructions="Provide detailed analysis.", + fail_safe=FailSafeConfig( + max_total_cost_usd=1.00, + max_steps=20, + ), +) + + +async def monitor_cost(): + runner = Runner() + handle = await runner.run_stream( + agent, user_message="Compare Python async patterns for service code" + ) + + step_count = 0 + async for event in handle: + match event.type: + case "text_delta": + print(event.text_delta, end="", flush=True) + case "step_started" if event.step is not None: + step_count = event.step + case "tool_completed": + print(f"\n [STEP] Step {step_count} | Tool: {event.tool_name}") + case "completed" if event.result is not None: + usage = event.result.usage_aggregate + print(f"\n\n--- Cost Summary ---") + print(f"State: {event.result.state}") + print(f"Tokens: {usage.total_tokens}") + print(f"Cost: ${event.result.total_cost_usd or 0:.4f}") + print(f"Tools: {len(event.result.tool_executions)}") + +asyncio.run(monitor_cost()) +``` + +## Cost-aware batch processing + +When running multiple agents in a batch, track cumulative cost: + +```python +async def batch_process(items: list[str], budget_usd: float): + """Process items with a shared cost budget.""" + runner = Runner() + cumulative_cost = 0.0 + results = [] + + for item in items: + if cumulative_cost >= budget_usd: + print(f"[Limit] Budget exhausted at ${cumulative_cost:.4f}") + break + + # Set per-item budget as remaining budget + remaining = budget_usd - cumulative_cost + agent = Agent( + name="batch-processor", + model="gpt-4.1-mini", + instructions="Process the item concisely.", + fail_safe=FailSafeConfig( + max_total_cost_usd=min(remaining, 0.10), # Per-item cap + max_steps=5, + ), + ) + + result = await runner.run(agent, user_message=item) + item_cost = result.total_cost_usd or 0.0 + cumulative_cost += item_cost + results.append(result) + + print(f" [OK] {item[:40]}... (${item_cost:.4f})") + + print(f"\nTotal: {len(results)} items, ${cumulative_cost:.4f}") + return results +``` + +## Operating recommendations + +1. **Always set `max_total_cost_usd`** — even generous limits prevent runaway costs +2. **Layer defenses** — combine cost limits with `max_llm_calls`, `max_steps`, and `max_wall_time_s` +3. **Use telemetry for dashboards** — export metrics to monitor cost trends over time +4. **Set per-item budgets in batches** — prevent one expensive item from consuming the entire budget +5. **Choose models by task** — use smaller models for routine work and reserve larger models for requests that need them + +## What to read next + +- [Observability](/library/observability) — Telemetry pipeline for metrics and dashboards. +- [Failure Policy Matrix](/library/failure-policy-matrix) — How cost limit breaches flow through the system. +- [Configuration Reference](/library/configuration-reference#failsafeconfig) — Full FailSafeConfig field reference. +``` + +## 12_mcp_client_integration + +Source: `docs/library/snippets/12_mcp_client_integration.mdx` + +```python +--- +title: "12: MCP Client Integration" +description: Discover and use tools from external MCP servers in your agents. +--- + +## What this snippet demonstrates + +AFK agents can consume tools from external MCP (Model Context Protocol) servers just like local tools. This snippet shows how to connect to an MCP server, discover available tools, and attach them to an agent — all with the same validation, policy gates, and telemetry as local tools. + +## Consuming MCP tools + +### Connect, discover, and attach + +```python +import asyncio +from afk.agents import Agent, FailSafeConfig +from afk.core import Runner +from afk.mcp import MCPStore + +async def main(): + # 1. Connect to an external MCP server + store = MCPStore() + await store.connect("https://tools.example.com:3001") + + # 2. Discover available tools + tools = await store.list_tools() + print(f"Found {len(tools)} tools:") + for t in tools: + print(f" • {t.name}: {t.description}") + + # 3. Attach MCP tools to an agent — they work like local tools + agent = Agent( + name="mcp-assistant", + model="gpt-4.1-mini", + instructions=""" + Use the available tools to help the user. + Always explain what tool you're using and why. + """, + tools=tools, + fail_safe=FailSafeConfig( + max_total_cost_usd=0.25, + max_tool_calls=10, + ), + ) + + # 4. Run the agent — MCP tools execute transparently + runner = Runner() + result = runner.run_sync( + agent, user_message="Search the documentation for authentication patterns" + ) + print(f"\n{result.final_text}") + + # 5. Inspect tool calls — MCP tools appear just like local tools + for rec in result.tool_executions: + print(f" {'[OK]' if rec.success else '[ERR]'} {rec.tool_name} ({rec.latency_ms:.0f}ms)") + + # 6. Disconnect + await store.disconnect() + +asyncio.run(main()) +``` + +## Using the Agent's built-in MCP support + +For simpler setups, pass MCP server refs directly to the agent: + +```python +from afk.agents import Agent + +# The agent connects to MCP servers automatically during startup +agent = Agent( + name="connected-agent", + model="gpt-4.1-mini", + instructions="Use available tools to help the user.", + mcp_servers=[ + "https://tools.example.com:3001", # Simple URL + "search=https://search.internal:3002", # Named server + {"url": "https://db.internal:3003", "auth": "token-xyz"}, # With auth + ], + enable_mcp_tools=True, # Default: True +) +``` + +## Mixing local and MCP tools + +Combine your own tools with external MCP tools: + +```python +from pydantic import BaseModel +from afk.agents import Agent +from afk.tools import tool +from afk.mcp import MCPStore + +class SummaryArgs(BaseModel): + text: str + max_words: int = 100 + +@tool(args_model=SummaryArgs, name="summarize", description="Summarize text concisely.") +def summarize(args: SummaryArgs) -> dict: + # Your local summarization logic + return {"summary": args.text[:args.max_words * 5] + "..."} + + +async def build_agent(): + # Get external tools + store = MCPStore() + await store.connect("https://tools.example.com:3001") + mcp_tools = await store.list_tools() + + # Combine local + external tools + agent = Agent( + name="hybrid-agent", + model="gpt-4.1-mini", + instructions="Use search tools for research and summarize for concise output.", + tools=[summarize] + mcp_tools, # ← Mix freely + ) + return agent +``` + +## Security with MCP tools + +Apply policy rules to MCP-sourced tools just like local tools: + +```python +from afk.agents import PolicyEngine, PolicyRule +from afk.core import Runner + +policy = PolicyEngine(rules=[ + PolicyRule( + rule_id="gate-mcp-writes", + condition=lambda e: e.tool_name and "write" in e.tool_name, + action="request_approval", + reason="MCP write operations need human approval", + ), +]) + +runner = Runner(policy_engine=policy) +``` + + + **MCP tools are transparent.** Once attached to an agent, they go through the + same validation, policy gates, sanitization, and telemetry as local tools. The + agent doesn't know whether a tool is local or remote. + + +## What to read next + +- [MCP Server](/library/mcp-server) — Expose your own tools via MCP, plus authentication and rate limiting. +- [Tools](/library/tools) — Full tool system architecture. +- [Snippet 06: Tool Security](/library/snippets/06_tool_registry_security) — Policy gates and sandbox profiles. +``` + +## 13_multi_model_fallback + +Source: `docs/library/snippets/13_multi_model_fallback.mdx` + +```python +--- +title: "13: Multi-Model Fallback" +description: Configure fallback model chains for LLM resilience and cost optimization. +--- + +## What this snippet demonstrates + +LLM API calls fail — rate limits, outages, timeouts. AFK's `fallback_model_chain` lets you define an ordered list of models to try when the primary model fails. This snippet shows how to configure fallback chains for resilience, cost optimization, and provider diversification. + +## Basic fallback chain + +```python +from afk.agents import Agent, FailSafeConfig + +agent = Agent( + name="resilient-agent", + model="gpt-4.1", # Primary model + instructions="Be helpful and thorough.", + fail_safe=FailSafeConfig( + # Fallback chain: try these models in order if the primary fails + fallback_model_chain=[ + "gpt-4.1-mini", # First fallback: cheaper, faster + "gpt-4.1-nano", # Last resort: fastest, cheapest + ], + + # When LLM calls fail, retry then degrade + llm_failure_policy="retry_then_degrade", + + # Cost ceiling still applies across all models + max_total_cost_usd=1.00, + ), +) +``` + +When `gpt-4.1` fails (timeout, rate limit, outage): + +1. AFK retries with the primary model (controlled by retry policy) +2. If retries exhaust, it falls through to `gpt-4.1-mini` +3. If that also fails, it tries `gpt-4.1-nano` +4. If all models fail, the `llm_failure_policy` determines the outcome + +## Cost-optimized fallback + +Use expensive models only when needed: + +```python +from afk.agents import Agent, FailSafeConfig +from afk.core import Runner + +# Start cheap, escalate if quality is insufficient +simple_agent = Agent( + name="classifier", + model="gpt-4.1-nano", # Start with cheapest + instructions=""" + Classify the support ticket. Output exactly one label: + billing, technical, account, other. + """, + fail_safe=FailSafeConfig( + fallback_model_chain=["gpt-4.1-mini", "gpt-4.1"], + max_total_cost_usd=0.05, + ), +) + +# Complex tasks get the big model with fallbacks +analysis_agent = Agent( + name="analyst", + model="gpt-4.1", # Start with most capable + instructions=""" + Provide detailed technical analysis with code examples. + Be thorough and precise. + """, + fail_safe=FailSafeConfig( + fallback_model_chain=["gpt-4.1-mini"], + llm_failure_policy="retry_then_degrade", + max_total_cost_usd=2.00, + ), +) + +runner = Runner() + +# Simple task -> cheap model handles it +r1 = runner.run_sync(simple_agent, user_message="I can't log in") +print(f"Classification: {r1.final_text} (${r1.total_cost_usd or 0:.4f})") + +# Complex task -> powerful model with safety net +r2 = runner.run_sync(analysis_agent, user_message="Analyze Python's asyncio event loop") +print(f"Analysis: {r2.final_text[:100]}... (${r2.total_cost_usd or 0:.4f})") +``` + +## Circuit breaker integration + +AFK's built-in circuit breaker works with fallback chains. When a model triggers too many failures, the breaker opens and the system skips straight to the next fallback: + +```python +agent = Agent( + name="breaker-demo", + model="gpt-4.1", + instructions="...", + fail_safe=FailSafeConfig( + fallback_model_chain=["gpt-4.1-mini", "gpt-4.1-nano"], + + # Circuit breaker settings + breaker_failure_threshold=5, # Open after 5 consecutive failures + breaker_cooldown_s=30.0, # Wait 30s before retrying the model + + # Failure handling + llm_failure_policy="retry_then_degrade", + max_total_cost_usd=1.00, + ), +) +``` + +```mermaid +flowchart LR + A["gpt-4.1 fails 5x"] --> B["Circuit opens"] + B --> C["Skip to gpt-4.1-mini"] + C --> D["30s cooldown"] + D --> E["gpt-4.1 retried"] + E -->|"succeeds"| F["Circuit closes"] + E -->|"fails again"| B +``` + +## Multi-agent with different model tiers + +Use different model tiers for different specialists: + +```python +from afk.agents import Agent, FailSafeConfig + +# Cheap model for simple classification +router = Agent( + name="router", + model="gpt-4.1-nano", + instructions="Route to the correct specialist.", + fail_safe=FailSafeConfig(fallback_model_chain=["gpt-4.1-mini"]), + subagents=[ + # Powerful model for complex analysis + Agent( + name="analyst", + model="gpt-4.1", + instructions="Provide deep technical analysis.", + fail_safe=FailSafeConfig( + fallback_model_chain=["gpt-4.1-mini"], + max_total_cost_usd=1.00, + ), + ), + # Mid-tier model for summarization + Agent( + name="summarizer", + model="gpt-4.1-mini", + instructions="Summarize findings concisely.", + fail_safe=FailSafeConfig( + fallback_model_chain=["gpt-4.1-nano"], + max_total_cost_usd=0.25, + ), + ), + ], +) +``` + +## Inspecting which model was used + +After a run, check the result metadata and usage aggregate: + +```python +result = runner.run_sync(agent, user_message="Analyze this...") + +print(f"State: {result.state}") +print(f"Requested model: {result.requested_model}") +print(f"Normalized model: {result.normalized_model}") +print(f"Provider: {result.provider_adapter}") +print(f"Total tokens: {result.usage_aggregate.total_tokens}") +print(f"Total cost: ${result.total_cost_usd or 0:.4f}") +``` + +## Recommendations + +| Scenario | Primary Model | Fallback Chain | +| ------------------------ | -------------- | ------------------------------- | +| **Classification** | `gpt-4.1-nano` | `gpt-4.1-mini` | +| **General chat** | `gpt-4.1-mini` | `gpt-4.1-nano` | +| **Complex analysis** | `gpt-4.1` | `gpt-4.1-mini` → `gpt-4.1-nano` | +| **Code generation** | `gpt-4.1` | `gpt-4.1-mini` | +| **Cost-sensitive batch** | `gpt-4.1-nano` | _(none)_ | + +## What to read next + +- [Configuration Reference](/library/configuration-reference#failsafeconfig) — Full FailSafeConfig fields including circuit breaker settings. +- [Failure Policy Matrix](/library/failure-policy-matrix) — How failures flow through the system. +- [Snippet 11: Cost Monitoring](/library/snippets/11_cost_monitoring) — Track and control costs in real time. +``` + +## 14_production_client + +Source: `docs/library/snippets/14_production_client.mdx` + +```python +--- +title: "14: Client Timeouts and Redis Pooling" +description: Configure LLM client timeouts and Redis connection pooling. +--- + +## What this snippet demonstrates + +This snippet shows how to configure: +1. **Timeout middleware** to bound slow provider calls +2. **Redis connection pooling** for shared cache or memory connections +3. **Shutdown handling** so runners and Redis pools close cleanly + +## Timeout middleware + +Apply per-request timeouts to prevent runaway LLM calls: + +```python +import asyncio +from afk.llms import LLMBuilder, LLMRequest +from afk.llms.middleware import MiddlewareStack +from afk.llms.middleware.timeout import ( + TimeoutMiddleware, + EmbedTimeoutMiddleware, + StreamTimeoutMiddleware, + TimeoutConfig, +) + +config = TimeoutConfig( + default_timeout_s=30.0, + chat_timeout_s=60.0, + embed_timeout_s=15.0, + stream_timeout_s=45.0, +) + +stack = MiddlewareStack( + chat=[TimeoutMiddleware(config)], + embed=[EmbedTimeoutMiddleware(config)], + stream=[StreamTimeoutMiddleware(config)], +) + +production_client = ( + LLMBuilder() + .provider("openai") + .model("gpt-4.1-mini") + .profile("production") + .with_middlewares(stack) + .build() +) +``` + +### Per-request timeout override + +```python +from afk.llms import TimeoutPolicy + +req = LLMRequest( + model="gpt-4.1-mini", + messages=[...], + timeout_policy=TimeoutPolicy(request_timeout_s=120.0), # Override default +) + +response = await production_client.chat(req) +``` + +## Redis connection pooling + +For Redis deployments, use connection pooling instead of creating a new client per request: + +```python +from afk.llms.cache.redis_pool import ( + get_redis_pool, + PoolConfig, + close_all_pools, +) + +async def setup_redis_pool(): + pool = await get_redis_pool( + "redis://localhost:6379/0", + config=PoolConfig( + max_connections=50, + max_idle_connections=10, + socket_timeout=5.0, + socket_connect_timeout=5.0, + socket_keepalive=True, + health_check_interval_s=30.0, + ), + ) + + if await pool.health_check(): + print("Redis connection pool healthy") + + return pool +``` + +### Using with memory store + +```python +import asyncio +from afk.memory.adapters.redis import RedisMemoryStore +from afk.core import Runner + +async def main(): + pool = await get_redis_pool( + "redis://localhost:6379/0", + config=PoolConfig(max_connections=50), + ) + + runner = Runner( + memory_store=RedisMemoryStore(url="redis://localhost:6379/0"), + ) + + result = await runner.run(agent, user_message="Hello") + print(result.final_text) + + await runner.close() + await close_all_pools() + +asyncio.run(main()) +``` + +## Full example + +```python +import asyncio +from afk.llms import LLMBuilder +from afk.llms.middleware import MiddlewareStack +from afk.llms.middleware.timeout import ( + TimeoutMiddleware, + TimeoutConfig, +) +from afk.llms.cache.redis_pool import ( + get_redis_pool, + PoolConfig, + close_all_pools, +) +from afk.memory.adapters.redis import RedisMemoryStore +from afk.core import Runner +from afk.agents import Agent + +class ProductionSetup: + def __init__(self): + self.llm_client = None + self.runner = None + self.pool = None + + async def __aenter__(self): + pool_config = PoolConfig( + max_connections=50, + max_idle_connections=10, + socket_timeout=5.0, + socket_connect_timeout=5.0, + ) + self.pool = await get_redis_pool( + "redis://localhost:6379/0", + config=pool_config, + ) + + timeout_config = TimeoutConfig( + default_timeout_s=30.0, + chat_timeout_s=60.0, + ) + stack = MiddlewareStack( + chat=[TimeoutMiddleware(timeout_config)], + ) + + self.llm_client = ( + LLMBuilder() + .provider("openai") + .model("gpt-4.1-mini") + .profile("production") + .with_middlewares(stack) + .build() + ) + + self.runner = Runner( + memory_store=RedisMemoryStore(url="redis://localhost:6379/0"), + ) + + return self + + async def __aexit__(self, exc_type, exc_val, exc_tb): + if self.runner: + await self.runner.close() + await close_all_pools() + return False + + +async def main(): + agent = Agent( + name="assistant", + model="gpt-4.1-mini", + instructions="You are a helpful assistant.", + ) + + async with ProductionSetup() as setup: + result = await setup.runner.run( + agent, + user_message="Hello, world!", + ) + print(result.final_text) + +asyncio.run(main()) +``` + +## Configuration reference + +### TimeoutConfig + +| Parameter | Default | Description | +| --- | --- | --- | +| `default_timeout_s` | 30.0 | Default timeout for all operations | +| `chat_timeout_s` | None | Specific timeout for chat requests | +| `embed_timeout_s` | None | Specific timeout for embeddings | +| `stream_timeout_s` | None | Specific timeout for streaming | + +### PoolConfig + +| Parameter | Default | Description | +| --- | --- | --- | +| `max_connections` | 50 | Maximum total connections | +| `max_idle_connections` | 10 | Maximum idle connections | +| `socket_timeout` | 5.0 | Socket read/write timeout | +| `socket_connect_timeout` | 5.0 | Connection establishment timeout | +| `socket_keepalive` | False | Enable TCP keepalive | +| `health_check_interval_s` | 30.0 | Interval for health checks | + +## What to read next + +- [LLM Control & Session](/llms/control-and-session) -- Retry, caching, and circuit breaker policies +- [Deployment Guide](/library/deployment) -- Production deployment with Docker and Kubernetes +- [Performance Guide](/library/performance) -- Optimize latency and throughput +``` diff --git a/skills/afk-coder/references/cookbook-examples.md b/skills/afk-coder/references/cookbook-examples.md index 650cbe3..1bc0ade 100644 --- a/skills/afk-coder/references/cookbook-examples.md +++ b/skills/afk-coder/references/cookbook-examples.md @@ -14,10 +14,11 @@ All examples use **public imports only** and target Python 3.13+. The simplest possible AFK agent. Five lines of code. ```python -from afk.agents import Agent, Runner +from afk.agents import Agent +from afk.core import Runner agent = Agent( - model="gpt-4.1-mini", + model="gpt-5.2-mini", instructions="You are a helpful assistant.", ) @@ -39,7 +40,8 @@ Define and attach tools using the `@tool` decorator. ```python from pydantic import BaseModel -from afk.agents import Agent, Runner +from afk.agents import Agent +from afk.core import Runner from afk.tools import tool, ToolContext class WeatherArgs(BaseModel): @@ -58,7 +60,14 @@ class CalculateArgs(BaseModel): @tool(args_model=CalculateArgs) async def calculate(args: CalculateArgs): """Evaluate a math expression.""" - return {"result": eval(args.expression)} + left, op, right = args.expression.split() + operations = { + "+": lambda a, b: a + b, + "-": lambda a, b: a - b, + "*": lambda a, b: a * b, + "/": lambda a, b: a / b, + } + return {"result": operations[op](float(left), float(right))} agent = Agent( model="gpt-4.1-mini", @@ -79,16 +88,17 @@ print(f"Tools used: {[t.tool_name for t in result.tool_executions]}") Parent agent delegates to specialist subagents. ```python -from afk.agents import Agent, Runner +from afk.agents import Agent +from afk.core import Runner researcher = Agent( - model="gpt-4.1-mini", + model="gpt-5.2-mini", name="researcher", instructions="You research topics thoroughly and return findings.", ) writer = Agent( - model="gpt-4.1-mini", + model="gpt-5.2-mini", name="writer", instructions="You write polished articles from research notes.", ) @@ -122,11 +132,12 @@ Real-time token-by-token output with run events. ```python import asyncio -from afk.agents import Agent, Runner +from afk.agents import Agent +from afk.core import Runner async def main(): agent = Agent( - model="gpt-4.1-mini", + model="gpt-5.2-mini", instructions="You explain concepts clearly.", ) runner = Runner() @@ -156,10 +167,14 @@ asyncio.run(main()) Persist conversation history with SQLite. ```python -from afk.agents import Agent, Runner -from afk.memory import create_memory_store +from afk.agents import Agent +from afk.core import Runner +from afk.memory import create_memory_store_from_env -store = create_memory_store("sqlite", database_path="./agent_memory.db") +import os +os.environ["AFK_MEMORY_BACKEND"] = "sqlite" +os.environ["AFK_SQLITE_PATH"] = "./agent_memory.db" +store = create_memory_store_from_env() await store.setup() agent = Agent( @@ -197,8 +212,8 @@ print(result2.final_text) # "Your name is Alice and you love Python." Sandbox profiles, tool output limits, and fail-safe settings. ```python -from afk.agents import Agent, Runner, FailSafeConfig -from afk.core import RunnerConfig +from afk.agents import Agent, FailSafeConfig +from afk.core import Runner, RunnerConfig from afk.tools.security import SandboxProfile sandbox = SandboxProfile( @@ -246,7 +261,8 @@ result = runner.run_sync(agent, user_message="Analyze sales data") Custom LLM setup with builder pattern. ```python -from afk.agents import Agent, Runner +from afk.agents import Agent +from afk.core import Runner from afk.llms import LLMBuilder, LLMSettings # Simple -- just set model string @@ -286,9 +302,9 @@ See [llm-configuration.md](./llm-configuration.md) for profiles, routing, and en Interactive approval flow for sensitive operations. ```python -from afk.agents import Agent, Runner, PolicyRule, PolicyRuleCondition, PolicyEngine +from afk.agents import Agent, PolicyRule, PolicyRuleCondition, PolicyEngine from afk.agents.types import ApprovalDecision -from afk.core import InteractionProvider, RunnerConfig +from afk.core import InteractionProvider, Runner, RunnerConfig class TerminalApprovalProvider: """Simple terminal-based approval provider.""" @@ -461,7 +477,7 @@ server = MCPServer(agent=agent) server.run(host="0.0.0.0", port=8080) ``` -See the MCP docs at `docs/library/mcp.mdx` for full server configuration. +See the MCP docs at `docs/library/mcp-server.mdx` for full server configuration. --- @@ -473,7 +489,7 @@ See the MCP docs at `docs/library/mcp.mdx` for full server configuration. | Custom tools | `@tool`, `ToolContext`, `ToolResult` | [tools-system.md](./tools-system.md) | | Multi-agent | `Agent(subagents=[...])` | [multi-agent-and-delegation.md](./multi-agent-and-delegation.md) | | Streaming | `runner.run_stream()` | [streaming-and-interaction.md](./streaming-and-interaction.md) | -| Memory | `create_memory_store()` | [memory-and-state.md](./memory-and-state.md) | +| Memory | `create_memory_store_from_env()` | [memory-and-state.md](./memory-and-state.md) | | Safety | `SandboxProfile`, `RunnerConfig` | [security-and-policies.md](./security-and-policies.md) | | LLM config | `LLMBuilder`, `LLMSettings` | [llm-configuration.md](./llm-configuration.md) | | HITL | `InteractionProvider`, `PolicyEngine` | [streaming-and-interaction.md](./streaming-and-interaction.md) | diff --git a/skills/afk-coder/references/llm-configuration.md b/skills/afk-coder/references/llm-configuration.md index 8af2e5d..d22de32 100644 --- a/skills/afk-coder/references/llm-configuration.md +++ b/skills/afk-coder/references/llm-configuration.md @@ -236,7 +236,7 @@ a frozen dataclass imported from `afk.llms`. | Rate Limit | `RateLimitPolicy` | `requests_per_second`, `burst` | Token bucket per provider and operation | | Circuit Breaker | `CircuitBreakerPolicy` | `failure_threshold`, `cooldown_s`, `half_open_max_calls` | Consecutive failure detection with half-open probes | | Hedging | `HedgingPolicy` | `enabled`, `delay_s` | Speculative secondary call to reduce tail latency | -| Cache | `CachePolicy` | `enabled`, `ttl_s` | Response cache with configurable TTL | +| Cache | `CachePolicy` | `enabled`, `ttl_s`, `namespace` | Response cache with configurable TTL and optional tenant/user namespace | | Coalescing | `CoalescingPolicy` | `enabled` | In-flight request deduplication for identical payloads | Policies are set via profiles or individually through the builder: diff --git a/skills/afk-coder/references/queues.md b/skills/afk-coder/references/queues.md index 8446847..00c60b0 100644 --- a/skills/afk-coder/references/queues.md +++ b/skills/afk-coder/references/queues.md @@ -212,7 +212,11 @@ queue = RedisTaskQueue( ) ``` -Requires `redis` package: `pip install afk[redis]` +Requires the Redis client package alongside AFK: + +```bash +python -m pip install afk-py redis +``` --- @@ -284,4 +288,4 @@ await queue.ack(item.task_id, success=True) ## Cross-References - **Agents**: See [agents-and-runner.md](./agents-and-runner.md) for `Agent` configuration -- **Evals**: See [evals-and-testing.md](./evals-and-testing.md) for testing queued agents \ No newline at end of file +- **Evals**: See [evals-and-testing.md](./evals-and-testing.md) for testing queued agents diff --git a/skills/afk-coder/references/security-and-policies.md b/skills/afk-coder/references/security-and-policies.md index 2f4a2ec..0cba28a 100644 --- a/skills/afk-coder/references/security-and-policies.md +++ b/skills/afk-coder/references/security-and-policies.md @@ -212,8 +212,8 @@ profile = SandboxProfile( |-------|------|---------|-------------| | `profile_id` | `str` | `"default"` | Identifier for this profile | | `allow_network` | `bool` | `False` | Allow network-accessing tools and URL args | -| `allow_command_execution` | `bool` | `True` | Allow command execution tools | -| `allowed_command_prefixes` | `list[str]` | `[]` | Allowlisted command prefixes (empty = all allowed) | +| `allow_command_execution` | `bool` | `False` | Allow command execution tools | +| `allowed_command_prefixes` | `list[str]` | `[]` | Allowlisted command prefixes (empty = none) | | `deny_shell_operators` | `bool` | `True` | Block `&&`, `\|\|`, `;`, `\|`, backticks, `$(`, redirects | | `allowed_paths` | `list[str]` | `[]` | Allowed filesystem paths (empty = no restriction) | | `denied_paths` | `list[str]` | `[]` | Denied filesystem paths (checked before allowlist) | @@ -246,7 +246,7 @@ skill_policy = SkillToolPolicy( | Field | Type | Default | Description | |-------|------|---------|-------------| -| `command_allowlist` | `list[str]` | `[]` | Allowed command prefixes (empty = all allowed) | +| `command_allowlist` | `list[str]` | `[]` | Allowed command prefixes (empty = none) | | `deny_shell_operators` | `bool` | `True` | Block shell chaining operators | | `max_stdout_chars` | `int` | `20_000` | Maximum stdout characters retained | | `max_stderr_chars` | `int` | `20_000` | Maximum stderr characters retained | @@ -327,6 +327,7 @@ from afk.tools.security import SandboxProfile profile = SandboxProfile( allow_network=False, + allow_command_execution=True, allowed_command_prefixes=["ls", "cat"], allowed_paths=["/workspace"], denied_paths=["/etc", "/root", "/var"], diff --git a/skills/afk-coder/references/streaming-and-interaction.md b/skills/afk-coder/references/streaming-and-interaction.md index b787279..82d2656 100644 --- a/skills/afk-coder/references/streaming-and-interaction.md +++ b/skills/afk-coder/references/streaming-and-interaction.md @@ -13,9 +13,10 @@ `Runner.run_stream()` returns an `AgentStreamHandle` that yields real-time events. ```python -from afk.agents import Agent, Runner +from afk.agents import Agent +from afk.core import Runner -agent = Agent(model="gpt-4.1-mini", instructions="You are helpful.") +agent = Agent(model="gpt-5.2-mini", instructions="You are helpful.") runner = Runner() handle = await runner.run_stream(agent, user_message="Explain async/await") diff --git a/skills/afk-coder/references/tools-system.md b/skills/afk-coder/references/tools-system.md index 8489da4..416d427 100644 --- a/skills/afk-coder/references/tools-system.md +++ b/skills/afk-coder/references/tools-system.md @@ -66,14 +66,22 @@ class CalculateArgs(BaseModel): @tool(args_model=CalculateArgs) async def calculate(args: CalculateArgs): - return eval(args.expression) # simplified for brevity + left, op, right = args.expression.split() + operations = { + "+": lambda a, b: a + b, + "-": lambda a, b: a - b, + "*": lambda a, b: a * b, + "/": lambda a, b: a / b, + } + return operations[op](float(left), float(right)) ``` ```python # WRONG - raw dict, no model @tool() async def calculate(expression: str): - return eval(expression) + left, op, right = expression.split() + return {"+": lambda a, b: a + b}[op](float(left), float(right)) ``` --- diff --git a/skills/afk-coder/scripts/search_afk_docs.py b/skills/afk-coder/scripts/search_afk_docs.py old mode 100644 new mode 100755 index 73d6d89..75684e4 --- a/skills/afk-coder/scripts/search_afk_docs.py +++ b/skills/afk-coder/scripts/search_afk_docs.py @@ -1,133 +1,73 @@ #!/usr/bin/env python3 -""" -Search AFK coder skill reference docs. - -Usage: - python search_afk_docs.py "query" - python search_afk_docs.py "query" --format json - python search_afk_docs.py --help -""" - from __future__ import annotations import argparse import json -import re -import sys from pathlib import Path -REFERENCES_DIR = Path(__file__).resolve().parent.parent / "references" -SKILL_FILE = Path(__file__).resolve().parent.parent / "SKILL.md" -LLMS_TXT = Path(__file__).resolve().parent.parent / "llms.txt" - - -def find_matches( - query: str, files: list[Path], *, context_lines: int = 2 -) -> list[dict]: - """Search files for query, returning matches with context.""" - results: list[dict] = [] - pattern = re.compile(re.escape(query), re.IGNORECASE) - - for filepath in files: - if not filepath.exists(): - continue - try: - lines = filepath.read_text(encoding="utf-8").splitlines() - except Exception: - continue - - for i, line in enumerate(lines): - if pattern.search(line): - start = max(0, i - context_lines) - end = min(len(lines), i + context_lines + 1) - context = "\n".join(lines[start:end]) - results.append( - { - "file": str(filepath.relative_to(filepath.parent.parent)), - "line": i + 1, - "match": line.strip(), - "context": context, - } - ) - return results - - -def collect_files() -> list[Path]: - """Collect all searchable files.""" - files: list[Path] = [] - - if REFERENCES_DIR.exists(): - files.extend(sorted(REFERENCES_DIR.glob("*.md"))) - - if SKILL_FILE.exists(): - files.append(SKILL_FILE) - if LLMS_TXT.exists(): - files.append(LLMS_TXT) - return files +def load_records(path: Path) -> list[dict]: + rows = [] + with path.open("r", encoding="utf-8") as f: + for line in f: + line = line.strip() + if not line: + continue + rows.append(json.loads(line)) + return rows -def format_text(results: list[dict], query: str) -> str: - """Format results as human-readable text.""" - if not results: - return f"No matches found for '{query}'." +def score(query_terms: list[str], doc: dict) -> int: + text = (doc.get("title", "") + " " + doc.get("description", "") + " " + doc.get("content", "")).lower() + return sum(text.count(term) for term in query_terms) - parts: list[str] = [f"Found {len(results)} match(es) for '{query}':\n"] - for i, r in enumerate(results, 1): - parts.append(f"--- Match {i}: {r['file']}:{r['line']} ---") - parts.append(r["context"]) - parts.append("") - return "\n".join(parts) - -def main() -> None: - parser = argparse.ArgumentParser( - description="Search AFK coder skill reference documentation.", - epilog="Examples:\n" - ' python search_afk_docs.py "RunnerConfig"\n' - ' python search_afk_docs.py "@tool" --format json\n' - ' python search_afk_docs.py "MemoryStore" --context 5\n', - formatter_class=argparse.RawDescriptionHelpFormatter, - ) - parser.add_argument("query", nargs="?", help="Search query string") - parser.add_argument( - "--format", - choices=["text", "json"], - default="text", - help="Output format (default: text)", - ) - parser.add_argument( - "--context", - type=int, - default=2, - help="Number of context lines around each match (default: 2)", - ) +def main() -> int: + parser = argparse.ArgumentParser(description="Search bundled AFK docs index for this skill.") + parser.add_argument("query", help="Search query") + parser.add_argument("--top-k", type=int, default=8) parser.add_argument( - "--list-files", - action="store_true", - help="List all searchable files and exit", + "--index", + default=str(Path(__file__).resolve().parent.parent / "references" / "afk-docs" / "docs-index.jsonl"), + help="Path to docs-index.jsonl", ) - args = parser.parse_args() - if args.list_files: - files = collect_files() - for f in files: - print(f.relative_to(f.parent.parent)) - return - - if not args.query: - parser.print_help() - sys.exit(1) - - files = collect_files() - results = find_matches(args.query, files, context_lines=args.context) - - if args.format == "json": - print(json.dumps(results, indent=2)) - else: - print(format_text(results, args.query)) + query_terms = [t.lower() for t in args.query.split() if t.strip()] + if not query_terms: + print("Empty query.") + return 1 + + idx_path = Path(args.index) + if not idx_path.exists(): + print(f"Index not found: {idx_path}") + return 1 + + rows = load_records(idx_path) + ranked = sorted( + ((score(query_terms, r), r) for r in rows), + key=lambda x: x[0], + reverse=True, + ) + shown = 0 + for s, row in ranked: + if s <= 0: + continue + print(f"[{row['id']}] {row.get('title', '')}") + print(f" url: {row.get('url', '')}") + print(f" path: {row.get('path', '')}") + desc = row.get("description", "") + if desc: + print(f" desc: {desc}") + print() + shown += 1 + if shown >= args.top_k: + break + + if shown == 0: + print("No matches.") + return 0 if __name__ == "__main__": - main() + raise SystemExit(main()) diff --git a/skills/afk-maintainer/SKILL.md b/skills/afk-maintainer/SKILL.md index 0eb2c9f..3b27075 100644 --- a/skills/afk-maintainer/SKILL.md +++ b/skills/afk-maintainer/SKILL.md @@ -1,157 +1,92 @@ --- name: afk-maintainer -description: Governance skill for the AFK framework. Enforces coding principles, DX-first design, extensibility patterns, production safety, and library quality standards across every contribution. Use this skill when reviewing PRs, triaging issues,planning releases, or making architectural decisions for AFK. +description: Maintain and review the AFK Python SDK repository. Use when changing AFK internals, reviewing PRs, triaging issues, planning releases, updating docs/skills, or making architecture, public API, safety, dependency, testing, and compatibility decisions for AFK. --- # AFK Maintainer -You are a maintainer of **AFK (Agent Framework Kit)** — a contract-first Python -framework for building reliable, deterministic AI agent systems. - -This skill is the **top-level governance authority** for the repository. Every -contributor, reviewer, and agentic coding tool must comply with these standards. - -## When to use this skill - -- Reviewing pull requests or code changes to AFK -- Triaging issues and classifying severity -- Planning releases and writing changelogs -- Making architectural or design decisions -- Auditing code for safety, correctness, or DX quality - -## Reference files - -Load references on demand as the task requires: - -- **[Coding principles](references/coding-principles-and-patterns.md)** — The soul of AFK's design. Core patterns and anti-patterns. -- **[Operating rules](references/maintainer-operating-rules.md)** — PR standards, risk assessment, review protocol, red flags. -- **[Quality standards](references/repo-design-and-quality-standards.md)** — DX, docs, examples, extensibility, code style. -- **[Review checklist](references/code-review-checklist.md)** — Concrete per-PR-type checklists. -- **[Release playbook](references/release-and-triage-playbook.md)** — Issue triage, release hygiene, backport, emergency response. -- **[Dependency rules](references/dependency-and-compatibility-rules.md)** — Versioning, compatibility matrix, supply chain. -- **[Claude SDK playbook](references/claude-agent-sdk-playbook.md)** — Claude Agent SDK integration guidelines. -- **[LiteLLM playbook](references/litellm-playbook.md)** — LiteLLM transport/adapter guidelines. -- **[Examples](references/examples.md)** — Triage notes, release notes, PR comments, review decisions. - -Search bundled docs when needed: +Use this skill as the repository governance guide for AFK itself. It is for framework changes, not for building downstream applications. + +## First Steps + +1. Identify the affected subsystem: agents, core runner, tools, LLM runtime, memory, queues, observability, evals, MCP, A2A, docs, or skills. +2. Search the generated docs when the public behavior or docs contract matters: + + ```bash + python skills/afk-maintainer/scripts/search_afk_docs.py "public imports runner" + ``` + +3. Read the minimum reference files needed for the task. +4. Preserve public API boundaries and avoid unrelated churn. +5. Validate with targeted tests first; broaden only when the change crosses subsystem boundaries. + +## Reference Map + +| Need | Read | +| --- | --- | +| Architecture principles and anti-patterns | `references/coding-principles-and-patterns.md` | +| PR protocol, risk, red flags | `references/maintainer-operating-rules.md` | +| DX, docs, examples, extensibility | `references/repo-design-and-quality-standards.md` | +| Review checklists | `references/code-review-checklist.md` | +| Release, triage, backport, emergency flow | `references/release-and-triage-playbook.md` | +| Dependencies and compatibility | `references/dependency-and-compatibility-rules.md` | +| Claude Agent SDK integrations | `references/claude-agent-sdk-playbook.md` | +| LiteLLM integrations | `references/litellm-playbook.md` | +| Example review/triage/release text | `references/examples.md` | +| Current generated docs index | `references/afk-docs/docs-index.jsonl` | + +## Non-Negotiable Boundaries + +| Boundary | Rule | +| --- | --- | +| Agent | Configuration only: identity, model, instructions, tools, subagents, policies, skills | +| Runner | Execution only: step loop, tool dispatch, memory, streaming, checkpoints, policy/HITL | +| Runtime | Capabilities only: LLM adapters, tools, memory, queues, telemetry, MCP/A2A | +| Public docs | Public `afk.*` imports only | +| Provider code | Provider-specific behavior stays in adapter/provider modules | + +## Review Workflow + +1. Classify risk: low, medium, high. +2. Identify user-visible behavior and public API impact. +3. Check package-boundary violations and internal imports in examples. +4. Check failure semantics: timeout, retry, cancellation, degraded state, partial completion, persistence. +5. Check docs/examples/env vars when behavior is user-visible. +6. Require tests for the changed contract. +7. Regenerate agent-facing docs when docs, snippets, navigation, or skill metadata change: + + ```bash + ./scripts/build_agentic_ai_assets.sh + ``` + +## High-Risk Areas + +- `src/afk/core/runner/`: execution loop, checkpoints, resume, policy/failure routing. +- `src/afk/core/streaming.py`: stream events and handle lifecycle. +- `src/afk/tools/core/base.py` and `src/afk/tools/registry.py`: tool invocation semantics. +- `src/afk/tools/security.py`: sandbox, secret scope, output limiting. +- `src/afk/llms/runtime/`: retries, circuit breakers, rate limits, caching, routing. +- `src/afk/memory/`: persistence, checkpoint keys, compaction, vector search. +- `src/afk/queues/`: execution contracts, retry/DLQ, worker lifecycle. +- `src/afk/agents/a2a/`: auth, delivery guarantees, protocol compatibility. + +## Quality Bar + +- Public functions and classes have type annotations. +- Errors are actionable and classified where the runner needs policy decisions. +- Async code does not block the event loop. +- Defaults are safe and useful for the zero-config path. +- New configuration has docs and tests. +- Breaking changes include migration notes. +- Docs examples use `afk-py` for install and `afk` for imports. + +## Common Validation Commands ```bash -python scripts/search_afk_docs.py "query terms" -``` - -## AFK identity - -AFK is built on three pillars: - -| Pillar | Role | Key Principle | -|--------|------|---------------| -| **Agent** | Stateless identity + instructions + tools | Configuration object, never execution | -| **Runner** | Stateful execution engine with event loop | Deterministic step loop with checkpoints | -| **Runtime** | LLM I/O, tool registry, memory, telemetry | Provider-portable, fail-safe by default | - -These pillars are **not negotiable**. Every change must respect their boundaries. - -## Core design philosophy - -1. **Contract-first** — Every boundary has typed contracts (Pydantic models, Protocols, ABCs). No implicit assumptions across module boundaries. -2. **DX-first** — Sensible defaults, minimal boilerplate, progressive disclosure of complexity. A junior engineer should get a working agent in 5 lines. -3. **Deterministic by default** — The runner step loop is predictable. Same inputs produce same execution paths. Side effects are explicit and contained. -4. **Fail-safe by default** — Cost limits, step limits, tool timeouts, output sanitization, and circuit breakers are always on. Safety is opt-out, never opt-in. -5. **Provider-portable** — Zero provider lock-in. LLM adapters normalize everything to AFK types. Switching from OpenAI to Anthropic is a one-line change. -6. **Composition over inheritance** — Behavior wiring uses middleware, hooks, policies, and registries. Deep class hierarchies are forbidden. -7. **Extensible without forking** — Every major system (tools, memory, LLM, telemetry, queues) has a pluggable interface. Users extend via protocols, not patches. - -## Mandatory guardrails - -### 1. Behavior safety first -- Never weaken fail-safe defaults (cost limits, step limits, timeouts, sanitization) without maintainer-approved rationale and migration notes. -- Changes touching runner/tool/memory lifecycle must include failure-mode review: what happens on timeout, crash, partial completion. -- Circuit breakers, retry policies, and budget enforcement must be tested for happy path and edge cases. - -### 2. Public API discipline -- All user-facing imports flow through `__init__.py` re-exports. Internal modules never imported directly by users. -- Prefer backward-compatible changes. Breaking behavior requires: migration notes, deprecation warnings, updated docs. -- Every public function/class must have clear type annotations. - -### 3. DX quality gates -- Every error must have an actionable message. "Something went wrong" is never acceptable. -- Configuration objects must have sensible defaults. The zero-config path must work. -- Require `ruff` lint passing, test suite green, and PR-template checks before merge. - -### 4. Extensibility enforcement -- Every major subsystem must have a pluggable interface (Protocol or ABC). -- Reject PRs that add provider-specific logic outside adapter modules. -- Reject PRs that introduce hidden shared mutable state between components. - -### 5. Provider integration discipline -- Provider-specific code lives exclusively in adapter modules. -- AFK types (`LLMRequest`, `LLMResponse`, `ToolCall`, `ToolResult`) are the lingua franca. - -## Decision workflow - -For every issue or PR: - -``` -1. TRIAGE — Classify risk (low/medium/high) and affected subsystem(s) -2. SCOPE — Identify: runner, tools, memory, queues, LLM, A2A, MCP, docs, skills -3. REPRODUCE — Require minimal repro for bugs, acceptance criteria for features -4. PLAN — Apply smallest safe fix; avoid unrelated churn -5. IMPLEMENT — Follow coding-principles-and-patterns.md strictly -6. VALIDATE — Targeted tests first, then full suite if cross-cutting -7. DOCUMENT — Changelog entry, updated docs/examples, migration notes if needed -8. SHIP — Ensure all quality gates pass before merge -``` - -### Risk classification - -| Risk | Examples | Requirements | -|------|----------|--------------| -| **Low** | Docs typo, example fix, test improvement | Standard review | -| **Medium** | New tool, new memory adapter, config change | Tests + docs + review | -| **High** | Runner lifecycle, event-loop semantics, public API change | RFC + staged rollout + multi-reviewer | - -### High-risk subsystems - -These areas require extra scrutiny: - -- `src/afk/core/runner/` — Execution loop, checkpointing, budget enforcement -- `src/afk/core/streaming.py` — Stream bridge, event emission, handle lifecycle -- `src/afk/tools/core/base.py` — Tool calling pipeline, hooks, middleware chain -- `src/afk/llms/runtime/` — Circuit breakers, retry logic, fallback chains -- `src/afk/memory/` — Store lifecycle, concurrent access, data integrity -- `src/afk/agents/a2a/` — Authentication, protocol correctness, state management - -## Architecture quick reference - -``` -src/afk/ - agents/ # Agent definition, A2A, lifecycle, policies, skills - core/ # BaseAgent, ChatAgent - a2a/ # Agent-to-Agent communication - lifecycle/ # Runtime health, versioning/migration - policy/ # PolicyEngine (deterministic rule evaluation) - security/ # Input/output sanitization - core/ # Execution engine - runner/ # Runner API, execution loop, internals, checkpointing - runtime/ # Delegation dispatcher, retry engine - streaming.py # AgentStreamHandle, stream events - tools/ # Tool system - core/ # Tool, ToolSpec, @tool decorator, hooks, middleware - prebuilts/ # Built-in runtime tools (filesystem, shell, skills) - registry.py # ToolRegistry with concurrency, middleware, policy - security.py # SandboxProfile enforcement - llms/ # LLM abstraction layer - clients/ # Provider adapters (OpenAI, Anthropic, LiteLLM) - runtime/ # LLMClient with circuit breakers, retry, fallback - cache/ # Response caching (in-memory, Redis) - memory/ # Persistent state - adapters/ # In-memory, SQLite, Postgres, Redis backends - lifecycle.py # Retention policies, compaction - vector.py # Cosine similarity for vector search - queues/ # Task queue system - mcp/ # Model Context Protocol server/client - observability/ # Telemetry pipeline (collectors, projectors, exporters) - debugger/ # Debug instrumentation - evals/ # Evaluation suite - messaging/ # Internal messaging contracts +PYTHONPATH=src pytest -q +PYTHONPATH=src pytest -q tests/agents/test_agent_runtime.py +PYTHONPATH=src pytest -q tests/llms/test_llm_settings.py +PYTHONPATH=src pytest -q tests/queues/test_queue_factory.py +ruff check src tests +./scripts/build_agentic_ai_assets.sh ``` diff --git a/skills/afk-maintainer/agents/openai.yaml b/skills/afk-maintainer/agents/openai.yaml new file mode 100644 index 0000000..a2e5aa2 --- /dev/null +++ b/skills/afk-maintainer/agents/openai.yaml @@ -0,0 +1,4 @@ +interface: + display_name: "AFK Maintainer" + short_description: "Review and maintain AFK." + default_prompt: "Use $afk-maintainer to review or change AFK while preserving public contracts and safety rules." diff --git a/skills/afk-maintainer/references/README.md b/skills/afk-maintainer/references/README.md index 63db399..bc6b3fa 100644 --- a/skills/afk-maintainer/references/README.md +++ b/skills/afk-maintainer/references/README.md @@ -1,53 +1,21 @@ -# Maintainer Skill References +# Bundled AFK Docs Index -Reference documents for the AFK maintainer skill. These are loaded on demand -when the agent needs detailed guidance on a specific topic. - ---- - -## Reference Documents - -| # | File | Purpose | -|---|------|---------| -| 1 | `coding-principles-and-patterns.md` | Core coding philosophy, design patterns, anti-patterns | -| 2 | `maintainer-operating-rules.md` | PR standards, review protocol, red flags | -| 3 | `repo-design-and-quality-standards.md` | DX, docs, examples, extensibility, code style | -| 4 | `code-review-checklist.md` | Concrete per-PR-type checklists | -| 5 | `release-and-triage-playbook.md` | Issue triage, release hygiene, backport, emergency response | -| 6 | `dependency-and-compatibility-rules.md` | Versioning, compatibility matrix, supply chain security | -| 7 | `claude-agent-sdk-playbook.md` | Claude Agent SDK integration guidelines | -| 8 | `litellm-playbook.md` | LiteLLM transport/adapter guidelines | -| 9 | `examples.md` | Concrete examples of triage notes, PR comments, release notes | - ---- - -## Bundled Docs Search - -If the bundled docs index is present, search it: +This folder is generated by: ```bash -python scripts/search_afk_docs.py "event loop run_sync" -python scripts/search_afk_docs.py --format json "tool middleware" -python scripts/search_afk_docs.py --top-k 5 "memory compaction" +./scripts/build_agentic_ai_assets.sh ``` -### Index Files - -| File | Content | -|------|---------| -| `afk-docs/docs-index.jsonl` | Line-delimited JSON, one doc per line | -| `afk-docs/inverted-index.json` | Term-to-document mapping for search | -| `afk-docs/id-to-path.json` | Document ID to file path mapping | -| `afk-docs/path-to-id.json` | File path to document ID mapping | -| `afk-docs/manifest.json` | Index metadata (doc count, generation time) | +It keeps machine-readable AFK docs inside this skill package for agent runtimes that expect docs to live with `SKILL.md`. ---- +Files: +- `afk-docs/docs-index.jsonl`: searchable doc records +- `afk-docs/inverted-index.json`: token -> doc id map +- `afk-docs/id-to-path.json`: doc id -> docs path +- `afk-examples.md`: merged runnable examples from snippets -## Quick Reference Links +Quick search: -- Documentation: https://afk.arpan.sh -- API Reference: https://afk.arpan.sh/library/api-reference -- Developer Guide: https://afk.arpan.sh/library/developer-guide -- Configuration Reference: https://afk.arpan.sh/library/configuration-reference -- Security Model: https://afk.arpan.sh/library/security-model -- Tested Behaviors: https://afk.arpan.sh/library/tested-behaviors +```bash +python scripts/search_afk_docs.py "system prompts" +``` diff --git a/skills/afk-maintainer/references/afk-docs/docs-index.json b/skills/afk-maintainer/references/afk-docs/docs-index.json new file mode 100644 index 0000000..70ff6f9 --- /dev/null +++ b/skills/afk-maintainer/references/afk-docs/docs-index.json @@ -0,0 +1,1421 @@ +{ + "generated_at_utc": "2026-05-31T13:25:20.446492+00:00", + "docs_root": "/Users/arpanbhandari/Code/afk-py/docs", + "document_count": 63, + "documents": [ + { + "id": "doc_3aa0b3792d22", + "path": "docs/docs.json", + "url": "/docs.json", + "title": "docs", + "description": "", + "headings": [], + "content_sha256": "9808c9300f5fc80e270e826e601d4e2ec57dbe59c97606f751af389bc1cd54d5", + "content": "{ \"$schema\": \"https://mintlify.com/schema.json\", \"name\": \"AFK\", \"logo\": { \"light\": \"/logo-light.svg\", \"dark\": \"/logo-dark.svg\", \"href\": \"https://github.com/arpan404/afk\" }, \"theme\": \"mint\", \"layout\": \"sidenav\", \"favicon\": \"/favicon.svg\", \"topbar\": { \"style\": \"gradient\" }, \"topbarCtaButton\": { \"type\": \"github\", \"url\": \"https://github.com/arpan404/afk\" }, \"topbarLinks\": [ { \"type\": \"link\", \"name\": \"Quickstart\", \"url\": \"/library/quickstart\", \"arrow\": false }, { \"type\": \"link\", \"name\": \"Examples\", \"url\": \"/library/examples/\", \"arrow\": false }, { \"type\": \"link\", \"name\": \"API\", \"url\": \"/library/api-reference\", \"arrow\": false }, { \"type\": \"link\", \"name\": \"Skills\", \"url\": \"/library/agent-skills\", \"arrow\": false } ], \"sidebar\": { \"items\": \"undecorated\" }, \"rounded\": \"default\", \"colors\": { \"primary\": \"#0B6E4F\", \"light\": \"#2FB67E\", \"dark\": \"#084C38\" }, \"footer\": { \"socials\": { \"github\": \"https://github.com/arpan404/afk\" } }, \"navigation\": { \"tabs\": [ { \"tab\": \"Build with AFK\", \"groups\": [ { \"group\": \"Start Here\", \"pages\": [ \"index\", \"library/overview\", \"library/mental-model\", \"library/quickstart\", \"library/learn-in-15-minutes\", \"library/how-to-use-afk\" ] }, { \"group\": \"Core Building Blocks\", \"pages\": [ \"library/agents\", \"library/core-runner\", \"library/tools\", \"library/streaming\", \"library/memory\", \"library/system-prompts\", \"library/agent-skills\" ] }, { \"group\": \"LLM Runtime\", \"pages\": [ \"llms/index\", \"llms/contracts\", \"llms/adapters\", \"llms/control-and-session\", \"llms/agent-integration\" ] }, { \"group\": \"Production\", \"pages\": [ \"library/building-with-ai\", \"library/evals\", \"library/observability\", \"library/security-model\", \"library/task-queues\", \"library/deployment\", \"library/performance\", \"library/troubleshooting\" ] }, { \"group\": \"Integrations\", \"pages\": [ \"library/mcp-server\", \"library/a2a\", \"library/messaging\" ] } ] }, { \"tab\": \"Maintain AFK\", \"groups\": [ { \"group\": \"Contributor Guide\", \"pages\": [ \"library/developer-guide\", \"library/architecture\", \"library/public-imports-and-function-improvement\", \"library/tested-behaviors\" ] }, { \"group\": \"Internal Contracts\", \"pages\": [ \"library/agentic-behavior\", \"library/agentic-levels\", \"library/failure-policy-matrix\", \"library/tool-call-lifecycle\", \"library/tools-system-walkthrough\", \"library/run-event-contract\", \"library/checkpoint-schema\", \"library/llm-interaction\", \"library/debugger\" ] }, { \"group\": \"Migration\", \"pages\": [\"library/migration\"] } ] }, { \"tab\": \"Reference\", \"groups\": [ { \"group\": \"API and Configuration\", \"pages\": [ \"library/api-reference\", \"library/configuration-reference\", \"library/environment-variables\", \"library/full-module-reference\" ] } ] }, { \"tab\": \"Examples\", \"groups\": [ { \"group\": \"Runnable Snippets\", \"pages\": [ \"library/examples/index\", \"library/snippets/01_minimal_chat_agent\", \"library/snippets/02_policy_with_hitl\", \"library/snippets/03_subagents_with_router\", \"library/snippets/04_resume_and_compact\", \"library/snippets/05_direct_llm_structured_output\", \"library/snippets/06_tool_registry_security\", \"library/snippets/07_tool_hooks_and_middleware\", \"library/snippets/08_prebuilt_runtime_tools\", \"library/snippets/09_system_prompt_loader\", \"library/snippets/10_streaming_chat_with_memory\", \"library/snippets/11_cost_monitoring\", \"library/snippets/12_mcp_client_integration\", \"library/snippets/13_multi_model_fallback\", \"library/snippets/14_production_client\" ] } ] } ] } }", + "token_count": 289 + }, + { + "id": "doc_deb460ffa5ee", + "path": "docs/index.mdx", + "url": "/", + "title": "AFK - Agent Forge Kit", + "description": "Build reliable Python agents with typed tools, runtime controls, and production observability.", + "headings": [ + "Choose your path", + "Install", + "First agent", + "Core capabilities", + "AFK skills", + "How AFK fits together", + "Next steps" + ], + "content_sha256": "e7a820d1c12c3306b696c759c97965bfc83dd0ae9847e39832b1caa2dd31a7b9", + "content": "AFK is a Python 3.13+ SDK for building AI agents that need to run reliably outside a demo. You define agents, tools, policies, and memory as typed Python contracts. The runner handles the agent loop, LLM calls, tool execution, streaming, checkpoints, telemetry, and safety limits. Choose your path Start here if you are building an application, workflow, chatbot, coding tool, or production agent on top of AFK. Start here if you are changing AFK itself, reviewing internals, or updating public contracts. Install The distribution package is afk-py; the import package is afk. Set an LLM provider key before running examples: First agent What matters: - Agent is configuration: model, instructions, tools, policies, subagents, and defaults. - Runner is execution: LLM calls, tool calls, memory, streaming, checkpoints, and telemetry. - AgentResult is the run record: final text, terminal state, tool/subagent records, usage, and cost. Core capabilities Declarative agent definitions with instructions, tools, subagents, skills, MCP servers, and safety limits. Sync, async, and streaming execution with lifecycle control and thread continuity. Typed Python tools with Pydantic validation, hooks, middleware, sandboxing, and bounded output. Provider-portable LLM clients with retries, timeouts, rate limits, caching, circuit breakers, and fallback chains. Thread state, checkpoints, retention, compaction, and persistent stores. Evals, observability, queues, security controls, deployment guidance, and troubleshooting. AFK skills Install the AFK skills with Vercel's Skills CLI when you want Codex or another supported agent to use AFK-specific guidance: Use afk-coder when building applications with AFK. Use afk-maintainer when reviewing or changing AFK itself. See Agent Skills for details. How AFK fits together Next steps Build one runnable agent with one typed tool. Walk through agents, tools, streaming, memory, and safety. Find complete snippets by scenario. Check canonical imports, signatures, result fields, and stability rules.", + "token_count": 228 + }, + { + "id": "doc_1c5f37ea0ad3", + "path": "docs/library/a2a.mdx", + "url": "/library/a2a", + "title": "Agent-to-Agent (A2A)", + "description": "Cross-system agent communication with authentication and delivery guarantees.", + "headings": [ + "Architecture", + "Three integration layers", + "Request flow", + "Invocation contracts", + "Hosting an A2A service", + "Define the agent", + "Create auth provider", + "Start the server", + "Authentication providers", + "Google A2A adapter", + "Security considerations", + "Next steps" + ], + "content_sha256": "654fb4bacfee24f10a177606048474a5012fa7cb25ee7df203b1869ede8447cd", + "content": "The A2A protocol enables agent communication **across system boundaries** \u2014 between services, organizations, or deployment environments. It builds on internal messaging by adding authentication, authorization, and external transport. Architecture Three integration layers | Layer | What it does | When you need it | | --------------------- | --------------------------------- | ------------------------------------- | | **Internal Protocol** | Typed envelopes with idempotency | Always (agents in the same system) | | **Auth Provider** | Token validation, caller identity | When agents are in different services | | **External Adapter** | HTTP/gRPC transport, discovery | When agents are in different systems | Request flow The server validates the auth token, extracts the caller identity, and checks authorization rules. The target agent executes locally with a Runner, using the invocation request as input. Invocation contracts Hosting an A2A service Expose your agents as an A2A-accessible service: Authentication providers AFK ships with three auth providers: Permits all requests without authentication. **Never use in production.** Validates requests against pre-shared API keys with HMAC-SHA256 hashing. Validates JWT tokens with configurable issuer and audience claims. Google A2A adapter For interoperability with Google's A2A protocol, use the Google adapter: The adapter wraps a configured Google A2A SDK client behind AFK's AgentCommunicationProtocol. Security considerations | Concern | Mechanism | | -------------------- | ---------------------------------------------------------------- | | **Authentication** | Token-based (API keys, JWT, OAuth) | | **Authorization** | Per-agent access control (which callers can invoke which agents) | | **Idempotency** | idempotency_key prevents duplicate processing on retries | | **Rate limiting** | Configure per-caller request limits | | **Input validation** | All requests validated against AgentInvocationRequest schema | | **Cost isolation** | Each invocation has its own FailSafeConfig budget | **Always authenticate A2A endpoints.** An unauthenticated A2A server allows anyone to invoke your agents, consuming your LLM API credits. Next steps Async job processing for long-running work. Expose tools via the Model Context Protocol.", + "token_count": 216 + }, + { + "id": "doc_b6088784caab", + "path": "docs/library/agent-skills.mdx", + "url": "/library/agent-skills", + "title": "Agent Skills", + "description": "Reusable knowledge bundles that agents load on demand.", + "headings": [ + "When to use skills vs tools", + "Creating a skill", + "SKILL.md format", + "Deployment Guide", + "Prerequisites", + "Deploy Steps", + "Rollback", + "Monitoring", + "Installing repository skills", + "Registering skills on an agent", + "Built-in skill tools", + "How the agent uses skills", + "Security controls", + "Design guidelines", + "Next steps" + ], + "content_sha256": "69777dc325761bdda7d06e8dee698faab76ddd3ee55f7301e7c9143018f6aa8f", + "content": "**Skills** are reusable knowledge bundles \u2014 directories of instructions, scripts, and resources that extend an agent's capabilities without being hard-coded into the system prompt. Agents discover and read skills at runtime using built-in skill tools. When to use skills vs tools | Feature | Tools | Skills | | ---------------- | --------------------------------------------------- | ---------------------------------------- | | **What it is** | A Python function the agent calls | A directory of knowledge the agent reads | | **When it runs** | During the agent loop (function execution) | During the agent loop (file reads) | | **Best for** | Taking actions (API calls, calculations, mutations) | How-to guides, playbooks, reference docs | | **Schema** | Typed Pydantic model | Unstructured markdown + files | | **Side effects** | Yes (executes code) | Read-only (by default) | **Rule of thumb:** If the agent needs to *do something*, use a tool. If it needs to *know something*, use a skill. Creating a skill A skill is a directory with a SKILL.md file: SKILL.md format The YAML frontmatter (name, description) helps the agent decide which skill is relevant. The markdown body is the knowledge content. Installing repository skills This repository includes two skills for agentic development: - afk-coder for building applications with AFK. - afk-maintainer for reviewing or changing AFK itself. Install them with Vercel's Skills CLI: Install one skill at a time: For another repository, use the same shape: The Skills CLI installs the selected skill into the configured agent environment. See skills.sh for CLI details and supported agents. Registering skills on an agent AFK scans the skills_dir and registers each subdirectory as an available skill. The agent can discover and read skills using built-in tools. Built-in skill tools When an agent has skills, AFK automatically provides these tools: | Tool | Purpose | Returns | | ------------------- | ---------------------------------------- | ----------------------------------------- | | list_skills | List all available skills | [{\"name\": \"...\", \"description\": \"...\"}] | | read_skill_md | Read a skill's SKILL.md | Markdown content | | read_skill_file | Read any file in a skill directory | File content | | run_skill_command | Execute a command from a skill's scripts | Command output | How the agent uses skills The agent autonomously decides which skills to read based on the user's question. You don't need to explicitly tell it which skill to use. Security controls By default, read_skill_file only allows reading files within the skill directory. Path traversal (../) is blocked. run_skill_command is gated by policy. You can allowlist specific commands: To disable command execution entirely, only register the read-only skill tools: Design guidelines - **One skill per topic.** Keep skills focused (e.g., deployment, monitoring, database) rather than creating one giant knowledge base. - **Write SKILL.md for the agent, not a human.** The agent reads this file, so be explicit about what to do in each scenario. - **Include examples.** Show the agent what good output looks like. - **Version your skills.** Store skills in git alongside your agent code. - **Gate commands.** Always use policy rules for run_skill_command. Next steps Define typed functions for taking actions. Policy gates for tools and skill commands.", + "token_count": 347 + }, + { + "id": "doc_f12bbbc027a7", + "path": "docs/library/agentic-behavior.mdx", + "url": "/library/agentic-behavior", + "title": "Agentic Behavior", + "description": "Orchestrate multi-agent DAGs with fan-out, join policies, and backpressure.", + "headings": [ + "Quick example", + "Specialist agents", + "Coordinator", + "Delegation DAG model", + "Orchestration pipeline", + "Join policies", + "Failure handling", + "Backpressure", + "When to use multi-agent delegation", + "Next steps" + ], + "content_sha256": "01c6fa33586359217ba0e6ce21454895f25b3d20c57a0b496abe2ca8423d3e25", + "content": "When a single agent isn't enough, AFK lets you orchestrate teams of specialist agents using **delegation DAGs** \u2014 directed acyclic graphs where a coordinator fans out work, collects results, and combines them. Quick example Delegation DAG model The coordinator makes all delegation decisions. Subagents don't talk to each other directly \u2014 they report back to the coordinator, which decides what to do next. Orchestration pipeline The coordinator decides which subagents to call and in what order, based on the user's request and its instructions. AFK validates the delegation request: does the subagent exist? Are the arguments valid? Does the policy allow it? The subagent is enqueued for execution. With fan-out, multiple subagents can run in parallel. Each subagent runs a full agent loop (LLM calls, tool execution, etc.) and returns an AgentResult. Results are collected according to the join policy. The coordinator receives them and decides whether to delegate more or produce a final response. Join policies When multiple subagents run in parallel (fan-out), the **join policy** controls how the coordinator handles results: All subagents must succeed. Any failure fails the entire delegation batch. **Use when:** Every subagent's output is essential for the final result. Failures are tolerated. The coordinator proceeds with whatever results are available. **Use when:** Some subagents provide nice-to-have additions (e.g., fact-checking, sentiment analysis). Return as soon as one subagent succeeds. Cancel the rest. **Use when:** Multiple agents attempt the same task with different strategies. First correct answer wins. Succeed when N out of M subagents complete successfully. **Use when:** You need consensus from a majority of agents. Failure handling | Failure | all_required | allow_optional_failures | first_success | quorum | | ------------------ | ------------------------------------------------------------------ | ------------------------------------------------------------------------------------------- | ------------------------------------------------------------------ | ------------------------------------------------------------------ | | One subagent fails | Batch fails | Continue with others | Continue waiting | Continue if quorum not needed | | All subagents fail | Batch fails | Batch fails | Batch fails | Batch fails | | Timeout | Batch fails | Use available results | Batch fails | Depends on completed count | Backpressure AFK limits concurrent subagent executions to prevent resource exhaustion: When the concurrency limit is reached, additional subagent calls are queued and execute as slots become available. When to use multi-agent delegation | Scenario | Single agent | Multi-agent | | --------------------------------- | ------------------------------------------------------------------------ | -------------------------------------------------------------------------------------- | | Simple Q&A or classification | | Overkill | | Task needs different expertise | Consider | | | Need to parallelize work | N/A | | | Task needs consensus/verification | N/A | | | Tight latency budget | (fewer LLM calls) | (more LLM calls) | Next steps Capability maturity model \u2014 when to add agents. How orchestration and execution are separated.", + "token_count": 303 + }, + { + "id": "doc_57e2139f0873", + "path": "docs/library/agentic-levels.mdx", + "url": "/library/agentic-levels", + "title": "Agentic Levels", + "description": "A capability maturity model \u2014 know when to add complexity.", + "headings": [ + "The five levels", + "Capability comparison", + "Decision guide", + "Signals to level up", + "Next steps" + ], + "content_sha256": "05994fd901c3045dec62b4bac7a8f98bcb6c12d58749ed51680689589ed90bba", + "content": "AFK applications progress through five levels of capability. Each level adds features and complexity. Start at Level 1 and move up only when you have clear signals that the current level isn't enough. The five levels One agent, one model, no tools. The simplest possible setup. **AFK features used:** Agent, Runner, run_sync **Good for:** Text classification, summarization, translation, simple Q&A **Move up when:** The agent needs to take actions or access external data Add typed tool functions for the agent to call. **AFK features added:** @tool, Pydantic models, FailSafeConfig **Good for:** RAG, data lookup, calculations, API integrations **Move up when:** Different parts of the task need different expertise or models Coordinator delegates to specialist subagents. **AFK features added:** subagents, join policies, backpressure **Good for:** Complex tasks, parallel work, specialist expertise, consensus **Move up when:** Tasks take minutes, need async processing, or need queue-based reliability Decouple producers and consumers with task queues. Long-running jobs execute asynchronously. **AFK features added:** TaskQueue, TaskItem, workers, dead-letter handling **Good for:** Batch processing, background jobs, retryable pipelines, high-throughput **Move up when:** You need cross-system communication or external agent interop Agents communicate across systems using the A2A protocol with authenticated endpoints. **AFK features added:** InternalA2AProtocol, A2AServiceHost, auth providers, external adapters **Good for:** Microservice agent meshes, third-party integrations, federated AI systems **This is the ceiling** \u2014 most applications don't need Level 5. Capability comparison | Capability | L1 | L2 | L3 | L4 | L5 | | -------------------------- | ------------------------------------------------------ | ------------------------------------------------------ | ------------------------------------------------------ | ------------------------------------------------------ | ------------------------------------------------------ | | Text generation | | | | | | | Tool calling | \u2014 | | | | | | Multi-agent delegation | \u2014 | \u2014 | | | | | Async processing | \u2014 | \u2014 | \u2014 | | | | Cross-system communication | \u2014 | \u2014 | \u2014 | \u2014 | | | Policy engine | | | | | | | Observability | | | | | | | Evals | | | | | | = essential at this level    = recommended    \u2014 = not applicable Decision guide > [!TIP] > **Start at Level 1.** The simplest system that works is the best system. Premature complexity is the most common mistake in agent design. Signals to level up | Signal | Current Level | Move to | | ----------------------------------------------- | ------------- | -------------------------- | | Agent needs external data | 1 | 2 \u2014 Add tools | | One prompt can't cover all expertise | 2 | 3 \u2014 Split into specialists | | Users waiting too long for results | 3 | 4 \u2014 Queue for async | | Tasks fail and need to be retried automatically | 3 | 4 \u2014 Queue with DLQ | | Other systems need to invoke your agents | 4 | 5 \u2014 Expose A2A | | Third-party agents need to use your tools | 4 | 5 \u2014 Use MCP server | Next steps DAG orchestration, join policies, and fan-out. How AFK separates orchestration from execution.", + "token_count": 296 + }, + { + "id": "doc_e67e762fb6ab", + "path": "docs/library/agents.mdx", + "url": "/library/agents", + "title": "Agents", + "description": "Define agents with instructions, tools, and subagents.", + "headings": [ + "Your first agent", + "Agent fields reference", + "Single agent vs multi-agent", + "How subagent delegation works", + "Adding safety limits", + "Policy-aware agents", + "Design guidelines", + "Next steps" + ], + "content_sha256": "41b56ac8a4f6f74fd32265ec3b5c0ad682960f13a814fb9faf3517c23c943143", + "content": "An **Agent** is a configuration object that describes _what_ your AI agent is \u2014 its identity, capabilities, and boundaries. Agents don't execute themselves; they're run by a Runner. Your first agent Those are the fields most examples should set. model is the only required constructor argument, but name and instructions make traces and behavior easier to understand. Agent fields reference | Field | Type | Default | Purpose | | ------------------ | -------------------- | -------- | --------------------------------------------------------------- | | model | str or LLM | required | LLM model name or pre-built client instance | | name | str | None | Agent identity for logs, telemetry, and subagent routing | | instructions | str | None | System prompt \u2014 what the agent knows and how it behaves | | instruction_file | str or Path | None | Path to a .txt or .md file containing the system prompt | | prompts_dir | str or Path | None | Directory containing prompt files resolved by the prompt store | | tools | list[tool] | None | Typed functions the agent can call | | subagents | list[Agent] | None | Specialist agents this agent can delegate to | | skills | list[str] | None | Skill names to resolve from skills_dir | | skills_dir | str or Path | \".agents/skills\" | Directory containing skill packs | | mcp_servers | list[MCPServerLike]| None | MCP server configs for external tool discovery | | fail_safe | FailSafeConfig | defaults | Step limits, cost budgets, timeout, and failure policies | | context_defaults | dict | None | Default JSON context merged into each run before caller context | | inherit_context_keys | list[str] | None | Context keys inherited from parent agent in delegation | | model_resolver | callable | None | Custom function to resolve model names to LLM clients | | instruction_roles | list[InstructionRole] | None | Structured instruction sections with role-based ordering | | policy_roles | list[PolicyRole] | None | Role-based policy rules applied during execution | | policy_engine | PolicyEngine | None | Policy engine for tool/action gating | | subagent_router | SubagentRouter | None | Custom routing logic for subagent delegation | | max_steps | int | 20 | Maximum agent loop iterations | | tool_parallelism | int | None | Max concurrent tool executions per step | | subagent_parallelism_mode | str | \"configurable\" | How subagent concurrency is managed | | reasoning_enabled| bool | None | Enable provider-supported reasoning controls | | reasoning_effort | str | None | Thinking effort level (e.g. \"low\", \"medium\", \"high\") | | reasoning_max_tokens | int | None | Token budget for extended thinking | | skill_tool_policy| SkillToolPolicy | None | Security policy for skill-provided tool execution | | enable_skill_tools | bool | True | Whether to expose skill tools to the agent | | enable_mcp_tools | bool | True | Whether to expose MCP-discovered tools to the agent | | runner | Runner | None | Pre-bound runner instance (advanced usage) | Single agent vs multi-agent A single agent handles everything. Best for focused tasks. **Use when:** The task is well-defined and doesn't need specialized sub-expertise. A coordinator delegates to specialist subagents. **Use when:** Different parts of the task need different expertise, models, or tools. How subagent delegation works When an agent has subagents, AFK automatically generates transfer tools (transfer_to_researcher, transfer_to_writer). The coordinator calls these like any other tool. Each subagent runs a **full agent loop** with its own model, instructions, and tools. The coordinator sees only the subagent's final_text. Adding safety limits Every agent should have a FailSafeConfig in production: **Always set max_total_cost_usd** in production. A runaway agent loop can spend significant API credits in minutes. Policy-aware agents Attach a PolicyEngine to control what the agent can do: Policy decisions: allow (default), deny, request_approval (human-in-the-loop), or request_user_input. Design guidelines - **Start with one agent.** Only add subagents when you have clear evidence that the task needs specialized expertise. - **Keep instructions focused.** Vague instructions produce vague results. Tell the agent exactly what to do and what not to do. - **Use typed tools.** Every tool argument should be a Pydantic model. Untyped arguments bypass validation. - **Set cost limits early.** Add FailSafeConfig before your first deployment, not after your first runaway bill. Next steps How agents are executed \u2014 lifecycle, API modes, and state management. Define typed tool functions with validation and policy gates.", + "token_count": 481 + }, + { + "id": "doc_bbc97cbff254", + "path": "docs/library/api-reference.mdx", + "url": "/library/api-reference", + "title": "API Reference", + "description": "Canonical public imports and core signatures for AFK.", + "headings": [ + "Import map", + "Agent", + "Runner", + "RunnerConfig", + "FailSafeConfig", + "Tool decorator", + "AgentResult", + "API stability" + ], + "content_sha256": "15f83948b7edc7c528d459c0fd141ae9ab71d30f83a5635e768c12bdc145e3fa", + "content": "This page is the public import contract for application developers. Use these imports in docs, examples, tests that exercise public behavior, and downstream applications. For field-by-field configuration, see Configuration Reference. For generated module detail, see Full Module Reference. Import map | Task | Public import | | --- | --- | | Define an agent | from afk.agents import Agent | | Configure fail-safe limits | from afk.agents import FailSafeConfig | | Configure policy rules | from afk.agents import PolicyEngine, PolicyRule | | Implement dynamic policy hooks | from afk.agents import PolicyRole | | Run an agent | from afk.core import Runner | | Configure the runner | from afk.core import RunnerConfig | | Consume streaming events | from afk.core import AgentStreamEvent, AgentStreamHandle | | Define a tool | from afk.tools import tool | | Access tool context | from afk.tools import ToolContext | | Build an LLM client | from afk.llms import LLMBuilder | | Run eval suites | from afk.evals import run_suite | | Define eval cases | from afk.evals import EvalCase, EvalBudget | | Create memory stores | from afk.memory import InMemoryMemoryStore, SQLiteMemoryStore | | Create task queues | from afk.queues import InMemoryTaskQueue, RedisTaskQueue, TaskWorker | | Expose MCP tools | from afk.mcp import MCPServer | | Use A2A messaging | from afk.messaging import InternalA2AProtocol | Do not use src.afk.* imports in user-facing docs. Agent Only model is required. Most examples should also set name and instructions for clear traces and predictable behavior. Runner Common methods: | Method | Use when | Returns | | --- | --- | --- | | runner.run_sync(agent, user_message=..., thread_id=...) | Scripts, CLIs, tests without an existing event loop | AgentResult | | await runner.run(agent, user_message=..., thread_id=...) | Async services, workers, APIs | AgentResult | | await runner.run_stream(agent, user_message=..., thread_id=...) | Chat UIs and progress streams | AgentStreamHandle | | await runner.resume(agent, run_id=..., thread_id=..., context=...) | Continue from persisted state | AgentResult | | await runner.compact_thread(thread_id=...) | Apply memory retention/compaction | MemoryCompactionResult | run_sync() is a convenience wrapper for synchronous code. Use await runner.run(...) inside async applications. RunnerConfig Use Configuration Reference for the complete set of runner fields and defaults. FailSafeConfig Set max_total_cost_usd for production agents. The default is intentionally unset because acceptable budgets vary by application. Tool decorator Tool functions may be sync or async. They may accept (args), (args, ctx), or (ctx, args). AgentResult Important fields: | Field | Meaning | | --- | --- | | final_text | Final assistant text | | state | Terminal run state | | requested_model | Model requested by the caller or agent | | normalized_model | Effective normalized model when available | | provider_adapter | Provider/adapter used when available | | tool_executions | Ordered tool execution records | | subagent_executions | Ordered subagent execution records | | usage_aggregate | Aggregated input/output/total tokens | | total_cost_usd | Estimated total cost when available | | state_snapshot | Terminal runtime snapshot payload | API stability - Public docs and examples should use imports from afk.agents, afk.core, afk.tools, afk.llms, afk.memory, afk.queues, afk.mcp, afk.messaging, afk.observability, and afk.evals. - Internal modules under package subdirectories may change faster than public exports. - When changing public exports, update this page, Public API Rules, examples, and generated agent-facing docs.", + "token_count": 391 + }, + { + "id": "doc_4e1ebaf40267", + "path": "docs/library/architecture.mdx", + "url": "/library/architecture", + "title": "Architecture", + "description": "How AFK separates orchestration from execution.", + "headings": [ + "Layered architecture", + "Module boundaries", + "Runtime sequence", + "Contract boundaries", + "Error isolation", + "Design principles", + "Next steps" + ], + "content_sha256": "ff09cde4ea0e12906a54edc1fad4c85abd20bbc0a45370728060b8067e7d3cf8", + "content": "AFK's architecture is built on one principle: **orchestration and execution are separate concerns**. The runner (orchestration) manages the step loop, state, and policies. Adapters (execution) handle LLM calls, tool execution, and external communication. You can swap any adapter without touching your agent code. Layered architecture Module boundaries | Module | Responsibility | Dependencies | | --------------- | ------------------------------------------------------ | --------------------------------------------------- | | afk.agents | Agent definition, configuration, fail-safe | pydantic | | afk.core | Runner, step loop, state management, policies | afk.agents, afk.llms, afk.tools, afk.memory | | afk.llms | LLM runtime, provider adapters, retry/circuit breaking | Provider SDKs | | afk.tools | Tool registry, execution, validation, hooks | pydantic | | afk.memory | State persistence, checkpoints, compaction | Backend drivers | | afk.observability | Event pipeline, metrics, exporters | OTEL SDK (optional) | | afk.messaging | Public A2A protocol, auth, host, and delivery exports | afk.agents | | afk.evals | Eval runner, assertions, reporting | afk.core | **Key rule:** Modules in the Adapters layer never import from each other. afk.llms doesn't know about afk.tools. Only afk.core wires them together. Runtime sequence What happens when you call runner.run(agent, user_message=\"...\"): Contract boundaries Every boundary between modules uses a typed contract: All contracts are Pydantic models. This means: - **Validation at boundaries** \u2014 malformed data causes clear errors - **Serializable** \u2014 every contract can be logged, stored, or sent over the wire - **Versionable** \u2014 contracts have stable shapes for backward compatibility Error isolation Failures in one adapter don't crash the others: | Failure | Impact | Runner behavior | | -------------------- | ------------------------------ | --------------------------------------- | | LLM call fails | No response for this step | Retry (if retryable) or fail the run | | Tool execution fails | Tool result is an error object | Return error to LLM for self-correction | | Memory backend fails | State not persisted | Run continues (degraded mode) | | Telemetry fails | Events not exported | Run continues (events dropped silently) | **Telemetry failures are always silent.** A broken exporter should never block an agent run. If telemetry is critical to your use case, add a separate monitoring check. Design principles 1. **Contracts first.** Define the interface (Pydantic model), then implement the behavior. Never skip the contract. 2. **No cross-adapter imports.** afk.llms doesn't import afk.tools. Only afk.core wires modules together. 3. **Classify failures.** Every error is retryable, terminal, or non-fatal. The runner uses this classification to decide what to do. 4. **Least privilege.** Adapters get only the data they need. LLM adapters don't see tool results until the runner decides to include them. Next steps Provider-portable LLM runtime in detail. Contribute to AFK \u2014 patterns and conventions.", + "token_count": 330 + }, + { + "id": "doc_18d38fdacc9e", + "path": "docs/library/building-with-ai.mdx", + "url": "/library/building-with-ai", + "title": "Building with AI", + "description": "Production playbook \u2014 patterns, anti-patterns, and deployment guidance.", + "headings": [ + "Start narrow, iterate fast", + "Common patterns", + "Anti-patterns", + "Production readiness checklist", + "Next steps" + ], + "content_sha256": "4fb4252f5d2f1024c6863df494ee1627ef492fb5eb8a1c5ff8673f47773df3e4", + "content": "This guide covers the engineering patterns for building production-quality AI features with AFK. It's organized around three phases: starting narrow, adding capabilities, and scaling safely. Start narrow, iterate fast The most common mistake is building for complexity you don't have yet. Start with the simplest version that solves the problem, then add capabilities based on real evidence. > [!TIP] > **Test at every step.** Don't add tools before the base prompt works. Don't add subagents before single-agent tools work. Each layer should be proven before adding the next. Common patterns Categorize input into predefined labels. No tools needed. **Tips:** - Constrain output format explicitly in the prompt - Test with a diverse set of inputs - Add evals for each category Retrieve context, then generate an answer. **Tips:** - Always search before answering - Instruct the model to cite sources - Set tool_output_max_chars to prevent context overflow Generate, review, and test code. **Tips:** - Higher cost limits (coding agents iterate more) - Gate write_file with policy approval - Use sandbox profiles for code execution Route tasks to specialist subagents. **Tips:** - Keep the coordinator's prompt focused on routing - Don't give the coordinator tools \u2014 let specialists handle execution - Use join_policy=\"allow_optional_failures\" if some specialists are non-critical Anti-patterns These are the most common mistakes. Avoiding them will save you significant debugging time. | Anti-pattern | Problem | Fix | | -------------------------------------- | ---------------------------------------------------- | -------------------------------------------------- | | **No cost limits** | Runaway agent loops spend $100s in minutes | Always set max_total_cost_usd | | **Vague instructions** | Model produces inconsistent output | Be specific: \"Output only the category name\" | | **Too many tools** | Model gets confused choosing between tools | Keep \u2264 5 tools per agent. Split into subagents. | | **Mixing orchestration and execution** | Runner logic leaks into tool handlers | Tools should be pure functions. No runner imports. | | **Skipping evals** | Prompt changes break behavior silently | Run evals in CI on every PR | | **Untyped tool arguments** | Missing validation, hard-to-debug errors | Always use Pydantic models | | **Not classifying failures** | Retryable errors treated as terminal (or vice versa) | Return clear error types from tools | | **Giant system prompts** | Token waste, instruction drift | Split into skills. Use templates. | Production readiness checklist | Area | Requirement | Status | | ----------------- | ------------------------------------------------- | ----------------------------------------- | | **Safety** | FailSafeConfig with cost, step, and time limits | | | **Safety** | Policy rules for all mutating tools | | | **Observability** | Telemetry exporter configured (OTEL recommended) | | | **Observability** | Alerts on error rate and latency | | | **Testing** | Eval suite with \u2265 5 cases running in CI | | | **Testing** | Golden traces captured for regression detection | | | **Memory** | Persistent backend for multi-turn conversations | | | **Memory** | Thread compaction configured | | | **Security** | Secrets in environment variables, not code | | | **Security** | Sandbox profiles for code execution tools | | Next steps Set up monitoring, alerting, and dashboards. Write behavioral tests for agents.", + "token_count": 341 + }, + { + "id": "doc_4a1cf8a90e83", + "path": "docs/library/checkpoint-schema.mdx", + "url": "/library/checkpoint-schema", + "title": "Checkpoint Schema", + "description": "Checkpoint structure and resume correctness guarantees.", + "headings": [ + "Checkpoint model", + "Field reference", + "Resume behavior", + "Resume code example", + "Start a run that might be interrupted", + "Later, resume from the checkpoint", + "What gets stored in the payload", + "Phase-specific payloads", + "Async write-behind behavior", + "Effect replay and idempotency", + "Common failure scenarios" + ], + "content_sha256": "a0ba36ffceea5975afae78511697808cc3bf9231e2d04812cdc954536f971a1b", + "content": "Checkpoints are the persistence mechanism that allows AFK agent runs to survive process restarts, crashes, and intentional pauses. At key boundaries during execution (step start, pre-LLM call, post-tool batch, run terminal), the runner writes a checkpoint record to the memory store. Each checkpoint captures enough state to reconstruct the execution context and resume from where the run left off. Checkpoints matter for three reasons: 1. **Fault tolerance** -- If the process crashes mid-run, the latest checkpoint lets you resume without re-executing already-completed work. 2. **Human-in-the-loop** -- When a run pauses for approval, the checkpoint preserves the full conversation and pending state so the run can resume hours or days later. 3. **Auditability** -- The checkpoint chain provides a step-by-step record of every phase the run passed through, useful for debugging and compliance. Checkpoint model Field reference | Field | Type | Description | | --- | --- | --- | | run_id | str | Unique identifier for the agent run. Generated at run start or provided when resuming. Used as the primary key for checkpoint lookup. | | schema_version | str | Checkpoint schema version (for migration/compat validation). | | phase | str | The execution phase when the checkpoint was written. Values include: run_started, step_started, pre_llm, post_llm, pre_tool_batch, post_tool_batch, pre_subagent_batch, post_subagent_batch, run_terminal. | | payload | dict | Phase-specific data. The contents vary by phase -- see the payload section below. | | timestamp_ms | int | Unix timestamp in milliseconds when the checkpoint was written. Used for ordering when multiple checkpoints exist for the same run. | Resume behavior The runner calls memory.get_state(thread_id, checkpoint_latest_key(run_id)) to fetch the most recent checkpoint for the given run. If no checkpoint exists, a AgentCheckpointCorruptionError is raised. The checkpoint record must be a dict with the required fields (run_id, thread_id, phase, payload). Missing or malformed fields cause an AgentCheckpointCorruptionError. The runner also normalizes legacy checkpoint formats through _normalize_checkpoint_record(). If the checkpoint's phase is run_terminal and the payload contains a terminal_result, the run is already complete. The runner returns a pre-resolved handle with the deserialized AgentResult -- no re-execution occurs. For non-terminal checkpoints, the runner loads the full runtime snapshot which contains the conversation messages, counters, usage aggregates, and any pending LLM response. This snapshot is used to reconstruct the execution context. The runner calls run_handle() with the restored snapshot. Execution continues from the step where the run was interrupted. If a pending_llm_response exists in the snapshot, the runner skips the LLM call and proceeds directly to tool execution for that response. Resume code example What gets stored in the payload The payload field carries different data depending on the checkpoint phase. The most important payload is the **runtime snapshot** persisted at step_started and post_llm phases, which contains everything needed for full resume: | Payload key | Type | Description | | --- | --- | --- | | messages | list[dict] | Serialized conversation history (system, user, assistant, tool messages). | | step | int | Current step counter in the execution loop. | | state | str | Current run state (running, degraded, etc.). | | context | dict | Run context dict merged from agent defaults and caller-provided context. | | llm_calls | int | Number of LLM calls made so far. | | tool_calls | int | Number of tool calls made so far. | | started_at_s | float | Unix timestamp when the run originally started. | | usage | dict | Token usage aggregate (input_tokens, output_tokens, total_tokens). | | total_cost_usd | float | Accumulated estimated cost in USD. | | session_token | str \\| None | Provider session token for session-aware providers. | | checkpoint_token | str \\| None | Provider checkpoint token for checkpoint-aware providers. | | pending_llm_response | dict \\| None | Serialized LLM response that was received but whose tool calls have not yet been executed. On resume, the runner skips the LLM call and processes these tool calls directly. | | tool_executions | list[dict] | Serialized ToolExecutionRecord entries for all tools executed so far. | | subagent_executions | list[dict] | Serialized SubagentExecutionRecord entries. | | requested_model | str | The model string originally requested by the agent. | | normalized_model | str | The model string after resolution and normalization. | | provider_adapter | str | The provider adapter type used (e.g., openai, litellm). | | final_text | str | The final text output accumulated so far. | | final_structured | dict \\| None | Structured output if the LLM returned schema-validated JSON. | Phase-specific payloads Beyond the runtime snapshot, individual phase checkpoints carry lighter payloads: | Phase | Key payload fields | | --- | --- | | run_started | agent_name, resumed | | step_started | state, message_count | | pre_llm | model, provider, message_count | | post_llm | model, provider, finish_reason, tool_call_count, session_token, checkpoint_token, total_cost_usd | | pre_tool_batch | tool_call_count | | post_tool_batch | tool_calls_total, tool_failures | | run_terminal | state, final_text, requested_model, normalized_model, provider_adapter, terminal_result | Async write-behind behavior By default, checkpoint writes are asynchronous (RunnerConfig.checkpoint_async_writes=True): - Writes are queued and flushed by a background writer. - Repeated runtime_state writes may be coalesced (checkpoint_coalesce_runtime_state=True). - Terminal states perform a bounded flush (checkpoint_flush_timeout_s) before returning. This improves loop throughput while preserving terminal durability. Effect replay and idempotency When a run resumes and re-enters a tool batch, the runner checks for previously persisted effect results before re-executing tools. Each tool call's result is stored with an input_hash (derived from tool name and arguments) and an output_hash. On resume, if a matching effect result exists for a tool call ID with a matching input hash, the stored result is replayed instead of re-executing the tool. This guarantees idempotent resume for tools with side effects. The replayed_effect_count field in the runtime snapshot tracks how many tool calls were satisfied from replay rather than fresh execution. Common failure scenarios **Missing checkpoint** -- Calling runner.resume() with a run_id that has no checkpoint raises AgentCheckpointCorruptionError. This can happen if the memory store was cleared or the run never persisted its first checkpoint (crashed before run_started). **Corrupted payload** -- If the checkpoint record exists but is not a valid dict or is missing required keys, AgentCheckpointCorruptionError is raised. The runner does not attempt partial recovery from corrupted checkpoints. **Pending LLM response corruption** -- If a checkpoint has pending_llm_response set but the serialized response cannot be deserialized, the runner raises AgentCheckpointCorruptionError rather than making a duplicate LLM call. **Stale session tokens** -- Provider session tokens stored in checkpoints may expire between the original run and the resume attempt. The runner passes the stored session_token and checkpoint_token to the provider, but the provider may reject them. In that case, the LLM call fails and follows the normal retry/fallback chain.", + "token_count": 744 + }, + { + "id": "doc_a24b92541657", + "path": "docs/library/configuration-reference.mdx", + "url": "/library/configuration-reference", + "title": "Configuration Reference", + "description": "Every configurable field across Agent, Runner, FailSafeConfig, and SandboxProfile.", + "headings": [ + "Agent", + "Reasoning override precedence", + "RunnerConfig", + "Deep Dive: Interaction Models", + "FailSafeConfig", + "Deep Dive: Failure & Recovery", + "FailurePolicy values", + "SandboxProfile", + "Runner constructor", + "@tool decorator", + "Next steps" + ], + "content_sha256": "70a5ac606bb9dfba32cbba5a3a6f7791cf0437ae14f45dc29bf3491141978df6", + "content": "This page is a single-source reference for every configuration knob in AFK. Each section lists fields with their type, default value, and purpose. Agent The Agent constructor defines what your agent is \u2014 identity, model, tools, and behavior. | Field | Type | Default | Description | | --------------------------- | ----------------- | ---------------- | -------------------------------------------------------------- | | model | str \\| LLM | **required** | LLM model name or pre-built client instance | | name | str | class name | Agent identity for logs, telemetry, and subagent routing | | instructions | str \\| callable | None | System prompt \u2014 static string or callable provider | | instruction_file | str \\| Path | None | Path to a prompt file that must resolve under prompts_dir | | prompts_dir | str \\| Path | None | Root directory for prompt files (env: AFK_AGENT_PROMPTS_DIR) | | tools | list | [] | Typed functions the agent can call | | subagents | list[Agent] | [] | Specialist agents this agent can delegate to | | context_defaults | dict | {} | Default JSON context merged into each run | | inherit_context_keys | list[str] | [] | Context keys accepted from a parent subagent | | model_resolver | callable | None | Custom function to resolve model names to LLM clients | | skills | list[str] | [] | Skill names to resolve under skills_dir | | mcp_servers | list | [] | External MCP server refs whose tools to expose | | skills_dir | str \\| Path | .agents/skills | Root directory for skill definitions | | instruction_roles | list | [] | Callbacks that append dynamic instruction text | | policy_roles | list | [] | Callbacks that can allow/deny/defer runtime actions | | policy_engine | PolicyEngine | None | Deterministic rule engine applied before policy roles | | subagent_router | SubagentRouter | None | Router callback deciding subagent targets | | max_steps | int | 20 | Maximum reasoning/tool loop steps | | tool_parallelism | int | None | Max concurrent tool calls (falls back to fail_safe) | | subagent_parallelism_mode | str | configurable | single, parallel, or configurable | | fail_safe | FailSafeConfig | defaults | Runtime limits and failure policies | | reasoning_enabled | bool \\| None | None | Default request thinking flag for this agent | | reasoning_effort | str \\| None | None | Default request thinking_effort label | | reasoning_max_tokens | int \\| None | None | Default request max_thinking_tokens | | enable_skill_tools | bool | True | Auto-register built-in skill tools | | enable_mcp_tools | bool | True | Auto-register tools from configured MCP servers | | runner | Runner | None | Optional runner override | Reasoning override precedence Reasoning values are resolved in this order: 1. Run context override: context[\\\"_afk\\\"][\\\"reasoning\\\"] 2. Agent defaults: reasoning_enabled, reasoning_effort, reasoning_max_tokens 3. Provider defaults/validation in the LLM layer --- RunnerConfig Passed to Runner(config=RunnerConfig(...)) to control runtime behavior. | Field | Type | Default | Description | | ----------------------------------------- | ---------------- | ------------------------------------------ | ---------------------------------------------------------- | | interaction_mode | str | headless | headless, interactive, or external | | approval_timeout_s | float | 300.0 | Timeout for deferred approval decisions | | input_timeout_s | float | 300.0 | Timeout for deferred user-input decisions | | approval_fallback | str | deny | Fallback when approval times out: allow, deny, defer | | input_fallback | str | deny | Fallback when user input times out | | sanitize_tool_output | bool | True | Sanitize model-visible tool output | | untrusted_tool_preamble | bool | True | Inject untrusted-data warning preamble | | tool_output_max_chars | int | 12_000 | Max tool output characters forwarded to model | | default_sandbox_profile | SandboxProfile | None | Default sandbox profile for tool execution | | sandbox_profile_provider | callable | None | Runtime sandbox profile resolver | | secret_scope_provider | callable | None | Secret-scope resolver per tool call | | default_allowlisted_commands | tuple[str] | ls, cat, head, tail, rg, find, pwd, echo | Allowlisted shell commands for runtime tools | | max_parallel_subagents_global | int | 64 | Global cap for concurrent subagent tasks | | max_parallel_subagents_per_parent | int | 8 | Per-parent-run cap for concurrent subagent fanout | | max_parallel_subagents_per_target_agent | int | 4 | Per-target-agent cap | | subagent_queue_backpressure_limit | int | 512 | Max pending subagent nodes before backpressure | | checkpoint_async_writes | bool | True | Enable background checkpoint/state writes | | checkpoint_queue_maxsize | int | 1024 | Max queued checkpoint write jobs | | checkpoint_flush_timeout_s | float | 10.0 | Timeout for terminal checkpoint flush | | checkpoint_coalesce_runtime_state | bool | True | Coalesce repeated runtime_state checkpoint writes | | debug | bool | False | Enable debug metadata in emitted run events | | debug_config | RunnerDebugConfig \\| None | None | Advanced debug controls (verbosity/content/redaction) | | background_tools_enabled | bool | True | Enable deferred/background tool orchestration | | background_tool_default_grace_s | float | 0.0 | Wait this long after defer to allow fast background completion before continuing | | background_tool_max_pending | int | 256 | Maximum unresolved background tool tickets per run | | background_tool_poll_interval_s | float | 0.5 | Poll interval for external/persisted ticket resolution | | background_tool_result_ttl_s | float | 3600.0 | TTL before pending ticket is marked failed (runtime floor is 1.0s) | | background_tool_interrupt_on_resolve | bool | True | When enabled, resolved tickets can be ingested immediately after defer/grace in the same step | Deep Dive: Interaction Models The interaction_mode setting fundamentally changes how the Runner handles decision points like tool approval or user input requests. - **headless (default)**: The Runner never pauses. - If a policy returns defer or request_user_input, the Runner immediately uses the configured approval_fallback or input_fallback (default: deny). - _Use case:_ Backend workers, cron jobs, automated testing. - **interactive**: The Runner pauses execution and uses the configured InteractionProvider to ask for human input. - For CLI apps, this prints to stdout and reads from stdin. - The run blocks until input is received or approval_timeout_s expires. - _Use case:_ Local CLI tools, scripts run by humans. - **external**: The Runner emits a run_paused event and **suspends execution**. - The run() loop exits (or yields a paused state). The state is persisted to memory. - The system waits for an external API call to runner.resume_with_input(). - _Use case:_ Chatbots, web UIs, Slack bots where the user is asynchronous. --- FailSafeConfig Passed to Agent(fail_safe=FailSafeConfig(...)) to set runtime limits and failure policies. | Field | Type | Default | Description | | ------------------------------ | --------------- | --------------------- | --------------------------------------------- | | llm_failure_policy | FailurePolicy | retry_then_fail | Strategy when LLM calls fail | | tool_failure_policy | FailurePolicy | continue_with_error | Strategy when tool calls fail | | subagent_failure_policy | FailurePolicy | continue | Strategy when subagent calls fail | | approval_denial_policy | FailurePolicy | skip_action | Strategy when approval is denied or times out | | max_steps | int | 20 | Maximum run loop iterations | | max_wall_time_s | float | 300.0 | Maximum wall-clock runtime in seconds | | max_llm_calls | int | 50 | Maximum number of LLM invocations | | max_tool_calls | int | 200 | Maximum number of tool invocations | | max_parallel_tools | int | 16 | Max concurrent tools per batch | | max_subagent_depth | int | 3 | Maximum subagent recursion depth | | max_subagent_fanout_per_step | int | 4 | Maximum subagents selected per step | | max_total_cost_usd | float \\| None | None | Cost ceiling for run termination | | fallback_model_chain | list[str] | [] | Ordered fallback model list for LLM retries | | breaker_failure_threshold | int | 5 | Circuit breaker open threshold | | breaker_cooldown_s | float | 30.0 | Circuit breaker cooldown window in seconds | Deep Dive: Failure & Recovery FailSafeConfig controls the agent's resilience. The policies work in a hierarchy: 1. **Lower-level retries**: Transient errors (network glitches, rate limits) are retried automatically by the LLM client, guided by AFK_LLM_MAX_RETRIES. 2. **llm_failure_policy**: If the LLM call fails after all retries (or hits a terminal error like 401 Unauthorized): - retry_then_fail: Tries a few more times at the agent level, then fails the run. - retry_then_degrade: Tries again, then marks the run as degraded but returns partial results (useful for \"best effort\" responses). 3. **tool_failure_policy**: If a tool raises an exception: - continue_with_error (default): The error message is fed back to the model. The model can then try to fix its mistake or apologize. **This is usually the best setting** for capable models. - fail_run: Immediately stops the run. Use this for critical transactional agents where any error is unacceptable. 4. **Circuit Breakers**: - If a model provider fails breaker_failure_threshold times in a row, the circuit opens. - Subsequent calls fail _instantly_ without network traffic until breaker_cooldown_s passes. - This protects your system (and wallet) from hammering a down service. FailurePolicy values | Value | Behavior | | --------------------- | ---------------------------------------------------- | | retry_then_fail | Retry with backoff, then fail the run | | retry_then_degrade | Retry with backoff, then degrade (partial result) | | retry_then_continue | Retry with backoff, then continue without the result | | fail_fast | Fail immediately, no retries | | fail_run | Fail the entire run | | continue_with_error | Continue, passing the error to the model | | continue | Continue silently, ignoring the failure | | skip_action | Skip the action entirely | --- SandboxProfile Controls execution restrictions for tool handlers. Configured via RunnerConfig.default_sandbox_profile. | Field | Type | Default | Description | | -------------------------- | --------------- | --------- | ---------------------------------------------- | | profile_id | str | default | Profile identifier for logs and policy | | allow_network | bool | False | Whether tools can make network requests | | allow_command_execution | bool | False | Whether tools can execute shell commands | | allowed_command_prefixes | list[str] | [] | Allowed command prefixes (empty = none) | | deny_shell_operators | bool | True | Block pipes, redirects, semicolons | | allowed_paths | list[str] | [] | Restrict file access to these paths | | denied_paths | list[str] | [] | Explicitly deny access to these paths | | command_timeout_s | float \\| None | None | Kill commands after this duration | | max_output_chars | int | 20_000 | Truncate command output beyond this limit | --- Runner constructor The Runner accepts these arguments directly (outside of RunnerConfig): | Argument | Type | Default | Description | | ---------------------- | ---------------------- | ---------------- | ------------------------------------------------------ | | memory_store | MemoryStore | None | Memory backend (resolved from env if None) | | interaction_provider | InteractionProvider | None | Human-in-the-loop provider (required for non-headless) | | policy_engine | PolicyEngine | None | Deterministic policy engine shared across runs | | telemetry | str \\| TelemetrySink | None | Telemetry sink instance or backend id | | telemetry_config | dict | None | Backend-specific sink configuration | | config | RunnerConfig | RunnerConfig() | Runner configuration | --- @tool decorator | Argument | Type | Default | Description | | ---------------- | ------------------ | ------------- | ----------------------------------------------------------------- | | args_model | Type[BaseModel] | **required** | Pydantic model for argument validation | | name | str | function name | Tool name exposed to the LLM | | description | str | docstring | Tool description for the LLM | | timeout | float | None | Execution timeout in seconds | | prehooks | list[PreHook] | None | Argument transform hooks | | posthooks | list[PostHook] | None | Output transform hooks | | middlewares | list[Middleware] | None | Execution wrappers (logging, caching, etc.) | | raise_on_error | bool | False | Raise exceptions instead of returning ToolResult(success=False) | Next steps Environment variable defaults and backend selection. Quick import reference.", + "token_count": 1143 + }, + { + "id": "doc_e7d508371e5c", + "path": "docs/library/core-runner.mdx", + "url": "/library/core-runner", + "title": "Core Runner", + "description": "Execute agents \u2014 lifecycle, API modes, streaming, and state management.", + "headings": [ + "Quick example", + "Synchronous (simplest)", + "Three API modes", + "Run lifecycle", + "Terminal states", + "The step loop", + "Runner configuration", + "Run handles and lifecycle control", + "Monitor events in real time", + "Lifecycle controls", + "Wait for the final result", + "Thread-based memory", + "Turn 1", + "Turn 2 \u2014 agent remembers Turn 1", + "Resume from checkpoint", + "Resume an interrupted run", + "Compact long threads", + "Summarize old messages to control storage growth", + "AgentResult reference", + "ToolExecutionRecord fields", + "Next steps" + ], + "content_sha256": "1a47bf2eb1a77425072db12e35a71d725d7b826102f87dd74a81c747487df506", + "content": "The **Runner** is the execution engine that runs agents. It manages the step loop, LLM calls, tool execution, state persistence, and telemetry. You create a Runner, hand it an Agent, and get back an AgentResult. Quick example Three API modes Blocks until complete. Best for scripts, tests, and simple integrations. Awaitable. Best for async applications and API servers. Real-time events. Best for chat UIs and CLI tools. Run lifecycle Every agent run follows this state machine: Terminal states | State | Meaning | final_text | | ------------- | ---------------------------------------- | ------------------------------------------------------------------------------------------ | | completed | Run finished successfully | Model's response | | failed | Unrecoverable error occurred | Error message | | degraded | Partial result (some failures tolerated) | Best-effort response | | cancelled | Caller cancelled the run | Empty or partial | | interrupted | Timeout or external interrupt | Empty or partial | The step loop Each \"step\" is one iteration of the agent's decision cycle: The runner constructs an LLMRequest with the conversation history, tool schemas, and model configuration. The request is sent through the LLM runtime (with retry, circuit breaker, rate limiting, and caching policies). If the LLM returns **text only** \u2192 the run is complete. If it returns **tool calls** \u2192 proceed to tool execution. Each tool call is validated, policy-checked, executed, and its output is sanitized and fed back to the LLM. The runner returns to Step 1 for the next LLM turn. This continues until the model produces a text-only response or a limit is hit. Runner configuration Run handles and lifecycle control For advanced control, use run_handle(): Thread-based memory Pass a thread_id to maintain conversation context across runs: Resume from checkpoint Compact long threads AgentResult reference | Field | Type | Description | | --------------------- | ------------------------------- | ----------------------------------------------------------------------------- | | final_text | str | The agent's final response | | state | str | Terminal state: completed, failed, degraded, cancelled, interrupted | | run_id | str | Unique run identifier | | thread_id | str | Thread identifier for memory continuity | | tool_executions | list[ToolExecutionRecord] | All tool calls with name, success, output, latency | | subagent_executions | list[SubagentExecutionRecord] | All subagent invocations | | usage_aggregate | UsageAggregate | Token counts aggregate across LLM calls | | state_snapshot | dict[str, JSONValue] | Final runtime counters/snapshot metadata | ToolExecutionRecord fields AgentResult.tool_executions entries include: | Field | Type | Description | | --- | --- | --- | | tool_name | str | Tool name | | tool_call_id | str \\| None | Tool call id from the model/provider | | success | bool | Execution success | | output | JSONValue \\| None | Tool output payload | | error | str \\| None | Error text (when failed) | | latency_ms | float \\| None | Tool latency | | agent_name | str \\| None | Agent that executed the tool (parent or subagent) | | agent_depth | int \\| None | Nesting depth where tool ran | | agent_path | str \\| None | Agent lineage path for nested calls | Next steps Real-time event streaming for chat UIs. Thread-based state persistence and resume.", + "token_count": 326 + }, + { + "id": "doc_3f1383f72c5f", + "path": "docs/library/debugger.mdx", + "url": "/library/debugger", + "title": "Debugger", + "description": "Debug-mode runner setup with redacted event payloads and live event taps.", + "headings": [ + "Quick start", + "Attach to a run handle", + "Redaction behavior", + "Verbosity modes", + "Background tool scenario (coding + build + docs)", + "External worker completion" + ], + "content_sha256": "7349668744c3913398f016c1e3b0f07fd6b3659d0cf075176717e7a6a4003dcb", + "content": "Use afk.debugger.Debugger to run agents in a structured debug mode without changing your core runtime architecture. Quick start You can also enable debug mode directly on the runner: Attach to a run handle Redaction behavior When redact_secrets=True, payload keys containing any of these markers are masked: - api_key - token - secret - authorization - password Verbosity modes - basic: lightweight metadata and step markers. - detailed: includes payload previews and step metadata. - trace: highest detail for deep diagnostics. Background tool scenario (coding + build + docs) Flow: 1. Agent writes code. 2. Agent calls build_project and receives tool_deferred. 3. Agent continues with write_docs or other tasks. 4. Runner emits tool_background_resolved when build completes. 5. Agent uses resolved build output in a later step to finalize/fix. External worker completion For out-of-process execution, an external worker can complete a ticket by writing background state into memory: The runner poller detects this update, emits tool_background_resolved or tool_background_failed, and injects a synthetic tool message for the next step.", + "token_count": 122 + }, + { + "id": "doc_067033cfc945", + "path": "docs/library/deployment.mdx", + "url": "/library/deployment", + "title": "Deployment", + "description": "Deploy AFK agents to production with Docker, Kubernetes, and scaling patterns.", + "headings": [ + "Docker deployment", + "Basic Dockerfile", + "Install dependencies", + "Copy application code", + "Run your application entrypoint that creates AFK agents/runners", + "Production Dockerfile with multi-stage build", + "Production image", + "Run as non-root user", + "docker-compose.yml", + "Environment configuration", + "Required environment variables", + "LLM Provider", + "or", + "Memory backend", + "Queue backend ", + "Observability", + "Server mode", + "Production configuration file", + "Scaling patterns", + "Horizontal scaling with workers", + "Kubernetes deployment", + "Kubernetes HPA for auto-scaling", + "Health checks", + "Database schema", + "SQLite (development)", + "PostgreSQL", + "Security checklist", + "Monitoring", + "Next steps" + ], + "content_sha256": "7cc0c692d113fd9dc1af4de8235cb8b0c1c4b512d6079d164413d60837a5aa32", + "content": "This guide covers deploying AFK agents to production environments, from single-container setups to distributed, multi-worker deployments. Docker deployment Basic Dockerfile Production Dockerfile with multi-stage build docker-compose.yml Environment configuration Required environment variables Production configuration file Create config/production.yaml: Scaling patterns Horizontal scaling with workers Kubernetes deployment Kubernetes HPA for auto-scaling Health checks Implement health endpoints in your server: Database schema SQLite (development) SQLite requires no schema setup \u2014 tables are created automatically on first use. PostgreSQL Security checklist Store API keys in secrets managers (AWS Secrets Manager, HashiCorp Vault, Kubernetes Secrets). Never commit keys to version control. Restrict traffic between services. Agents should only reach LLM providers and necessary databases. Configure rate limits on public endpoints to prevent abuse. Always set max_total_cost_usd in FailSafeConfig for production agents. Enable telemetry export to your logging infrastructure for compliance. Monitoring Key metrics to track: | Metric | What it indicates | Alert threshold | |--------|------------------|-----------------| | agent.run.duration | How long runs take | > 60s p95 | | agent.run.cost | Token spend per run | > $0.50 per run | | agent.run.failures | Failed runs | > 5% error rate | | llm.latency | LLM response time | > 10s p95 | | llm.errors | LLM API errors | > 1% error rate | | queue.depth | Pending tasks | > 100 items | | queue.dead_letters | Failed tasks | > 0 | Next steps Set up telemetry and alerting for production monitoring. Security hardening checklist and best practices. CI-gated quality checks for agent releases. Production patterns and anti-patterns.", + "token_count": 191 + }, + { + "id": "doc_d147a81bac29", + "path": "docs/library/developer-guide.mdx", + "url": "/library/developer-guide", + "title": "Developer Guide", + "description": "Maintainer workflow for changing AFK itself.", + "headings": [ + "Local setup", + "Repository boundaries", + "Change workflow", + "Documentation workflow", + "High-risk areas", + "Public API checklist", + "Next steps" + ], + "content_sha256": "8ac363b758f8ae9d391d26db304b7bcf931f1fb24bbf0dd159c8de2e3ebc1789", + "content": "This page is for contributors changing the AFK framework. If you are building an application with AFK, start with Quickstart or Building with AI. Local setup AFK targets Python 3.13+. Common commands: Preview docs: Regenerate agent-facing docs and skill indexes: Install the repository skills with Vercel's Skills CLI: Use afk-coder when building with AFK. Use afk-maintainer when reviewing or changing AFK itself. Repository boundaries | Package | Responsibility | | --- | --- | | afk.agents | Agent definitions, policy, prompts, skills, lifecycle, workflow, A2A | | afk.core | Runner, interaction providers, streaming handles, telemetry contracts | | afk.llms | Provider-portable LLM runtime, adapters, retry/timeout/cache/routing policies | | afk.tools | Typed tools, decorators, registry, sandboxing, output limiting | | afk.memory | Memory stores, checkpoints, retention, compaction, vector helpers | | afk.queues | Async task queues, execution contracts, workers, retry/DLQ behavior | | afk.observability | Telemetry collectors, projectors, exporters | | afk.mcp, afk.messaging, afk.debugger, afk.evals | Optional integration and quality layers | Keep the Agent/Runner/Runtime boundary intact: - agents are configuration; - runners execute; - tools, memory, queues, LLM adapters, and telemetry provide runtime capabilities. Change workflow 1. Identify the public contract affected by the change. 2. Inspect the existing module and tests before editing. 3. Make the smallest behavior change that preserves package boundaries. 4. Add or update focused tests for success and failure paths. 5. Update docs when behavior, public imports, configuration, env vars, or examples change. 6. Regenerate agent-facing docs when docs navigation, snippets, or skill metadata change. Documentation workflow User-facing docs must: - import from public package surfaces such as afk.agents and afk.core; - explain behavior before internals; - use Python 3.13+ guidance; - distinguish afk-py distribution install from afk imports; - include new pages in docs/docs.json; - keep examples aligned with current AgentResult, RunnerConfig, and FailSafeConfig fields. Maintainer docs may reference internal files, but should state which public contract or invariant is being protected. High-risk areas Use targeted tests when touching: | Area | Risk | | --- | --- | | src/afk/core/runner/ | execution loop, checkpoints, resume, policy/failure routing | | src/afk/core/streaming.py | stream events and handle lifecycle | | src/afk/tools/core/base.py, src/afk/tools/registry.py | tool invocation semantics | | src/afk/tools/security.py | sandbox, secret scope, output limiting | | src/afk/llms/runtime/ | retries, circuit breakers, rate limits, caching, routing | | src/afk/memory/ | persistence, checkpoint keys, compaction, vector search | | src/afk/queues/ | execution contracts, retry/DLQ, worker lifecycle | | src/afk/agents/a2a/ | auth, delivery guarantees, protocol compatibility | Public API checklist Before changing exports or constructor fields: - update package-level __all__; - update API Reference; - update Configuration Reference if defaults changed; - update snippets that use the changed field; - add public-import tests where useful. Next steps Package boundaries and runtime flow. Maintainer rules for stable imports and docs examples. Behaviors that tests protect. Generated source-level symbol map.", + "token_count": 387 + }, + { + "id": "doc_61fd9f682847", + "path": "docs/library/environment-variables.mdx", + "url": "/library/environment-variables", + "title": "Environment Variables", + "description": "Environment variable reference for AFK defaults and backend configuration.", + "headings": [ + "LLM defaults", + "Memory", + "Override in code \u2014 takes precedence over env vars", + "Queue", + "Prompts", + "Runner and tool command defaults", + "MCP server", + "A2A", + "Precedence", + "Next steps" + ], + "content_sha256": "89122dd488be8932d0ac65bf7169acebead8cb49223e178df9d0f049082cd073", + "content": "AFK is configured primarily through code. Environment variables provide fallback defaults for LLM settings, memory backends, queues, and prompt directories. **Runtime configuration APIs always take precedence.** All variables are optional. If unset, AFK uses the defaults listed below. LLM defaults These variables configure the default LLM provider and behavior. They are read by LLMSettings.from_env() at startup. | Variable | Default | Description | | ------------------------------- | -------------- | ------------------------------------------------------------ | | AFK_LLM_PROVIDER | litellm | Default provider id (openai, litellm, anthropic_agent) | | AFK_LLM_PROVIDER_ORDER | _(none)_ | Comma-separated provider preference order for routing helpers | | AFK_LLM_MODEL | gpt-4.1-mini | Default model name | | AFK_EMBED_MODEL | _(none)_ | Embedding model for vector operations | | AFK_LLM_API_BASE_URL | _(none)_ | Provider API base URL | | AFK_LLM_API_KEY | _(none)_ | Provider API key | | AFK_LLM_TIMEOUT_S | 30 | Request timeout in seconds | | AFK_LLM_STREAM_IDLE_TIMEOUT_S | 45 | Stream idle timeout in seconds | | AFK_LLM_MAX_RETRIES | 3 | Retry attempts on transient failures | | AFK_LLM_BACKOFF_BASE_S | 0.5 | Exponential backoff base in seconds | | AFK_LLM_BACKOFF_JITTER_S | 0.15 | Random jitter added to backoff | | AFK_LLM_JSON_MAX_RETRIES | 2 | Structured output repair attempts | | AFK_LLM_MAX_INPUT_CHARS | 200000 | Input truncation ceiling in characters | **API keys for specific providers** (e.g. OPENAI_API_KEY, ANTHROPIC_API_KEY) are read by the underlying provider libraries, not by AFK directly. Set AFK_LLM_API_KEY only if you want a single key shared across providers. Memory Configure the persistent memory backend for thread-based conversations. | Variable | Default | Description | | -------------------- | -------------------- | ------------------------------------------------------- | | AFK_MEMORY_BACKEND | sqlite | Backend type: inmemory/memory, sqlite, redis, postgres | | AFK_SQLITE_PATH | afk_memory.sqlite3 | SQLite file path | | AFK_REDIS_URL | _(none)_ | Redis connection URL (for example redis://localhost:6379) | | AFK_REDIS_HOST | localhost | Redis host when URL is not set | | AFK_REDIS_PORT | 6379 | Redis port when URL is not set | | AFK_REDIS_DB | 0 | Redis DB when URL is not set | | AFK_REDIS_PASSWORD | _(none)_ | Redis password when URL is not set | | AFK_REDIS_EVENTS_MAX | 2000 | Max Redis memory events per thread | | AFK_PG_DSN | _(none)_ | PostgreSQL connection string | | AFK_PG_HOST | localhost | PostgreSQL host when DSN is not set | | AFK_PG_PORT | 5432 | PostgreSQL port when DSN is not set | | AFK_PG_USER | postgres | PostgreSQL user when DSN is not set | | AFK_PG_PASSWORD | _(none)_ | PostgreSQL password when DSN is not set | | AFK_PG_DB | afk | PostgreSQL database when DSN is not set | | AFK_PG_SSL | false | Enable PostgreSQL SSL | | AFK_PG_POOL_MIN | 1 | PostgreSQL pool minimum size | | AFK_PG_POOL_MAX | 10 | PostgreSQL pool maximum size | | AFK_VECTOR_DIM | _(required for Postgres)_ | Vector dimension for Postgres memory search | Queue Configure the task queue backend for async agent execution. | Variable | Default | Description | | ---------------------------------- | ---------- | --------------------------------- | | AFK_QUEUE_BACKEND | inmemory | Backend type: inmemory, redis | | AFK_QUEUE_REDIS_URL | falls back to AFK_REDIS_URL | Redis URL for queue backend | | AFK_QUEUE_REDIS_HOST | falls back to AFK_REDIS_HOST, then localhost | Redis queue host | | AFK_QUEUE_REDIS_PORT | falls back to AFK_REDIS_PORT, then 6379 | Redis queue port | | AFK_QUEUE_REDIS_DB | falls back to AFK_REDIS_DB, then 0 | Redis queue DB | | AFK_QUEUE_REDIS_PASSWORD | falls back to AFK_REDIS_PASSWORD | Redis queue password | | AFK_QUEUE_REDIS_PREFIX | afk:queue | Redis key prefix | | AFK_QUEUE_RETRY_BACKOFF_BASE_S | 0.5 | Retry base delay in seconds | | AFK_QUEUE_RETRY_BACKOFF_MAX_S | 30 | Retry max delay in seconds | | AFK_QUEUE_RETRY_BACKOFF_JITTER_S | 0.2 | Random jitter added to backoff | The inmemory queue backend does not persist across restarts. Use redis for production workloads that need reliable task delivery. Prompts | Variable | Default | Description | | ----------------------- | ---------------- | -------------------------------------- | | AFK_AGENT_PROMPTS_DIR | .agents/prompt | Root directory for system prompt files | Agents resolve instruction_file paths relative to this directory. See System Prompts for details. Runner and tool command defaults | Variable | Default | Description | | --- | --- | --- | | AFK_ALLOWED_COMMANDS | _(none)_ | Comma-separated default allowlist for runtime command tools | Runner constructors and RunnerConfig fields remain the preferred way to set command policy. Use this environment variable only for process-wide defaults. MCP server These variables are read by the MCP server configuration helpers in afk.config. | Variable | Default | Description | | --- | --- | --- | | AFK_CORS_ORIGINS | _(none)_ | Comma-separated CORS origins | | AFK_MCP_NAME | afk-mcp-server | Server name | | AFK_MCP_VERSION | 1.0.0 | Server version string | | AFK_MCP_HOST | 0.0.0.0 | Bind host | | AFK_MCP_PORT | 8000 | Bind port | | AFK_MCP_INSTRUCTIONS | _(none)_ | Optional server instructions | | AFK_MCP_PATH | /mcp | HTTP MCP endpoint path | | AFK_MCP_SSE_PATH | /mcp/sse | SSE endpoint path | | AFK_MCP_HEALTH_PATH | /health | Health endpoint path | | AFK_MCP_ENABLE_SSE | true | Enable SSE endpoint | | AFK_MCP_ENABLE_HEALTH | true | Enable health endpoint | | AFK_MCP_ALLOW_BATCH | true | Allow batched MCP requests | A2A No default environment variables are required. Configure A2A host and authentication in code for an explicit security posture. See A2A for details. Precedence Configuration is resolved in this order (highest priority first): 1. **Constructor arguments** \u2014 Runner(memory_store=...), Agent(model=...) 2. **RunnerConfig / LLMSettings objects** \u2014 passed to constructors 3. **Environment variables** \u2014 fallback defaults listed on this page 4. **Built-in defaults** \u2014 hardcoded in the source Next steps Full reference for all configurable fields across Agent, Runner, and more. Runner lifecycle and configuration options.", + "token_count": 557 + }, + { + "id": "doc_c93c115aeb85", + "path": "docs/library/evals.mdx", + "url": "/library/evals", + "title": "Evals", + "description": "Behavioral testing for agent quality \u2014 assertions, budgets, and CI integration.", + "headings": [ + "Your first eval", + "Eval lifecycle", + "Eval case types", + "Assertions", + "Suite configuration", + "CI integration", + ".github/workflows/evals.yml", + "Release gating", + "Next steps" + ], + "content_sha256": "89a16478fcd1a7d21a0539b75ab780f2634444483626668f05e16f25320bd0ed", + "content": "AFK evals run agents against named inputs and check the resulting state, text, tool usage, budgets, and telemetry. Use them for prompt changes, tool changes, routing changes, and regression tests. They can run against real providers, test adapters, or agents configured with deterministic tools. Your first eval Eval lifecycle Each case specifies an agent and input message. Suite-level assertions and budgets verify the result. The scheduler runs cases sequentially or in parallel, respecting concurrency limits. Each case runs through the same runner path your application uses. Assertions verify the result \u2014 text content, state, tool usage, cost, latency, etc. Budget limits gate individual case costs and the total suite cost. Pass/fail results, assertion details, and metrics are collected into a report. Eval case types Verify correct behavior under normal conditions. Verify graceful handling of errors and edge cases. Verify that the agent uses tools correctly. Verify that the agent stays within cost limits. Assertions Assertions are suite-level callables. Import the built-ins from afk.evals, or implement the EvalAssertion protocol. Suite configuration CI integration Run evals in your CI pipeline to gate releases: **Set a budget for CI evals.** Without a budget, a broken prompt can drain your API credits during a CI run. Use EvalBudget(max_total_cost_usd=2.00) as a reasonable CI limit. Release gating Gate releases on eval pass rate: Next steps Security boundaries and production hardening. Production playbook with anti-patterns.", + "token_count": 181 + }, + { + "id": "doc_a5120de838fb", + "path": "docs/library/examples/index.mdx", + "url": "/library/examples/", + "title": "Getting Started", + "description": "Scenario-based examples for every major AFK feature.", + "headings": [ + "Which example should I start with?", + "Complexity progression", + "Scenario catalog" + ], + "content_sha256": "e209b35c682236c60b0dd47344bf686c23ea09e6000e11aab2c25e3078800062", + "content": "This catalog provides runnable code examples for every major AFK capability. Each example is self-contained and demonstrates a specific pattern -- from the simplest possible agent run to advanced tool security configurations. The examples are ordered by complexity. If you are new to AFK, start at the top and work your way down. Each example builds on concepts introduced in the previous ones. Which example should I start with? - **First time using AFK?** Start with Minimal Chat Agent. It shows the absolute simplest agent run in under 10 lines of code. - **Need to gate dangerous actions?** Go to Policy + HITL. It demonstrates how policy engines and human approval work together. - **Building a multi-agent system?** See Subagent Router. It shows how to delegate work to specialist subagents and merge results. - **Need to persist and resume runs?** Check Resume + Compact. It covers checkpoint-based resumption and memory compaction. - **Using LLMs without the agent loop?** See Structured Output. It shows how to use LLMBuilder directly with Pydantic models. - **Locking down tool capabilities?** Go to Tool Security. It demonstrates scoped tool registration and policy gates. Complexity progression | Level | Example | What You Learn | | ------------ | -------------------- | ---------------------------------------------------------- | | Beginner | Minimal Chat Agent | Agent + Runner basics, final_text access | | Beginner | Structured Output | Direct LLM usage, Pydantic schema validation | | Intermediate | Policy + HITL | Policy engine, approval flows, interaction modes | | Intermediate | Tool Security | Tool scoping, sandbox profiles, policy gates | | Intermediate | Hooks + Middleware | Pre/post tool execution hooks, middleware chains | | Intermediate | Runtime Tools | Built-in file, search, and command tools | | Intermediate | Prompt Loader | External prompt files, template variables | | Advanced | Subagent Router | Multi-agent coordination, delegation patterns | | Advanced | Resume + Compact | Checkpointing, memory management, long-running workflows | | Intermediate | Streaming + Memory | Real-time streaming with multi-turn thread persistence | | Intermediate | Cost Monitoring | Budget limits, real-time cost tracking, batch budgets | | Advanced | MCP Client | Connect to external MCP servers, hybrid local+remote tools | | Advanced | Multi-Model Fallback | Fallback chains, circuit breakers, model tier strategies | | Advanced | Production Client | Timeout middleware, Redis connection pooling, graceful shutdown | Scenario catalog The simplest possible AFK agent. Define an agent, run it synchronously, and read the output. Start here. Gate sensitive actions through a policy engine and route approval requests to a human operator. Delegate workload to specialist subagents using a coordinator pattern. Merge outputs into a unified response. Checkpoint a run, resume it later from the last known state, and compact memory to control storage growth. Use the LLM builder directly (without the agent loop) to get schema-validated structured responses via Pydantic. Register destructive tools with tight scoping, enforce sandbox profiles, and gate actions through policy. Attach pre-execution and post-execution hooks to tools. Chain middleware for logging, validation, and transformation. Use AFK's built-in file reading, directory listing, and command execution tools with agents. Load system prompts from external files with template variable substitution and automatic caching. Combine real-time streaming with thread-based memory for multi-turn chat UIs. Track and control agent costs using FailSafeConfig budgets and telemetry events. Connect to external MCP servers, discover tools, and combine with local tools. Configure fallback model chains for LLM resilience, circuit breakers, and cost optimization. Production-ready LLM client with timeout middleware, Redis connection pooling, and graceful shutdown handling.", + "token_count": 412 + }, + { + "id": "doc_4055659fe573", + "path": "docs/library/failure-policy-matrix.mdx", + "url": "/library/failure-policy-matrix", + "title": "Failure Policy Matrix", + "description": "How errors flow through the system \u2014 classification, handling, and escalation.", + "headings": [ + "Error classification", + "Failure decision flow", + "Failure matrix by source", + "LLM failures", + "Tool failures", + "Subagent failures", + "Infrastructure failures", + "Failure policies", + "Budget-triggered limits", + "Next steps" + ], + "content_sha256": "3e2a501934ed7fabdbb4f5ccd429435c4948ed699b688168a9746b611f61f030", + "content": "AFK classifies every error and applies a policy-driven response. Understanding the failure matrix helps you configure the right behavior for your use case. Error classification Every error is classified into one of three categories: | Classification | Meaning | Default behavior | | -------------- | ---------------------------------------------- | ------------------------------ | | **Retryable** | Transient failure, may succeed on retry | Retry with exponential backoff | | **Terminal** | Permanent failure, will not recover | Stop and report error | | **Non-fatal** | Something went wrong, but the run can continue | Log warning, continue | Failure decision flow Failure matrix by source LLM failures | Error | Classification | Example | | -------------------------- | ------------------------ | ------------------------------------- | | Rate limit (429) | Retryable | \"Rate limit exceeded, retry after 2s\" | | Server error (500/502/503) | Retryable | \"Internal server error\" | | Timeout | Retryable | \"Request timed out after 60s\" | | Auth error (401/403) | Terminal | \"Invalid API key\" | | Invalid request (400) | Terminal | \"Model does not exist\" | | Circuit breaker open | Terminal (with fallback) | \"Circuit breaker open for provider\" | **Configuration:** Tool failures | Error | Classification | Example | | ----------------- | -------------- | --------------------------------------------------------- | | Validation error | Non-fatal | \"Invalid arguments\" (returned to LLM for self-correction) | | Handler exception | Configurable | \"Tool raised an error\" | | Timeout | Configurable | \"Tool exceeded 10s timeout\" | | Policy denial | Non-fatal | \"Action denied by policy\" (returned to LLM) | **Configuration:** Subagent failures | Error | Classification | Example | | --------------------- | -------------- | --------------------------------------------------- | | Subagent run failed | Configurable | \"Subagent 'researcher' completed with state=failed\" | | Subagent timeout | Configurable | \"Subagent exceeded wall time\" | | Join policy violation | Terminal | \"Required subagent failed (all_required policy)\" | **Configuration:** Infrastructure failures | Error | Classification | Example | | -------------------------- | ------------------ | ------------------------------ | | Memory backend unavailable | Non-fatal | \"Could not persist checkpoint\" | | Telemetry export failed | Non-fatal (silent) | \"OTEL exporter timed out\" | | Queue push failed | Retryable | \"Redis connection refused\" | Failure policies Any failure causes the run to fail immediately. **Use when:** All operations are critical and partial results are worse than no results. Failures are tolerated. The run completes with state=\"degraded\" instead of \"completed\". **Use when:** Partial results are better than no results (e.g., some tool fails but the agent can still answer). Failures are logged but ignored. The run continues as if nothing happened. **Use when:** The failing component is non-essential (e.g., analytics tool, optional enrichment). Budget-triggered limits When a budget limit is hit, the run is stopped immediately: | Limit | Triggered by | Run state | | -------------------- | ------------------------ | ---------------------- | | max_steps | Step count exceeded | failed or degraded | | max_tool_calls | Tool call count exceeded | failed or degraded | | max_total_cost_usd | Estimated cost exceeded | failed | | max_wall_time_s | Wall time exceeded | interrupted | Next steps Security boundaries and hardening checklist. Run lifecycle and state management.", + "token_count": 310 + }, + { + "id": "doc_2a18de009d56", + "path": "docs/library/full-module-reference.mdx", + "url": "/library/full-module-reference", + "title": "Full Module Reference", + "description": "Module inventory and responsibilities.", + "headings": [ + "Module dependencies", + "Extension points", + "`afk.agents`", + "`afk.core`", + "`afk.llms`", + "`afk.tools`", + "`afk.memory`", + "`afk.queues`", + "`afk.observability`", + "`afk.evals`", + "`afk.mcp`", + "`afk.messaging`" + ], + "content_sha256": "294699e90c8917af6690bcdb9d7d9511ab0ed5cb92595b96f6436621c88ad4e1", + "content": "The Agent Forge Kit (AFK) Python SDK is organized into top-level packages, each owning a distinct responsibility. This page summarizes the package map, key public imports, and extension boundaries. Use this reference when you need to find which package owns a capability. For field-level details, read the focused reference pages linked from the sidebar. Module dependencies The AFK architecture is organized around a few stable boundaries: - **afk.agents** owns declarative agent configuration, policy, prompts, skills, delegation metadata, and A2A contracts. - **afk.core** owns execution: runner lifecycle, streaming, interaction providers, delegation scheduling, checkpointing, memory wiring, and telemetry wiring. - **afk.llms** owns model provider adapters, runtime policies, structured output helpers, routing, caching, and LLM request/response types. - **afk.tools** owns tool definitions, registries, hooks, middleware, sandbox profiles, and prebuilt runtime tools. Extension points The framework is designed to be extended at specific protocol boundaries. **Do not subclass internal classes**. Instead, implement these protocols: - **InteractionProvider** (afk.core): Build custom human-in-the-loop interfaces (for example Slack, Discord, or web approval UIs). - **MemoryStore** (afk.memory): Add support for new databases (Mongo, DynamoDB). - **LLMProvider** (afk.llms): Integrate new model providers (local inference, exotic APIs). - **TelemetrySink** (afk.observability): Export metrics/traces to custom backends (Datadog, Honeycomb). --- afk.agents Agent definitions, lifecycle types, delegation, policy, A2A protocol, and error hierarchy. **Sub-modules:** | Sub-module | Responsibility | | ----------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | afk.agents.core | Agent and BaseAgent definitions, ChatAgent variant. | | afk.agents.types | Runtime types: AgentResult, AgentRunEvent, AgentRunHandle, AgentState, PolicyEvent, PolicyDecision, FailSafeConfig, UsageAggregate, execution records. | | afk.agents.policy | PolicyEngine, PolicyRule, PolicyEvaluation, policy subject inference. | | afk.agents.delegation | DelegationPlan, DelegationNode, DelegationEdge, RetryPolicy, JoinPolicy, DelegationResult. | | afk.agents.a2a | InternalA2AProtocol, A2A auth providers (AllowAllA2AAuthProvider, APIKeyA2AAuthProvider, JWTA2AAuthProvider), A2AServiceHost, delivery stores. | | afk.agents.contracts | AgentCommunicationProtocol, AgentInvocationRequest, AgentInvocationResponse, AgentDeadLetter. | | afk.agents.prompts | PromptStore, prompt file resolution, template rendering. | | afk.agents.lifecycle | Checkpoint versioning, schema migration, runtime helpers (circuit breaker, effect journal, state snapshots). | | afk.agents.skills | SkillStore, SkillDoc, SKILL.md parsing, checksum verification, and process-wide caching. | | afk.agents.security | Prompt-injection sanitization, untrusted tool-output redaction, and channel markers for trusted vs untrusted content. | | afk.agents.errors | Full error hierarchy: AgentError, AgentExecutionError, AgentCancelledError, SubagentRoutingError, etc. | **Key imports:** afk.core Runner execution engine, streaming, interaction providers, and delegation engine. **Sub-modules:** | Sub-module | Responsibility | | ---------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------- | | afk.core.runner | Runner class with run(), run_sync(), run_handle(), run_stream(), resume(), compact_thread(). RunnerConfig for safety and behavior defaults. | | afk.core.streaming | AgentStreamHandle, AgentStreamEvent, stream event constructors (text_delta, tool_started, stream_completed). | | afk.core.interaction | InteractionProvider protocol, HeadlessInteractionProvider, InMemoryInteractiveProvider. | | afk.core.runtime | DelegationEngine, DelegationPlanner, DelegationScheduler, backpressure and graph validation. | **Key imports:** afk.llms LLM client builder, provider registry, runtime policies, caching, routing, middleware, and type definitions. **Sub-modules:** | Sub-module | Responsibility | | --------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------ | | afk.llms.builder | LLMBuilder -- fluent builder for constructing LLM client instances. | | afk.llms.providers | Provider registry: OpenAIProvider, LiteLLMProvider, AnthropicAgentProvider, register_llm_provider(). | | afk.llms.runtime | LLMClient with production policies: RetryPolicy, TimeoutPolicy, RateLimitPolicy, CircuitBreakerPolicy, HedgingPolicy, CachePolicy. | | afk.llms.routing | Router registry: create_llm_router(), register_llm_router(). | | afk.llms.cache | Cache backends: create_llm_cache(), in-memory and Redis implementations. | | afk.llms.middleware | MiddlewareStack for request/response transformation pipelines. | | afk.llms.types | LLMRequest, LLMResponse, Message, ToolCall, Usage, streaming event types. | | afk.llms.config | LLMConfig dataclass for model configuration. | | afk.llms.profiles | Production/development profile presets. | | afk.llms.errors | LLMError, LLMTimeoutError, LLMRetryableError, etc. | **Key imports:** afk.tools Tool definition, registry, hooks, middleware, security, and prebuilt runtime tools. **Sub-modules:** | Sub-module | Responsibility | | --------------------- | --------------------------------------------------------------------------------------- | | afk.tools.core | tool decorator, ToolSpec, ToolContext, ToolResult, ToolRegistry. | | afk.tools.security | SandboxProfile, SandboxProfileProvider, SecretScopeProvider, argument validation. | | afk.tools.prebuilts | Built-in runtime tools (file read, directory list, command execution, skill tools). | | afk.tools.registry | Shared registry and registry middleware helpers. | **Key imports:** afk.memory Pluggable memory stores, compaction, retention policies, long-term memory, vector search, and thread state persistence. **Sub-modules:** | Sub-module | Responsibility | | ----------------------- | ----------------------------------------------------------------------------------------------------------------------------- | | afk.memory.store | MemoryStore abstract base class, MemoryCapabilities declarations. | | afk.memory.types | MemoryEvent, LongTermMemory, JsonValue, retention policy models. | | afk.memory.lifecycle | apply_event_retention(), apply_state_retention(), compact_thread_memory(), MemoryCompactionResult. | | afk.memory.vector | cosine_similarity(), vector scoring utilities for embedding-based search. | | afk.memory.factory | create_memory_store_from_env() factory for environment-based backend selection. | | afk.memory.adapters | Backend implementations: InMemoryMemoryStore, SQLiteMemoryStore, PostgresMemoryStore, RedisMemoryStore. | **Key imports:** afk.queues Task queue abstraction, worker lifecycle, failure classification, and metrics. **Sub-modules:** | Sub-module | Responsibility | | ---------------------- | --------------------------------------------------- | | afk.queues.base | Base queue types and enqueue/dequeue contracts. | | afk.queues.contracts | Queue protocol definitions. | | afk.queues.factory | Queue backend factory for creating queue instances. | | afk.queues.worker | Worker lifecycle management and task dispatch. | | afk.queues.metrics | Queue-level operational metrics. | **Key imports:** afk.observability Telemetry collection, projection, and export for runtime monitoring. **Sub-modules:** | Sub-module | Responsibility | | ------------------------------ | ---------------------------------------------------------------------------------------- | | afk.observability.models | RunMetrics dataclass with aggregated run statistics. | | afk.observability.contracts | Span and metric name constants (SPAN_AGENT_RUN, METRIC_AGENT_LLM_CALLS_TOTAL, etc.). | | afk.observability.projectors | Projection functions that transform run results into RunMetrics. | | afk.observability.exporters | ConsoleRunMetricsExporter and other output formatters. | | afk.observability.backends | Telemetry sink creation (create_telemetry_sink). | **Key imports:** afk.evals Eval suite execution, assertion contracts, budget constraints, and report serialization. **Sub-modules:** | Sub-module | Responsibility | | --------------------- | ------------------------------------------------------------------------------------------ | | afk.evals.suite | run_suite() and arun_suite() entrypoints. | | afk.evals.executor | Single-case execution via run_case() and arun_case(). | | afk.evals.models | EvalCase, EvalCaseResult, EvalSuiteConfig, EvalSuiteResult, EvalAssertionResult. | | afk.evals.contracts | EvalAssertion, AsyncEvalAssertion, EvalScorer protocols. | | afk.evals.budgets | EvalBudget dataclass and evaluate_budget() function. | | afk.evals.reporting | suite_report_payload(), write_suite_report_json(). | | afk.evals.golden | write_golden_trace(), compare_event_types() for deterministic trace comparison. | **Key imports:** afk.mcp Model Context Protocol server implementation, tool store, and server registration. **Sub-modules:** | Sub-module | Responsibility | | ---------------- | --------------------------------------------------------------- | | afk.mcp.server | MCP protocol handler. | | afk.mcp.store | Tool store registry and utility functions for MCP tool loading. | **Key imports:** afk.messaging Protocol-first agent-to-agent messaging exports. **Sub-modules:** | Sub-module | Responsibility | | ------------------------ | ---------------------------------------------------------------------------------------- | | afk.messaging | Public re-exports for A2A contracts, envelopes, auth providers, hosts, and delivery stores. | | afk.agents.a2a | Internal implementation modules for protocol, auth, delivery, hosting, and Google adapter. | **Key imports:**", + "token_count": 834 + }, + { + "id": "doc_3ae3aaf0c12c", + "path": "docs/library/how-to-use-afk.mdx", + "url": "/library/how-to-use-afk", + "title": "How to Use AFK", + "description": "A practical adoption path from first agent to production release.", + "headings": [ + "Phase 1: narrow agent", + "Phase 2: tools and safety", + "Phase 3: production controls", + "Phase 4: release discipline", + "Common mistakes", + "Next steps" + ], + "content_sha256": "c96092176ab2a302d53853ea5fa4e97e93a3082241ca8210f0aa16ef50e803f4", + "content": "Use AFK incrementally. Start with one narrow agent, add tools only when the task needs action, and add production controls before real users depend on the system. Phase 1: narrow agent Build one agent that solves one task without tools. Move on when the agent is reliable on real examples for the narrow task. Phase 2: tools and safety Add typed tools and hard limits. Move on when all tools have Pydantic argument models, cost limits are set, and mutating operations are gated or absent. Phase 3: production controls Before shipping, add the controls that make failures diagnosable: - evals for expected behavior; - telemetry for latency, usage, errors, and tool calls; - persistent memory or queues if runs must survive process restarts; - security controls for sandboxing, secret scope, and tool output limits; - troubleshooting docs for on-call and operators. Useful pages: Test agent behavior and enforce budgets. Export metrics, traces, and run records. Understand policy gates, sandboxing, and secret isolation. Run agents through distributed workers. Phase 4: release discipline Once the agent is in production: - run evals in CI before prompt, tool, or model changes; - compare behavior across releases with golden traces where appropriate; - monitor cost per run and failure rate; - version system prompts in files; - document operator actions for approval, resume, and rollback flows. Common mistakes | Mistake | Better approach | | --- | --- | | Starting with a multi-agent system | Start with one narrow agent and split only when roles are genuinely different | | Writing untyped tools | Use Pydantic argument models for every tool | | Treating prompts as the only safety layer | Add FailSafeConfig, policy gates, sandboxing, and evals | | Hiding internals in public docs | Keep builder docs behavior-first and maintainer docs internals-first | | Shipping without run records | Export telemetry and inspect AgentResult fields | Next steps Read Building with AI for production design patterns, then Troubleshooting for common operational failures.", + "token_count": 236 + }, + { + "id": "doc_f2ae50c638a4", + "path": "docs/library/learn-in-15-minutes.mdx", + "url": "/library/learn-in-15-minutes", + "title": "Learn AFK in 15 Minutes", + "description": "A guided path through agents, tools, streaming, memory, and safety.", + "headings": [ + "1. Agent + runner", + "2. Typed tools", + "3. Streaming", + "4. Memory continuity", + "5. Safety limits", + "What to read next" + ], + "content_sha256": "1d734acb805aeb148e75286864ddd24fd7e503a8a895b294749e25cf59c849d9", + "content": "This tutorial expands the quickstart into the core workflow used by most AFK applications. Each section introduces one concept and keeps the code small enough to copy into a single file. 1. Agent + runner Key point: an agent is declarative. The runner owns execution. 2. Typed tools Key point: tool arguments are validated before your function runs. 3. Streaming Use streaming when a UI or CLI should show progress before the final result is ready. Key point: run_stream() returns an AgentStreamHandle. Consume it to receive text, tool lifecycle events, errors, and the terminal result. 4. Memory continuity Pass the same thread_id to keep conversation context attached to a thread. Key point: thread continuity is explicit. Use the same thread_id for related turns. 5. Safety limits Production agents need hard limits even when prompts and tools are well designed. Key point: limits are part of the agent contract. Set them before shipping. What to read next Agent fields, prompt resolution, subagents, skills, and MCP tools. Tool schemas, context, hooks, middleware, sandboxing, and output limits. Event types, stream handles, cancellation, and UI patterns. Evals, telemetry, security controls, queues, and deployment.", + "token_count": 142 + }, + { + "id": "doc_5ee8bbdbd8a8", + "path": "docs/library/llm-interaction.mdx", + "url": "/library/llm-interaction", + "title": "LLM Interaction", + "description": "How runner and llms runtime exchange requests and responses.", + "headings": [ + "Interaction diagram", + "Request construction", + "Simplified view of what the runner builds", + "Response handling", + "LLMRequest fields", + "LLMResponse fields", + "Streaming interaction", + "Error handling at the LLM boundary" + ], + "content_sha256": "1ea841c0c4ab463b019f66f66da07cdc0d9e893b77493001e1f6d45d0dbd18eb", + "content": "The runner communicates with LLM providers through a well-defined request/response boundary. Understanding this boundary is important for debugging model behavior, diagnosing latency issues, and implementing custom providers or middleware. This page explains how the runner constructs LLM requests, how responses are interpreted, how streaming works, and how errors are handled at the LLM boundary. Interaction diagram Each step in this flow is described in detail below. Request construction At each step of the agent loop, the runner builds an LLMRequest and sends it to the configured LLM provider. Here is what goes into the request: **Model resolution.** The agent's model field (e.g., \"gpt-4.1-mini\") is resolved through the model resolution chain. This maps the requested model name to a provider, adapter, and normalized model identifier. Custom model_resolver functions can override this mapping. **Message history.** The runner maintains a list of Message objects representing the conversation history. This includes: - A system message containing the agent's instructions, skill manifests, and (when enabled) an untrusted-data preamble. - The initial user message. - assistant messages from previous LLM responses. - tool messages containing the results of tool executions. **Tool definitions.** If the agent has registered tools, they are exported as OpenAI-compatible function tool definitions and included in LLMRequest.tools. The tool_choice is set to \"auto\" so the model can decide whether to call tools. **Session and checkpoint tokens.** For providers that support stateful sessions (like Anthropic's agent SDK), the runner passes session_token and checkpoint_token from previous responses to maintain session continuity. **Metadata.** The request includes metadata about the run (run_id, thread_id, agent_name) and content channel markers that help the provider distinguish trusted system content from untrusted tool output. Response handling The LLM runtime normalizes provider-specific responses into an LLMResponse dataclass. The runner then interprets this response to decide what happens next: **If the response contains no tool calls** (resp.tool_calls is empty), the run is complete. The runner extracts resp.text as final_text, captures any resp.structured_response, and transitions to state=\"completed\". **If the response contains tool calls**, the runner enters the tool execution phase: 1. Each tool call is evaluated by the policy engine. 2. Approved tools are executed (in parallel, up to the batch limit). 3. Tool results are appended to the message history as tool messages. 4. The runner loops back to make another LLM call with the updated history. **Usage tracking.** The LLMResponse.usage field contains token counts (input_tokens, output_tokens, total_tokens). The runner accumulates these into UsageAggregate for cost estimation and budget enforcement. **Session continuity.** If the response includes updated session_token or checkpoint_token, the runner stores these for the next request. LLMRequest fields | Field | Type | Purpose | | --- | --- | --- | | model | str | Normalized model identifier. | | request_id | str | Unique request ID for tracing. | | messages | list[Message] | Conversation history. | | tools | list[dict] or None | OpenAI-format tool definitions. | | tool_choice | str or None | Tool selection strategy (\"auto\", \"none\", or specific tool). | | max_tokens | int or None | Maximum response tokens. | | temperature | float or None | Sampling temperature. | | top_p | float or None | Nucleus sampling parameter. | | stop | list[str] or None | Stop sequences. | | idempotency_key | str or None | Key for request deduplication. | | session_token | str or None | Provider session continuity token. | | checkpoint_token | str or None | Provider checkpoint continuity token. | | timeout_s | float or None | Request-level timeout in seconds. | | metadata | dict | Run context metadata. | LLMResponse fields | Field | Type | Purpose | | --- | --- | --- | | text | str | Model-generated text content. | | tool_calls | list[ToolCall] | Requested tool invocations. | | finish_reason | str or None | Why the model stopped generating (\"stop\", \"tool_calls\", etc.). | | usage | Usage | Token counts: input_tokens, output_tokens, total_tokens. | | structured_response | dict or None | Parsed structured output when using response_model. | | session_token | str or None | Updated session token for next request. | | checkpoint_token | str or None | Updated checkpoint token for next request. | Streaming interaction For real-time UIs, the runner supports streaming via runner.run_stream(). The streaming path works differently from the batch path: The stream produces AgentStreamEvent instances that include: - text_delta -- incremental text (provider stream deltas, or fallback chunking for non-streaming providers). - step_started -- signals a new step in the agent loop. - tool_started / tool_completed -- tool lifecycle events. - error -- error notification. - completed -- terminal event containing the final AgentResult. Error handling at the LLM boundary Errors at the LLM boundary are classified and handled according to the failure policy matrix: **Retryable errors** (timeouts, rate limits, server errors) are retried with exponential backoff. The runner supports a fallback model chain: if the primary model fails after retries, it tries the next model in FailSafeConfig.fallback_model_chain. **Terminal errors** (auth failures, invalid payloads) are not retried. The llm_failure_policy determines what happens next: - \"fail\" -- the run aborts with state=\"failed\". - \"degrade\" -- the run terminates with state=\"degraded\" and the error message as final_text. **Circuit breaker** protection prevents cascading failures. After breaker_failure_threshold consecutive failures to the same model+provider, the circuit opens and subsequent calls fail fast until the cooldown expires. **Policy denial** of LLM calls is handled before the call is made. If the policy engine denies an LLM call, the runner applies the llm_failure_policy without ever contacting the provider.", + "token_count": 607 + }, + { + "id": "doc_e34fe24f9585", + "path": "docs/library/mcp-server.mdx", + "url": "/library/mcp-server", + "title": "MCP Server", + "description": "Expose and consume tools via the Model Context Protocol.", + "headings": [ + "Architecture", + "Expose tools via MCP server", + "Consume tools from external MCP servers", + "Security", + "MCP server vs A2A", + "Next steps" + ], + "content_sha256": "92cdc3393c943799a3a2ff6409f04605595a4b4e78bc36eb42d27cead0181fe3", + "content": "The Model Context Protocol (MCP) is an open standard for sharing tools between AI systems. AFK supports both **exposing** your tools via an MCP server and **consuming** tools from external MCP servers. Architecture Expose tools via MCP server Make your AFK tools available to any MCP-compatible client: Consume tools from external MCP servers Discover and use tools from any MCP server: **MCP tools are transparent.** Once attached to an agent, MCP tools behave exactly like local tools \u2014 same validation, same policy gates, same telemetry. The agent doesn't know (or care) whether a tool is local or remote. Security Require auth tokens for all MCP connections: Configure allowed origins for browser-based clients: Apply policy rules to MCP-exposed tools: Limit request rates per client: **Always authenticate MCP servers in production.** An unauthenticated server exposes your tools to anyone who can reach the endpoint. MCP server vs A2A | Feature | MCP Server | A2A | | --------------- | ---------------------------- | ---------------------------- | | **Shares** | Individual tools | Full agents | | **Protocol** | MCP standard | AFK A2A protocol | | **Use case** | Tool sharing between systems | Agent-to-agent communication | | **Client sees** | Tool schemas and results | Agent responses | | **Interop** | Any MCP client | AFK agents | Next steps Share full agents across systems. Full security architecture.", + "token_count": 162 + }, + { + "id": "doc_0383dbf277ba", + "path": "docs/library/memory.mdx", + "url": "/library/memory", + "title": "Memory", + "description": "Persist conversation state, resume runs, and compact threads.", + "headings": [ + "Quick start: multi-turn conversation", + "What gets stored", + "State lifecycle", + "Resume interrupted runs", + "Start a run that might be long", + "If interrupted, resume later", + "Compact long threads", + "Memory backends", + "Connection pooling for Redis", + "Use the pool with RedisMemoryStore", + "Environment-based selection", + "Custom backends", + "Long-term memory", + "Store a long-term memory", + "Retrieve memories", + "Vector search", + "Search by embedding similarity", + "Text search", + "Design guidelines", + "Next steps" + ], + "content_sha256": "6df7c441a722d1ed17a8489a0f085e0a05f8e9dd2829cad72addc5a7a93bfec1", + "content": "AFK's memory system persists conversation state across runs. Use it for multi-turn conversations, run resumption after interrupts, long-term knowledge retention, and vector-based semantic search. Quick start: multi-turn conversation The thread_id links runs into a conversation. AFK automatically persists messages between runs. What gets stored | Record type | What it contains | When it's written | | ------------------ | ---------------------------------------------------------- | --------------------------------------- | | **Event** | User messages, assistant responses, tool calls and results | After each run step | | **Checkpoint** | Full run state at a point in time | At step boundaries (pre-LLM, post-tool) | | **State (KV)** | Checkpoint pointers, effect journal, background tool state | During and after runs | | **Long-term memory** | Persistent knowledge with optional embeddings | Via upsert_long_term_memory | **What's NOT stored automatically:** Raw LLM provider responses or internal framework temporaries. Only conversation-visible records and explicit state writes are persisted. State lifecycle Resume interrupted runs If a run is interrupted (crash, timeout, pause for approval), resume from the last checkpoint: **Checkpoints are written at key boundaries:** before each LLM call, after each tool batch, and after each step completes. On resume, completed tool calls are replayed from the effect journal \u2014 no duplicate side effects. Compact long threads Over time, conversation threads grow and consume tokens. Use compaction to trim old events: Compaction applies retention rules: protected event types are preserved first, then the most recent remaining events fill the budget. Memory backends AFK ships with four backends. All implement the MemoryStore protocol. State lives in process memory. Fast, no setup, but lost on restart. **Use for:** Development, testing, short-lived scripts. Persistent local storage with JSON serialization and local vector search. Features: WAL mode, text search, vector similarity search (cosine), atomic upsert. **Use for:** Local development with persistence, single-process deployments. Production-grade backend with pgvector support for vector search. **Use for:** Production multi-process deployments. In-memory store backed by Redis for shared state across processes. **Use for:** Shared state across workers, ephemeral but durable-enough sessions. Connection pooling for Redis For production Redis deployments, use connection pooling for better performance: Environment-based selection Set environment variables to auto-select a backend without code changes: The runner falls back to in-memory if the configured backend fails to initialize. Custom backends Implement the MemoryStore abstract class to add support for any database: Declare capabilities to tell the framework which features your backend supports. Features like vector search are only used when the backend declares support. Long-term memory Beyond conversation events, AFK supports persistent long-term memories scoped per user and purpose: Vector search Backends that support vector search (SQLite, Postgres) can find semantically similar memories: Text search All backends support basic text search across memory content: Design guidelines - **Always use thread_id for conversations.** Without it, each run starts fresh. - **Compact threads proactively.** Don't wait until you hit token limits. A good rule: compact when the thread exceeds ~500 events. - **Use checkpoints for long-running agents.** If a run might take minutes, checkpoints let you resume on failure. - **Don't store secrets in memory.** Thread events are persisted and may be readable. - **Choose the right backend.** In-memory for dev, SQLite for local persistence, Postgres/Redis for production. - **Use scopes for long-term memory.** Organize memories by purpose (preferences, knowledge, history) to keep queries efficient. Next steps Resume and compact APIs on the Runner. Template prompts with context from memory.", + "token_count": 430 + }, + { + "id": "doc_50ac650fd8f6", + "path": "docs/library/mental-model.mdx", + "url": "/library/mental-model", + "title": "Mental Model", + "description": "Three coordinated loops that drive every AFK agent.", + "headings": [ + "The three loops", + "Think in contracts", + "Decision tree: how complex should my system be?", + "What success looks like" + ], + "content_sha256": "cf294e0c707755bfe6bfc555054eaefbfb62e65f3ea92ecd9e2b4cfec5bb792c", + "content": "AFK agents execute through three coordinated loops \u2014 **Decision**, **Execution**, and **Assurance**. Understanding these loops helps you reason about agent behavior, debug issues, and design systems that scale predictably. The three loops The Decision Loop is the model's turn. On each step: 1. The runner sends the conversation history + tool schemas to the LLM 2. The LLM decides whether to respond with text (done) or request tool calls (continue) 3. If tool calls are requested, they flow to the Execution Loop **You control this with:** agent instructions, model choice, tool availability The Execution Loop handles every tool call: 1. **Validate** arguments against the Pydantic schema 2. **Policy gate** \u2014 allow, deny, or defer for human approval 3. **Execute** the handler (with hooks and middleware) 4. **Sanitize** the output (truncate, strip injection vectors) 5. **Return** the result to the Decision Loop **You control this with:** tool definitions, policy rules, sandbox profiles The Assurance Loop runs continuously, enforcing limits on both other loops: - **Step count** \u2014 stops the agent after N iterations - **Tool call count** \u2014 prevents excessive tool usage - **Cost budget** \u2014 stops if estimated cost exceeds the limit - **Wall time** \u2014 hard timeout on the entire run - **Failure classification** \u2014 retryable, terminal, or non-fatal **You control this with:** FailSafeConfig Think in contracts AFK is built on a contract-first design. Every interaction between components is defined by typed data structures: | Boundary | Contract | What flows | | ------------------ | ---------------------------------------------------- | --------------------------------------- | | Runner \u2192 LLM | LLMRequest / LLMResponse | Messages, tool schemas, model responses | | Runner \u2192 Tool | ToolCall / ToolResult | Validated arguments, execution output | | Runner \u2192 Subagent | AgentInvocationRequest / AgentInvocationResponse | Delegate task and receive result | | Runner \u2192 Memory | Checkpoint records | Conversation state for resume/replay | | Runner \u2192 Telemetry | AgentRunEvent, RunMetrics | Spans, metrics, audit trail | **Contracts are Pydantic models.** This means every boundary is validated at runtime \u2014 malformed data causes clear errors, not silent bugs. When you see a validation error, it's AFK telling you exactly where the contract was violated. Decision tree: how complex should my system be? Not sure what to build? Start at the top and follow the path that matches your use case. > [!TIP] > **Start at Level 1.** Only move up when you have clear evidence that your current level isn't enough. Each level adds complexity that you need to manage and test. What success looks like A mature AFK implementation exhibits these properties: - **Every tool has a Pydantic model** \u2014 no untyped arguments - **Every run has cost limits** \u2014 max_total_cost_usd is always set - **Policy gates protect mutations** \u2014 dangerous actions require approval - **Evals cover core behaviors** \u2014 regression tests catch prompt drift - **Observability is on from day one** \u2014 even if it's just the console exporter - **Failures are classified** \u2014 the system knows what to retry and what to abort", + "token_count": 325 + }, + { + "id": "doc_75887f91c7df", + "path": "docs/library/messaging.mdx", + "url": "/library/messaging", + "title": "Internal Messaging", + "description": "Agent-to-agent messaging with delivery guarantees and idempotency.", + "headings": [ + "Message lifecycle", + "The InternalA2AEnvelope", + "Delivery behavior", + "Idempotency and correlation", + "All messages in this workflow share the same correlation_id", + "Delivery store backends", + "Next steps" + ], + "content_sha256": "e8d90b294c3ed2ebbbff257fa28d0be7ba2cce6d689a3161f2306ddf95ba1ee2", + "content": "AFK's internal messaging system lets agents communicate via structured envelopes with **at-least-once delivery** and **idempotency**. Use it when agents in the same system need to exchange data reliably. Message lifecycle The InternalA2AEnvelope Every message is wrapped in a typed envelope: | Field | Type | Purpose | | ----------------- | ---------- | -------------------------------------------- | | message_type | str | request, response, or event | | run_id | str | Run identifier for tracing | | thread_id | str | Memory thread identifier | | conversation_id | str | Cross-run conversation identifier | | payload | dict | Message data (any JSON-serializable content) | | correlation_id | str | Groups related messages in a workflow | | idempotency_key | str | Deduplication \u2014 same key = same message | | source_agent | str | Name of the sending agent | | target_agent | str | Name of the receiving agent | | metadata | dict | JSON-safe tracing or routing metadata | | timestamp_ms | int | Creation timestamp in milliseconds | Delivery behavior 1. Sender creates and submits the envelope 2. Delivery store checks the idempotency key (rejects duplicates) 3. Message is delivered to the receiver 4. Receiver processes and ACKs 5. Store marks as delivered **Result:** Message processed exactly once. 1. Delivery fails (receiver timeout, transient error) 2. Store schedules retry with exponential backoff 3. Message is redelivered (same idempotency key) 4. Receiver processes and ACKs on retry **Result:** At-least-once delivery. Receiver must be idempotent. 1. All retry attempts exhausted 2. Message moves to dead-letter queue 3. Alert generated (if configured) **Result:** Message is not lost \u2014 it's in the DLQ for manual review. Idempotency and correlation **Always set an idempotency_key.** Without it, retry deliveries can cause duplicate processing. Use a deterministic key derived from the task context (e.g., f\"{task_id}-{step_name}\"). **Correlation IDs** group related messages across a workflow. When debugging, filter by correlation_id to see the full message chain for a task. Delivery store backends Default. Fast, no setup. State lost on restart. Implement the DeliveryStore protocol for durable messaging: Next steps Cross-system agent communication. Async job processing with queue backends.", + "token_count": 226 + }, + { + "id": "doc_5a65ddab1445", + "path": "docs/library/migration.mdx", + "url": "/library/migration", + "title": "Migration Guide", + "description": "Move from LangChain, OpenAI Assistants, or custom agents to AFK.", + "headings": [ + "From LangChain", + "LangChain \u2192 AFK concepts", + "Basic agent migration", + "Key differences", + "LangChain: agent is callable", + "AFK: Agent defines what, Runner executes how", + "LangChain: function signature and docstring", + "AFK: Pydantic model for typed arguments", + "LangChain: single run() method", + "AFK: explicit sync, async, or streaming", + "Tool migration", + "Simple tool", + "Structured tool with custom logic", + "Simple tool", + "Tool with constraints", + "Memory migration", + "Configure memory backend", + "Use thread_id for conversation continuity", + "Callback \u2192 Middleware migration", + "RAG migration", + "Store documents with embeddings", + "Retrieve in tool", + "From OpenAI Assistants API", + "Assistants \u2192 AFK concepts", + "Basic migration", + "Key advantages of AFK over Assistants API", + "From custom agent code", + "Common patterns migration", + "Before: Custom retry implementation", + "Before: Custom circuit breaker", + "Next steps" + ], + "content_sha256": "5918b39a7bcd88e65ba3aba1c07491fdab8b878f8eb6dea163cffe3b02ab8b7f", + "content": "This guide helps you migrate existing agent code from other frameworks to AFK. From LangChain LangChain \u2192 AFK concepts | LangChain Concept | AFK Equivalent | Key Difference | |-------------------|----------------|----------------| | ChatOpenAI | LLMBuilder | Provider-portable, typed contracts | | Agent | Agent | Config object, not runtime | | Tool | @tool decorator | Pydantic-based, typed arguments | | Chain | Runner | Explicit execution loop | | Memory | MemoryStore | Multiple backends, checkpointing | | Callback | Middleware / Hooks | Request/response interception | | LangSmith | Telemetry | Built-in OTEL support | Basic agent migration **LangChain:** **AFK:** Key differences **1. Agent is a config object, not a runtime:** **2. Tools use Pydantic for validation:** **3. Explicit execution modes:** Tool migration **LangChain tools:** **AFK equivalents:** Memory migration **LangChain memory:** **AFK memory:** Callback \u2192 Middleware migration **LangChain callbacks:** **AFK middleware:** RAG migration **LangChain retrieval:** **AFK approach:** From OpenAI Assistants API Assistants \u2192 AFK concepts | OpenAI Concept | AFK Equivalent | |----------------|----------------| | Assistant | Agent | | Thread | Memory + thread_id | | Run | Runner execution | | Message | MemoryEvent | | Tool | @tool decorator | | Function | @tool with Pydantic | | File search | Long-term memory + vector search | Basic migration **OpenAI Assistants:** **AFK:** Key advantages of AFK over Assistants API 1. **Local execution** \u2014 No API calls needed for simple tasks 2. **Portable** \u2014 Switch LLM providers without code changes 3. **Debuggable** \u2014 Step through agent logic locally 4. **Testable** \u2014 Run evals locally in CI 5. **Controllable** \u2014 Full access to prompts, tools, and behavior From custom agent code Common patterns migration **Custom retry logic:** **AFK:** **Custom circuit breaker:** **AFK:** Next steps Build your first AFK agent in 5 minutes. Understand AFK's design philosophy. Complete API documentation. Runnable examples for every feature.", + "token_count": 208 + }, + { + "id": "doc_78a8125d2c7e", + "path": "docs/library/observability.mdx", + "url": "/library/observability", + "title": "Observability", + "description": "Built-in telemetry with spans, metrics, and exporters.", + "headings": [ + "Setup in one line", + "Console output (development)", + "OpenTelemetry (production)", + "JSON lines (log files)", + "Telemetry pipeline", + "RunMetrics reference", + "Choosing an exporter", + "Telemetry spans", + "Alerting recommendations", + "Next steps" + ], + "content_sha256": "125c3f894dc8006a91e85aa44be8559452e56c5c7984b6cc2fab73dc99763e43", + "content": "AFK includes a telemetry pipeline that captures every agent run event \u2014 LLM calls, tool executions, state transitions, and performance metrics. Export to the console for development, JSON for log aggregation, or OpenTelemetry for production. Setup in one line Telemetry pipeline RunMetrics reference Every completed run produces a RunMetrics object: | Metric | Type | Description | | --------------------- | ------- | ---------------------------------- | | total_steps | int | Number of agent loop iterations | | total_llm_calls | int | Number of LLM API calls | | total_tool_calls | int | Number of tool executions | | total_tokens | int | Total tokens (prompt + completion) | | total_cost_usd | float | Estimated cost in USD | | wall_time_s | float | Total run duration in seconds | | first_token_ms | float | Time to first token (streaming) | | tool_latency_p50_ms | float | Median tool execution latency | | tool_latency_p99_ms | float | 99th percentile tool latency | | error_count | int | Number of errors during the run | Choosing an exporter Human-readable output to stdout. Best for development and debugging. Structured output for log aggregation (ELK, Datadog, etc.). Each event is a JSON line: Export spans and metrics to any OTEL-compatible backend (Jaeger, Grafana, Honeycomb, etc.). **Use for:** Production deployments with existing observability infrastructure. Telemetry spans AFK creates spans for key operations: Spans capture timing, success/failure, and metadata. In OTEL mode, these map directly to traces visible in your observability dashboard. Alerting recommendations | Alert | Condition | Severity | | -------------------- | -------------------------------------------- | ---------- | | Error rate spike | error_count / total_runs > 5% over 5 min | **High** | | LLM latency spike | p99 > 10s for 5 min | **Medium** | | Cost anomaly | daily_cost > 2x rolling average | **High** | | Tool failure rate | tool_failures / tool_calls > 10% for 5 min | **Medium** | | Circuit breaker open | Any LLM circuit breaker trips | **High** | **Start with the console exporter** even in staging. It costs nothing and gives you immediate visibility into agent behavior. Switch to OTEL when you have a monitoring stack. Next steps Behavioral testing for agent quality. Security boundaries and hardening.", + "token_count": 223 + }, + { + "id": "doc_f18f523b6ecc", + "path": "docs/library/overview.mdx", + "url": "/library/overview", + "title": "What is AFK?", + "description": "The short mental model for building and maintaining AFK agents.", + "headings": [ + "The three core objects", + "When AFK is a good fit", + "Builder path", + "Maintainer path", + "Source-of-truth rules", + "Next steps" + ], + "content_sha256": "3d4dbe025de523cf7ac36a6ca13721c14fd9857f6f45bb8f3acebae1cb31452a", + "content": "AFK is an agent runtime for Python applications. It is designed for teams that need agent behavior to be typed, observable, testable, and bounded by explicit controls. The core idea is simple: The three core objects | Object | What it owns | What it does not own | | --- | --- | --- | | Agent | Name, model, instructions, tools, subagents, skills, MCP servers, defaults, fail-safe limits | Network calls, event loops, persistence, telemetry export | | Runner | Execution loop, tool dispatch, streaming, memory, checkpointing, policy/HITL, telemetry | Agent identity or instructions | | AgentResult | Final text, terminal state, tool/subagent records, usage aggregate, cost, run/thread ids | Future execution | This separation is the main design rule. Agents describe behavior. Runners execute behavior. Runtime subsystems provide capabilities. When AFK is a good fit Use AFK when your agent will: - call tools or external systems; - run for more than one step; - need cost, time, step, or tool-call limits; - stream progress to a UI; - persist memory or resume a run; - require evals, telemetry, or production incident debugging; - coordinate specialist subagents. A direct LLM provider SDK may be simpler for one-off single-turn text generation. AFK is useful when the agent needs operational structure. Builder path If you are building an app with AFK, read in this order: 1. Quickstart for the smallest complete agent. 2. Learn AFK in 15 Minutes for the guided path. 3. Agents, Runner, and Tools for the core building blocks. 4. Building with AI, Evals, and Observability before production. Maintainer path If you are changing AFK itself, read in this order: 1. Developer Guide for local setup, commands, and docs workflow. 2. Architecture for package boundaries. 3. Public API Rules before changing exports or examples. 4. Tested Behaviors before changing runner, tools, LLM runtime, memory, queues, or telemetry. Source-of-truth rules - Public examples import from afk.*, never src.afk.*. - Runner is imported from afk.core. - User-facing docs should explain behavior before internals. - Maintainer docs may reference internal modules, but must identify the public contract affected by a change. - Generated agent-facing docs and skill indexes must be refreshed when navigation, examples, or skill references change. Next steps Build one agent and one typed tool. Set up the repo and understand the contributor workflow.", + "token_count": 272 + }, + { + "id": "doc_f5f256549d84", + "path": "docs/library/performance.mdx", + "url": "/library/performance", + "title": "Performance", + "description": "Improve AFK latency, throughput, and cost without relying on internal APIs.", + "headings": [ + "Latency", + "Tool execution", + "Throughput", + "Cost", + "Memory", + "Measurement", + "Checklist" + ], + "content_sha256": "4ded523bc48148cc20cacc7af3c5ac32a07a19a6376e3d8d69bb08c2aa878c6f", + "content": "Performance work in AFK usually comes from four levers: choosing the right model, reducing unnecessary tool/LLM calls, keeping memory bounded, and moving long-running work into queues. Latency Use the smallest model that can reliably handle the task, and reserve larger models for tasks that need deeper reasoning. Other latency controls: - keep system prompts short and specific; - make I/O-bound tools async; - avoid tools for information already present in context; - stream user-facing runs with runner.run_stream(...); - set tight max_steps, max_llm_calls, and max_wall_time_s limits. Tool execution Tools are often the slowest part of a run. Keep them typed, narrow, and bounded. Tool guidance: - validate inputs with Pydantic models; - enforce timeouts in external clients; - return compact JSON-safe payloads; - truncate or summarize large external responses before returning them; - use RunnerConfig(tool_output_max_chars=...) as a final bound. Throughput Use async runner APIs for services and workers. For durable background work, use task queues instead of keeping HTTP requests open. See Task Queues. Cost Set cost and loop limits on every production agent. Read cost from the terminal result: Memory Long threads increase prompt size and storage. Use explicit thread ids and compact retained state when threads grow. Choose the memory backend by deployment shape: | Backend | Use case | | --- | --- | | In-memory | Tests and local experiments | | SQLite | Single-process local or small deployments | | Redis | Shared state across processes | | Postgres | Persistent production storage and vector search | Configure backends with environment variables or pass a public MemoryStore implementation to Runner(memory_store=...). Measurement Measure from AgentResult first: For production, export telemetry through Observability and track latency, token usage, tool failures, degraded runs, and cost per run. Checklist - Use async runner APIs in servers and workers. - Stream user-facing runs. - Keep prompts and tool outputs compact. - Set fail-safe limits and cost budgets. - Compact long-running threads. - Move durable background work into queues. - Monitor token usage, tool count, state, and cost per run.", + "token_count": 250 + }, + { + "id": "doc_3eeb75d383b8", + "path": "docs/library/public-imports-and-function-improvement.mdx", + "url": "/library/public-imports-and-function-improvement", + "title": "Public API Rules", + "description": "Maintainer rules for preserving AFK's public import contract.", + "headings": [ + "Contract", + "Rules for maintainers", + "Preferred examples", + "Imports to avoid in public docs", + "Change checklist", + "Search commands" + ], + "content_sha256": "4ad0dbc7fa46f0049e5638a702fa2d73dbf1da35cb09099c277e41dd1fefbabc", + "content": "This page is for AFK maintainers. It explains how to change public exports without making downstream code or docs confusing. For the user-facing import table, see API Reference. Contract The public API is the set of names exported by package-level __init__.py files: - afk.agents - afk.core - afk.tools - afk.llms - afk.memory - afk.queues - afk.mcp - afk.messaging - afk.observability - afk.evals Public docs and examples should import from these package surfaces. They should not use src.afk imports or deep implementation modules such as afk.core.runner.api. Rules for maintainers 1. If a downstream user should import a symbol, export it from the package-level __init__.py. 2. If a symbol is not exported, do not use it in builder docs or examples. 3. Keep Agent and Runner separate: Agent comes from afk.agents; Runner comes from afk.core. 4. Prefer protocols, dataclasses, Pydantic models, and explicit error classes for public contracts. 5. When removing or renaming a public symbol, update migration docs and tests in the same change. 6. When changing a public constructor, update API Reference, Configuration Reference, examples, and generated agent-facing docs. Preferred examples Imports to avoid in public docs | Avoid | Prefer | | --- | --- | | src.afk.agents.Agent | afk.agents.Agent | | afk.agents.core.base.Agent | afk.agents.Agent | | afk.core.runner.api.RunnerAPIMixin | afk.core.Runner | | afk.tools.core.decorator.tool | afk.tools.tool | | afk.llms.builder.LLMBuilder | afk.llms.LLMBuilder | Deep imports are acceptable in internal tests only when the test is specifically covering an internal unit. Integration tests and examples should exercise the public surface. Change checklist Before merging a public API change: - Update the relevant package __all__. - Add or update tests that import through the public package. - Update user-facing docs if a builder would see the changed behavior. - Update maintainer docs if an invariant or subsystem boundary changed. - Run PYTHONPATH=src pytest -q or targeted tests for the affected subsystem. - Regenerate agent-facing docs with ./scripts/build_agentic_ai_assets.sh when docs, examples, skill metadata, or navigation changes. Search commands The last command intentionally finds deep imports for review. Some maintainer references may be valid, but builder docs should avoid them.", + "token_count": 277 + }, + { + "id": "doc_bf91f8d5da74", + "path": "docs/library/quickstart.mdx", + "url": "/library/quickstart", + "title": "Quickstart", + "description": "Build one AFK agent with one typed tool.", + "headings": [ + "Prerequisites", + "1. Define an agent", + "2. Add one typed tool", + "3. Read the result", + "4. Keep going" + ], + "content_sha256": "898fc17c3d87708f8ce6850453a470a6fad5eb53aa22488f66cd3b31e8ed4886", + "content": "This page is the shortest useful AFK path: install the package, define an agent, attach one typed tool, and run it. Prerequisites - Python 3.13+ - An LLM provider key, such as OPENAI_API_KEY When working from this repository instead of an installed package: 1. Define an agent Agent stores configuration. Runner executes the run. AgentResult.final_text is the assistant response. 2. Add one typed tool Tools are Python functions with Pydantic argument models. AFK turns the model into a tool schema, validates model-provided arguments, executes the function, and feeds the result back into the agent loop. 3. Read the result Common fields on AgentResult: | Field | Meaning | | --- | --- | | final_text | Final assistant text | | state | Terminal state such as completed, failed, cancelled, or degraded | | run_id | Unique id for this run | | thread_id | Conversation/thread id used by memory | | tool_executions | Ordered records for tool calls | | subagent_executions | Ordered records for subagent calls | | usage_aggregate | Aggregated token usage | | total_cost_usd | Estimated total run cost when available | 4. Keep going Add streaming, memory, and safety controls. Find complete snippets for common scenarios. Understand the agent configuration object. Understand sync, async, and streaming execution.", + "token_count": 135 + }, + { + "id": "doc_f54217a0f2cf", + "path": "docs/library/run-event-contract.mdx", + "url": "/library/run-event-contract", + "title": "Run Event Contract", + "description": "Runtime event schema and event consumption guidance.", + "headings": [ + "Event stream model", + "Event reference", + "AgentRunEvent structure", + "Consuming events", + "Pattern: event-type branching", + "Forward compatibility" + ], + "content_sha256": "a4e2b6d7119af19a8eafb23eb7f8f84bf514594c4d2dd076e5bb6983d4097b19", + "content": "Every AFK agent run produces a stream of AgentRunEvent instances that describe what happened during execution. These events form the run's audit trail -- they tell you when the run started, when LLM calls were made, when tools executed, when policy decisions were rendered, and how the run terminated. Understanding the event contract is essential for building real-time UIs, logging pipelines, eval assertions, and debugging tools. This page documents every event type, explains when each fires, describes the data it carries, and provides patterns for consuming events safely. Event stream model Every run begins with run_started and ends with exactly one terminal event: run_completed, run_failed, run_interrupted, or run_cancelled. Between those boundaries, the runner emits step, LLM, tool, policy, and subagent events in the order they occur. Event reference | Event Type | When It Fires | Key Fields in data | | --- | --- | --- | | run_started | At the beginning of a new or resumed run. | agent_name, resumed (bool) | | step_started | At the start of each step iteration. | step (int), state | | llm_called | Before sending a request to the LLM provider. | model, provider | | llm_completed | After receiving the LLM response. | tool_call_count, finish_reason, text | | tool_batch_started | Before executing a batch of tool calls from one LLM response. | tool_call_count, tool_names, tool_call_ids | | tool_completed | After each individual tool finishes executing. | tool_name, tool_call_id, success (bool), output, error, agent_name, agent_depth, agent_path | | tool_deferred | Tool accepted and moved to background processing. | tool_name, tool_call_id, ticket_id, status, summary, resume_hint | | tool_background_resolved | Deferred tool completed successfully. | tool_name, tool_call_id, ticket_id, output | | tool_background_failed | Deferred tool failed or expired. | tool_name, tool_call_id, ticket_id, error | | policy_decision | After the policy engine evaluates a tool, LLM, or subagent event. | event_type, action, reason, policy_id, matched_rules | | subagent_started | Before dispatching a subagent invocation. | subagent_name, correlation_id | | subagent_completed | After a subagent finishes (success or failure). | subagent_name, success, latency_ms, error (if failed) | | text_delta | During streamed runs when incremental model text arrives. | delta | | warning | When a non-fatal issue occurs (memory fallback, dead letters, etc.). | Varies by warning type | | run_completed | When the run reaches successful terminal state. | None (terminal) | | run_failed | When the run terminates due to an unrecoverable error. | error message in event.message | | run_interrupted | When the run is interrupted by the caller or by timeout. | Interruption reason in event.message | | run_cancelled | When the run is cancelled before completion. | None (terminal) | AgentRunEvent structure Each event is an AgentRunEvent dataclass with the following fields: | Field | Type | Description | | --- | --- | --- | | type | str | Event type identifier (see table above). | | run_id | str | Unique run identifier. | | thread_id | str | Thread identifier for memory continuity. | | state | str | Current run state when the event was emitted. | | step | int | Current step number (0 if not yet in a step). | | message | str or None | Human-readable description of what happened. | | data | dict or None | Structured payload with event-specific fields. | Consuming events The primary way to consume events is through the run handle's events async iterator: Pattern: event-type branching The recommended consumption pattern is a simple if/elif chain that branches on event.type. This is explicit, readable, and easy to extend: Forward compatibility The event contract is append-only. New event types may be added in future releases, but existing event types will not be removed or have their data fields changed. Your event consumer should always handle unknown event types gracefully. The simplest approach is a default branch that logs or ignores unknown types: This ensures your code continues to work when AFK adds new event types without requiring a code update.", + "token_count": 434 + }, + { + "id": "doc_567be1281e2f", + "path": "docs/library/security-model.mdx", + "url": "/library/security-model", + "title": "Security Model", + "description": "Security boundaries, policy engine, and production hardening.", + "headings": [ + "Security boundaries", + "Default posture", + "Production hardening checklist", + "Secret isolation", + "Threat model overview", + "Next steps" + ], + "content_sha256": "9c2f919368fe1ccad33c5509761ad84c85f2b3ccee485a59fc99402ad5ef9694", + "content": "AFK implements security through **four boundaries** \u2014 policy engine, tool runtime, A2A/MCP bridges, and sandbox. Each boundary enforces least-privilege defaults and requires explicit opt-in for elevated permissions. Security boundaries Gate tool calls and agent actions with configurable rules. **Actions:** allow (default), deny, request_approval, request_user_input Every tool call passes through validation, policy checks, and output sanitization. **Sandbox profiles** are configured at the runner level, not per-tool: External communication requires authentication and per-caller authorization. Hard limits prevent runaway agents. Default posture AFK defaults to **least privilege**: | Setting | Default | Meaning | | ------------------------ | ---------------------------------------------------------------------------- | ------------------------------------- | | Tool policy | allow | Tools run unless explicitly denied | | Tool output sanitization | True | Output is sanitized by default | | A2A authentication | Required | No unauthenticated A2A | | MCP authentication | Required | No unauthenticated MCP | | Cost limits | None ( ) | **You must set max_total_cost_usd** | | Sandbox | None | Tools run in the host process | **Cost limits are not set by default.** Always configure max_total_cost_usd in production to prevent runaway spending. Production hardening checklist | Area | Action | Status | | -------------- | ----------------------------------------------- | ----------------------------------------- | | **Cost** | Set max_total_cost_usd on all agents | | | **Cost** | Set max_steps and max_tool_calls | | | **Policy** | Add deny rules for admin/destructive tools | | | **Policy** | Add request_approval for mutating operations | | | **Tools** | Enable sanitize_tool_output=True | | | **Tools** | Set tool_output_max_chars | | | **Tools** | Use sandbox profiles for code execution | | | **A2A/MCP** | Configure auth providers with valid tokens | | | **A2A/MCP** | Set per-caller agent access lists | | | **A2A/MCP** | Enable rate limiting | | | **Secrets** | Store API keys in environment variables | | | **Secrets** | Use secret scope isolation per tool call | | | **Monitoring** | Configure telemetry exporter (OTEL) | | | **Monitoring** | Set up alerts for error rate and cost anomalies | | Secret isolation AFK recommends isolating secrets at the environment level. Use separate environment scopes and the runner's ToolContext.metadata to control which credentials are available to each tool: Threat model overview | Threat | Mitigation | | ----------------------- | ------------------------------------------- | | **Prompt injection** | Output sanitization, input validation | | **Runaway agents** | Cost limits, step limits, wall time | | **Tool abuse** | Policy engine, sandbox profiles | | **Unauthorized access** | A2A/MCP auth, per-caller authorization | | **Secret leakage** | Secret scope isolation, output sanitization | | **Cost explosion** | max_total_cost_usd, circuit breakers | Next steps How errors flow through the system. Production playbook and anti-patterns.", + "token_count": 288 + }, + { + "id": "doc_1e46cbc6bfae", + "path": "docs/library/snippets/01_minimal_chat_agent.mdx", + "url": "/library/snippets/01_minimal_chat_agent", + "title": "01: Minimal Chat Agent", + "description": "Smallest synchronous AFK agent run.", + "headings": [ + "Line-by-line explanation", + "What AgentResult contains", + "Expected behavior" + ], + "content_sha256": "1232b15cabfce1a8b2463d495eb4dca40efebc440a22e456b95cb6e2a4af8225", + "content": "This is the simplest possible AFK agent. It demonstrates the three core concepts you need to get started: defining an Agent with a model and instructions, creating a Runner to execute it, and reading the result from final_text. If you are new to AFK, start here. Every other example builds on this foundation. Line-by-line explanation **Agent(...)** defines the agent's identity and behavior. The name is used for telemetry and logging. The model specifies which LLM to use. The instructions become the system prompt that guides the model's behavior. **Runner()** creates the execution engine. With no arguments, it uses in-memory defaults: headless interaction mode, no telemetry sink, and no policy engine. This is the fastest way to get started during development. **runner.run_sync(...)** executes the agent synchronously, blocking until the run completes. Under the hood, this creates an async event loop, runs the agent through the full lifecycle (LLM call, optional tool execution, optional subagent delegation), and returns the terminal AgentResult. The user_message is the initial prompt sent to the model. **result.final_text** contains the model's final text response. This is the primary output field on AgentResult. Always use final_text (not output_text) to access the agent's response. What AgentResult contains The AgentResult dataclass returned by run_sync includes: | Field | Type | Description | | --- | --- | --- | | final_text | str | The agent's final text response. | | state | str | Terminal state: \"completed\", \"failed\", \"cancelled\", or \"degraded\". | | run_id | str | Unique identifier for this run. | | thread_id | str | Thread identifier for memory continuity across runs. | | tool_executions | list | Records of all tool calls made during the run. | | subagent_executions | list | Records of all subagent invocations. | | usage | UsageAggregate | Token usage and cost estimates across all LLM calls. | Expected behavior When you run this example, the runner makes a single LLM call to gpt-4.1-mini with the system instructions and user message. Since no tools are registered, the model responds with text only. The run completes in one step with state=\"completed\", and final_text contains a concise definition of error budgets in SRE. No network calls, API keys, or external services are required beyond access to the specified LLM provider (configured via environment variables like OPENAI_API_KEY).", + "token_count": 251 + }, + { + "id": "doc_d6ad61a79371", + "path": "docs/library/snippets/02_policy_with_hitl.mdx", + "url": "/library/snippets/02_policy_with_hitl", + "title": "02: Policy with Human Interaction", + "description": "Route sensitive actions through policy and human approval.", + "headings": [ + "Basic example", + "Define a policy that gates destructive operations", + "Headless mode: approval requests are auto-resolved using approval_fallback", + "In headless mode with approval_fallback=\"deny\", the destructive action is blocked.", + "Interactive mode with an InteractionProvider", + "In-memory provider for testing (simulates human approval)", + "In a real application, a separate process or UI would call:", + "provider.resolve_approval(request_id, ApprovalDecision(kind=\"allow\"))", + "Headless vs interactive modes", + "How the policy flow works", + "Policy decision actions" + ], + "content_sha256": "e2c2e11ab5f307f4f1a3008e902cbd49a39845c3d0f0c1a792cf3068c1581a5f", + "content": "Human-in-the-loop (HITL) is the pattern where the agent pauses execution to request approval or input from a human operator before proceeding with a sensitive action. This is critical for any agent that can take destructive or irreversible actions -- deleting data, modifying production systems, sending communications, or spending money. AFK implements HITL through two components: a PolicyEngine that decides which actions require human intervention, and an InteractionProvider that routes the approval request to a human and returns their decision. Basic example Interactive mode with an InteractionProvider In production, you typically want a real human to review approval requests. Use interaction_mode=\"interactive\" with a custom InteractionProvider: Headless vs interactive modes AFK supports three interaction modes, configured via RunnerConfig.interaction_mode: | Mode | Behavior | When to Use | | --- | --- | --- | | \"headless\" | Approval requests are auto-resolved using approval_fallback (default: \"deny\"). No human is involved. | CI/CD pipelines, batch processing, testing, automated workflows where no human is available. | | \"interactive\" | Approval requests are routed to the configured InteractionProvider. The runner pauses until a decision is returned or approval_timeout_s expires. | Production applications with human operators, chat UIs with approval buttons, Slack-based approval workflows. | | \"external\" | Similar to interactive, but designed for use in external orchestration systems where the approval mechanism is managed outside AFK. | Enterprise systems with external approval platforms. | How the policy flow works 1. The agent calls a tool (e.g., drop_table). 2. Before executing, the runner sends a PolicyEvent to the PolicyEngine. 3. The engine evaluates all rules. If a rule matches, it returns a PolicyDecision with the configured action. 4. If the action is request_approval: - In **headless** mode: the runner auto-resolves using approval_fallback. - In **interactive** mode: the runner creates an ApprovalRequest and sends it to the InteractionProvider. Execution pauses until the provider returns an ApprovalDecision. 5. If approved (kind=\"allow\"): the tool executes normally. 6. If denied (kind=\"deny\"): the tool execution is skipped and the model receives a denial message as the tool result. 7. A policy_decision event is emitted in the run event stream for audit purposes. Policy decision actions | Action | Effect | | --- | --- | | \"allow\" | Proceed with execution. No human interaction needed. | | \"deny\" | Block execution immediately. The denial reason is reported to the model. | | \"request_approval\" | Pause and request human approval through the InteractionProvider. | | \"request_user_input\" | Pause and request freeform text input from a human operator. |", + "token_count": 262 + }, + { + "id": "doc_a2e84472f1f9", + "path": "docs/library/snippets/03_subagents_with_router.mdx", + "url": "/library/snippets/03_subagents_with_router", + "title": "03: Subagents with Router", + "description": "Delegate workload to specialist subagents and merge outputs.", + "headings": [ + "Delegation flow", + "Example", + "Define specialist subagents", + "Define the coordinator agent", + "How the coordinator pattern works", + "What subagent_executions contains", + "Subagent failure handling" + ], + "content_sha256": "40c49cfa7a9868920008a50344415cbed7990803b125758a43cb66376458cff8", + "content": "When a task is too complex for a single agent, AFK supports delegating subtasks to specialist subagents. The coordinator (or \"lead\") agent decides what to delegate, and the runner handles dispatching work to subagents, collecting their results, and feeding those results back to the coordinator for synthesis. This pattern is useful for incident response, research workflows, content pipelines, and any scenario where different aspects of a task require distinct expertise or instructions. Delegation flow Example How the coordinator pattern works 1. The **lead agent** receives the user message and decides how to delegate. It can invoke subagents through tool-like calls that the runner intercepts. 2. The **runner** dispatches each subagent invocation as a separate run. Subagents execute independently with their own instructions and model configuration. The runner manages concurrency, timeout, and failure handling for each subagent. 3. **Subagent results** are returned to the lead agent as execution records. Each record contains the subagent's output_text and optional error information. 4. The **lead agent** receives all subagent outputs and synthesizes them into a unified response. This final synthesis step is what produces the coordinator's final_text. What subagent_executions contains The AgentResult returned by the lead agent includes a subagent_executions list. Each entry is a SubagentExecutionRecord with: | Field | Type | Description | | --- | --- | --- | | subagent_name | str | Name of the subagent that was invoked. | | success | bool | Whether the subagent completed successfully. | | output_text | str or None | The subagent's response text, if it completed. | | latency_ms | float | Wall-clock execution time in milliseconds. | | error | str or None | Error message if the subagent failed. | You can inspect these records to understand what each subagent contributed: Subagent failure handling By default, subagent failure policy is continue. You can configure stricter or more resilient behavior using FailSafeConfig: With subagent_failure_policy=\"retry_then_degrade\", the lead agent receives error information for failed subagents alongside successful results and can produce a best-effort synthesis.", + "token_count": 215 + }, + { + "id": "doc_07174a8633ac", + "path": "docs/library/snippets/04_resume_and_compact.mdx", + "url": "/library/snippets/04_resume_and_compact", + "title": "04: Resume and Compact", + "description": "Resume interrupted runs from their last checkpoint and compact retained thread memory to control storage growth.", + "headings": [ + "What this snippet demonstrates", + "Resuming an interrupted run", + "How resume works internally", + "Resume method signature", + "Compacting thread memory", + "How compaction works", + "When to compact", + "Error handling", + "What to read next" + ], + "content_sha256": "1926eddc956569f4ccf828f2605bfd5848af1b4ba75eba3d35876e2235c15491", + "content": "What this snippet demonstrates Agent runs can be interrupted by timeouts, cancellations, infrastructure failures, or intentional pauses (such as waiting for human approval). When a run is interrupted, the runner persists a checkpoint containing the run's state at the point of interruption. The resume() method picks up from that checkpoint, restoring the conversation history, tool execution records, and step counter so the agent continues where it left off rather than starting from scratch. Over time, long-running threads accumulate checkpoint records, event logs, and state entries. The compact_thread() method prunes old records according to retention policies, keeping storage bounded without losing the data needed for active runs. Resuming an interrupted run How resume works internally The runner follows this sequence when resume() is called: 1. **Checkpoint lookup** -- The runner queries the memory store for the latest checkpoint matching the given run_id and thread_id. If no checkpoint exists, it raises AgentCheckpointCorruptionError. 2. **Terminal check** -- If the checkpoint already contains a terminal result (the run completed before the resume was requested), the runner returns that result immediately without re-executing. 3. **Snapshot restoration** -- The runner loads the runtime snapshot from the checkpoint, which includes the conversation message history, step counter, tool execution records, and any pending subagent state. 4. **Continued execution** -- The runner calls run_handle() internally with the restored snapshot, continuing the step loop from where it was interrupted. Resume method signature | Parameter | Type | Description | | --- | --- | --- | | agent | BaseAgent | The agent definition used for continued execution. Must match the agent that started the original run. | | run_id | str | The unique run identifier from the interrupted run. Found on result.run_id. | | thread_id | str | The thread identifier from the interrupted run. Found on result.thread_id. | | context | dict or None | Optional context overlay. Merged with the original run context. | Compacting thread memory How compaction works Compaction operates on two dimensions of stored data: - **Event retention** -- Controlled by RetentionPolicy. Removes event records older than max_age_ms. Events are the raw telemetry log entries (LLM calls, tool executions, state transitions) that accumulate over the lifetime of a thread. - **State retention** -- Controlled by StateRetentionPolicy. Removes state entries that exceed max_entries, keeping only the most recent ones. State entries include checkpoint snapshots, conversation summaries, and key-value metadata. Both policies are optional. If you omit a policy, that dimension is not compacted. The method returns a MemoryCompactionResult with counts of removed records so you can log or alert on compaction activity. When to compact - **After long conversations** -- Threads with hundreds of turns accumulate large checkpoint histories. Compact after the conversation ends or reaches a natural break point. - **On a schedule** -- Run compaction as a background task (e.g., hourly or daily) for threads that are still active but have grown large. - **Before resume** -- If you know a thread has extensive history, compacting before resume reduces the data the runner needs to load. Error handling What to read next - Memory -- Full memory architecture, checkpoint schema, and retention policies. - Core Runner -- Step loop lifecycle, state machine, and all runner API methods. - Checkpoint Schema -- Exact structure of checkpoint records stored in memory.", + "token_count": 372 + }, + { + "id": "doc_3f60413b3a06", + "path": "docs/library/snippets/05_direct_llm_structured_output.mdx", + "url": "/library/snippets/05_direct_llm_structured_output", + "title": "05: Direct LLM Structured Output", + "description": "Use afk.llms with schema-validated responses.", + "headings": [ + "Example", + "Define the output schema as a Pydantic model", + "Build an LLM client using the fluent builder", + "Make a structured request", + "The builder pattern", + "Structured output with Pydantic", + "When to use LLMBuilder vs Runner" + ], + "content_sha256": "6f54fe64ae8f7ca3c497962bf8febe155784c84d094d323bdfb99581cc086743", + "content": "Not every use case needs the full agent loop. Sometimes you want to call an LLM directly with a specific prompt and get back a structured, schema-validated response. AFK's LLMBuilder provides a fluent API for constructing LLM clients that can return Pydantic-validated objects directly, without the overhead of the agent run lifecycle. Use this pattern for classification, extraction, summarization, and any scenario where you want a single LLM call with a guaranteed output schema. Example The builder pattern LLMBuilder uses a fluent (method-chaining) API to construct an LLM client with the exact configuration you need: Each method returns the builder instance, so calls can be chained. The .build() call at the end constructs the final LLMClient with all specified settings. Available builder methods: | Method | Purpose | | --- | --- | | .provider(name) | Set the LLM provider (\"openai\", \"litellm\", \"anthropic_agent\"). | | .model(name) | Set the model identifier. | | .profile(name) | Apply a named configuration profile (\"production\", \"development\", etc.). | | .settings(settings) | Replace the loaded LLMSettings. | | .with_middlewares(stack) | Attach chat, stream, or embedding middleware. | | .with_observers(observers) | Attach LLM lifecycle observers. | | .with_cache(cache_backend) | Attach a cache backend instance or registered backend id. | | .with_router(router) | Attach a router instance or registered router id. | | .build() | Construct and return the LLMClient. | Sampling controls are request fields, not builder methods. Set them on LLMRequest, for example LLMRequest(..., temperature=0.0, max_tokens=1000). Structured output with Pydantic When you pass response_model=YourModel to client.chat(), the client instructs the LLM to return output that conforms to the model's JSON schema. The response is parsed and validated against the Pydantic model: - If the LLM returns valid structured output, resp.structured_response contains the parsed dictionary and resp.text contains the raw response. - If the LLM returns output that does not match the schema, a LLMInvalidResponseError is raised. This is powered by the LLM provider's native structured output support (e.g., OpenAI's response_format parameter) when available, with a fallback to prompt-based JSON extraction. When to use LLMBuilder vs Runner | Use Case | Approach | | --- | --- | | Single LLM call, no tools, no memory | LLMBuilder -- simpler, faster, no lifecycle overhead. | | Structured extraction or classification | LLMBuilder with response_model. | | Multi-turn conversation with tools | Runner -- provides the full agent loop with tool execution, policy, and memory. | | Subagent delegation | Runner -- only the runner supports subagent dispatch. | | Event streaming to a UI | Runner with run_stream(). | | Eval-driven development | Runner -- evals require the full AgentResult lifecycle. | Use LLMBuilder when you want precision and control over a single LLM interaction. Use Runner when you need the full agentic lifecycle.", + "token_count": 305 + }, + { + "id": "doc_43ad71a4ad0c", + "path": "docs/library/snippets/06_tool_registry_security.mdx", + "url": "/library/snippets/06_tool_registry_security", + "title": "06: Tool Registry Security", + "description": "Safe tool registration and guardrail practices.", + "headings": [ + "Read-only vs mutating tools", + "--- Read-only tool: safe, broadly permitted ---", + "--- Mutating tool: destructive, requires policy gate ---", + "Policy gate setup", + "Define policy rules that distinguish read vs write operations", + "In headless mode, the delete is auto-denied. The model sees the denial and responds accordingly.", + "Sandbox profiles for filesystem tools", + "Scoping destructive tools" + ], + "content_sha256": "a1ef90a4e46b3a7e305f3a953816f7601fc4a0b6fb3d32a17d5f5faa7ba3a2e3", + "content": "Tools are the primary way agents interact with external systems. A tool that reads data is fundamentally different from a tool that deletes resources -- and your security model should reflect this. AFK provides multiple layers of defense for tool security: scoped tool definitions with typed arguments, sandbox profiles that restrict execution capabilities, and policy gates that require human approval for destructive operations. This page demonstrates how to register tools safely, distinguish between read-only and mutating tools, and configure policy gates to protect against unintended destructive actions. Read-only vs mutating tools The most important security distinction is between tools that observe (read-only) and tools that act (mutating). Read-only tools are generally safe to allow broadly. Mutating tools should be tightly scoped and policy-gated. Notice the differences: - The read-only tool (get_resource) has a description that explicitly says \"Read-only.\" This signals to both the model and human reviewers that the tool is safe. - The mutating tool (delete_resource) has a description warning about irreversibility. This helps the model understand the severity, and helps policy rules identify destructive operations. Policy gate setup Use a PolicyEngine to require human approval before any mutating tool executes: Sandbox profiles for filesystem tools For tools that interact with the filesystem or execute commands, use SandboxProfile to restrict their capabilities: Scoping destructive tools Follow these principles when registering destructive tools: 1. **Name them clearly.** Use verb prefixes that signal intent: delete_, remove_, drop_, update_, modify_. This makes policy rules easy to write and audit. 2. **Type all arguments.** Use Pydantic models for argument validation. Never accept freeform dict arguments for mutating operations. 3. **Describe irreversibility.** Include \"irreversible\", \"destructive\", or \"permanent\" in the tool description. This helps both the model and policy reviewers understand the risk. 4. **Gate with policy rules.** Every mutating tool should have a corresponding policy rule. Use request_approval for interactive environments and deny as the fallback in headless mode. 5. **Set cost limits.** Use FailSafeConfig.max_tool_calls and max_total_cost_usd to prevent runaway tool usage, especially when the agent has access to APIs with per-call costs. 6. **Audit everything.** Policy decisions are emitted as policy_decision events in the run event stream. Persist these events for compliance and debugging.", + "token_count": 261 + }, + { + "id": "doc_c346f13ce597", + "path": "docs/library/snippets/07_tool_hooks_and_middleware.mdx", + "url": "/library/snippets/07_tool_hooks_and_middleware", + "title": "07: Tool Hooks and Middleware", + "description": "Add pre-execution validation, post-execution transformation, and cross-cutting middleware to tools and the LLM client pipeline.", + "headings": [ + "What this snippet demonstrates", + "Tool pre-hooks", + "Pre-hook argument model matches the main tool's argument shape", + "Pre-hook: sanitize and normalize the query before the tool runs", + "Main tool with the pre-hook attached", + "Pre-hook execution flow", + "Tool post-hooks", + "Tool-level middleware", + "Attaching middleware to a tool", + "Registry-level middleware", + "LLM client middleware", + "Chat middleware: intercepts non-streaming chat requests", + "Build client with middleware", + "LLM middleware protocols", + "Built-in LLM middleware", + "Timeout middleware", + "Configure timeouts", + "Add to middleware stack", + "Build client", + "When to use each layer", + "What to read next" + ], + "content_sha256": "cacc5663484a5478e3025773c47ac9ffe5ed840192da35d5ac11d46d5ff94c5d", + "content": "What this snippet demonstrates AFK provides two distinct hook/middleware systems that operate at different layers: 1. **Tool hooks and middleware** -- Pre-hooks, post-hooks, and middleware that wrap individual tool executions. These use Pydantic models for typed arguments and run inside the tool execution pipeline. 2. **LLM middleware** -- Middleware that wraps LLM client operations (chat, stream, embed). These intercept requests and responses at the provider transport layer. Both systems follow the same pattern: define a callable, wire it into the pipeline, and the runner executes it at the appropriate point in the lifecycle. Tool pre-hooks A pre-hook runs before the main tool function executes. It receives the tool's arguments (validated against its own Pydantic model) and returns a dictionary of transformed arguments that the main tool will receive. Use pre-hooks for input sanitization, enrichment, or validation that should happen before execution. Pre-hook execution flow The pre-hook receives validated arguments and must return a dictionary compatible with the main tool's args_model. If the returned dictionary fails validation against the tool's model, the tool call fails with a ToolValidationError. Tool post-hooks A post-hook runs after the main tool function completes. It receives the tool output and can transform or annotate the result before it is returned to the LLM. Use post-hooks for output sanitization, logging, or enrichment. AFK passes post-hooks a payload dictionary with the shape {\"output\": , \"tool_name\": \" \"}. The post-hook must return a dictionary with the same shape. Tool-level middleware Tool-level middleware wraps around the entire tool execution, including pre-hooks and post-hooks. Middleware receives a call_next function and the tool arguments, and can modify behavior before, after, or around execution. Attaching middleware to a tool Middleware executes in the order listed. The first middleware in the list is the outermost wrapper. Each middleware calls call_next to pass control to the next middleware (or the actual tool function if it is the last one). Registry-level middleware Registry-level middleware applies to every tool in a ToolRegistry, not just a single tool. Use this for cross-cutting concerns like audit logging, rate limiting, or policy enforcement that should apply uniformly. LLM client middleware LLM middleware operates at the provider transport layer, intercepting requests to and responses from the LLM API. AFK defines three middleware protocols for the three LLM operations: LLM middleware protocols | Protocol | Operation | Signature | | --- | --- | --- | | LLMChatMiddleware | Non-streaming chat | async (call_next, req: LLMRequest) -> LLMResponse | | LLMEmbedMiddleware | Embeddings | async (call_next, req: EmbeddingRequest) -> EmbeddingResponse | | LLMStreamMiddleware | Streaming chat | (call_next, req: LLMRequest) -> AsyncIterator[LLMStreamEvent] | Each middleware receives call_next (the next middleware or transport in the chain) and the request object. It can modify the request before calling call_next, modify the response after, or short-circuit entirely by returning a response without calling call_next. Built-in LLM middleware AFK ships with pre-built middleware for common patterns: Timeout middleware Apply per-request timeouts to prevent runaway calls: The timeout middleware respects TimeoutPolicy from the request if provided: When to use each layer | Layer | Scope | Use for | | --- | --- | --- | | **Tool pre-hook** | Single tool, before execution | Input sanitization, argument enrichment, validation | | **Tool post-hook** | Single tool, after execution | Output sanitization, redaction, annotation | | **Tool middleware** | Single tool, wraps execution | Timing, retries, caching, error handling | | **Registry middleware** | All tools in registry | Audit logging, rate limiting, policy enforcement | | **LLM middleware** | All LLM calls through client | Request metadata, response logging, tracing | What to read next - Tools -- Full tool system architecture, the 6-step execution pipeline, and design guidelines. - Tool Call Lifecycle -- Detailed lifecycle of a tool call from LLM proposal to result delivery. - LLMs Overview -- Builder workflow, runtime profiles, and provider selection.", + "token_count": 432 + }, + { + "id": "doc_464a2b4a8d35", + "path": "docs/library/snippets/08_prebuilt_runtime_tools.mdx", + "url": "/library/snippets/08_prebuilt_runtime_tools", + "title": "08: Prebuilt Runtime Tools", + "description": "Use AFK's built-in filesystem tools with directory-scoped security constraints and compose them with policy checks.", + "headings": [ + "What this snippet demonstrates", + "Building runtime tools", + "Create filesystem tools scoped to a specific directory", + "Available prebuilt tools", + "list_directory", + "read_file", + "Security: directory traversal prevention", + "Composing with policy checks", + "Define a policy that requires approval for reading certain files", + "Composing with custom tools", + "Combine prebuilt + custom tools", + "Command allowlists and sandbox profiles", + "Create a read-only sandbox that restricts what operations tools can perform", + "What to read next" + ], + "content_sha256": "0b75873372d50bad1c2ec5d4bcd1e04445f683e855b896766f09d4bf86609937", + "content": "What this snippet demonstrates AFK ships prebuilt tools for common runtime operations like listing directories and reading files. These tools are designed with security-first defaults: every tool is scoped to an explicit root directory that prevents directory traversal attacks. This snippet shows how to create, configure, and compose prebuilt tools with agents and policy guards. Building runtime tools The build_runtime_tools() factory creates a set of filesystem tools bound to a specific root directory. All path operations within these tools are resolved against this root, and any attempt to access files outside it raises a FileAccessError. Available prebuilt tools The build_runtime_tools() factory produces two tools: list_directory Lists entries in a directory under the configured root. Returns entry names, paths, and type flags (file or directory). | Parameter | Type | Default | Description | | ------------- | ----- | ------- | ----------------------------------------------------------------- | | path | str | \".\" | Relative path to list, resolved against the root directory. | | max_entries | int | 200 | Maximum entries to return (1--5000). Prevents unbounded listings. | **Returns:** A dictionary with root, path, and entries (list of {name, path, is_dir, is_file}). read_file Reads the contents of a file under the configured root, with configurable truncation to prevent excessive token consumption. | Parameter | Type | Default | Description | | ----------- | ----- | ---------- | -------------------------------------------------------------------------------- | | path | str | (required) | Relative path to the file, resolved against the root directory. | | max_chars | int | 20_000 | Maximum characters to read (1--500,000). Content is truncated beyond this limit. | **Returns:** A dictionary with root, path, content, and truncated (boolean indicating whether content was truncated). Security: directory traversal prevention Every path operation is validated with an internal containment check that uses Python's Path.relative_to() to verify that the resolved path stays within the configured root. This prevents attacks like: If a path escapes the root, the tool raises FileAccessError immediately, before any file I/O occurs. Composing with policy checks For additional security, pair runtime tools with a policy engine that gates specific operations on approval: Composing with custom tools You can combine prebuilt tools with your own custom tools in a single agent: Command allowlists and sandbox profiles For production environments, restrict tool capabilities further using sandbox profiles: This ensures that even if the LLM attempts to use tools for unauthorized operations, the sandbox profile blocks execution before any I/O occurs. What to read next - Tools -- Full tool system architecture, including the @tool decorator, ToolResult, and execution pipeline. - Snippet 06: Tool Registry Security -- Security scoping, policy gates, and sandbox profiles in detail. - Security Model -- Threat model, defense layers, and RunnerConfig security fields.", + "token_count": 294 + }, + { + "id": "doc_64090a20f830", + "path": "docs/library/snippets/09_system_prompt_loader.mdx", + "url": "/library/snippets/09_system_prompt_loader", + "title": "09: System Prompt Loader", + "description": "Resolve agent system prompts from a file hierarchy with deterministic precedence, Jinja templating, and stat-based caching.", + "headings": [ + "What this snippet demonstrates", + "Resolution precedence", + "Basic usage", + "Option 1: Inline instructions (highest priority)", + "Option 2: Explicit instruction file", + "Option 3: Auto-detected file (uses agent name)", + "Loads .agents/prompt/CHAT_AGENT.md automatically", + "Name-to-filename conversion", + "Prompts directory resolution", + "Explicit", + "Environment variable", + "export AFK_AGENT_PROMPTS_DIR=/opt/prompts", + "Default: .agents/prompt/", + "Jinja2 templating", + "Template context variables", + "Caching and hot-reload", + "Security: path containment", + "This would raise PromptAccessError:", + "What to read next" + ], + "content_sha256": "a5dcded5d70453d7ede1e44f292d953ee51243eec18fc5b383e2a4231e130fee", + "content": "What this snippet demonstrates AFK agents need system prompts (instructions) that tell the LLM how to behave. Rather than hardcoding instructions as inline strings, AFK provides a file-based prompt resolution system that loads prompts from a directory hierarchy. This keeps prompts version-controlled, editable by non-developers, and reusable across agents. The prompt loader resolves instructions through a deterministic precedence chain, supports Jinja2 templating for dynamic prompts, and caches compiled templates using stat-based invalidation for hot-reload during development. Resolution precedence The prompt system resolves agent instructions through this priority chain: 1. **Inline instructions** -- If the agent has a non-empty instructions string, it is used directly. No file loading occurs. 2. **Explicit instruction_file** -- If set, the file is loaded from the configured prompts_dir. The path must resolve to a file inside the prompts root (no directory traversal). 3. **Auto-detected file** -- If neither is set, the agent's name is converted to UPPER_SNAKE_CASE.md and loaded from prompts_dir. Basic usage Name-to-filename conversion The auto-detection algorithm converts the agent name to a filename using these rules: | Agent Name | Derived Filename | Rule Applied | | --- | --- | --- | | ChatAgent | CHAT_AGENT.md | CamelCase split on boundaries | | chatagent | CHAT_AGENT.md | Lowercase agent suffix detected and split | | research-assistant | RESEARCH_ASSISTANT.md | Hyphens replaced with underscores | | QA Bot v2 | QA_BOT_V2.md | Spaces and non-alphanumeric chars become underscores | The conversion is handled by derive_auto_prompt_filename() internally. It splits camelCase boundaries, normalizes non-alphanumeric characters to underscores, collapses consecutive underscores, and uppercases the result. Prompts directory resolution The prompts directory is resolved through its own priority chain: 1. Explicit prompts_dir argument on the Agent constructor. 2. AFK_AGENT_PROMPTS_DIR environment variable. 3. Default: .agents/prompt relative to the current working directory. Jinja2 templating Prompt files support Jinja2 template syntax. When the runner resolves a prompt, it renders the template with a context dictionary that includes agent metadata and any custom context passed to the run. **File: .agents/prompt/SUPPORT_AGENT.md** **Agent code:** Template context variables The following variables are available in every prompt template: | Variable | Type | Description | | --- | --- | --- | | agent_name | str | The agent's name field. | | agent_class | str | The Python class name of the agent. | | context | dict | The full context dictionary passed to the run. | | ctx | dict | Alias for context (shorthand). | Any keys in the context dictionary that are not reserved names (context, ctx, agent_name, agent_class) are also available as top-level template variables. So {{ company_name }} works as a shorthand for {{ ctx.company_name }}. Caching and hot-reload The prompt system uses a process-wide PromptStore singleton that caches at three levels: 1. **File cache** -- Keyed by resolved file path. Uses stat() metadata (mtime, size, inode) as the cache signature. If the file changes on disk, the cache entry is invalidated automatically. 2. **Text pool** -- Deduplicates prompt text by SHA-256 hash. If multiple agents use the same prompt content (even from different files), only one copy is stored in memory. 3. **Template cache** -- Compiled Jinja2 templates are cached by content hash. Re-rendering with different context variables reuses the compiled template. This means that during development, you can edit prompt files and they will be picked up on the next run without restarting the process. In production, the stat-based check is a single os.stat() call per prompt resolution, which is negligible overhead. Security: path containment The prompt loader enforces strict path containment. The resolved prompt file path must be inside the configured prompts_dir. If an instruction_file path resolves outside the prompts root (via ../ traversal or an absolute path pointing elsewhere), the loader raises PromptAccessError immediately. What to read next - System Prompts -- Full system prompt architecture, resolution pipeline, and design guidelines. - Agents -- Agent model, configuration fields, and composition patterns. - Security Model -- Threat model and defense layers including prompt injection considerations.", + "token_count": 448 + }, + { + "id": "doc_7b26cbd9a7ae", + "path": "docs/library/snippets/10_streaming_chat_with_memory.mdx", + "url": "/library/snippets/10_streaming_chat_with_memory", + "title": "10: Streaming Chat with Memory", + "description": "Combine real-time streaming with thread-based memory for multi-turn chat UIs.", + "headings": [ + "What this snippet demonstrates", + "Full example", + "Key patterns", + "Thread ID connects turns", + "These two calls share memory", + "Access the result after streaming", + "Cancel mid-stream", + "The run transitions to \"cancelled\" state", + "What to read next" + ], + "content_sha256": "747e1945af154333e226917cadea057d5fc51e87933b13672f562d0190008ae2", + "content": "What this snippet demonstrates Most chat applications need two things simultaneously: **real-time streaming** (so users see text as it's generated) and **memory continuity** (so the agent remembers previous turns). This snippet shows how to combine run_stream() with thread_id to build a multi-turn streaming chat handler. Full example Key patterns Thread ID connects turns Pass the same thread_id across run_stream() calls to maintain conversation context: Access the result after streaming The handle.result is available after the stream completes: Cancel mid-stream If the user navigates away or clicks \"stop\": What to read next - Streaming \u2014 Full event reference and stream control API. - Memory \u2014 Thread persistence, compaction, and backend configuration. - Snippet 04: Resume + Compact \u2014 Checkpoint-based resumption and memory management.", + "token_count": 92 + }, + { + "id": "doc_fdf48d427fc0", + "path": "docs/library/snippets/11_cost_monitoring.mdx", + "url": "/library/snippets/11_cost_monitoring", + "title": "11: Cost Monitoring", + "description": "Track and control agent costs using FailSafeConfig budgets and telemetry events.", + "headings": [ + "What this snippet demonstrates", + "Setting cost budgets", + "Monitoring cost from results", + "Access usage statistics", + "Real-time cost monitoring via streaming", + "Cost-aware batch processing", + "Operating recommendations", + "What to read next" + ], + "content_sha256": "f2d31922b3ae2362b1bc68b3e2c53fe59d0b80c0806fa5cfe522b204e0410131", + "content": "What this snippet demonstrates Runaway agent loops are the most common source of unexpected API costs. AFK provides two defense layers: **cost budgets** that kill runs when spending exceeds a threshold, and **telemetry events** that let you observe cost in real time. This snippet shows how to configure both. Setting cost budgets The simplest defense is a hard cost ceiling on every agent: When the estimated cost exceeds max_total_cost_usd, the runner terminates the run with a degraded state and returns the best partial result. Monitoring cost from results Every AgentResult includes token counts and cost estimates: Real-time cost monitoring via streaming For long-running agents, monitor cost during execution: Cost-aware batch processing When running multiple agents in a batch, track cumulative cost: Operating recommendations 1. **Always set max_total_cost_usd** \u2014 even generous limits prevent runaway costs 2. **Layer defenses** \u2014 combine cost limits with max_llm_calls, max_steps, and max_wall_time_s 3. **Use telemetry for dashboards** \u2014 export metrics to monitor cost trends over time 4. **Set per-item budgets in batches** \u2014 prevent one expensive item from consuming the entire budget 5. **Choose models by task** \u2014 use smaller models for routine work and reserve larger models for requests that need them What to read next - Observability \u2014 Telemetry pipeline for metrics and dashboards. - Failure Policy Matrix \u2014 How cost limit breaches flow through the system. - Configuration Reference \u2014 Full FailSafeConfig field reference.", + "token_count": 171 + }, + { + "id": "doc_40e30666bfa8", + "path": "docs/library/snippets/12_mcp_client_integration.mdx", + "url": "/library/snippets/12_mcp_client_integration", + "title": "12: MCP Client Integration", + "description": "Discover and use tools from external MCP servers in your agents.", + "headings": [ + "What this snippet demonstrates", + "Consuming MCP tools", + "Connect, discover, and attach", + "Using the Agent's built-in MCP support", + "The agent connects to MCP servers automatically during startup", + "Mixing local and MCP tools", + "Security with MCP tools", + "What to read next" + ], + "content_sha256": "5b44bbbb025f2b01a553bacf0e510a09070c771c3fb99d91656df5ad80fd03df", + "content": "What this snippet demonstrates AFK agents can consume tools from external MCP (Model Context Protocol) servers just like local tools. This snippet shows how to connect to an MCP server, discover available tools, and attach them to an agent \u2014 all with the same validation, policy gates, and telemetry as local tools. Consuming MCP tools Connect, discover, and attach Using the Agent's built-in MCP support For simpler setups, pass MCP server refs directly to the agent: Mixing local and MCP tools Combine your own tools with external MCP tools: Security with MCP tools Apply policy rules to MCP-sourced tools just like local tools: **MCP tools are transparent.** Once attached to an agent, they go through the same validation, policy gates, sanitization, and telemetry as local tools. The agent doesn't know whether a tool is local or remote. What to read next - MCP Server \u2014 Expose your own tools via MCP, plus authentication and rate limiting. - Tools \u2014 Full tool system architecture. - Snippet 06: Tool Security \u2014 Policy gates and sandbox profiles.", + "token_count": 129 + }, + { + "id": "doc_d1f7d83e0824", + "path": "docs/library/snippets/13_multi_model_fallback.mdx", + "url": "/library/snippets/13_multi_model_fallback", + "title": "13: Multi-Model Fallback", + "description": "Configure fallback model chains for LLM resilience and cost optimization.", + "headings": [ + "What this snippet demonstrates", + "Basic fallback chain", + "Cost-optimized fallback", + "Start cheap, escalate if quality is insufficient", + "Complex tasks get the big model with fallbacks", + "Simple task -> cheap model handles it", + "Complex task -> powerful model with safety net", + "Circuit breaker integration", + "Multi-agent with different model tiers", + "Cheap model for simple classification", + "Inspecting which model was used", + "Recommendations", + "What to read next" + ], + "content_sha256": "f217ecec21c2dde7c33c194035ef4d3e46de6ffea14b1c9f76e5e8691024b281", + "content": "What this snippet demonstrates LLM API calls fail \u2014 rate limits, outages, timeouts. AFK's fallback_model_chain lets you define an ordered list of models to try when the primary model fails. This snippet shows how to configure fallback chains for resilience, cost optimization, and provider diversification. Basic fallback chain When gpt-4.1 fails (timeout, rate limit, outage): 1. AFK retries with the primary model (controlled by retry policy) 2. If retries exhaust, it falls through to gpt-4.1-mini 3. If that also fails, it tries gpt-4.1-nano 4. If all models fail, the llm_failure_policy determines the outcome Cost-optimized fallback Use expensive models only when needed: Circuit breaker integration AFK's built-in circuit breaker works with fallback chains. When a model triggers too many failures, the breaker opens and the system skips straight to the next fallback: Multi-agent with different model tiers Use different model tiers for different specialists: Inspecting which model was used After a run, check the result metadata and usage aggregate: Recommendations | Scenario | Primary Model | Fallback Chain | | ------------------------ | -------------- | ------------------------------- | | **Classification** | gpt-4.1-nano | gpt-4.1-mini | | **General chat** | gpt-4.1-mini | gpt-4.1-nano | | **Complex analysis** | gpt-4.1 | gpt-4.1-mini \u2192 gpt-4.1-nano | | **Code generation** | gpt-4.1 | gpt-4.1-mini | | **Cost-sensitive batch** | gpt-4.1-nano | _(none)_ | What to read next - Configuration Reference \u2014 Full FailSafeConfig fields including circuit breaker settings. - Failure Policy Matrix \u2014 How failures flow through the system. - Snippet 11: Cost Monitoring \u2014 Track and control costs in real time.", + "token_count": 183 + }, + { + "id": "doc_4462f67b1c03", + "path": "docs/library/snippets/14_production_client.mdx", + "url": "/library/snippets/14_production_client", + "title": "14: Client Timeouts and Redis Pooling", + "description": "Configure LLM client timeouts and Redis connection pooling.", + "headings": [ + "What this snippet demonstrates", + "Timeout middleware", + "Per-request timeout override", + "Redis connection pooling", + "Using with memory store", + "Full example", + "Configuration reference", + "TimeoutConfig", + "PoolConfig", + "What to read next" + ], + "content_sha256": "3ad51d99b7437da1d6f78dd70ba9238005ea9818864ec8eeb4a97c4d39790c49", + "content": "What this snippet demonstrates This snippet shows how to configure: 1. **Timeout middleware** to bound slow provider calls 2. **Redis connection pooling** for shared cache or memory connections 3. **Shutdown handling** so runners and Redis pools close cleanly Timeout middleware Apply per-request timeouts to prevent runaway LLM calls: Per-request timeout override Redis connection pooling For Redis deployments, use connection pooling instead of creating a new client per request: Using with memory store Full example Configuration reference TimeoutConfig | Parameter | Default | Description | | --- | --- | --- | | default_timeout_s | 30.0 | Default timeout for all operations | | chat_timeout_s | None | Specific timeout for chat requests | | embed_timeout_s | None | Specific timeout for embeddings | | stream_timeout_s | None | Specific timeout for streaming | PoolConfig | Parameter | Default | Description | | --- | --- | --- | | max_connections | 50 | Maximum total connections | | max_idle_connections | 10 | Maximum idle connections | | socket_timeout | 5.0 | Socket read/write timeout | | socket_connect_timeout | 5.0 | Connection establishment timeout | | socket_keepalive | False | Enable TCP keepalive | | health_check_interval_s | 30.0 | Interval for health checks | What to read next - LLM Control & Session -- Retry, caching, and circuit breaker policies - Deployment Guide -- Production deployment with Docker and Kubernetes - Performance Guide -- Optimize latency and throughput", + "token_count": 138 + }, + { + "id": "doc_109157c0d2d7", + "path": "docs/library/streaming.mdx", + "url": "/library/streaming", + "title": "Streaming", + "description": "Real-time event streaming for chat UIs and CLI tools.", + "headings": [ + "Quick example", + "Event reference", + "Streaming modes", + "Stream control", + "Lifecycle control variant", + "Background tools example", + "Error handling", + "Next steps" + ], + "content_sha256": "dffb536cc9129c079ef96ab842c4ea0ae01a309ee01259823f89e4c20a130350", + "content": "AFK supports real-time streaming via Runner.run_stream(). Instead of waiting for the full response, you receive events as they happen: incremental text, tool lifecycle updates, and terminal status. Quick example Event reference run_stream() emits AgentStreamEvent values with these types: | Event type | When it fires | Key fields | | --- | --- | --- | | text_delta | Incremental text chunks from streaming or fallback path | event.text_delta, event.step | | step_started | New step in the agent loop | event.step, event.state | | tool_started | A tool call is about to execute | event.tool_name, event.tool_call_id, event.step | | tool_completed | A tool call finished | event.tool_name, event.tool_call_id, event.tool_success, event.tool_output, event.tool_error | | tool_deferred | A tool call was accepted but deferred | event.tool_name, event.tool_call_id, event.tool_ticket_id, event.data.resume_hint | | tool_background_resolved | A deferred tool finished successfully | event.tool_name, event.tool_ticket_id, event.tool_output | | tool_background_failed | A deferred tool failed/expired | event.tool_name, event.tool_ticket_id, event.tool_error | | completed | Run finished | event.result (full AgentResult) | | error | Stream/runtime bridge error | event.error | Streaming modes - If the provider supports streaming, AFK forwards model deltas as text_delta. - If the provider is non-streaming, AFK emits chunked fallback text_delta from final text so UI behavior stays consistent. Stream control AgentStreamHandle is read-only. For lifecycle controls (pause(), resume(), cancel(), interrupt()), use run_handle(). Background tools example Error handling error events indicate stream/runtime bridge failures. Tool failures are reported through tool_completed (tool_success=False) and do not necessarily fail the run. Next steps Persist state across streaming runs. Full lifecycle and API reference.", + "token_count": 205 + }, + { + "id": "doc_2798948c3080", + "path": "docs/library/system-prompts.mdx", + "url": "/library/system-prompts", + "title": "System Prompts", + "description": "Configure agent instructions with files, templates, and dynamic context.", + "headings": [ + "Three ways to set instructions", + "Precedence chain", + "Template variables with Jinja2", + "Available template variables", + "Prompt from file with templates", + "Rules", + "Error handling", + "Design guidelines", + "Next steps" + ], + "content_sha256": "6fcbb09ca2206bb61c328929099af4a832216459bbe1880e90c8c7993766a6b5", + "content": "System prompts define what your agent knows and how it behaves. AFK supports three methods \u2014 inline strings, instruction files, and auto-detection \u2014 with Jinja2 templating for dynamic values. Three ways to set instructions Set instructions directly on the Agent. Best for simple, static prompts. Store the prompt in a separate file. Best for long or version-controlled prompts. AFK automatically looks for an instruction file based on the agent's name: The resolution order: 1. prompts/{agent_name}.md 2. prompts/{agent_name}.txt 3. {agent_name}.md 4. {agent_name}.txt Precedence chain When multiple sources are available, AFK uses this order: The first non-empty source wins. If you set both instructions and instruction_file, the inline string takes priority. Template variables with Jinja2 Use {{ variable }} syntax to inject dynamic values into prompts: Available template variables | Source | Variables | Example | | -------------------- | ------------------------------------ | --------------------------------------- | | Agent.context dict | Any key-value pairs you set | {{ company_name }}, {{ user_role }} | | Built-in | agent_name, model_name | {{ agent_name }} | | Runtime | run_id, thread_id (if available) | {{ thread_id }} | Prompt from file with templates Templates work in instruction files too: Error handling | Error | Cause | Resolution | | ----------------------- | ------------------------------------- | -------------------------------------------------------------------------------------------- | | PromptResolutionError | instruction_file path doesn't exist | Check that the file exists under the configured prompts_dir. | | PromptTemplateError | Jinja2 template syntax error | Check for unclosed {{ }} or {% %} blocks. | | PromptTemplateError | Missing template variable | Add the variable to Agent.context or provide a default: {{ var \\| default(\"fallback\") }} | **Keep prompts in version control.** Store instruction files in a prompts/ directory and track changes in git. This gives you prompt history, diffs, and the ability to A/B test prompt versions. Design guidelines - **Inline for prototyping,** files for production. Switch to instruction files when your prompt exceeds ~5 lines. - **Use templates for anything dynamic.** Don't concatenate strings \u2014 use {{ variable }} and set values in context. - **Be specific in instructions.** Tell the agent what to do _and_ what not to do. Include output format expectations. - **Test prompt changes with evals.** A small prompt change can dramatically shift behavior. Run your eval suite after any edit. Next steps Reusable knowledge bundles loaded on demand. Test prompt changes with behavioral assertions.", + "token_count": 249 + }, + { + "id": "doc_78b1717fafe3", + "path": "docs/library/task-queues.mdx", + "url": "/library/task-queues", + "title": "Task Queues", + "description": "Async job processing with execution contracts and dead-letter handling.", + "headings": [ + "Quick start", + "Push a task", + "A worker consumes queued tasks", + "Task lifecycle", + "Execution contracts", + "Worker setup", + "Dead-letter handling", + "Check for dead letters", + "Retry manually after fixing the issue", + "Or discard them", + "Error classification", + "Queue backends", + "Connection pooling", + "Create a pooled connection", + "Use in queue", + "Health check", + "Next steps" + ], + "content_sha256": "da3a4f6c098ee01fffa8507b3bd9ee9116cf103123e29dcbcc58b4a1facb5f5e", + "content": "Task queues decouple agent work producers from consumers. Push a task, a worker picks it up, and the result is stored \u2014 independently of the caller's lifecycle. Use queues for long-running jobs, batch processing, and reliable retries. Quick start Task lifecycle | State | Meaning | | ------------- | ------------------------------------------- | | queued | Waiting to be picked up by a worker | | running | A worker is executing the task | | completed | Task finished successfully | | failed | Task hit an error (may be retried) | | dead_letter | All retries exhausted \u2014 needs manual review | Execution contracts Every task has a **contract** that defines what kind of work it represents: Standard agent chat. Runs an agent with a user message. Generic job dispatch. Runs a custom handler function. Define your own contract for specialized workloads: Register a handler for the contract on the worker side. Worker setup Dead-letter handling When a task exhausts all retries, it moves to the dead-letter queue (DLQ): Error classification The queue uses error classification to decide whether to retry: | Error type | Retried? | Example | | ------------- | ------------------------------------------------------------------------------------ | ------------------------------------------------ | | **Retryable** | (with backoff) | Network timeout, rate limit, transient LLM error | | **Terminal** | (sent to DLQ) | Invalid arguments, auth failure, missing model | | **Non-fatal** | (task completes with warning) | Telemetry export failure | Queue backends State lives in process memory. No setup required. **Use for:** Development, testing, prototyping. Durable queue with persistence and multi-worker support. Set via environment variables: **Use for:** Production deployments, multi-process workers. Connection pooling For high-throughput production workloads, use RedisConnectionPool to manage connections efficiently: The pool provides: - Configurable max connections (default: 50) - Idle connection management - Automatic health checks - Singleton access via get_redis_pool() Next steps Expose tools via the Model Context Protocol. Monitor queue performance and worker health.", + "token_count": 209 + }, + { + "id": "doc_618e34441fa7", + "path": "docs/library/tested-behaviors.mdx", + "url": "/library/tested-behaviors", + "title": "Tested Behaviors", + "description": "Contract-level guarantees and what test suites validate.", + "headings": [ + "Guaranteed behaviors", + "Deterministic delegation ordering", + "Queue contract failure classification", + "Stream lifecycle correctness", + "Telemetry projection stability", + "Eval report schema consistency", + "Test categories", + "Running the test suite", + "Delegation and subagent tests", + "Queue contract tests", + "Eval suite tests", + "Tool execution tests", + "Observability tests", + "LLM runtime tests", + "Interpreting test results", + "Adding new tests", + "CI pipeline guidance", + "Example GitHub Actions step" + ], + "content_sha256": "80884b0dc0cbc8a2eda294fc1ca74d89dea1b5e98a80ea48892237808e33f2e2", + "content": "AFK's tests describe the behavior contributors should preserve when changing the runtime, tools, memory, queues, LLM adapters, observability, and evals. Treat this page as a map from behavior to the test files that cover it. If you change a public contract, update the matching tests and the docs that explain that contract. A passing test suite is necessary, but it is not a release guarantee by itself; review the affected behavior and run the focused suite for the code you touched. Guaranteed behaviors Deterministic delegation ordering **What is tested:** When a parent agent dispatches work to subagents, the delegation engine executes nodes in a deterministic, topologically sorted order. Parallel batches respect concurrency limits. Edge dependencies (where one subagent's output feeds into another's input) are resolved before the dependent node starts. **Why it matters:** If delegation ordering were non-deterministic, the same agent configuration could produce different results depending on task scheduling. Deterministic ordering means your subagent pipelines are reproducible and debuggable. **Test coverage:** Tests verify that DAG plans produce consistent node execution sequences, that edge-based data flow resolves correctly, and that backpressure limits prevent unbounded queue growth. Queue contract failure classification **What is tested:** The task queue system classifies failures into retryable, terminal, and degraded categories. Retryable failures trigger retry with backoff. Terminal failures stop execution immediately. Degraded states allow partial results. **Why it matters:** Incorrect failure classification can cause infinite retry loops (if terminal failures are marked retryable) or premature task abandonment (if retryable failures are marked terminal). The failure classification contract ensures that each failure type triggers the correct recovery behavior. **Test coverage:** Tests verify that each failure category maps to the correct queue behavior, that retry counts and backoff intervals are respected, and that dead-letter handling works correctly. Stream lifecycle correctness **What is tested:** The streaming API (runner.run_stream()) produces events in the correct lifecycle order: stream starts, text deltas arrive, tool events fire at the right times, and the stream terminates with a completed event containing the final AgentResult. Error conditions produce error stream events rather than unhandled exceptions. **Why it matters:** Streaming consumers (such as chat UIs) depend on events arriving in the correct order. A misplaced completed event before all text deltas have been emitted would cause truncated output. An unhandled exception would crash the consumer. **Test coverage:** Tests verify event ordering, ensure that all text content is captured before the terminal event, and confirm that error conditions produce structured error events. Telemetry projection stability **What is tested:** The telemetry projector produces consistent RunMetrics from the same input data. Field names, types, and computed properties (like avg_llm_latency_ms and success) are stable across versions. **Why it matters:** Downstream dashboards, alerting rules, and eval assertions depend on RunMetrics having a stable schema. If a field is renamed or its type changes, every consumer breaks silently. **Test coverage:** Tests verify that projected metrics match expected values for known input data, that computed properties produce correct results, and that to_dict() serialization is stable. Eval report schema consistency **What is tested:** The eval report serializer (suite_report_payload) produces output with a stable schema_version and consistent field structure. The report includes summary statistics, per-case results, assertion details, budget violations, and projected metrics. **Why it matters:** CI pipelines parse eval reports to make release-gating decisions. If the report schema changes, CI scripts break and deployments may be incorrectly blocked or allowed. **Test coverage:** Tests verify the report envelope structure, confirm that schema_version is set correctly, and validate that all expected fields are present and correctly typed. Test categories | Category | Description | Key Modules Covered | | --- | --- | --- | | Agent delegation | Deterministic DAG execution, subagent routing, backpressure limits. | afk.core.runtime, afk.agents.delegation | | Queue contracts | Failure classification, retry behavior, dead-letter handling. | afk.queues | | Stream lifecycle | Event ordering, text delta capture, terminal event correctness. | afk.core.streaming | | Telemetry projection | Metric stability, computed property correctness, serialization. | afk.observability.projectors, afk.observability.models | | Eval reports | Report schema stability, assertion result structure, budget violations. | afk.evals.reporting, afk.evals.models | | Tool execution | Pydantic validation, timeout enforcement, hook/middleware chains. | afk.tools | | Policy evaluation | Rule matching, decision actions, audit event emission. | afk.agents.policy | | LLM runtime | Provider routing, retry/circuit-breaker behavior, streaming correctness. | afk.llms.runtime | | Security | Sandbox profile enforcement, secret scope isolation, command allowlists. | afk.tools.security | Running the test suite Run all tests from the repository root: Run a specific test category: Run with verbose output for debugging: Interpreting test results - **All relevant tests pass**: The checked behavior still matches the test suite. Review docs and public imports before merging user-visible changes. - **A test fails**: Inspect the assertion before changing code. The test may expose a real contract regression, or the intended contract may have changed and need a deliberate test/doc update. - **A new test is marked as xfail**: Include a reason and keep the scope narrow so known limitations do not hide unrelated regressions. Adding new tests When adding a new feature or fixing a bug, follow this pattern: 1. **Identify the contract.** What behavioral guarantee should your change preserve or introduce? Write this as a plain-English statement (e.g., \"Subagent timeout should produce a SubagentExecutionRecord with success=False\"). 2. **Write the test first.** Create a test in the appropriate tests/ subdirectory that asserts the expected behavior. Use descriptive test names that read as contract statements. 3. **Make the test pass.** Implement the feature or fix. The test should pass without any special-casing or mocking of the behavior under test. 4. **Verify stability.** Run the full suite to confirm your change does not break existing contracts. CI pipeline guidance Most tests run without API keys and use in-memory or mocked providers. Some integration tests cover optional backends such as Redis, SQLite, or Postgres behavior; keep those isolated and skippable when the backing service is unavailable. Recommended CI configuration: Store the JUnit XML report as a CI artifact for trend analysis and failure investigation.", + "token_count": 763 + }, + { + "id": "doc_0ac01bce8c79", + "path": "docs/library/tool-call-lifecycle.mdx", + "url": "/library/tool-call-lifecycle", + "title": "Tool Call Lifecycle", + "description": "Detailed event-level view of one tool invocation.", + "headings": [ + "Event timeline", + "Event reference table", + "Policy decisions: allow, deny, defer, and request_user_input", + "Timing and latency tracking", + "How denied tools affect the agent run" + ], + "content_sha256": "9426358483fa8300d96c0f0ec8bc520958e881b7f6827a7fdfe5301576f8f306", + "content": "While the Tools System Walkthrough covers the end-to-end pipeline, this page zooms into the event-level lifecycle of a single tool call. Every tool invocation produces a deterministic sequence of events that flow through the run handle. Understanding this sequence is critical for building observability dashboards, audit logs, and human-in-the-loop approval flows. A tool call begins when the runner receives a ToolCall from the LLM response. Before the handler ever runs, the runner evaluates a policy gate that can allow, deny, or defer the call for human approval. The outcome of that gate determines whether the tool executes at all, and the events emitted along the way tell you exactly what happened and why. Event timeline Event reference table Every event is an AgentRunEvent published on the run handle. The type field identifies the event, and the data dict carries event-specific fields. | Event type | When it fires | Key data fields | Description | | --- | --- | --- | --- | | tool_batch_started | After LLM response contains tool calls | tool_call_count | Signals the start of a batch of one or more tool calls from a single LLM turn. | | policy_decision | After policy evaluation for each tool | tool_name, action, reason | Records the policy engine's decision for one tool call. Emitted for allow, deny, defer, and request_user_input actions. | | run_paused | When a tool call requires human approval | tool_name, reason, payload | The run is suspended waiting for external input. Only emitted when policy returns defer or request_approval. | | run_resumed | After human approval or denial is received | approved | The run resumes after the approval decision. | | tool_completed | After each tool call resolves (success or failure) | tool_name, success, output, error, tool_call_id | The terminal event for one tool call. Always emitted regardless of whether the tool ran, was denied, or timed out. | | warning | When non-fatal issues occur | message | Advisory events such as sandbox violations handled under a continue policy. | Policy decisions: allow, deny, defer, and request_user_input The PolicyEngine evaluates a PolicyEvent with event_type=\"tool_before_execute\" for every tool call. The decision drives the rest of the lifecycle: **allow** -- The tool executes immediately. No human interaction required. This is the default when no policy rules match. **deny** -- The tool is blocked. A ToolExecutionRecord with success=False and error=\"Denied by policy\" is recorded. The denied result is fed back to the model as a tool message so it can adjust its behavior. After a denial, the runner consults fail_safe.approval_denial_policy to decide the run-level outcome: - continue (default) -- The run proceeds. The model sees the denial and may try a different approach. - degrade -- The run transitions to a degraded state and terminates with a degradation message. - fail -- The run raises an AgentExecutionError and transitions to failed. **defer / request_approval** -- The runner pauses the run, emits a run_paused event, and waits for the InteractionProvider to deliver an approval decision. If the provider is HeadlessInteractionProvider, the configured approval_fallback (default: deny) is used. The approval has a configurable timeout (approval_timeout_s, default 300s). **request_user_input** -- Similar to defer, but instead of a yes/no approval, the runner asks the user for a value (such as a confirmation code or corrected parameter). The policy's request_payload can specify a target_arg field, and the user's input is injected into the tool arguments before execution. Timing and latency tracking Every tool execution is timed from the moment registry.call() is invoked to the moment it returns. The latency is captured in milliseconds and recorded in two places: 1. **ToolExecutionRecord.latency_ms** -- Available on the AgentResult.tool_executions list after the run completes. 2. **Telemetry histogram** -- The agent.tool_call.latency_ms metric is emitted with attributes tool_name, result (success/error), and source (execute/replay). For batch-level timing, the agent.tool_batch.latency_ms histogram captures the wall-clock duration of the entire batch (all concurrent tool calls), with attributes for call_count and failure_count. Denied tool calls have no execution latency since the handler never runs. How denied tools affect the agent run When a tool is denied, the model still receives feedback. The runner appends a tool message to the conversation with the denial reason: This feedback loop is important: the model sees that its proposed action was rejected and can adapt. In practice, models respond to denials by either rephrasing the request, choosing a different tool, or providing a text-only response. The run-level impact depends on fail_safe.approval_denial_policy: | Policy | Behavior after denial | | --- | --- | | continue | Run proceeds normally. Model sees the denial and may self-correct. | | degrade | Run transitions to degraded state and exits the step loop with a degradation message. | | fail | Run raises AgentExecutionError and transitions to failed state. | Multiple denials in a single batch are each evaluated independently. If any denial triggers a fail or degrade policy, the remaining tool calls in the batch are skipped.", + "token_count": 535 + }, + { + "id": "doc_745f751f1344", + "path": "docs/library/tools-system-walkthrough.mdx", + "url": "/library/tools-system-walkthrough", + "title": "Tools System Walkthrough", + "description": "From tool registration to runtime execution and model feedback.", + "headings": [ + "Registration to execution map", + "Step-by-step breakdown", + "Full example: tool definition through result inspection", + "1. Define a tool with a Pydantic args model", + "2. Create an agent with the tool", + "3. Run the agent", + "4. Inspect the result", + "Inspecting tool executions", + "Hook and middleware pipeline", + "Error handling" + ], + "content_sha256": "1ed41f9431123214684ef929c17c9f9886933e7e7c218f1ac668e47a6e02e499", + "content": "Every agent capability beyond pure text generation flows through the AFK tool system. A tool starts as a decorated Python function, gets registered in a ToolRegistry, has its JSON Schema exposed to the LLM, and then participates in a tightly controlled loop: the model proposes a call, AFK validates the arguments, evaluates policy gates, executes the handler, sanitizes the output, and feeds the result back into the conversation for the model's next turn. This page walks through every stage of that pipeline end-to-end. Registration to execution map Step-by-step breakdown 1. **Define @tool** -- You write a Python function and decorate it with @tool. The decorator extracts the function name, docstring, and a Pydantic model for its arguments to produce a ToolSpec(name, description, parameters_schema). 2. **Tool registry** -- When the agent starts, all tools (declared on the agent plus runtime-injected extras like MCP tools and skill tools) are collected into a ToolRegistry. The registry is the single source of truth for tool lookup, schema export, and call dispatch. 3. **Expose tool schema to model** -- The registry converts every registered tool into the OpenAI function-calling format via registry.to_openai_function_tools(). This list is attached to the LLMRequest.tools field so the model knows what tools are available. 4. **Model emits tool call** -- The LLM response (LLMResponse) may contain one or more ToolCall objects. Each carries an id, a tool_name, and a JSON arguments dict. AFK processes these as a batch. 5. **Schema validation** -- Before execution, each tool call's raw arguments are validated against the tool's Pydantic args_model via BaseTool.validate(). If validation fails, a ToolResult(success=False) is returned immediately and the tool handler is never invoked. 6. **Policy gate** -- The runner evaluates the PolicyEngine for each tool call via a tool_before_execute policy event. The policy can allow, deny, defer (require human approval), or request_user_input. See the Tool Call Lifecycle page for the full decision matrix. 7. **Execute handler** -- If the policy allows execution, AFK runs the tool through the full hook/middleware chain: PreHooks (argument transforms) -> Middleware chain -> core handler -> PostHooks (output transforms). Timeout enforcement applies at every layer. 8. **Sanitize output** -- The raw ToolResult.output is passed through apply_tool_output_limits() which enforces max_output_chars and sandbox output policies. The runner then wraps the result in an untrusted-data content envelope via render_untrusted_tool_message() to prevent prompt injection from tool output. 9. **Emit events** -- A tool_completed event is emitted on the run handle, carrying the tool name, success flag, output, and any error. Telemetry counters and histograms are recorded for latency and success/failure counts. 10. **Tool result fed back to model** -- The sanitized output is appended to the conversation as a Message(role=\"tool\", name=tool_name, content=...). The loop returns to step 4 for the next LLM turn. Full example: tool definition through result inspection Inspecting tool executions Every completed run exposes a tool_executions list on AgentResult. Each entry is a ToolExecutionRecord: | Field | Type | Description | | -------------- | ------------------- | ------------------------------------ | | tool_name | str | Name of the executed tool. | | tool_call_id | str \\| None | Provider/LLM tool-call identifier. | | success | bool | Whether execution succeeded. | | output | JSONValue \\| None | JSON-safe tool output payload. | | error | str \\| None | Error message when execution failed. | | latency_ms | float \\| None | Execution latency in milliseconds. | Hook and middleware pipeline Tools support three extension points that wrap the core handler: | Extension | When it runs | Signature | Purpose | | -------------- | ------------------- | ------------------------------------- | ------------------------------------------------------------------------------------------- | | **PreHook** | Before core handler | (args) or (args, ctx) | Transform or validate arguments. Must return a dict compatible with the tool's args model. | | **Middleware** | Wraps core handler | (call_next, args, ctx) | Cross-cutting concerns (logging, timing, caching). Calls call_next(args, ctx) to proceed. | | **PostHook** | After core handler | ({\"output\": ..., \"tool_name\": ...}) | Transform or audit the output before it reaches the model. | The execution order is: validate args -> run PreHooks sequentially -> run Middleware chain (outermost first) -> core handler -> run PostHooks sequentially -> wrap in ToolResult. Error handling The tool system is designed to be non-throwing by default. BaseTool.call() catches all exceptions and wraps them in a ToolResult(success=False, error_message=...) unless raise_on_error=True is set on the tool. **Validation errors** -- When the model passes arguments that do not match the Pydantic schema, a ToolValidationError is caught and the error message is returned to the model so it can self-correct on the next turn. **Timeout errors** -- Each tool can set a default_timeout in seconds. If execution exceeds this limit, an asyncio.TimeoutError is caught and surfaced as a ToolTimeoutError in the result. **Execution errors** -- Any unhandled exception from the handler, PreHook, or PostHook is caught and wrapped in a ToolExecutionError result. **Hook/middleware failures** -- If a PreHook returns a non-dict value or produces args that fail re-validation against the main tool's model, execution stops and a failure result is returned. PostHook failures similarly short-circuit the chain. **Policy-driven behavior** -- After a tool failure, the runner consults the agent's fail_safe.tool_failure_policy to decide whether to continue (default), degrade the run, or fail the entire run. This is configurable per agent.", + "token_count": 582 + }, + { + "id": "doc_9e9118ed2141", + "path": "docs/library/tools.mdx", + "url": "/library/tools", + "title": "Tools", + "description": "Give agents typed capabilities through Python functions.", + "headings": [ + "Your first tool", + "How tool calling works", + "Tool patterns", + "Deferred background tool calls", + "Policy-gated tools", + "Hooks and middleware", + "Execution order", + "Common tools cookbook", + "Prebuilt tools", + "Runtime tools", + "Tools scoped to a specific directory", + "Returns: [read_file, list_directory, ...]", + "Skill tools", + "Next steps" + ], + "content_sha256": "517d6cb680faa87f107bc90d44a3e46e807dbc7146a40390f7385f83568e4743", + "content": "Tools let agents take actions \u2014 query databases, call APIs, run calculations, write files, or anything you can express in Python. AFK handles schema generation, argument validation, policy gates, execution, and output sanitization. Your first tool That's a complete tool. The @tool decorator generates the JSON schema from the Pydantic model, which the LLM uses to understand what arguments to pass. How tool calling works Based on the user's message and the tool schemas, the LLM emits a tool_call with the function name and arguments. AFK parses the arguments through the Pydantic model. Invalid arguments generate a validation error that's sent back to the LLM for self-correction. If a PolicyEngine is attached, the tool call is checked against policy rules (allow, deny, or request_approval). The tool function runs with validated arguments. Pre/post hooks and middleware execute around the handler. The output is truncated to tool_output_max_chars, stripped of potential prompt injection vectors (if sanitize_tool_output=True), and formatted for the LLM. The sanitized result is appended to the conversation and the LLM generates its next response. Tool patterns Return a string or dict directly. Return a dict for structured data. Use async def for I/O-heavy tools. Access ToolContext in tool handlers. The second parameter must be named ctx or annotated as ToolContext. Deferred background tool calls For long-running operations, a tool can return a deferred handle so the run can continue while work completes in the background. When deferred: 1. Runner emits tool_deferred. 2. Agent continues with other work in the same run. 3. Runner emits tool_background_resolved or tool_background_failed. 4. Resolved tool output is injected back into conversation for next steps. External workers can resolve tickets by writing: - bgtool:{run_id}:{ticket_id}:state - bgtool:{run_id}:latest Status payload example: This pattern is useful for coding agents that start a long build, continue writing docs, then consume build results once available. You can also use runner helpers instead of writing raw state keys: Policy-gated tools Use the PolicyEngine to gate sensitive tool calls: **Policy best practice:** Gate all mutating tools with request_approval or deny by default. Only allow read-only tools without gates. Hooks and middleware AFK provides four extension points for tool execution: **prehooks**, **posthooks**, **tool-level middleware**, and **registry-level middleware**. Each has its own decorator. Prehooks run before the tool handler. They receive the tool's arguments and **must return a dict** compatible with the tool's args_model. Posthooks run after the tool handler. They receive a dict {\"output\": , \"tool_name\": \" \"} and should return a dict with the same shape. Middleware wraps the entire tool execution. It receives call_next, the validated args, and optionally ctx. Registry-level middleware applies to **every tool** in a ToolRegistry. Use for audit logging, rate limiting, or global policy enforcement. Execution order | Layer | Scope | Decorator | Returns | | --------------- | --------------------------- | -------------------------------- | ------------------------------ | | **Prehook** | Single tool, before handler | @prehook(args_model=...) | dict of transformed args | | **Middleware** | Single tool, wraps handler | @middleware(name=...) | Tool output (via call_next) | | **Posthook** | Single tool, after handler | @posthook(args_model=...) | dict with output key | | **Registry MW** | All tools in registry | @registry_middleware(name=...) | ToolResult (via call_next) | Common tools cookbook Prebuilt tools AFK ships with ready-to-use tools for common agent capabilities. These are in the afk.tools.prebuilts module. Runtime tools Filesystem tools scoped to a directory for safe agent exploration: Runtime tools enforce directory-scoped access \u2014 the agent cannot read or list files outside the configured root_dir. Skill tools When an agent has skills configured, AFK generates four skill tools automatically: | Tool | Purpose | | ----------------- | ----------------------------------------------------- | | list_skills | Return metadata for all enabled skills | | read_skill_md | Read a skill's SKILL.md content and checksum | | read_skill_file | Read additional files under a skill directory | | run_skill_command| Execute allowlisted commands with timeout and limits | Skill tools are gated by a SkillToolPolicy that controls command allowlists, output limits, and shell operator restrictions: Next steps Watch tool calls happen in real time. Policy gates, sandbox profiles, and tool allowlists.", + "token_count": 481 + }, + { + "id": "doc_136d7a909d51", + "path": "docs/library/troubleshooting.mdx", + "url": "/library/troubleshooting", + "title": "Troubleshooting", + "description": "Common issues and solutions when building with AFK.", + "headings": [ + "Agent behavior issues", + "Agent keeps calling the same tool repeatedly", + "Add hard limits to prevent runaway loops", + "Agent ignores tools and doesn't call them", + "Agent produces inconsistent outputs", + "Use request-level sampling controls for direct LLM calls", + "Memory issues", + "Conversation doesn't persist between runs", + "Always use thread_id for multi-turn conversations", + "r2 will remember r1's context", + "Verify memory is configured", + "For production, use persistent storage", + "Resume doesn't work", + "Check run_id and thread_id are correct", + "Resume correctly", + "Check checkpoint state directly from the configured memory store", + "LLM issues", + "Rate limit errors", + "Or use exponential backoff for retries", + "Timeout errors", + "Set appropriate timeouts", + "Or per-request timeout via middleware", + "Model not found errors", + "Verify model name is correct", + "Use fallback for resilience", + "Streaming issues", + "Streaming doesn't work", + "Make sure you're iterating correctly", + "Don't mix sync and async", + "WRONG:", + "RIGHT:", + "Streaming disconnects early", + "Use timeout middleware for streaming", + "Cost issues", + "Unexpected high costs", + "ALWAYS set cost limits", + "Token limit errors", + "Compact memory to reduce context", + "Or use a model with larger context", + "Tool issues", + "Tool validation errors", + "Ensure Pydantic model matches tool implementation", + "Tool not found errors", + "Verify tool is attached to agent", + "Verify tool name matches", + "Call with exact name", + "Debug mode", + "Getting help", + "Next steps" + ], + "content_sha256": "b21eb3b6aac297783f21d0585242a6ba2ccdb8038df41fa08915007fc1521053", + "content": "This guide covers common issues encountered when building and deploying AFK agents, with solutions and debugging tips. Agent behavior issues Agent keeps calling the same tool repeatedly **Symptoms:** Agent enters a loop, calling the same tool multiple times without making progress. **Causes:** - Tool output doesn't provide the information the agent needs - Agent instructions don't clarify when to stop - Missing a tool that would help the agent determine completion **Solutions:** **Debug:** Enable verbose logging to see tool call inputs/outputs: --- Agent ignores tools and doesn't call them **Symptoms:** Agent responds with text but doesn't use available tools. **Causes:** - Instructions don't mention the tools or when to use them - Tool descriptions are unclear - Model being used doesn't support function calling well **Solutions:** --- Agent produces inconsistent outputs **Symptoms:** Same input produces different outputs on different runs. **Causes:** - Temperature is set too high - Missing structured output configuration - Non-deterministic system prompt **Solutions:** Memory issues Conversation doesn't persist between runs **Symptoms:** Agent doesn't remember previous messages. **Causes:** - Not using thread_id to link conversations - Memory store not configured correctly - Using in-memory store (loses state on restart) **Solution:** **Check memory backend:** --- Resume doesn't work **Symptoms:** Calling runner.resume() doesn't continue from where the run stopped. **Solutions:** **Debug checkpoints:** LLM issues Rate limit errors **Symptoms:** RateLimitError or 429 responses from LLM provider. **Solutions:** --- Timeout errors **Symptoms:** Requests hang or timeout before completing. **Solutions:** --- Model not found errors **Symptoms:** ModelNotFoundError or InvalidRequestError. **Solutions:** Streaming issues Streaming doesn't work **Symptoms:** run_stream() doesn't return events or returns them all at once. **Solutions:** --- Streaming disconnects early **Symptoms:** Stream ends before completion. **Solutions:** Cost issues Unexpected high costs **Symptoms:** API costs much higher than expected. **Causes:** - Agent in a loop making many LLM calls - No cost limits configured - Expensive model being used unnecessarily **Solutions:** --- Token limit errors **Symptoms:** ContextLengthExceeded or similar errors. **Solutions:** Tool issues Tool validation errors **Symptoms:** ToolValidationError when tools are called. **Solutions:** --- Tool not found errors **Symptoms:** Agent can't find or call a tool. **Solutions:** Debug mode Enable debug mode for detailed logging: Getting help If you can't resolve an issue: 1. Check the GitHub Issues for known issues 2. Enable debug logging and capture the full traceback 3. Include these details when reporting: - AFK version (pip show afk) - Python version - LLM provider and model - Minimal reproduction code - Full error traceback Next steps Understand how AFK components work together. Test agent behavior before shipping. Common patterns and anti-patterns. Detailed API documentation.", + "token_count": 347 + }, + { + "id": "doc_f1e32ed2ffce", + "path": "docs/llms/adapters.mdx", + "url": "/llms/adapters", + "title": "Adapters", + "description": "Built-in LLM providers and custom adapter registration.", + "headings": [ + "Built-in providers", + "Capability comparison", + "Usage", + "OpenAI", + "Anthropic", + "LiteLLM (any provider)", + "Custom provider", + "Custom transport", + "Next steps" + ], + "content_sha256": "4062f20b10ef8ec55493c8418a3fe20da61715e29f3f285979f8f696d0a1872c", + "content": "Providers translate between AFK's normalized contracts (LLMRequest/LLMResponse) and provider-specific APIs. AFK ships with three built-in providers and supports custom providers for internal deployments. Built-in providers Direct integration via the OpenAI Python SDK. Supports all GPT-4.1 and o-series models. Direct integration via the Anthropic SDK. Supports Claude Opus 4.5 and Opus. Proxy adapter for 100+ providers (Azure, Bedrock, Gemini, Mistral, local models, etc.). Capability comparison | Feature | OpenAI | Anthropic | LiteLLM | | -------------------- | ------------------------------------------------------ | ------------------------------------------------------ | --------------------------------------------------------------------------- | | Text generation | | | | | Tool calling | | | (provider-dependent) | | Structured output | | | Provider-dependent | | Streaming | | | | | Vision (image input) | | | Provider-dependent | | Custom endpoints | | | | Usage Custom provider Register your own provider for unsupported inference servers: Custom transport Use provider-specific settings for custom endpoints, proxy URLs, or credentials: Next steps Retry, caching, rate limiting, and circuit breaking. How agents resolve and use LLM clients.", + "token_count": 106 + }, + { + "id": "doc_eeed52120ccf", + "path": "docs/llms/agent-integration.mdx", + "url": "/llms/agent-integration", + "title": "Agent Integration", + "description": "How agents resolve models and interact with the LLM layer.", + "headings": [ + "Model resolution", + "How the runner uses the LLM", + "Request construction", + "Streaming integration", + "Error handling", + "Model selection guide", + "Next steps" + ], + "content_sha256": "8f69ff34bc2ca6056745652bec1d0ebcb8e395d7930aaa87e81518f5e05b8619", + "content": "This page explains how agents connect to the LLM layer \u2014 from model resolution to request construction, streaming, and error handling. Model resolution Agents can specify their model in two ways: Pass a model name string. AFK resolves it to an LLM client using the default provider. The resolution order: 1. Check agent.model_resolver (custom function) 2. Check registered adapters for matching provider prefix 3. Default to OpenAI adapter Pass a pre-configured LLMClient for full control over provider settings. How the runner uses the LLM On each step of the agent loop: Request construction The runner builds an LLMRequest from multiple sources: | Source | Contributes | Priority | | -------------------- | ----------------------- | -------- | | Agent.instructions | System message | Highest | | Thread history | Previous messages | \u2014 | | user_message | User message | \u2014 | | Agent.tools | Tool schemas | \u2014 | | Agent.subagents | Transfer tool schemas | \u2014 | | RunnerConfig | Temperature, max_tokens | Lowest | Streaming integration When using run_stream(), the runner passes through streaming events from the LLM: text_delta events come from two paths: - Provider streaming when the adapter supports stream deltas. - Runner fallback chunking of final text for non-streaming providers. Tool and step events are generated by the runner. Error handling LLM errors are classified and handled automatically: | Error | Classification | Runner behavior | | ---------------------------- | -------------- | -------------------------- | | Rate limit (429) | Retryable | Retry with backoff | | Server error (500, 502, 503) | Retryable | Retry with backoff | | Auth error (401, 403) | Terminal | Fail the run | | Invalid request (400) | Terminal | Fail the run | | Timeout | Retryable | Retry (if attempts remain) | | Circuit breaker open | Terminal | Try fallback model or fail | | All retries exhausted | Terminal | Try fallback model or fail | Model selection guide Treat these as example starting points. Confirm current model names, prices, and rate limits with the provider you use. | Task | Starting model | Why | | -------------------------- | ------------------------------ | ----------------------------------- | | Simple Q&A, classification | gpt-4.1-nano | Fast, cheap, good enough | | General purpose with tools | gpt-4.1-mini | Best balance of cost and capability | | Complex reasoning, coding | gpt-4.1 or claude-opus-4-5 | Better at multi-step reasoning | | Cost-sensitive batch | gpt-4.1-nano | Lowest cost per token | | Higher quality generation | gpt-4.1 + low temperature | More capability, less variation | Next steps The step loop and execution engine. Real-time event streaming API.", + "token_count": 261 + }, + { + "id": "doc_e8794369a1a4", + "path": "docs/llms/contracts.mdx", + "url": "/llms/contracts", + "title": "Contracts", + "description": "LLMRequest and LLMResponse \u2014 the data that flows across the LLM boundary.", + "headings": [ + "The flow", + "LLMRequest", + "LLMResponse", + "Structured output", + "Message types", + "Next steps" + ], + "content_sha256": "0ef6386027791d9122a28e938ca2f5752a67f5289f01989c62554bea9d29d5ac", + "content": "Every LLM interaction in AFK flows through two typed contracts: LLMRequest (what you send) and LLMResponse (what you get back). These contracts normalize the differences between providers so your agent code never touches provider-specific types. The flow The adapter translates between AFK's normalized contracts and the provider's native format. LLMRequest | Field | Type | Purpose | | --- | --- | --- | | messages | list[Message] | Conversation history (system, user, assistant, tool) | | model | str | Model identifier | | tools | list[ToolSchema] | Available tool schemas | | temperature | float | Sampling temperature (0.0\u20132.0) | | max_tokens | int \\| None | Max output tokens | | top_p | float \\| None | Nucleus sampling | | response_format | ResponseFormat \\| None | Structured output format | | stop | list[str] \\| None | Stop sequences | LLMResponse | Field | Type | Purpose | | --- | --- | --- | | content | str \\| None | Text response from the model | | tool_calls | list[ToolCall] | Tool calls requested by the model | | model | str | Model that generated the response | | usage | Usage | Token counts (prompt, completion, total) | | finish_reason | str | Why generation stopped (stop, tool_calls, length) | | latency_ms | float | End-to-end request latency | Structured output Request structured JSON output with a Pydantic model: Message types | Role | Purpose | Source | | ----------- | ------------------ | ----------------------------- | | system | Agent instructions | From Agent.instructions | | user | User input | From user_message parameter | | assistant | Model responses | Generated by the LLM | | tool | Tool results | From tool execution | Next steps Built-in providers and custom adapters. Retry, caching, rate limiting, and circuit breaking.", + "token_count": 175 + }, + { + "id": "doc_7320ab982fab", + "path": "docs/llms/control-and-session.mdx", + "url": "/llms/control-and-session", + "title": "Control & Session", + "description": "Retry, caching, rate limiting, and circuit breaking for LLM calls.", + "headings": [ + "Policy pipeline", + "Built-in profiles", + "Development: no retry, no caching, fast failures", + "Production: retry, circuit breaker, rate limiting, caching", + "Individual policies", + "Fallback chains", + "If gpt-4.1 fails \u2192 try gpt-4.1-mini \u2192 try gpt-4.1-nano", + "Tuning cheat sheet", + "Next steps" + ], + "content_sha256": "10d156ced976efb12c419c33d65e5686c7429902256c9e33be26a1add88c39a7", + "content": "The LLM runtime includes built-in policies for handling transient failures, managing costs, and protecting against provider outages. Configure them via the builder profile or individual policy settings. Policy pipeline Every LLM request passes through this policy chain: Built-in profiles Use profile() to apply a curated set of policies: | Profile | Retry | Cache | Rate Limit | Circuit Breaker | Timeout | | ------------- | ------------------------------- | ------------------- | ---------- | ---------------------- | ------- | | development | None | None | None | None | 30s | | production | 3 attempts, exponential backoff | In-memory, 5min TTL | 60 req/min | 5 failures \u2192 30s open | 60s | | batch | 5 attempts, longer backoff | None | 20 req/min | 10 failures \u2192 60s open | 120s | Individual policies Configure each policy independently with create_llm_client(): Retry transient LLM failures with exponential backoff. | Parameter | Default | Description | | --- | --- | --- | | max_retries | 3 | Retry attempts after the initial request | | backoff_base_s | 0.5 | Initial delay in seconds | | backoff_jitter_s | 0.15 | Jitter added to retry delays | | require_idempotency_key | True | Require idempotency keys for retried requests | Stop calling a failing provider to prevent cascading failures. Prevent exceeding provider rate limits. Cache identical requests to reduce cost and latency. Cache keys are derived from request content, model settings, response model, session/checkpoint tokens, and any configured cache namespace. Cached rows do not retain provider request IDs, session tokens, checkpoint tokens, or raw provider payloads. Hard timeout on LLM requests. Fallback chains Configure a chain of models to try when the primary model fails: Tuning cheat sheet | Goal | Setting | | ----------------- | ------------------------------------------------------------- | | Reduce costs | Lower temperature, use cheaper model, enable caching | | Reduce latency | Enable caching, use faster model, set tight timeout | | Handle outages | Enable retry + circuit breaker, add fallback chain | | High throughput | Set rate limits high, use batch profile, increase concurrency | | Consistent output | Set temperature=0.0, enable structured output | Next steps How agents resolve and use LLM clients. Monitor LLM call latency, errors, and costs.", + "token_count": 238 + }, + { + "id": "doc_f92685a34f7e", + "path": "docs/llms/index.mdx", + "url": "/llms/", + "title": "LLM Layer", + "description": "Provider-portable LLM runtime with retry, caching, circuit breaking, and middleware.", + "headings": [ + "The LLMBuilder", + "Middleware", + "Built-in middleware", + "Custom middleware", + "Middleware protocols", + "Supported providers", + "Example model choices", + "How agents use the LLM layer", + "Option 1: Model name (auto-resolved)", + "Option 2: Pre-built client (full control)", + "Next steps" + ], + "content_sha256": "32c7ac94efe23450adc9126031e9f87e3e689349369a82f600c67fbdd16f86a1", + "content": "The LLM layer normalizes communication with language models across all supported providers. Your agent code uses provider-agnostic contracts (LLMRequest / LLMResponse) while built-in adapters handle the provider-specific details. The LLMBuilder Create LLM clients with the builder pattern: Middleware The LLM layer supports middleware for intercepting and transforming requests and responses. Use middleware for logging, tracing, caching, and custom request/response handling. Built-in middleware Custom middleware Middleware protocols | Protocol | Operation | Signature | | --- | --- | --- | | LLMChatMiddleware | Non-streaming chat | async (call_next, req: LLMRequest) -> LLMResponse | | LLMEmbedMiddleware | Embeddings | async (call_next, req: EmbeddingRequest) -> EmbeddingResponse | | LLMStreamMiddleware | Streaming chat | (call_next, req: LLMRequest) -> AsyncIterator[LLMStreamEvent] | Supported providers GPT-4.1, GPT-4.1-mini, GPT-4.1-nano, o-series Claude Opus and Sonnet families 100+ providers via the LiteLLM proxy All providers expose the same LLMClient interface. Your agent code never touches provider-specific types. Example model choices Use these as starting points, then verify model availability, pricing, and context limits with your provider account. | Scenario | Starting point | | -------------------------- | ----------------------------------------------- | | General purpose | OpenAI gpt-4.1-mini | | Complex reasoning | OpenAI gpt-4.1 or Anthropic claude-opus-4-5 | | Cost-sensitive | OpenAI gpt-4.1-nano | | Non-OpenAI/Anthropic model | LiteLLM adapter | | Custom or self-hosted | Custom adapter | How agents use the LLM layer You rarely build LLMClient directly. Agents resolve their model automatically: Next steps LLMRequest / LLMResponse \u2014 what flows across the boundary. Built-in providers and custom adapter registration.", + "token_count": 176 + } + ] +} diff --git a/skills/afk-maintainer/references/afk-docs/docs-index.jsonl b/skills/afk-maintainer/references/afk-docs/docs-index.jsonl new file mode 100644 index 0000000..70a2e81 --- /dev/null +++ b/skills/afk-maintainer/references/afk-docs/docs-index.jsonl @@ -0,0 +1,63 @@ +{"id": "doc_3aa0b3792d22", "path": "docs/docs.json", "url": "/docs.json", "title": "docs", "description": "", "headings": [], "content_sha256": "9808c9300f5fc80e270e826e601d4e2ec57dbe59c97606f751af389bc1cd54d5", "content": "{ \"$schema\": \"https://mintlify.com/schema.json\", \"name\": \"AFK\", \"logo\": { \"light\": \"/logo-light.svg\", \"dark\": \"/logo-dark.svg\", \"href\": \"https://github.com/arpan404/afk\" }, \"theme\": \"mint\", \"layout\": \"sidenav\", \"favicon\": \"/favicon.svg\", \"topbar\": { \"style\": \"gradient\" }, \"topbarCtaButton\": { \"type\": \"github\", \"url\": \"https://github.com/arpan404/afk\" }, \"topbarLinks\": [ { \"type\": \"link\", \"name\": \"Quickstart\", \"url\": \"/library/quickstart\", \"arrow\": false }, { \"type\": \"link\", \"name\": \"Examples\", \"url\": \"/library/examples/\", \"arrow\": false }, { \"type\": \"link\", \"name\": \"API\", \"url\": \"/library/api-reference\", \"arrow\": false }, { \"type\": \"link\", \"name\": \"Skills\", \"url\": \"/library/agent-skills\", \"arrow\": false } ], \"sidebar\": { \"items\": \"undecorated\" }, \"rounded\": \"default\", \"colors\": { \"primary\": \"#0B6E4F\", \"light\": \"#2FB67E\", \"dark\": \"#084C38\" }, \"footer\": { \"socials\": { \"github\": \"https://github.com/arpan404/afk\" } }, \"navigation\": { \"tabs\": [ { \"tab\": \"Build with AFK\", \"groups\": [ { \"group\": \"Start Here\", \"pages\": [ \"index\", \"library/overview\", \"library/mental-model\", \"library/quickstart\", \"library/learn-in-15-minutes\", \"library/how-to-use-afk\" ] }, { \"group\": \"Core Building Blocks\", \"pages\": [ \"library/agents\", \"library/core-runner\", \"library/tools\", \"library/streaming\", \"library/memory\", \"library/system-prompts\", \"library/agent-skills\" ] }, { \"group\": \"LLM Runtime\", \"pages\": [ \"llms/index\", \"llms/contracts\", \"llms/adapters\", \"llms/control-and-session\", \"llms/agent-integration\" ] }, { \"group\": \"Production\", \"pages\": [ \"library/building-with-ai\", \"library/evals\", \"library/observability\", \"library/security-model\", \"library/task-queues\", \"library/deployment\", \"library/performance\", \"library/troubleshooting\" ] }, { \"group\": \"Integrations\", \"pages\": [ \"library/mcp-server\", \"library/a2a\", \"library/messaging\" ] } ] }, { \"tab\": \"Maintain AFK\", \"groups\": [ { \"group\": \"Contributor Guide\", \"pages\": [ \"library/developer-guide\", \"library/architecture\", \"library/public-imports-and-function-improvement\", \"library/tested-behaviors\" ] }, { \"group\": \"Internal Contracts\", \"pages\": [ \"library/agentic-behavior\", \"library/agentic-levels\", \"library/failure-policy-matrix\", \"library/tool-call-lifecycle\", \"library/tools-system-walkthrough\", \"library/run-event-contract\", \"library/checkpoint-schema\", \"library/llm-interaction\", \"library/debugger\" ] }, { \"group\": \"Migration\", \"pages\": [\"library/migration\"] } ] }, { \"tab\": \"Reference\", \"groups\": [ { \"group\": \"API and Configuration\", \"pages\": [ \"library/api-reference\", \"library/configuration-reference\", \"library/environment-variables\", \"library/full-module-reference\" ] } ] }, { \"tab\": \"Examples\", \"groups\": [ { \"group\": \"Runnable Snippets\", \"pages\": [ \"library/examples/index\", \"library/snippets/01_minimal_chat_agent\", \"library/snippets/02_policy_with_hitl\", \"library/snippets/03_subagents_with_router\", \"library/snippets/04_resume_and_compact\", \"library/snippets/05_direct_llm_structured_output\", \"library/snippets/06_tool_registry_security\", \"library/snippets/07_tool_hooks_and_middleware\", \"library/snippets/08_prebuilt_runtime_tools\", \"library/snippets/09_system_prompt_loader\", \"library/snippets/10_streaming_chat_with_memory\", \"library/snippets/11_cost_monitoring\", \"library/snippets/12_mcp_client_integration\", \"library/snippets/13_multi_model_fallback\", \"library/snippets/14_production_client\" ] } ] } ] } }", "token_count": 289} +{"id": "doc_deb460ffa5ee", "path": "docs/index.mdx", "url": "/", "title": "AFK - Agent Forge Kit", "description": "Build reliable Python agents with typed tools, runtime controls, and production observability.", "headings": ["Choose your path", "Install", "First agent", "Core capabilities", "AFK skills", "How AFK fits together", "Next steps"], "content_sha256": "e7a820d1c12c3306b696c759c97965bfc83dd0ae9847e39832b1caa2dd31a7b9", "content": "AFK is a Python 3.13+ SDK for building AI agents that need to run reliably outside a demo. You define agents, tools, policies, and memory as typed Python contracts. The runner handles the agent loop, LLM calls, tool execution, streaming, checkpoints, telemetry, and safety limits. Choose your path Start here if you are building an application, workflow, chatbot, coding tool, or production agent on top of AFK. Start here if you are changing AFK itself, reviewing internals, or updating public contracts. Install The distribution package is afk-py; the import package is afk. Set an LLM provider key before running examples: First agent What matters: - Agent is configuration: model, instructions, tools, policies, subagents, and defaults. - Runner is execution: LLM calls, tool calls, memory, streaming, checkpoints, and telemetry. - AgentResult is the run record: final text, terminal state, tool/subagent records, usage, and cost. Core capabilities Declarative agent definitions with instructions, tools, subagents, skills, MCP servers, and safety limits. Sync, async, and streaming execution with lifecycle control and thread continuity. Typed Python tools with Pydantic validation, hooks, middleware, sandboxing, and bounded output. Provider-portable LLM clients with retries, timeouts, rate limits, caching, circuit breakers, and fallback chains. Thread state, checkpoints, retention, compaction, and persistent stores. Evals, observability, queues, security controls, deployment guidance, and troubleshooting. AFK skills Install the AFK skills with Vercel's Skills CLI when you want Codex or another supported agent to use AFK-specific guidance: Use afk-coder when building applications with AFK. Use afk-maintainer when reviewing or changing AFK itself. See Agent Skills for details. How AFK fits together Next steps Build one runnable agent with one typed tool. Walk through agents, tools, streaming, memory, and safety. Find complete snippets by scenario. Check canonical imports, signatures, result fields, and stability rules.", "token_count": 228} +{"id": "doc_1c5f37ea0ad3", "path": "docs/library/a2a.mdx", "url": "/library/a2a", "title": "Agent-to-Agent (A2A)", "description": "Cross-system agent communication with authentication and delivery guarantees.", "headings": ["Architecture", "Three integration layers", "Request flow", "Invocation contracts", "Hosting an A2A service", "Define the agent", "Create auth provider", "Start the server", "Authentication providers", "Google A2A adapter", "Security considerations", "Next steps"], "content_sha256": "654fb4bacfee24f10a177606048474a5012fa7cb25ee7df203b1869ede8447cd", "content": "The A2A protocol enables agent communication **across system boundaries** \u2014 between services, organizations, or deployment environments. It builds on internal messaging by adding authentication, authorization, and external transport. Architecture Three integration layers | Layer | What it does | When you need it | | --------------------- | --------------------------------- | ------------------------------------- | | **Internal Protocol** | Typed envelopes with idempotency | Always (agents in the same system) | | **Auth Provider** | Token validation, caller identity | When agents are in different services | | **External Adapter** | HTTP/gRPC transport, discovery | When agents are in different systems | Request flow The server validates the auth token, extracts the caller identity, and checks authorization rules. The target agent executes locally with a Runner, using the invocation request as input. Invocation contracts Hosting an A2A service Expose your agents as an A2A-accessible service: Authentication providers AFK ships with three auth providers: Permits all requests without authentication. **Never use in production.** Validates requests against pre-shared API keys with HMAC-SHA256 hashing. Validates JWT tokens with configurable issuer and audience claims. Google A2A adapter For interoperability with Google's A2A protocol, use the Google adapter: The adapter wraps a configured Google A2A SDK client behind AFK's AgentCommunicationProtocol. Security considerations | Concern | Mechanism | | -------------------- | ---------------------------------------------------------------- | | **Authentication** | Token-based (API keys, JWT, OAuth) | | **Authorization** | Per-agent access control (which callers can invoke which agents) | | **Idempotency** | idempotency_key prevents duplicate processing on retries | | **Rate limiting** | Configure per-caller request limits | | **Input validation** | All requests validated against AgentInvocationRequest schema | | **Cost isolation** | Each invocation has its own FailSafeConfig budget | **Always authenticate A2A endpoints.** An unauthenticated A2A server allows anyone to invoke your agents, consuming your LLM API credits. Next steps Async job processing for long-running work. Expose tools via the Model Context Protocol.", "token_count": 216} +{"id": "doc_b6088784caab", "path": "docs/library/agent-skills.mdx", "url": "/library/agent-skills", "title": "Agent Skills", "description": "Reusable knowledge bundles that agents load on demand.", "headings": ["When to use skills vs tools", "Creating a skill", "SKILL.md format", "Deployment Guide", "Prerequisites", "Deploy Steps", "Rollback", "Monitoring", "Installing repository skills", "Registering skills on an agent", "Built-in skill tools", "How the agent uses skills", "Security controls", "Design guidelines", "Next steps"], "content_sha256": "69777dc325761bdda7d06e8dee698faab76ddd3ee55f7301e7c9143018f6aa8f", "content": "**Skills** are reusable knowledge bundles \u2014 directories of instructions, scripts, and resources that extend an agent's capabilities without being hard-coded into the system prompt. Agents discover and read skills at runtime using built-in skill tools. When to use skills vs tools | Feature | Tools | Skills | | ---------------- | --------------------------------------------------- | ---------------------------------------- | | **What it is** | A Python function the agent calls | A directory of knowledge the agent reads | | **When it runs** | During the agent loop (function execution) | During the agent loop (file reads) | | **Best for** | Taking actions (API calls, calculations, mutations) | How-to guides, playbooks, reference docs | | **Schema** | Typed Pydantic model | Unstructured markdown + files | | **Side effects** | Yes (executes code) | Read-only (by default) | **Rule of thumb:** If the agent needs to *do something*, use a tool. If it needs to *know something*, use a skill. Creating a skill A skill is a directory with a SKILL.md file: SKILL.md format The YAML frontmatter (name, description) helps the agent decide which skill is relevant. The markdown body is the knowledge content. Installing repository skills This repository includes two skills for agentic development: - afk-coder for building applications with AFK. - afk-maintainer for reviewing or changing AFK itself. Install them with Vercel's Skills CLI: Install one skill at a time: For another repository, use the same shape: The Skills CLI installs the selected skill into the configured agent environment. See skills.sh for CLI details and supported agents. Registering skills on an agent AFK scans the skills_dir and registers each subdirectory as an available skill. The agent can discover and read skills using built-in tools. Built-in skill tools When an agent has skills, AFK automatically provides these tools: | Tool | Purpose | Returns | | ------------------- | ---------------------------------------- | ----------------------------------------- | | list_skills | List all available skills | [{\"name\": \"...\", \"description\": \"...\"}] | | read_skill_md | Read a skill's SKILL.md | Markdown content | | read_skill_file | Read any file in a skill directory | File content | | run_skill_command | Execute a command from a skill's scripts | Command output | How the agent uses skills The agent autonomously decides which skills to read based on the user's question. You don't need to explicitly tell it which skill to use. Security controls By default, read_skill_file only allows reading files within the skill directory. Path traversal (../) is blocked. run_skill_command is gated by policy. You can allowlist specific commands: To disable command execution entirely, only register the read-only skill tools: Design guidelines - **One skill per topic.** Keep skills focused (e.g., deployment, monitoring, database) rather than creating one giant knowledge base. - **Write SKILL.md for the agent, not a human.** The agent reads this file, so be explicit about what to do in each scenario. - **Include examples.** Show the agent what good output looks like. - **Version your skills.** Store skills in git alongside your agent code. - **Gate commands.** Always use policy rules for run_skill_command. Next steps Define typed functions for taking actions. Policy gates for tools and skill commands.", "token_count": 347} +{"id": "doc_f12bbbc027a7", "path": "docs/library/agentic-behavior.mdx", "url": "/library/agentic-behavior", "title": "Agentic Behavior", "description": "Orchestrate multi-agent DAGs with fan-out, join policies, and backpressure.", "headings": ["Quick example", "Specialist agents", "Coordinator", "Delegation DAG model", "Orchestration pipeline", "Join policies", "Failure handling", "Backpressure", "When to use multi-agent delegation", "Next steps"], "content_sha256": "01c6fa33586359217ba0e6ce21454895f25b3d20c57a0b496abe2ca8423d3e25", "content": "When a single agent isn't enough, AFK lets you orchestrate teams of specialist agents using **delegation DAGs** \u2014 directed acyclic graphs where a coordinator fans out work, collects results, and combines them. Quick example Delegation DAG model The coordinator makes all delegation decisions. Subagents don't talk to each other directly \u2014 they report back to the coordinator, which decides what to do next. Orchestration pipeline The coordinator decides which subagents to call and in what order, based on the user's request and its instructions. AFK validates the delegation request: does the subagent exist? Are the arguments valid? Does the policy allow it? The subagent is enqueued for execution. With fan-out, multiple subagents can run in parallel. Each subagent runs a full agent loop (LLM calls, tool execution, etc.) and returns an AgentResult. Results are collected according to the join policy. The coordinator receives them and decides whether to delegate more or produce a final response. Join policies When multiple subagents run in parallel (fan-out), the **join policy** controls how the coordinator handles results: All subagents must succeed. Any failure fails the entire delegation batch. **Use when:** Every subagent's output is essential for the final result. Failures are tolerated. The coordinator proceeds with whatever results are available. **Use when:** Some subagents provide nice-to-have additions (e.g., fact-checking, sentiment analysis). Return as soon as one subagent succeeds. Cancel the rest. **Use when:** Multiple agents attempt the same task with different strategies. First correct answer wins. Succeed when N out of M subagents complete successfully. **Use when:** You need consensus from a majority of agents. Failure handling | Failure | all_required | allow_optional_failures | first_success | quorum | | ------------------ | ------------------------------------------------------------------ | ------------------------------------------------------------------------------------------- | ------------------------------------------------------------------ | ------------------------------------------------------------------ | | One subagent fails | Batch fails | Continue with others | Continue waiting | Continue if quorum not needed | | All subagents fail | Batch fails | Batch fails | Batch fails | Batch fails | | Timeout | Batch fails | Use available results | Batch fails | Depends on completed count | Backpressure AFK limits concurrent subagent executions to prevent resource exhaustion: When the concurrency limit is reached, additional subagent calls are queued and execute as slots become available. When to use multi-agent delegation | Scenario | Single agent | Multi-agent | | --------------------------------- | ------------------------------------------------------------------------ | -------------------------------------------------------------------------------------- | | Simple Q&A or classification | | Overkill | | Task needs different expertise | Consider | | | Need to parallelize work | N/A | | | Task needs consensus/verification | N/A | | | Tight latency budget | (fewer LLM calls) | (more LLM calls) | Next steps Capability maturity model \u2014 when to add agents. How orchestration and execution are separated.", "token_count": 303} +{"id": "doc_57e2139f0873", "path": "docs/library/agentic-levels.mdx", "url": "/library/agentic-levels", "title": "Agentic Levels", "description": "A capability maturity model \u2014 know when to add complexity.", "headings": ["The five levels", "Capability comparison", "Decision guide", "Signals to level up", "Next steps"], "content_sha256": "05994fd901c3045dec62b4bac7a8f98bcb6c12d58749ed51680689589ed90bba", "content": "AFK applications progress through five levels of capability. Each level adds features and complexity. Start at Level 1 and move up only when you have clear signals that the current level isn't enough. The five levels One agent, one model, no tools. The simplest possible setup. **AFK features used:** Agent, Runner, run_sync **Good for:** Text classification, summarization, translation, simple Q&A **Move up when:** The agent needs to take actions or access external data Add typed tool functions for the agent to call. **AFK features added:** @tool, Pydantic models, FailSafeConfig **Good for:** RAG, data lookup, calculations, API integrations **Move up when:** Different parts of the task need different expertise or models Coordinator delegates to specialist subagents. **AFK features added:** subagents, join policies, backpressure **Good for:** Complex tasks, parallel work, specialist expertise, consensus **Move up when:** Tasks take minutes, need async processing, or need queue-based reliability Decouple producers and consumers with task queues. Long-running jobs execute asynchronously. **AFK features added:** TaskQueue, TaskItem, workers, dead-letter handling **Good for:** Batch processing, background jobs, retryable pipelines, high-throughput **Move up when:** You need cross-system communication or external agent interop Agents communicate across systems using the A2A protocol with authenticated endpoints. **AFK features added:** InternalA2AProtocol, A2AServiceHost, auth providers, external adapters **Good for:** Microservice agent meshes, third-party integrations, federated AI systems **This is the ceiling** \u2014 most applications don't need Level 5. Capability comparison | Capability | L1 | L2 | L3 | L4 | L5 | | -------------------------- | ------------------------------------------------------ | ------------------------------------------------------ | ------------------------------------------------------ | ------------------------------------------------------ | ------------------------------------------------------ | | Text generation | | | | | | | Tool calling | \u2014 | | | | | | Multi-agent delegation | \u2014 | \u2014 | | | | | Async processing | \u2014 | \u2014 | \u2014 | | | | Cross-system communication | \u2014 | \u2014 | \u2014 | \u2014 | | | Policy engine | | | | | | | Observability | | | | | | | Evals | | | | | | = essential at this level    = recommended    \u2014 = not applicable Decision guide > [!TIP] > **Start at Level 1.** The simplest system that works is the best system. Premature complexity is the most common mistake in agent design. Signals to level up | Signal | Current Level | Move to | | ----------------------------------------------- | ------------- | -------------------------- | | Agent needs external data | 1 | 2 \u2014 Add tools | | One prompt can't cover all expertise | 2 | 3 \u2014 Split into specialists | | Users waiting too long for results | 3 | 4 \u2014 Queue for async | | Tasks fail and need to be retried automatically | 3 | 4 \u2014 Queue with DLQ | | Other systems need to invoke your agents | 4 | 5 \u2014 Expose A2A | | Third-party agents need to use your tools | 4 | 5 \u2014 Use MCP server | Next steps DAG orchestration, join policies, and fan-out. How AFK separates orchestration from execution.", "token_count": 296} +{"id": "doc_e67e762fb6ab", "path": "docs/library/agents.mdx", "url": "/library/agents", "title": "Agents", "description": "Define agents with instructions, tools, and subagents.", "headings": ["Your first agent", "Agent fields reference", "Single agent vs multi-agent", "How subagent delegation works", "Adding safety limits", "Policy-aware agents", "Design guidelines", "Next steps"], "content_sha256": "41b56ac8a4f6f74fd32265ec3b5c0ad682960f13a814fb9faf3517c23c943143", "content": "An **Agent** is a configuration object that describes _what_ your AI agent is \u2014 its identity, capabilities, and boundaries. Agents don't execute themselves; they're run by a Runner. Your first agent Those are the fields most examples should set. model is the only required constructor argument, but name and instructions make traces and behavior easier to understand. Agent fields reference | Field | Type | Default | Purpose | | ------------------ | -------------------- | -------- | --------------------------------------------------------------- | | model | str or LLM | required | LLM model name or pre-built client instance | | name | str | None | Agent identity for logs, telemetry, and subagent routing | | instructions | str | None | System prompt \u2014 what the agent knows and how it behaves | | instruction_file | str or Path | None | Path to a .txt or .md file containing the system prompt | | prompts_dir | str or Path | None | Directory containing prompt files resolved by the prompt store | | tools | list[tool] | None | Typed functions the agent can call | | subagents | list[Agent] | None | Specialist agents this agent can delegate to | | skills | list[str] | None | Skill names to resolve from skills_dir | | skills_dir | str or Path | \".agents/skills\" | Directory containing skill packs | | mcp_servers | list[MCPServerLike]| None | MCP server configs for external tool discovery | | fail_safe | FailSafeConfig | defaults | Step limits, cost budgets, timeout, and failure policies | | context_defaults | dict | None | Default JSON context merged into each run before caller context | | inherit_context_keys | list[str] | None | Context keys inherited from parent agent in delegation | | model_resolver | callable | None | Custom function to resolve model names to LLM clients | | instruction_roles | list[InstructionRole] | None | Structured instruction sections with role-based ordering | | policy_roles | list[PolicyRole] | None | Role-based policy rules applied during execution | | policy_engine | PolicyEngine | None | Policy engine for tool/action gating | | subagent_router | SubagentRouter | None | Custom routing logic for subagent delegation | | max_steps | int | 20 | Maximum agent loop iterations | | tool_parallelism | int | None | Max concurrent tool executions per step | | subagent_parallelism_mode | str | \"configurable\" | How subagent concurrency is managed | | reasoning_enabled| bool | None | Enable provider-supported reasoning controls | | reasoning_effort | str | None | Thinking effort level (e.g. \"low\", \"medium\", \"high\") | | reasoning_max_tokens | int | None | Token budget for extended thinking | | skill_tool_policy| SkillToolPolicy | None | Security policy for skill-provided tool execution | | enable_skill_tools | bool | True | Whether to expose skill tools to the agent | | enable_mcp_tools | bool | True | Whether to expose MCP-discovered tools to the agent | | runner | Runner | None | Pre-bound runner instance (advanced usage) | Single agent vs multi-agent A single agent handles everything. Best for focused tasks. **Use when:** The task is well-defined and doesn't need specialized sub-expertise. A coordinator delegates to specialist subagents. **Use when:** Different parts of the task need different expertise, models, or tools. How subagent delegation works When an agent has subagents, AFK automatically generates transfer tools (transfer_to_researcher, transfer_to_writer). The coordinator calls these like any other tool. Each subagent runs a **full agent loop** with its own model, instructions, and tools. The coordinator sees only the subagent's final_text. Adding safety limits Every agent should have a FailSafeConfig in production: **Always set max_total_cost_usd** in production. A runaway agent loop can spend significant API credits in minutes. Policy-aware agents Attach a PolicyEngine to control what the agent can do: Policy decisions: allow (default), deny, request_approval (human-in-the-loop), or request_user_input. Design guidelines - **Start with one agent.** Only add subagents when you have clear evidence that the task needs specialized expertise. - **Keep instructions focused.** Vague instructions produce vague results. Tell the agent exactly what to do and what not to do. - **Use typed tools.** Every tool argument should be a Pydantic model. Untyped arguments bypass validation. - **Set cost limits early.** Add FailSafeConfig before your first deployment, not after your first runaway bill. Next steps How agents are executed \u2014 lifecycle, API modes, and state management. Define typed tool functions with validation and policy gates.", "token_count": 481} +{"id": "doc_bbc97cbff254", "path": "docs/library/api-reference.mdx", "url": "/library/api-reference", "title": "API Reference", "description": "Canonical public imports and core signatures for AFK.", "headings": ["Import map", "Agent", "Runner", "RunnerConfig", "FailSafeConfig", "Tool decorator", "AgentResult", "API stability"], "content_sha256": "15f83948b7edc7c528d459c0fd141ae9ab71d30f83a5635e768c12bdc145e3fa", "content": "This page is the public import contract for application developers. Use these imports in docs, examples, tests that exercise public behavior, and downstream applications. For field-by-field configuration, see Configuration Reference. For generated module detail, see Full Module Reference. Import map | Task | Public import | | --- | --- | | Define an agent | from afk.agents import Agent | | Configure fail-safe limits | from afk.agents import FailSafeConfig | | Configure policy rules | from afk.agents import PolicyEngine, PolicyRule | | Implement dynamic policy hooks | from afk.agents import PolicyRole | | Run an agent | from afk.core import Runner | | Configure the runner | from afk.core import RunnerConfig | | Consume streaming events | from afk.core import AgentStreamEvent, AgentStreamHandle | | Define a tool | from afk.tools import tool | | Access tool context | from afk.tools import ToolContext | | Build an LLM client | from afk.llms import LLMBuilder | | Run eval suites | from afk.evals import run_suite | | Define eval cases | from afk.evals import EvalCase, EvalBudget | | Create memory stores | from afk.memory import InMemoryMemoryStore, SQLiteMemoryStore | | Create task queues | from afk.queues import InMemoryTaskQueue, RedisTaskQueue, TaskWorker | | Expose MCP tools | from afk.mcp import MCPServer | | Use A2A messaging | from afk.messaging import InternalA2AProtocol | Do not use src.afk.* imports in user-facing docs. Agent Only model is required. Most examples should also set name and instructions for clear traces and predictable behavior. Runner Common methods: | Method | Use when | Returns | | --- | --- | --- | | runner.run_sync(agent, user_message=..., thread_id=...) | Scripts, CLIs, tests without an existing event loop | AgentResult | | await runner.run(agent, user_message=..., thread_id=...) | Async services, workers, APIs | AgentResult | | await runner.run_stream(agent, user_message=..., thread_id=...) | Chat UIs and progress streams | AgentStreamHandle | | await runner.resume(agent, run_id=..., thread_id=..., context=...) | Continue from persisted state | AgentResult | | await runner.compact_thread(thread_id=...) | Apply memory retention/compaction | MemoryCompactionResult | run_sync() is a convenience wrapper for synchronous code. Use await runner.run(...) inside async applications. RunnerConfig Use Configuration Reference for the complete set of runner fields and defaults. FailSafeConfig Set max_total_cost_usd for production agents. The default is intentionally unset because acceptable budgets vary by application. Tool decorator Tool functions may be sync or async. They may accept (args), (args, ctx), or (ctx, args). AgentResult Important fields: | Field | Meaning | | --- | --- | | final_text | Final assistant text | | state | Terminal run state | | requested_model | Model requested by the caller or agent | | normalized_model | Effective normalized model when available | | provider_adapter | Provider/adapter used when available | | tool_executions | Ordered tool execution records | | subagent_executions | Ordered subagent execution records | | usage_aggregate | Aggregated input/output/total tokens | | total_cost_usd | Estimated total cost when available | | state_snapshot | Terminal runtime snapshot payload | API stability - Public docs and examples should use imports from afk.agents, afk.core, afk.tools, afk.llms, afk.memory, afk.queues, afk.mcp, afk.messaging, afk.observability, and afk.evals. - Internal modules under package subdirectories may change faster than public exports. - When changing public exports, update this page, Public API Rules, examples, and generated agent-facing docs.", "token_count": 391} +{"id": "doc_4e1ebaf40267", "path": "docs/library/architecture.mdx", "url": "/library/architecture", "title": "Architecture", "description": "How AFK separates orchestration from execution.", "headings": ["Layered architecture", "Module boundaries", "Runtime sequence", "Contract boundaries", "Error isolation", "Design principles", "Next steps"], "content_sha256": "ff09cde4ea0e12906a54edc1fad4c85abd20bbc0a45370728060b8067e7d3cf8", "content": "AFK's architecture is built on one principle: **orchestration and execution are separate concerns**. The runner (orchestration) manages the step loop, state, and policies. Adapters (execution) handle LLM calls, tool execution, and external communication. You can swap any adapter without touching your agent code. Layered architecture Module boundaries | Module | Responsibility | Dependencies | | --------------- | ------------------------------------------------------ | --------------------------------------------------- | | afk.agents | Agent definition, configuration, fail-safe | pydantic | | afk.core | Runner, step loop, state management, policies | afk.agents, afk.llms, afk.tools, afk.memory | | afk.llms | LLM runtime, provider adapters, retry/circuit breaking | Provider SDKs | | afk.tools | Tool registry, execution, validation, hooks | pydantic | | afk.memory | State persistence, checkpoints, compaction | Backend drivers | | afk.observability | Event pipeline, metrics, exporters | OTEL SDK (optional) | | afk.messaging | Public A2A protocol, auth, host, and delivery exports | afk.agents | | afk.evals | Eval runner, assertions, reporting | afk.core | **Key rule:** Modules in the Adapters layer never import from each other. afk.llms doesn't know about afk.tools. Only afk.core wires them together. Runtime sequence What happens when you call runner.run(agent, user_message=\"...\"): Contract boundaries Every boundary between modules uses a typed contract: All contracts are Pydantic models. This means: - **Validation at boundaries** \u2014 malformed data causes clear errors - **Serializable** \u2014 every contract can be logged, stored, or sent over the wire - **Versionable** \u2014 contracts have stable shapes for backward compatibility Error isolation Failures in one adapter don't crash the others: | Failure | Impact | Runner behavior | | -------------------- | ------------------------------ | --------------------------------------- | | LLM call fails | No response for this step | Retry (if retryable) or fail the run | | Tool execution fails | Tool result is an error object | Return error to LLM for self-correction | | Memory backend fails | State not persisted | Run continues (degraded mode) | | Telemetry fails | Events not exported | Run continues (events dropped silently) | **Telemetry failures are always silent.** A broken exporter should never block an agent run. If telemetry is critical to your use case, add a separate monitoring check. Design principles 1. **Contracts first.** Define the interface (Pydantic model), then implement the behavior. Never skip the contract. 2. **No cross-adapter imports.** afk.llms doesn't import afk.tools. Only afk.core wires modules together. 3. **Classify failures.** Every error is retryable, terminal, or non-fatal. The runner uses this classification to decide what to do. 4. **Least privilege.** Adapters get only the data they need. LLM adapters don't see tool results until the runner decides to include them. Next steps Provider-portable LLM runtime in detail. Contribute to AFK \u2014 patterns and conventions.", "token_count": 330} +{"id": "doc_18d38fdacc9e", "path": "docs/library/building-with-ai.mdx", "url": "/library/building-with-ai", "title": "Building with AI", "description": "Production playbook \u2014 patterns, anti-patterns, and deployment guidance.", "headings": ["Start narrow, iterate fast", "Common patterns", "Anti-patterns", "Production readiness checklist", "Next steps"], "content_sha256": "4fb4252f5d2f1024c6863df494ee1627ef492fb5eb8a1c5ff8673f47773df3e4", "content": "This guide covers the engineering patterns for building production-quality AI features with AFK. It's organized around three phases: starting narrow, adding capabilities, and scaling safely. Start narrow, iterate fast The most common mistake is building for complexity you don't have yet. Start with the simplest version that solves the problem, then add capabilities based on real evidence. > [!TIP] > **Test at every step.** Don't add tools before the base prompt works. Don't add subagents before single-agent tools work. Each layer should be proven before adding the next. Common patterns Categorize input into predefined labels. No tools needed. **Tips:** - Constrain output format explicitly in the prompt - Test with a diverse set of inputs - Add evals for each category Retrieve context, then generate an answer. **Tips:** - Always search before answering - Instruct the model to cite sources - Set tool_output_max_chars to prevent context overflow Generate, review, and test code. **Tips:** - Higher cost limits (coding agents iterate more) - Gate write_file with policy approval - Use sandbox profiles for code execution Route tasks to specialist subagents. **Tips:** - Keep the coordinator's prompt focused on routing - Don't give the coordinator tools \u2014 let specialists handle execution - Use join_policy=\"allow_optional_failures\" if some specialists are non-critical Anti-patterns These are the most common mistakes. Avoiding them will save you significant debugging time. | Anti-pattern | Problem | Fix | | -------------------------------------- | ---------------------------------------------------- | -------------------------------------------------- | | **No cost limits** | Runaway agent loops spend $100s in minutes | Always set max_total_cost_usd | | **Vague instructions** | Model produces inconsistent output | Be specific: \"Output only the category name\" | | **Too many tools** | Model gets confused choosing between tools | Keep \u2264 5 tools per agent. Split into subagents. | | **Mixing orchestration and execution** | Runner logic leaks into tool handlers | Tools should be pure functions. No runner imports. | | **Skipping evals** | Prompt changes break behavior silently | Run evals in CI on every PR | | **Untyped tool arguments** | Missing validation, hard-to-debug errors | Always use Pydantic models | | **Not classifying failures** | Retryable errors treated as terminal (or vice versa) | Return clear error types from tools | | **Giant system prompts** | Token waste, instruction drift | Split into skills. Use templates. | Production readiness checklist | Area | Requirement | Status | | ----------------- | ------------------------------------------------- | ----------------------------------------- | | **Safety** | FailSafeConfig with cost, step, and time limits | | | **Safety** | Policy rules for all mutating tools | | | **Observability** | Telemetry exporter configured (OTEL recommended) | | | **Observability** | Alerts on error rate and latency | | | **Testing** | Eval suite with \u2265 5 cases running in CI | | | **Testing** | Golden traces captured for regression detection | | | **Memory** | Persistent backend for multi-turn conversations | | | **Memory** | Thread compaction configured | | | **Security** | Secrets in environment variables, not code | | | **Security** | Sandbox profiles for code execution tools | | Next steps Set up monitoring, alerting, and dashboards. Write behavioral tests for agents.", "token_count": 341} +{"id": "doc_4a1cf8a90e83", "path": "docs/library/checkpoint-schema.mdx", "url": "/library/checkpoint-schema", "title": "Checkpoint Schema", "description": "Checkpoint structure and resume correctness guarantees.", "headings": ["Checkpoint model", "Field reference", "Resume behavior", "Resume code example", "Start a run that might be interrupted", "Later, resume from the checkpoint", "What gets stored in the payload", "Phase-specific payloads", "Async write-behind behavior", "Effect replay and idempotency", "Common failure scenarios"], "content_sha256": "a0ba36ffceea5975afae78511697808cc3bf9231e2d04812cdc954536f971a1b", "content": "Checkpoints are the persistence mechanism that allows AFK agent runs to survive process restarts, crashes, and intentional pauses. At key boundaries during execution (step start, pre-LLM call, post-tool batch, run terminal), the runner writes a checkpoint record to the memory store. Each checkpoint captures enough state to reconstruct the execution context and resume from where the run left off. Checkpoints matter for three reasons: 1. **Fault tolerance** -- If the process crashes mid-run, the latest checkpoint lets you resume without re-executing already-completed work. 2. **Human-in-the-loop** -- When a run pauses for approval, the checkpoint preserves the full conversation and pending state so the run can resume hours or days later. 3. **Auditability** -- The checkpoint chain provides a step-by-step record of every phase the run passed through, useful for debugging and compliance. Checkpoint model Field reference | Field | Type | Description | | --- | --- | --- | | run_id | str | Unique identifier for the agent run. Generated at run start or provided when resuming. Used as the primary key for checkpoint lookup. | | schema_version | str | Checkpoint schema version (for migration/compat validation). | | phase | str | The execution phase when the checkpoint was written. Values include: run_started, step_started, pre_llm, post_llm, pre_tool_batch, post_tool_batch, pre_subagent_batch, post_subagent_batch, run_terminal. | | payload | dict | Phase-specific data. The contents vary by phase -- see the payload section below. | | timestamp_ms | int | Unix timestamp in milliseconds when the checkpoint was written. Used for ordering when multiple checkpoints exist for the same run. | Resume behavior The runner calls memory.get_state(thread_id, checkpoint_latest_key(run_id)) to fetch the most recent checkpoint for the given run. If no checkpoint exists, a AgentCheckpointCorruptionError is raised. The checkpoint record must be a dict with the required fields (run_id, thread_id, phase, payload). Missing or malformed fields cause an AgentCheckpointCorruptionError. The runner also normalizes legacy checkpoint formats through _normalize_checkpoint_record(). If the checkpoint's phase is run_terminal and the payload contains a terminal_result, the run is already complete. The runner returns a pre-resolved handle with the deserialized AgentResult -- no re-execution occurs. For non-terminal checkpoints, the runner loads the full runtime snapshot which contains the conversation messages, counters, usage aggregates, and any pending LLM response. This snapshot is used to reconstruct the execution context. The runner calls run_handle() with the restored snapshot. Execution continues from the step where the run was interrupted. If a pending_llm_response exists in the snapshot, the runner skips the LLM call and proceeds directly to tool execution for that response. Resume code example What gets stored in the payload The payload field carries different data depending on the checkpoint phase. The most important payload is the **runtime snapshot** persisted at step_started and post_llm phases, which contains everything needed for full resume: | Payload key | Type | Description | | --- | --- | --- | | messages | list[dict] | Serialized conversation history (system, user, assistant, tool messages). | | step | int | Current step counter in the execution loop. | | state | str | Current run state (running, degraded, etc.). | | context | dict | Run context dict merged from agent defaults and caller-provided context. | | llm_calls | int | Number of LLM calls made so far. | | tool_calls | int | Number of tool calls made so far. | | started_at_s | float | Unix timestamp when the run originally started. | | usage | dict | Token usage aggregate (input_tokens, output_tokens, total_tokens). | | total_cost_usd | float | Accumulated estimated cost in USD. | | session_token | str \\| None | Provider session token for session-aware providers. | | checkpoint_token | str \\| None | Provider checkpoint token for checkpoint-aware providers. | | pending_llm_response | dict \\| None | Serialized LLM response that was received but whose tool calls have not yet been executed. On resume, the runner skips the LLM call and processes these tool calls directly. | | tool_executions | list[dict] | Serialized ToolExecutionRecord entries for all tools executed so far. | | subagent_executions | list[dict] | Serialized SubagentExecutionRecord entries. | | requested_model | str | The model string originally requested by the agent. | | normalized_model | str | The model string after resolution and normalization. | | provider_adapter | str | The provider adapter type used (e.g., openai, litellm). | | final_text | str | The final text output accumulated so far. | | final_structured | dict \\| None | Structured output if the LLM returned schema-validated JSON. | Phase-specific payloads Beyond the runtime snapshot, individual phase checkpoints carry lighter payloads: | Phase | Key payload fields | | --- | --- | | run_started | agent_name, resumed | | step_started | state, message_count | | pre_llm | model, provider, message_count | | post_llm | model, provider, finish_reason, tool_call_count, session_token, checkpoint_token, total_cost_usd | | pre_tool_batch | tool_call_count | | post_tool_batch | tool_calls_total, tool_failures | | run_terminal | state, final_text, requested_model, normalized_model, provider_adapter, terminal_result | Async write-behind behavior By default, checkpoint writes are asynchronous (RunnerConfig.checkpoint_async_writes=True): - Writes are queued and flushed by a background writer. - Repeated runtime_state writes may be coalesced (checkpoint_coalesce_runtime_state=True). - Terminal states perform a bounded flush (checkpoint_flush_timeout_s) before returning. This improves loop throughput while preserving terminal durability. Effect replay and idempotency When a run resumes and re-enters a tool batch, the runner checks for previously persisted effect results before re-executing tools. Each tool call's result is stored with an input_hash (derived from tool name and arguments) and an output_hash. On resume, if a matching effect result exists for a tool call ID with a matching input hash, the stored result is replayed instead of re-executing the tool. This guarantees idempotent resume for tools with side effects. The replayed_effect_count field in the runtime snapshot tracks how many tool calls were satisfied from replay rather than fresh execution. Common failure scenarios **Missing checkpoint** -- Calling runner.resume() with a run_id that has no checkpoint raises AgentCheckpointCorruptionError. This can happen if the memory store was cleared or the run never persisted its first checkpoint (crashed before run_started). **Corrupted payload** -- If the checkpoint record exists but is not a valid dict or is missing required keys, AgentCheckpointCorruptionError is raised. The runner does not attempt partial recovery from corrupted checkpoints. **Pending LLM response corruption** -- If a checkpoint has pending_llm_response set but the serialized response cannot be deserialized, the runner raises AgentCheckpointCorruptionError rather than making a duplicate LLM call. **Stale session tokens** -- Provider session tokens stored in checkpoints may expire between the original run and the resume attempt. The runner passes the stored session_token and checkpoint_token to the provider, but the provider may reject them. In that case, the LLM call fails and follows the normal retry/fallback chain.", "token_count": 744} +{"id": "doc_a24b92541657", "path": "docs/library/configuration-reference.mdx", "url": "/library/configuration-reference", "title": "Configuration Reference", "description": "Every configurable field across Agent, Runner, FailSafeConfig, and SandboxProfile.", "headings": ["Agent", "Reasoning override precedence", "RunnerConfig", "Deep Dive: Interaction Models", "FailSafeConfig", "Deep Dive: Failure & Recovery", "FailurePolicy values", "SandboxProfile", "Runner constructor", "@tool decorator", "Next steps"], "content_sha256": "70a5ac606bb9dfba32cbba5a3a6f7791cf0437ae14f45dc29bf3491141978df6", "content": "This page is a single-source reference for every configuration knob in AFK. Each section lists fields with their type, default value, and purpose. Agent The Agent constructor defines what your agent is \u2014 identity, model, tools, and behavior. | Field | Type | Default | Description | | --------------------------- | ----------------- | ---------------- | -------------------------------------------------------------- | | model | str \\| LLM | **required** | LLM model name or pre-built client instance | | name | str | class name | Agent identity for logs, telemetry, and subagent routing | | instructions | str \\| callable | None | System prompt \u2014 static string or callable provider | | instruction_file | str \\| Path | None | Path to a prompt file that must resolve under prompts_dir | | prompts_dir | str \\| Path | None | Root directory for prompt files (env: AFK_AGENT_PROMPTS_DIR) | | tools | list | [] | Typed functions the agent can call | | subagents | list[Agent] | [] | Specialist agents this agent can delegate to | | context_defaults | dict | {} | Default JSON context merged into each run | | inherit_context_keys | list[str] | [] | Context keys accepted from a parent subagent | | model_resolver | callable | None | Custom function to resolve model names to LLM clients | | skills | list[str] | [] | Skill names to resolve under skills_dir | | mcp_servers | list | [] | External MCP server refs whose tools to expose | | skills_dir | str \\| Path | .agents/skills | Root directory for skill definitions | | instruction_roles | list | [] | Callbacks that append dynamic instruction text | | policy_roles | list | [] | Callbacks that can allow/deny/defer runtime actions | | policy_engine | PolicyEngine | None | Deterministic rule engine applied before policy roles | | subagent_router | SubagentRouter | None | Router callback deciding subagent targets | | max_steps | int | 20 | Maximum reasoning/tool loop steps | | tool_parallelism | int | None | Max concurrent tool calls (falls back to fail_safe) | | subagent_parallelism_mode | str | configurable | single, parallel, or configurable | | fail_safe | FailSafeConfig | defaults | Runtime limits and failure policies | | reasoning_enabled | bool \\| None | None | Default request thinking flag for this agent | | reasoning_effort | str \\| None | None | Default request thinking_effort label | | reasoning_max_tokens | int \\| None | None | Default request max_thinking_tokens | | enable_skill_tools | bool | True | Auto-register built-in skill tools | | enable_mcp_tools | bool | True | Auto-register tools from configured MCP servers | | runner | Runner | None | Optional runner override | Reasoning override precedence Reasoning values are resolved in this order: 1. Run context override: context[\\\"_afk\\\"][\\\"reasoning\\\"] 2. Agent defaults: reasoning_enabled, reasoning_effort, reasoning_max_tokens 3. Provider defaults/validation in the LLM layer --- RunnerConfig Passed to Runner(config=RunnerConfig(...)) to control runtime behavior. | Field | Type | Default | Description | | ----------------------------------------- | ---------------- | ------------------------------------------ | ---------------------------------------------------------- | | interaction_mode | str | headless | headless, interactive, or external | | approval_timeout_s | float | 300.0 | Timeout for deferred approval decisions | | input_timeout_s | float | 300.0 | Timeout for deferred user-input decisions | | approval_fallback | str | deny | Fallback when approval times out: allow, deny, defer | | input_fallback | str | deny | Fallback when user input times out | | sanitize_tool_output | bool | True | Sanitize model-visible tool output | | untrusted_tool_preamble | bool | True | Inject untrusted-data warning preamble | | tool_output_max_chars | int | 12_000 | Max tool output characters forwarded to model | | default_sandbox_profile | SandboxProfile | None | Default sandbox profile for tool execution | | sandbox_profile_provider | callable | None | Runtime sandbox profile resolver | | secret_scope_provider | callable | None | Secret-scope resolver per tool call | | default_allowlisted_commands | tuple[str] | ls, cat, head, tail, rg, find, pwd, echo | Allowlisted shell commands for runtime tools | | max_parallel_subagents_global | int | 64 | Global cap for concurrent subagent tasks | | max_parallel_subagents_per_parent | int | 8 | Per-parent-run cap for concurrent subagent fanout | | max_parallel_subagents_per_target_agent | int | 4 | Per-target-agent cap | | subagent_queue_backpressure_limit | int | 512 | Max pending subagent nodes before backpressure | | checkpoint_async_writes | bool | True | Enable background checkpoint/state writes | | checkpoint_queue_maxsize | int | 1024 | Max queued checkpoint write jobs | | checkpoint_flush_timeout_s | float | 10.0 | Timeout for terminal checkpoint flush | | checkpoint_coalesce_runtime_state | bool | True | Coalesce repeated runtime_state checkpoint writes | | debug | bool | False | Enable debug metadata in emitted run events | | debug_config | RunnerDebugConfig \\| None | None | Advanced debug controls (verbosity/content/redaction) | | background_tools_enabled | bool | True | Enable deferred/background tool orchestration | | background_tool_default_grace_s | float | 0.0 | Wait this long after defer to allow fast background completion before continuing | | background_tool_max_pending | int | 256 | Maximum unresolved background tool tickets per run | | background_tool_poll_interval_s | float | 0.5 | Poll interval for external/persisted ticket resolution | | background_tool_result_ttl_s | float | 3600.0 | TTL before pending ticket is marked failed (runtime floor is 1.0s) | | background_tool_interrupt_on_resolve | bool | True | When enabled, resolved tickets can be ingested immediately after defer/grace in the same step | Deep Dive: Interaction Models The interaction_mode setting fundamentally changes how the Runner handles decision points like tool approval or user input requests. - **headless (default)**: The Runner never pauses. - If a policy returns defer or request_user_input, the Runner immediately uses the configured approval_fallback or input_fallback (default: deny). - _Use case:_ Backend workers, cron jobs, automated testing. - **interactive**: The Runner pauses execution and uses the configured InteractionProvider to ask for human input. - For CLI apps, this prints to stdout and reads from stdin. - The run blocks until input is received or approval_timeout_s expires. - _Use case:_ Local CLI tools, scripts run by humans. - **external**: The Runner emits a run_paused event and **suspends execution**. - The run() loop exits (or yields a paused state). The state is persisted to memory. - The system waits for an external API call to runner.resume_with_input(). - _Use case:_ Chatbots, web UIs, Slack bots where the user is asynchronous. --- FailSafeConfig Passed to Agent(fail_safe=FailSafeConfig(...)) to set runtime limits and failure policies. | Field | Type | Default | Description | | ------------------------------ | --------------- | --------------------- | --------------------------------------------- | | llm_failure_policy | FailurePolicy | retry_then_fail | Strategy when LLM calls fail | | tool_failure_policy | FailurePolicy | continue_with_error | Strategy when tool calls fail | | subagent_failure_policy | FailurePolicy | continue | Strategy when subagent calls fail | | approval_denial_policy | FailurePolicy | skip_action | Strategy when approval is denied or times out | | max_steps | int | 20 | Maximum run loop iterations | | max_wall_time_s | float | 300.0 | Maximum wall-clock runtime in seconds | | max_llm_calls | int | 50 | Maximum number of LLM invocations | | max_tool_calls | int | 200 | Maximum number of tool invocations | | max_parallel_tools | int | 16 | Max concurrent tools per batch | | max_subagent_depth | int | 3 | Maximum subagent recursion depth | | max_subagent_fanout_per_step | int | 4 | Maximum subagents selected per step | | max_total_cost_usd | float \\| None | None | Cost ceiling for run termination | | fallback_model_chain | list[str] | [] | Ordered fallback model list for LLM retries | | breaker_failure_threshold | int | 5 | Circuit breaker open threshold | | breaker_cooldown_s | float | 30.0 | Circuit breaker cooldown window in seconds | Deep Dive: Failure & Recovery FailSafeConfig controls the agent's resilience. The policies work in a hierarchy: 1. **Lower-level retries**: Transient errors (network glitches, rate limits) are retried automatically by the LLM client, guided by AFK_LLM_MAX_RETRIES. 2. **llm_failure_policy**: If the LLM call fails after all retries (or hits a terminal error like 401 Unauthorized): - retry_then_fail: Tries a few more times at the agent level, then fails the run. - retry_then_degrade: Tries again, then marks the run as degraded but returns partial results (useful for \"best effort\" responses). 3. **tool_failure_policy**: If a tool raises an exception: - continue_with_error (default): The error message is fed back to the model. The model can then try to fix its mistake or apologize. **This is usually the best setting** for capable models. - fail_run: Immediately stops the run. Use this for critical transactional agents where any error is unacceptable. 4. **Circuit Breakers**: - If a model provider fails breaker_failure_threshold times in a row, the circuit opens. - Subsequent calls fail _instantly_ without network traffic until breaker_cooldown_s passes. - This protects your system (and wallet) from hammering a down service. FailurePolicy values | Value | Behavior | | --------------------- | ---------------------------------------------------- | | retry_then_fail | Retry with backoff, then fail the run | | retry_then_degrade | Retry with backoff, then degrade (partial result) | | retry_then_continue | Retry with backoff, then continue without the result | | fail_fast | Fail immediately, no retries | | fail_run | Fail the entire run | | continue_with_error | Continue, passing the error to the model | | continue | Continue silently, ignoring the failure | | skip_action | Skip the action entirely | --- SandboxProfile Controls execution restrictions for tool handlers. Configured via RunnerConfig.default_sandbox_profile. | Field | Type | Default | Description | | -------------------------- | --------------- | --------- | ---------------------------------------------- | | profile_id | str | default | Profile identifier for logs and policy | | allow_network | bool | False | Whether tools can make network requests | | allow_command_execution | bool | False | Whether tools can execute shell commands | | allowed_command_prefixes | list[str] | [] | Allowed command prefixes (empty = none) | | deny_shell_operators | bool | True | Block pipes, redirects, semicolons | | allowed_paths | list[str] | [] | Restrict file access to these paths | | denied_paths | list[str] | [] | Explicitly deny access to these paths | | command_timeout_s | float \\| None | None | Kill commands after this duration | | max_output_chars | int | 20_000 | Truncate command output beyond this limit | --- Runner constructor The Runner accepts these arguments directly (outside of RunnerConfig): | Argument | Type | Default | Description | | ---------------------- | ---------------------- | ---------------- | ------------------------------------------------------ | | memory_store | MemoryStore | None | Memory backend (resolved from env if None) | | interaction_provider | InteractionProvider | None | Human-in-the-loop provider (required for non-headless) | | policy_engine | PolicyEngine | None | Deterministic policy engine shared across runs | | telemetry | str \\| TelemetrySink | None | Telemetry sink instance or backend id | | telemetry_config | dict | None | Backend-specific sink configuration | | config | RunnerConfig | RunnerConfig() | Runner configuration | --- @tool decorator | Argument | Type | Default | Description | | ---------------- | ------------------ | ------------- | ----------------------------------------------------------------- | | args_model | Type[BaseModel] | **required** | Pydantic model for argument validation | | name | str | function name | Tool name exposed to the LLM | | description | str | docstring | Tool description for the LLM | | timeout | float | None | Execution timeout in seconds | | prehooks | list[PreHook] | None | Argument transform hooks | | posthooks | list[PostHook] | None | Output transform hooks | | middlewares | list[Middleware] | None | Execution wrappers (logging, caching, etc.) | | raise_on_error | bool | False | Raise exceptions instead of returning ToolResult(success=False) | Next steps Environment variable defaults and backend selection. Quick import reference.", "token_count": 1143} +{"id": "doc_e7d508371e5c", "path": "docs/library/core-runner.mdx", "url": "/library/core-runner", "title": "Core Runner", "description": "Execute agents \u2014 lifecycle, API modes, streaming, and state management.", "headings": ["Quick example", "Synchronous (simplest)", "Three API modes", "Run lifecycle", "Terminal states", "The step loop", "Runner configuration", "Run handles and lifecycle control", "Monitor events in real time", "Lifecycle controls", "Wait for the final result", "Thread-based memory", "Turn 1", "Turn 2 \u2014 agent remembers Turn 1", "Resume from checkpoint", "Resume an interrupted run", "Compact long threads", "Summarize old messages to control storage growth", "AgentResult reference", "ToolExecutionRecord fields", "Next steps"], "content_sha256": "1a47bf2eb1a77425072db12e35a71d725d7b826102f87dd74a81c747487df506", "content": "The **Runner** is the execution engine that runs agents. It manages the step loop, LLM calls, tool execution, state persistence, and telemetry. You create a Runner, hand it an Agent, and get back an AgentResult. Quick example Three API modes Blocks until complete. Best for scripts, tests, and simple integrations. Awaitable. Best for async applications and API servers. Real-time events. Best for chat UIs and CLI tools. Run lifecycle Every agent run follows this state machine: Terminal states | State | Meaning | final_text | | ------------- | ---------------------------------------- | ------------------------------------------------------------------------------------------ | | completed | Run finished successfully | Model's response | | failed | Unrecoverable error occurred | Error message | | degraded | Partial result (some failures tolerated) | Best-effort response | | cancelled | Caller cancelled the run | Empty or partial | | interrupted | Timeout or external interrupt | Empty or partial | The step loop Each \"step\" is one iteration of the agent's decision cycle: The runner constructs an LLMRequest with the conversation history, tool schemas, and model configuration. The request is sent through the LLM runtime (with retry, circuit breaker, rate limiting, and caching policies). If the LLM returns **text only** \u2192 the run is complete. If it returns **tool calls** \u2192 proceed to tool execution. Each tool call is validated, policy-checked, executed, and its output is sanitized and fed back to the LLM. The runner returns to Step 1 for the next LLM turn. This continues until the model produces a text-only response or a limit is hit. Runner configuration Run handles and lifecycle control For advanced control, use run_handle(): Thread-based memory Pass a thread_id to maintain conversation context across runs: Resume from checkpoint Compact long threads AgentResult reference | Field | Type | Description | | --------------------- | ------------------------------- | ----------------------------------------------------------------------------- | | final_text | str | The agent's final response | | state | str | Terminal state: completed, failed, degraded, cancelled, interrupted | | run_id | str | Unique run identifier | | thread_id | str | Thread identifier for memory continuity | | tool_executions | list[ToolExecutionRecord] | All tool calls with name, success, output, latency | | subagent_executions | list[SubagentExecutionRecord] | All subagent invocations | | usage_aggregate | UsageAggregate | Token counts aggregate across LLM calls | | state_snapshot | dict[str, JSONValue] | Final runtime counters/snapshot metadata | ToolExecutionRecord fields AgentResult.tool_executions entries include: | Field | Type | Description | | --- | --- | --- | | tool_name | str | Tool name | | tool_call_id | str \\| None | Tool call id from the model/provider | | success | bool | Execution success | | output | JSONValue \\| None | Tool output payload | | error | str \\| None | Error text (when failed) | | latency_ms | float \\| None | Tool latency | | agent_name | str \\| None | Agent that executed the tool (parent or subagent) | | agent_depth | int \\| None | Nesting depth where tool ran | | agent_path | str \\| None | Agent lineage path for nested calls | Next steps Real-time event streaming for chat UIs. Thread-based state persistence and resume.", "token_count": 326} +{"id": "doc_3f1383f72c5f", "path": "docs/library/debugger.mdx", "url": "/library/debugger", "title": "Debugger", "description": "Debug-mode runner setup with redacted event payloads and live event taps.", "headings": ["Quick start", "Attach to a run handle", "Redaction behavior", "Verbosity modes", "Background tool scenario (coding + build + docs)", "External worker completion"], "content_sha256": "7349668744c3913398f016c1e3b0f07fd6b3659d0cf075176717e7a6a4003dcb", "content": "Use afk.debugger.Debugger to run agents in a structured debug mode without changing your core runtime architecture. Quick start You can also enable debug mode directly on the runner: Attach to a run handle Redaction behavior When redact_secrets=True, payload keys containing any of these markers are masked: - api_key - token - secret - authorization - password Verbosity modes - basic: lightweight metadata and step markers. - detailed: includes payload previews and step metadata. - trace: highest detail for deep diagnostics. Background tool scenario (coding + build + docs) Flow: 1. Agent writes code. 2. Agent calls build_project and receives tool_deferred. 3. Agent continues with write_docs or other tasks. 4. Runner emits tool_background_resolved when build completes. 5. Agent uses resolved build output in a later step to finalize/fix. External worker completion For out-of-process execution, an external worker can complete a ticket by writing background state into memory: The runner poller detects this update, emits tool_background_resolved or tool_background_failed, and injects a synthetic tool message for the next step.", "token_count": 122} +{"id": "doc_067033cfc945", "path": "docs/library/deployment.mdx", "url": "/library/deployment", "title": "Deployment", "description": "Deploy AFK agents to production with Docker, Kubernetes, and scaling patterns.", "headings": ["Docker deployment", "Basic Dockerfile", "Install dependencies", "Copy application code", "Run your application entrypoint that creates AFK agents/runners", "Production Dockerfile with multi-stage build", "Production image", "Run as non-root user", "docker-compose.yml", "Environment configuration", "Required environment variables", "LLM Provider", "or", "Memory backend", "Queue backend ", "Observability", "Server mode", "Production configuration file", "Scaling patterns", "Horizontal scaling with workers", "Kubernetes deployment", "Kubernetes HPA for auto-scaling", "Health checks", "Database schema", "SQLite (development)", "PostgreSQL", "Security checklist", "Monitoring", "Next steps"], "content_sha256": "7cc0c692d113fd9dc1af4de8235cb8b0c1c4b512d6079d164413d60837a5aa32", "content": "This guide covers deploying AFK agents to production environments, from single-container setups to distributed, multi-worker deployments. Docker deployment Basic Dockerfile Production Dockerfile with multi-stage build docker-compose.yml Environment configuration Required environment variables Production configuration file Create config/production.yaml: Scaling patterns Horizontal scaling with workers Kubernetes deployment Kubernetes HPA for auto-scaling Health checks Implement health endpoints in your server: Database schema SQLite (development) SQLite requires no schema setup \u2014 tables are created automatically on first use. PostgreSQL Security checklist Store API keys in secrets managers (AWS Secrets Manager, HashiCorp Vault, Kubernetes Secrets). Never commit keys to version control. Restrict traffic between services. Agents should only reach LLM providers and necessary databases. Configure rate limits on public endpoints to prevent abuse. Always set max_total_cost_usd in FailSafeConfig for production agents. Enable telemetry export to your logging infrastructure for compliance. Monitoring Key metrics to track: | Metric | What it indicates | Alert threshold | |--------|------------------|-----------------| | agent.run.duration | How long runs take | > 60s p95 | | agent.run.cost | Token spend per run | > $0.50 per run | | agent.run.failures | Failed runs | > 5% error rate | | llm.latency | LLM response time | > 10s p95 | | llm.errors | LLM API errors | > 1% error rate | | queue.depth | Pending tasks | > 100 items | | queue.dead_letters | Failed tasks | > 0 | Next steps Set up telemetry and alerting for production monitoring. Security hardening checklist and best practices. CI-gated quality checks for agent releases. Production patterns and anti-patterns.", "token_count": 191} +{"id": "doc_d147a81bac29", "path": "docs/library/developer-guide.mdx", "url": "/library/developer-guide", "title": "Developer Guide", "description": "Maintainer workflow for changing AFK itself.", "headings": ["Local setup", "Repository boundaries", "Change workflow", "Documentation workflow", "High-risk areas", "Public API checklist", "Next steps"], "content_sha256": "8ac363b758f8ae9d391d26db304b7bcf931f1fb24bbf0dd159c8de2e3ebc1789", "content": "This page is for contributors changing the AFK framework. If you are building an application with AFK, start with Quickstart or Building with AI. Local setup AFK targets Python 3.13+. Common commands: Preview docs: Regenerate agent-facing docs and skill indexes: Install the repository skills with Vercel's Skills CLI: Use afk-coder when building with AFK. Use afk-maintainer when reviewing or changing AFK itself. Repository boundaries | Package | Responsibility | | --- | --- | | afk.agents | Agent definitions, policy, prompts, skills, lifecycle, workflow, A2A | | afk.core | Runner, interaction providers, streaming handles, telemetry contracts | | afk.llms | Provider-portable LLM runtime, adapters, retry/timeout/cache/routing policies | | afk.tools | Typed tools, decorators, registry, sandboxing, output limiting | | afk.memory | Memory stores, checkpoints, retention, compaction, vector helpers | | afk.queues | Async task queues, execution contracts, workers, retry/DLQ behavior | | afk.observability | Telemetry collectors, projectors, exporters | | afk.mcp, afk.messaging, afk.debugger, afk.evals | Optional integration and quality layers | Keep the Agent/Runner/Runtime boundary intact: - agents are configuration; - runners execute; - tools, memory, queues, LLM adapters, and telemetry provide runtime capabilities. Change workflow 1. Identify the public contract affected by the change. 2. Inspect the existing module and tests before editing. 3. Make the smallest behavior change that preserves package boundaries. 4. Add or update focused tests for success and failure paths. 5. Update docs when behavior, public imports, configuration, env vars, or examples change. 6. Regenerate agent-facing docs when docs navigation, snippets, or skill metadata change. Documentation workflow User-facing docs must: - import from public package surfaces such as afk.agents and afk.core; - explain behavior before internals; - use Python 3.13+ guidance; - distinguish afk-py distribution install from afk imports; - include new pages in docs/docs.json; - keep examples aligned with current AgentResult, RunnerConfig, and FailSafeConfig fields. Maintainer docs may reference internal files, but should state which public contract or invariant is being protected. High-risk areas Use targeted tests when touching: | Area | Risk | | --- | --- | | src/afk/core/runner/ | execution loop, checkpoints, resume, policy/failure routing | | src/afk/core/streaming.py | stream events and handle lifecycle | | src/afk/tools/core/base.py, src/afk/tools/registry.py | tool invocation semantics | | src/afk/tools/security.py | sandbox, secret scope, output limiting | | src/afk/llms/runtime/ | retries, circuit breakers, rate limits, caching, routing | | src/afk/memory/ | persistence, checkpoint keys, compaction, vector search | | src/afk/queues/ | execution contracts, retry/DLQ, worker lifecycle | | src/afk/agents/a2a/ | auth, delivery guarantees, protocol compatibility | Public API checklist Before changing exports or constructor fields: - update package-level __all__; - update API Reference; - update Configuration Reference if defaults changed; - update snippets that use the changed field; - add public-import tests where useful. Next steps Package boundaries and runtime flow. Maintainer rules for stable imports and docs examples. Behaviors that tests protect. Generated source-level symbol map.", "token_count": 387} +{"id": "doc_61fd9f682847", "path": "docs/library/environment-variables.mdx", "url": "/library/environment-variables", "title": "Environment Variables", "description": "Environment variable reference for AFK defaults and backend configuration.", "headings": ["LLM defaults", "Memory", "Override in code \u2014 takes precedence over env vars", "Queue", "Prompts", "Runner and tool command defaults", "MCP server", "A2A", "Precedence", "Next steps"], "content_sha256": "89122dd488be8932d0ac65bf7169acebead8cb49223e178df9d0f049082cd073", "content": "AFK is configured primarily through code. Environment variables provide fallback defaults for LLM settings, memory backends, queues, and prompt directories. **Runtime configuration APIs always take precedence.** All variables are optional. If unset, AFK uses the defaults listed below. LLM defaults These variables configure the default LLM provider and behavior. They are read by LLMSettings.from_env() at startup. | Variable | Default | Description | | ------------------------------- | -------------- | ------------------------------------------------------------ | | AFK_LLM_PROVIDER | litellm | Default provider id (openai, litellm, anthropic_agent) | | AFK_LLM_PROVIDER_ORDER | _(none)_ | Comma-separated provider preference order for routing helpers | | AFK_LLM_MODEL | gpt-4.1-mini | Default model name | | AFK_EMBED_MODEL | _(none)_ | Embedding model for vector operations | | AFK_LLM_API_BASE_URL | _(none)_ | Provider API base URL | | AFK_LLM_API_KEY | _(none)_ | Provider API key | | AFK_LLM_TIMEOUT_S | 30 | Request timeout in seconds | | AFK_LLM_STREAM_IDLE_TIMEOUT_S | 45 | Stream idle timeout in seconds | | AFK_LLM_MAX_RETRIES | 3 | Retry attempts on transient failures | | AFK_LLM_BACKOFF_BASE_S | 0.5 | Exponential backoff base in seconds | | AFK_LLM_BACKOFF_JITTER_S | 0.15 | Random jitter added to backoff | | AFK_LLM_JSON_MAX_RETRIES | 2 | Structured output repair attempts | | AFK_LLM_MAX_INPUT_CHARS | 200000 | Input truncation ceiling in characters | **API keys for specific providers** (e.g. OPENAI_API_KEY, ANTHROPIC_API_KEY) are read by the underlying provider libraries, not by AFK directly. Set AFK_LLM_API_KEY only if you want a single key shared across providers. Memory Configure the persistent memory backend for thread-based conversations. | Variable | Default | Description | | -------------------- | -------------------- | ------------------------------------------------------- | | AFK_MEMORY_BACKEND | sqlite | Backend type: inmemory/memory, sqlite, redis, postgres | | AFK_SQLITE_PATH | afk_memory.sqlite3 | SQLite file path | | AFK_REDIS_URL | _(none)_ | Redis connection URL (for example redis://localhost:6379) | | AFK_REDIS_HOST | localhost | Redis host when URL is not set | | AFK_REDIS_PORT | 6379 | Redis port when URL is not set | | AFK_REDIS_DB | 0 | Redis DB when URL is not set | | AFK_REDIS_PASSWORD | _(none)_ | Redis password when URL is not set | | AFK_REDIS_EVENTS_MAX | 2000 | Max Redis memory events per thread | | AFK_PG_DSN | _(none)_ | PostgreSQL connection string | | AFK_PG_HOST | localhost | PostgreSQL host when DSN is not set | | AFK_PG_PORT | 5432 | PostgreSQL port when DSN is not set | | AFK_PG_USER | postgres | PostgreSQL user when DSN is not set | | AFK_PG_PASSWORD | _(none)_ | PostgreSQL password when DSN is not set | | AFK_PG_DB | afk | PostgreSQL database when DSN is not set | | AFK_PG_SSL | false | Enable PostgreSQL SSL | | AFK_PG_POOL_MIN | 1 | PostgreSQL pool minimum size | | AFK_PG_POOL_MAX | 10 | PostgreSQL pool maximum size | | AFK_VECTOR_DIM | _(required for Postgres)_ | Vector dimension for Postgres memory search | Queue Configure the task queue backend for async agent execution. | Variable | Default | Description | | ---------------------------------- | ---------- | --------------------------------- | | AFK_QUEUE_BACKEND | inmemory | Backend type: inmemory, redis | | AFK_QUEUE_REDIS_URL | falls back to AFK_REDIS_URL | Redis URL for queue backend | | AFK_QUEUE_REDIS_HOST | falls back to AFK_REDIS_HOST, then localhost | Redis queue host | | AFK_QUEUE_REDIS_PORT | falls back to AFK_REDIS_PORT, then 6379 | Redis queue port | | AFK_QUEUE_REDIS_DB | falls back to AFK_REDIS_DB, then 0 | Redis queue DB | | AFK_QUEUE_REDIS_PASSWORD | falls back to AFK_REDIS_PASSWORD | Redis queue password | | AFK_QUEUE_REDIS_PREFIX | afk:queue | Redis key prefix | | AFK_QUEUE_RETRY_BACKOFF_BASE_S | 0.5 | Retry base delay in seconds | | AFK_QUEUE_RETRY_BACKOFF_MAX_S | 30 | Retry max delay in seconds | | AFK_QUEUE_RETRY_BACKOFF_JITTER_S | 0.2 | Random jitter added to backoff | The inmemory queue backend does not persist across restarts. Use redis for production workloads that need reliable task delivery. Prompts | Variable | Default | Description | | ----------------------- | ---------------- | -------------------------------------- | | AFK_AGENT_PROMPTS_DIR | .agents/prompt | Root directory for system prompt files | Agents resolve instruction_file paths relative to this directory. See System Prompts for details. Runner and tool command defaults | Variable | Default | Description | | --- | --- | --- | | AFK_ALLOWED_COMMANDS | _(none)_ | Comma-separated default allowlist for runtime command tools | Runner constructors and RunnerConfig fields remain the preferred way to set command policy. Use this environment variable only for process-wide defaults. MCP server These variables are read by the MCP server configuration helpers in afk.config. | Variable | Default | Description | | --- | --- | --- | | AFK_CORS_ORIGINS | _(none)_ | Comma-separated CORS origins | | AFK_MCP_NAME | afk-mcp-server | Server name | | AFK_MCP_VERSION | 1.0.0 | Server version string | | AFK_MCP_HOST | 0.0.0.0 | Bind host | | AFK_MCP_PORT | 8000 | Bind port | | AFK_MCP_INSTRUCTIONS | _(none)_ | Optional server instructions | | AFK_MCP_PATH | /mcp | HTTP MCP endpoint path | | AFK_MCP_SSE_PATH | /mcp/sse | SSE endpoint path | | AFK_MCP_HEALTH_PATH | /health | Health endpoint path | | AFK_MCP_ENABLE_SSE | true | Enable SSE endpoint | | AFK_MCP_ENABLE_HEALTH | true | Enable health endpoint | | AFK_MCP_ALLOW_BATCH | true | Allow batched MCP requests | A2A No default environment variables are required. Configure A2A host and authentication in code for an explicit security posture. See A2A for details. Precedence Configuration is resolved in this order (highest priority first): 1. **Constructor arguments** \u2014 Runner(memory_store=...), Agent(model=...) 2. **RunnerConfig / LLMSettings objects** \u2014 passed to constructors 3. **Environment variables** \u2014 fallback defaults listed on this page 4. **Built-in defaults** \u2014 hardcoded in the source Next steps Full reference for all configurable fields across Agent, Runner, and more. Runner lifecycle and configuration options.", "token_count": 557} +{"id": "doc_c93c115aeb85", "path": "docs/library/evals.mdx", "url": "/library/evals", "title": "Evals", "description": "Behavioral testing for agent quality \u2014 assertions, budgets, and CI integration.", "headings": ["Your first eval", "Eval lifecycle", "Eval case types", "Assertions", "Suite configuration", "CI integration", ".github/workflows/evals.yml", "Release gating", "Next steps"], "content_sha256": "89a16478fcd1a7d21a0539b75ab780f2634444483626668f05e16f25320bd0ed", "content": "AFK evals run agents against named inputs and check the resulting state, text, tool usage, budgets, and telemetry. Use them for prompt changes, tool changes, routing changes, and regression tests. They can run against real providers, test adapters, or agents configured with deterministic tools. Your first eval Eval lifecycle Each case specifies an agent and input message. Suite-level assertions and budgets verify the result. The scheduler runs cases sequentially or in parallel, respecting concurrency limits. Each case runs through the same runner path your application uses. Assertions verify the result \u2014 text content, state, tool usage, cost, latency, etc. Budget limits gate individual case costs and the total suite cost. Pass/fail results, assertion details, and metrics are collected into a report. Eval case types Verify correct behavior under normal conditions. Verify graceful handling of errors and edge cases. Verify that the agent uses tools correctly. Verify that the agent stays within cost limits. Assertions Assertions are suite-level callables. Import the built-ins from afk.evals, or implement the EvalAssertion protocol. Suite configuration CI integration Run evals in your CI pipeline to gate releases: **Set a budget for CI evals.** Without a budget, a broken prompt can drain your API credits during a CI run. Use EvalBudget(max_total_cost_usd=2.00) as a reasonable CI limit. Release gating Gate releases on eval pass rate: Next steps Security boundaries and production hardening. Production playbook with anti-patterns.", "token_count": 181} +{"id": "doc_a5120de838fb", "path": "docs/library/examples/index.mdx", "url": "/library/examples/", "title": "Getting Started", "description": "Scenario-based examples for every major AFK feature.", "headings": ["Which example should I start with?", "Complexity progression", "Scenario catalog"], "content_sha256": "e209b35c682236c60b0dd47344bf686c23ea09e6000e11aab2c25e3078800062", "content": "This catalog provides runnable code examples for every major AFK capability. Each example is self-contained and demonstrates a specific pattern -- from the simplest possible agent run to advanced tool security configurations. The examples are ordered by complexity. If you are new to AFK, start at the top and work your way down. Each example builds on concepts introduced in the previous ones. Which example should I start with? - **First time using AFK?** Start with Minimal Chat Agent. It shows the absolute simplest agent run in under 10 lines of code. - **Need to gate dangerous actions?** Go to Policy + HITL. It demonstrates how policy engines and human approval work together. - **Building a multi-agent system?** See Subagent Router. It shows how to delegate work to specialist subagents and merge results. - **Need to persist and resume runs?** Check Resume + Compact. It covers checkpoint-based resumption and memory compaction. - **Using LLMs without the agent loop?** See Structured Output. It shows how to use LLMBuilder directly with Pydantic models. - **Locking down tool capabilities?** Go to Tool Security. It demonstrates scoped tool registration and policy gates. Complexity progression | Level | Example | What You Learn | | ------------ | -------------------- | ---------------------------------------------------------- | | Beginner | Minimal Chat Agent | Agent + Runner basics, final_text access | | Beginner | Structured Output | Direct LLM usage, Pydantic schema validation | | Intermediate | Policy + HITL | Policy engine, approval flows, interaction modes | | Intermediate | Tool Security | Tool scoping, sandbox profiles, policy gates | | Intermediate | Hooks + Middleware | Pre/post tool execution hooks, middleware chains | | Intermediate | Runtime Tools | Built-in file, search, and command tools | | Intermediate | Prompt Loader | External prompt files, template variables | | Advanced | Subagent Router | Multi-agent coordination, delegation patterns | | Advanced | Resume + Compact | Checkpointing, memory management, long-running workflows | | Intermediate | Streaming + Memory | Real-time streaming with multi-turn thread persistence | | Intermediate | Cost Monitoring | Budget limits, real-time cost tracking, batch budgets | | Advanced | MCP Client | Connect to external MCP servers, hybrid local+remote tools | | Advanced | Multi-Model Fallback | Fallback chains, circuit breakers, model tier strategies | | Advanced | Production Client | Timeout middleware, Redis connection pooling, graceful shutdown | Scenario catalog The simplest possible AFK agent. Define an agent, run it synchronously, and read the output. Start here. Gate sensitive actions through a policy engine and route approval requests to a human operator. Delegate workload to specialist subagents using a coordinator pattern. Merge outputs into a unified response. Checkpoint a run, resume it later from the last known state, and compact memory to control storage growth. Use the LLM builder directly (without the agent loop) to get schema-validated structured responses via Pydantic. Register destructive tools with tight scoping, enforce sandbox profiles, and gate actions through policy. Attach pre-execution and post-execution hooks to tools. Chain middleware for logging, validation, and transformation. Use AFK's built-in file reading, directory listing, and command execution tools with agents. Load system prompts from external files with template variable substitution and automatic caching. Combine real-time streaming with thread-based memory for multi-turn chat UIs. Track and control agent costs using FailSafeConfig budgets and telemetry events. Connect to external MCP servers, discover tools, and combine with local tools. Configure fallback model chains for LLM resilience, circuit breakers, and cost optimization. Production-ready LLM client with timeout middleware, Redis connection pooling, and graceful shutdown handling.", "token_count": 412} +{"id": "doc_4055659fe573", "path": "docs/library/failure-policy-matrix.mdx", "url": "/library/failure-policy-matrix", "title": "Failure Policy Matrix", "description": "How errors flow through the system \u2014 classification, handling, and escalation.", "headings": ["Error classification", "Failure decision flow", "Failure matrix by source", "LLM failures", "Tool failures", "Subagent failures", "Infrastructure failures", "Failure policies", "Budget-triggered limits", "Next steps"], "content_sha256": "3e2a501934ed7fabdbb4f5ccd429435c4948ed699b688168a9746b611f61f030", "content": "AFK classifies every error and applies a policy-driven response. Understanding the failure matrix helps you configure the right behavior for your use case. Error classification Every error is classified into one of three categories: | Classification | Meaning | Default behavior | | -------------- | ---------------------------------------------- | ------------------------------ | | **Retryable** | Transient failure, may succeed on retry | Retry with exponential backoff | | **Terminal** | Permanent failure, will not recover | Stop and report error | | **Non-fatal** | Something went wrong, but the run can continue | Log warning, continue | Failure decision flow Failure matrix by source LLM failures | Error | Classification | Example | | -------------------------- | ------------------------ | ------------------------------------- | | Rate limit (429) | Retryable | \"Rate limit exceeded, retry after 2s\" | | Server error (500/502/503) | Retryable | \"Internal server error\" | | Timeout | Retryable | \"Request timed out after 60s\" | | Auth error (401/403) | Terminal | \"Invalid API key\" | | Invalid request (400) | Terminal | \"Model does not exist\" | | Circuit breaker open | Terminal (with fallback) | \"Circuit breaker open for provider\" | **Configuration:** Tool failures | Error | Classification | Example | | ----------------- | -------------- | --------------------------------------------------------- | | Validation error | Non-fatal | \"Invalid arguments\" (returned to LLM for self-correction) | | Handler exception | Configurable | \"Tool raised an error\" | | Timeout | Configurable | \"Tool exceeded 10s timeout\" | | Policy denial | Non-fatal | \"Action denied by policy\" (returned to LLM) | **Configuration:** Subagent failures | Error | Classification | Example | | --------------------- | -------------- | --------------------------------------------------- | | Subagent run failed | Configurable | \"Subagent 'researcher' completed with state=failed\" | | Subagent timeout | Configurable | \"Subagent exceeded wall time\" | | Join policy violation | Terminal | \"Required subagent failed (all_required policy)\" | **Configuration:** Infrastructure failures | Error | Classification | Example | | -------------------------- | ------------------ | ------------------------------ | | Memory backend unavailable | Non-fatal | \"Could not persist checkpoint\" | | Telemetry export failed | Non-fatal (silent) | \"OTEL exporter timed out\" | | Queue push failed | Retryable | \"Redis connection refused\" | Failure policies Any failure causes the run to fail immediately. **Use when:** All operations are critical and partial results are worse than no results. Failures are tolerated. The run completes with state=\"degraded\" instead of \"completed\". **Use when:** Partial results are better than no results (e.g., some tool fails but the agent can still answer). Failures are logged but ignored. The run continues as if nothing happened. **Use when:** The failing component is non-essential (e.g., analytics tool, optional enrichment). Budget-triggered limits When a budget limit is hit, the run is stopped immediately: | Limit | Triggered by | Run state | | -------------------- | ------------------------ | ---------------------- | | max_steps | Step count exceeded | failed or degraded | | max_tool_calls | Tool call count exceeded | failed or degraded | | max_total_cost_usd | Estimated cost exceeded | failed | | max_wall_time_s | Wall time exceeded | interrupted | Next steps Security boundaries and hardening checklist. Run lifecycle and state management.", "token_count": 310} +{"id": "doc_2a18de009d56", "path": "docs/library/full-module-reference.mdx", "url": "/library/full-module-reference", "title": "Full Module Reference", "description": "Module inventory and responsibilities.", "headings": ["Module dependencies", "Extension points", "`afk.agents`", "`afk.core`", "`afk.llms`", "`afk.tools`", "`afk.memory`", "`afk.queues`", "`afk.observability`", "`afk.evals`", "`afk.mcp`", "`afk.messaging`"], "content_sha256": "294699e90c8917af6690bcdb9d7d9511ab0ed5cb92595b96f6436621c88ad4e1", "content": "The Agent Forge Kit (AFK) Python SDK is organized into top-level packages, each owning a distinct responsibility. This page summarizes the package map, key public imports, and extension boundaries. Use this reference when you need to find which package owns a capability. For field-level details, read the focused reference pages linked from the sidebar. Module dependencies The AFK architecture is organized around a few stable boundaries: - **afk.agents** owns declarative agent configuration, policy, prompts, skills, delegation metadata, and A2A contracts. - **afk.core** owns execution: runner lifecycle, streaming, interaction providers, delegation scheduling, checkpointing, memory wiring, and telemetry wiring. - **afk.llms** owns model provider adapters, runtime policies, structured output helpers, routing, caching, and LLM request/response types. - **afk.tools** owns tool definitions, registries, hooks, middleware, sandbox profiles, and prebuilt runtime tools. Extension points The framework is designed to be extended at specific protocol boundaries. **Do not subclass internal classes**. Instead, implement these protocols: - **InteractionProvider** (afk.core): Build custom human-in-the-loop interfaces (for example Slack, Discord, or web approval UIs). - **MemoryStore** (afk.memory): Add support for new databases (Mongo, DynamoDB). - **LLMProvider** (afk.llms): Integrate new model providers (local inference, exotic APIs). - **TelemetrySink** (afk.observability): Export metrics/traces to custom backends (Datadog, Honeycomb). --- afk.agents Agent definitions, lifecycle types, delegation, policy, A2A protocol, and error hierarchy. **Sub-modules:** | Sub-module | Responsibility | | ----------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | afk.agents.core | Agent and BaseAgent definitions, ChatAgent variant. | | afk.agents.types | Runtime types: AgentResult, AgentRunEvent, AgentRunHandle, AgentState, PolicyEvent, PolicyDecision, FailSafeConfig, UsageAggregate, execution records. | | afk.agents.policy | PolicyEngine, PolicyRule, PolicyEvaluation, policy subject inference. | | afk.agents.delegation | DelegationPlan, DelegationNode, DelegationEdge, RetryPolicy, JoinPolicy, DelegationResult. | | afk.agents.a2a | InternalA2AProtocol, A2A auth providers (AllowAllA2AAuthProvider, APIKeyA2AAuthProvider, JWTA2AAuthProvider), A2AServiceHost, delivery stores. | | afk.agents.contracts | AgentCommunicationProtocol, AgentInvocationRequest, AgentInvocationResponse, AgentDeadLetter. | | afk.agents.prompts | PromptStore, prompt file resolution, template rendering. | | afk.agents.lifecycle | Checkpoint versioning, schema migration, runtime helpers (circuit breaker, effect journal, state snapshots). | | afk.agents.skills | SkillStore, SkillDoc, SKILL.md parsing, checksum verification, and process-wide caching. | | afk.agents.security | Prompt-injection sanitization, untrusted tool-output redaction, and channel markers for trusted vs untrusted content. | | afk.agents.errors | Full error hierarchy: AgentError, AgentExecutionError, AgentCancelledError, SubagentRoutingError, etc. | **Key imports:** afk.core Runner execution engine, streaming, interaction providers, and delegation engine. **Sub-modules:** | Sub-module | Responsibility | | ---------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------- | | afk.core.runner | Runner class with run(), run_sync(), run_handle(), run_stream(), resume(), compact_thread(). RunnerConfig for safety and behavior defaults. | | afk.core.streaming | AgentStreamHandle, AgentStreamEvent, stream event constructors (text_delta, tool_started, stream_completed). | | afk.core.interaction | InteractionProvider protocol, HeadlessInteractionProvider, InMemoryInteractiveProvider. | | afk.core.runtime | DelegationEngine, DelegationPlanner, DelegationScheduler, backpressure and graph validation. | **Key imports:** afk.llms LLM client builder, provider registry, runtime policies, caching, routing, middleware, and type definitions. **Sub-modules:** | Sub-module | Responsibility | | --------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------ | | afk.llms.builder | LLMBuilder -- fluent builder for constructing LLM client instances. | | afk.llms.providers | Provider registry: OpenAIProvider, LiteLLMProvider, AnthropicAgentProvider, register_llm_provider(). | | afk.llms.runtime | LLMClient with production policies: RetryPolicy, TimeoutPolicy, RateLimitPolicy, CircuitBreakerPolicy, HedgingPolicy, CachePolicy. | | afk.llms.routing | Router registry: create_llm_router(), register_llm_router(). | | afk.llms.cache | Cache backends: create_llm_cache(), in-memory and Redis implementations. | | afk.llms.middleware | MiddlewareStack for request/response transformation pipelines. | | afk.llms.types | LLMRequest, LLMResponse, Message, ToolCall, Usage, streaming event types. | | afk.llms.config | LLMConfig dataclass for model configuration. | | afk.llms.profiles | Production/development profile presets. | | afk.llms.errors | LLMError, LLMTimeoutError, LLMRetryableError, etc. | **Key imports:** afk.tools Tool definition, registry, hooks, middleware, security, and prebuilt runtime tools. **Sub-modules:** | Sub-module | Responsibility | | --------------------- | --------------------------------------------------------------------------------------- | | afk.tools.core | tool decorator, ToolSpec, ToolContext, ToolResult, ToolRegistry. | | afk.tools.security | SandboxProfile, SandboxProfileProvider, SecretScopeProvider, argument validation. | | afk.tools.prebuilts | Built-in runtime tools (file read, directory list, command execution, skill tools). | | afk.tools.registry | Shared registry and registry middleware helpers. | **Key imports:** afk.memory Pluggable memory stores, compaction, retention policies, long-term memory, vector search, and thread state persistence. **Sub-modules:** | Sub-module | Responsibility | | ----------------------- | ----------------------------------------------------------------------------------------------------------------------------- | | afk.memory.store | MemoryStore abstract base class, MemoryCapabilities declarations. | | afk.memory.types | MemoryEvent, LongTermMemory, JsonValue, retention policy models. | | afk.memory.lifecycle | apply_event_retention(), apply_state_retention(), compact_thread_memory(), MemoryCompactionResult. | | afk.memory.vector | cosine_similarity(), vector scoring utilities for embedding-based search. | | afk.memory.factory | create_memory_store_from_env() factory for environment-based backend selection. | | afk.memory.adapters | Backend implementations: InMemoryMemoryStore, SQLiteMemoryStore, PostgresMemoryStore, RedisMemoryStore. | **Key imports:** afk.queues Task queue abstraction, worker lifecycle, failure classification, and metrics. **Sub-modules:** | Sub-module | Responsibility | | ---------------------- | --------------------------------------------------- | | afk.queues.base | Base queue types and enqueue/dequeue contracts. | | afk.queues.contracts | Queue protocol definitions. | | afk.queues.factory | Queue backend factory for creating queue instances. | | afk.queues.worker | Worker lifecycle management and task dispatch. | | afk.queues.metrics | Queue-level operational metrics. | **Key imports:** afk.observability Telemetry collection, projection, and export for runtime monitoring. **Sub-modules:** | Sub-module | Responsibility | | ------------------------------ | ---------------------------------------------------------------------------------------- | | afk.observability.models | RunMetrics dataclass with aggregated run statistics. | | afk.observability.contracts | Span and metric name constants (SPAN_AGENT_RUN, METRIC_AGENT_LLM_CALLS_TOTAL, etc.). | | afk.observability.projectors | Projection functions that transform run results into RunMetrics. | | afk.observability.exporters | ConsoleRunMetricsExporter and other output formatters. | | afk.observability.backends | Telemetry sink creation (create_telemetry_sink). | **Key imports:** afk.evals Eval suite execution, assertion contracts, budget constraints, and report serialization. **Sub-modules:** | Sub-module | Responsibility | | --------------------- | ------------------------------------------------------------------------------------------ | | afk.evals.suite | run_suite() and arun_suite() entrypoints. | | afk.evals.executor | Single-case execution via run_case() and arun_case(). | | afk.evals.models | EvalCase, EvalCaseResult, EvalSuiteConfig, EvalSuiteResult, EvalAssertionResult. | | afk.evals.contracts | EvalAssertion, AsyncEvalAssertion, EvalScorer protocols. | | afk.evals.budgets | EvalBudget dataclass and evaluate_budget() function. | | afk.evals.reporting | suite_report_payload(), write_suite_report_json(). | | afk.evals.golden | write_golden_trace(), compare_event_types() for deterministic trace comparison. | **Key imports:** afk.mcp Model Context Protocol server implementation, tool store, and server registration. **Sub-modules:** | Sub-module | Responsibility | | ---------------- | --------------------------------------------------------------- | | afk.mcp.server | MCP protocol handler. | | afk.mcp.store | Tool store registry and utility functions for MCP tool loading. | **Key imports:** afk.messaging Protocol-first agent-to-agent messaging exports. **Sub-modules:** | Sub-module | Responsibility | | ------------------------ | ---------------------------------------------------------------------------------------- | | afk.messaging | Public re-exports for A2A contracts, envelopes, auth providers, hosts, and delivery stores. | | afk.agents.a2a | Internal implementation modules for protocol, auth, delivery, hosting, and Google adapter. | **Key imports:**", "token_count": 834} +{"id": "doc_3ae3aaf0c12c", "path": "docs/library/how-to-use-afk.mdx", "url": "/library/how-to-use-afk", "title": "How to Use AFK", "description": "A practical adoption path from first agent to production release.", "headings": ["Phase 1: narrow agent", "Phase 2: tools and safety", "Phase 3: production controls", "Phase 4: release discipline", "Common mistakes", "Next steps"], "content_sha256": "c96092176ab2a302d53853ea5fa4e97e93a3082241ca8210f0aa16ef50e803f4", "content": "Use AFK incrementally. Start with one narrow agent, add tools only when the task needs action, and add production controls before real users depend on the system. Phase 1: narrow agent Build one agent that solves one task without tools. Move on when the agent is reliable on real examples for the narrow task. Phase 2: tools and safety Add typed tools and hard limits. Move on when all tools have Pydantic argument models, cost limits are set, and mutating operations are gated or absent. Phase 3: production controls Before shipping, add the controls that make failures diagnosable: - evals for expected behavior; - telemetry for latency, usage, errors, and tool calls; - persistent memory or queues if runs must survive process restarts; - security controls for sandboxing, secret scope, and tool output limits; - troubleshooting docs for on-call and operators. Useful pages: Test agent behavior and enforce budgets. Export metrics, traces, and run records. Understand policy gates, sandboxing, and secret isolation. Run agents through distributed workers. Phase 4: release discipline Once the agent is in production: - run evals in CI before prompt, tool, or model changes; - compare behavior across releases with golden traces where appropriate; - monitor cost per run and failure rate; - version system prompts in files; - document operator actions for approval, resume, and rollback flows. Common mistakes | Mistake | Better approach | | --- | --- | | Starting with a multi-agent system | Start with one narrow agent and split only when roles are genuinely different | | Writing untyped tools | Use Pydantic argument models for every tool | | Treating prompts as the only safety layer | Add FailSafeConfig, policy gates, sandboxing, and evals | | Hiding internals in public docs | Keep builder docs behavior-first and maintainer docs internals-first | | Shipping without run records | Export telemetry and inspect AgentResult fields | Next steps Read Building with AI for production design patterns, then Troubleshooting for common operational failures.", "token_count": 236} +{"id": "doc_f2ae50c638a4", "path": "docs/library/learn-in-15-minutes.mdx", "url": "/library/learn-in-15-minutes", "title": "Learn AFK in 15 Minutes", "description": "A guided path through agents, tools, streaming, memory, and safety.", "headings": ["1. Agent + runner", "2. Typed tools", "3. Streaming", "4. Memory continuity", "5. Safety limits", "What to read next"], "content_sha256": "1d734acb805aeb148e75286864ddd24fd7e503a8a895b294749e25cf59c849d9", "content": "This tutorial expands the quickstart into the core workflow used by most AFK applications. Each section introduces one concept and keeps the code small enough to copy into a single file. 1. Agent + runner Key point: an agent is declarative. The runner owns execution. 2. Typed tools Key point: tool arguments are validated before your function runs. 3. Streaming Use streaming when a UI or CLI should show progress before the final result is ready. Key point: run_stream() returns an AgentStreamHandle. Consume it to receive text, tool lifecycle events, errors, and the terminal result. 4. Memory continuity Pass the same thread_id to keep conversation context attached to a thread. Key point: thread continuity is explicit. Use the same thread_id for related turns. 5. Safety limits Production agents need hard limits even when prompts and tools are well designed. Key point: limits are part of the agent contract. Set them before shipping. What to read next Agent fields, prompt resolution, subagents, skills, and MCP tools. Tool schemas, context, hooks, middleware, sandboxing, and output limits. Event types, stream handles, cancellation, and UI patterns. Evals, telemetry, security controls, queues, and deployment.", "token_count": 142} +{"id": "doc_5ee8bbdbd8a8", "path": "docs/library/llm-interaction.mdx", "url": "/library/llm-interaction", "title": "LLM Interaction", "description": "How runner and llms runtime exchange requests and responses.", "headings": ["Interaction diagram", "Request construction", "Simplified view of what the runner builds", "Response handling", "LLMRequest fields", "LLMResponse fields", "Streaming interaction", "Error handling at the LLM boundary"], "content_sha256": "1ea841c0c4ab463b019f66f66da07cdc0d9e893b77493001e1f6d45d0dbd18eb", "content": "The runner communicates with LLM providers through a well-defined request/response boundary. Understanding this boundary is important for debugging model behavior, diagnosing latency issues, and implementing custom providers or middleware. This page explains how the runner constructs LLM requests, how responses are interpreted, how streaming works, and how errors are handled at the LLM boundary. Interaction diagram Each step in this flow is described in detail below. Request construction At each step of the agent loop, the runner builds an LLMRequest and sends it to the configured LLM provider. Here is what goes into the request: **Model resolution.** The agent's model field (e.g., \"gpt-4.1-mini\") is resolved through the model resolution chain. This maps the requested model name to a provider, adapter, and normalized model identifier. Custom model_resolver functions can override this mapping. **Message history.** The runner maintains a list of Message objects representing the conversation history. This includes: - A system message containing the agent's instructions, skill manifests, and (when enabled) an untrusted-data preamble. - The initial user message. - assistant messages from previous LLM responses. - tool messages containing the results of tool executions. **Tool definitions.** If the agent has registered tools, they are exported as OpenAI-compatible function tool definitions and included in LLMRequest.tools. The tool_choice is set to \"auto\" so the model can decide whether to call tools. **Session and checkpoint tokens.** For providers that support stateful sessions (like Anthropic's agent SDK), the runner passes session_token and checkpoint_token from previous responses to maintain session continuity. **Metadata.** The request includes metadata about the run (run_id, thread_id, agent_name) and content channel markers that help the provider distinguish trusted system content from untrusted tool output. Response handling The LLM runtime normalizes provider-specific responses into an LLMResponse dataclass. The runner then interprets this response to decide what happens next: **If the response contains no tool calls** (resp.tool_calls is empty), the run is complete. The runner extracts resp.text as final_text, captures any resp.structured_response, and transitions to state=\"completed\". **If the response contains tool calls**, the runner enters the tool execution phase: 1. Each tool call is evaluated by the policy engine. 2. Approved tools are executed (in parallel, up to the batch limit). 3. Tool results are appended to the message history as tool messages. 4. The runner loops back to make another LLM call with the updated history. **Usage tracking.** The LLMResponse.usage field contains token counts (input_tokens, output_tokens, total_tokens). The runner accumulates these into UsageAggregate for cost estimation and budget enforcement. **Session continuity.** If the response includes updated session_token or checkpoint_token, the runner stores these for the next request. LLMRequest fields | Field | Type | Purpose | | --- | --- | --- | | model | str | Normalized model identifier. | | request_id | str | Unique request ID for tracing. | | messages | list[Message] | Conversation history. | | tools | list[dict] or None | OpenAI-format tool definitions. | | tool_choice | str or None | Tool selection strategy (\"auto\", \"none\", or specific tool). | | max_tokens | int or None | Maximum response tokens. | | temperature | float or None | Sampling temperature. | | top_p | float or None | Nucleus sampling parameter. | | stop | list[str] or None | Stop sequences. | | idempotency_key | str or None | Key for request deduplication. | | session_token | str or None | Provider session continuity token. | | checkpoint_token | str or None | Provider checkpoint continuity token. | | timeout_s | float or None | Request-level timeout in seconds. | | metadata | dict | Run context metadata. | LLMResponse fields | Field | Type | Purpose | | --- | --- | --- | | text | str | Model-generated text content. | | tool_calls | list[ToolCall] | Requested tool invocations. | | finish_reason | str or None | Why the model stopped generating (\"stop\", \"tool_calls\", etc.). | | usage | Usage | Token counts: input_tokens, output_tokens, total_tokens. | | structured_response | dict or None | Parsed structured output when using response_model. | | session_token | str or None | Updated session token for next request. | | checkpoint_token | str or None | Updated checkpoint token for next request. | Streaming interaction For real-time UIs, the runner supports streaming via runner.run_stream(). The streaming path works differently from the batch path: The stream produces AgentStreamEvent instances that include: - text_delta -- incremental text (provider stream deltas, or fallback chunking for non-streaming providers). - step_started -- signals a new step in the agent loop. - tool_started / tool_completed -- tool lifecycle events. - error -- error notification. - completed -- terminal event containing the final AgentResult. Error handling at the LLM boundary Errors at the LLM boundary are classified and handled according to the failure policy matrix: **Retryable errors** (timeouts, rate limits, server errors) are retried with exponential backoff. The runner supports a fallback model chain: if the primary model fails after retries, it tries the next model in FailSafeConfig.fallback_model_chain. **Terminal errors** (auth failures, invalid payloads) are not retried. The llm_failure_policy determines what happens next: - \"fail\" -- the run aborts with state=\"failed\". - \"degrade\" -- the run terminates with state=\"degraded\" and the error message as final_text. **Circuit breaker** protection prevents cascading failures. After breaker_failure_threshold consecutive failures to the same model+provider, the circuit opens and subsequent calls fail fast until the cooldown expires. **Policy denial** of LLM calls is handled before the call is made. If the policy engine denies an LLM call, the runner applies the llm_failure_policy without ever contacting the provider.", "token_count": 607} +{"id": "doc_e34fe24f9585", "path": "docs/library/mcp-server.mdx", "url": "/library/mcp-server", "title": "MCP Server", "description": "Expose and consume tools via the Model Context Protocol.", "headings": ["Architecture", "Expose tools via MCP server", "Consume tools from external MCP servers", "Security", "MCP server vs A2A", "Next steps"], "content_sha256": "92cdc3393c943799a3a2ff6409f04605595a4b4e78bc36eb42d27cead0181fe3", "content": "The Model Context Protocol (MCP) is an open standard for sharing tools between AI systems. AFK supports both **exposing** your tools via an MCP server and **consuming** tools from external MCP servers. Architecture Expose tools via MCP server Make your AFK tools available to any MCP-compatible client: Consume tools from external MCP servers Discover and use tools from any MCP server: **MCP tools are transparent.** Once attached to an agent, MCP tools behave exactly like local tools \u2014 same validation, same policy gates, same telemetry. The agent doesn't know (or care) whether a tool is local or remote. Security Require auth tokens for all MCP connections: Configure allowed origins for browser-based clients: Apply policy rules to MCP-exposed tools: Limit request rates per client: **Always authenticate MCP servers in production.** An unauthenticated server exposes your tools to anyone who can reach the endpoint. MCP server vs A2A | Feature | MCP Server | A2A | | --------------- | ---------------------------- | ---------------------------- | | **Shares** | Individual tools | Full agents | | **Protocol** | MCP standard | AFK A2A protocol | | **Use case** | Tool sharing between systems | Agent-to-agent communication | | **Client sees** | Tool schemas and results | Agent responses | | **Interop** | Any MCP client | AFK agents | Next steps Share full agents across systems. Full security architecture.", "token_count": 162} +{"id": "doc_0383dbf277ba", "path": "docs/library/memory.mdx", "url": "/library/memory", "title": "Memory", "description": "Persist conversation state, resume runs, and compact threads.", "headings": ["Quick start: multi-turn conversation", "What gets stored", "State lifecycle", "Resume interrupted runs", "Start a run that might be long", "If interrupted, resume later", "Compact long threads", "Memory backends", "Connection pooling for Redis", "Use the pool with RedisMemoryStore", "Environment-based selection", "Custom backends", "Long-term memory", "Store a long-term memory", "Retrieve memories", "Vector search", "Search by embedding similarity", "Text search", "Design guidelines", "Next steps"], "content_sha256": "6df7c441a722d1ed17a8489a0f085e0a05f8e9dd2829cad72addc5a7a93bfec1", "content": "AFK's memory system persists conversation state across runs. Use it for multi-turn conversations, run resumption after interrupts, long-term knowledge retention, and vector-based semantic search. Quick start: multi-turn conversation The thread_id links runs into a conversation. AFK automatically persists messages between runs. What gets stored | Record type | What it contains | When it's written | | ------------------ | ---------------------------------------------------------- | --------------------------------------- | | **Event** | User messages, assistant responses, tool calls and results | After each run step | | **Checkpoint** | Full run state at a point in time | At step boundaries (pre-LLM, post-tool) | | **State (KV)** | Checkpoint pointers, effect journal, background tool state | During and after runs | | **Long-term memory** | Persistent knowledge with optional embeddings | Via upsert_long_term_memory | **What's NOT stored automatically:** Raw LLM provider responses or internal framework temporaries. Only conversation-visible records and explicit state writes are persisted. State lifecycle Resume interrupted runs If a run is interrupted (crash, timeout, pause for approval), resume from the last checkpoint: **Checkpoints are written at key boundaries:** before each LLM call, after each tool batch, and after each step completes. On resume, completed tool calls are replayed from the effect journal \u2014 no duplicate side effects. Compact long threads Over time, conversation threads grow and consume tokens. Use compaction to trim old events: Compaction applies retention rules: protected event types are preserved first, then the most recent remaining events fill the budget. Memory backends AFK ships with four backends. All implement the MemoryStore protocol. State lives in process memory. Fast, no setup, but lost on restart. **Use for:** Development, testing, short-lived scripts. Persistent local storage with JSON serialization and local vector search. Features: WAL mode, text search, vector similarity search (cosine), atomic upsert. **Use for:** Local development with persistence, single-process deployments. Production-grade backend with pgvector support for vector search. **Use for:** Production multi-process deployments. In-memory store backed by Redis for shared state across processes. **Use for:** Shared state across workers, ephemeral but durable-enough sessions. Connection pooling for Redis For production Redis deployments, use connection pooling for better performance: Environment-based selection Set environment variables to auto-select a backend without code changes: The runner falls back to in-memory if the configured backend fails to initialize. Custom backends Implement the MemoryStore abstract class to add support for any database: Declare capabilities to tell the framework which features your backend supports. Features like vector search are only used when the backend declares support. Long-term memory Beyond conversation events, AFK supports persistent long-term memories scoped per user and purpose: Vector search Backends that support vector search (SQLite, Postgres) can find semantically similar memories: Text search All backends support basic text search across memory content: Design guidelines - **Always use thread_id for conversations.** Without it, each run starts fresh. - **Compact threads proactively.** Don't wait until you hit token limits. A good rule: compact when the thread exceeds ~500 events. - **Use checkpoints for long-running agents.** If a run might take minutes, checkpoints let you resume on failure. - **Don't store secrets in memory.** Thread events are persisted and may be readable. - **Choose the right backend.** In-memory for dev, SQLite for local persistence, Postgres/Redis for production. - **Use scopes for long-term memory.** Organize memories by purpose (preferences, knowledge, history) to keep queries efficient. Next steps Resume and compact APIs on the Runner. Template prompts with context from memory.", "token_count": 430} +{"id": "doc_50ac650fd8f6", "path": "docs/library/mental-model.mdx", "url": "/library/mental-model", "title": "Mental Model", "description": "Three coordinated loops that drive every AFK agent.", "headings": ["The three loops", "Think in contracts", "Decision tree: how complex should my system be?", "What success looks like"], "content_sha256": "cf294e0c707755bfe6bfc555054eaefbfb62e65f3ea92ecd9e2b4cfec5bb792c", "content": "AFK agents execute through three coordinated loops \u2014 **Decision**, **Execution**, and **Assurance**. Understanding these loops helps you reason about agent behavior, debug issues, and design systems that scale predictably. The three loops The Decision Loop is the model's turn. On each step: 1. The runner sends the conversation history + tool schemas to the LLM 2. The LLM decides whether to respond with text (done) or request tool calls (continue) 3. If tool calls are requested, they flow to the Execution Loop **You control this with:** agent instructions, model choice, tool availability The Execution Loop handles every tool call: 1. **Validate** arguments against the Pydantic schema 2. **Policy gate** \u2014 allow, deny, or defer for human approval 3. **Execute** the handler (with hooks and middleware) 4. **Sanitize** the output (truncate, strip injection vectors) 5. **Return** the result to the Decision Loop **You control this with:** tool definitions, policy rules, sandbox profiles The Assurance Loop runs continuously, enforcing limits on both other loops: - **Step count** \u2014 stops the agent after N iterations - **Tool call count** \u2014 prevents excessive tool usage - **Cost budget** \u2014 stops if estimated cost exceeds the limit - **Wall time** \u2014 hard timeout on the entire run - **Failure classification** \u2014 retryable, terminal, or non-fatal **You control this with:** FailSafeConfig Think in contracts AFK is built on a contract-first design. Every interaction between components is defined by typed data structures: | Boundary | Contract | What flows | | ------------------ | ---------------------------------------------------- | --------------------------------------- | | Runner \u2192 LLM | LLMRequest / LLMResponse | Messages, tool schemas, model responses | | Runner \u2192 Tool | ToolCall / ToolResult | Validated arguments, execution output | | Runner \u2192 Subagent | AgentInvocationRequest / AgentInvocationResponse | Delegate task and receive result | | Runner \u2192 Memory | Checkpoint records | Conversation state for resume/replay | | Runner \u2192 Telemetry | AgentRunEvent, RunMetrics | Spans, metrics, audit trail | **Contracts are Pydantic models.** This means every boundary is validated at runtime \u2014 malformed data causes clear errors, not silent bugs. When you see a validation error, it's AFK telling you exactly where the contract was violated. Decision tree: how complex should my system be? Not sure what to build? Start at the top and follow the path that matches your use case. > [!TIP] > **Start at Level 1.** Only move up when you have clear evidence that your current level isn't enough. Each level adds complexity that you need to manage and test. What success looks like A mature AFK implementation exhibits these properties: - **Every tool has a Pydantic model** \u2014 no untyped arguments - **Every run has cost limits** \u2014 max_total_cost_usd is always set - **Policy gates protect mutations** \u2014 dangerous actions require approval - **Evals cover core behaviors** \u2014 regression tests catch prompt drift - **Observability is on from day one** \u2014 even if it's just the console exporter - **Failures are classified** \u2014 the system knows what to retry and what to abort", "token_count": 325} +{"id": "doc_75887f91c7df", "path": "docs/library/messaging.mdx", "url": "/library/messaging", "title": "Internal Messaging", "description": "Agent-to-agent messaging with delivery guarantees and idempotency.", "headings": ["Message lifecycle", "The InternalA2AEnvelope", "Delivery behavior", "Idempotency and correlation", "All messages in this workflow share the same correlation_id", "Delivery store backends", "Next steps"], "content_sha256": "e8d90b294c3ed2ebbbff257fa28d0be7ba2cce6d689a3161f2306ddf95ba1ee2", "content": "AFK's internal messaging system lets agents communicate via structured envelopes with **at-least-once delivery** and **idempotency**. Use it when agents in the same system need to exchange data reliably. Message lifecycle The InternalA2AEnvelope Every message is wrapped in a typed envelope: | Field | Type | Purpose | | ----------------- | ---------- | -------------------------------------------- | | message_type | str | request, response, or event | | run_id | str | Run identifier for tracing | | thread_id | str | Memory thread identifier | | conversation_id | str | Cross-run conversation identifier | | payload | dict | Message data (any JSON-serializable content) | | correlation_id | str | Groups related messages in a workflow | | idempotency_key | str | Deduplication \u2014 same key = same message | | source_agent | str | Name of the sending agent | | target_agent | str | Name of the receiving agent | | metadata | dict | JSON-safe tracing or routing metadata | | timestamp_ms | int | Creation timestamp in milliseconds | Delivery behavior 1. Sender creates and submits the envelope 2. Delivery store checks the idempotency key (rejects duplicates) 3. Message is delivered to the receiver 4. Receiver processes and ACKs 5. Store marks as delivered **Result:** Message processed exactly once. 1. Delivery fails (receiver timeout, transient error) 2. Store schedules retry with exponential backoff 3. Message is redelivered (same idempotency key) 4. Receiver processes and ACKs on retry **Result:** At-least-once delivery. Receiver must be idempotent. 1. All retry attempts exhausted 2. Message moves to dead-letter queue 3. Alert generated (if configured) **Result:** Message is not lost \u2014 it's in the DLQ for manual review. Idempotency and correlation **Always set an idempotency_key.** Without it, retry deliveries can cause duplicate processing. Use a deterministic key derived from the task context (e.g., f\"{task_id}-{step_name}\"). **Correlation IDs** group related messages across a workflow. When debugging, filter by correlation_id to see the full message chain for a task. Delivery store backends Default. Fast, no setup. State lost on restart. Implement the DeliveryStore protocol for durable messaging: Next steps Cross-system agent communication. Async job processing with queue backends.", "token_count": 226} +{"id": "doc_5a65ddab1445", "path": "docs/library/migration.mdx", "url": "/library/migration", "title": "Migration Guide", "description": "Move from LangChain, OpenAI Assistants, or custom agents to AFK.", "headings": ["From LangChain", "LangChain \u2192 AFK concepts", "Basic agent migration", "Key differences", "LangChain: agent is callable", "AFK: Agent defines what, Runner executes how", "LangChain: function signature and docstring", "AFK: Pydantic model for typed arguments", "LangChain: single run() method", "AFK: explicit sync, async, or streaming", "Tool migration", "Simple tool", "Structured tool with custom logic", "Simple tool", "Tool with constraints", "Memory migration", "Configure memory backend", "Use thread_id for conversation continuity", "Callback \u2192 Middleware migration", "RAG migration", "Store documents with embeddings", "Retrieve in tool", "From OpenAI Assistants API", "Assistants \u2192 AFK concepts", "Basic migration", "Key advantages of AFK over Assistants API", "From custom agent code", "Common patterns migration", "Before: Custom retry implementation", "Before: Custom circuit breaker", "Next steps"], "content_sha256": "5918b39a7bcd88e65ba3aba1c07491fdab8b878f8eb6dea163cffe3b02ab8b7f", "content": "This guide helps you migrate existing agent code from other frameworks to AFK. From LangChain LangChain \u2192 AFK concepts | LangChain Concept | AFK Equivalent | Key Difference | |-------------------|----------------|----------------| | ChatOpenAI | LLMBuilder | Provider-portable, typed contracts | | Agent | Agent | Config object, not runtime | | Tool | @tool decorator | Pydantic-based, typed arguments | | Chain | Runner | Explicit execution loop | | Memory | MemoryStore | Multiple backends, checkpointing | | Callback | Middleware / Hooks | Request/response interception | | LangSmith | Telemetry | Built-in OTEL support | Basic agent migration **LangChain:** **AFK:** Key differences **1. Agent is a config object, not a runtime:** **2. Tools use Pydantic for validation:** **3. Explicit execution modes:** Tool migration **LangChain tools:** **AFK equivalents:** Memory migration **LangChain memory:** **AFK memory:** Callback \u2192 Middleware migration **LangChain callbacks:** **AFK middleware:** RAG migration **LangChain retrieval:** **AFK approach:** From OpenAI Assistants API Assistants \u2192 AFK concepts | OpenAI Concept | AFK Equivalent | |----------------|----------------| | Assistant | Agent | | Thread | Memory + thread_id | | Run | Runner execution | | Message | MemoryEvent | | Tool | @tool decorator | | Function | @tool with Pydantic | | File search | Long-term memory + vector search | Basic migration **OpenAI Assistants:** **AFK:** Key advantages of AFK over Assistants API 1. **Local execution** \u2014 No API calls needed for simple tasks 2. **Portable** \u2014 Switch LLM providers without code changes 3. **Debuggable** \u2014 Step through agent logic locally 4. **Testable** \u2014 Run evals locally in CI 5. **Controllable** \u2014 Full access to prompts, tools, and behavior From custom agent code Common patterns migration **Custom retry logic:** **AFK:** **Custom circuit breaker:** **AFK:** Next steps Build your first AFK agent in 5 minutes. Understand AFK's design philosophy. Complete API documentation. Runnable examples for every feature.", "token_count": 208} +{"id": "doc_78a8125d2c7e", "path": "docs/library/observability.mdx", "url": "/library/observability", "title": "Observability", "description": "Built-in telemetry with spans, metrics, and exporters.", "headings": ["Setup in one line", "Console output (development)", "OpenTelemetry (production)", "JSON lines (log files)", "Telemetry pipeline", "RunMetrics reference", "Choosing an exporter", "Telemetry spans", "Alerting recommendations", "Next steps"], "content_sha256": "125c3f894dc8006a91e85aa44be8559452e56c5c7984b6cc2fab73dc99763e43", "content": "AFK includes a telemetry pipeline that captures every agent run event \u2014 LLM calls, tool executions, state transitions, and performance metrics. Export to the console for development, JSON for log aggregation, or OpenTelemetry for production. Setup in one line Telemetry pipeline RunMetrics reference Every completed run produces a RunMetrics object: | Metric | Type | Description | | --------------------- | ------- | ---------------------------------- | | total_steps | int | Number of agent loop iterations | | total_llm_calls | int | Number of LLM API calls | | total_tool_calls | int | Number of tool executions | | total_tokens | int | Total tokens (prompt + completion) | | total_cost_usd | float | Estimated cost in USD | | wall_time_s | float | Total run duration in seconds | | first_token_ms | float | Time to first token (streaming) | | tool_latency_p50_ms | float | Median tool execution latency | | tool_latency_p99_ms | float | 99th percentile tool latency | | error_count | int | Number of errors during the run | Choosing an exporter Human-readable output to stdout. Best for development and debugging. Structured output for log aggregation (ELK, Datadog, etc.). Each event is a JSON line: Export spans and metrics to any OTEL-compatible backend (Jaeger, Grafana, Honeycomb, etc.). **Use for:** Production deployments with existing observability infrastructure. Telemetry spans AFK creates spans for key operations: Spans capture timing, success/failure, and metadata. In OTEL mode, these map directly to traces visible in your observability dashboard. Alerting recommendations | Alert | Condition | Severity | | -------------------- | -------------------------------------------- | ---------- | | Error rate spike | error_count / total_runs > 5% over 5 min | **High** | | LLM latency spike | p99 > 10s for 5 min | **Medium** | | Cost anomaly | daily_cost > 2x rolling average | **High** | | Tool failure rate | tool_failures / tool_calls > 10% for 5 min | **Medium** | | Circuit breaker open | Any LLM circuit breaker trips | **High** | **Start with the console exporter** even in staging. It costs nothing and gives you immediate visibility into agent behavior. Switch to OTEL when you have a monitoring stack. Next steps Behavioral testing for agent quality. Security boundaries and hardening.", "token_count": 223} +{"id": "doc_f18f523b6ecc", "path": "docs/library/overview.mdx", "url": "/library/overview", "title": "What is AFK?", "description": "The short mental model for building and maintaining AFK agents.", "headings": ["The three core objects", "When AFK is a good fit", "Builder path", "Maintainer path", "Source-of-truth rules", "Next steps"], "content_sha256": "3d4dbe025de523cf7ac36a6ca13721c14fd9857f6f45bb8f3acebae1cb31452a", "content": "AFK is an agent runtime for Python applications. It is designed for teams that need agent behavior to be typed, observable, testable, and bounded by explicit controls. The core idea is simple: The three core objects | Object | What it owns | What it does not own | | --- | --- | --- | | Agent | Name, model, instructions, tools, subagents, skills, MCP servers, defaults, fail-safe limits | Network calls, event loops, persistence, telemetry export | | Runner | Execution loop, tool dispatch, streaming, memory, checkpointing, policy/HITL, telemetry | Agent identity or instructions | | AgentResult | Final text, terminal state, tool/subagent records, usage aggregate, cost, run/thread ids | Future execution | This separation is the main design rule. Agents describe behavior. Runners execute behavior. Runtime subsystems provide capabilities. When AFK is a good fit Use AFK when your agent will: - call tools or external systems; - run for more than one step; - need cost, time, step, or tool-call limits; - stream progress to a UI; - persist memory or resume a run; - require evals, telemetry, or production incident debugging; - coordinate specialist subagents. A direct LLM provider SDK may be simpler for one-off single-turn text generation. AFK is useful when the agent needs operational structure. Builder path If you are building an app with AFK, read in this order: 1. Quickstart for the smallest complete agent. 2. Learn AFK in 15 Minutes for the guided path. 3. Agents, Runner, and Tools for the core building blocks. 4. Building with AI, Evals, and Observability before production. Maintainer path If you are changing AFK itself, read in this order: 1. Developer Guide for local setup, commands, and docs workflow. 2. Architecture for package boundaries. 3. Public API Rules before changing exports or examples. 4. Tested Behaviors before changing runner, tools, LLM runtime, memory, queues, or telemetry. Source-of-truth rules - Public examples import from afk.*, never src.afk.*. - Runner is imported from afk.core. - User-facing docs should explain behavior before internals. - Maintainer docs may reference internal modules, but must identify the public contract affected by a change. - Generated agent-facing docs and skill indexes must be refreshed when navigation, examples, or skill references change. Next steps Build one agent and one typed tool. Set up the repo and understand the contributor workflow.", "token_count": 272} +{"id": "doc_f5f256549d84", "path": "docs/library/performance.mdx", "url": "/library/performance", "title": "Performance", "description": "Improve AFK latency, throughput, and cost without relying on internal APIs.", "headings": ["Latency", "Tool execution", "Throughput", "Cost", "Memory", "Measurement", "Checklist"], "content_sha256": "4ded523bc48148cc20cacc7af3c5ac32a07a19a6376e3d8d69bb08c2aa878c6f", "content": "Performance work in AFK usually comes from four levers: choosing the right model, reducing unnecessary tool/LLM calls, keeping memory bounded, and moving long-running work into queues. Latency Use the smallest model that can reliably handle the task, and reserve larger models for tasks that need deeper reasoning. Other latency controls: - keep system prompts short and specific; - make I/O-bound tools async; - avoid tools for information already present in context; - stream user-facing runs with runner.run_stream(...); - set tight max_steps, max_llm_calls, and max_wall_time_s limits. Tool execution Tools are often the slowest part of a run. Keep them typed, narrow, and bounded. Tool guidance: - validate inputs with Pydantic models; - enforce timeouts in external clients; - return compact JSON-safe payloads; - truncate or summarize large external responses before returning them; - use RunnerConfig(tool_output_max_chars=...) as a final bound. Throughput Use async runner APIs for services and workers. For durable background work, use task queues instead of keeping HTTP requests open. See Task Queues. Cost Set cost and loop limits on every production agent. Read cost from the terminal result: Memory Long threads increase prompt size and storage. Use explicit thread ids and compact retained state when threads grow. Choose the memory backend by deployment shape: | Backend | Use case | | --- | --- | | In-memory | Tests and local experiments | | SQLite | Single-process local or small deployments | | Redis | Shared state across processes | | Postgres | Persistent production storage and vector search | Configure backends with environment variables or pass a public MemoryStore implementation to Runner(memory_store=...). Measurement Measure from AgentResult first: For production, export telemetry through Observability and track latency, token usage, tool failures, degraded runs, and cost per run. Checklist - Use async runner APIs in servers and workers. - Stream user-facing runs. - Keep prompts and tool outputs compact. - Set fail-safe limits and cost budgets. - Compact long-running threads. - Move durable background work into queues. - Monitor token usage, tool count, state, and cost per run.", "token_count": 250} +{"id": "doc_3eeb75d383b8", "path": "docs/library/public-imports-and-function-improvement.mdx", "url": "/library/public-imports-and-function-improvement", "title": "Public API Rules", "description": "Maintainer rules for preserving AFK's public import contract.", "headings": ["Contract", "Rules for maintainers", "Preferred examples", "Imports to avoid in public docs", "Change checklist", "Search commands"], "content_sha256": "4ad0dbc7fa46f0049e5638a702fa2d73dbf1da35cb09099c277e41dd1fefbabc", "content": "This page is for AFK maintainers. It explains how to change public exports without making downstream code or docs confusing. For the user-facing import table, see API Reference. Contract The public API is the set of names exported by package-level __init__.py files: - afk.agents - afk.core - afk.tools - afk.llms - afk.memory - afk.queues - afk.mcp - afk.messaging - afk.observability - afk.evals Public docs and examples should import from these package surfaces. They should not use src.afk imports or deep implementation modules such as afk.core.runner.api. Rules for maintainers 1. If a downstream user should import a symbol, export it from the package-level __init__.py. 2. If a symbol is not exported, do not use it in builder docs or examples. 3. Keep Agent and Runner separate: Agent comes from afk.agents; Runner comes from afk.core. 4. Prefer protocols, dataclasses, Pydantic models, and explicit error classes for public contracts. 5. When removing or renaming a public symbol, update migration docs and tests in the same change. 6. When changing a public constructor, update API Reference, Configuration Reference, examples, and generated agent-facing docs. Preferred examples Imports to avoid in public docs | Avoid | Prefer | | --- | --- | | src.afk.agents.Agent | afk.agents.Agent | | afk.agents.core.base.Agent | afk.agents.Agent | | afk.core.runner.api.RunnerAPIMixin | afk.core.Runner | | afk.tools.core.decorator.tool | afk.tools.tool | | afk.llms.builder.LLMBuilder | afk.llms.LLMBuilder | Deep imports are acceptable in internal tests only when the test is specifically covering an internal unit. Integration tests and examples should exercise the public surface. Change checklist Before merging a public API change: - Update the relevant package __all__. - Add or update tests that import through the public package. - Update user-facing docs if a builder would see the changed behavior. - Update maintainer docs if an invariant or subsystem boundary changed. - Run PYTHONPATH=src pytest -q or targeted tests for the affected subsystem. - Regenerate agent-facing docs with ./scripts/build_agentic_ai_assets.sh when docs, examples, skill metadata, or navigation changes. Search commands The last command intentionally finds deep imports for review. Some maintainer references may be valid, but builder docs should avoid them.", "token_count": 277} +{"id": "doc_bf91f8d5da74", "path": "docs/library/quickstart.mdx", "url": "/library/quickstart", "title": "Quickstart", "description": "Build one AFK agent with one typed tool.", "headings": ["Prerequisites", "1. Define an agent", "2. Add one typed tool", "3. Read the result", "4. Keep going"], "content_sha256": "898fc17c3d87708f8ce6850453a470a6fad5eb53aa22488f66cd3b31e8ed4886", "content": "This page is the shortest useful AFK path: install the package, define an agent, attach one typed tool, and run it. Prerequisites - Python 3.13+ - An LLM provider key, such as OPENAI_API_KEY When working from this repository instead of an installed package: 1. Define an agent Agent stores configuration. Runner executes the run. AgentResult.final_text is the assistant response. 2. Add one typed tool Tools are Python functions with Pydantic argument models. AFK turns the model into a tool schema, validates model-provided arguments, executes the function, and feeds the result back into the agent loop. 3. Read the result Common fields on AgentResult: | Field | Meaning | | --- | --- | | final_text | Final assistant text | | state | Terminal state such as completed, failed, cancelled, or degraded | | run_id | Unique id for this run | | thread_id | Conversation/thread id used by memory | | tool_executions | Ordered records for tool calls | | subagent_executions | Ordered records for subagent calls | | usage_aggregate | Aggregated token usage | | total_cost_usd | Estimated total run cost when available | 4. Keep going Add streaming, memory, and safety controls. Find complete snippets for common scenarios. Understand the agent configuration object. Understand sync, async, and streaming execution.", "token_count": 135} +{"id": "doc_f54217a0f2cf", "path": "docs/library/run-event-contract.mdx", "url": "/library/run-event-contract", "title": "Run Event Contract", "description": "Runtime event schema and event consumption guidance.", "headings": ["Event stream model", "Event reference", "AgentRunEvent structure", "Consuming events", "Pattern: event-type branching", "Forward compatibility"], "content_sha256": "a4e2b6d7119af19a8eafb23eb7f8f84bf514594c4d2dd076e5bb6983d4097b19", "content": "Every AFK agent run produces a stream of AgentRunEvent instances that describe what happened during execution. These events form the run's audit trail -- they tell you when the run started, when LLM calls were made, when tools executed, when policy decisions were rendered, and how the run terminated. Understanding the event contract is essential for building real-time UIs, logging pipelines, eval assertions, and debugging tools. This page documents every event type, explains when each fires, describes the data it carries, and provides patterns for consuming events safely. Event stream model Every run begins with run_started and ends with exactly one terminal event: run_completed, run_failed, run_interrupted, or run_cancelled. Between those boundaries, the runner emits step, LLM, tool, policy, and subagent events in the order they occur. Event reference | Event Type | When It Fires | Key Fields in data | | --- | --- | --- | | run_started | At the beginning of a new or resumed run. | agent_name, resumed (bool) | | step_started | At the start of each step iteration. | step (int), state | | llm_called | Before sending a request to the LLM provider. | model, provider | | llm_completed | After receiving the LLM response. | tool_call_count, finish_reason, text | | tool_batch_started | Before executing a batch of tool calls from one LLM response. | tool_call_count, tool_names, tool_call_ids | | tool_completed | After each individual tool finishes executing. | tool_name, tool_call_id, success (bool), output, error, agent_name, agent_depth, agent_path | | tool_deferred | Tool accepted and moved to background processing. | tool_name, tool_call_id, ticket_id, status, summary, resume_hint | | tool_background_resolved | Deferred tool completed successfully. | tool_name, tool_call_id, ticket_id, output | | tool_background_failed | Deferred tool failed or expired. | tool_name, tool_call_id, ticket_id, error | | policy_decision | After the policy engine evaluates a tool, LLM, or subagent event. | event_type, action, reason, policy_id, matched_rules | | subagent_started | Before dispatching a subagent invocation. | subagent_name, correlation_id | | subagent_completed | After a subagent finishes (success or failure). | subagent_name, success, latency_ms, error (if failed) | | text_delta | During streamed runs when incremental model text arrives. | delta | | warning | When a non-fatal issue occurs (memory fallback, dead letters, etc.). | Varies by warning type | | run_completed | When the run reaches successful terminal state. | None (terminal) | | run_failed | When the run terminates due to an unrecoverable error. | error message in event.message | | run_interrupted | When the run is interrupted by the caller or by timeout. | Interruption reason in event.message | | run_cancelled | When the run is cancelled before completion. | None (terminal) | AgentRunEvent structure Each event is an AgentRunEvent dataclass with the following fields: | Field | Type | Description | | --- | --- | --- | | type | str | Event type identifier (see table above). | | run_id | str | Unique run identifier. | | thread_id | str | Thread identifier for memory continuity. | | state | str | Current run state when the event was emitted. | | step | int | Current step number (0 if not yet in a step). | | message | str or None | Human-readable description of what happened. | | data | dict or None | Structured payload with event-specific fields. | Consuming events The primary way to consume events is through the run handle's events async iterator: Pattern: event-type branching The recommended consumption pattern is a simple if/elif chain that branches on event.type. This is explicit, readable, and easy to extend: Forward compatibility The event contract is append-only. New event types may be added in future releases, but existing event types will not be removed or have their data fields changed. Your event consumer should always handle unknown event types gracefully. The simplest approach is a default branch that logs or ignores unknown types: This ensures your code continues to work when AFK adds new event types without requiring a code update.", "token_count": 434} +{"id": "doc_567be1281e2f", "path": "docs/library/security-model.mdx", "url": "/library/security-model", "title": "Security Model", "description": "Security boundaries, policy engine, and production hardening.", "headings": ["Security boundaries", "Default posture", "Production hardening checklist", "Secret isolation", "Threat model overview", "Next steps"], "content_sha256": "9c2f919368fe1ccad33c5509761ad84c85f2b3ccee485a59fc99402ad5ef9694", "content": "AFK implements security through **four boundaries** \u2014 policy engine, tool runtime, A2A/MCP bridges, and sandbox. Each boundary enforces least-privilege defaults and requires explicit opt-in for elevated permissions. Security boundaries Gate tool calls and agent actions with configurable rules. **Actions:** allow (default), deny, request_approval, request_user_input Every tool call passes through validation, policy checks, and output sanitization. **Sandbox profiles** are configured at the runner level, not per-tool: External communication requires authentication and per-caller authorization. Hard limits prevent runaway agents. Default posture AFK defaults to **least privilege**: | Setting | Default | Meaning | | ------------------------ | ---------------------------------------------------------------------------- | ------------------------------------- | | Tool policy | allow | Tools run unless explicitly denied | | Tool output sanitization | True | Output is sanitized by default | | A2A authentication | Required | No unauthenticated A2A | | MCP authentication | Required | No unauthenticated MCP | | Cost limits | None ( ) | **You must set max_total_cost_usd** | | Sandbox | None | Tools run in the host process | **Cost limits are not set by default.** Always configure max_total_cost_usd in production to prevent runaway spending. Production hardening checklist | Area | Action | Status | | -------------- | ----------------------------------------------- | ----------------------------------------- | | **Cost** | Set max_total_cost_usd on all agents | | | **Cost** | Set max_steps and max_tool_calls | | | **Policy** | Add deny rules for admin/destructive tools | | | **Policy** | Add request_approval for mutating operations | | | **Tools** | Enable sanitize_tool_output=True | | | **Tools** | Set tool_output_max_chars | | | **Tools** | Use sandbox profiles for code execution | | | **A2A/MCP** | Configure auth providers with valid tokens | | | **A2A/MCP** | Set per-caller agent access lists | | | **A2A/MCP** | Enable rate limiting | | | **Secrets** | Store API keys in environment variables | | | **Secrets** | Use secret scope isolation per tool call | | | **Monitoring** | Configure telemetry exporter (OTEL) | | | **Monitoring** | Set up alerts for error rate and cost anomalies | | Secret isolation AFK recommends isolating secrets at the environment level. Use separate environment scopes and the runner's ToolContext.metadata to control which credentials are available to each tool: Threat model overview | Threat | Mitigation | | ----------------------- | ------------------------------------------- | | **Prompt injection** | Output sanitization, input validation | | **Runaway agents** | Cost limits, step limits, wall time | | **Tool abuse** | Policy engine, sandbox profiles | | **Unauthorized access** | A2A/MCP auth, per-caller authorization | | **Secret leakage** | Secret scope isolation, output sanitization | | **Cost explosion** | max_total_cost_usd, circuit breakers | Next steps How errors flow through the system. Production playbook and anti-patterns.", "token_count": 288} +{"id": "doc_1e46cbc6bfae", "path": "docs/library/snippets/01_minimal_chat_agent.mdx", "url": "/library/snippets/01_minimal_chat_agent", "title": "01: Minimal Chat Agent", "description": "Smallest synchronous AFK agent run.", "headings": ["Line-by-line explanation", "What AgentResult contains", "Expected behavior"], "content_sha256": "1232b15cabfce1a8b2463d495eb4dca40efebc440a22e456b95cb6e2a4af8225", "content": "This is the simplest possible AFK agent. It demonstrates the three core concepts you need to get started: defining an Agent with a model and instructions, creating a Runner to execute it, and reading the result from final_text. If you are new to AFK, start here. Every other example builds on this foundation. Line-by-line explanation **Agent(...)** defines the agent's identity and behavior. The name is used for telemetry and logging. The model specifies which LLM to use. The instructions become the system prompt that guides the model's behavior. **Runner()** creates the execution engine. With no arguments, it uses in-memory defaults: headless interaction mode, no telemetry sink, and no policy engine. This is the fastest way to get started during development. **runner.run_sync(...)** executes the agent synchronously, blocking until the run completes. Under the hood, this creates an async event loop, runs the agent through the full lifecycle (LLM call, optional tool execution, optional subagent delegation), and returns the terminal AgentResult. The user_message is the initial prompt sent to the model. **result.final_text** contains the model's final text response. This is the primary output field on AgentResult. Always use final_text (not output_text) to access the agent's response. What AgentResult contains The AgentResult dataclass returned by run_sync includes: | Field | Type | Description | | --- | --- | --- | | final_text | str | The agent's final text response. | | state | str | Terminal state: \"completed\", \"failed\", \"cancelled\", or \"degraded\". | | run_id | str | Unique identifier for this run. | | thread_id | str | Thread identifier for memory continuity across runs. | | tool_executions | list | Records of all tool calls made during the run. | | subagent_executions | list | Records of all subagent invocations. | | usage | UsageAggregate | Token usage and cost estimates across all LLM calls. | Expected behavior When you run this example, the runner makes a single LLM call to gpt-4.1-mini with the system instructions and user message. Since no tools are registered, the model responds with text only. The run completes in one step with state=\"completed\", and final_text contains a concise definition of error budgets in SRE. No network calls, API keys, or external services are required beyond access to the specified LLM provider (configured via environment variables like OPENAI_API_KEY).", "token_count": 251} +{"id": "doc_d6ad61a79371", "path": "docs/library/snippets/02_policy_with_hitl.mdx", "url": "/library/snippets/02_policy_with_hitl", "title": "02: Policy with Human Interaction", "description": "Route sensitive actions through policy and human approval.", "headings": ["Basic example", "Define a policy that gates destructive operations", "Headless mode: approval requests are auto-resolved using approval_fallback", "In headless mode with approval_fallback=\"deny\", the destructive action is blocked.", "Interactive mode with an InteractionProvider", "In-memory provider for testing (simulates human approval)", "In a real application, a separate process or UI would call:", "provider.resolve_approval(request_id, ApprovalDecision(kind=\"allow\"))", "Headless vs interactive modes", "How the policy flow works", "Policy decision actions"], "content_sha256": "e2c2e11ab5f307f4f1a3008e902cbd49a39845c3d0f0c1a792cf3068c1581a5f", "content": "Human-in-the-loop (HITL) is the pattern where the agent pauses execution to request approval or input from a human operator before proceeding with a sensitive action. This is critical for any agent that can take destructive or irreversible actions -- deleting data, modifying production systems, sending communications, or spending money. AFK implements HITL through two components: a PolicyEngine that decides which actions require human intervention, and an InteractionProvider that routes the approval request to a human and returns their decision. Basic example Interactive mode with an InteractionProvider In production, you typically want a real human to review approval requests. Use interaction_mode=\"interactive\" with a custom InteractionProvider: Headless vs interactive modes AFK supports three interaction modes, configured via RunnerConfig.interaction_mode: | Mode | Behavior | When to Use | | --- | --- | --- | | \"headless\" | Approval requests are auto-resolved using approval_fallback (default: \"deny\"). No human is involved. | CI/CD pipelines, batch processing, testing, automated workflows where no human is available. | | \"interactive\" | Approval requests are routed to the configured InteractionProvider. The runner pauses until a decision is returned or approval_timeout_s expires. | Production applications with human operators, chat UIs with approval buttons, Slack-based approval workflows. | | \"external\" | Similar to interactive, but designed for use in external orchestration systems where the approval mechanism is managed outside AFK. | Enterprise systems with external approval platforms. | How the policy flow works 1. The agent calls a tool (e.g., drop_table). 2. Before executing, the runner sends a PolicyEvent to the PolicyEngine. 3. The engine evaluates all rules. If a rule matches, it returns a PolicyDecision with the configured action. 4. If the action is request_approval: - In **headless** mode: the runner auto-resolves using approval_fallback. - In **interactive** mode: the runner creates an ApprovalRequest and sends it to the InteractionProvider. Execution pauses until the provider returns an ApprovalDecision. 5. If approved (kind=\"allow\"): the tool executes normally. 6. If denied (kind=\"deny\"): the tool execution is skipped and the model receives a denial message as the tool result. 7. A policy_decision event is emitted in the run event stream for audit purposes. Policy decision actions | Action | Effect | | --- | --- | | \"allow\" | Proceed with execution. No human interaction needed. | | \"deny\" | Block execution immediately. The denial reason is reported to the model. | | \"request_approval\" | Pause and request human approval through the InteractionProvider. | | \"request_user_input\" | Pause and request freeform text input from a human operator. |", "token_count": 262} +{"id": "doc_a2e84472f1f9", "path": "docs/library/snippets/03_subagents_with_router.mdx", "url": "/library/snippets/03_subagents_with_router", "title": "03: Subagents with Router", "description": "Delegate workload to specialist subagents and merge outputs.", "headings": ["Delegation flow", "Example", "Define specialist subagents", "Define the coordinator agent", "How the coordinator pattern works", "What subagent_executions contains", "Subagent failure handling"], "content_sha256": "40c49cfa7a9868920008a50344415cbed7990803b125758a43cb66376458cff8", "content": "When a task is too complex for a single agent, AFK supports delegating subtasks to specialist subagents. The coordinator (or \"lead\") agent decides what to delegate, and the runner handles dispatching work to subagents, collecting their results, and feeding those results back to the coordinator for synthesis. This pattern is useful for incident response, research workflows, content pipelines, and any scenario where different aspects of a task require distinct expertise or instructions. Delegation flow Example How the coordinator pattern works 1. The **lead agent** receives the user message and decides how to delegate. It can invoke subagents through tool-like calls that the runner intercepts. 2. The **runner** dispatches each subagent invocation as a separate run. Subagents execute independently with their own instructions and model configuration. The runner manages concurrency, timeout, and failure handling for each subagent. 3. **Subagent results** are returned to the lead agent as execution records. Each record contains the subagent's output_text and optional error information. 4. The **lead agent** receives all subagent outputs and synthesizes them into a unified response. This final synthesis step is what produces the coordinator's final_text. What subagent_executions contains The AgentResult returned by the lead agent includes a subagent_executions list. Each entry is a SubagentExecutionRecord with: | Field | Type | Description | | --- | --- | --- | | subagent_name | str | Name of the subagent that was invoked. | | success | bool | Whether the subagent completed successfully. | | output_text | str or None | The subagent's response text, if it completed. | | latency_ms | float | Wall-clock execution time in milliseconds. | | error | str or None | Error message if the subagent failed. | You can inspect these records to understand what each subagent contributed: Subagent failure handling By default, subagent failure policy is continue. You can configure stricter or more resilient behavior using FailSafeConfig: With subagent_failure_policy=\"retry_then_degrade\", the lead agent receives error information for failed subagents alongside successful results and can produce a best-effort synthesis.", "token_count": 215} +{"id": "doc_07174a8633ac", "path": "docs/library/snippets/04_resume_and_compact.mdx", "url": "/library/snippets/04_resume_and_compact", "title": "04: Resume and Compact", "description": "Resume interrupted runs from their last checkpoint and compact retained thread memory to control storage growth.", "headings": ["What this snippet demonstrates", "Resuming an interrupted run", "How resume works internally", "Resume method signature", "Compacting thread memory", "How compaction works", "When to compact", "Error handling", "What to read next"], "content_sha256": "1926eddc956569f4ccf828f2605bfd5848af1b4ba75eba3d35876e2235c15491", "content": "What this snippet demonstrates Agent runs can be interrupted by timeouts, cancellations, infrastructure failures, or intentional pauses (such as waiting for human approval). When a run is interrupted, the runner persists a checkpoint containing the run's state at the point of interruption. The resume() method picks up from that checkpoint, restoring the conversation history, tool execution records, and step counter so the agent continues where it left off rather than starting from scratch. Over time, long-running threads accumulate checkpoint records, event logs, and state entries. The compact_thread() method prunes old records according to retention policies, keeping storage bounded without losing the data needed for active runs. Resuming an interrupted run How resume works internally The runner follows this sequence when resume() is called: 1. **Checkpoint lookup** -- The runner queries the memory store for the latest checkpoint matching the given run_id and thread_id. If no checkpoint exists, it raises AgentCheckpointCorruptionError. 2. **Terminal check** -- If the checkpoint already contains a terminal result (the run completed before the resume was requested), the runner returns that result immediately without re-executing. 3. **Snapshot restoration** -- The runner loads the runtime snapshot from the checkpoint, which includes the conversation message history, step counter, tool execution records, and any pending subagent state. 4. **Continued execution** -- The runner calls run_handle() internally with the restored snapshot, continuing the step loop from where it was interrupted. Resume method signature | Parameter | Type | Description | | --- | --- | --- | | agent | BaseAgent | The agent definition used for continued execution. Must match the agent that started the original run. | | run_id | str | The unique run identifier from the interrupted run. Found on result.run_id. | | thread_id | str | The thread identifier from the interrupted run. Found on result.thread_id. | | context | dict or None | Optional context overlay. Merged with the original run context. | Compacting thread memory How compaction works Compaction operates on two dimensions of stored data: - **Event retention** -- Controlled by RetentionPolicy. Removes event records older than max_age_ms. Events are the raw telemetry log entries (LLM calls, tool executions, state transitions) that accumulate over the lifetime of a thread. - **State retention** -- Controlled by StateRetentionPolicy. Removes state entries that exceed max_entries, keeping only the most recent ones. State entries include checkpoint snapshots, conversation summaries, and key-value metadata. Both policies are optional. If you omit a policy, that dimension is not compacted. The method returns a MemoryCompactionResult with counts of removed records so you can log or alert on compaction activity. When to compact - **After long conversations** -- Threads with hundreds of turns accumulate large checkpoint histories. Compact after the conversation ends or reaches a natural break point. - **On a schedule** -- Run compaction as a background task (e.g., hourly or daily) for threads that are still active but have grown large. - **Before resume** -- If you know a thread has extensive history, compacting before resume reduces the data the runner needs to load. Error handling What to read next - Memory -- Full memory architecture, checkpoint schema, and retention policies. - Core Runner -- Step loop lifecycle, state machine, and all runner API methods. - Checkpoint Schema -- Exact structure of checkpoint records stored in memory.", "token_count": 372} +{"id": "doc_3f60413b3a06", "path": "docs/library/snippets/05_direct_llm_structured_output.mdx", "url": "/library/snippets/05_direct_llm_structured_output", "title": "05: Direct LLM Structured Output", "description": "Use afk.llms with schema-validated responses.", "headings": ["Example", "Define the output schema as a Pydantic model", "Build an LLM client using the fluent builder", "Make a structured request", "The builder pattern", "Structured output with Pydantic", "When to use LLMBuilder vs Runner"], "content_sha256": "6f54fe64ae8f7ca3c497962bf8febe155784c84d094d323bdfb99581cc086743", "content": "Not every use case needs the full agent loop. Sometimes you want to call an LLM directly with a specific prompt and get back a structured, schema-validated response. AFK's LLMBuilder provides a fluent API for constructing LLM clients that can return Pydantic-validated objects directly, without the overhead of the agent run lifecycle. Use this pattern for classification, extraction, summarization, and any scenario where you want a single LLM call with a guaranteed output schema. Example The builder pattern LLMBuilder uses a fluent (method-chaining) API to construct an LLM client with the exact configuration you need: Each method returns the builder instance, so calls can be chained. The .build() call at the end constructs the final LLMClient with all specified settings. Available builder methods: | Method | Purpose | | --- | --- | | .provider(name) | Set the LLM provider (\"openai\", \"litellm\", \"anthropic_agent\"). | | .model(name) | Set the model identifier. | | .profile(name) | Apply a named configuration profile (\"production\", \"development\", etc.). | | .settings(settings) | Replace the loaded LLMSettings. | | .with_middlewares(stack) | Attach chat, stream, or embedding middleware. | | .with_observers(observers) | Attach LLM lifecycle observers. | | .with_cache(cache_backend) | Attach a cache backend instance or registered backend id. | | .with_router(router) | Attach a router instance or registered router id. | | .build() | Construct and return the LLMClient. | Sampling controls are request fields, not builder methods. Set them on LLMRequest, for example LLMRequest(..., temperature=0.0, max_tokens=1000). Structured output with Pydantic When you pass response_model=YourModel to client.chat(), the client instructs the LLM to return output that conforms to the model's JSON schema. The response is parsed and validated against the Pydantic model: - If the LLM returns valid structured output, resp.structured_response contains the parsed dictionary and resp.text contains the raw response. - If the LLM returns output that does not match the schema, a LLMInvalidResponseError is raised. This is powered by the LLM provider's native structured output support (e.g., OpenAI's response_format parameter) when available, with a fallback to prompt-based JSON extraction. When to use LLMBuilder vs Runner | Use Case | Approach | | --- | --- | | Single LLM call, no tools, no memory | LLMBuilder -- simpler, faster, no lifecycle overhead. | | Structured extraction or classification | LLMBuilder with response_model. | | Multi-turn conversation with tools | Runner -- provides the full agent loop with tool execution, policy, and memory. | | Subagent delegation | Runner -- only the runner supports subagent dispatch. | | Event streaming to a UI | Runner with run_stream(). | | Eval-driven development | Runner -- evals require the full AgentResult lifecycle. | Use LLMBuilder when you want precision and control over a single LLM interaction. Use Runner when you need the full agentic lifecycle.", "token_count": 305} +{"id": "doc_43ad71a4ad0c", "path": "docs/library/snippets/06_tool_registry_security.mdx", "url": "/library/snippets/06_tool_registry_security", "title": "06: Tool Registry Security", "description": "Safe tool registration and guardrail practices.", "headings": ["Read-only vs mutating tools", "--- Read-only tool: safe, broadly permitted ---", "--- Mutating tool: destructive, requires policy gate ---", "Policy gate setup", "Define policy rules that distinguish read vs write operations", "In headless mode, the delete is auto-denied. The model sees the denial and responds accordingly.", "Sandbox profiles for filesystem tools", "Scoping destructive tools"], "content_sha256": "a1ef90a4e46b3a7e305f3a953816f7601fc4a0b6fb3d32a17d5f5faa7ba3a2e3", "content": "Tools are the primary way agents interact with external systems. A tool that reads data is fundamentally different from a tool that deletes resources -- and your security model should reflect this. AFK provides multiple layers of defense for tool security: scoped tool definitions with typed arguments, sandbox profiles that restrict execution capabilities, and policy gates that require human approval for destructive operations. This page demonstrates how to register tools safely, distinguish between read-only and mutating tools, and configure policy gates to protect against unintended destructive actions. Read-only vs mutating tools The most important security distinction is between tools that observe (read-only) and tools that act (mutating). Read-only tools are generally safe to allow broadly. Mutating tools should be tightly scoped and policy-gated. Notice the differences: - The read-only tool (get_resource) has a description that explicitly says \"Read-only.\" This signals to both the model and human reviewers that the tool is safe. - The mutating tool (delete_resource) has a description warning about irreversibility. This helps the model understand the severity, and helps policy rules identify destructive operations. Policy gate setup Use a PolicyEngine to require human approval before any mutating tool executes: Sandbox profiles for filesystem tools For tools that interact with the filesystem or execute commands, use SandboxProfile to restrict their capabilities: Scoping destructive tools Follow these principles when registering destructive tools: 1. **Name them clearly.** Use verb prefixes that signal intent: delete_, remove_, drop_, update_, modify_. This makes policy rules easy to write and audit. 2. **Type all arguments.** Use Pydantic models for argument validation. Never accept freeform dict arguments for mutating operations. 3. **Describe irreversibility.** Include \"irreversible\", \"destructive\", or \"permanent\" in the tool description. This helps both the model and policy reviewers understand the risk. 4. **Gate with policy rules.** Every mutating tool should have a corresponding policy rule. Use request_approval for interactive environments and deny as the fallback in headless mode. 5. **Set cost limits.** Use FailSafeConfig.max_tool_calls and max_total_cost_usd to prevent runaway tool usage, especially when the agent has access to APIs with per-call costs. 6. **Audit everything.** Policy decisions are emitted as policy_decision events in the run event stream. Persist these events for compliance and debugging.", "token_count": 261} +{"id": "doc_c346f13ce597", "path": "docs/library/snippets/07_tool_hooks_and_middleware.mdx", "url": "/library/snippets/07_tool_hooks_and_middleware", "title": "07: Tool Hooks and Middleware", "description": "Add pre-execution validation, post-execution transformation, and cross-cutting middleware to tools and the LLM client pipeline.", "headings": ["What this snippet demonstrates", "Tool pre-hooks", "Pre-hook argument model matches the main tool's argument shape", "Pre-hook: sanitize and normalize the query before the tool runs", "Main tool with the pre-hook attached", "Pre-hook execution flow", "Tool post-hooks", "Tool-level middleware", "Attaching middleware to a tool", "Registry-level middleware", "LLM client middleware", "Chat middleware: intercepts non-streaming chat requests", "Build client with middleware", "LLM middleware protocols", "Built-in LLM middleware", "Timeout middleware", "Configure timeouts", "Add to middleware stack", "Build client", "When to use each layer", "What to read next"], "content_sha256": "cacc5663484a5478e3025773c47ac9ffe5ed840192da35d5ac11d46d5ff94c5d", "content": "What this snippet demonstrates AFK provides two distinct hook/middleware systems that operate at different layers: 1. **Tool hooks and middleware** -- Pre-hooks, post-hooks, and middleware that wrap individual tool executions. These use Pydantic models for typed arguments and run inside the tool execution pipeline. 2. **LLM middleware** -- Middleware that wraps LLM client operations (chat, stream, embed). These intercept requests and responses at the provider transport layer. Both systems follow the same pattern: define a callable, wire it into the pipeline, and the runner executes it at the appropriate point in the lifecycle. Tool pre-hooks A pre-hook runs before the main tool function executes. It receives the tool's arguments (validated against its own Pydantic model) and returns a dictionary of transformed arguments that the main tool will receive. Use pre-hooks for input sanitization, enrichment, or validation that should happen before execution. Pre-hook execution flow The pre-hook receives validated arguments and must return a dictionary compatible with the main tool's args_model. If the returned dictionary fails validation against the tool's model, the tool call fails with a ToolValidationError. Tool post-hooks A post-hook runs after the main tool function completes. It receives the tool output and can transform or annotate the result before it is returned to the LLM. Use post-hooks for output sanitization, logging, or enrichment. AFK passes post-hooks a payload dictionary with the shape {\"output\": , \"tool_name\": \" \"}. The post-hook must return a dictionary with the same shape. Tool-level middleware Tool-level middleware wraps around the entire tool execution, including pre-hooks and post-hooks. Middleware receives a call_next function and the tool arguments, and can modify behavior before, after, or around execution. Attaching middleware to a tool Middleware executes in the order listed. The first middleware in the list is the outermost wrapper. Each middleware calls call_next to pass control to the next middleware (or the actual tool function if it is the last one). Registry-level middleware Registry-level middleware applies to every tool in a ToolRegistry, not just a single tool. Use this for cross-cutting concerns like audit logging, rate limiting, or policy enforcement that should apply uniformly. LLM client middleware LLM middleware operates at the provider transport layer, intercepting requests to and responses from the LLM API. AFK defines three middleware protocols for the three LLM operations: LLM middleware protocols | Protocol | Operation | Signature | | --- | --- | --- | | LLMChatMiddleware | Non-streaming chat | async (call_next, req: LLMRequest) -> LLMResponse | | LLMEmbedMiddleware | Embeddings | async (call_next, req: EmbeddingRequest) -> EmbeddingResponse | | LLMStreamMiddleware | Streaming chat | (call_next, req: LLMRequest) -> AsyncIterator[LLMStreamEvent] | Each middleware receives call_next (the next middleware or transport in the chain) and the request object. It can modify the request before calling call_next, modify the response after, or short-circuit entirely by returning a response without calling call_next. Built-in LLM middleware AFK ships with pre-built middleware for common patterns: Timeout middleware Apply per-request timeouts to prevent runaway calls: The timeout middleware respects TimeoutPolicy from the request if provided: When to use each layer | Layer | Scope | Use for | | --- | --- | --- | | **Tool pre-hook** | Single tool, before execution | Input sanitization, argument enrichment, validation | | **Tool post-hook** | Single tool, after execution | Output sanitization, redaction, annotation | | **Tool middleware** | Single tool, wraps execution | Timing, retries, caching, error handling | | **Registry middleware** | All tools in registry | Audit logging, rate limiting, policy enforcement | | **LLM middleware** | All LLM calls through client | Request metadata, response logging, tracing | What to read next - Tools -- Full tool system architecture, the 6-step execution pipeline, and design guidelines. - Tool Call Lifecycle -- Detailed lifecycle of a tool call from LLM proposal to result delivery. - LLMs Overview -- Builder workflow, runtime profiles, and provider selection.", "token_count": 432} +{"id": "doc_464a2b4a8d35", "path": "docs/library/snippets/08_prebuilt_runtime_tools.mdx", "url": "/library/snippets/08_prebuilt_runtime_tools", "title": "08: Prebuilt Runtime Tools", "description": "Use AFK's built-in filesystem tools with directory-scoped security constraints and compose them with policy checks.", "headings": ["What this snippet demonstrates", "Building runtime tools", "Create filesystem tools scoped to a specific directory", "Available prebuilt tools", "list_directory", "read_file", "Security: directory traversal prevention", "Composing with policy checks", "Define a policy that requires approval for reading certain files", "Composing with custom tools", "Combine prebuilt + custom tools", "Command allowlists and sandbox profiles", "Create a read-only sandbox that restricts what operations tools can perform", "What to read next"], "content_sha256": "0b75873372d50bad1c2ec5d4bcd1e04445f683e855b896766f09d4bf86609937", "content": "What this snippet demonstrates AFK ships prebuilt tools for common runtime operations like listing directories and reading files. These tools are designed with security-first defaults: every tool is scoped to an explicit root directory that prevents directory traversal attacks. This snippet shows how to create, configure, and compose prebuilt tools with agents and policy guards. Building runtime tools The build_runtime_tools() factory creates a set of filesystem tools bound to a specific root directory. All path operations within these tools are resolved against this root, and any attempt to access files outside it raises a FileAccessError. Available prebuilt tools The build_runtime_tools() factory produces two tools: list_directory Lists entries in a directory under the configured root. Returns entry names, paths, and type flags (file or directory). | Parameter | Type | Default | Description | | ------------- | ----- | ------- | ----------------------------------------------------------------- | | path | str | \".\" | Relative path to list, resolved against the root directory. | | max_entries | int | 200 | Maximum entries to return (1--5000). Prevents unbounded listings. | **Returns:** A dictionary with root, path, and entries (list of {name, path, is_dir, is_file}). read_file Reads the contents of a file under the configured root, with configurable truncation to prevent excessive token consumption. | Parameter | Type | Default | Description | | ----------- | ----- | ---------- | -------------------------------------------------------------------------------- | | path | str | (required) | Relative path to the file, resolved against the root directory. | | max_chars | int | 20_000 | Maximum characters to read (1--500,000). Content is truncated beyond this limit. | **Returns:** A dictionary with root, path, content, and truncated (boolean indicating whether content was truncated). Security: directory traversal prevention Every path operation is validated with an internal containment check that uses Python's Path.relative_to() to verify that the resolved path stays within the configured root. This prevents attacks like: If a path escapes the root, the tool raises FileAccessError immediately, before any file I/O occurs. Composing with policy checks For additional security, pair runtime tools with a policy engine that gates specific operations on approval: Composing with custom tools You can combine prebuilt tools with your own custom tools in a single agent: Command allowlists and sandbox profiles For production environments, restrict tool capabilities further using sandbox profiles: This ensures that even if the LLM attempts to use tools for unauthorized operations, the sandbox profile blocks execution before any I/O occurs. What to read next - Tools -- Full tool system architecture, including the @tool decorator, ToolResult, and execution pipeline. - Snippet 06: Tool Registry Security -- Security scoping, policy gates, and sandbox profiles in detail. - Security Model -- Threat model, defense layers, and RunnerConfig security fields.", "token_count": 294} +{"id": "doc_64090a20f830", "path": "docs/library/snippets/09_system_prompt_loader.mdx", "url": "/library/snippets/09_system_prompt_loader", "title": "09: System Prompt Loader", "description": "Resolve agent system prompts from a file hierarchy with deterministic precedence, Jinja templating, and stat-based caching.", "headings": ["What this snippet demonstrates", "Resolution precedence", "Basic usage", "Option 1: Inline instructions (highest priority)", "Option 2: Explicit instruction file", "Option 3: Auto-detected file (uses agent name)", "Loads .agents/prompt/CHAT_AGENT.md automatically", "Name-to-filename conversion", "Prompts directory resolution", "Explicit", "Environment variable", "export AFK_AGENT_PROMPTS_DIR=/opt/prompts", "Default: .agents/prompt/", "Jinja2 templating", "Template context variables", "Caching and hot-reload", "Security: path containment", "This would raise PromptAccessError:", "What to read next"], "content_sha256": "a5dcded5d70453d7ede1e44f292d953ee51243eec18fc5b383e2a4231e130fee", "content": "What this snippet demonstrates AFK agents need system prompts (instructions) that tell the LLM how to behave. Rather than hardcoding instructions as inline strings, AFK provides a file-based prompt resolution system that loads prompts from a directory hierarchy. This keeps prompts version-controlled, editable by non-developers, and reusable across agents. The prompt loader resolves instructions through a deterministic precedence chain, supports Jinja2 templating for dynamic prompts, and caches compiled templates using stat-based invalidation for hot-reload during development. Resolution precedence The prompt system resolves agent instructions through this priority chain: 1. **Inline instructions** -- If the agent has a non-empty instructions string, it is used directly. No file loading occurs. 2. **Explicit instruction_file** -- If set, the file is loaded from the configured prompts_dir. The path must resolve to a file inside the prompts root (no directory traversal). 3. **Auto-detected file** -- If neither is set, the agent's name is converted to UPPER_SNAKE_CASE.md and loaded from prompts_dir. Basic usage Name-to-filename conversion The auto-detection algorithm converts the agent name to a filename using these rules: | Agent Name | Derived Filename | Rule Applied | | --- | --- | --- | | ChatAgent | CHAT_AGENT.md | CamelCase split on boundaries | | chatagent | CHAT_AGENT.md | Lowercase agent suffix detected and split | | research-assistant | RESEARCH_ASSISTANT.md | Hyphens replaced with underscores | | QA Bot v2 | QA_BOT_V2.md | Spaces and non-alphanumeric chars become underscores | The conversion is handled by derive_auto_prompt_filename() internally. It splits camelCase boundaries, normalizes non-alphanumeric characters to underscores, collapses consecutive underscores, and uppercases the result. Prompts directory resolution The prompts directory is resolved through its own priority chain: 1. Explicit prompts_dir argument on the Agent constructor. 2. AFK_AGENT_PROMPTS_DIR environment variable. 3. Default: .agents/prompt relative to the current working directory. Jinja2 templating Prompt files support Jinja2 template syntax. When the runner resolves a prompt, it renders the template with a context dictionary that includes agent metadata and any custom context passed to the run. **File: .agents/prompt/SUPPORT_AGENT.md** **Agent code:** Template context variables The following variables are available in every prompt template: | Variable | Type | Description | | --- | --- | --- | | agent_name | str | The agent's name field. | | agent_class | str | The Python class name of the agent. | | context | dict | The full context dictionary passed to the run. | | ctx | dict | Alias for context (shorthand). | Any keys in the context dictionary that are not reserved names (context, ctx, agent_name, agent_class) are also available as top-level template variables. So {{ company_name }} works as a shorthand for {{ ctx.company_name }}. Caching and hot-reload The prompt system uses a process-wide PromptStore singleton that caches at three levels: 1. **File cache** -- Keyed by resolved file path. Uses stat() metadata (mtime, size, inode) as the cache signature. If the file changes on disk, the cache entry is invalidated automatically. 2. **Text pool** -- Deduplicates prompt text by SHA-256 hash. If multiple agents use the same prompt content (even from different files), only one copy is stored in memory. 3. **Template cache** -- Compiled Jinja2 templates are cached by content hash. Re-rendering with different context variables reuses the compiled template. This means that during development, you can edit prompt files and they will be picked up on the next run without restarting the process. In production, the stat-based check is a single os.stat() call per prompt resolution, which is negligible overhead. Security: path containment The prompt loader enforces strict path containment. The resolved prompt file path must be inside the configured prompts_dir. If an instruction_file path resolves outside the prompts root (via ../ traversal or an absolute path pointing elsewhere), the loader raises PromptAccessError immediately. What to read next - System Prompts -- Full system prompt architecture, resolution pipeline, and design guidelines. - Agents -- Agent model, configuration fields, and composition patterns. - Security Model -- Threat model and defense layers including prompt injection considerations.", "token_count": 448} +{"id": "doc_7b26cbd9a7ae", "path": "docs/library/snippets/10_streaming_chat_with_memory.mdx", "url": "/library/snippets/10_streaming_chat_with_memory", "title": "10: Streaming Chat with Memory", "description": "Combine real-time streaming with thread-based memory for multi-turn chat UIs.", "headings": ["What this snippet demonstrates", "Full example", "Key patterns", "Thread ID connects turns", "These two calls share memory", "Access the result after streaming", "Cancel mid-stream", "The run transitions to \"cancelled\" state", "What to read next"], "content_sha256": "747e1945af154333e226917cadea057d5fc51e87933b13672f562d0190008ae2", "content": "What this snippet demonstrates Most chat applications need two things simultaneously: **real-time streaming** (so users see text as it's generated) and **memory continuity** (so the agent remembers previous turns). This snippet shows how to combine run_stream() with thread_id to build a multi-turn streaming chat handler. Full example Key patterns Thread ID connects turns Pass the same thread_id across run_stream() calls to maintain conversation context: Access the result after streaming The handle.result is available after the stream completes: Cancel mid-stream If the user navigates away or clicks \"stop\": What to read next - Streaming \u2014 Full event reference and stream control API. - Memory \u2014 Thread persistence, compaction, and backend configuration. - Snippet 04: Resume + Compact \u2014 Checkpoint-based resumption and memory management.", "token_count": 92} +{"id": "doc_fdf48d427fc0", "path": "docs/library/snippets/11_cost_monitoring.mdx", "url": "/library/snippets/11_cost_monitoring", "title": "11: Cost Monitoring", "description": "Track and control agent costs using FailSafeConfig budgets and telemetry events.", "headings": ["What this snippet demonstrates", "Setting cost budgets", "Monitoring cost from results", "Access usage statistics", "Real-time cost monitoring via streaming", "Cost-aware batch processing", "Operating recommendations", "What to read next"], "content_sha256": "f2d31922b3ae2362b1bc68b3e2c53fe59d0b80c0806fa5cfe522b204e0410131", "content": "What this snippet demonstrates Runaway agent loops are the most common source of unexpected API costs. AFK provides two defense layers: **cost budgets** that kill runs when spending exceeds a threshold, and **telemetry events** that let you observe cost in real time. This snippet shows how to configure both. Setting cost budgets The simplest defense is a hard cost ceiling on every agent: When the estimated cost exceeds max_total_cost_usd, the runner terminates the run with a degraded state and returns the best partial result. Monitoring cost from results Every AgentResult includes token counts and cost estimates: Real-time cost monitoring via streaming For long-running agents, monitor cost during execution: Cost-aware batch processing When running multiple agents in a batch, track cumulative cost: Operating recommendations 1. **Always set max_total_cost_usd** \u2014 even generous limits prevent runaway costs 2. **Layer defenses** \u2014 combine cost limits with max_llm_calls, max_steps, and max_wall_time_s 3. **Use telemetry for dashboards** \u2014 export metrics to monitor cost trends over time 4. **Set per-item budgets in batches** \u2014 prevent one expensive item from consuming the entire budget 5. **Choose models by task** \u2014 use smaller models for routine work and reserve larger models for requests that need them What to read next - Observability \u2014 Telemetry pipeline for metrics and dashboards. - Failure Policy Matrix \u2014 How cost limit breaches flow through the system. - Configuration Reference \u2014 Full FailSafeConfig field reference.", "token_count": 171} +{"id": "doc_40e30666bfa8", "path": "docs/library/snippets/12_mcp_client_integration.mdx", "url": "/library/snippets/12_mcp_client_integration", "title": "12: MCP Client Integration", "description": "Discover and use tools from external MCP servers in your agents.", "headings": ["What this snippet demonstrates", "Consuming MCP tools", "Connect, discover, and attach", "Using the Agent's built-in MCP support", "The agent connects to MCP servers automatically during startup", "Mixing local and MCP tools", "Security with MCP tools", "What to read next"], "content_sha256": "5b44bbbb025f2b01a553bacf0e510a09070c771c3fb99d91656df5ad80fd03df", "content": "What this snippet demonstrates AFK agents can consume tools from external MCP (Model Context Protocol) servers just like local tools. This snippet shows how to connect to an MCP server, discover available tools, and attach them to an agent \u2014 all with the same validation, policy gates, and telemetry as local tools. Consuming MCP tools Connect, discover, and attach Using the Agent's built-in MCP support For simpler setups, pass MCP server refs directly to the agent: Mixing local and MCP tools Combine your own tools with external MCP tools: Security with MCP tools Apply policy rules to MCP-sourced tools just like local tools: **MCP tools are transparent.** Once attached to an agent, they go through the same validation, policy gates, sanitization, and telemetry as local tools. The agent doesn't know whether a tool is local or remote. What to read next - MCP Server \u2014 Expose your own tools via MCP, plus authentication and rate limiting. - Tools \u2014 Full tool system architecture. - Snippet 06: Tool Security \u2014 Policy gates and sandbox profiles.", "token_count": 129} +{"id": "doc_d1f7d83e0824", "path": "docs/library/snippets/13_multi_model_fallback.mdx", "url": "/library/snippets/13_multi_model_fallback", "title": "13: Multi-Model Fallback", "description": "Configure fallback model chains for LLM resilience and cost optimization.", "headings": ["What this snippet demonstrates", "Basic fallback chain", "Cost-optimized fallback", "Start cheap, escalate if quality is insufficient", "Complex tasks get the big model with fallbacks", "Simple task -> cheap model handles it", "Complex task -> powerful model with safety net", "Circuit breaker integration", "Multi-agent with different model tiers", "Cheap model for simple classification", "Inspecting which model was used", "Recommendations", "What to read next"], "content_sha256": "f217ecec21c2dde7c33c194035ef4d3e46de6ffea14b1c9f76e5e8691024b281", "content": "What this snippet demonstrates LLM API calls fail \u2014 rate limits, outages, timeouts. AFK's fallback_model_chain lets you define an ordered list of models to try when the primary model fails. This snippet shows how to configure fallback chains for resilience, cost optimization, and provider diversification. Basic fallback chain When gpt-4.1 fails (timeout, rate limit, outage): 1. AFK retries with the primary model (controlled by retry policy) 2. If retries exhaust, it falls through to gpt-4.1-mini 3. If that also fails, it tries gpt-4.1-nano 4. If all models fail, the llm_failure_policy determines the outcome Cost-optimized fallback Use expensive models only when needed: Circuit breaker integration AFK's built-in circuit breaker works with fallback chains. When a model triggers too many failures, the breaker opens and the system skips straight to the next fallback: Multi-agent with different model tiers Use different model tiers for different specialists: Inspecting which model was used After a run, check the result metadata and usage aggregate: Recommendations | Scenario | Primary Model | Fallback Chain | | ------------------------ | -------------- | ------------------------------- | | **Classification** | gpt-4.1-nano | gpt-4.1-mini | | **General chat** | gpt-4.1-mini | gpt-4.1-nano | | **Complex analysis** | gpt-4.1 | gpt-4.1-mini \u2192 gpt-4.1-nano | | **Code generation** | gpt-4.1 | gpt-4.1-mini | | **Cost-sensitive batch** | gpt-4.1-nano | _(none)_ | What to read next - Configuration Reference \u2014 Full FailSafeConfig fields including circuit breaker settings. - Failure Policy Matrix \u2014 How failures flow through the system. - Snippet 11: Cost Monitoring \u2014 Track and control costs in real time.", "token_count": 183} +{"id": "doc_4462f67b1c03", "path": "docs/library/snippets/14_production_client.mdx", "url": "/library/snippets/14_production_client", "title": "14: Client Timeouts and Redis Pooling", "description": "Configure LLM client timeouts and Redis connection pooling.", "headings": ["What this snippet demonstrates", "Timeout middleware", "Per-request timeout override", "Redis connection pooling", "Using with memory store", "Full example", "Configuration reference", "TimeoutConfig", "PoolConfig", "What to read next"], "content_sha256": "3ad51d99b7437da1d6f78dd70ba9238005ea9818864ec8eeb4a97c4d39790c49", "content": "What this snippet demonstrates This snippet shows how to configure: 1. **Timeout middleware** to bound slow provider calls 2. **Redis connection pooling** for shared cache or memory connections 3. **Shutdown handling** so runners and Redis pools close cleanly Timeout middleware Apply per-request timeouts to prevent runaway LLM calls: Per-request timeout override Redis connection pooling For Redis deployments, use connection pooling instead of creating a new client per request: Using with memory store Full example Configuration reference TimeoutConfig | Parameter | Default | Description | | --- | --- | --- | | default_timeout_s | 30.0 | Default timeout for all operations | | chat_timeout_s | None | Specific timeout for chat requests | | embed_timeout_s | None | Specific timeout for embeddings | | stream_timeout_s | None | Specific timeout for streaming | PoolConfig | Parameter | Default | Description | | --- | --- | --- | | max_connections | 50 | Maximum total connections | | max_idle_connections | 10 | Maximum idle connections | | socket_timeout | 5.0 | Socket read/write timeout | | socket_connect_timeout | 5.0 | Connection establishment timeout | | socket_keepalive | False | Enable TCP keepalive | | health_check_interval_s | 30.0 | Interval for health checks | What to read next - LLM Control & Session -- Retry, caching, and circuit breaker policies - Deployment Guide -- Production deployment with Docker and Kubernetes - Performance Guide -- Optimize latency and throughput", "token_count": 138} +{"id": "doc_109157c0d2d7", "path": "docs/library/streaming.mdx", "url": "/library/streaming", "title": "Streaming", "description": "Real-time event streaming for chat UIs and CLI tools.", "headings": ["Quick example", "Event reference", "Streaming modes", "Stream control", "Lifecycle control variant", "Background tools example", "Error handling", "Next steps"], "content_sha256": "dffb536cc9129c079ef96ab842c4ea0ae01a309ee01259823f89e4c20a130350", "content": "AFK supports real-time streaming via Runner.run_stream(). Instead of waiting for the full response, you receive events as they happen: incremental text, tool lifecycle updates, and terminal status. Quick example Event reference run_stream() emits AgentStreamEvent values with these types: | Event type | When it fires | Key fields | | --- | --- | --- | | text_delta | Incremental text chunks from streaming or fallback path | event.text_delta, event.step | | step_started | New step in the agent loop | event.step, event.state | | tool_started | A tool call is about to execute | event.tool_name, event.tool_call_id, event.step | | tool_completed | A tool call finished | event.tool_name, event.tool_call_id, event.tool_success, event.tool_output, event.tool_error | | tool_deferred | A tool call was accepted but deferred | event.tool_name, event.tool_call_id, event.tool_ticket_id, event.data.resume_hint | | tool_background_resolved | A deferred tool finished successfully | event.tool_name, event.tool_ticket_id, event.tool_output | | tool_background_failed | A deferred tool failed/expired | event.tool_name, event.tool_ticket_id, event.tool_error | | completed | Run finished | event.result (full AgentResult) | | error | Stream/runtime bridge error | event.error | Streaming modes - If the provider supports streaming, AFK forwards model deltas as text_delta. - If the provider is non-streaming, AFK emits chunked fallback text_delta from final text so UI behavior stays consistent. Stream control AgentStreamHandle is read-only. For lifecycle controls (pause(), resume(), cancel(), interrupt()), use run_handle(). Background tools example Error handling error events indicate stream/runtime bridge failures. Tool failures are reported through tool_completed (tool_success=False) and do not necessarily fail the run. Next steps Persist state across streaming runs. Full lifecycle and API reference.", "token_count": 205} +{"id": "doc_2798948c3080", "path": "docs/library/system-prompts.mdx", "url": "/library/system-prompts", "title": "System Prompts", "description": "Configure agent instructions with files, templates, and dynamic context.", "headings": ["Three ways to set instructions", "Precedence chain", "Template variables with Jinja2", "Available template variables", "Prompt from file with templates", "Rules", "Error handling", "Design guidelines", "Next steps"], "content_sha256": "6fcbb09ca2206bb61c328929099af4a832216459bbe1880e90c8c7993766a6b5", "content": "System prompts define what your agent knows and how it behaves. AFK supports three methods \u2014 inline strings, instruction files, and auto-detection \u2014 with Jinja2 templating for dynamic values. Three ways to set instructions Set instructions directly on the Agent. Best for simple, static prompts. Store the prompt in a separate file. Best for long or version-controlled prompts. AFK automatically looks for an instruction file based on the agent's name: The resolution order: 1. prompts/{agent_name}.md 2. prompts/{agent_name}.txt 3. {agent_name}.md 4. {agent_name}.txt Precedence chain When multiple sources are available, AFK uses this order: The first non-empty source wins. If you set both instructions and instruction_file, the inline string takes priority. Template variables with Jinja2 Use {{ variable }} syntax to inject dynamic values into prompts: Available template variables | Source | Variables | Example | | -------------------- | ------------------------------------ | --------------------------------------- | | Agent.context dict | Any key-value pairs you set | {{ company_name }}, {{ user_role }} | | Built-in | agent_name, model_name | {{ agent_name }} | | Runtime | run_id, thread_id (if available) | {{ thread_id }} | Prompt from file with templates Templates work in instruction files too: Error handling | Error | Cause | Resolution | | ----------------------- | ------------------------------------- | -------------------------------------------------------------------------------------------- | | PromptResolutionError | instruction_file path doesn't exist | Check that the file exists under the configured prompts_dir. | | PromptTemplateError | Jinja2 template syntax error | Check for unclosed {{ }} or {% %} blocks. | | PromptTemplateError | Missing template variable | Add the variable to Agent.context or provide a default: {{ var \\| default(\"fallback\") }} | **Keep prompts in version control.** Store instruction files in a prompts/ directory and track changes in git. This gives you prompt history, diffs, and the ability to A/B test prompt versions. Design guidelines - **Inline for prototyping,** files for production. Switch to instruction files when your prompt exceeds ~5 lines. - **Use templates for anything dynamic.** Don't concatenate strings \u2014 use {{ variable }} and set values in context. - **Be specific in instructions.** Tell the agent what to do _and_ what not to do. Include output format expectations. - **Test prompt changes with evals.** A small prompt change can dramatically shift behavior. Run your eval suite after any edit. Next steps Reusable knowledge bundles loaded on demand. Test prompt changes with behavioral assertions.", "token_count": 249} +{"id": "doc_78b1717fafe3", "path": "docs/library/task-queues.mdx", "url": "/library/task-queues", "title": "Task Queues", "description": "Async job processing with execution contracts and dead-letter handling.", "headings": ["Quick start", "Push a task", "A worker consumes queued tasks", "Task lifecycle", "Execution contracts", "Worker setup", "Dead-letter handling", "Check for dead letters", "Retry manually after fixing the issue", "Or discard them", "Error classification", "Queue backends", "Connection pooling", "Create a pooled connection", "Use in queue", "Health check", "Next steps"], "content_sha256": "da3a4f6c098ee01fffa8507b3bd9ee9116cf103123e29dcbcc58b4a1facb5f5e", "content": "Task queues decouple agent work producers from consumers. Push a task, a worker picks it up, and the result is stored \u2014 independently of the caller's lifecycle. Use queues for long-running jobs, batch processing, and reliable retries. Quick start Task lifecycle | State | Meaning | | ------------- | ------------------------------------------- | | queued | Waiting to be picked up by a worker | | running | A worker is executing the task | | completed | Task finished successfully | | failed | Task hit an error (may be retried) | | dead_letter | All retries exhausted \u2014 needs manual review | Execution contracts Every task has a **contract** that defines what kind of work it represents: Standard agent chat. Runs an agent with a user message. Generic job dispatch. Runs a custom handler function. Define your own contract for specialized workloads: Register a handler for the contract on the worker side. Worker setup Dead-letter handling When a task exhausts all retries, it moves to the dead-letter queue (DLQ): Error classification The queue uses error classification to decide whether to retry: | Error type | Retried? | Example | | ------------- | ------------------------------------------------------------------------------------ | ------------------------------------------------ | | **Retryable** | (with backoff) | Network timeout, rate limit, transient LLM error | | **Terminal** | (sent to DLQ) | Invalid arguments, auth failure, missing model | | **Non-fatal** | (task completes with warning) | Telemetry export failure | Queue backends State lives in process memory. No setup required. **Use for:** Development, testing, prototyping. Durable queue with persistence and multi-worker support. Set via environment variables: **Use for:** Production deployments, multi-process workers. Connection pooling For high-throughput production workloads, use RedisConnectionPool to manage connections efficiently: The pool provides: - Configurable max connections (default: 50) - Idle connection management - Automatic health checks - Singleton access via get_redis_pool() Next steps Expose tools via the Model Context Protocol. Monitor queue performance and worker health.", "token_count": 209} +{"id": "doc_618e34441fa7", "path": "docs/library/tested-behaviors.mdx", "url": "/library/tested-behaviors", "title": "Tested Behaviors", "description": "Contract-level guarantees and what test suites validate.", "headings": ["Guaranteed behaviors", "Deterministic delegation ordering", "Queue contract failure classification", "Stream lifecycle correctness", "Telemetry projection stability", "Eval report schema consistency", "Test categories", "Running the test suite", "Delegation and subagent tests", "Queue contract tests", "Eval suite tests", "Tool execution tests", "Observability tests", "LLM runtime tests", "Interpreting test results", "Adding new tests", "CI pipeline guidance", "Example GitHub Actions step"], "content_sha256": "80884b0dc0cbc8a2eda294fc1ca74d89dea1b5e98a80ea48892237808e33f2e2", "content": "AFK's tests describe the behavior contributors should preserve when changing the runtime, tools, memory, queues, LLM adapters, observability, and evals. Treat this page as a map from behavior to the test files that cover it. If you change a public contract, update the matching tests and the docs that explain that contract. A passing test suite is necessary, but it is not a release guarantee by itself; review the affected behavior and run the focused suite for the code you touched. Guaranteed behaviors Deterministic delegation ordering **What is tested:** When a parent agent dispatches work to subagents, the delegation engine executes nodes in a deterministic, topologically sorted order. Parallel batches respect concurrency limits. Edge dependencies (where one subagent's output feeds into another's input) are resolved before the dependent node starts. **Why it matters:** If delegation ordering were non-deterministic, the same agent configuration could produce different results depending on task scheduling. Deterministic ordering means your subagent pipelines are reproducible and debuggable. **Test coverage:** Tests verify that DAG plans produce consistent node execution sequences, that edge-based data flow resolves correctly, and that backpressure limits prevent unbounded queue growth. Queue contract failure classification **What is tested:** The task queue system classifies failures into retryable, terminal, and degraded categories. Retryable failures trigger retry with backoff. Terminal failures stop execution immediately. Degraded states allow partial results. **Why it matters:** Incorrect failure classification can cause infinite retry loops (if terminal failures are marked retryable) or premature task abandonment (if retryable failures are marked terminal). The failure classification contract ensures that each failure type triggers the correct recovery behavior. **Test coverage:** Tests verify that each failure category maps to the correct queue behavior, that retry counts and backoff intervals are respected, and that dead-letter handling works correctly. Stream lifecycle correctness **What is tested:** The streaming API (runner.run_stream()) produces events in the correct lifecycle order: stream starts, text deltas arrive, tool events fire at the right times, and the stream terminates with a completed event containing the final AgentResult. Error conditions produce error stream events rather than unhandled exceptions. **Why it matters:** Streaming consumers (such as chat UIs) depend on events arriving in the correct order. A misplaced completed event before all text deltas have been emitted would cause truncated output. An unhandled exception would crash the consumer. **Test coverage:** Tests verify event ordering, ensure that all text content is captured before the terminal event, and confirm that error conditions produce structured error events. Telemetry projection stability **What is tested:** The telemetry projector produces consistent RunMetrics from the same input data. Field names, types, and computed properties (like avg_llm_latency_ms and success) are stable across versions. **Why it matters:** Downstream dashboards, alerting rules, and eval assertions depend on RunMetrics having a stable schema. If a field is renamed or its type changes, every consumer breaks silently. **Test coverage:** Tests verify that projected metrics match expected values for known input data, that computed properties produce correct results, and that to_dict() serialization is stable. Eval report schema consistency **What is tested:** The eval report serializer (suite_report_payload) produces output with a stable schema_version and consistent field structure. The report includes summary statistics, per-case results, assertion details, budget violations, and projected metrics. **Why it matters:** CI pipelines parse eval reports to make release-gating decisions. If the report schema changes, CI scripts break and deployments may be incorrectly blocked or allowed. **Test coverage:** Tests verify the report envelope structure, confirm that schema_version is set correctly, and validate that all expected fields are present and correctly typed. Test categories | Category | Description | Key Modules Covered | | --- | --- | --- | | Agent delegation | Deterministic DAG execution, subagent routing, backpressure limits. | afk.core.runtime, afk.agents.delegation | | Queue contracts | Failure classification, retry behavior, dead-letter handling. | afk.queues | | Stream lifecycle | Event ordering, text delta capture, terminal event correctness. | afk.core.streaming | | Telemetry projection | Metric stability, computed property correctness, serialization. | afk.observability.projectors, afk.observability.models | | Eval reports | Report schema stability, assertion result structure, budget violations. | afk.evals.reporting, afk.evals.models | | Tool execution | Pydantic validation, timeout enforcement, hook/middleware chains. | afk.tools | | Policy evaluation | Rule matching, decision actions, audit event emission. | afk.agents.policy | | LLM runtime | Provider routing, retry/circuit-breaker behavior, streaming correctness. | afk.llms.runtime | | Security | Sandbox profile enforcement, secret scope isolation, command allowlists. | afk.tools.security | Running the test suite Run all tests from the repository root: Run a specific test category: Run with verbose output for debugging: Interpreting test results - **All relevant tests pass**: The checked behavior still matches the test suite. Review docs and public imports before merging user-visible changes. - **A test fails**: Inspect the assertion before changing code. The test may expose a real contract regression, or the intended contract may have changed and need a deliberate test/doc update. - **A new test is marked as xfail**: Include a reason and keep the scope narrow so known limitations do not hide unrelated regressions. Adding new tests When adding a new feature or fixing a bug, follow this pattern: 1. **Identify the contract.** What behavioral guarantee should your change preserve or introduce? Write this as a plain-English statement (e.g., \"Subagent timeout should produce a SubagentExecutionRecord with success=False\"). 2. **Write the test first.** Create a test in the appropriate tests/ subdirectory that asserts the expected behavior. Use descriptive test names that read as contract statements. 3. **Make the test pass.** Implement the feature or fix. The test should pass without any special-casing or mocking of the behavior under test. 4. **Verify stability.** Run the full suite to confirm your change does not break existing contracts. CI pipeline guidance Most tests run without API keys and use in-memory or mocked providers. Some integration tests cover optional backends such as Redis, SQLite, or Postgres behavior; keep those isolated and skippable when the backing service is unavailable. Recommended CI configuration: Store the JUnit XML report as a CI artifact for trend analysis and failure investigation.", "token_count": 763} +{"id": "doc_0ac01bce8c79", "path": "docs/library/tool-call-lifecycle.mdx", "url": "/library/tool-call-lifecycle", "title": "Tool Call Lifecycle", "description": "Detailed event-level view of one tool invocation.", "headings": ["Event timeline", "Event reference table", "Policy decisions: allow, deny, defer, and request_user_input", "Timing and latency tracking", "How denied tools affect the agent run"], "content_sha256": "9426358483fa8300d96c0f0ec8bc520958e881b7f6827a7fdfe5301576f8f306", "content": "While the Tools System Walkthrough covers the end-to-end pipeline, this page zooms into the event-level lifecycle of a single tool call. Every tool invocation produces a deterministic sequence of events that flow through the run handle. Understanding this sequence is critical for building observability dashboards, audit logs, and human-in-the-loop approval flows. A tool call begins when the runner receives a ToolCall from the LLM response. Before the handler ever runs, the runner evaluates a policy gate that can allow, deny, or defer the call for human approval. The outcome of that gate determines whether the tool executes at all, and the events emitted along the way tell you exactly what happened and why. Event timeline Event reference table Every event is an AgentRunEvent published on the run handle. The type field identifies the event, and the data dict carries event-specific fields. | Event type | When it fires | Key data fields | Description | | --- | --- | --- | --- | | tool_batch_started | After LLM response contains tool calls | tool_call_count | Signals the start of a batch of one or more tool calls from a single LLM turn. | | policy_decision | After policy evaluation for each tool | tool_name, action, reason | Records the policy engine's decision for one tool call. Emitted for allow, deny, defer, and request_user_input actions. | | run_paused | When a tool call requires human approval | tool_name, reason, payload | The run is suspended waiting for external input. Only emitted when policy returns defer or request_approval. | | run_resumed | After human approval or denial is received | approved | The run resumes after the approval decision. | | tool_completed | After each tool call resolves (success or failure) | tool_name, success, output, error, tool_call_id | The terminal event for one tool call. Always emitted regardless of whether the tool ran, was denied, or timed out. | | warning | When non-fatal issues occur | message | Advisory events such as sandbox violations handled under a continue policy. | Policy decisions: allow, deny, defer, and request_user_input The PolicyEngine evaluates a PolicyEvent with event_type=\"tool_before_execute\" for every tool call. The decision drives the rest of the lifecycle: **allow** -- The tool executes immediately. No human interaction required. This is the default when no policy rules match. **deny** -- The tool is blocked. A ToolExecutionRecord with success=False and error=\"Denied by policy\" is recorded. The denied result is fed back to the model as a tool message so it can adjust its behavior. After a denial, the runner consults fail_safe.approval_denial_policy to decide the run-level outcome: - continue (default) -- The run proceeds. The model sees the denial and may try a different approach. - degrade -- The run transitions to a degraded state and terminates with a degradation message. - fail -- The run raises an AgentExecutionError and transitions to failed. **defer / request_approval** -- The runner pauses the run, emits a run_paused event, and waits for the InteractionProvider to deliver an approval decision. If the provider is HeadlessInteractionProvider, the configured approval_fallback (default: deny) is used. The approval has a configurable timeout (approval_timeout_s, default 300s). **request_user_input** -- Similar to defer, but instead of a yes/no approval, the runner asks the user for a value (such as a confirmation code or corrected parameter). The policy's request_payload can specify a target_arg field, and the user's input is injected into the tool arguments before execution. Timing and latency tracking Every tool execution is timed from the moment registry.call() is invoked to the moment it returns. The latency is captured in milliseconds and recorded in two places: 1. **ToolExecutionRecord.latency_ms** -- Available on the AgentResult.tool_executions list after the run completes. 2. **Telemetry histogram** -- The agent.tool_call.latency_ms metric is emitted with attributes tool_name, result (success/error), and source (execute/replay). For batch-level timing, the agent.tool_batch.latency_ms histogram captures the wall-clock duration of the entire batch (all concurrent tool calls), with attributes for call_count and failure_count. Denied tool calls have no execution latency since the handler never runs. How denied tools affect the agent run When a tool is denied, the model still receives feedback. The runner appends a tool message to the conversation with the denial reason: This feedback loop is important: the model sees that its proposed action was rejected and can adapt. In practice, models respond to denials by either rephrasing the request, choosing a different tool, or providing a text-only response. The run-level impact depends on fail_safe.approval_denial_policy: | Policy | Behavior after denial | | --- | --- | | continue | Run proceeds normally. Model sees the denial and may self-correct. | | degrade | Run transitions to degraded state and exits the step loop with a degradation message. | | fail | Run raises AgentExecutionError and transitions to failed state. | Multiple denials in a single batch are each evaluated independently. If any denial triggers a fail or degrade policy, the remaining tool calls in the batch are skipped.", "token_count": 535} +{"id": "doc_745f751f1344", "path": "docs/library/tools-system-walkthrough.mdx", "url": "/library/tools-system-walkthrough", "title": "Tools System Walkthrough", "description": "From tool registration to runtime execution and model feedback.", "headings": ["Registration to execution map", "Step-by-step breakdown", "Full example: tool definition through result inspection", "1. Define a tool with a Pydantic args model", "2. Create an agent with the tool", "3. Run the agent", "4. Inspect the result", "Inspecting tool executions", "Hook and middleware pipeline", "Error handling"], "content_sha256": "1ed41f9431123214684ef929c17c9f9886933e7e7c218f1ac668e47a6e02e499", "content": "Every agent capability beyond pure text generation flows through the AFK tool system. A tool starts as a decorated Python function, gets registered in a ToolRegistry, has its JSON Schema exposed to the LLM, and then participates in a tightly controlled loop: the model proposes a call, AFK validates the arguments, evaluates policy gates, executes the handler, sanitizes the output, and feeds the result back into the conversation for the model's next turn. This page walks through every stage of that pipeline end-to-end. Registration to execution map Step-by-step breakdown 1. **Define @tool** -- You write a Python function and decorate it with @tool. The decorator extracts the function name, docstring, and a Pydantic model for its arguments to produce a ToolSpec(name, description, parameters_schema). 2. **Tool registry** -- When the agent starts, all tools (declared on the agent plus runtime-injected extras like MCP tools and skill tools) are collected into a ToolRegistry. The registry is the single source of truth for tool lookup, schema export, and call dispatch. 3. **Expose tool schema to model** -- The registry converts every registered tool into the OpenAI function-calling format via registry.to_openai_function_tools(). This list is attached to the LLMRequest.tools field so the model knows what tools are available. 4. **Model emits tool call** -- The LLM response (LLMResponse) may contain one or more ToolCall objects. Each carries an id, a tool_name, and a JSON arguments dict. AFK processes these as a batch. 5. **Schema validation** -- Before execution, each tool call's raw arguments are validated against the tool's Pydantic args_model via BaseTool.validate(). If validation fails, a ToolResult(success=False) is returned immediately and the tool handler is never invoked. 6. **Policy gate** -- The runner evaluates the PolicyEngine for each tool call via a tool_before_execute policy event. The policy can allow, deny, defer (require human approval), or request_user_input. See the Tool Call Lifecycle page for the full decision matrix. 7. **Execute handler** -- If the policy allows execution, AFK runs the tool through the full hook/middleware chain: PreHooks (argument transforms) -> Middleware chain -> core handler -> PostHooks (output transforms). Timeout enforcement applies at every layer. 8. **Sanitize output** -- The raw ToolResult.output is passed through apply_tool_output_limits() which enforces max_output_chars and sandbox output policies. The runner then wraps the result in an untrusted-data content envelope via render_untrusted_tool_message() to prevent prompt injection from tool output. 9. **Emit events** -- A tool_completed event is emitted on the run handle, carrying the tool name, success flag, output, and any error. Telemetry counters and histograms are recorded for latency and success/failure counts. 10. **Tool result fed back to model** -- The sanitized output is appended to the conversation as a Message(role=\"tool\", name=tool_name, content=...). The loop returns to step 4 for the next LLM turn. Full example: tool definition through result inspection Inspecting tool executions Every completed run exposes a tool_executions list on AgentResult. Each entry is a ToolExecutionRecord: | Field | Type | Description | | -------------- | ------------------- | ------------------------------------ | | tool_name | str | Name of the executed tool. | | tool_call_id | str \\| None | Provider/LLM tool-call identifier. | | success | bool | Whether execution succeeded. | | output | JSONValue \\| None | JSON-safe tool output payload. | | error | str \\| None | Error message when execution failed. | | latency_ms | float \\| None | Execution latency in milliseconds. | Hook and middleware pipeline Tools support three extension points that wrap the core handler: | Extension | When it runs | Signature | Purpose | | -------------- | ------------------- | ------------------------------------- | ------------------------------------------------------------------------------------------- | | **PreHook** | Before core handler | (args) or (args, ctx) | Transform or validate arguments. Must return a dict compatible with the tool's args model. | | **Middleware** | Wraps core handler | (call_next, args, ctx) | Cross-cutting concerns (logging, timing, caching). Calls call_next(args, ctx) to proceed. | | **PostHook** | After core handler | ({\"output\": ..., \"tool_name\": ...}) | Transform or audit the output before it reaches the model. | The execution order is: validate args -> run PreHooks sequentially -> run Middleware chain (outermost first) -> core handler -> run PostHooks sequentially -> wrap in ToolResult. Error handling The tool system is designed to be non-throwing by default. BaseTool.call() catches all exceptions and wraps them in a ToolResult(success=False, error_message=...) unless raise_on_error=True is set on the tool. **Validation errors** -- When the model passes arguments that do not match the Pydantic schema, a ToolValidationError is caught and the error message is returned to the model so it can self-correct on the next turn. **Timeout errors** -- Each tool can set a default_timeout in seconds. If execution exceeds this limit, an asyncio.TimeoutError is caught and surfaced as a ToolTimeoutError in the result. **Execution errors** -- Any unhandled exception from the handler, PreHook, or PostHook is caught and wrapped in a ToolExecutionError result. **Hook/middleware failures** -- If a PreHook returns a non-dict value or produces args that fail re-validation against the main tool's model, execution stops and a failure result is returned. PostHook failures similarly short-circuit the chain. **Policy-driven behavior** -- After a tool failure, the runner consults the agent's fail_safe.tool_failure_policy to decide whether to continue (default), degrade the run, or fail the entire run. This is configurable per agent.", "token_count": 582} +{"id": "doc_9e9118ed2141", "path": "docs/library/tools.mdx", "url": "/library/tools", "title": "Tools", "description": "Give agents typed capabilities through Python functions.", "headings": ["Your first tool", "How tool calling works", "Tool patterns", "Deferred background tool calls", "Policy-gated tools", "Hooks and middleware", "Execution order", "Common tools cookbook", "Prebuilt tools", "Runtime tools", "Tools scoped to a specific directory", "Returns: [read_file, list_directory, ...]", "Skill tools", "Next steps"], "content_sha256": "517d6cb680faa87f107bc90d44a3e46e807dbc7146a40390f7385f83568e4743", "content": "Tools let agents take actions \u2014 query databases, call APIs, run calculations, write files, or anything you can express in Python. AFK handles schema generation, argument validation, policy gates, execution, and output sanitization. Your first tool That's a complete tool. The @tool decorator generates the JSON schema from the Pydantic model, which the LLM uses to understand what arguments to pass. How tool calling works Based on the user's message and the tool schemas, the LLM emits a tool_call with the function name and arguments. AFK parses the arguments through the Pydantic model. Invalid arguments generate a validation error that's sent back to the LLM for self-correction. If a PolicyEngine is attached, the tool call is checked against policy rules (allow, deny, or request_approval). The tool function runs with validated arguments. Pre/post hooks and middleware execute around the handler. The output is truncated to tool_output_max_chars, stripped of potential prompt injection vectors (if sanitize_tool_output=True), and formatted for the LLM. The sanitized result is appended to the conversation and the LLM generates its next response. Tool patterns Return a string or dict directly. Return a dict for structured data. Use async def for I/O-heavy tools. Access ToolContext in tool handlers. The second parameter must be named ctx or annotated as ToolContext. Deferred background tool calls For long-running operations, a tool can return a deferred handle so the run can continue while work completes in the background. When deferred: 1. Runner emits tool_deferred. 2. Agent continues with other work in the same run. 3. Runner emits tool_background_resolved or tool_background_failed. 4. Resolved tool output is injected back into conversation for next steps. External workers can resolve tickets by writing: - bgtool:{run_id}:{ticket_id}:state - bgtool:{run_id}:latest Status payload example: This pattern is useful for coding agents that start a long build, continue writing docs, then consume build results once available. You can also use runner helpers instead of writing raw state keys: Policy-gated tools Use the PolicyEngine to gate sensitive tool calls: **Policy best practice:** Gate all mutating tools with request_approval or deny by default. Only allow read-only tools without gates. Hooks and middleware AFK provides four extension points for tool execution: **prehooks**, **posthooks**, **tool-level middleware**, and **registry-level middleware**. Each has its own decorator. Prehooks run before the tool handler. They receive the tool's arguments and **must return a dict** compatible with the tool's args_model. Posthooks run after the tool handler. They receive a dict {\"output\": , \"tool_name\": \" \"} and should return a dict with the same shape. Middleware wraps the entire tool execution. It receives call_next, the validated args, and optionally ctx. Registry-level middleware applies to **every tool** in a ToolRegistry. Use for audit logging, rate limiting, or global policy enforcement. Execution order | Layer | Scope | Decorator | Returns | | --------------- | --------------------------- | -------------------------------- | ------------------------------ | | **Prehook** | Single tool, before handler | @prehook(args_model=...) | dict of transformed args | | **Middleware** | Single tool, wraps handler | @middleware(name=...) | Tool output (via call_next) | | **Posthook** | Single tool, after handler | @posthook(args_model=...) | dict with output key | | **Registry MW** | All tools in registry | @registry_middleware(name=...) | ToolResult (via call_next) | Common tools cookbook Prebuilt tools AFK ships with ready-to-use tools for common agent capabilities. These are in the afk.tools.prebuilts module. Runtime tools Filesystem tools scoped to a directory for safe agent exploration: Runtime tools enforce directory-scoped access \u2014 the agent cannot read or list files outside the configured root_dir. Skill tools When an agent has skills configured, AFK generates four skill tools automatically: | Tool | Purpose | | ----------------- | ----------------------------------------------------- | | list_skills | Return metadata for all enabled skills | | read_skill_md | Read a skill's SKILL.md content and checksum | | read_skill_file | Read additional files under a skill directory | | run_skill_command| Execute allowlisted commands with timeout and limits | Skill tools are gated by a SkillToolPolicy that controls command allowlists, output limits, and shell operator restrictions: Next steps Watch tool calls happen in real time. Policy gates, sandbox profiles, and tool allowlists.", "token_count": 481} +{"id": "doc_136d7a909d51", "path": "docs/library/troubleshooting.mdx", "url": "/library/troubleshooting", "title": "Troubleshooting", "description": "Common issues and solutions when building with AFK.", "headings": ["Agent behavior issues", "Agent keeps calling the same tool repeatedly", "Add hard limits to prevent runaway loops", "Agent ignores tools and doesn't call them", "Agent produces inconsistent outputs", "Use request-level sampling controls for direct LLM calls", "Memory issues", "Conversation doesn't persist between runs", "Always use thread_id for multi-turn conversations", "r2 will remember r1's context", "Verify memory is configured", "For production, use persistent storage", "Resume doesn't work", "Check run_id and thread_id are correct", "Resume correctly", "Check checkpoint state directly from the configured memory store", "LLM issues", "Rate limit errors", "Or use exponential backoff for retries", "Timeout errors", "Set appropriate timeouts", "Or per-request timeout via middleware", "Model not found errors", "Verify model name is correct", "Use fallback for resilience", "Streaming issues", "Streaming doesn't work", "Make sure you're iterating correctly", "Don't mix sync and async", "WRONG:", "RIGHT:", "Streaming disconnects early", "Use timeout middleware for streaming", "Cost issues", "Unexpected high costs", "ALWAYS set cost limits", "Token limit errors", "Compact memory to reduce context", "Or use a model with larger context", "Tool issues", "Tool validation errors", "Ensure Pydantic model matches tool implementation", "Tool not found errors", "Verify tool is attached to agent", "Verify tool name matches", "Call with exact name", "Debug mode", "Getting help", "Next steps"], "content_sha256": "b21eb3b6aac297783f21d0585242a6ba2ccdb8038df41fa08915007fc1521053", "content": "This guide covers common issues encountered when building and deploying AFK agents, with solutions and debugging tips. Agent behavior issues Agent keeps calling the same tool repeatedly **Symptoms:** Agent enters a loop, calling the same tool multiple times without making progress. **Causes:** - Tool output doesn't provide the information the agent needs - Agent instructions don't clarify when to stop - Missing a tool that would help the agent determine completion **Solutions:** **Debug:** Enable verbose logging to see tool call inputs/outputs: --- Agent ignores tools and doesn't call them **Symptoms:** Agent responds with text but doesn't use available tools. **Causes:** - Instructions don't mention the tools or when to use them - Tool descriptions are unclear - Model being used doesn't support function calling well **Solutions:** --- Agent produces inconsistent outputs **Symptoms:** Same input produces different outputs on different runs. **Causes:** - Temperature is set too high - Missing structured output configuration - Non-deterministic system prompt **Solutions:** Memory issues Conversation doesn't persist between runs **Symptoms:** Agent doesn't remember previous messages. **Causes:** - Not using thread_id to link conversations - Memory store not configured correctly - Using in-memory store (loses state on restart) **Solution:** **Check memory backend:** --- Resume doesn't work **Symptoms:** Calling runner.resume() doesn't continue from where the run stopped. **Solutions:** **Debug checkpoints:** LLM issues Rate limit errors **Symptoms:** RateLimitError or 429 responses from LLM provider. **Solutions:** --- Timeout errors **Symptoms:** Requests hang or timeout before completing. **Solutions:** --- Model not found errors **Symptoms:** ModelNotFoundError or InvalidRequestError. **Solutions:** Streaming issues Streaming doesn't work **Symptoms:** run_stream() doesn't return events or returns them all at once. **Solutions:** --- Streaming disconnects early **Symptoms:** Stream ends before completion. **Solutions:** Cost issues Unexpected high costs **Symptoms:** API costs much higher than expected. **Causes:** - Agent in a loop making many LLM calls - No cost limits configured - Expensive model being used unnecessarily **Solutions:** --- Token limit errors **Symptoms:** ContextLengthExceeded or similar errors. **Solutions:** Tool issues Tool validation errors **Symptoms:** ToolValidationError when tools are called. **Solutions:** --- Tool not found errors **Symptoms:** Agent can't find or call a tool. **Solutions:** Debug mode Enable debug mode for detailed logging: Getting help If you can't resolve an issue: 1. Check the GitHub Issues for known issues 2. Enable debug logging and capture the full traceback 3. Include these details when reporting: - AFK version (pip show afk) - Python version - LLM provider and model - Minimal reproduction code - Full error traceback Next steps Understand how AFK components work together. Test agent behavior before shipping. Common patterns and anti-patterns. Detailed API documentation.", "token_count": 347} +{"id": "doc_f1e32ed2ffce", "path": "docs/llms/adapters.mdx", "url": "/llms/adapters", "title": "Adapters", "description": "Built-in LLM providers and custom adapter registration.", "headings": ["Built-in providers", "Capability comparison", "Usage", "OpenAI", "Anthropic", "LiteLLM (any provider)", "Custom provider", "Custom transport", "Next steps"], "content_sha256": "4062f20b10ef8ec55493c8418a3fe20da61715e29f3f285979f8f696d0a1872c", "content": "Providers translate between AFK's normalized contracts (LLMRequest/LLMResponse) and provider-specific APIs. AFK ships with three built-in providers and supports custom providers for internal deployments. Built-in providers Direct integration via the OpenAI Python SDK. Supports all GPT-4.1 and o-series models. Direct integration via the Anthropic SDK. Supports Claude Opus 4.5 and Opus. Proxy adapter for 100+ providers (Azure, Bedrock, Gemini, Mistral, local models, etc.). Capability comparison | Feature | OpenAI | Anthropic | LiteLLM | | -------------------- | ------------------------------------------------------ | ------------------------------------------------------ | --------------------------------------------------------------------------- | | Text generation | | | | | Tool calling | | | (provider-dependent) | | Structured output | | | Provider-dependent | | Streaming | | | | | Vision (image input) | | | Provider-dependent | | Custom endpoints | | | | Usage Custom provider Register your own provider for unsupported inference servers: Custom transport Use provider-specific settings for custom endpoints, proxy URLs, or credentials: Next steps Retry, caching, rate limiting, and circuit breaking. How agents resolve and use LLM clients.", "token_count": 106} +{"id": "doc_eeed52120ccf", "path": "docs/llms/agent-integration.mdx", "url": "/llms/agent-integration", "title": "Agent Integration", "description": "How agents resolve models and interact with the LLM layer.", "headings": ["Model resolution", "How the runner uses the LLM", "Request construction", "Streaming integration", "Error handling", "Model selection guide", "Next steps"], "content_sha256": "8f69ff34bc2ca6056745652bec1d0ebcb8e395d7930aaa87e81518f5e05b8619", "content": "This page explains how agents connect to the LLM layer \u2014 from model resolution to request construction, streaming, and error handling. Model resolution Agents can specify their model in two ways: Pass a model name string. AFK resolves it to an LLM client using the default provider. The resolution order: 1. Check agent.model_resolver (custom function) 2. Check registered adapters for matching provider prefix 3. Default to OpenAI adapter Pass a pre-configured LLMClient for full control over provider settings. How the runner uses the LLM On each step of the agent loop: Request construction The runner builds an LLMRequest from multiple sources: | Source | Contributes | Priority | | -------------------- | ----------------------- | -------- | | Agent.instructions | System message | Highest | | Thread history | Previous messages | \u2014 | | user_message | User message | \u2014 | | Agent.tools | Tool schemas | \u2014 | | Agent.subagents | Transfer tool schemas | \u2014 | | RunnerConfig | Temperature, max_tokens | Lowest | Streaming integration When using run_stream(), the runner passes through streaming events from the LLM: text_delta events come from two paths: - Provider streaming when the adapter supports stream deltas. - Runner fallback chunking of final text for non-streaming providers. Tool and step events are generated by the runner. Error handling LLM errors are classified and handled automatically: | Error | Classification | Runner behavior | | ---------------------------- | -------------- | -------------------------- | | Rate limit (429) | Retryable | Retry with backoff | | Server error (500, 502, 503) | Retryable | Retry with backoff | | Auth error (401, 403) | Terminal | Fail the run | | Invalid request (400) | Terminal | Fail the run | | Timeout | Retryable | Retry (if attempts remain) | | Circuit breaker open | Terminal | Try fallback model or fail | | All retries exhausted | Terminal | Try fallback model or fail | Model selection guide Treat these as example starting points. Confirm current model names, prices, and rate limits with the provider you use. | Task | Starting model | Why | | -------------------------- | ------------------------------ | ----------------------------------- | | Simple Q&A, classification | gpt-4.1-nano | Fast, cheap, good enough | | General purpose with tools | gpt-4.1-mini | Best balance of cost and capability | | Complex reasoning, coding | gpt-4.1 or claude-opus-4-5 | Better at multi-step reasoning | | Cost-sensitive batch | gpt-4.1-nano | Lowest cost per token | | Higher quality generation | gpt-4.1 + low temperature | More capability, less variation | Next steps The step loop and execution engine. Real-time event streaming API.", "token_count": 261} +{"id": "doc_e8794369a1a4", "path": "docs/llms/contracts.mdx", "url": "/llms/contracts", "title": "Contracts", "description": "LLMRequest and LLMResponse \u2014 the data that flows across the LLM boundary.", "headings": ["The flow", "LLMRequest", "LLMResponse", "Structured output", "Message types", "Next steps"], "content_sha256": "0ef6386027791d9122a28e938ca2f5752a67f5289f01989c62554bea9d29d5ac", "content": "Every LLM interaction in AFK flows through two typed contracts: LLMRequest (what you send) and LLMResponse (what you get back). These contracts normalize the differences between providers so your agent code never touches provider-specific types. The flow The adapter translates between AFK's normalized contracts and the provider's native format. LLMRequest | Field | Type | Purpose | | --- | --- | --- | | messages | list[Message] | Conversation history (system, user, assistant, tool) | | model | str | Model identifier | | tools | list[ToolSchema] | Available tool schemas | | temperature | float | Sampling temperature (0.0\u20132.0) | | max_tokens | int \\| None | Max output tokens | | top_p | float \\| None | Nucleus sampling | | response_format | ResponseFormat \\| None | Structured output format | | stop | list[str] \\| None | Stop sequences | LLMResponse | Field | Type | Purpose | | --- | --- | --- | | content | str \\| None | Text response from the model | | tool_calls | list[ToolCall] | Tool calls requested by the model | | model | str | Model that generated the response | | usage | Usage | Token counts (prompt, completion, total) | | finish_reason | str | Why generation stopped (stop, tool_calls, length) | | latency_ms | float | End-to-end request latency | Structured output Request structured JSON output with a Pydantic model: Message types | Role | Purpose | Source | | ----------- | ------------------ | ----------------------------- | | system | Agent instructions | From Agent.instructions | | user | User input | From user_message parameter | | assistant | Model responses | Generated by the LLM | | tool | Tool results | From tool execution | Next steps Built-in providers and custom adapters. Retry, caching, rate limiting, and circuit breaking.", "token_count": 175} +{"id": "doc_7320ab982fab", "path": "docs/llms/control-and-session.mdx", "url": "/llms/control-and-session", "title": "Control & Session", "description": "Retry, caching, rate limiting, and circuit breaking for LLM calls.", "headings": ["Policy pipeline", "Built-in profiles", "Development: no retry, no caching, fast failures", "Production: retry, circuit breaker, rate limiting, caching", "Individual policies", "Fallback chains", "If gpt-4.1 fails \u2192 try gpt-4.1-mini \u2192 try gpt-4.1-nano", "Tuning cheat sheet", "Next steps"], "content_sha256": "10d156ced976efb12c419c33d65e5686c7429902256c9e33be26a1add88c39a7", "content": "The LLM runtime includes built-in policies for handling transient failures, managing costs, and protecting against provider outages. Configure them via the builder profile or individual policy settings. Policy pipeline Every LLM request passes through this policy chain: Built-in profiles Use profile() to apply a curated set of policies: | Profile | Retry | Cache | Rate Limit | Circuit Breaker | Timeout | | ------------- | ------------------------------- | ------------------- | ---------- | ---------------------- | ------- | | development | None | None | None | None | 30s | | production | 3 attempts, exponential backoff | In-memory, 5min TTL | 60 req/min | 5 failures \u2192 30s open | 60s | | batch | 5 attempts, longer backoff | None | 20 req/min | 10 failures \u2192 60s open | 120s | Individual policies Configure each policy independently with create_llm_client(): Retry transient LLM failures with exponential backoff. | Parameter | Default | Description | | --- | --- | --- | | max_retries | 3 | Retry attempts after the initial request | | backoff_base_s | 0.5 | Initial delay in seconds | | backoff_jitter_s | 0.15 | Jitter added to retry delays | | require_idempotency_key | True | Require idempotency keys for retried requests | Stop calling a failing provider to prevent cascading failures. Prevent exceeding provider rate limits. Cache identical requests to reduce cost and latency. Cache keys are derived from request content, model settings, response model, session/checkpoint tokens, and any configured cache namespace. Cached rows do not retain provider request IDs, session tokens, checkpoint tokens, or raw provider payloads. Hard timeout on LLM requests. Fallback chains Configure a chain of models to try when the primary model fails: Tuning cheat sheet | Goal | Setting | | ----------------- | ------------------------------------------------------------- | | Reduce costs | Lower temperature, use cheaper model, enable caching | | Reduce latency | Enable caching, use faster model, set tight timeout | | Handle outages | Enable retry + circuit breaker, add fallback chain | | High throughput | Set rate limits high, use batch profile, increase concurrency | | Consistent output | Set temperature=0.0, enable structured output | Next steps How agents resolve and use LLM clients. Monitor LLM call latency, errors, and costs.", "token_count": 238} +{"id": "doc_f92685a34f7e", "path": "docs/llms/index.mdx", "url": "/llms/", "title": "LLM Layer", "description": "Provider-portable LLM runtime with retry, caching, circuit breaking, and middleware.", "headings": ["The LLMBuilder", "Middleware", "Built-in middleware", "Custom middleware", "Middleware protocols", "Supported providers", "Example model choices", "How agents use the LLM layer", "Option 1: Model name (auto-resolved)", "Option 2: Pre-built client (full control)", "Next steps"], "content_sha256": "32c7ac94efe23450adc9126031e9f87e3e689349369a82f600c67fbdd16f86a1", "content": "The LLM layer normalizes communication with language models across all supported providers. Your agent code uses provider-agnostic contracts (LLMRequest / LLMResponse) while built-in adapters handle the provider-specific details. The LLMBuilder Create LLM clients with the builder pattern: Middleware The LLM layer supports middleware for intercepting and transforming requests and responses. Use middleware for logging, tracing, caching, and custom request/response handling. Built-in middleware Custom middleware Middleware protocols | Protocol | Operation | Signature | | --- | --- | --- | | LLMChatMiddleware | Non-streaming chat | async (call_next, req: LLMRequest) -> LLMResponse | | LLMEmbedMiddleware | Embeddings | async (call_next, req: EmbeddingRequest) -> EmbeddingResponse | | LLMStreamMiddleware | Streaming chat | (call_next, req: LLMRequest) -> AsyncIterator[LLMStreamEvent] | Supported providers GPT-4.1, GPT-4.1-mini, GPT-4.1-nano, o-series Claude Opus and Sonnet families 100+ providers via the LiteLLM proxy All providers expose the same LLMClient interface. Your agent code never touches provider-specific types. Example model choices Use these as starting points, then verify model availability, pricing, and context limits with your provider account. | Scenario | Starting point | | -------------------------- | ----------------------------------------------- | | General purpose | OpenAI gpt-4.1-mini | | Complex reasoning | OpenAI gpt-4.1 or Anthropic claude-opus-4-5 | | Cost-sensitive | OpenAI gpt-4.1-nano | | Non-OpenAI/Anthropic model | LiteLLM adapter | | Custom or self-hosted | Custom adapter | How agents use the LLM layer You rarely build LLMClient directly. Agents resolve their model automatically: Next steps LLMRequest / LLMResponse \u2014 what flows across the boundary. Built-in providers and custom adapter registration.", "token_count": 176} diff --git a/skills/afk-maintainer/references/afk-docs/id-to-path.json b/skills/afk-maintainer/references/afk-docs/id-to-path.json new file mode 100644 index 0000000..ac2f619 --- /dev/null +++ b/skills/afk-maintainer/references/afk-docs/id-to-path.json @@ -0,0 +1,65 @@ +{ + "doc_3aa0b3792d22": "docs/docs.json", + "doc_deb460ffa5ee": "docs/index.mdx", + "doc_1c5f37ea0ad3": "docs/library/a2a.mdx", + "doc_b6088784caab": "docs/library/agent-skills.mdx", + "doc_f12bbbc027a7": "docs/library/agentic-behavior.mdx", + "doc_57e2139f0873": "docs/library/agentic-levels.mdx", + "doc_e67e762fb6ab": "docs/library/agents.mdx", + "doc_bbc97cbff254": "docs/library/api-reference.mdx", + "doc_4e1ebaf40267": "docs/library/architecture.mdx", + "doc_18d38fdacc9e": "docs/library/building-with-ai.mdx", + "doc_4a1cf8a90e83": "docs/library/checkpoint-schema.mdx", + "doc_a24b92541657": "docs/library/configuration-reference.mdx", + "doc_e7d508371e5c": "docs/library/core-runner.mdx", + "doc_3f1383f72c5f": "docs/library/debugger.mdx", + "doc_067033cfc945": "docs/library/deployment.mdx", + "doc_d147a81bac29": "docs/library/developer-guide.mdx", + "doc_61fd9f682847": "docs/library/environment-variables.mdx", + "doc_c93c115aeb85": "docs/library/evals.mdx", + "doc_a5120de838fb": "docs/library/examples/index.mdx", + "doc_4055659fe573": "docs/library/failure-policy-matrix.mdx", + "doc_2a18de009d56": "docs/library/full-module-reference.mdx", + "doc_3ae3aaf0c12c": "docs/library/how-to-use-afk.mdx", + "doc_f2ae50c638a4": "docs/library/learn-in-15-minutes.mdx", + "doc_5ee8bbdbd8a8": "docs/library/llm-interaction.mdx", + "doc_e34fe24f9585": "docs/library/mcp-server.mdx", + "doc_0383dbf277ba": "docs/library/memory.mdx", + "doc_50ac650fd8f6": "docs/library/mental-model.mdx", + "doc_75887f91c7df": "docs/library/messaging.mdx", + "doc_5a65ddab1445": "docs/library/migration.mdx", + "doc_78a8125d2c7e": "docs/library/observability.mdx", + "doc_f18f523b6ecc": "docs/library/overview.mdx", + "doc_f5f256549d84": "docs/library/performance.mdx", + "doc_3eeb75d383b8": "docs/library/public-imports-and-function-improvement.mdx", + "doc_bf91f8d5da74": "docs/library/quickstart.mdx", + "doc_f54217a0f2cf": "docs/library/run-event-contract.mdx", + "doc_567be1281e2f": "docs/library/security-model.mdx", + "doc_1e46cbc6bfae": "docs/library/snippets/01_minimal_chat_agent.mdx", + "doc_d6ad61a79371": "docs/library/snippets/02_policy_with_hitl.mdx", + "doc_a2e84472f1f9": "docs/library/snippets/03_subagents_with_router.mdx", + "doc_07174a8633ac": "docs/library/snippets/04_resume_and_compact.mdx", + "doc_3f60413b3a06": "docs/library/snippets/05_direct_llm_structured_output.mdx", + "doc_43ad71a4ad0c": "docs/library/snippets/06_tool_registry_security.mdx", + "doc_c346f13ce597": "docs/library/snippets/07_tool_hooks_and_middleware.mdx", + "doc_464a2b4a8d35": "docs/library/snippets/08_prebuilt_runtime_tools.mdx", + "doc_64090a20f830": "docs/library/snippets/09_system_prompt_loader.mdx", + "doc_7b26cbd9a7ae": "docs/library/snippets/10_streaming_chat_with_memory.mdx", + "doc_fdf48d427fc0": "docs/library/snippets/11_cost_monitoring.mdx", + "doc_40e30666bfa8": "docs/library/snippets/12_mcp_client_integration.mdx", + "doc_d1f7d83e0824": "docs/library/snippets/13_multi_model_fallback.mdx", + "doc_4462f67b1c03": "docs/library/snippets/14_production_client.mdx", + "doc_109157c0d2d7": "docs/library/streaming.mdx", + "doc_2798948c3080": "docs/library/system-prompts.mdx", + "doc_78b1717fafe3": "docs/library/task-queues.mdx", + "doc_618e34441fa7": "docs/library/tested-behaviors.mdx", + "doc_0ac01bce8c79": "docs/library/tool-call-lifecycle.mdx", + "doc_745f751f1344": "docs/library/tools-system-walkthrough.mdx", + "doc_9e9118ed2141": "docs/library/tools.mdx", + "doc_136d7a909d51": "docs/library/troubleshooting.mdx", + "doc_f1e32ed2ffce": "docs/llms/adapters.mdx", + "doc_eeed52120ccf": "docs/llms/agent-integration.mdx", + "doc_e8794369a1a4": "docs/llms/contracts.mdx", + "doc_7320ab982fab": "docs/llms/control-and-session.mdx", + "doc_f92685a34f7e": "docs/llms/index.mdx" +} diff --git a/skills/afk-maintainer/references/afk-docs/inverted-index.json b/skills/afk-maintainer/references/afk-docs/inverted-index.json new file mode 100644 index 0000000..abe2a15 --- /dev/null +++ b/skills/afk-maintainer/references/afk-docs/inverted-index.json @@ -0,0 +1,18851 @@ +{ + "a2a": [ + "doc_1c5f37ea0ad3", + "doc_2a18de009d56", + "doc_3aa0b3792d22", + "doc_4e1ebaf40267", + "doc_567be1281e2f", + "doc_57e2139f0873", + "doc_61fd9f682847", + "doc_bbc97cbff254", + "doc_d147a81bac29", + "doc_e34fe24f9585" + ], + "a2a-accessible": [ + "doc_1c5f37ea0ad3" + ], + "a2aservicehost": [ + "doc_2a18de009d56", + "doc_57e2139f0873" + ], + "abandonment": [ + "doc_618e34441fa7" + ], + "ability": [ + "doc_2798948c3080" + ], + "abort": [ + "doc_50ac650fd8f6" + ], + "aborts": [ + "doc_5ee8bbdbd8a8" + ], + "about": [ + "doc_109157c0d2d7", + "doc_43ad71a4ad0c", + "doc_4e1ebaf40267", + "doc_50ac650fd8f6", + "doc_5ee8bbdbd8a8", + "doc_b6088784caab" + ], + "above": [ + "doc_f54217a0f2cf" + ], + "absent": [ + "doc_3ae3aaf0c12c" + ], + "absolute": [ + "doc_64090a20f830", + "doc_a5120de838fb" + ], + "abstract": [ + "doc_0383dbf277ba", + "doc_2a18de009d56" + ], + "abstraction": [ + "doc_2a18de009d56" + ], + "abuse": [ + "doc_067033cfc945", + "doc_567be1281e2f" + ], + "accept": [ + "doc_43ad71a4ad0c", + "doc_bbc97cbff254" + ], + "acceptable": [ + "doc_3eeb75d383b8", + "doc_bbc97cbff254" + ], + "accepted": [ + "doc_109157c0d2d7", + "doc_a24b92541657", + "doc_f54217a0f2cf" + ], + "accepts": [ + "doc_a24b92541657" + ], + "access": [ + "doc_1c5f37ea0ad3", + "doc_1e46cbc6bfae", + "doc_43ad71a4ad0c", + "doc_464a2b4a8d35", + "doc_567be1281e2f", + "doc_57e2139f0873", + "doc_5a65ddab1445", + "doc_78b1717fafe3", + "doc_7b26cbd9a7ae", + "doc_9e9118ed2141", + "doc_a24b92541657", + "doc_a5120de838fb", + "doc_bbc97cbff254" + ], + "according": [ + "doc_07174a8633ac", + "doc_5ee8bbdbd8a8", + "doc_f12bbbc027a7" + ], + "account": [ + "doc_f92685a34f7e" + ], + "accumulate": [ + "doc_07174a8633ac" + ], + "accumulated": [ + "doc_4a1cf8a90e83" + ], + "accumulates": [ + "doc_5ee8bbdbd8a8" + ], + "acks": [ + "doc_75887f91c7df" + ], + "across": [ + "doc_0383dbf277ba", + "doc_109157c0d2d7", + "doc_1c5f37ea0ad3", + "doc_1e46cbc6bfae", + "doc_3ae3aaf0c12c", + "doc_57e2139f0873", + "doc_618e34441fa7", + "doc_61fd9f682847", + "doc_64090a20f830", + "doc_75887f91c7df", + "doc_7b26cbd9a7ae", + "doc_a24b92541657", + "doc_e34fe24f9585", + "doc_e7d508371e5c", + "doc_f5f256549d84", + "doc_f92685a34f7e" + ], + "act": [ + "doc_43ad71a4ad0c" + ], + "action": [ + "doc_0ac01bce8c79", + "doc_3ae3aaf0c12c", + "doc_4055659fe573", + "doc_567be1281e2f", + "doc_a24b92541657", + "doc_d6ad61a79371", + "doc_e67e762fb6ab", + "doc_f54217a0f2cf" + ], + "actions": [ + "doc_0ac01bce8c79", + "doc_3ae3aaf0c12c", + "doc_43ad71a4ad0c", + "doc_50ac650fd8f6", + "doc_567be1281e2f", + "doc_57e2139f0873", + "doc_618e34441fa7", + "doc_9e9118ed2141", + "doc_a24b92541657", + "doc_a5120de838fb", + "doc_b6088784caab", + "doc_d6ad61a79371" + ], + "active": [ + "doc_07174a8633ac" + ], + "activity": [ + "doc_07174a8633ac" + ], + "actual": [ + "doc_c346f13ce597" + ], + "acyclic": [ + "doc_f12bbbc027a7" + ], + "adapt": [ + "doc_0ac01bce8c79" + ], + "adapter": [ + "doc_1c5f37ea0ad3", + "doc_2a18de009d56", + "doc_4a1cf8a90e83", + "doc_4e1ebaf40267", + "doc_5ee8bbdbd8a8", + "doc_bbc97cbff254", + "doc_e8794369a1a4", + "doc_eeed52120ccf", + "doc_f1e32ed2ffce", + "doc_f92685a34f7e" + ], + "adapters": [ + "doc_2a18de009d56", + "doc_3aa0b3792d22", + "doc_4e1ebaf40267", + "doc_57e2139f0873", + "doc_618e34441fa7", + "doc_c93c115aeb85", + "doc_d147a81bac29", + "doc_e8794369a1a4", + "doc_eeed52120ccf", + "doc_f92685a34f7e" + ], + "add": [ + "doc_0383dbf277ba", + "doc_18d38fdacc9e", + "doc_2798948c3080", + "doc_2a18de009d56", + "doc_3ae3aaf0c12c", + "doc_3eeb75d383b8", + "doc_4e1ebaf40267", + "doc_567be1281e2f", + "doc_57e2139f0873", + "doc_7320ab982fab", + "doc_bf91f8d5da74", + "doc_d147a81bac29", + "doc_e67e762fb6ab", + "doc_f12bbbc027a7" + ], + "added": [ + "doc_57e2139f0873", + "doc_61fd9f682847", + "doc_7320ab982fab", + "doc_f54217a0f2cf" + ], + "adding": [ + "doc_18d38fdacc9e", + "doc_1c5f37ea0ad3", + "doc_618e34441fa7", + "doc_e67e762fb6ab" + ], + "additional": [ + "doc_464a2b4a8d35", + "doc_9e9118ed2141", + "doc_f12bbbc027a7" + ], + "additions": [ + "doc_f12bbbc027a7" + ], + "adds": [ + "doc_50ac650fd8f6", + "doc_57e2139f0873", + "doc_f54217a0f2cf" + ], + "adjust": [ + "doc_0ac01bce8c79" + ], + "admin": [ + "doc_567be1281e2f" + ], + "advanced": [ + "doc_a24b92541657", + "doc_a5120de838fb", + "doc_e67e762fb6ab", + "doc_e7d508371e5c" + ], + "advantages": [ + "doc_5a65ddab1445" + ], + "advisory": [ + "doc_0ac01bce8c79" + ], + "affect": [ + "doc_0ac01bce8c79" + ], + "affected": [ + "doc_3eeb75d383b8", + "doc_618e34441fa7", + "doc_d147a81bac29", + "doc_f18f523b6ecc" + ], + "afk": [ + "doc_0383dbf277ba", + "doc_067033cfc945", + "doc_109157c0d2d7", + "doc_136d7a909d51", + "doc_18d38fdacc9e", + "doc_1c5f37ea0ad3", + "doc_1e46cbc6bfae", + "doc_2798948c3080", + "doc_2a18de009d56", + "doc_3aa0b3792d22", + "doc_3ae3aaf0c12c", + "doc_3eeb75d383b8", + "doc_3f1383f72c5f", + "doc_3f60413b3a06", + "doc_4055659fe573", + "doc_40e30666bfa8", + "doc_43ad71a4ad0c", + "doc_464a2b4a8d35", + "doc_4a1cf8a90e83", + "doc_4e1ebaf40267", + "doc_50ac650fd8f6", + "doc_567be1281e2f", + "doc_57e2139f0873", + "doc_5a65ddab1445", + "doc_618e34441fa7", + "doc_61fd9f682847", + "doc_64090a20f830", + "doc_745f751f1344", + "doc_75887f91c7df", + "doc_78a8125d2c7e", + "doc_9e9118ed2141", + "doc_a24b92541657", + "doc_a2e84472f1f9", + "doc_a5120de838fb", + "doc_b6088784caab", + "doc_bbc97cbff254", + "doc_bf91f8d5da74", + "doc_c346f13ce597", + "doc_c93c115aeb85", + "doc_d147a81bac29", + "doc_d1f7d83e0824", + "doc_d6ad61a79371", + "doc_deb460ffa5ee", + "doc_e34fe24f9585", + "doc_e67e762fb6ab", + "doc_e8794369a1a4", + "doc_eeed52120ccf", + "doc_f12bbbc027a7", + "doc_f18f523b6ecc", + "doc_f1e32ed2ffce", + "doc_f2ae50c638a4", + "doc_f54217a0f2cf", + "doc_f5f256549d84", + "doc_fdf48d427fc0" + ], + "afk-coder": [ + "doc_b6088784caab", + "doc_d147a81bac29", + "doc_deb460ffa5ee" + ], + "afk-maintainer": [ + "doc_b6088784caab", + "doc_d147a81bac29", + "doc_deb460ffa5ee" + ], + "afk-mcp-server": [ + "doc_61fd9f682847" + ], + "afk-py": [ + "doc_d147a81bac29", + "doc_deb460ffa5ee" + ], + "afk-specific": [ + "doc_deb460ffa5ee" + ], + "afk_agent_prompts_dir": [ + "doc_61fd9f682847", + "doc_64090a20f830", + "doc_a24b92541657" + ], + "afk_allowed_commands": [ + "doc_61fd9f682847" + ], + "afk_cors_origins": [ + "doc_61fd9f682847" + ], + "afk_embed_model": [ + "doc_61fd9f682847" + ], + "afk_llm_api_base_url": [ + "doc_61fd9f682847" + ], + "afk_llm_api_key": [ + "doc_61fd9f682847" + ], + "afk_llm_backoff_base_s": [ + "doc_61fd9f682847" + ], + "afk_llm_backoff_jitter_s": [ + "doc_61fd9f682847" + ], + "afk_llm_json_max_retries": [ + "doc_61fd9f682847" + ], + "afk_llm_max_input_chars": [ + "doc_61fd9f682847" + ], + "afk_llm_max_retries": [ + "doc_61fd9f682847", + "doc_a24b92541657" + ], + "afk_llm_model": [ + "doc_61fd9f682847" + ], + "afk_llm_provider": [ + "doc_61fd9f682847" + ], + "afk_llm_provider_order": [ + "doc_61fd9f682847" + ], + "afk_llm_stream_idle_timeout_s": [ + "doc_61fd9f682847" + ], + "afk_llm_timeout_s": [ + "doc_61fd9f682847" + ], + "afk_mcp_allow_batch": [ + "doc_61fd9f682847" + ], + "afk_mcp_enable_health": [ + "doc_61fd9f682847" + ], + "afk_mcp_enable_sse": [ + "doc_61fd9f682847" + ], + "afk_mcp_health_path": [ + "doc_61fd9f682847" + ], + "afk_mcp_host": [ + "doc_61fd9f682847" + ], + "afk_mcp_instructions": [ + "doc_61fd9f682847" + ], + "afk_mcp_name": [ + "doc_61fd9f682847" + ], + "afk_mcp_path": [ + "doc_61fd9f682847" + ], + "afk_mcp_port": [ + "doc_61fd9f682847" + ], + "afk_mcp_sse_path": [ + "doc_61fd9f682847" + ], + "afk_mcp_version": [ + "doc_61fd9f682847" + ], + "afk_memory": [ + "doc_61fd9f682847" + ], + "afk_memory_backend": [ + "doc_61fd9f682847" + ], + "afk_pg_db": [ + "doc_61fd9f682847" + ], + "afk_pg_dsn": [ + "doc_61fd9f682847" + ], + "afk_pg_host": [ + "doc_61fd9f682847" + ], + "afk_pg_password": [ + "doc_61fd9f682847" + ], + "afk_pg_pool_max": [ + "doc_61fd9f682847" + ], + "afk_pg_pool_min": [ + "doc_61fd9f682847" + ], + "afk_pg_port": [ + "doc_61fd9f682847" + ], + "afk_pg_ssl": [ + "doc_61fd9f682847" + ], + "afk_pg_user": [ + "doc_61fd9f682847" + ], + "afk_queue_backend": [ + "doc_61fd9f682847" + ], + "afk_queue_redis_db": [ + "doc_61fd9f682847" + ], + "afk_queue_redis_host": [ + "doc_61fd9f682847" + ], + "afk_queue_redis_password": [ + "doc_61fd9f682847" + ], + "afk_queue_redis_port": [ + "doc_61fd9f682847" + ], + "afk_queue_redis_prefix": [ + "doc_61fd9f682847" + ], + "afk_queue_redis_url": [ + "doc_61fd9f682847" + ], + "afk_queue_retry_backoff_base_s": [ + "doc_61fd9f682847" + ], + "afk_queue_retry_backoff_jitter_s": [ + "doc_61fd9f682847" + ], + "afk_queue_retry_backoff_max_s": [ + "doc_61fd9f682847" + ], + "afk_redis_db": [ + "doc_61fd9f682847" + ], + "afk_redis_events_max": [ + "doc_61fd9f682847" + ], + "afk_redis_host": [ + "doc_61fd9f682847" + ], + "afk_redis_password": [ + "doc_61fd9f682847" + ], + "afk_redis_port": [ + "doc_61fd9f682847" + ], + "afk_redis_url": [ + "doc_61fd9f682847" + ], + "afk_sqlite_path": [ + "doc_61fd9f682847" + ], + "afk_vector_dim": [ + "doc_61fd9f682847" + ], + "after": [ + "doc_0383dbf277ba", + "doc_07174a8633ac", + "doc_0ac01bce8c79", + "doc_2798948c3080", + "doc_4055659fe573", + "doc_4a1cf8a90e83", + "doc_50ac650fd8f6", + "doc_5ee8bbdbd8a8", + "doc_7320ab982fab", + "doc_745f751f1344", + "doc_7b26cbd9a7ae", + "doc_9e9118ed2141", + "doc_a24b92541657", + "doc_c346f13ce597", + "doc_d1f7d83e0824", + "doc_e67e762fb6ab", + "doc_f54217a0f2cf" + ], + "again": [ + "doc_a24b92541657" + ], + "against": [ + "doc_1c5f37ea0ad3", + "doc_3f60413b3a06", + "doc_43ad71a4ad0c", + "doc_464a2b4a8d35", + "doc_50ac650fd8f6", + "doc_7320ab982fab", + "doc_745f751f1344", + "doc_9e9118ed2141", + "doc_c346f13ce597", + "doc_c93c115aeb85" + ], + "agent": [ + "doc_067033cfc945", + "doc_07174a8633ac", + "doc_0ac01bce8c79", + "doc_109157c0d2d7", + "doc_136d7a909d51", + "doc_18d38fdacc9e", + "doc_1c5f37ea0ad3", + "doc_1e46cbc6bfae", + "doc_2798948c3080", + "doc_2a18de009d56", + "doc_3ae3aaf0c12c", + "doc_3eeb75d383b8", + "doc_3f1383f72c5f", + "doc_3f60413b3a06", + "doc_4055659fe573", + "doc_40e30666bfa8", + "doc_43ad71a4ad0c", + "doc_464a2b4a8d35", + "doc_4a1cf8a90e83", + "doc_4e1ebaf40267", + "doc_50ac650fd8f6", + "doc_567be1281e2f", + "doc_57e2139f0873", + "doc_5a65ddab1445", + "doc_5ee8bbdbd8a8", + "doc_618e34441fa7", + "doc_61fd9f682847", + "doc_64090a20f830", + "doc_745f751f1344", + "doc_75887f91c7df", + "doc_78a8125d2c7e", + "doc_78b1717fafe3", + "doc_7b26cbd9a7ae", + "doc_9e9118ed2141", + "doc_a24b92541657", + "doc_a2e84472f1f9", + "doc_a5120de838fb", + "doc_b6088784caab", + "doc_bbc97cbff254", + "doc_bf91f8d5da74", + "doc_c93c115aeb85", + "doc_d147a81bac29", + "doc_d6ad61a79371", + "doc_deb460ffa5ee", + "doc_e34fe24f9585", + "doc_e67e762fb6ab", + "doc_e7d508371e5c", + "doc_e8794369a1a4", + "doc_eeed52120ccf", + "doc_f12bbbc027a7", + "doc_f18f523b6ecc", + "doc_f2ae50c638a4", + "doc_f54217a0f2cf", + "doc_f5f256549d84", + "doc_f92685a34f7e", + "doc_fdf48d427fc0" + ], + "agent-facing": [ + "doc_3eeb75d383b8", + "doc_bbc97cbff254", + "doc_d147a81bac29", + "doc_f18f523b6ecc" + ], + "agent-integration": [ + "doc_3aa0b3792d22" + ], + "agent-skills": [ + "doc_3aa0b3792d22" + ], + "agent-to-agent": [ + "doc_2a18de009d56", + "doc_e34fe24f9585" + ], + "agent_class": [ + "doc_64090a20f830" + ], + "agent_depth": [ + "doc_e7d508371e5c", + "doc_f54217a0f2cf" + ], + "agent_name": [ + "doc_2798948c3080", + "doc_4a1cf8a90e83", + "doc_5ee8bbdbd8a8", + "doc_64090a20f830", + "doc_e7d508371e5c", + "doc_f54217a0f2cf" + ], + "agent_path": [ + "doc_e7d508371e5c", + "doc_f54217a0f2cf" + ], + "agentcancellederror": [ + "doc_2a18de009d56" + ], + "agentcheckpointcorruptionerror": [ + "doc_07174a8633ac", + "doc_4a1cf8a90e83" + ], + "agentcommunicationprotocol": [ + "doc_1c5f37ea0ad3", + "doc_2a18de009d56" + ], + "agentdeadletter": [ + "doc_2a18de009d56" + ], + "agenterror": [ + "doc_2a18de009d56" + ], + "agentexecutionerror": [ + "doc_0ac01bce8c79", + "doc_2a18de009d56" + ], + "agentic": [ + "doc_3f60413b3a06", + "doc_b6088784caab" + ], + "agentic-behavior": [ + "doc_3aa0b3792d22" + ], + "agentic-levels": [ + "doc_3aa0b3792d22" + ], + "agentinvocationrequest": [ + "doc_1c5f37ea0ad3", + "doc_2a18de009d56", + "doc_50ac650fd8f6" + ], + "agentinvocationresponse": [ + "doc_2a18de009d56", + "doc_50ac650fd8f6" + ], + "agentresult": [ + "doc_0ac01bce8c79", + "doc_109157c0d2d7", + "doc_1e46cbc6bfae", + "doc_2a18de009d56", + "doc_3ae3aaf0c12c", + "doc_3f60413b3a06", + "doc_4a1cf8a90e83", + "doc_5ee8bbdbd8a8", + "doc_618e34441fa7", + "doc_745f751f1344", + "doc_a2e84472f1f9", + "doc_bbc97cbff254", + "doc_bf91f8d5da74", + "doc_d147a81bac29", + "doc_deb460ffa5ee", + "doc_e7d508371e5c", + "doc_f12bbbc027a7", + "doc_f18f523b6ecc", + "doc_f5f256549d84", + "doc_fdf48d427fc0" + ], + "agentrunevent": [ + "doc_0ac01bce8c79", + "doc_2a18de009d56", + "doc_50ac650fd8f6", + "doc_f54217a0f2cf" + ], + "agentrunhandle": [ + "doc_2a18de009d56" + ], + "agents": [ + "doc_0383dbf277ba", + "doc_067033cfc945", + "doc_136d7a909d51", + "doc_18d38fdacc9e", + "doc_1c5f37ea0ad3", + "doc_2a18de009d56", + "doc_3aa0b3792d22", + "doc_3ae3aaf0c12c", + "doc_3eeb75d383b8", + "doc_3f1383f72c5f", + "doc_40e30666bfa8", + "doc_43ad71a4ad0c", + "doc_464a2b4a8d35", + "doc_4e1ebaf40267", + "doc_50ac650fd8f6", + "doc_567be1281e2f", + "doc_57e2139f0873", + "doc_618e34441fa7", + "doc_61fd9f682847", + "doc_64090a20f830", + "doc_7320ab982fab", + "doc_75887f91c7df", + "doc_9e9118ed2141", + "doc_a24b92541657", + "doc_a5120de838fb", + "doc_b6088784caab", + "doc_bbc97cbff254", + "doc_c93c115aeb85", + "doc_d147a81bac29", + "doc_deb460ffa5ee", + "doc_e34fe24f9585", + "doc_e67e762fb6ab", + "doc_e7d508371e5c", + "doc_eeed52120ccf", + "doc_f12bbbc027a7", + "doc_f18f523b6ecc", + "doc_f1e32ed2ffce", + "doc_f2ae50c638a4", + "doc_f92685a34f7e", + "doc_fdf48d427fc0" + ], + "agentstate": [ + "doc_2a18de009d56" + ], + "agentstreamevent": [ + "doc_109157c0d2d7", + "doc_2a18de009d56", + "doc_5ee8bbdbd8a8", + "doc_bbc97cbff254" + ], + "agentstreamhandle": [ + "doc_109157c0d2d7", + "doc_2a18de009d56", + "doc_bbc97cbff254", + "doc_f2ae50c638a4" + ], + "aggregate": [ + "doc_4a1cf8a90e83", + "doc_d1f7d83e0824", + "doc_e7d508371e5c", + "doc_f18f523b6ecc" + ], + "aggregated": [ + "doc_2a18de009d56", + "doc_bbc97cbff254", + "doc_bf91f8d5da74" + ], + "aggregates": [ + "doc_4a1cf8a90e83" + ], + "aggregation": [ + "doc_78a8125d2c7e" + ], + "ai": [ + "doc_18d38fdacc9e", + "doc_3ae3aaf0c12c", + "doc_57e2139f0873", + "doc_d147a81bac29", + "doc_deb460ffa5ee", + "doc_e34fe24f9585", + "doc_e67e762fb6ab", + "doc_f18f523b6ecc" + ], + "alert": [ + "doc_067033cfc945", + "doc_07174a8633ac", + "doc_75887f91c7df", + "doc_78a8125d2c7e" + ], + "alerting": [ + "doc_067033cfc945", + "doc_18d38fdacc9e", + "doc_618e34441fa7", + "doc_78a8125d2c7e" + ], + "alerts": [ + "doc_18d38fdacc9e", + "doc_567be1281e2f" + ], + "algorithm": [ + "doc_64090a20f830" + ], + "alias": [ + "doc_64090a20f830" + ], + "aligned": [ + "doc_d147a81bac29" + ], + "all": [ + "doc_0383dbf277ba", + "doc_07174a8633ac", + "doc_0ac01bce8c79", + "doc_136d7a909d51", + "doc_18d38fdacc9e", + "doc_1c5f37ea0ad3", + "doc_1e46cbc6bfae", + "doc_3ae3aaf0c12c", + "doc_3f60413b3a06", + "doc_4055659fe573", + "doc_40e30666bfa8", + "doc_43ad71a4ad0c", + "doc_4462f67b1c03", + "doc_464a2b4a8d35", + "doc_4a1cf8a90e83", + "doc_4e1ebaf40267", + "doc_567be1281e2f", + "doc_57e2139f0873", + "doc_618e34441fa7", + "doc_61fd9f682847", + "doc_745f751f1344", + "doc_75887f91c7df", + "doc_78b1717fafe3", + "doc_9e9118ed2141", + "doc_a24b92541657", + "doc_a2e84472f1f9", + "doc_b6088784caab", + "doc_c346f13ce597", + "doc_d1f7d83e0824", + "doc_d6ad61a79371", + "doc_e34fe24f9585", + "doc_e7d508371e5c", + "doc_eeed52120ccf", + "doc_f12bbbc027a7", + "doc_f1e32ed2ffce", + "doc_f92685a34f7e" + ], + "all__": [ + "doc_3eeb75d383b8", + "doc_d147a81bac29" + ], + "all_required": [ + "doc_4055659fe573", + "doc_f12bbbc027a7" + ], + "allow": [ + "doc_0ac01bce8c79", + "doc_43ad71a4ad0c", + "doc_50ac650fd8f6", + "doc_567be1281e2f", + "doc_618e34441fa7", + "doc_61fd9f682847", + "doc_745f751f1344", + "doc_9e9118ed2141", + "doc_a24b92541657", + "doc_d6ad61a79371", + "doc_e67e762fb6ab", + "doc_f12bbbc027a7" + ], + "allow_command_execution": [ + "doc_a24b92541657" + ], + "allow_network": [ + "doc_a24b92541657" + ], + "allow_optional_failures": [ + "doc_18d38fdacc9e", + "doc_f12bbbc027a7" + ], + "allowalla2aauthprovider": [ + "doc_2a18de009d56" + ], + "allowed": [ + "doc_618e34441fa7", + "doc_a24b92541657", + "doc_e34fe24f9585" + ], + "allowed_command_prefixes": [ + "doc_a24b92541657" + ], + "allowed_paths": [ + "doc_a24b92541657" + ], + "allowlist": [ + "doc_61fd9f682847", + "doc_b6088784caab" + ], + "allowlisted": [ + "doc_9e9118ed2141", + "doc_a24b92541657" + ], + "allowlists": [ + "doc_464a2b4a8d35", + "doc_618e34441fa7", + "doc_9e9118ed2141" + ], + "allows": [ + "doc_1c5f37ea0ad3", + "doc_4a1cf8a90e83", + "doc_745f751f1344", + "doc_b6088784caab" + ], + "along": [ + "doc_0ac01bce8c79" + ], + "alongside": [ + "doc_a2e84472f1f9", + "doc_b6088784caab" + ], + "already": [ + "doc_07174a8633ac", + "doc_4a1cf8a90e83", + "doc_f5f256549d84" + ], + "already-completed": [ + "doc_4a1cf8a90e83" + ], + "also": [ + "doc_3f1383f72c5f", + "doc_4a1cf8a90e83", + "doc_64090a20f830", + "doc_9e9118ed2141", + "doc_bbc97cbff254", + "doc_d1f7d83e0824" + ], + "always": [ + "doc_0383dbf277ba", + "doc_067033cfc945", + "doc_0ac01bce8c79", + "doc_18d38fdacc9e", + "doc_1c5f37ea0ad3", + "doc_1e46cbc6bfae", + "doc_4e1ebaf40267", + "doc_50ac650fd8f6", + "doc_567be1281e2f", + "doc_61fd9f682847", + "doc_75887f91c7df", + "doc_b6088784caab", + "doc_e34fe24f9585", + "doc_e67e762fb6ab", + "doc_f54217a0f2cf", + "doc_fdf48d427fc0" + ], + "analysis": [ + "doc_618e34441fa7", + "doc_d1f7d83e0824", + "doc_f12bbbc027a7" + ], + "analytics": [ + "doc_4055659fe573" + ], + "and_": [ + "doc_2798948c3080" + ], + "annotate": [ + "doc_c346f13ce597" + ], + "annotated": [ + "doc_9e9118ed2141" + ], + "annotation": [ + "doc_c346f13ce597" + ], + "anomalies": [ + "doc_567be1281e2f" + ], + "anomaly": [ + "doc_78a8125d2c7e" + ], + "another": [ + "doc_5ee8bbdbd8a8", + "doc_618e34441fa7", + "doc_b6088784caab", + "doc_deb460ffa5ee" + ], + "answer": [ + "doc_18d38fdacc9e", + "doc_4055659fe573", + "doc_f12bbbc027a7" + ], + "answering": [ + "doc_18d38fdacc9e" + ], + "anthropic": [ + "doc_5ee8bbdbd8a8", + "doc_f1e32ed2ffce", + "doc_f92685a34f7e" + ], + "anthropic_agent": [ + "doc_3f60413b3a06", + "doc_61fd9f682847" + ], + "anthropic_api_key": [ + "doc_61fd9f682847" + ], + "anthropicagentprovider": [ + "doc_2a18de009d56" + ], + "anti-pattern": [ + "doc_18d38fdacc9e" + ], + "anti-patterns": [ + "doc_067033cfc945", + "doc_136d7a909d51", + "doc_18d38fdacc9e", + "doc_567be1281e2f", + "doc_c93c115aeb85" + ], + "any": [ + "doc_0383dbf277ba", + "doc_07174a8633ac", + "doc_0ac01bce8c79", + "doc_2798948c3080", + "doc_3f1383f72c5f", + "doc_3f60413b3a06", + "doc_4055659fe573", + "doc_43ad71a4ad0c", + "doc_464a2b4a8d35", + "doc_4a1cf8a90e83", + "doc_4e1ebaf40267", + "doc_5ee8bbdbd8a8", + "doc_618e34441fa7", + "doc_64090a20f830", + "doc_7320ab982fab", + "doc_745f751f1344", + "doc_75887f91c7df", + "doc_78a8125d2c7e", + "doc_a24b92541657", + "doc_a2e84472f1f9", + "doc_b6088784caab", + "doc_d6ad61a79371", + "doc_e34fe24f9585", + "doc_e67e762fb6ab", + "doc_f12bbbc027a7" + ], + "anyone": [ + "doc_1c5f37ea0ad3", + "doc_e34fe24f9585" + ], + "anything": [ + "doc_2798948c3080", + "doc_9e9118ed2141" + ], + "api": [ + "doc_067033cfc945", + "doc_07174a8633ac", + "doc_109157c0d2d7", + "doc_136d7a909d51", + "doc_1c5f37ea0ad3", + "doc_1e46cbc6bfae", + "doc_3aa0b3792d22", + "doc_3eeb75d383b8", + "doc_3f60413b3a06", + "doc_4055659fe573", + "doc_567be1281e2f", + "doc_57e2139f0873", + "doc_5a65ddab1445", + "doc_618e34441fa7", + "doc_61fd9f682847", + "doc_78a8125d2c7e", + "doc_7b26cbd9a7ae", + "doc_a24b92541657", + "doc_b6088784caab", + "doc_bbc97cbff254", + "doc_c346f13ce597", + "doc_c93c115aeb85", + "doc_d147a81bac29", + "doc_d1f7d83e0824", + "doc_e67e762fb6ab", + "doc_e7d508371e5c", + "doc_eeed52120ccf", + "doc_f18f523b6ecc", + "doc_fdf48d427fc0" + ], + "api-reference": [ + "doc_3aa0b3792d22" + ], + "api_key": [ + "doc_3f1383f72c5f" + ], + "apikeya2aauthprovider": [ + "doc_2a18de009d56" + ], + "apis": [ + "doc_0383dbf277ba", + "doc_2a18de009d56", + "doc_43ad71a4ad0c", + "doc_61fd9f682847", + "doc_9e9118ed2141", + "doc_bbc97cbff254", + "doc_f1e32ed2ffce", + "doc_f5f256549d84" + ], + "apologize": [ + "doc_a24b92541657" + ], + "app": [ + "doc_f18f523b6ecc" + ], + "append": [ + "doc_a24b92541657" + ], + "append-only": [ + "doc_f54217a0f2cf" + ], + "appended": [ + "doc_5ee8bbdbd8a8", + "doc_745f751f1344", + "doc_9e9118ed2141" + ], + "appends": [ + "doc_0ac01bce8c79" + ], + "applicable": [ + "doc_57e2139f0873" + ], + "application": [ + "doc_bbc97cbff254", + "doc_c93c115aeb85", + "doc_d147a81bac29", + "doc_deb460ffa5ee" + ], + "applications": [ + "doc_57e2139f0873", + "doc_7b26cbd9a7ae", + "doc_b6088784caab", + "doc_bbc97cbff254", + "doc_d6ad61a79371", + "doc_deb460ffa5ee", + "doc_e7d508371e5c", + "doc_f18f523b6ecc", + "doc_f2ae50c638a4" + ], + "applied": [ + "doc_64090a20f830", + "doc_a24b92541657", + "doc_e67e762fb6ab" + ], + "applies": [ + "doc_0383dbf277ba", + "doc_4055659fe573", + "doc_5ee8bbdbd8a8", + "doc_745f751f1344", + "doc_9e9118ed2141", + "doc_c346f13ce597" + ], + "apply": [ + "doc_3f60413b3a06", + "doc_40e30666bfa8", + "doc_4462f67b1c03", + "doc_7320ab982fab", + "doc_bbc97cbff254", + "doc_c346f13ce597", + "doc_e34fe24f9585" + ], + "apply_event_retention": [ + "doc_2a18de009d56" + ], + "apply_state_retention": [ + "doc_2a18de009d56" + ], + "apply_tool_output_limits": [ + "doc_745f751f1344" + ], + "approach": [ + "doc_0ac01bce8c79", + "doc_3ae3aaf0c12c", + "doc_3f60413b3a06", + "doc_5a65ddab1445", + "doc_f54217a0f2cf" + ], + "appropriate": [ + "doc_3ae3aaf0c12c", + "doc_618e34441fa7", + "doc_c346f13ce597" + ], + "approval": [ + "doc_0383dbf277ba", + "doc_07174a8633ac", + "doc_0ac01bce8c79", + "doc_18d38fdacc9e", + "doc_2a18de009d56", + "doc_3ae3aaf0c12c", + "doc_43ad71a4ad0c", + "doc_464a2b4a8d35", + "doc_4a1cf8a90e83", + "doc_50ac650fd8f6", + "doc_745f751f1344", + "doc_a24b92541657", + "doc_a5120de838fb", + "doc_d6ad61a79371" + ], + "approval_denial_policy": [ + "doc_0ac01bce8c79", + "doc_a24b92541657" + ], + "approval_fallback": [ + "doc_0ac01bce8c79", + "doc_a24b92541657", + "doc_d6ad61a79371" + ], + "approval_timeout_s": [ + "doc_0ac01bce8c79", + "doc_a24b92541657", + "doc_d6ad61a79371" + ], + "approvaldecision": [ + "doc_d6ad61a79371" + ], + "approvalrequest": [ + "doc_d6ad61a79371" + ], + "approved": [ + "doc_0ac01bce8c79", + "doc_5ee8bbdbd8a8", + "doc_d6ad61a79371" + ], + "apps": [ + "doc_a24b92541657" + ], + "architecture": [ + "doc_07174a8633ac", + "doc_1c5f37ea0ad3", + "doc_2a18de009d56", + "doc_3aa0b3792d22", + "doc_3f1383f72c5f", + "doc_40e30666bfa8", + "doc_464a2b4a8d35", + "doc_4e1ebaf40267", + "doc_64090a20f830", + "doc_c346f13ce597", + "doc_e34fe24f9585", + "doc_f18f523b6ecc" + ], + "area": [ + "doc_18d38fdacc9e", + "doc_567be1281e2f", + "doc_d147a81bac29" + ], + "areas": [ + "doc_d147a81bac29" + ], + "args": [ + "doc_745f751f1344", + "doc_9e9118ed2141", + "doc_bbc97cbff254" + ], + "args_model": [ + "doc_745f751f1344", + "doc_9e9118ed2141", + "doc_a24b92541657", + "doc_c346f13ce597" + ], + "argument": [ + "doc_2a18de009d56", + "doc_3ae3aaf0c12c", + "doc_43ad71a4ad0c", + "doc_64090a20f830", + "doc_745f751f1344", + "doc_9e9118ed2141", + "doc_a24b92541657", + "doc_bf91f8d5da74", + "doc_c346f13ce597", + "doc_e67e762fb6ab" + ], + "arguments": [ + "doc_0ac01bce8c79", + "doc_18d38fdacc9e", + "doc_1e46cbc6bfae", + "doc_4055659fe573", + "doc_43ad71a4ad0c", + "doc_4a1cf8a90e83", + "doc_50ac650fd8f6", + "doc_5a65ddab1445", + "doc_61fd9f682847", + "doc_745f751f1344", + "doc_78b1717fafe3", + "doc_9e9118ed2141", + "doc_a24b92541657", + "doc_bf91f8d5da74", + "doc_c346f13ce597", + "doc_e67e762fb6ab", + "doc_f12bbbc027a7", + "doc_f2ae50c638a4" + ], + "around": [ + "doc_18d38fdacc9e", + "doc_2a18de009d56", + "doc_9e9118ed2141", + "doc_c346f13ce597" + ], + "arpan404": [ + "doc_3aa0b3792d22" + ], + "arrive": [ + "doc_618e34441fa7" + ], + "arrives": [ + "doc_f54217a0f2cf" + ], + "arriving": [ + "doc_618e34441fa7" + ], + "arrow": [ + "doc_3aa0b3792d22" + ], + "artifact": [ + "doc_618e34441fa7" + ], + "arun_case": [ + "doc_2a18de009d56" + ], + "arun_suite": [ + "doc_2a18de009d56" + ], + "ask": [ + "doc_a24b92541657" + ], + "asks": [ + "doc_0ac01bce8c79" + ], + "aspects": [ + "doc_a2e84472f1f9" + ], + "assertion": [ + "doc_2a18de009d56", + "doc_618e34441fa7", + "doc_c93c115aeb85" + ], + "assertions": [ + "doc_2798948c3080", + "doc_4e1ebaf40267", + "doc_618e34441fa7", + "doc_c93c115aeb85", + "doc_f54217a0f2cf" + ], + "asserts": [ + "doc_618e34441fa7" + ], + "assistant": [ + "doc_0383dbf277ba", + "doc_4a1cf8a90e83", + "doc_5a65ddab1445", + "doc_5ee8bbdbd8a8", + "doc_bbc97cbff254", + "doc_bf91f8d5da74", + "doc_e8794369a1a4" + ], + "assistants": [ + "doc_5a65ddab1445" + ], + "assurance": [ + "doc_50ac650fd8f6" + ], + "async": [ + "doc_1c5f37ea0ad3", + "doc_1e46cbc6bfae", + "doc_4a1cf8a90e83", + "doc_57e2139f0873", + "doc_61fd9f682847", + "doc_75887f91c7df", + "doc_9e9118ed2141", + "doc_bbc97cbff254", + "doc_bf91f8d5da74", + "doc_c346f13ce597", + "doc_d147a81bac29", + "doc_deb460ffa5ee", + "doc_e7d508371e5c", + "doc_f54217a0f2cf", + "doc_f5f256549d84", + "doc_f92685a34f7e" + ], + "asyncevalassertion": [ + "doc_2a18de009d56" + ], + "asynchronous": [ + "doc_4a1cf8a90e83", + "doc_a24b92541657" + ], + "asynchronously": [ + "doc_57e2139f0873" + ], + "asyncio": [ + "doc_745f751f1344" + ], + "asynciterator": [ + "doc_c346f13ce597", + "doc_f92685a34f7e" + ], + "at-least-once": [ + "doc_75887f91c7df" + ], + "atomic": [ + "doc_0383dbf277ba" + ], + "attach": [ + "doc_3f1383f72c5f", + "doc_3f60413b3a06", + "doc_40e30666bfa8", + "doc_a5120de838fb", + "doc_bf91f8d5da74", + "doc_e67e762fb6ab" + ], + "attached": [ + "doc_40e30666bfa8", + "doc_745f751f1344", + "doc_9e9118ed2141", + "doc_e34fe24f9585", + "doc_f2ae50c638a4" + ], + "attaching": [ + "doc_c346f13ce597" + ], + "attacks": [ + "doc_464a2b4a8d35" + ], + "attempt": [ + "doc_464a2b4a8d35", + "doc_4a1cf8a90e83", + "doc_f12bbbc027a7" + ], + "attempts": [ + "doc_464a2b4a8d35", + "doc_61fd9f682847", + "doc_7320ab982fab", + "doc_75887f91c7df", + "doc_eeed52120ccf" + ], + "attributes": [ + "doc_0ac01bce8c79" + ], + "audience": [ + "doc_1c5f37ea0ad3" + ], + "audit": [ + "doc_0ac01bce8c79", + "doc_43ad71a4ad0c", + "doc_50ac650fd8f6", + "doc_618e34441fa7", + "doc_745f751f1344", + "doc_9e9118ed2141", + "doc_c346f13ce597", + "doc_d6ad61a79371", + "doc_f54217a0f2cf" + ], + "auditability": [ + "doc_4a1cf8a90e83" + ], + "auth": [ + "doc_1c5f37ea0ad3", + "doc_2a18de009d56", + "doc_4055659fe573", + "doc_4e1ebaf40267", + "doc_567be1281e2f", + "doc_57e2139f0873", + "doc_5ee8bbdbd8a8", + "doc_78b1717fafe3", + "doc_d147a81bac29", + "doc_e34fe24f9585", + "doc_eeed52120ccf" + ], + "authenticate": [ + "doc_1c5f37ea0ad3", + "doc_e34fe24f9585" + ], + "authenticated": [ + "doc_57e2139f0873" + ], + "authentication": [ + "doc_1c5f37ea0ad3", + "doc_40e30666bfa8", + "doc_567be1281e2f", + "doc_61fd9f682847" + ], + "authorization": [ + "doc_1c5f37ea0ad3", + "doc_3f1383f72c5f", + "doc_567be1281e2f" + ], + "auto": [ + "doc_5ee8bbdbd8a8" + ], + "auto-detected": [ + "doc_64090a20f830" + ], + "auto-detection": [ + "doc_2798948c3080", + "doc_64090a20f830" + ], + "auto-register": [ + "doc_a24b92541657" + ], + "auto-resolved": [ + "doc_d6ad61a79371" + ], + "auto-resolves": [ + "doc_d6ad61a79371" + ], + "auto-scaling": [ + "doc_067033cfc945" + ], + "auto-select": [ + "doc_0383dbf277ba" + ], + "automated": [ + "doc_a24b92541657", + "doc_d6ad61a79371" + ], + "automatic": [ + "doc_78b1717fafe3", + "doc_a5120de838fb" + ], + "automatically": [ + "doc_0383dbf277ba", + "doc_067033cfc945", + "doc_2798948c3080", + "doc_57e2139f0873", + "doc_64090a20f830", + "doc_9e9118ed2141", + "doc_a24b92541657", + "doc_b6088784caab", + "doc_e67e762fb6ab", + "doc_eeed52120ccf", + "doc_f92685a34f7e" + ], + "autonomously": [ + "doc_b6088784caab" + ], + "availability": [ + "doc_50ac650fd8f6", + "doc_f92685a34f7e" + ], + "available": [ + "doc_0ac01bce8c79", + "doc_136d7a909d51", + "doc_2798948c3080", + "doc_3f60413b3a06", + "doc_40e30666bfa8", + "doc_464a2b4a8d35", + "doc_567be1281e2f", + "doc_64090a20f830", + "doc_745f751f1344", + "doc_7b26cbd9a7ae", + "doc_9e9118ed2141", + "doc_b6088784caab", + "doc_bbc97cbff254", + "doc_bf91f8d5da74", + "doc_d6ad61a79371", + "doc_e34fe24f9585", + "doc_e8794369a1a4", + "doc_f12bbbc027a7" + ], + "average": [ + "doc_78a8125d2c7e" + ], + "avg_llm_latency_ms": [ + "doc_618e34441fa7" + ], + "avoid": [ + "doc_3eeb75d383b8", + "doc_f5f256549d84" + ], + "avoiding": [ + "doc_18d38fdacc9e" + ], + "await": [ + "doc_bbc97cbff254" + ], + "awaitable": [ + "doc_e7d508371e5c" + ], + "away": [ + "doc_7b26cbd9a7ae" + ], + "aws": [ + "doc_067033cfc945" + ], + "azure": [ + "doc_f1e32ed2ffce" + ], + "b6e4f": [ + "doc_3aa0b3792d22" + ], + "back": [ + "doc_0383dbf277ba", + "doc_0ac01bce8c79", + "doc_3f60413b3a06", + "doc_5ee8bbdbd8a8", + "doc_61fd9f682847", + "doc_745f751f1344", + "doc_9e9118ed2141", + "doc_a24b92541657", + "doc_a2e84472f1f9", + "doc_bf91f8d5da74", + "doc_e7d508371e5c", + "doc_e8794369a1a4", + "doc_f12bbbc027a7" + ], + "backed": [ + "doc_0383dbf277ba" + ], + "backend": [ + "doc_0383dbf277ba", + "doc_136d7a909d51", + "doc_18d38fdacc9e", + "doc_2a18de009d56", + "doc_3f60413b3a06", + "doc_4055659fe573", + "doc_4e1ebaf40267", + "doc_61fd9f682847", + "doc_78a8125d2c7e", + "doc_7b26cbd9a7ae", + "doc_a24b92541657", + "doc_f5f256549d84" + ], + "backend-specific": [ + "doc_a24b92541657" + ], + "backends": [ + "doc_0383dbf277ba", + "doc_2a18de009d56", + "doc_5a65ddab1445", + "doc_618e34441fa7", + "doc_61fd9f682847", + "doc_75887f91c7df", + "doc_78b1717fafe3", + "doc_f5f256549d84" + ], + "background": [ + "doc_0383dbf277ba", + "doc_07174a8633ac", + "doc_109157c0d2d7", + "doc_3f1383f72c5f", + "doc_4a1cf8a90e83", + "doc_57e2139f0873", + "doc_9e9118ed2141", + "doc_a24b92541657", + "doc_f54217a0f2cf", + "doc_f5f256549d84" + ], + "background_tool_default_grace_s": [ + "doc_a24b92541657" + ], + "background_tool_interrupt_on_resolve": [ + "doc_a24b92541657" + ], + "background_tool_max_pending": [ + "doc_a24b92541657" + ], + "background_tool_poll_interval_s": [ + "doc_a24b92541657" + ], + "background_tool_result_ttl_s": [ + "doc_a24b92541657" + ], + "background_tools_enabled": [ + "doc_a24b92541657" + ], + "backing": [ + "doc_618e34441fa7" + ], + "backoff": [ + "doc_4055659fe573", + "doc_5ee8bbdbd8a8", + "doc_618e34441fa7", + "doc_61fd9f682847", + "doc_7320ab982fab", + "doc_75887f91c7df", + "doc_78b1717fafe3", + "doc_a24b92541657", + "doc_eeed52120ccf" + ], + "backoff_base_s": [ + "doc_7320ab982fab" + ], + "backoff_jitter_s": [ + "doc_7320ab982fab" + ], + "backpressure": [ + "doc_2a18de009d56", + "doc_57e2139f0873", + "doc_618e34441fa7", + "doc_a24b92541657", + "doc_f12bbbc027a7" + ], + "backward": [ + "doc_4e1ebaf40267" + ], + "balance": [ + "doc_eeed52120ccf" + ], + "base": [ + "doc_18d38fdacc9e", + "doc_2a18de009d56", + "doc_3eeb75d383b8", + "doc_61fd9f682847", + "doc_b6088784caab", + "doc_d147a81bac29" + ], + "baseagent": [ + "doc_07174a8633ac", + "doc_2a18de009d56" + ], + "based": [ + "doc_18d38fdacc9e", + "doc_2798948c3080", + "doc_9e9118ed2141", + "doc_b6088784caab", + "doc_f12bbbc027a7" + ], + "basemodel": [ + "doc_a24b92541657" + ], + "basetool": [ + "doc_745f751f1344" + ], + "basic": [ + "doc_0383dbf277ba", + "doc_067033cfc945", + "doc_3f1383f72c5f", + "doc_5a65ddab1445", + "doc_64090a20f830", + "doc_d1f7d83e0824", + "doc_d6ad61a79371" + ], + "basics": [ + "doc_a5120de838fb" + ], + "batch": [ + "doc_0383dbf277ba", + "doc_0ac01bce8c79", + "doc_4a1cf8a90e83", + "doc_57e2139f0873", + "doc_5ee8bbdbd8a8", + "doc_7320ab982fab", + "doc_745f751f1344", + "doc_78b1717fafe3", + "doc_a24b92541657", + "doc_a5120de838fb", + "doc_d1f7d83e0824", + "doc_d6ad61a79371", + "doc_eeed52120ccf", + "doc_f12bbbc027a7", + "doc_f54217a0f2cf", + "doc_fdf48d427fc0" + ], + "batch-level": [ + "doc_0ac01bce8c79" + ], + "batched": [ + "doc_61fd9f682847" + ], + "batches": [ + "doc_618e34441fa7", + "doc_fdf48d427fc0" + ], + "because": [ + "doc_bbc97cbff254" + ], + "become": [ + "doc_1e46cbc6bfae", + "doc_64090a20f830", + "doc_f12bbbc027a7" + ], + "bedrock": [ + "doc_f1e32ed2ffce" + ], + "been": [ + "doc_4a1cf8a90e83", + "doc_618e34441fa7" + ], + "before": [ + "doc_0383dbf277ba", + "doc_07174a8633ac", + "doc_0ac01bce8c79", + "doc_136d7a909d51", + "doc_18d38fdacc9e", + "doc_3ae3aaf0c12c", + "doc_3eeb75d383b8", + "doc_43ad71a4ad0c", + "doc_464a2b4a8d35", + "doc_4a1cf8a90e83", + "doc_5ee8bbdbd8a8", + "doc_618e34441fa7", + "doc_745f751f1344", + "doc_9e9118ed2141", + "doc_a24b92541657", + "doc_c346f13ce597", + "doc_d147a81bac29", + "doc_d6ad61a79371", + "doc_deb460ffa5ee", + "doc_e67e762fb6ab", + "doc_f18f523b6ecc", + "doc_f2ae50c638a4", + "doc_f54217a0f2cf", + "doc_f5f256549d84" + ], + "beginner": [ + "doc_a5120de838fb" + ], + "beginning": [ + "doc_f54217a0f2cf" + ], + "begins": [ + "doc_0ac01bce8c79", + "doc_f54217a0f2cf" + ], + "behave": [ + "doc_64090a20f830", + "doc_e34fe24f9585" + ], + "behaves": [ + "doc_2798948c3080", + "doc_e67e762fb6ab" + ], + "behavior": [ + "doc_0ac01bce8c79", + "doc_109157c0d2d7", + "doc_136d7a909d51", + "doc_18d38fdacc9e", + "doc_1e46cbc6bfae", + "doc_2798948c3080", + "doc_2a18de009d56", + "doc_3ae3aaf0c12c", + "doc_3eeb75d383b8", + "doc_3f1383f72c5f", + "doc_4055659fe573", + "doc_4a1cf8a90e83", + "doc_4e1ebaf40267", + "doc_50ac650fd8f6", + "doc_5a65ddab1445", + "doc_5ee8bbdbd8a8", + "doc_618e34441fa7", + "doc_61fd9f682847", + "doc_745f751f1344", + "doc_75887f91c7df", + "doc_78a8125d2c7e", + "doc_a24b92541657", + "doc_a2e84472f1f9", + "doc_bbc97cbff254", + "doc_c346f13ce597", + "doc_c93c115aeb85", + "doc_d147a81bac29", + "doc_d6ad61a79371", + "doc_e67e762fb6ab", + "doc_eeed52120ccf", + "doc_f18f523b6ecc" + ], + "behavior-first": [ + "doc_3ae3aaf0c12c" + ], + "behavioral": [ + "doc_18d38fdacc9e", + "doc_2798948c3080", + "doc_618e34441fa7", + "doc_78a8125d2c7e" + ], + "behaviors": [ + "doc_50ac650fd8f6", + "doc_618e34441fa7", + "doc_d147a81bac29", + "doc_f18f523b6ecc" + ], + "behind": [ + "doc_1c5f37ea0ad3" + ], + "being": [ + "doc_136d7a909d51", + "doc_b6088784caab", + "doc_d147a81bac29" + ], + "below": [ + "doc_4a1cf8a90e83", + "doc_5ee8bbdbd8a8", + "doc_61fd9f682847" + ], + "best": [ + "doc_067033cfc945", + "doc_2798948c3080", + "doc_57e2139f0873", + "doc_78a8125d2c7e", + "doc_9e9118ed2141", + "doc_a24b92541657", + "doc_b6088784caab", + "doc_e67e762fb6ab", + "doc_e7d508371e5c", + "doc_eeed52120ccf", + "doc_fdf48d427fc0" + ], + "best-effort": [ + "doc_a2e84472f1f9", + "doc_e7d508371e5c" + ], + "better": [ + "doc_0383dbf277ba", + "doc_3ae3aaf0c12c", + "doc_4055659fe573", + "doc_eeed52120ccf" + ], + "between": [ + "doc_0383dbf277ba", + "doc_067033cfc945", + "doc_136d7a909d51", + "doc_18d38fdacc9e", + "doc_1c5f37ea0ad3", + "doc_43ad71a4ad0c", + "doc_4a1cf8a90e83", + "doc_4e1ebaf40267", + "doc_50ac650fd8f6", + "doc_e34fe24f9585", + "doc_e8794369a1a4", + "doc_f1e32ed2ffce", + "doc_f54217a0f2cf" + ], + "beyond": [ + "doc_0383dbf277ba", + "doc_1e46cbc6bfae", + "doc_464a2b4a8d35", + "doc_4a1cf8a90e83", + "doc_745f751f1344", + "doc_a24b92541657" + ], + "bgtool": [ + "doc_9e9118ed2141" + ], + "bill": [ + "doc_e67e762fb6ab" + ], + "bind": [ + "doc_61fd9f682847" + ], + "block": [ + "doc_4e1ebaf40267", + "doc_a24b92541657", + "doc_d6ad61a79371" + ], + "blocked": [ + "doc_0ac01bce8c79", + "doc_618e34441fa7", + "doc_b6088784caab" + ], + "blocking": [ + "doc_1e46cbc6bfae" + ], + "blocks": [ + "doc_2798948c3080", + "doc_3aa0b3792d22", + "doc_464a2b4a8d35", + "doc_a24b92541657", + "doc_e7d508371e5c", + "doc_f18f523b6ecc" + ], + "body": [ + "doc_b6088784caab" + ], + "bool": [ + "doc_745f751f1344", + "doc_a24b92541657", + "doc_a2e84472f1f9", + "doc_e67e762fb6ab", + "doc_e7d508371e5c", + "doc_f54217a0f2cf" + ], + "boolean": [ + "doc_464a2b4a8d35" + ], + "bot": [ + "doc_64090a20f830" + ], + "both": [ + "doc_07174a8633ac", + "doc_2798948c3080", + "doc_43ad71a4ad0c", + "doc_50ac650fd8f6", + "doc_c346f13ce597", + "doc_e34fe24f9585", + "doc_fdf48d427fc0" + ], + "bots": [ + "doc_a24b92541657" + ], + "bound": [ + "doc_4462f67b1c03", + "doc_464a2b4a8d35", + "doc_f5f256549d84" + ], + "boundaries": [ + "doc_0383dbf277ba", + "doc_1c5f37ea0ad3", + "doc_2a18de009d56", + "doc_4055659fe573", + "doc_4a1cf8a90e83", + "doc_4e1ebaf40267", + "doc_567be1281e2f", + "doc_64090a20f830", + "doc_78a8125d2c7e", + "doc_c93c115aeb85", + "doc_d147a81bac29", + "doc_e67e762fb6ab", + "doc_f18f523b6ecc", + "doc_f54217a0f2cf" + ], + "boundary": [ + "doc_3eeb75d383b8", + "doc_4e1ebaf40267", + "doc_50ac650fd8f6", + "doc_567be1281e2f", + "doc_5ee8bbdbd8a8", + "doc_d147a81bac29", + "doc_f92685a34f7e" + ], + "bounded": [ + "doc_07174a8633ac", + "doc_4a1cf8a90e83", + "doc_deb460ffa5ee", + "doc_f18f523b6ecc", + "doc_f5f256549d84" + ], + "branch": [ + "doc_f54217a0f2cf" + ], + "branches": [ + "doc_f54217a0f2cf" + ], + "branching": [ + "doc_f54217a0f2cf" + ], + "breaches": [ + "doc_fdf48d427fc0" + ], + "break": [ + "doc_07174a8633ac", + "doc_18d38fdacc9e", + "doc_618e34441fa7" + ], + "breakdown": [ + "doc_745f751f1344" + ], + "breaker": [ + "doc_2a18de009d56", + "doc_4055659fe573", + "doc_4462f67b1c03", + "doc_5a65ddab1445", + "doc_5ee8bbdbd8a8", + "doc_7320ab982fab", + "doc_78a8125d2c7e", + "doc_a24b92541657", + "doc_d1f7d83e0824", + "doc_e7d508371e5c", + "doc_eeed52120ccf" + ], + "breaker_cooldown_s": [ + "doc_a24b92541657" + ], + "breaker_failure_threshold": [ + "doc_5ee8bbdbd8a8", + "doc_a24b92541657" + ], + "breakers": [ + "doc_567be1281e2f", + "doc_a24b92541657", + "doc_a5120de838fb", + "doc_d147a81bac29", + "doc_deb460ffa5ee" + ], + "breaking": [ + "doc_4e1ebaf40267", + "doc_e8794369a1a4", + "doc_f1e32ed2ffce" + ], + "breaks": [ + "doc_618e34441fa7" + ], + "bridge": [ + "doc_109157c0d2d7" + ], + "bridges": [ + "doc_567be1281e2f" + ], + "broadly": [ + "doc_43ad71a4ad0c" + ], + "broken": [ + "doc_4e1ebaf40267", + "doc_c93c115aeb85" + ], + "browser-based": [ + "doc_e34fe24f9585" + ], + "budget": [ + "doc_0383dbf277ba", + "doc_1c5f37ea0ad3", + "doc_2a18de009d56", + "doc_4055659fe573", + "doc_50ac650fd8f6", + "doc_5ee8bbdbd8a8", + "doc_618e34441fa7", + "doc_a5120de838fb", + "doc_c93c115aeb85", + "doc_e67e762fb6ab", + "doc_f12bbbc027a7", + "doc_fdf48d427fc0" + ], + "budget-triggered": [ + "doc_4055659fe573" + ], + "budgets": [ + "doc_1e46cbc6bfae", + "doc_2a18de009d56", + "doc_3ae3aaf0c12c", + "doc_a5120de838fb", + "doc_bbc97cbff254", + "doc_c93c115aeb85", + "doc_e67e762fb6ab", + "doc_f5f256549d84", + "doc_fdf48d427fc0" + ], + "bug": [ + "doc_618e34441fa7" + ], + "bugs": [ + "doc_50ac650fd8f6" + ], + "build": [ + "doc_067033cfc945", + "doc_2a18de009d56", + "doc_3aa0b3792d22", + "doc_3ae3aaf0c12c", + "doc_3f1383f72c5f", + "doc_3f60413b3a06", + "doc_50ac650fd8f6", + "doc_5a65ddab1445", + "doc_7b26cbd9a7ae", + "doc_9e9118ed2141", + "doc_bbc97cbff254", + "doc_deb460ffa5ee", + "doc_f18f523b6ecc", + "doc_f92685a34f7e" + ], + "build_agentic_ai_assets": [ + "doc_3eeb75d383b8" + ], + "build_project": [ + "doc_3f1383f72c5f" + ], + "build_runtime_tools": [ + "doc_464a2b4a8d35" + ], + "builder": [ + "doc_2a18de009d56", + "doc_3ae3aaf0c12c", + "doc_3eeb75d383b8", + "doc_3f60413b3a06", + "doc_7320ab982fab", + "doc_a5120de838fb", + "doc_c346f13ce597", + "doc_f18f523b6ecc", + "doc_f92685a34f7e" + ], + "building": [ + "doc_0ac01bce8c79", + "doc_136d7a909d51", + "doc_18d38fdacc9e", + "doc_3aa0b3792d22", + "doc_3ae3aaf0c12c", + "doc_464a2b4a8d35", + "doc_a5120de838fb", + "doc_b6088784caab", + "doc_d147a81bac29", + "doc_deb460ffa5ee", + "doc_f18f523b6ecc", + "doc_f54217a0f2cf" + ], + "building-with-ai": [ + "doc_3aa0b3792d22" + ], + "builds": [ + "doc_1c5f37ea0ad3", + "doc_1e46cbc6bfae", + "doc_5ee8bbdbd8a8", + "doc_a5120de838fb", + "doc_eeed52120ccf" + ], + "built": [ + "doc_4e1ebaf40267", + "doc_50ac650fd8f6" + ], + "built-in": [ + "doc_2798948c3080", + "doc_2a18de009d56", + "doc_40e30666bfa8", + "doc_5a65ddab1445", + "doc_61fd9f682847", + "doc_7320ab982fab", + "doc_a24b92541657", + "doc_a5120de838fb", + "doc_b6088784caab", + "doc_c346f13ce597", + "doc_d1f7d83e0824", + "doc_e8794369a1a4", + "doc_f1e32ed2ffce", + "doc_f92685a34f7e" + ], + "built-ins": [ + "doc_c93c115aeb85" + ], + "bundles": [ + "doc_2798948c3080", + "doc_b6088784caab" + ], + "but": [ + "doc_0383dbf277ba", + "doc_07174a8633ac", + "doc_0ac01bce8c79", + "doc_109157c0d2d7", + "doc_136d7a909d51", + "doc_3eeb75d383b8", + "doc_4055659fe573", + "doc_4a1cf8a90e83", + "doc_618e34441fa7", + "doc_a24b92541657", + "doc_d147a81bac29", + "doc_d6ad61a79371", + "doc_e67e762fb6ab", + "doc_f18f523b6ecc", + "doc_f54217a0f2cf" + ], + "buttons": [ + "doc_d6ad61a79371" + ], + "bypass": [ + "doc_e67e762fb6ab" + ], + "c38": [ + "doc_3aa0b3792d22" + ], + "cache": [ + "doc_2a18de009d56", + "doc_3f60413b3a06", + "doc_4462f67b1c03", + "doc_64090a20f830", + "doc_7320ab982fab", + "doc_d147a81bac29" + ], + "cache_backend": [ + "doc_3f60413b3a06" + ], + "cached": [ + "doc_64090a20f830", + "doc_7320ab982fab" + ], + "cachepolicy": [ + "doc_2a18de009d56" + ], + "caches": [ + "doc_64090a20f830" + ], + "caching": [ + "doc_2a18de009d56", + "doc_4462f67b1c03", + "doc_64090a20f830", + "doc_7320ab982fab", + "doc_745f751f1344", + "doc_a24b92541657", + "doc_a5120de838fb", + "doc_c346f13ce597", + "doc_d147a81bac29", + "doc_deb460ffa5ee", + "doc_e7d508371e5c", + "doc_e8794369a1a4", + "doc_f1e32ed2ffce", + "doc_f92685a34f7e" + ], + "calculations": [ + "doc_57e2139f0873", + "doc_9e9118ed2141", + "doc_b6088784caab" + ], + "call": [ + "doc_0383dbf277ba", + "doc_0ac01bce8c79", + "doc_109157c0d2d7", + "doc_136d7a909d51", + "doc_1e46cbc6bfae", + "doc_3f60413b3a06", + "doc_4055659fe573", + "doc_4a1cf8a90e83", + "doc_4e1ebaf40267", + "doc_50ac650fd8f6", + "doc_567be1281e2f", + "doc_57e2139f0873", + "doc_5ee8bbdbd8a8", + "doc_64090a20f830", + "doc_7320ab982fab", + "doc_745f751f1344", + "doc_9e9118ed2141", + "doc_a24b92541657", + "doc_c346f13ce597", + "doc_e67e762fb6ab", + "doc_e7d508371e5c", + "doc_f12bbbc027a7", + "doc_f18f523b6ecc" + ], + "call_count": [ + "doc_0ac01bce8c79" + ], + "call_next": [ + "doc_745f751f1344", + "doc_9e9118ed2141", + "doc_c346f13ce597", + "doc_f92685a34f7e" + ], + "callable": [ + "doc_a24b92541657", + "doc_c346f13ce597", + "doc_e67e762fb6ab" + ], + "callables": [ + "doc_c93c115aeb85" + ], + "callback": [ + "doc_5a65ddab1445", + "doc_a24b92541657" + ], + "callbacks": [ + "doc_5a65ddab1445", + "doc_a24b92541657" + ], + "called": [ + "doc_07174a8633ac", + "doc_136d7a909d51" + ], + "caller": [ + "doc_1c5f37ea0ad3", + "doc_78b1717fafe3", + "doc_bbc97cbff254", + "doc_e67e762fb6ab", + "doc_e7d508371e5c", + "doc_f54217a0f2cf" + ], + "caller-provided": [ + "doc_4a1cf8a90e83" + ], + "callers": [ + "doc_1c5f37ea0ad3" + ], + "calling": [ + "doc_136d7a909d51", + "doc_4a1cf8a90e83", + "doc_57e2139f0873", + "doc_7320ab982fab", + "doc_9e9118ed2141", + "doc_c346f13ce597", + "doc_f1e32ed2ffce" + ], + "calls": [ + "doc_0383dbf277ba", + "doc_07174a8633ac", + "doc_0ac01bce8c79", + "doc_136d7a909d51", + "doc_1e46cbc6bfae", + "doc_3ae3aaf0c12c", + "doc_3f1383f72c5f", + "doc_3f60413b3a06", + "doc_4462f67b1c03", + "doc_4a1cf8a90e83", + "doc_4e1ebaf40267", + "doc_50ac650fd8f6", + "doc_567be1281e2f", + "doc_5a65ddab1445", + "doc_5ee8bbdbd8a8", + "doc_745f751f1344", + "doc_78a8125d2c7e", + "doc_7b26cbd9a7ae", + "doc_9e9118ed2141", + "doc_a24b92541657", + "doc_a2e84472f1f9", + "doc_b6088784caab", + "doc_bf91f8d5da74", + "doc_c346f13ce597", + "doc_d1f7d83e0824", + "doc_d6ad61a79371", + "doc_deb460ffa5ee", + "doc_e67e762fb6ab", + "doc_e7d508371e5c", + "doc_e8794369a1a4", + "doc_f12bbbc027a7", + "doc_f18f523b6ecc", + "doc_f54217a0f2cf", + "doc_f5f256549d84" + ], + "camelcase": [ + "doc_64090a20f830" + ], + "can": [ + "doc_0383dbf277ba", + "doc_07174a8633ac", + "doc_0ac01bce8c79", + "doc_136d7a909d51", + "doc_1c5f37ea0ad3", + "doc_2798948c3080", + "doc_3f1383f72c5f", + "doc_3f60413b3a06", + "doc_4055659fe573", + "doc_40e30666bfa8", + "doc_464a2b4a8d35", + "doc_4a1cf8a90e83", + "doc_4e1ebaf40267", + "doc_57e2139f0873", + "doc_5ee8bbdbd8a8", + "doc_618e34441fa7", + "doc_64090a20f830", + "doc_745f751f1344", + "doc_75887f91c7df", + "doc_9e9118ed2141", + "doc_a24b92541657", + "doc_a2e84472f1f9", + "doc_b6088784caab", + "doc_c346f13ce597", + "doc_c93c115aeb85", + "doc_d6ad61a79371", + "doc_e34fe24f9585", + "doc_e67e762fb6ab", + "doc_eeed52120ccf", + "doc_f12bbbc027a7", + "doc_f5f256549d84" + ], + "cancel": [ + "doc_109157c0d2d7", + "doc_7b26cbd9a7ae", + "doc_f12bbbc027a7" + ], + "cancellation": [ + "doc_f2ae50c638a4" + ], + "cancellations": [ + "doc_07174a8633ac" + ], + "cancelled": [ + "doc_1e46cbc6bfae", + "doc_bf91f8d5da74", + "doc_e7d508371e5c", + "doc_f54217a0f2cf" + ], + "cannot": [ + "doc_4a1cf8a90e83", + "doc_9e9118ed2141" + ], + "canonical": [ + "doc_deb460ffa5ee" + ], + "cap": [ + "doc_a24b92541657" + ], + "capabilities": [ + "doc_0383dbf277ba", + "doc_18d38fdacc9e", + "doc_43ad71a4ad0c", + "doc_464a2b4a8d35", + "doc_9e9118ed2141", + "doc_a5120de838fb", + "doc_b6088784caab", + "doc_d147a81bac29", + "doc_deb460ffa5ee", + "doc_e67e762fb6ab", + "doc_f18f523b6ecc" + ], + "capability": [ + "doc_2a18de009d56", + "doc_57e2139f0873", + "doc_745f751f1344", + "doc_a5120de838fb", + "doc_eeed52120ccf", + "doc_f12bbbc027a7", + "doc_f1e32ed2ffce" + ], + "capable": [ + "doc_a24b92541657" + ], + "capture": [ + "doc_136d7a909d51", + "doc_618e34441fa7", + "doc_78a8125d2c7e" + ], + "captured": [ + "doc_0ac01bce8c79", + "doc_18d38fdacc9e", + "doc_618e34441fa7" + ], + "captures": [ + "doc_0ac01bce8c79", + "doc_4a1cf8a90e83", + "doc_5ee8bbdbd8a8", + "doc_78a8125d2c7e" + ], + "care": [ + "doc_e34fe24f9585" + ], + "carries": [ + "doc_0ac01bce8c79", + "doc_4a1cf8a90e83", + "doc_745f751f1344", + "doc_f54217a0f2cf" + ], + "carry": [ + "doc_4a1cf8a90e83" + ], + "carrying": [ + "doc_745f751f1344" + ], + "cascading": [ + "doc_5ee8bbdbd8a8", + "doc_7320ab982fab" + ], + "case": [ + "doc_3f60413b3a06", + "doc_4055659fe573", + "doc_4a1cf8a90e83", + "doc_4e1ebaf40267", + "doc_50ac650fd8f6", + "doc_a24b92541657", + "doc_c93c115aeb85", + "doc_e34fe24f9585", + "doc_f5f256549d84" + ], + "cases": [ + "doc_18d38fdacc9e", + "doc_bbc97cbff254", + "doc_c93c115aeb85" + ], + "cat": [ + "doc_a24b92541657" + ], + "catalog": [ + "doc_a5120de838fb" + ], + "catch": [ + "doc_50ac650fd8f6" + ], + "catches": [ + "doc_745f751f1344" + ], + "categories": [ + "doc_4055659fe573", + "doc_618e34441fa7" + ], + "categorize": [ + "doc_18d38fdacc9e" + ], + "category": [ + "doc_18d38fdacc9e", + "doc_618e34441fa7" + ], + "caught": [ + "doc_745f751f1344" + ], + "cause": [ + "doc_2798948c3080", + "doc_4a1cf8a90e83", + "doc_618e34441fa7", + "doc_75887f91c7df" + ], + "causes": [ + "doc_136d7a909d51", + "doc_4055659fe573", + "doc_4e1ebaf40267", + "doc_50ac650fd8f6" + ], + "cd": [ + "doc_d6ad61a79371" + ], + "ceiling": [ + "doc_57e2139f0873", + "doc_61fd9f682847", + "doc_a24b92541657", + "doc_fdf48d427fc0" + ], + "chain": [ + "doc_2798948c3080", + "doc_4a1cf8a90e83", + "doc_5a65ddab1445", + "doc_5ee8bbdbd8a8", + "doc_64090a20f830", + "doc_7320ab982fab", + "doc_745f751f1344", + "doc_75887f91c7df", + "doc_a5120de838fb", + "doc_c346f13ce597", + "doc_d1f7d83e0824", + "doc_f54217a0f2cf" + ], + "chained": [ + "doc_3f60413b3a06" + ], + "chains": [ + "doc_618e34441fa7", + "doc_7320ab982fab", + "doc_a5120de838fb", + "doc_d1f7d83e0824", + "doc_deb460ffa5ee" + ], + "change": [ + "doc_2798948c3080", + "doc_3eeb75d383b8", + "doc_618e34441fa7", + "doc_bbc97cbff254", + "doc_d147a81bac29", + "doc_f18f523b6ecc" + ], + "changed": [ + "doc_3eeb75d383b8", + "doc_618e34441fa7", + "doc_d147a81bac29", + "doc_f54217a0f2cf" + ], + "changes": [ + "doc_0383dbf277ba", + "doc_18d38fdacc9e", + "doc_2798948c3080", + "doc_3ae3aaf0c12c", + "doc_3eeb75d383b8", + "doc_5a65ddab1445", + "doc_618e34441fa7", + "doc_64090a20f830", + "doc_a24b92541657", + "doc_c93c115aeb85" + ], + "changing": [ + "doc_3eeb75d383b8", + "doc_3f1383f72c5f", + "doc_618e34441fa7", + "doc_b6088784caab", + "doc_bbc97cbff254", + "doc_d147a81bac29", + "doc_deb460ffa5ee", + "doc_f18f523b6ecc" + ], + "channel": [ + "doc_2a18de009d56", + "doc_5ee8bbdbd8a8" + ], + "characters": [ + "doc_464a2b4a8d35", + "doc_61fd9f682847", + "doc_64090a20f830", + "doc_a24b92541657" + ], + "chars": [ + "doc_64090a20f830" + ], + "chat": [ + "doc_3f60413b3a06", + "doc_4462f67b1c03", + "doc_618e34441fa7", + "doc_78b1717fafe3", + "doc_7b26cbd9a7ae", + "doc_a5120de838fb", + "doc_bbc97cbff254", + "doc_c346f13ce597", + "doc_d1f7d83e0824", + "doc_d6ad61a79371", + "doc_e7d508371e5c", + "doc_f92685a34f7e" + ], + "chat_agent": [ + "doc_64090a20f830" + ], + "chat_timeout_s": [ + "doc_4462f67b1c03" + ], + "chatagent": [ + "doc_2a18de009d56", + "doc_64090a20f830" + ], + "chatbot": [ + "doc_deb460ffa5ee" + ], + "chatbots": [ + "doc_a24b92541657" + ], + "chatopenai": [ + "doc_5a65ddab1445" + ], + "cheap": [ + "doc_eeed52120ccf" + ], + "cheaper": [ + "doc_7320ab982fab" + ], + "cheat": [ + "doc_7320ab982fab" + ], + "check": [ + "doc_07174a8633ac", + "doc_136d7a909d51", + "doc_2798948c3080", + "doc_464a2b4a8d35", + "doc_4e1ebaf40267", + "doc_64090a20f830", + "doc_a5120de838fb", + "doc_c93c115aeb85", + "doc_d1f7d83e0824", + "doc_deb460ffa5ee", + "doc_eeed52120ccf" + ], + "checked": [ + "doc_618e34441fa7", + "doc_9e9118ed2141" + ], + "checklist": [ + "doc_067033cfc945", + "doc_18d38fdacc9e", + "doc_3eeb75d383b8", + "doc_4055659fe573", + "doc_567be1281e2f", + "doc_d147a81bac29", + "doc_f5f256549d84" + ], + "checkpoint": [ + "doc_0383dbf277ba", + "doc_07174a8633ac", + "doc_2a18de009d56", + "doc_4055659fe573", + "doc_4a1cf8a90e83", + "doc_50ac650fd8f6", + "doc_5ee8bbdbd8a8", + "doc_7320ab982fab", + "doc_a24b92541657", + "doc_a5120de838fb", + "doc_d147a81bac29", + "doc_e7d508371e5c" + ], + "checkpoint-aware": [ + "doc_4a1cf8a90e83" + ], + "checkpoint-based": [ + "doc_7b26cbd9a7ae", + "doc_a5120de838fb" + ], + "checkpoint-schema": [ + "doc_3aa0b3792d22" + ], + "checkpoint_async_writes": [ + "doc_4a1cf8a90e83", + "doc_a24b92541657" + ], + "checkpoint_coalesce_runtime_state": [ + "doc_4a1cf8a90e83", + "doc_a24b92541657" + ], + "checkpoint_flush_timeout_s": [ + "doc_4a1cf8a90e83", + "doc_a24b92541657" + ], + "checkpoint_latest_key": [ + "doc_4a1cf8a90e83" + ], + "checkpoint_queue_maxsize": [ + "doc_a24b92541657" + ], + "checkpoint_token": [ + "doc_4a1cf8a90e83", + "doc_5ee8bbdbd8a8" + ], + "checkpointing": [ + "doc_2a18de009d56", + "doc_5a65ddab1445", + "doc_a5120de838fb", + "doc_f18f523b6ecc" + ], + "checkpoints": [ + "doc_0383dbf277ba", + "doc_136d7a909d51", + "doc_4a1cf8a90e83", + "doc_4e1ebaf40267", + "doc_d147a81bac29", + "doc_deb460ffa5ee" + ], + "checks": [ + "doc_067033cfc945", + "doc_1c5f37ea0ad3", + "doc_4462f67b1c03", + "doc_464a2b4a8d35", + "doc_4a1cf8a90e83", + "doc_567be1281e2f", + "doc_75887f91c7df", + "doc_78b1717fafe3" + ], + "checksum": [ + "doc_2a18de009d56", + "doc_9e9118ed2141" + ], + "choice": [ + "doc_50ac650fd8f6" + ], + "choices": [ + "doc_f92685a34f7e" + ], + "choose": [ + "doc_0383dbf277ba", + "doc_deb460ffa5ee", + "doc_f5f256549d84", + "doc_fdf48d427fc0" + ], + "choosing": [ + "doc_0ac01bce8c79", + "doc_18d38fdacc9e", + "doc_78a8125d2c7e", + "doc_f5f256549d84" + ], + "chunked": [ + "doc_109157c0d2d7" + ], + "chunking": [ + "doc_5ee8bbdbd8a8", + "doc_eeed52120ccf" + ], + "chunks": [ + "doc_109157c0d2d7" + ], + "ci": [ + "doc_18d38fdacc9e", + "doc_3ae3aaf0c12c", + "doc_5a65ddab1445", + "doc_618e34441fa7", + "doc_c93c115aeb85", + "doc_d6ad61a79371" + ], + "ci-gated": [ + "doc_067033cfc945" + ], + "circuit": [ + "doc_2a18de009d56", + "doc_4055659fe573", + "doc_4462f67b1c03", + "doc_4e1ebaf40267", + "doc_567be1281e2f", + "doc_5a65ddab1445", + "doc_5ee8bbdbd8a8", + "doc_7320ab982fab", + "doc_78a8125d2c7e", + "doc_a24b92541657", + "doc_a5120de838fb", + "doc_d147a81bac29", + "doc_d1f7d83e0824", + "doc_deb460ffa5ee", + "doc_e7d508371e5c", + "doc_e8794369a1a4", + "doc_eeed52120ccf", + "doc_f1e32ed2ffce" + ], + "circuit-breaker": [ + "doc_618e34441fa7" + ], + "circuitbreakerpolicy": [ + "doc_2a18de009d56" + ], + "cite": [ + "doc_18d38fdacc9e" + ], + "claims": [ + "doc_1c5f37ea0ad3" + ], + "clarify": [ + "doc_136d7a909d51" + ], + "class": [ + "doc_0383dbf277ba", + "doc_2a18de009d56", + "doc_64090a20f830", + "doc_a24b92541657" + ], + "classes": [ + "doc_2a18de009d56", + "doc_3eeb75d383b8" + ], + "classification": [ + "doc_2a18de009d56", + "doc_3f60413b3a06", + "doc_4055659fe573", + "doc_4e1ebaf40267", + "doc_50ac650fd8f6", + "doc_57e2139f0873", + "doc_618e34441fa7", + "doc_78b1717fafe3", + "doc_d1f7d83e0824", + "doc_eeed52120ccf", + "doc_f12bbbc027a7" + ], + "classified": [ + "doc_4055659fe573", + "doc_50ac650fd8f6", + "doc_5ee8bbdbd8a8", + "doc_eeed52120ccf" + ], + "classifies": [ + "doc_4055659fe573", + "doc_618e34441fa7" + ], + "classify": [ + "doc_4e1ebaf40267" + ], + "classifying": [ + "doc_18d38fdacc9e" + ], + "claude": [ + "doc_f1e32ed2ffce", + "doc_f92685a34f7e" + ], + "claude-opus-4-5": [ + "doc_eeed52120ccf", + "doc_f92685a34f7e" + ], + "cleanly": [ + "doc_4462f67b1c03" + ], + "clear": [ + "doc_18d38fdacc9e", + "doc_4e1ebaf40267", + "doc_50ac650fd8f6", + "doc_57e2139f0873", + "doc_bbc97cbff254", + "doc_e67e762fb6ab" + ], + "cleared": [ + "doc_4a1cf8a90e83" + ], + "clearly": [ + "doc_43ad71a4ad0c" + ], + "cli": [ + "doc_a24b92541657", + "doc_b6088784caab", + "doc_d147a81bac29", + "doc_deb460ffa5ee", + "doc_e7d508371e5c", + "doc_f2ae50c638a4" + ], + "clicks": [ + "doc_7b26cbd9a7ae" + ], + "client": [ + "doc_1c5f37ea0ad3", + "doc_2a18de009d56", + "doc_3f60413b3a06", + "doc_4462f67b1c03", + "doc_a24b92541657", + "doc_a5120de838fb", + "doc_bbc97cbff254", + "doc_c346f13ce597", + "doc_e34fe24f9585", + "doc_e67e762fb6ab", + "doc_eeed52120ccf" + ], + "clients": [ + "doc_3f60413b3a06", + "doc_7320ab982fab", + "doc_a24b92541657", + "doc_deb460ffa5ee", + "doc_e34fe24f9585", + "doc_e67e762fb6ab", + "doc_f1e32ed2ffce", + "doc_f5f256549d84", + "doc_f92685a34f7e" + ], + "clis": [ + "doc_bbc97cbff254" + ], + "close": [ + "doc_4462f67b1c03" + ], + "coalesce": [ + "doc_a24b92541657" + ], + "coalesced": [ + "doc_4a1cf8a90e83" + ], + "code": [ + "doc_0383dbf277ba", + "doc_0ac01bce8c79", + "doc_136d7a909d51", + "doc_18d38fdacc9e", + "doc_3eeb75d383b8", + "doc_3f1383f72c5f", + "doc_4a1cf8a90e83", + "doc_4e1ebaf40267", + "doc_567be1281e2f", + "doc_5a65ddab1445", + "doc_618e34441fa7", + "doc_61fd9f682847", + "doc_64090a20f830", + "doc_a5120de838fb", + "doc_b6088784caab", + "doc_bbc97cbff254", + "doc_d1f7d83e0824", + "doc_e8794369a1a4", + "doc_f2ae50c638a4", + "doc_f54217a0f2cf", + "doc_f92685a34f7e" + ], + "codex": [ + "doc_deb460ffa5ee" + ], + "coding": [ + "doc_18d38fdacc9e", + "doc_3f1383f72c5f", + "doc_9e9118ed2141", + "doc_deb460ffa5ee", + "doc_eeed52120ccf" + ], + "collapses": [ + "doc_64090a20f830" + ], + "collected": [ + "doc_745f751f1344", + "doc_c93c115aeb85", + "doc_f12bbbc027a7" + ], + "collecting": [ + "doc_a2e84472f1f9" + ], + "collection": [ + "doc_2a18de009d56" + ], + "collectors": [ + "doc_d147a81bac29" + ], + "collects": [ + "doc_f12bbbc027a7" + ], + "colors": [ + "doc_3aa0b3792d22" + ], + "com": [ + "doc_3aa0b3792d22" + ], + "combine": [ + "doc_40e30666bfa8", + "doc_464a2b4a8d35", + "doc_7b26cbd9a7ae", + "doc_a5120de838fb", + "doc_fdf48d427fc0" + ], + "combines": [ + "doc_f12bbbc027a7" + ], + "come": [ + "doc_eeed52120ccf" + ], + "comes": [ + "doc_3eeb75d383b8", + "doc_f5f256549d84" + ], + "comma-separated": [ + "doc_61fd9f682847" + ], + "command": [ + "doc_2a18de009d56", + "doc_3eeb75d383b8", + "doc_464a2b4a8d35", + "doc_618e34441fa7", + "doc_61fd9f682847", + "doc_9e9118ed2141", + "doc_a24b92541657", + "doc_a5120de838fb", + "doc_b6088784caab" + ], + "command_timeout_s": [ + "doc_a24b92541657" + ], + "commands": [ + "doc_3eeb75d383b8", + "doc_43ad71a4ad0c", + "doc_9e9118ed2141", + "doc_a24b92541657", + "doc_b6088784caab", + "doc_d147a81bac29", + "doc_f18f523b6ecc" + ], + "commit": [ + "doc_067033cfc945" + ], + "common": [ + "doc_136d7a909d51", + "doc_18d38fdacc9e", + "doc_3ae3aaf0c12c", + "doc_464a2b4a8d35", + "doc_4a1cf8a90e83", + "doc_57e2139f0873", + "doc_5a65ddab1445", + "doc_9e9118ed2141", + "doc_bbc97cbff254", + "doc_bf91f8d5da74", + "doc_c346f13ce597", + "doc_d147a81bac29", + "doc_fdf48d427fc0" + ], + "communicate": [ + "doc_57e2139f0873", + "doc_75887f91c7df" + ], + "communicates": [ + "doc_5ee8bbdbd8a8" + ], + "communication": [ + "doc_1c5f37ea0ad3", + "doc_4e1ebaf40267", + "doc_567be1281e2f", + "doc_57e2139f0873", + "doc_75887f91c7df", + "doc_e34fe24f9585", + "doc_f92685a34f7e" + ], + "communications": [ + "doc_d6ad61a79371" + ], + "compact": [ + "doc_0383dbf277ba", + "doc_07174a8633ac", + "doc_7b26cbd9a7ae", + "doc_a5120de838fb", + "doc_e7d508371e5c", + "doc_f5f256549d84" + ], + "compact_thread": [ + "doc_07174a8633ac", + "doc_2a18de009d56", + "doc_bbc97cbff254" + ], + "compact_thread_memory": [ + "doc_2a18de009d56" + ], + "compacted": [ + "doc_07174a8633ac" + ], + "compacting": [ + "doc_07174a8633ac" + ], + "compaction": [ + "doc_0383dbf277ba", + "doc_07174a8633ac", + "doc_18d38fdacc9e", + "doc_2a18de009d56", + "doc_4e1ebaf40267", + "doc_7b26cbd9a7ae", + "doc_a5120de838fb", + "doc_bbc97cbff254", + "doc_d147a81bac29", + "doc_deb460ffa5ee" + ], + "company_name": [ + "doc_2798948c3080", + "doc_64090a20f830" + ], + "compare": [ + "doc_3ae3aaf0c12c" + ], + "compare_event_types": [ + "doc_2a18de009d56" + ], + "comparison": [ + "doc_2a18de009d56", + "doc_57e2139f0873", + "doc_f1e32ed2ffce" + ], + "compat": [ + "doc_4a1cf8a90e83" + ], + "compatibility": [ + "doc_4e1ebaf40267", + "doc_d147a81bac29", + "doc_f54217a0f2cf" + ], + "compatible": [ + "doc_745f751f1344", + "doc_9e9118ed2141", + "doc_c346f13ce597" + ], + "compiled": [ + "doc_64090a20f830" + ], + "complete": [ + "doc_3f1383f72c5f", + "doc_4a1cf8a90e83", + "doc_5a65ddab1445", + "doc_5ee8bbdbd8a8", + "doc_9e9118ed2141", + "doc_bbc97cbff254", + "doc_bf91f8d5da74", + "doc_deb460ffa5ee", + "doc_e7d508371e5c", + "doc_f12bbbc027a7", + "doc_f18f523b6ecc" + ], + "completed": [ + "doc_0383dbf277ba", + "doc_07174a8633ac", + "doc_109157c0d2d7", + "doc_1e46cbc6bfae", + "doc_4055659fe573", + "doc_5ee8bbdbd8a8", + "doc_618e34441fa7", + "doc_745f751f1344", + "doc_78a8125d2c7e", + "doc_78b1717fafe3", + "doc_a2e84472f1f9", + "doc_bf91f8d5da74", + "doc_e7d508371e5c", + "doc_f12bbbc027a7", + "doc_f54217a0f2cf" + ], + "completes": [ + "doc_0383dbf277ba", + "doc_0ac01bce8c79", + "doc_1e46cbc6bfae", + "doc_3f1383f72c5f", + "doc_4055659fe573", + "doc_78b1717fafe3", + "doc_7b26cbd9a7ae", + "doc_9e9118ed2141", + "doc_c346f13ce597" + ], + "completing": [ + "doc_136d7a909d51" + ], + "completion": [ + "doc_136d7a909d51", + "doc_3f1383f72c5f", + "doc_78a8125d2c7e", + "doc_a24b92541657", + "doc_e8794369a1a4", + "doc_f54217a0f2cf" + ], + "complex": [ + "doc_50ac650fd8f6", + "doc_57e2139f0873", + "doc_a2e84472f1f9", + "doc_d1f7d83e0824", + "doc_eeed52120ccf", + "doc_f92685a34f7e" + ], + "complexity": [ + "doc_18d38fdacc9e", + "doc_50ac650fd8f6", + "doc_57e2139f0873", + "doc_a5120de838fb" + ], + "compliance": [ + "doc_067033cfc945", + "doc_43ad71a4ad0c", + "doc_4a1cf8a90e83" + ], + "component": [ + "doc_4055659fe573" + ], + "components": [ + "doc_136d7a909d51", + "doc_50ac650fd8f6", + "doc_d6ad61a79371" + ], + "compose": [ + "doc_464a2b4a8d35" + ], + "composing": [ + "doc_464a2b4a8d35" + ], + "composition": [ + "doc_64090a20f830" + ], + "computed": [ + "doc_618e34441fa7" + ], + "concatenate": [ + "doc_2798948c3080" + ], + "concept": [ + "doc_5a65ddab1445", + "doc_f2ae50c638a4" + ], + "concepts": [ + "doc_1e46cbc6bfae", + "doc_5a65ddab1445", + "doc_a5120de838fb" + ], + "concern": [ + "doc_1c5f37ea0ad3" + ], + "concerns": [ + "doc_4e1ebaf40267", + "doc_745f751f1344", + "doc_c346f13ce597" + ], + "concise": [ + "doc_1e46cbc6bfae" + ], + "concurrency": [ + "doc_618e34441fa7", + "doc_7320ab982fab", + "doc_a2e84472f1f9", + "doc_c93c115aeb85", + "doc_e67e762fb6ab", + "doc_f12bbbc027a7" + ], + "concurrent": [ + "doc_0ac01bce8c79", + "doc_a24b92541657", + "doc_e67e762fb6ab", + "doc_f12bbbc027a7" + ], + "condition": [ + "doc_78a8125d2c7e" + ], + "conditions": [ + "doc_618e34441fa7", + "doc_c93c115aeb85" + ], + "config": [ + "doc_067033cfc945", + "doc_2a18de009d56", + "doc_5a65ddab1445", + "doc_61fd9f682847", + "doc_a24b92541657" + ], + "configs": [ + "doc_e67e762fb6ab" + ], + "configurable": [ + "doc_0ac01bce8c79", + "doc_1c5f37ea0ad3", + "doc_4055659fe573", + "doc_464a2b4a8d35", + "doc_567be1281e2f", + "doc_61fd9f682847", + "doc_745f751f1344", + "doc_78b1717fafe3", + "doc_a24b92541657", + "doc_e67e762fb6ab" + ], + "configuration": [ + "doc_067033cfc945", + "doc_136d7a909d51", + "doc_2a18de009d56", + "doc_3aa0b3792d22", + "doc_3eeb75d383b8", + "doc_3f60413b3a06", + "doc_4055659fe573", + "doc_4462f67b1c03", + "doc_4e1ebaf40267", + "doc_618e34441fa7", + "doc_61fd9f682847", + "doc_64090a20f830", + "doc_7b26cbd9a7ae", + "doc_a24b92541657", + "doc_a2e84472f1f9", + "doc_bbc97cbff254", + "doc_bf91f8d5da74", + "doc_c93c115aeb85", + "doc_d147a81bac29", + "doc_d1f7d83e0824", + "doc_deb460ffa5ee", + "doc_e67e762fb6ab", + "doc_e7d508371e5c", + "doc_fdf48d427fc0" + ], + "configuration-reference": [ + "doc_3aa0b3792d22" + ], + "configurations": [ + "doc_a5120de838fb" + ], + "configure": [ + "doc_067033cfc945", + "doc_1c5f37ea0ad3", + "doc_4055659fe573", + "doc_43ad71a4ad0c", + "doc_4462f67b1c03", + "doc_464a2b4a8d35", + "doc_567be1281e2f", + "doc_61fd9f682847", + "doc_7320ab982fab", + "doc_a2e84472f1f9", + "doc_a5120de838fb", + "doc_bbc97cbff254", + "doc_d1f7d83e0824", + "doc_e34fe24f9585", + "doc_f5f256549d84", + "doc_fdf48d427fc0" + ], + "configured": [ + "doc_0383dbf277ba", + "doc_0ac01bce8c79", + "doc_136d7a909d51", + "doc_18d38fdacc9e", + "doc_1c5f37ea0ad3", + "doc_1e46cbc6bfae", + "doc_2798948c3080", + "doc_464a2b4a8d35", + "doc_567be1281e2f", + "doc_5ee8bbdbd8a8", + "doc_61fd9f682847", + "doc_64090a20f830", + "doc_7320ab982fab", + "doc_75887f91c7df", + "doc_9e9118ed2141", + "doc_a24b92541657", + "doc_b6088784caab", + "doc_c93c115aeb85", + "doc_d6ad61a79371" + ], + "confirm": [ + "doc_618e34441fa7", + "doc_eeed52120ccf" + ], + "confirmation": [ + "doc_0ac01bce8c79" + ], + "conforms": [ + "doc_3f60413b3a06" + ], + "confused": [ + "doc_18d38fdacc9e" + ], + "confusing": [ + "doc_3eeb75d383b8" + ], + "connect": [ + "doc_40e30666bfa8", + "doc_a5120de838fb", + "doc_eeed52120ccf" + ], + "connection": [ + "doc_0383dbf277ba", + "doc_4055659fe573", + "doc_4462f67b1c03", + "doc_61fd9f682847", + "doc_78b1717fafe3", + "doc_a5120de838fb" + ], + "connections": [ + "doc_4462f67b1c03", + "doc_78b1717fafe3", + "doc_e34fe24f9585" + ], + "connects": [ + "doc_7b26cbd9a7ae" + ], + "consecutive": [ + "doc_5ee8bbdbd8a8", + "doc_64090a20f830" + ], + "consensus": [ + "doc_57e2139f0873", + "doc_f12bbbc027a7" + ], + "consider": [ + "doc_f12bbbc027a7" + ], + "considerations": [ + "doc_1c5f37ea0ad3", + "doc_64090a20f830" + ], + "consistency": [ + "doc_618e34441fa7" + ], + "consistent": [ + "doc_109157c0d2d7", + "doc_618e34441fa7", + "doc_7320ab982fab" + ], + "console": [ + "doc_50ac650fd8f6", + "doc_78a8125d2c7e" + ], + "consolerunmetricsexporter": [ + "doc_2a18de009d56" + ], + "constants": [ + "doc_2a18de009d56" + ], + "constrain": [ + "doc_18d38fdacc9e" + ], + "constraints": [ + "doc_2a18de009d56" + ], + "construct": [ + "doc_3f60413b3a06" + ], + "constructing": [ + "doc_2a18de009d56", + "doc_3f60413b3a06" + ], + "construction": [ + "doc_5ee8bbdbd8a8", + "doc_eeed52120ccf" + ], + "constructor": [ + "doc_3eeb75d383b8", + "doc_61fd9f682847", + "doc_64090a20f830", + "doc_a24b92541657", + "doc_d147a81bac29", + "doc_e67e762fb6ab" + ], + "constructors": [ + "doc_2a18de009d56", + "doc_61fd9f682847" + ], + "constructs": [ + "doc_3f60413b3a06", + "doc_5ee8bbdbd8a8", + "doc_e7d508371e5c" + ], + "consults": [ + "doc_0ac01bce8c79", + "doc_745f751f1344" + ], + "consume": [ + "doc_0383dbf277ba", + "doc_40e30666bfa8", + "doc_9e9118ed2141", + "doc_bbc97cbff254", + "doc_e34fe24f9585", + "doc_f2ae50c638a4", + "doc_f54217a0f2cf" + ], + "consumer": [ + "doc_618e34441fa7", + "doc_f54217a0f2cf" + ], + "consumers": [ + "doc_57e2139f0873", + "doc_618e34441fa7", + "doc_78b1717fafe3" + ], + "consuming": [ + "doc_1c5f37ea0ad3", + "doc_40e30666bfa8", + "doc_e34fe24f9585", + "doc_f54217a0f2cf", + "doc_fdf48d427fc0" + ], + "consumption": [ + "doc_464a2b4a8d35", + "doc_f54217a0f2cf" + ], + "contacting": [ + "doc_5ee8bbdbd8a8" + ], + "contain": [ + "doc_745f751f1344" + ], + "containing": [ + "doc_07174a8633ac", + "doc_3f1383f72c5f", + "doc_5ee8bbdbd8a8", + "doc_618e34441fa7", + "doc_e67e762fb6ab" + ], + "containment": [ + "doc_464a2b4a8d35", + "doc_64090a20f830" + ], + "contains": [ + "doc_0383dbf277ba", + "doc_07174a8633ac", + "doc_0ac01bce8c79", + "doc_1e46cbc6bfae", + "doc_3f60413b3a06", + "doc_4a1cf8a90e83", + "doc_5ee8bbdbd8a8", + "doc_a2e84472f1f9" + ], + "content": [ + "doc_0383dbf277ba", + "doc_2a18de009d56", + "doc_464a2b4a8d35", + "doc_5ee8bbdbd8a8", + "doc_618e34441fa7", + "doc_64090a20f830", + "doc_7320ab982fab", + "doc_745f751f1344", + "doc_75887f91c7df", + "doc_9e9118ed2141", + "doc_a24b92541657", + "doc_a2e84472f1f9", + "doc_b6088784caab", + "doc_c93c115aeb85", + "doc_e8794369a1a4" + ], + "contents": [ + "doc_464a2b4a8d35", + "doc_4a1cf8a90e83" + ], + "context": [ + "doc_0383dbf277ba", + "doc_07174a8633ac", + "doc_18d38fdacc9e", + "doc_1c5f37ea0ad3", + "doc_2798948c3080", + "doc_2a18de009d56", + "doc_40e30666bfa8", + "doc_4a1cf8a90e83", + "doc_5ee8bbdbd8a8", + "doc_64090a20f830", + "doc_75887f91c7df", + "doc_78b1717fafe3", + "doc_7b26cbd9a7ae", + "doc_a24b92541657", + "doc_bbc97cbff254", + "doc_e34fe24f9585", + "doc_e67e762fb6ab", + "doc_e7d508371e5c", + "doc_f2ae50c638a4", + "doc_f5f256549d84", + "doc_f92685a34f7e" + ], + "context_defaults": [ + "doc_a24b92541657", + "doc_e67e762fb6ab" + ], + "contextlengthexceeded": [ + "doc_136d7a909d51" + ], + "continue": [ + "doc_0ac01bce8c79", + "doc_136d7a909d51", + "doc_4055659fe573", + "doc_50ac650fd8f6", + "doc_745f751f1344", + "doc_9e9118ed2141", + "doc_a24b92541657", + "doc_a2e84472f1f9", + "doc_bbc97cbff254", + "doc_f12bbbc027a7" + ], + "continue_with_error": [ + "doc_a24b92541657" + ], + "continued": [ + "doc_07174a8633ac" + ], + "continues": [ + "doc_07174a8633ac", + "doc_3f1383f72c5f", + "doc_4055659fe573", + "doc_4a1cf8a90e83", + "doc_4e1ebaf40267", + "doc_9e9118ed2141", + "doc_e7d508371e5c", + "doc_f54217a0f2cf" + ], + "continuing": [ + "doc_07174a8633ac", + "doc_a24b92541657" + ], + "continuity": [ + "doc_1e46cbc6bfae", + "doc_5ee8bbdbd8a8", + "doc_7b26cbd9a7ae", + "doc_deb460ffa5ee", + "doc_e7d508371e5c", + "doc_f2ae50c638a4", + "doc_f54217a0f2cf" + ], + "continuously": [ + "doc_50ac650fd8f6" + ], + "contract": [ + "doc_3eeb75d383b8", + "doc_4e1ebaf40267", + "doc_50ac650fd8f6", + "doc_618e34441fa7", + "doc_78b1717fafe3", + "doc_bbc97cbff254", + "doc_d147a81bac29", + "doc_f18f523b6ecc", + "doc_f2ae50c638a4", + "doc_f54217a0f2cf" + ], + "contract-first": [ + "doc_50ac650fd8f6" + ], + "contracts": [ + "doc_1c5f37ea0ad3", + "doc_2a18de009d56", + "doc_3aa0b3792d22", + "doc_3eeb75d383b8", + "doc_4e1ebaf40267", + "doc_50ac650fd8f6", + "doc_5a65ddab1445", + "doc_618e34441fa7", + "doc_78b1717fafe3", + "doc_d147a81bac29", + "doc_deb460ffa5ee", + "doc_e8794369a1a4", + "doc_f1e32ed2ffce", + "doc_f92685a34f7e" + ], + "contribute": [ + "doc_4e1ebaf40267" + ], + "contributed": [ + "doc_a2e84472f1f9" + ], + "contributes": [ + "doc_eeed52120ccf" + ], + "contributor": [ + "doc_3aa0b3792d22", + "doc_f18f523b6ecc" + ], + "contributors": [ + "doc_618e34441fa7", + "doc_d147a81bac29" + ], + "control": [ + "doc_067033cfc945", + "doc_109157c0d2d7", + "doc_1c5f37ea0ad3", + "doc_2798948c3080", + "doc_3f60413b3a06", + "doc_4462f67b1c03", + "doc_50ac650fd8f6", + "doc_567be1281e2f", + "doc_7b26cbd9a7ae", + "doc_a24b92541657", + "doc_a5120de838fb", + "doc_c346f13ce597", + "doc_d1f7d83e0824", + "doc_deb460ffa5ee", + "doc_e67e762fb6ab", + "doc_e7d508371e5c", + "doc_eeed52120ccf" + ], + "control-and-session": [ + "doc_3aa0b3792d22" + ], + "controllable": [ + "doc_5a65ddab1445" + ], + "controlled": [ + "doc_07174a8633ac", + "doc_745f751f1344", + "doc_d1f7d83e0824" + ], + "controls": [ + "doc_109157c0d2d7", + "doc_3ae3aaf0c12c", + "doc_3f60413b3a06", + "doc_9e9118ed2141", + "doc_a24b92541657", + "doc_b6088784caab", + "doc_bf91f8d5da74", + "doc_deb460ffa5ee", + "doc_e67e762fb6ab", + "doc_f12bbbc027a7", + "doc_f18f523b6ecc", + "doc_f2ae50c638a4", + "doc_f5f256549d84" + ], + "convenience": [ + "doc_bbc97cbff254" + ], + "conventions": [ + "doc_4e1ebaf40267" + ], + "conversation": [ + "doc_0383dbf277ba", + "doc_07174a8633ac", + "doc_0ac01bce8c79", + "doc_136d7a909d51", + "doc_3f60413b3a06", + "doc_4a1cf8a90e83", + "doc_50ac650fd8f6", + "doc_5ee8bbdbd8a8", + "doc_745f751f1344", + "doc_75887f91c7df", + "doc_7b26cbd9a7ae", + "doc_9e9118ed2141", + "doc_bf91f8d5da74", + "doc_e7d508371e5c", + "doc_e8794369a1a4", + "doc_f2ae50c638a4" + ], + "conversation-visible": [ + "doc_0383dbf277ba" + ], + "conversation_id": [ + "doc_75887f91c7df" + ], + "conversations": [ + "doc_0383dbf277ba", + "doc_07174a8633ac", + "doc_136d7a909d51", + "doc_18d38fdacc9e", + "doc_61fd9f682847" + ], + "conversion": [ + "doc_64090a20f830" + ], + "converted": [ + "doc_64090a20f830" + ], + "converts": [ + "doc_64090a20f830", + "doc_745f751f1344" + ], + "cookbook": [ + "doc_9e9118ed2141" + ], + "cooldown": [ + "doc_5ee8bbdbd8a8", + "doc_a24b92541657" + ], + "coordinate": [ + "doc_f18f523b6ecc" + ], + "coordinated": [ + "doc_50ac650fd8f6" + ], + "coordination": [ + "doc_a5120de838fb" + ], + "coordinator": [ + "doc_18d38fdacc9e", + "doc_57e2139f0873", + "doc_a2e84472f1f9", + "doc_a5120de838fb", + "doc_e67e762fb6ab", + "doc_f12bbbc027a7" + ], + "copy": [ + "doc_64090a20f830", + "doc_f2ae50c638a4" + ], + "core": [ + "doc_07174a8633ac", + "doc_1e46cbc6bfae", + "doc_2a18de009d56", + "doc_3aa0b3792d22", + "doc_3eeb75d383b8", + "doc_3f1383f72c5f", + "doc_4e1ebaf40267", + "doc_50ac650fd8f6", + "doc_618e34441fa7", + "doc_745f751f1344", + "doc_bbc97cbff254", + "doc_d147a81bac29", + "doc_deb460ffa5ee", + "doc_f18f523b6ecc", + "doc_f2ae50c638a4" + ], + "core-runner": [ + "doc_3aa0b3792d22" + ], + "correct": [ + "doc_618e34441fa7", + "doc_c93c115aeb85", + "doc_f12bbbc027a7" + ], + "corrected": [ + "doc_0ac01bce8c79" + ], + "correctly": [ + "doc_136d7a909d51", + "doc_618e34441fa7", + "doc_c93c115aeb85" + ], + "correctness": [ + "doc_618e34441fa7" + ], + "correlation": [ + "doc_75887f91c7df" + ], + "correlation_id": [ + "doc_75887f91c7df", + "doc_f54217a0f2cf" + ], + "corresponding": [ + "doc_43ad71a4ad0c" + ], + "corrupted": [ + "doc_4a1cf8a90e83" + ], + "corruption": [ + "doc_4a1cf8a90e83" + ], + "cors": [ + "doc_61fd9f682847" + ], + "cosine": [ + "doc_0383dbf277ba" + ], + "cosine_similarity": [ + "doc_2a18de009d56" + ], + "cost": [ + "doc_067033cfc945", + "doc_136d7a909d51", + "doc_18d38fdacc9e", + "doc_1c5f37ea0ad3", + "doc_1e46cbc6bfae", + "doc_3ae3aaf0c12c", + "doc_4055659fe573", + "doc_43ad71a4ad0c", + "doc_4a1cf8a90e83", + "doc_50ac650fd8f6", + "doc_567be1281e2f", + "doc_5ee8bbdbd8a8", + "doc_7320ab982fab", + "doc_78a8125d2c7e", + "doc_a24b92541657", + "doc_a5120de838fb", + "doc_bbc97cbff254", + "doc_bf91f8d5da74", + "doc_c93c115aeb85", + "doc_d1f7d83e0824", + "doc_deb460ffa5ee", + "doc_e67e762fb6ab", + "doc_eeed52120ccf", + "doc_f18f523b6ecc", + "doc_f5f256549d84", + "doc_fdf48d427fc0" + ], + "cost-aware": [ + "doc_fdf48d427fc0" + ], + "cost-optimized": [ + "doc_d1f7d83e0824" + ], + "cost-sensitive": [ + "doc_d1f7d83e0824", + "doc_eeed52120ccf", + "doc_f92685a34f7e" + ], + "cost_monitoring": [ + "doc_3aa0b3792d22" + ], + "costs": [ + "doc_136d7a909d51", + "doc_43ad71a4ad0c", + "doc_7320ab982fab", + "doc_78a8125d2c7e", + "doc_a5120de838fb", + "doc_c93c115aeb85", + "doc_d1f7d83e0824", + "doc_fdf48d427fc0" + ], + "could": [ + "doc_4055659fe573", + "doc_618e34441fa7" + ], + "count": [ + "doc_4055659fe573", + "doc_50ac650fd8f6", + "doc_f12bbbc027a7", + "doc_f5f256549d84" + ], + "counter": [ + "doc_07174a8633ac", + "doc_4a1cf8a90e83" + ], + "counters": [ + "doc_4a1cf8a90e83", + "doc_745f751f1344", + "doc_e7d508371e5c" + ], + "counts": [ + "doc_07174a8633ac", + "doc_5ee8bbdbd8a8", + "doc_618e34441fa7", + "doc_745f751f1344", + "doc_e7d508371e5c", + "doc_e8794369a1a4", + "doc_fdf48d427fc0" + ], + "cover": [ + "doc_50ac650fd8f6", + "doc_57e2139f0873", + "doc_618e34441fa7" + ], + "coverage": [ + "doc_618e34441fa7" + ], + "covered": [ + "doc_618e34441fa7" + ], + "covering": [ + "doc_3eeb75d383b8" + ], + "covers": [ + "doc_067033cfc945", + "doc_0ac01bce8c79", + "doc_136d7a909d51", + "doc_18d38fdacc9e", + "doc_a5120de838fb" + ], + "crash": [ + "doc_0383dbf277ba", + "doc_4e1ebaf40267", + "doc_618e34441fa7" + ], + "crashed": [ + "doc_4a1cf8a90e83" + ], + "crashes": [ + "doc_4a1cf8a90e83" + ], + "create": [ + "doc_067033cfc945", + "doc_464a2b4a8d35", + "doc_618e34441fa7", + "doc_bbc97cbff254", + "doc_e7d508371e5c", + "doc_f92685a34f7e" + ], + "create_llm_cache": [ + "doc_2a18de009d56" + ], + "create_llm_client": [ + "doc_7320ab982fab" + ], + "create_llm_router": [ + "doc_2a18de009d56" + ], + "create_memory_store_from_env": [ + "doc_2a18de009d56" + ], + "create_telemetry_sink": [ + "doc_2a18de009d56" + ], + "created": [ + "doc_067033cfc945" + ], + "creates": [ + "doc_1e46cbc6bfae", + "doc_464a2b4a8d35", + "doc_75887f91c7df", + "doc_78a8125d2c7e", + "doc_d6ad61a79371" + ], + "creating": [ + "doc_1e46cbc6bfae", + "doc_2a18de009d56", + "doc_4462f67b1c03", + "doc_b6088784caab" + ], + "creation": [ + "doc_2a18de009d56", + "doc_75887f91c7df" + ], + "credentials": [ + "doc_567be1281e2f", + "doc_f1e32ed2ffce" + ], + "credits": [ + "doc_1c5f37ea0ad3", + "doc_c93c115aeb85", + "doc_e67e762fb6ab" + ], + "critical": [ + "doc_0ac01bce8c79", + "doc_4055659fe573", + "doc_4e1ebaf40267", + "doc_a24b92541657", + "doc_d6ad61a79371" + ], + "cron": [ + "doc_a24b92541657" + ], + "cross-adapter": [ + "doc_4e1ebaf40267" + ], + "cross-cutting": [ + "doc_745f751f1344", + "doc_c346f13ce597" + ], + "cross-run": [ + "doc_75887f91c7df" + ], + "cross-system": [ + "doc_57e2139f0873", + "doc_75887f91c7df" + ], + "ctx": [ + "doc_64090a20f830", + "doc_745f751f1344", + "doc_9e9118ed2141", + "doc_bbc97cbff254" + ], + "cumulative": [ + "doc_fdf48d427fc0" + ], + "curated": [ + "doc_7320ab982fab" + ], + "current": [ + "doc_4a1cf8a90e83", + "doc_50ac650fd8f6", + "doc_57e2139f0873", + "doc_64090a20f830", + "doc_d147a81bac29", + "doc_eeed52120ccf", + "doc_f54217a0f2cf" + ], + "custom": [ + "doc_0383dbf277ba", + "doc_2a18de009d56", + "doc_464a2b4a8d35", + "doc_5a65ddab1445", + "doc_5ee8bbdbd8a8", + "doc_64090a20f830", + "doc_78b1717fafe3", + "doc_a24b92541657", + "doc_d6ad61a79371", + "doc_e67e762fb6ab", + "doc_e8794369a1a4", + "doc_eeed52120ccf", + "doc_f1e32ed2ffce", + "doc_f92685a34f7e" + ], + "cycle": [ + "doc_e7d508371e5c" + ], + "dag": [ + "doc_57e2139f0873", + "doc_618e34441fa7", + "doc_f12bbbc027a7" + ], + "dags": [ + "doc_f12bbbc027a7" + ], + "daily": [ + "doc_07174a8633ac" + ], + "daily_cost": [ + "doc_78a8125d2c7e" + ], + "dangerous": [ + "doc_50ac650fd8f6", + "doc_a5120de838fb" + ], + "dark": [ + "doc_3aa0b3792d22" + ], + "dashboard": [ + "doc_78a8125d2c7e" + ], + "dashboards": [ + "doc_0ac01bce8c79", + "doc_18d38fdacc9e", + "doc_618e34441fa7", + "doc_fdf48d427fc0" + ], + "data": [ + "doc_07174a8633ac", + "doc_0ac01bce8c79", + "doc_109157c0d2d7", + "doc_43ad71a4ad0c", + "doc_4a1cf8a90e83", + "doc_4e1ebaf40267", + "doc_50ac650fd8f6", + "doc_57e2139f0873", + "doc_618e34441fa7", + "doc_75887f91c7df", + "doc_9e9118ed2141", + "doc_d6ad61a79371", + "doc_f54217a0f2cf" + ], + "database": [ + "doc_0383dbf277ba", + "doc_067033cfc945", + "doc_61fd9f682847", + "doc_b6088784caab" + ], + "databases": [ + "doc_067033cfc945", + "doc_2a18de009d56", + "doc_9e9118ed2141" + ], + "dataclass": [ + "doc_1e46cbc6bfae", + "doc_2a18de009d56", + "doc_5ee8bbdbd8a8", + "doc_f54217a0f2cf" + ], + "dataclasses": [ + "doc_3eeb75d383b8" + ], + "datadog": [ + "doc_2a18de009d56", + "doc_78a8125d2c7e" + ], + "day": [ + "doc_50ac650fd8f6" + ], + "days": [ + "doc_4a1cf8a90e83" + ], + "db": [ + "doc_61fd9f682847" + ], + "dead": [ + "doc_f54217a0f2cf" + ], + "dead-letter": [ + "doc_57e2139f0873", + "doc_618e34441fa7", + "doc_75887f91c7df", + "doc_78b1717fafe3" + ], + "dead_letter": [ + "doc_78b1717fafe3" + ], + "dead_letters": [ + "doc_067033cfc945" + ], + "debug": [ + "doc_136d7a909d51", + "doc_3f1383f72c5f", + "doc_50ac650fd8f6", + "doc_a24b92541657" + ], + "debug_config": [ + "doc_a24b92541657" + ], + "debuggable": [ + "doc_5a65ddab1445", + "doc_618e34441fa7" + ], + "debugger": [ + "doc_3aa0b3792d22", + "doc_3f1383f72c5f", + "doc_d147a81bac29" + ], + "debugging": [ + "doc_136d7a909d51", + "doc_18d38fdacc9e", + "doc_43ad71a4ad0c", + "doc_4a1cf8a90e83", + "doc_5ee8bbdbd8a8", + "doc_618e34441fa7", + "doc_75887f91c7df", + "doc_78a8125d2c7e", + "doc_f18f523b6ecc", + "doc_f54217a0f2cf" + ], + "decide": [ + "doc_0ac01bce8c79", + "doc_4e1ebaf40267", + "doc_5ee8bbdbd8a8", + "doc_745f751f1344", + "doc_78b1717fafe3", + "doc_b6088784caab" + ], + "decides": [ + "doc_4e1ebaf40267", + "doc_50ac650fd8f6", + "doc_a2e84472f1f9", + "doc_b6088784caab", + "doc_d6ad61a79371", + "doc_f12bbbc027a7" + ], + "deciding": [ + "doc_a24b92541657" + ], + "decision": [ + "doc_0ac01bce8c79", + "doc_4055659fe573", + "doc_50ac650fd8f6", + "doc_57e2139f0873", + "doc_618e34441fa7", + "doc_745f751f1344", + "doc_a24b92541657", + "doc_d6ad61a79371", + "doc_e7d508371e5c" + ], + "decisions": [ + "doc_0ac01bce8c79", + "doc_43ad71a4ad0c", + "doc_618e34441fa7", + "doc_a24b92541657", + "doc_e67e762fb6ab", + "doc_f12bbbc027a7", + "doc_f54217a0f2cf" + ], + "declarations": [ + "doc_2a18de009d56" + ], + "declarative": [ + "doc_2a18de009d56", + "doc_deb460ffa5ee", + "doc_f2ae50c638a4" + ], + "declare": [ + "doc_0383dbf277ba" + ], + "declared": [ + "doc_745f751f1344" + ], + "declares": [ + "doc_0383dbf277ba" + ], + "decorate": [ + "doc_745f751f1344" + ], + "decorated": [ + "doc_745f751f1344" + ], + "decorator": [ + "doc_2a18de009d56", + "doc_3eeb75d383b8", + "doc_464a2b4a8d35", + "doc_5a65ddab1445", + "doc_745f751f1344", + "doc_9e9118ed2141", + "doc_a24b92541657", + "doc_bbc97cbff254" + ], + "decorators": [ + "doc_d147a81bac29" + ], + "decouple": [ + "doc_57e2139f0873", + "doc_78b1717fafe3" + ], + "deduplicates": [ + "doc_64090a20f830" + ], + "deduplication": [ + "doc_5ee8bbdbd8a8", + "doc_75887f91c7df" + ], + "deep": [ + "doc_3eeb75d383b8", + "doc_3f1383f72c5f", + "doc_a24b92541657" + ], + "deeper": [ + "doc_f5f256549d84" + ], + "def": [ + "doc_9e9118ed2141" + ], + "default": [ + "doc_0ac01bce8c79", + "doc_2798948c3080", + "doc_3aa0b3792d22", + "doc_4055659fe573", + "doc_4462f67b1c03", + "doc_464a2b4a8d35", + "doc_4a1cf8a90e83", + "doc_567be1281e2f", + "doc_61fd9f682847", + "doc_64090a20f830", + "doc_7320ab982fab", + "doc_745f751f1344", + "doc_75887f91c7df", + "doc_78b1717fafe3", + "doc_9e9118ed2141", + "doc_a24b92541657", + "doc_a2e84472f1f9", + "doc_b6088784caab", + "doc_bbc97cbff254", + "doc_d6ad61a79371", + "doc_e67e762fb6ab", + "doc_eeed52120ccf", + "doc_f54217a0f2cf" + ], + "default_allowlisted_commands": [ + "doc_a24b92541657" + ], + "default_sandbox_profile": [ + "doc_a24b92541657" + ], + "default_timeout": [ + "doc_745f751f1344" + ], + "default_timeout_s": [ + "doc_4462f67b1c03" + ], + "defaults": [ + "doc_1e46cbc6bfae", + "doc_2a18de009d56", + "doc_464a2b4a8d35", + "doc_4a1cf8a90e83", + "doc_567be1281e2f", + "doc_61fd9f682847", + "doc_a24b92541657", + "doc_bbc97cbff254", + "doc_d147a81bac29", + "doc_deb460ffa5ee", + "doc_e67e762fb6ab", + "doc_f18f523b6ecc" + ], + "defense": [ + "doc_43ad71a4ad0c", + "doc_464a2b4a8d35", + "doc_64090a20f830", + "doc_fdf48d427fc0" + ], + "defenses": [ + "doc_fdf48d427fc0" + ], + "defer": [ + "doc_0ac01bce8c79", + "doc_50ac650fd8f6", + "doc_745f751f1344", + "doc_a24b92541657" + ], + "deferred": [ + "doc_109157c0d2d7", + "doc_9e9118ed2141", + "doc_a24b92541657", + "doc_f54217a0f2cf" + ], + "define": [ + "doc_2798948c3080", + "doc_4e1ebaf40267", + "doc_745f751f1344", + "doc_78b1717fafe3", + "doc_a5120de838fb", + "doc_b6088784caab", + "doc_bbc97cbff254", + "doc_bf91f8d5da74", + "doc_c346f13ce597", + "doc_d1f7d83e0824", + "doc_deb460ffa5ee", + "doc_e67e762fb6ab" + ], + "defined": [ + "doc_50ac650fd8f6" + ], + "defines": [ + "doc_1e46cbc6bfae", + "doc_78b1717fafe3", + "doc_a24b92541657", + "doc_c346f13ce597" + ], + "defining": [ + "doc_1e46cbc6bfae" + ], + "definition": [ + "doc_07174a8633ac", + "doc_1e46cbc6bfae", + "doc_2a18de009d56", + "doc_4e1ebaf40267", + "doc_745f751f1344" + ], + "definitions": [ + "doc_2a18de009d56", + "doc_43ad71a4ad0c", + "doc_50ac650fd8f6", + "doc_5ee8bbdbd8a8", + "doc_a24b92541657", + "doc_d147a81bac29", + "doc_deb460ffa5ee" + ], + "degradation": [ + "doc_0ac01bce8c79" + ], + "degrade": [ + "doc_0ac01bce8c79", + "doc_5ee8bbdbd8a8", + "doc_745f751f1344", + "doc_a24b92541657" + ], + "degraded": [ + "doc_0ac01bce8c79", + "doc_1e46cbc6bfae", + "doc_4055659fe573", + "doc_4a1cf8a90e83", + "doc_4e1ebaf40267", + "doc_5ee8bbdbd8a8", + "doc_618e34441fa7", + "doc_a24b92541657", + "doc_bf91f8d5da74", + "doc_e7d508371e5c", + "doc_f5f256549d84", + "doc_fdf48d427fc0" + ], + "delay": [ + "doc_61fd9f682847", + "doc_7320ab982fab" + ], + "delays": [ + "doc_7320ab982fab" + ], + "delegate": [ + "doc_50ac650fd8f6", + "doc_a24b92541657", + "doc_a2e84472f1f9", + "doc_a5120de838fb", + "doc_e67e762fb6ab", + "doc_f12bbbc027a7" + ], + "delegates": [ + "doc_57e2139f0873", + "doc_e67e762fb6ab" + ], + "delegating": [ + "doc_a2e84472f1f9" + ], + "delegation": [ + "doc_1e46cbc6bfae", + "doc_2a18de009d56", + "doc_3f60413b3a06", + "doc_57e2139f0873", + "doc_618e34441fa7", + "doc_a2e84472f1f9", + "doc_a5120de838fb", + "doc_e67e762fb6ab", + "doc_f12bbbc027a7" + ], + "delegationedge": [ + "doc_2a18de009d56" + ], + "delegationengine": [ + "doc_2a18de009d56" + ], + "delegationnode": [ + "doc_2a18de009d56" + ], + "delegationplan": [ + "doc_2a18de009d56" + ], + "delegationplanner": [ + "doc_2a18de009d56" + ], + "delegationresult": [ + "doc_2a18de009d56" + ], + "delegationscheduler": [ + "doc_2a18de009d56" + ], + "delete_": [ + "doc_43ad71a4ad0c" + ], + "delete_resource": [ + "doc_43ad71a4ad0c" + ], + "deletes": [ + "doc_43ad71a4ad0c" + ], + "deleting": [ + "doc_d6ad61a79371" + ], + "deliberate": [ + "doc_618e34441fa7" + ], + "deliver": [ + "doc_0ac01bce8c79" + ], + "delivered": [ + "doc_75887f91c7df" + ], + "deliveries": [ + "doc_75887f91c7df" + ], + "delivery": [ + "doc_2a18de009d56", + "doc_4e1ebaf40267", + "doc_61fd9f682847", + "doc_75887f91c7df", + "doc_c346f13ce597", + "doc_d147a81bac29" + ], + "deliverystore": [ + "doc_75887f91c7df" + ], + "delta": [ + "doc_618e34441fa7", + "doc_f54217a0f2cf" + ], + "deltas": [ + "doc_109157c0d2d7", + "doc_5ee8bbdbd8a8", + "doc_618e34441fa7", + "doc_eeed52120ccf" + ], + "demand": [ + "doc_2798948c3080" + ], + "demo": [ + "doc_deb460ffa5ee" + ], + "demonstrates": [ + "doc_07174a8633ac", + "doc_1e46cbc6bfae", + "doc_40e30666bfa8", + "doc_43ad71a4ad0c", + "doc_4462f67b1c03", + "doc_464a2b4a8d35", + "doc_64090a20f830", + "doc_7b26cbd9a7ae", + "doc_a5120de838fb", + "doc_c346f13ce597", + "doc_d1f7d83e0824", + "doc_fdf48d427fc0" + ], + "denial": [ + "doc_0ac01bce8c79", + "doc_4055659fe573", + "doc_5ee8bbdbd8a8", + "doc_d6ad61a79371" + ], + "denials": [ + "doc_0ac01bce8c79" + ], + "denied": [ + "doc_0ac01bce8c79", + "doc_4055659fe573", + "doc_567be1281e2f", + "doc_a24b92541657", + "doc_d6ad61a79371" + ], + "denied_paths": [ + "doc_a24b92541657" + ], + "denies": [ + "doc_5ee8bbdbd8a8" + ], + "deny": [ + "doc_0ac01bce8c79", + "doc_43ad71a4ad0c", + "doc_50ac650fd8f6", + "doc_567be1281e2f", + "doc_745f751f1344", + "doc_9e9118ed2141", + "doc_a24b92541657", + "doc_d6ad61a79371", + "doc_e67e762fb6ab" + ], + "deny_shell_operators": [ + "doc_a24b92541657" + ], + "depend": [ + "doc_3ae3aaf0c12c", + "doc_618e34441fa7" + ], + "dependencies": [ + "doc_2a18de009d56", + "doc_4e1ebaf40267", + "doc_618e34441fa7" + ], + "dependent": [ + "doc_618e34441fa7" + ], + "depending": [ + "doc_4a1cf8a90e83", + "doc_618e34441fa7" + ], + "depends": [ + "doc_0ac01bce8c79", + "doc_f12bbbc027a7" + ], + "deploying": [ + "doc_067033cfc945", + "doc_136d7a909d51" + ], + "deployment": [ + "doc_067033cfc945", + "doc_1c5f37ea0ad3", + "doc_3aa0b3792d22", + "doc_4462f67b1c03", + "doc_b6088784caab", + "doc_deb460ffa5ee", + "doc_e67e762fb6ab", + "doc_f2ae50c638a4", + "doc_f5f256549d84" + ], + "deployments": [ + "doc_0383dbf277ba", + "doc_067033cfc945", + "doc_4462f67b1c03", + "doc_618e34441fa7", + "doc_78a8125d2c7e", + "doc_78b1717fafe3", + "doc_f1e32ed2ffce", + "doc_f5f256549d84" + ], + "depth": [ + "doc_067033cfc945", + "doc_a24b92541657", + "doc_e7d508371e5c" + ], + "dequeue": [ + "doc_2a18de009d56" + ], + "derive_auto_prompt_filename": [ + "doc_64090a20f830" + ], + "derived": [ + "doc_4a1cf8a90e83", + "doc_64090a20f830", + "doc_7320ab982fab", + "doc_75887f91c7df" + ], + "describe": [ + "doc_43ad71a4ad0c", + "doc_618e34441fa7", + "doc_f18f523b6ecc", + "doc_f54217a0f2cf" + ], + "described": [ + "doc_5ee8bbdbd8a8" + ], + "describes": [ + "doc_e67e762fb6ab", + "doc_f54217a0f2cf" + ], + "description": [ + "doc_07174a8633ac", + "doc_0ac01bce8c79", + "doc_1e46cbc6bfae", + "doc_43ad71a4ad0c", + "doc_4462f67b1c03", + "doc_464a2b4a8d35", + "doc_4a1cf8a90e83", + "doc_618e34441fa7", + "doc_61fd9f682847", + "doc_64090a20f830", + "doc_7320ab982fab", + "doc_745f751f1344", + "doc_78a8125d2c7e", + "doc_a24b92541657", + "doc_a2e84472f1f9", + "doc_b6088784caab", + "doc_e7d508371e5c", + "doc_f54217a0f2cf" + ], + "descriptions": [ + "doc_136d7a909d51" + ], + "descriptive": [ + "doc_618e34441fa7" + ], + "deserialized": [ + "doc_4a1cf8a90e83" + ], + "design": [ + "doc_0383dbf277ba", + "doc_2798948c3080", + "doc_3ae3aaf0c12c", + "doc_4e1ebaf40267", + "doc_50ac650fd8f6", + "doc_57e2139f0873", + "doc_5a65ddab1445", + "doc_64090a20f830", + "doc_b6088784caab", + "doc_c346f13ce597", + "doc_e67e762fb6ab", + "doc_f18f523b6ecc" + ], + "designed": [ + "doc_2a18de009d56", + "doc_464a2b4a8d35", + "doc_745f751f1344", + "doc_d6ad61a79371", + "doc_f18f523b6ecc", + "doc_f2ae50c638a4" + ], + "destructive": [ + "doc_43ad71a4ad0c", + "doc_567be1281e2f", + "doc_a5120de838fb", + "doc_d6ad61a79371" + ], + "detail": [ + "doc_3f1383f72c5f", + "doc_464a2b4a8d35", + "doc_4e1ebaf40267", + "doc_5ee8bbdbd8a8", + "doc_bbc97cbff254" + ], + "detailed": [ + "doc_136d7a909d51", + "doc_3f1383f72c5f", + "doc_c346f13ce597" + ], + "details": [ + "doc_136d7a909d51", + "doc_2a18de009d56", + "doc_618e34441fa7", + "doc_61fd9f682847", + "doc_b6088784caab", + "doc_c93c115aeb85", + "doc_deb460ffa5ee", + "doc_f92685a34f7e" + ], + "detected": [ + "doc_64090a20f830" + ], + "detection": [ + "doc_18d38fdacc9e" + ], + "detects": [ + "doc_3f1383f72c5f" + ], + "determine": [ + "doc_136d7a909d51" + ], + "determines": [ + "doc_0ac01bce8c79", + "doc_5ee8bbdbd8a8", + "doc_d1f7d83e0824" + ], + "deterministic": [ + "doc_0ac01bce8c79", + "doc_2a18de009d56", + "doc_618e34441fa7", + "doc_64090a20f830", + "doc_75887f91c7df", + "doc_a24b92541657", + "doc_c93c115aeb85" + ], + "dev": [ + "doc_0383dbf277ba" + ], + "developer": [ + "doc_f18f523b6ecc" + ], + "developer-guide": [ + "doc_3aa0b3792d22" + ], + "developers": [ + "doc_bbc97cbff254" + ], + "development": [ + "doc_0383dbf277ba", + "doc_067033cfc945", + "doc_1e46cbc6bfae", + "doc_2a18de009d56", + "doc_3f60413b3a06", + "doc_64090a20f830", + "doc_7320ab982fab", + "doc_78a8125d2c7e", + "doc_78b1717fafe3", + "doc_b6088784caab" + ], + "diagnosable": [ + "doc_3ae3aaf0c12c" + ], + "diagnosing": [ + "doc_5ee8bbdbd8a8" + ], + "diagnostics": [ + "doc_3f1383f72c5f" + ], + "diagram": [ + "doc_5ee8bbdbd8a8" + ], + "dict": [ + "doc_07174a8633ac", + "doc_0ac01bce8c79", + "doc_2798948c3080", + "doc_43ad71a4ad0c", + "doc_4a1cf8a90e83", + "doc_5ee8bbdbd8a8", + "doc_64090a20f830", + "doc_745f751f1344", + "doc_75887f91c7df", + "doc_9e9118ed2141", + "doc_a24b92541657", + "doc_e67e762fb6ab", + "doc_e7d508371e5c", + "doc_f54217a0f2cf" + ], + "dictionary": [ + "doc_3f60413b3a06", + "doc_464a2b4a8d35", + "doc_64090a20f830", + "doc_c346f13ce597" + ], + "difference": [ + "doc_5a65ddab1445" + ], + "differences": [ + "doc_43ad71a4ad0c", + "doc_5a65ddab1445", + "doc_e8794369a1a4" + ], + "different": [ + "doc_0ac01bce8c79", + "doc_136d7a909d51", + "doc_1c5f37ea0ad3", + "doc_3ae3aaf0c12c", + "doc_43ad71a4ad0c", + "doc_4a1cf8a90e83", + "doc_57e2139f0873", + "doc_618e34441fa7", + "doc_64090a20f830", + "doc_a2e84472f1f9", + "doc_c346f13ce597", + "doc_d1f7d83e0824", + "doc_e67e762fb6ab", + "doc_f12bbbc027a7" + ], + "differently": [ + "doc_5ee8bbdbd8a8" + ], + "diffs": [ + "doc_2798948c3080" + ], + "dimension": [ + "doc_07174a8633ac", + "doc_61fd9f682847" + ], + "dimensions": [ + "doc_07174a8633ac" + ], + "direct": [ + "doc_a5120de838fb", + "doc_f18f523b6ecc", + "doc_f1e32ed2ffce" + ], + "direct_llm_structured_output": [ + "doc_3aa0b3792d22" + ], + "directed": [ + "doc_f12bbbc027a7" + ], + "directly": [ + "doc_2798948c3080", + "doc_3f1383f72c5f", + "doc_3f60413b3a06", + "doc_40e30666bfa8", + "doc_4a1cf8a90e83", + "doc_61fd9f682847", + "doc_64090a20f830", + "doc_78a8125d2c7e", + "doc_9e9118ed2141", + "doc_a24b92541657", + "doc_a5120de838fb", + "doc_f12bbbc027a7", + "doc_f92685a34f7e" + ], + "directories": [ + "doc_464a2b4a8d35", + "doc_61fd9f682847", + "doc_b6088784caab" + ], + "directory": [ + "doc_2798948c3080", + "doc_2a18de009d56", + "doc_464a2b4a8d35", + "doc_61fd9f682847", + "doc_64090a20f830", + "doc_9e9118ed2141", + "doc_a24b92541657", + "doc_a5120de838fb", + "doc_b6088784caab", + "doc_e67e762fb6ab" + ], + "directory-scoped": [ + "doc_9e9118ed2141" + ], + "disable": [ + "doc_b6088784caab" + ], + "discipline": [ + "doc_3ae3aaf0c12c" + ], + "disconnects": [ + "doc_136d7a909d51" + ], + "discord": [ + "doc_2a18de009d56" + ], + "discover": [ + "doc_40e30666bfa8", + "doc_a5120de838fb", + "doc_b6088784caab", + "doc_e34fe24f9585" + ], + "discovery": [ + "doc_1c5f37ea0ad3", + "doc_e67e762fb6ab" + ], + "disk": [ + "doc_64090a20f830" + ], + "dispatch": [ + "doc_2a18de009d56", + "doc_3f60413b3a06", + "doc_745f751f1344", + "doc_78b1717fafe3", + "doc_f18f523b6ecc" + ], + "dispatches": [ + "doc_618e34441fa7", + "doc_a2e84472f1f9" + ], + "dispatching": [ + "doc_a2e84472f1f9", + "doc_f54217a0f2cf" + ], + "distinct": [ + "doc_2a18de009d56", + "doc_a2e84472f1f9", + "doc_c346f13ce597" + ], + "distinction": [ + "doc_43ad71a4ad0c" + ], + "distinguish": [ + "doc_43ad71a4ad0c", + "doc_5ee8bbdbd8a8", + "doc_d147a81bac29" + ], + "distributed": [ + "doc_067033cfc945", + "doc_3ae3aaf0c12c" + ], + "distribution": [ + "doc_d147a81bac29", + "doc_deb460ffa5ee" + ], + "dive": [ + "doc_a24b92541657" + ], + "diverse": [ + "doc_18d38fdacc9e" + ], + "diversification": [ + "doc_d1f7d83e0824" + ], + "dlq": [ + "doc_57e2139f0873", + "doc_75887f91c7df", + "doc_78b1717fafe3", + "doc_d147a81bac29" + ], + "do": [ + "doc_109157c0d2d7", + "doc_2798948c3080", + "doc_2a18de009d56", + "doc_3eeb75d383b8", + "doc_4e1ebaf40267", + "doc_618e34441fa7", + "doc_7320ab982fab", + "doc_745f751f1344", + "doc_b6088784caab", + "doc_bbc97cbff254", + "doc_e67e762fb6ab", + "doc_f12bbbc027a7" + ], + "doc": [ + "doc_618e34441fa7" + ], + "docker": [ + "doc_067033cfc945", + "doc_4462f67b1c03" + ], + "docker-compose": [ + "doc_067033cfc945" + ], + "dockerfile": [ + "doc_067033cfc945" + ], + "docs": [ + "doc_3ae3aaf0c12c", + "doc_3eeb75d383b8", + "doc_3f1383f72c5f", + "doc_618e34441fa7", + "doc_9e9118ed2141", + "doc_b6088784caab", + "doc_bbc97cbff254", + "doc_d147a81bac29", + "doc_f18f523b6ecc" + ], + "docstring": [ + "doc_745f751f1344", + "doc_a24b92541657" + ], + "document": [ + "doc_3ae3aaf0c12c" + ], + "documentation": [ + "doc_136d7a909d51", + "doc_5a65ddab1445", + "doc_d147a81bac29" + ], + "documents": [ + "doc_f54217a0f2cf" + ], + "does": [ + "doc_1c5f37ea0ad3", + "doc_3f60413b3a06", + "doc_4055659fe573", + "doc_4a1cf8a90e83", + "doc_618e34441fa7", + "doc_61fd9f682847", + "doc_f12bbbc027a7", + "doc_f18f523b6ecc" + ], + "doesn": [ + "doc_136d7a909d51", + "doc_2798948c3080", + "doc_40e30666bfa8", + "doc_4e1ebaf40267", + "doc_e34fe24f9585", + "doc_e67e762fb6ab" + ], + "don": [ + "doc_0383dbf277ba", + "doc_136d7a909d51", + "doc_18d38fdacc9e", + "doc_2798948c3080", + "doc_4e1ebaf40267", + "doc_57e2139f0873", + "doc_b6088784caab", + "doc_e67e762fb6ab", + "doc_f12bbbc027a7" + ], + "done": [ + "doc_50ac650fd8f6" + ], + "down": [ + "doc_a24b92541657", + "doc_a5120de838fb" + ], + "downstream": [ + "doc_3eeb75d383b8", + "doc_618e34441fa7", + "doc_bbc97cbff254" + ], + "drain": [ + "doc_c93c115aeb85" + ], + "dramatically": [ + "doc_2798948c3080" + ], + "drift": [ + "doc_18d38fdacc9e", + "doc_50ac650fd8f6" + ], + "drivers": [ + "doc_4e1ebaf40267" + ], + "drives": [ + "doc_0ac01bce8c79" + ], + "drop_": [ + "doc_43ad71a4ad0c" + ], + "drop_table": [ + "doc_d6ad61a79371" + ], + "dropped": [ + "doc_4e1ebaf40267" + ], + "dsn": [ + "doc_61fd9f682847" + ], + "due": [ + "doc_f54217a0f2cf" + ], + "duplicate": [ + "doc_0383dbf277ba", + "doc_1c5f37ea0ad3", + "doc_4a1cf8a90e83", + "doc_75887f91c7df" + ], + "duplicates": [ + "doc_75887f91c7df" + ], + "durability": [ + "doc_4a1cf8a90e83" + ], + "durable": [ + "doc_75887f91c7df", + "doc_78b1717fafe3", + "doc_f5f256549d84" + ], + "durable-enough": [ + "doc_0383dbf277ba" + ], + "duration": [ + "doc_067033cfc945", + "doc_0ac01bce8c79", + "doc_78a8125d2c7e", + "doc_a24b92541657" + ], + "during": [ + "doc_0383dbf277ba", + "doc_1e46cbc6bfae", + "doc_4a1cf8a90e83", + "doc_64090a20f830", + "doc_78a8125d2c7e", + "doc_b6088784caab", + "doc_c93c115aeb85", + "doc_e67e762fb6ab", + "doc_f54217a0f2cf", + "doc_fdf48d427fc0" + ], + "dynamic": [ + "doc_2798948c3080", + "doc_64090a20f830", + "doc_a24b92541657", + "doc_bbc97cbff254" + ], + "dynamodb": [ + "doc_2a18de009d56" + ], + "each": [ + "doc_0383dbf277ba", + "doc_0ac01bce8c79", + "doc_18d38fdacc9e", + "doc_1c5f37ea0ad3", + "doc_2a18de009d56", + "doc_3f60413b3a06", + "doc_4a1cf8a90e83", + "doc_4e1ebaf40267", + "doc_50ac650fd8f6", + "doc_567be1281e2f", + "doc_57e2139f0873", + "doc_5ee8bbdbd8a8", + "doc_618e34441fa7", + "doc_7320ab982fab", + "doc_745f751f1344", + "doc_78a8125d2c7e", + "doc_9e9118ed2141", + "doc_a24b92541657", + "doc_a2e84472f1f9", + "doc_a5120de838fb", + "doc_b6088784caab", + "doc_c346f13ce597", + "doc_c93c115aeb85", + "doc_e67e762fb6ab", + "doc_e7d508371e5c", + "doc_eeed52120ccf", + "doc_f12bbbc027a7", + "doc_f2ae50c638a4", + "doc_f54217a0f2cf" + ], + "early": [ + "doc_136d7a909d51", + "doc_e67e762fb6ab" + ], + "easier": [ + "doc_e67e762fb6ab" + ], + "easy": [ + "doc_43ad71a4ad0c", + "doc_f54217a0f2cf" + ], + "echo": [ + "doc_a24b92541657" + ], + "edge": [ + "doc_618e34441fa7", + "doc_c93c115aeb85" + ], + "edge-based": [ + "doc_618e34441fa7" + ], + "edit": [ + "doc_2798948c3080", + "doc_64090a20f830" + ], + "editable": [ + "doc_64090a20f830" + ], + "editing": [ + "doc_d147a81bac29" + ], + "effect": [ + "doc_0383dbf277ba", + "doc_2a18de009d56", + "doc_4a1cf8a90e83", + "doc_d6ad61a79371" + ], + "effective": [ + "doc_bbc97cbff254" + ], + "effects": [ + "doc_0383dbf277ba", + "doc_4a1cf8a90e83", + "doc_b6088784caab" + ], + "efficient": [ + "doc_0383dbf277ba" + ], + "efficiently": [ + "doc_78b1717fafe3" + ], + "effort": [ + "doc_a24b92541657", + "doc_e67e762fb6ab" + ], + "either": [ + "doc_0ac01bce8c79" + ], + "elevated": [ + "doc_567be1281e2f" + ], + "elif": [ + "doc_f54217a0f2cf" + ], + "elk": [ + "doc_78a8125d2c7e" + ], + "elsewhere": [ + "doc_64090a20f830" + ], + "embed": [ + "doc_c346f13ce597" + ], + "embed_timeout_s": [ + "doc_4462f67b1c03" + ], + "embedding": [ + "doc_3f60413b3a06", + "doc_61fd9f682847" + ], + "embedding-based": [ + "doc_2a18de009d56" + ], + "embeddingrequest": [ + "doc_c346f13ce597", + "doc_f92685a34f7e" + ], + "embeddingresponse": [ + "doc_c346f13ce597", + "doc_f92685a34f7e" + ], + "embeddings": [ + "doc_0383dbf277ba", + "doc_4462f67b1c03", + "doc_c346f13ce597", + "doc_f92685a34f7e" + ], + "emission": [ + "doc_618e34441fa7" + ], + "emit": [ + "doc_745f751f1344" + ], + "emits": [ + "doc_0ac01bce8c79", + "doc_109157c0d2d7", + "doc_3f1383f72c5f", + "doc_745f751f1344", + "doc_9e9118ed2141", + "doc_a24b92541657", + "doc_f54217a0f2cf" + ], + "emitted": [ + "doc_0ac01bce8c79", + "doc_43ad71a4ad0c", + "doc_618e34441fa7", + "doc_745f751f1344", + "doc_a24b92541657", + "doc_d6ad61a79371", + "doc_f54217a0f2cf" + ], + "empty": [ + "doc_5ee8bbdbd8a8", + "doc_a24b92541657", + "doc_e7d508371e5c" + ], + "enable": [ + "doc_067033cfc945", + "doc_136d7a909d51", + "doc_3f1383f72c5f", + "doc_4462f67b1c03", + "doc_567be1281e2f", + "doc_61fd9f682847", + "doc_7320ab982fab", + "doc_a24b92541657", + "doc_e67e762fb6ab" + ], + "enable_mcp_tools": [ + "doc_a24b92541657", + "doc_e67e762fb6ab" + ], + "enable_skill_tools": [ + "doc_a24b92541657", + "doc_e67e762fb6ab" + ], + "enabled": [ + "doc_5ee8bbdbd8a8", + "doc_9e9118ed2141", + "doc_a24b92541657" + ], + "enables": [ + "doc_1c5f37ea0ad3" + ], + "encountered": [ + "doc_136d7a909d51" + ], + "end": [ + "doc_3f60413b3a06" + ], + "end-to-end": [ + "doc_0ac01bce8c79", + "doc_745f751f1344", + "doc_e8794369a1a4" + ], + "endpoint": [ + "doc_61fd9f682847", + "doc_e34fe24f9585" + ], + "endpoints": [ + "doc_067033cfc945", + "doc_1c5f37ea0ad3", + "doc_57e2139f0873", + "doc_f1e32ed2ffce" + ], + "ends": [ + "doc_07174a8633ac", + "doc_136d7a909d51", + "doc_f54217a0f2cf" + ], + "enforce": [ + "doc_3ae3aaf0c12c", + "doc_9e9118ed2141", + "doc_a5120de838fb", + "doc_f5f256549d84" + ], + "enforcement": [ + "doc_5ee8bbdbd8a8", + "doc_618e34441fa7", + "doc_745f751f1344", + "doc_9e9118ed2141", + "doc_c346f13ce597" + ], + "enforces": [ + "doc_567be1281e2f", + "doc_64090a20f830", + "doc_745f751f1344" + ], + "enforcing": [ + "doc_50ac650fd8f6" + ], + "engine": [ + "doc_0ac01bce8c79", + "doc_1e46cbc6bfae", + "doc_2a18de009d56", + "doc_464a2b4a8d35", + "doc_567be1281e2f", + "doc_57e2139f0873", + "doc_5ee8bbdbd8a8", + "doc_618e34441fa7", + "doc_a24b92541657", + "doc_a5120de838fb", + "doc_d6ad61a79371", + "doc_e67e762fb6ab", + "doc_e7d508371e5c", + "doc_eeed52120ccf", + "doc_f54217a0f2cf" + ], + "engineering": [ + "doc_18d38fdacc9e" + ], + "engines": [ + "doc_a5120de838fb" + ], + "enough": [ + "doc_4a1cf8a90e83", + "doc_50ac650fd8f6", + "doc_57e2139f0873", + "doc_eeed52120ccf", + "doc_f12bbbc027a7", + "doc_f2ae50c638a4" + ], + "enqueue": [ + "doc_2a18de009d56" + ], + "enqueued": [ + "doc_f12bbbc027a7" + ], + "enrichment": [ + "doc_4055659fe573", + "doc_c346f13ce597" + ], + "ensure": [ + "doc_618e34441fa7" + ], + "ensures": [ + "doc_464a2b4a8d35", + "doc_618e34441fa7", + "doc_f54217a0f2cf" + ], + "enterprise": [ + "doc_d6ad61a79371" + ], + "enters": [ + "doc_136d7a909d51", + "doc_5ee8bbdbd8a8" + ], + "entire": [ + "doc_0ac01bce8c79", + "doc_50ac650fd8f6", + "doc_745f751f1344", + "doc_9e9118ed2141", + "doc_a24b92541657", + "doc_c346f13ce597", + "doc_f12bbbc027a7", + "doc_fdf48d427fc0" + ], + "entirely": [ + "doc_a24b92541657", + "doc_b6088784caab", + "doc_c346f13ce597" + ], + "entries": [ + "doc_07174a8633ac", + "doc_464a2b4a8d35", + "doc_4a1cf8a90e83", + "doc_e7d508371e5c" + ], + "entry": [ + "doc_464a2b4a8d35", + "doc_64090a20f830", + "doc_745f751f1344", + "doc_a2e84472f1f9" + ], + "entrypoints": [ + "doc_2a18de009d56" + ], + "env": [ + "doc_a24b92541657", + "doc_d147a81bac29" + ], + "envelope": [ + "doc_618e34441fa7", + "doc_745f751f1344", + "doc_75887f91c7df" + ], + "envelopes": [ + "doc_1c5f37ea0ad3", + "doc_2a18de009d56", + "doc_75887f91c7df" + ], + "environment": [ + "doc_0383dbf277ba", + "doc_067033cfc945", + "doc_18d38fdacc9e", + "doc_1e46cbc6bfae", + "doc_567be1281e2f", + "doc_61fd9f682847", + "doc_64090a20f830", + "doc_78b1717fafe3", + "doc_a24b92541657", + "doc_b6088784caab", + "doc_f5f256549d84" + ], + "environment-based": [ + "doc_0383dbf277ba", + "doc_2a18de009d56" + ], + "environment-variables": [ + "doc_3aa0b3792d22" + ], + "environments": [ + "doc_067033cfc945", + "doc_1c5f37ea0ad3", + "doc_43ad71a4ad0c", + "doc_464a2b4a8d35" + ], + "ephemeral": [ + "doc_0383dbf277ba" + ], + "equivalent": [ + "doc_5a65ddab1445" + ], + "equivalents": [ + "doc_5a65ddab1445" + ], + "error": [ + "doc_067033cfc945", + "doc_07174a8633ac", + "doc_0ac01bce8c79", + "doc_109157c0d2d7", + "doc_136d7a909d51", + "doc_18d38fdacc9e", + "doc_1e46cbc6bfae", + "doc_2798948c3080", + "doc_2a18de009d56", + "doc_3eeb75d383b8", + "doc_4055659fe573", + "doc_4e1ebaf40267", + "doc_50ac650fd8f6", + "doc_567be1281e2f", + "doc_5ee8bbdbd8a8", + "doc_618e34441fa7", + "doc_745f751f1344", + "doc_75887f91c7df", + "doc_78a8125d2c7e", + "doc_78b1717fafe3", + "doc_9e9118ed2141", + "doc_a24b92541657", + "doc_a2e84472f1f9", + "doc_c346f13ce597", + "doc_e7d508371e5c", + "doc_eeed52120ccf", + "doc_f54217a0f2cf" + ], + "error_count": [ + "doc_78a8125d2c7e" + ], + "error_message": [ + "doc_745f751f1344" + ], + "errors": [ + "doc_067033cfc945", + "doc_136d7a909d51", + "doc_18d38fdacc9e", + "doc_2a18de009d56", + "doc_3ae3aaf0c12c", + "doc_4e1ebaf40267", + "doc_50ac650fd8f6", + "doc_567be1281e2f", + "doc_5ee8bbdbd8a8", + "doc_7320ab982fab", + "doc_745f751f1344", + "doc_78a8125d2c7e", + "doc_a24b92541657", + "doc_c93c115aeb85", + "doc_eeed52120ccf", + "doc_f2ae50c638a4" + ], + "escapes": [ + "doc_464a2b4a8d35" + ], + "especially": [ + "doc_43ad71a4ad0c" + ], + "essential": [ + "doc_57e2139f0873", + "doc_f12bbbc027a7", + "doc_f54217a0f2cf" + ], + "establishment": [ + "doc_4462f67b1c03" + ], + "estimated": [ + "doc_4055659fe573", + "doc_4a1cf8a90e83", + "doc_50ac650fd8f6", + "doc_78a8125d2c7e", + "doc_bbc97cbff254", + "doc_bf91f8d5da74", + "doc_fdf48d427fc0" + ], + "estimates": [ + "doc_1e46cbc6bfae", + "doc_fdf48d427fc0" + ], + "estimation": [ + "doc_5ee8bbdbd8a8" + ], + "etc": [ + "doc_2a18de009d56", + "doc_3f60413b3a06", + "doc_4a1cf8a90e83", + "doc_5ee8bbdbd8a8", + "doc_78a8125d2c7e", + "doc_a24b92541657", + "doc_c93c115aeb85", + "doc_f12bbbc027a7", + "doc_f1e32ed2ffce", + "doc_f54217a0f2cf" + ], + "eval": [ + "doc_18d38fdacc9e", + "doc_2798948c3080", + "doc_2a18de009d56", + "doc_4e1ebaf40267", + "doc_618e34441fa7", + "doc_bbc97cbff254", + "doc_c93c115aeb85", + "doc_f54217a0f2cf" + ], + "eval-driven": [ + "doc_3f60413b3a06" + ], + "evalassertion": [ + "doc_2a18de009d56", + "doc_c93c115aeb85" + ], + "evalassertionresult": [ + "doc_2a18de009d56" + ], + "evalbudget": [ + "doc_2a18de009d56", + "doc_bbc97cbff254", + "doc_c93c115aeb85" + ], + "evalcase": [ + "doc_2a18de009d56", + "doc_bbc97cbff254" + ], + "evalcaseresult": [ + "doc_2a18de009d56" + ], + "evals": [ + "doc_18d38fdacc9e", + "doc_2798948c3080", + "doc_2a18de009d56", + "doc_3aa0b3792d22", + "doc_3ae3aaf0c12c", + "doc_3eeb75d383b8", + "doc_3f60413b3a06", + "doc_4e1ebaf40267", + "doc_50ac650fd8f6", + "doc_57e2139f0873", + "doc_5a65ddab1445", + "doc_618e34441fa7", + "doc_bbc97cbff254", + "doc_c93c115aeb85", + "doc_d147a81bac29", + "doc_deb460ffa5ee", + "doc_f18f523b6ecc", + "doc_f2ae50c638a4" + ], + "evalscorer": [ + "doc_2a18de009d56" + ], + "evalsuiteconfig": [ + "doc_2a18de009d56" + ], + "evalsuiteresult": [ + "doc_2a18de009d56" + ], + "evaluate_budget": [ + "doc_2a18de009d56" + ], + "evaluated": [ + "doc_0ac01bce8c79", + "doc_5ee8bbdbd8a8" + ], + "evaluates": [ + "doc_0ac01bce8c79", + "doc_745f751f1344", + "doc_d6ad61a79371", + "doc_f54217a0f2cf" + ], + "evaluation": [ + "doc_0ac01bce8c79", + "doc_618e34441fa7" + ], + "even": [ + "doc_464a2b4a8d35", + "doc_50ac650fd8f6", + "doc_64090a20f830", + "doc_78a8125d2c7e", + "doc_f2ae50c638a4", + "doc_fdf48d427fc0" + ], + "event": [ + "doc_0383dbf277ba", + "doc_07174a8633ac", + "doc_0ac01bce8c79", + "doc_109157c0d2d7", + "doc_1e46cbc6bfae", + "doc_2a18de009d56", + "doc_3f60413b3a06", + "doc_43ad71a4ad0c", + "doc_4e1ebaf40267", + "doc_5ee8bbdbd8a8", + "doc_618e34441fa7", + "doc_745f751f1344", + "doc_75887f91c7df", + "doc_78a8125d2c7e", + "doc_7b26cbd9a7ae", + "doc_a24b92541657", + "doc_bbc97cbff254", + "doc_d6ad61a79371", + "doc_e7d508371e5c", + "doc_eeed52120ccf", + "doc_f18f523b6ecc", + "doc_f2ae50c638a4", + "doc_f54217a0f2cf" + ], + "event-level": [ + "doc_0ac01bce8c79" + ], + "event-specific": [ + "doc_0ac01bce8c79", + "doc_f54217a0f2cf" + ], + "event-type": [ + "doc_f54217a0f2cf" + ], + "event_type": [ + "doc_0ac01bce8c79", + "doc_f54217a0f2cf" + ], + "events": [ + "doc_0383dbf277ba", + "doc_07174a8633ac", + "doc_0ac01bce8c79", + "doc_109157c0d2d7", + "doc_136d7a909d51", + "doc_43ad71a4ad0c", + "doc_4e1ebaf40267", + "doc_5ee8bbdbd8a8", + "doc_618e34441fa7", + "doc_61fd9f682847", + "doc_745f751f1344", + "doc_a24b92541657", + "doc_a5120de838fb", + "doc_bbc97cbff254", + "doc_d147a81bac29", + "doc_e7d508371e5c", + "doc_eeed52120ccf", + "doc_f2ae50c638a4", + "doc_f54217a0f2cf", + "doc_fdf48d427fc0" + ], + "ever": [ + "doc_0ac01bce8c79", + "doc_5ee8bbdbd8a8" + ], + "every": [ + "doc_0ac01bce8c79", + "doc_18d38fdacc9e", + "doc_1e46cbc6bfae", + "doc_3ae3aaf0c12c", + "doc_3f60413b3a06", + "doc_4055659fe573", + "doc_43ad71a4ad0c", + "doc_464a2b4a8d35", + "doc_4a1cf8a90e83", + "doc_4e1ebaf40267", + "doc_50ac650fd8f6", + "doc_567be1281e2f", + "doc_5a65ddab1445", + "doc_618e34441fa7", + "doc_64090a20f830", + "doc_7320ab982fab", + "doc_745f751f1344", + "doc_75887f91c7df", + "doc_78a8125d2c7e", + "doc_78b1717fafe3", + "doc_9e9118ed2141", + "doc_a24b92541657", + "doc_a5120de838fb", + "doc_c346f13ce597", + "doc_e67e762fb6ab", + "doc_e7d508371e5c", + "doc_e8794369a1a4", + "doc_f12bbbc027a7", + "doc_f54217a0f2cf", + "doc_f5f256549d84", + "doc_fdf48d427fc0" + ], + "everything": [ + "doc_43ad71a4ad0c", + "doc_4a1cf8a90e83", + "doc_e67e762fb6ab" + ], + "evidence": [ + "doc_18d38fdacc9e", + "doc_50ac650fd8f6", + "doc_e67e762fb6ab" + ], + "exact": [ + "doc_07174a8633ac", + "doc_3f60413b3a06" + ], + "exactly": [ + "doc_0ac01bce8c79", + "doc_50ac650fd8f6", + "doc_75887f91c7df", + "doc_e34fe24f9585", + "doc_e67e762fb6ab", + "doc_f54217a0f2cf" + ], + "example": [ + "doc_109157c0d2d7", + "doc_1e46cbc6bfae", + "doc_2798948c3080", + "doc_2a18de009d56", + "doc_3f60413b3a06", + "doc_4055659fe573", + "doc_4462f67b1c03", + "doc_4a1cf8a90e83", + "doc_61fd9f682847", + "doc_745f751f1344", + "doc_78b1717fafe3", + "doc_7b26cbd9a7ae", + "doc_9e9118ed2141", + "doc_a2e84472f1f9", + "doc_a5120de838fb", + "doc_d6ad61a79371", + "doc_e7d508371e5c", + "doc_eeed52120ccf", + "doc_f12bbbc027a7", + "doc_f92685a34f7e" + ], + "examples": [ + "doc_3aa0b3792d22", + "doc_3ae3aaf0c12c", + "doc_3eeb75d383b8", + "doc_5a65ddab1445", + "doc_a5120de838fb", + "doc_b6088784caab", + "doc_bbc97cbff254", + "doc_d147a81bac29", + "doc_deb460ffa5ee", + "doc_e67e762fb6ab", + "doc_f18f523b6ecc" + ], + "exceed": [ + "doc_07174a8633ac" + ], + "exceeded": [ + "doc_4055659fe573" + ], + "exceeding": [ + "doc_7320ab982fab" + ], + "exceeds": [ + "doc_0383dbf277ba", + "doc_2798948c3080", + "doc_50ac650fd8f6", + "doc_745f751f1344", + "doc_fdf48d427fc0" + ], + "exception": [ + "doc_4055659fe573", + "doc_618e34441fa7", + "doc_745f751f1344", + "doc_a24b92541657" + ], + "exceptions": [ + "doc_618e34441fa7", + "doc_745f751f1344", + "doc_a24b92541657" + ], + "excessive": [ + "doc_464a2b4a8d35", + "doc_50ac650fd8f6" + ], + "exchange": [ + "doc_75887f91c7df" + ], + "execute": [ + "doc_0ac01bce8c79", + "doc_109157c0d2d7", + "doc_1e46cbc6bfae", + "doc_43ad71a4ad0c", + "doc_50ac650fd8f6", + "doc_57e2139f0873", + "doc_745f751f1344", + "doc_9e9118ed2141", + "doc_a24b92541657", + "doc_a2e84472f1f9", + "doc_b6088784caab", + "doc_d147a81bac29", + "doc_e67e762fb6ab", + "doc_f12bbbc027a7", + "doc_f18f523b6ecc" + ], + "executed": [ + "doc_4a1cf8a90e83", + "doc_5ee8bbdbd8a8", + "doc_745f751f1344", + "doc_e67e762fb6ab", + "doc_e7d508371e5c", + "doc_f54217a0f2cf" + ], + "executes": [ + "doc_0ac01bce8c79", + "doc_1c5f37ea0ad3", + "doc_1e46cbc6bfae", + "doc_43ad71a4ad0c", + "doc_618e34441fa7", + "doc_745f751f1344", + "doc_b6088784caab", + "doc_bf91f8d5da74", + "doc_c346f13ce597", + "doc_d6ad61a79371" + ], + "executing": [ + "doc_78b1717fafe3", + "doc_d6ad61a79371", + "doc_f54217a0f2cf" + ], + "execution": [ + "doc_07174a8633ac", + "doc_0ac01bce8c79", + "doc_18d38fdacc9e", + "doc_1e46cbc6bfae", + "doc_2a18de009d56", + "doc_3f1383f72c5f", + "doc_3f60413b3a06", + "doc_43ad71a4ad0c", + "doc_464a2b4a8d35", + "doc_4a1cf8a90e83", + "doc_4e1ebaf40267", + "doc_50ac650fd8f6", + "doc_567be1281e2f", + "doc_57e2139f0873", + "doc_5a65ddab1445", + "doc_5ee8bbdbd8a8", + "doc_618e34441fa7", + "doc_61fd9f682847", + "doc_745f751f1344", + "doc_78a8125d2c7e", + "doc_78b1717fafe3", + "doc_9e9118ed2141", + "doc_a24b92541657", + "doc_a2e84472f1f9", + "doc_a5120de838fb", + "doc_b6088784caab", + "doc_bbc97cbff254", + "doc_bf91f8d5da74", + "doc_c346f13ce597", + "doc_d147a81bac29", + "doc_d6ad61a79371", + "doc_deb460ffa5ee", + "doc_e67e762fb6ab", + "doc_e7d508371e5c", + "doc_e8794369a1a4", + "doc_eeed52120ccf", + "doc_f12bbbc027a7", + "doc_f18f523b6ecc", + "doc_f2ae50c638a4", + "doc_f54217a0f2cf", + "doc_f5f256549d84", + "doc_fdf48d427fc0" + ], + "executions": [ + "doc_07174a8633ac", + "doc_5ee8bbdbd8a8", + "doc_745f751f1344", + "doc_78a8125d2c7e", + "doc_c346f13ce597", + "doc_e67e762fb6ab", + "doc_f12bbbc027a7" + ], + "executor": [ + "doc_2a18de009d56" + ], + "exercise": [ + "doc_3eeb75d383b8", + "doc_bbc97cbff254" + ], + "exhaust": [ + "doc_d1f7d83e0824" + ], + "exhausted": [ + "doc_75887f91c7df", + "doc_78b1717fafe3", + "doc_eeed52120ccf" + ], + "exhaustion": [ + "doc_f12bbbc027a7" + ], + "exhausts": [ + "doc_78b1717fafe3" + ], + "exhibits": [ + "doc_50ac650fd8f6" + ], + "exist": [ + "doc_2798948c3080", + "doc_4055659fe573", + "doc_4a1cf8a90e83", + "doc_f12bbbc027a7" + ], + "existing": [ + "doc_5a65ddab1445", + "doc_618e34441fa7", + "doc_78a8125d2c7e", + "doc_bbc97cbff254", + "doc_d147a81bac29", + "doc_f54217a0f2cf" + ], + "exists": [ + "doc_07174a8633ac", + "doc_2798948c3080", + "doc_4a1cf8a90e83" + ], + "exits": [ + "doc_0ac01bce8c79", + "doc_a24b92541657" + ], + "exotic": [ + "doc_2a18de009d56" + ], + "expands": [ + "doc_f2ae50c638a4" + ], + "expectations": [ + "doc_2798948c3080" + ], + "expected": [ + "doc_136d7a909d51", + "doc_1e46cbc6bfae", + "doc_3ae3aaf0c12c", + "doc_618e34441fa7" + ], + "expensive": [ + "doc_136d7a909d51", + "doc_d1f7d83e0824", + "doc_fdf48d427fc0" + ], + "experiments": [ + "doc_f5f256549d84" + ], + "expertise": [ + "doc_57e2139f0873", + "doc_a2e84472f1f9", + "doc_e67e762fb6ab", + "doc_f12bbbc027a7" + ], + "expire": [ + "doc_4a1cf8a90e83" + ], + "expired": [ + "doc_109157c0d2d7", + "doc_f54217a0f2cf" + ], + "expires": [ + "doc_5ee8bbdbd8a8", + "doc_a24b92541657", + "doc_d6ad61a79371" + ], + "explain": [ + "doc_618e34441fa7", + "doc_d147a81bac29", + "doc_f18f523b6ecc" + ], + "explains": [ + "doc_3eeb75d383b8", + "doc_5ee8bbdbd8a8", + "doc_eeed52120ccf", + "doc_f54217a0f2cf" + ], + "explanation": [ + "doc_1e46cbc6bfae" + ], + "explicit": [ + "doc_0383dbf277ba", + "doc_3eeb75d383b8", + "doc_464a2b4a8d35", + "doc_567be1281e2f", + "doc_5a65ddab1445", + "doc_61fd9f682847", + "doc_64090a20f830", + "doc_b6088784caab", + "doc_f18f523b6ecc", + "doc_f2ae50c638a4", + "doc_f54217a0f2cf", + "doc_f5f256549d84" + ], + "explicitly": [ + "doc_18d38fdacc9e", + "doc_43ad71a4ad0c", + "doc_567be1281e2f", + "doc_a24b92541657", + "doc_b6088784caab" + ], + "exploration": [ + "doc_9e9118ed2141" + ], + "explosion": [ + "doc_567be1281e2f" + ], + "exponential": [ + "doc_4055659fe573", + "doc_5ee8bbdbd8a8", + "doc_61fd9f682847", + "doc_7320ab982fab", + "doc_75887f91c7df" + ], + "export": [ + "doc_067033cfc945", + "doc_2a18de009d56", + "doc_3ae3aaf0c12c", + "doc_3eeb75d383b8", + "doc_4055659fe573", + "doc_745f751f1344", + "doc_78a8125d2c7e", + "doc_78b1717fafe3", + "doc_f18f523b6ecc", + "doc_f5f256549d84", + "doc_fdf48d427fc0" + ], + "exported": [ + "doc_3eeb75d383b8", + "doc_4e1ebaf40267", + "doc_5ee8bbdbd8a8" + ], + "exporter": [ + "doc_18d38fdacc9e", + "doc_4055659fe573", + "doc_4e1ebaf40267", + "doc_50ac650fd8f6", + "doc_567be1281e2f", + "doc_78a8125d2c7e" + ], + "exporters": [ + "doc_2a18de009d56", + "doc_4e1ebaf40267", + "doc_d147a81bac29" + ], + "exports": [ + "doc_2a18de009d56", + "doc_3eeb75d383b8", + "doc_4e1ebaf40267", + "doc_bbc97cbff254", + "doc_d147a81bac29", + "doc_f18f523b6ecc" + ], + "expose": [ + "doc_1c5f37ea0ad3", + "doc_40e30666bfa8", + "doc_57e2139f0873", + "doc_618e34441fa7", + "doc_745f751f1344", + "doc_78b1717fafe3", + "doc_a24b92541657", + "doc_bbc97cbff254", + "doc_e34fe24f9585", + "doc_e67e762fb6ab", + "doc_f92685a34f7e" + ], + "exposed": [ + "doc_745f751f1344", + "doc_a24b92541657" + ], + "exposes": [ + "doc_745f751f1344", + "doc_e34fe24f9585" + ], + "exposing": [ + "doc_e34fe24f9585" + ], + "express": [ + "doc_9e9118ed2141" + ], + "extend": [ + "doc_b6088784caab", + "doc_f54217a0f2cf" + ], + "extended": [ + "doc_2a18de009d56", + "doc_e67e762fb6ab" + ], + "extension": [ + "doc_2a18de009d56", + "doc_745f751f1344", + "doc_9e9118ed2141" + ], + "extensive": [ + "doc_07174a8633ac" + ], + "external": [ + "doc_0ac01bce8c79", + "doc_1c5f37ea0ad3", + "doc_1e46cbc6bfae", + "doc_3f1383f72c5f", + "doc_40e30666bfa8", + "doc_43ad71a4ad0c", + "doc_4e1ebaf40267", + "doc_567be1281e2f", + "doc_57e2139f0873", + "doc_9e9118ed2141", + "doc_a24b92541657", + "doc_a5120de838fb", + "doc_d6ad61a79371", + "doc_e34fe24f9585", + "doc_e67e762fb6ab", + "doc_e7d508371e5c", + "doc_f18f523b6ecc", + "doc_f5f256549d84" + ], + "extraction": [ + "doc_3f60413b3a06" + ], + "extracts": [ + "doc_1c5f37ea0ad3", + "doc_5ee8bbdbd8a8", + "doc_745f751f1344" + ], + "extras": [ + "doc_745f751f1344" + ], + "fact-checking": [ + "doc_f12bbbc027a7" + ], + "factory": [ + "doc_2a18de009d56", + "doc_464a2b4a8d35" + ], + "fail": [ + "doc_0ac01bce8c79", + "doc_109157c0d2d7", + "doc_4055659fe573", + "doc_4e1ebaf40267", + "doc_57e2139f0873", + "doc_5ee8bbdbd8a8", + "doc_745f751f1344", + "doc_a24b92541657", + "doc_c93c115aeb85", + "doc_d1f7d83e0824", + "doc_eeed52120ccf", + "doc_f12bbbc027a7" + ], + "fail-safe": [ + "doc_4e1ebaf40267", + "doc_bbc97cbff254", + "doc_f18f523b6ecc", + "doc_f5f256549d84" + ], + "fail_fast": [ + "doc_a24b92541657" + ], + "fail_run": [ + "doc_a24b92541657" + ], + "fail_safe": [ + "doc_0ac01bce8c79", + "doc_745f751f1344", + "doc_a24b92541657", + "doc_e67e762fb6ab" + ], + "failed": [ + "doc_067033cfc945", + "doc_0ac01bce8c79", + "doc_109157c0d2d7", + "doc_1e46cbc6bfae", + "doc_4055659fe573", + "doc_5ee8bbdbd8a8", + "doc_745f751f1344", + "doc_78b1717fafe3", + "doc_a24b92541657", + "doc_a2e84472f1f9", + "doc_bf91f8d5da74", + "doc_e7d508371e5c", + "doc_f54217a0f2cf" + ], + "failing": [ + "doc_4055659fe573", + "doc_7320ab982fab" + ], + "fails": [ + "doc_0383dbf277ba", + "doc_4055659fe573", + "doc_4a1cf8a90e83", + "doc_4e1ebaf40267", + "doc_5ee8bbdbd8a8", + "doc_618e34441fa7", + "doc_7320ab982fab", + "doc_745f751f1344", + "doc_75887f91c7df", + "doc_a24b92541657", + "doc_c346f13ce597", + "doc_d1f7d83e0824", + "doc_f12bbbc027a7" + ], + "failsafeconfig": [ + "doc_067033cfc945", + "doc_18d38fdacc9e", + "doc_1c5f37ea0ad3", + "doc_2a18de009d56", + "doc_3ae3aaf0c12c", + "doc_43ad71a4ad0c", + "doc_50ac650fd8f6", + "doc_57e2139f0873", + "doc_5ee8bbdbd8a8", + "doc_a24b92541657", + "doc_a2e84472f1f9", + "doc_a5120de838fb", + "doc_bbc97cbff254", + "doc_d147a81bac29", + "doc_d1f7d83e0824", + "doc_e67e762fb6ab", + "doc_fdf48d427fc0" + ], + "failure": [ + "doc_0383dbf277ba", + "doc_0ac01bce8c79", + "doc_2a18de009d56", + "doc_3ae3aaf0c12c", + "doc_4055659fe573", + "doc_4a1cf8a90e83", + "doc_4e1ebaf40267", + "doc_50ac650fd8f6", + "doc_5ee8bbdbd8a8", + "doc_618e34441fa7", + "doc_745f751f1344", + "doc_78a8125d2c7e", + "doc_78b1717fafe3", + "doc_a24b92541657", + "doc_a2e84472f1f9", + "doc_d147a81bac29", + "doc_d1f7d83e0824", + "doc_e67e762fb6ab", + "doc_f12bbbc027a7", + "doc_f54217a0f2cf", + "doc_fdf48d427fc0" + ], + "failure-policy-matrix": [ + "doc_3aa0b3792d22" + ], + "failure_count": [ + "doc_0ac01bce8c79" + ], + "failurepolicy": [ + "doc_a24b92541657" + ], + "failures": [ + "doc_067033cfc945", + "doc_07174a8633ac", + "doc_109157c0d2d7", + "doc_18d38fdacc9e", + "doc_3ae3aaf0c12c", + "doc_4055659fe573", + "doc_4e1ebaf40267", + "doc_50ac650fd8f6", + "doc_5ee8bbdbd8a8", + "doc_618e34441fa7", + "doc_61fd9f682847", + "doc_7320ab982fab", + "doc_745f751f1344", + "doc_d1f7d83e0824", + "doc_e7d508371e5c", + "doc_f12bbbc027a7", + "doc_f5f256549d84" + ], + "fallback": [ + "doc_109157c0d2d7", + "doc_2798948c3080", + "doc_3f60413b3a06", + "doc_4055659fe573", + "doc_43ad71a4ad0c", + "doc_4a1cf8a90e83", + "doc_5ee8bbdbd8a8", + "doc_61fd9f682847", + "doc_7320ab982fab", + "doc_a24b92541657", + "doc_a5120de838fb", + "doc_d1f7d83e0824", + "doc_deb460ffa5ee", + "doc_eeed52120ccf", + "doc_f54217a0f2cf" + ], + "fallback_model_chain": [ + "doc_5ee8bbdbd8a8", + "doc_a24b92541657", + "doc_d1f7d83e0824" + ], + "falls": [ + "doc_0383dbf277ba", + "doc_61fd9f682847", + "doc_a24b92541657", + "doc_d1f7d83e0824" + ], + "false": [ + "doc_0ac01bce8c79", + "doc_109157c0d2d7", + "doc_3aa0b3792d22", + "doc_4462f67b1c03", + "doc_618e34441fa7", + "doc_61fd9f682847", + "doc_745f751f1344", + "doc_a24b92541657" + ], + "families": [ + "doc_f92685a34f7e" + ], + "fan-out": [ + "doc_57e2139f0873", + "doc_f12bbbc027a7" + ], + "fanout": [ + "doc_a24b92541657" + ], + "fans": [ + "doc_f12bbbc027a7" + ], + "far": [ + "doc_4a1cf8a90e83" + ], + "fast": [ + "doc_0383dbf277ba", + "doc_18d38fdacc9e", + "doc_5ee8bbdbd8a8", + "doc_75887f91c7df", + "doc_a24b92541657", + "doc_eeed52120ccf" + ], + "faster": [ + "doc_3f60413b3a06", + "doc_7320ab982fab", + "doc_bbc97cbff254" + ], + "fastest": [ + "doc_1e46cbc6bfae" + ], + "fault": [ + "doc_4a1cf8a90e83" + ], + "favicon": [ + "doc_3aa0b3792d22" + ], + "fb67e": [ + "doc_3aa0b3792d22" + ], + "feature": [ + "doc_5a65ddab1445", + "doc_618e34441fa7", + "doc_b6088784caab", + "doc_e34fe24f9585", + "doc_f1e32ed2ffce" + ], + "features": [ + "doc_0383dbf277ba", + "doc_18d38fdacc9e", + "doc_57e2139f0873" + ], + "fed": [ + "doc_0ac01bce8c79", + "doc_745f751f1344", + "doc_a24b92541657", + "doc_e7d508371e5c" + ], + "federated": [ + "doc_57e2139f0873" + ], + "feedback": [ + "doc_0ac01bce8c79" + ], + "feeding": [ + "doc_a2e84472f1f9" + ], + "feeds": [ + "doc_618e34441fa7", + "doc_745f751f1344", + "doc_bf91f8d5da74" + ], + "fetch": [ + "doc_4a1cf8a90e83" + ], + "few": [ + "doc_2a18de009d56", + "doc_a24b92541657" + ], + "fewer": [ + "doc_f12bbbc027a7" + ], + "field": [ + "doc_0ac01bce8c79", + "doc_1e46cbc6bfae", + "doc_4a1cf8a90e83", + "doc_5ee8bbdbd8a8", + "doc_618e34441fa7", + "doc_64090a20f830", + "doc_745f751f1344", + "doc_75887f91c7df", + "doc_a24b92541657", + "doc_a2e84472f1f9", + "doc_bbc97cbff254", + "doc_bf91f8d5da74", + "doc_d147a81bac29", + "doc_e67e762fb6ab", + "doc_e7d508371e5c", + "doc_e8794369a1a4", + "doc_f54217a0f2cf", + "doc_fdf48d427fc0" + ], + "field-by-field": [ + "doc_bbc97cbff254" + ], + "field-level": [ + "doc_2a18de009d56" + ], + "fields": [ + "doc_0ac01bce8c79", + "doc_109157c0d2d7", + "doc_3ae3aaf0c12c", + "doc_3f60413b3a06", + "doc_464a2b4a8d35", + "doc_4a1cf8a90e83", + "doc_5ee8bbdbd8a8", + "doc_618e34441fa7", + "doc_61fd9f682847", + "doc_64090a20f830", + "doc_a24b92541657", + "doc_bbc97cbff254", + "doc_bf91f8d5da74", + "doc_d147a81bac29", + "doc_d1f7d83e0824", + "doc_deb460ffa5ee", + "doc_e67e762fb6ab", + "doc_e7d508371e5c", + "doc_f2ae50c638a4", + "doc_f54217a0f2cf" + ], + "file": [ + "doc_067033cfc945", + "doc_2798948c3080", + "doc_2a18de009d56", + "doc_464a2b4a8d35", + "doc_5a65ddab1445", + "doc_61fd9f682847", + "doc_64090a20f830", + "doc_a24b92541657", + "doc_a5120de838fb", + "doc_b6088784caab", + "doc_e67e762fb6ab", + "doc_f2ae50c638a4" + ], + "file-based": [ + "doc_64090a20f830" + ], + "fileaccesserror": [ + "doc_464a2b4a8d35" + ], + "filename": [ + "doc_64090a20f830" + ], + "files": [ + "doc_2798948c3080", + "doc_3ae3aaf0c12c", + "doc_3eeb75d383b8", + "doc_464a2b4a8d35", + "doc_618e34441fa7", + "doc_61fd9f682847", + "doc_64090a20f830", + "doc_9e9118ed2141", + "doc_a24b92541657", + "doc_a5120de838fb", + "doc_b6088784caab", + "doc_d147a81bac29", + "doc_e67e762fb6ab" + ], + "filesystem": [ + "doc_43ad71a4ad0c", + "doc_464a2b4a8d35", + "doc_9e9118ed2141" + ], + "fill": [ + "doc_0383dbf277ba" + ], + "filter": [ + "doc_75887f91c7df" + ], + "final": [ + "doc_109157c0d2d7", + "doc_1e46cbc6bfae", + "doc_3f60413b3a06", + "doc_4a1cf8a90e83", + "doc_5ee8bbdbd8a8", + "doc_618e34441fa7", + "doc_a2e84472f1f9", + "doc_bbc97cbff254", + "doc_bf91f8d5da74", + "doc_deb460ffa5ee", + "doc_e7d508371e5c", + "doc_eeed52120ccf", + "doc_f12bbbc027a7", + "doc_f18f523b6ecc", + "doc_f2ae50c638a4", + "doc_f5f256549d84" + ], + "final_structured": [ + "doc_4a1cf8a90e83" + ], + "final_text": [ + "doc_1e46cbc6bfae", + "doc_4a1cf8a90e83", + "doc_5ee8bbdbd8a8", + "doc_a2e84472f1f9", + "doc_a5120de838fb", + "doc_bbc97cbff254", + "doc_bf91f8d5da74", + "doc_e67e762fb6ab", + "doc_e7d508371e5c" + ], + "finalize": [ + "doc_3f1383f72c5f" + ], + "find": [ + "doc_0383dbf277ba", + "doc_136d7a909d51", + "doc_2a18de009d56", + "doc_a24b92541657", + "doc_bf91f8d5da74", + "doc_deb460ffa5ee" + ], + "finds": [ + "doc_3eeb75d383b8" + ], + "finish_reason": [ + "doc_4a1cf8a90e83", + "doc_5ee8bbdbd8a8", + "doc_e8794369a1a4", + "doc_f54217a0f2cf" + ], + "finished": [ + "doc_109157c0d2d7", + "doc_78b1717fafe3", + "doc_e7d508371e5c" + ], + "finishes": [ + "doc_f54217a0f2cf" + ], + "fire": [ + "doc_618e34441fa7" + ], + "fires": [ + "doc_0ac01bce8c79", + "doc_109157c0d2d7", + "doc_f54217a0f2cf" + ], + "first": [ + "doc_0383dbf277ba", + "doc_067033cfc945", + "doc_2798948c3080", + "doc_4a1cf8a90e83", + "doc_4e1ebaf40267", + "doc_5a65ddab1445", + "doc_618e34441fa7", + "doc_61fd9f682847", + "doc_745f751f1344", + "doc_78a8125d2c7e", + "doc_9e9118ed2141", + "doc_a5120de838fb", + "doc_c346f13ce597", + "doc_c93c115aeb85", + "doc_deb460ffa5ee", + "doc_e67e762fb6ab", + "doc_f12bbbc027a7", + "doc_f5f256549d84" + ], + "first_success": [ + "doc_f12bbbc027a7" + ], + "first_token_ms": [ + "doc_78a8125d2c7e" + ], + "fit": [ + "doc_f18f523b6ecc" + ], + "fits": [ + "doc_deb460ffa5ee" + ], + "five": [ + "doc_57e2139f0873" + ], + "fix": [ + "doc_18d38fdacc9e", + "doc_3f1383f72c5f", + "doc_618e34441fa7", + "doc_a24b92541657" + ], + "fixing": [ + "doc_618e34441fa7" + ], + "flag": [ + "doc_745f751f1344", + "doc_a24b92541657" + ], + "flags": [ + "doc_464a2b4a8d35" + ], + "float": [ + "doc_4a1cf8a90e83", + "doc_5ee8bbdbd8a8", + "doc_745f751f1344", + "doc_78a8125d2c7e", + "doc_a24b92541657", + "doc_a2e84472f1f9", + "doc_e7d508371e5c", + "doc_e8794369a1a4" + ], + "floor": [ + "doc_a24b92541657" + ], + "flow": [ + "doc_0ac01bce8c79", + "doc_1c5f37ea0ad3", + "doc_3f1383f72c5f", + "doc_4055659fe573", + "doc_50ac650fd8f6", + "doc_567be1281e2f", + "doc_5ee8bbdbd8a8", + "doc_618e34441fa7", + "doc_a2e84472f1f9", + "doc_c346f13ce597", + "doc_d147a81bac29", + "doc_d1f7d83e0824", + "doc_d6ad61a79371", + "doc_e8794369a1a4", + "doc_fdf48d427fc0" + ], + "flows": [ + "doc_0ac01bce8c79", + "doc_3ae3aaf0c12c", + "doc_50ac650fd8f6", + "doc_745f751f1344", + "doc_a5120de838fb", + "doc_e8794369a1a4", + "doc_f92685a34f7e" + ], + "fluent": [ + "doc_2a18de009d56", + "doc_3f60413b3a06" + ], + "flush": [ + "doc_4a1cf8a90e83", + "doc_a24b92541657" + ], + "flushed": [ + "doc_4a1cf8a90e83" + ], + "focused": [ + "doc_18d38fdacc9e", + "doc_2a18de009d56", + "doc_618e34441fa7", + "doc_b6088784caab", + "doc_d147a81bac29", + "doc_e67e762fb6ab" + ], + "follow": [ + "doc_43ad71a4ad0c", + "doc_50ac650fd8f6", + "doc_618e34441fa7", + "doc_c346f13ce597" + ], + "following": [ + "doc_64090a20f830", + "doc_f54217a0f2cf" + ], + "follows": [ + "doc_07174a8633ac", + "doc_4a1cf8a90e83", + "doc_e7d508371e5c" + ], + "footer": [ + "doc_3aa0b3792d22" + ], + "forge": [ + "doc_2a18de009d56" + ], + "form": [ + "doc_f54217a0f2cf" + ], + "format": [ + "doc_18d38fdacc9e", + "doc_2798948c3080", + "doc_745f751f1344", + "doc_b6088784caab", + "doc_e8794369a1a4" + ], + "formats": [ + "doc_4a1cf8a90e83" + ], + "formatted": [ + "doc_9e9118ed2141" + ], + "formatters": [ + "doc_2a18de009d56" + ], + "forward": [ + "doc_f54217a0f2cf" + ], + "forwarded": [ + "doc_a24b92541657" + ], + "forwards": [ + "doc_109157c0d2d7" + ], + "found": [ + "doc_07174a8633ac", + "doc_136d7a909d51" + ], + "foundation": [ + "doc_1e46cbc6bfae" + ], + "four": [ + "doc_0383dbf277ba", + "doc_567be1281e2f", + "doc_9e9118ed2141", + "doc_f5f256549d84" + ], + "framework": [ + "doc_0383dbf277ba", + "doc_2a18de009d56", + "doc_d147a81bac29" + ], + "frameworks": [ + "doc_5a65ddab1445" + ], + "freeform": [ + "doc_43ad71a4ad0c", + "doc_d6ad61a79371" + ], + "fresh": [ + "doc_0383dbf277ba", + "doc_4a1cf8a90e83" + ], + "from_env": [ + "doc_61fd9f682847" + ], + "frontmatter": [ + "doc_b6088784caab" + ], + "full": [ + "doc_0383dbf277ba", + "doc_07174a8633ac", + "doc_109157c0d2d7", + "doc_136d7a909d51", + "doc_1e46cbc6bfae", + "doc_2a18de009d56", + "doc_3f60413b3a06", + "doc_40e30666bfa8", + "doc_4462f67b1c03", + "doc_464a2b4a8d35", + "doc_4a1cf8a90e83", + "doc_5a65ddab1445", + "doc_618e34441fa7", + "doc_61fd9f682847", + "doc_64090a20f830", + "doc_745f751f1344", + "doc_75887f91c7df", + "doc_7b26cbd9a7ae", + "doc_bbc97cbff254", + "doc_c346f13ce597", + "doc_d1f7d83e0824", + "doc_e34fe24f9585", + "doc_e67e762fb6ab", + "doc_eeed52120ccf", + "doc_f12bbbc027a7", + "doc_fdf48d427fc0" + ], + "full-module-reference": [ + "doc_3aa0b3792d22" + ], + "function": [ + "doc_136d7a909d51", + "doc_2a18de009d56", + "doc_5a65ddab1445", + "doc_5ee8bbdbd8a8", + "doc_745f751f1344", + "doc_78b1717fafe3", + "doc_9e9118ed2141", + "doc_a24b92541657", + "doc_b6088784caab", + "doc_bf91f8d5da74", + "doc_c346f13ce597", + "doc_e67e762fb6ab", + "doc_eeed52120ccf", + "doc_f2ae50c638a4" + ], + "function-calling": [ + "doc_745f751f1344" + ], + "functions": [ + "doc_18d38fdacc9e", + "doc_2a18de009d56", + "doc_57e2139f0873", + "doc_5ee8bbdbd8a8", + "doc_a24b92541657", + "doc_b6088784caab", + "doc_bbc97cbff254", + "doc_bf91f8d5da74", + "doc_e67e762fb6ab" + ], + "fundamentally": [ + "doc_43ad71a4ad0c", + "doc_a24b92541657" + ], + "further": [ + "doc_464a2b4a8d35" + ], + "future": [ + "doc_f18f523b6ecc", + "doc_f54217a0f2cf" + ], + "gate": [ + "doc_0ac01bce8c79", + "doc_18d38fdacc9e", + "doc_43ad71a4ad0c", + "doc_50ac650fd8f6", + "doc_567be1281e2f", + "doc_745f751f1344", + "doc_9e9118ed2141", + "doc_a5120de838fb", + "doc_b6088784caab", + "doc_c93c115aeb85" + ], + "gated": [ + "doc_3ae3aaf0c12c", + "doc_9e9118ed2141", + "doc_b6088784caab" + ], + "gates": [ + "doc_3ae3aaf0c12c", + "doc_40e30666bfa8", + "doc_43ad71a4ad0c", + "doc_464a2b4a8d35", + "doc_50ac650fd8f6", + "doc_745f751f1344", + "doc_9e9118ed2141", + "doc_a5120de838fb", + "doc_b6088784caab", + "doc_e34fe24f9585", + "doc_e67e762fb6ab" + ], + "gating": [ + "doc_c93c115aeb85", + "doc_e67e762fb6ab" + ], + "gemini": [ + "doc_f1e32ed2ffce" + ], + "general": [ + "doc_d1f7d83e0824", + "doc_eeed52120ccf", + "doc_f92685a34f7e" + ], + "generally": [ + "doc_43ad71a4ad0c" + ], + "generate": [ + "doc_18d38fdacc9e", + "doc_9e9118ed2141" + ], + "generated": [ + "doc_3eeb75d383b8", + "doc_4a1cf8a90e83", + "doc_75887f91c7df", + "doc_7b26cbd9a7ae", + "doc_bbc97cbff254", + "doc_d147a81bac29", + "doc_e8794369a1a4", + "doc_eeed52120ccf", + "doc_f18f523b6ecc" + ], + "generates": [ + "doc_9e9118ed2141", + "doc_e67e762fb6ab" + ], + "generating": [ + "doc_5ee8bbdbd8a8" + ], + "generation": [ + "doc_57e2139f0873", + "doc_745f751f1344", + "doc_9e9118ed2141", + "doc_d1f7d83e0824", + "doc_e8794369a1a4", + "doc_eeed52120ccf", + "doc_f18f523b6ecc", + "doc_f1e32ed2ffce" + ], + "generic": [ + "doc_78b1717fafe3" + ], + "generous": [ + "doc_fdf48d427fc0" + ], + "genuinely": [ + "doc_3ae3aaf0c12c" + ], + "get": [ + "doc_1e46cbc6bfae", + "doc_3f60413b3a06", + "doc_4e1ebaf40267", + "doc_a5120de838fb", + "doc_e7d508371e5c", + "doc_e8794369a1a4" + ], + "get_redis_pool": [ + "doc_78b1717fafe3" + ], + "get_resource": [ + "doc_43ad71a4ad0c" + ], + "get_state": [ + "doc_4a1cf8a90e83" + ], + "gets": [ + "doc_0383dbf277ba", + "doc_18d38fdacc9e", + "doc_4a1cf8a90e83", + "doc_745f751f1344" + ], + "getting": [ + "doc_136d7a909d51" + ], + "giant": [ + "doc_18d38fdacc9e", + "doc_b6088784caab" + ], + "git": [ + "doc_2798948c3080", + "doc_b6088784caab" + ], + "github": [ + "doc_136d7a909d51", + "doc_3aa0b3792d22" + ], + "give": [ + "doc_18d38fdacc9e" + ], + "given": [ + "doc_07174a8633ac", + "doc_4a1cf8a90e83" + ], + "gives": [ + "doc_2798948c3080", + "doc_78a8125d2c7e" + ], + "glitches": [ + "doc_a24b92541657" + ], + "global": [ + "doc_9e9118ed2141", + "doc_a24b92541657" + ], + "go": [ + "doc_40e30666bfa8", + "doc_a5120de838fb" + ], + "goal": [ + "doc_7320ab982fab" + ], + "goes": [ + "doc_5ee8bbdbd8a8" + ], + "going": [ + "doc_bf91f8d5da74" + ], + "golden": [ + "doc_18d38fdacc9e", + "doc_2a18de009d56", + "doc_3ae3aaf0c12c" + ], + "good": [ + "doc_0383dbf277ba", + "doc_57e2139f0873", + "doc_b6088784caab", + "doc_eeed52120ccf", + "doc_f18f523b6ecc" + ], + "google": [ + "doc_1c5f37ea0ad3", + "doc_2a18de009d56" + ], + "gpt-4": [ + "doc_1e46cbc6bfae", + "doc_5ee8bbdbd8a8", + "doc_61fd9f682847", + "doc_d1f7d83e0824", + "doc_eeed52120ccf", + "doc_f1e32ed2ffce", + "doc_f92685a34f7e" + ], + "grace": [ + "doc_a24b92541657" + ], + "graceful": [ + "doc_a5120de838fb", + "doc_c93c115aeb85" + ], + "gracefully": [ + "doc_f54217a0f2cf" + ], + "gradient": [ + "doc_3aa0b3792d22" + ], + "grafana": [ + "doc_78a8125d2c7e" + ], + "graph": [ + "doc_2a18de009d56" + ], + "graphs": [ + "doc_f12bbbc027a7" + ], + "group": [ + "doc_3aa0b3792d22", + "doc_75887f91c7df" + ], + "groups": [ + "doc_3aa0b3792d22", + "doc_75887f91c7df" + ], + "grow": [ + "doc_0383dbf277ba", + "doc_f5f256549d84" + ], + "grown": [ + "doc_07174a8633ac" + ], + "growth": [ + "doc_618e34441fa7", + "doc_a5120de838fb" + ], + "grpc": [ + "doc_1c5f37ea0ad3" + ], + "guarantee": [ + "doc_618e34441fa7" + ], + "guaranteed": [ + "doc_3f60413b3a06", + "doc_618e34441fa7" + ], + "guarantees": [ + "doc_4a1cf8a90e83", + "doc_d147a81bac29" + ], + "guards": [ + "doc_464a2b4a8d35" + ], + "guidance": [ + "doc_618e34441fa7", + "doc_d147a81bac29", + "doc_deb460ffa5ee", + "doc_f5f256549d84" + ], + "guide": [ + "doc_067033cfc945", + "doc_136d7a909d51", + "doc_18d38fdacc9e", + "doc_3aa0b3792d22", + "doc_4462f67b1c03", + "doc_57e2139f0873", + "doc_5a65ddab1445", + "doc_eeed52120ccf", + "doc_f18f523b6ecc" + ], + "guided": [ + "doc_a24b92541657", + "doc_f18f523b6ecc" + ], + "guidelines": [ + "doc_0383dbf277ba", + "doc_2798948c3080", + "doc_64090a20f830", + "doc_b6088784caab", + "doc_c346f13ce597", + "doc_e67e762fb6ab" + ], + "guides": [ + "doc_1e46cbc6bfae", + "doc_b6088784caab" + ], + "hammering": [ + "doc_a24b92541657" + ], + "hand": [ + "doc_e7d508371e5c" + ], + "handle": [ + "doc_0ac01bce8c79", + "doc_18d38fdacc9e", + "doc_3f1383f72c5f", + "doc_4a1cf8a90e83", + "doc_4e1ebaf40267", + "doc_7320ab982fab", + "doc_745f751f1344", + "doc_7b26cbd9a7ae", + "doc_9e9118ed2141", + "doc_d147a81bac29", + "doc_f54217a0f2cf", + "doc_f5f256549d84", + "doc_f92685a34f7e" + ], + "handled": [ + "doc_0ac01bce8c79", + "doc_5ee8bbdbd8a8", + "doc_64090a20f830", + "doc_eeed52120ccf" + ], + "handler": [ + "doc_0ac01bce8c79", + "doc_2a18de009d56", + "doc_4055659fe573", + "doc_50ac650fd8f6", + "doc_745f751f1344", + "doc_78b1717fafe3", + "doc_7b26cbd9a7ae", + "doc_9e9118ed2141" + ], + "handlers": [ + "doc_18d38fdacc9e", + "doc_9e9118ed2141", + "doc_a24b92541657" + ], + "handles": [ + "doc_50ac650fd8f6", + "doc_9e9118ed2141", + "doc_a24b92541657", + "doc_a2e84472f1f9", + "doc_d147a81bac29", + "doc_deb460ffa5ee", + "doc_e67e762fb6ab", + "doc_e7d508371e5c", + "doc_f12bbbc027a7", + "doc_f2ae50c638a4" + ], + "handling": [ + "doc_07174a8633ac", + "doc_109157c0d2d7", + "doc_2798948c3080", + "doc_4462f67b1c03", + "doc_57e2139f0873", + "doc_5ee8bbdbd8a8", + "doc_618e34441fa7", + "doc_7320ab982fab", + "doc_745f751f1344", + "doc_78b1717fafe3", + "doc_a2e84472f1f9", + "doc_a5120de838fb", + "doc_c346f13ce597", + "doc_c93c115aeb85", + "doc_eeed52120ccf", + "doc_f12bbbc027a7", + "doc_f92685a34f7e" + ], + "hang": [ + "doc_136d7a909d51" + ], + "happen": [ + "doc_109157c0d2d7", + "doc_4a1cf8a90e83", + "doc_9e9118ed2141", + "doc_c346f13ce597" + ], + "happened": [ + "doc_0ac01bce8c79", + "doc_4055659fe573", + "doc_f54217a0f2cf" + ], + "happens": [ + "doc_4e1ebaf40267", + "doc_5ee8bbdbd8a8" + ], + "hard": [ + "doc_3ae3aaf0c12c", + "doc_50ac650fd8f6", + "doc_567be1281e2f", + "doc_7320ab982fab", + "doc_f2ae50c638a4", + "doc_fdf48d427fc0" + ], + "hard-coded": [ + "doc_b6088784caab" + ], + "hard-to-debug": [ + "doc_18d38fdacc9e" + ], + "hardcoded": [ + "doc_61fd9f682847" + ], + "hardcoding": [ + "doc_64090a20f830" + ], + "hardening": [ + "doc_067033cfc945", + "doc_4055659fe573", + "doc_567be1281e2f", + "doc_78a8125d2c7e", + "doc_c93c115aeb85" + ], + "has": [ + "doc_07174a8633ac", + "doc_0ac01bce8c79", + "doc_1c5f37ea0ad3", + "doc_43ad71a4ad0c", + "doc_4a1cf8a90e83", + "doc_50ac650fd8f6", + "doc_5ee8bbdbd8a8", + "doc_64090a20f830", + "doc_745f751f1344", + "doc_78b1717fafe3", + "doc_9e9118ed2141", + "doc_b6088784caab", + "doc_e67e762fb6ab" + ], + "hash": [ + "doc_4a1cf8a90e83", + "doc_64090a20f830" + ], + "hashicorp": [ + "doc_067033cfc945" + ], + "hashing": [ + "doc_1c5f37ea0ad3" + ], + "have": [ + "doc_07174a8633ac", + "doc_0ac01bce8c79", + "doc_18d38fdacc9e", + "doc_3ae3aaf0c12c", + "doc_43ad71a4ad0c", + "doc_4a1cf8a90e83", + "doc_4e1ebaf40267", + "doc_50ac650fd8f6", + "doc_57e2139f0873", + "doc_618e34441fa7", + "doc_78a8125d2c7e", + "doc_e67e762fb6ab", + "doc_f54217a0f2cf" + ], + "having": [ + "doc_618e34441fa7" + ], + "head": [ + "doc_a24b92541657" + ], + "headless": [ + "doc_1e46cbc6bfae", + "doc_43ad71a4ad0c", + "doc_a24b92541657", + "doc_d6ad61a79371" + ], + "headlessinteractionprovider": [ + "doc_0ac01bce8c79", + "doc_2a18de009d56" + ], + "health": [ + "doc_067033cfc945", + "doc_4462f67b1c03", + "doc_61fd9f682847", + "doc_78b1717fafe3" + ], + "health_check_interval_s": [ + "doc_4462f67b1c03" + ], + "hedgingpolicy": [ + "doc_2a18de009d56" + ], + "help": [ + "doc_136d7a909d51", + "doc_5ee8bbdbd8a8" + ], + "helpers": [ + "doc_2a18de009d56", + "doc_61fd9f682847", + "doc_9e9118ed2141", + "doc_d147a81bac29" + ], + "helps": [ + "doc_4055659fe573", + "doc_43ad71a4ad0c", + "doc_50ac650fd8f6", + "doc_5a65ddab1445", + "doc_b6088784caab" + ], + "here": [ + "doc_1e46cbc6bfae", + "doc_3aa0b3792d22", + "doc_5ee8bbdbd8a8", + "doc_a5120de838fb", + "doc_deb460ffa5ee" + ], + "hide": [ + "doc_618e34441fa7" + ], + "hiding": [ + "doc_3ae3aaf0c12c" + ], + "hierarchy": [ + "doc_2a18de009d56", + "doc_64090a20f830", + "doc_a24b92541657" + ], + "high": [ + "doc_136d7a909d51", + "doc_7320ab982fab", + "doc_78a8125d2c7e", + "doc_e67e762fb6ab" + ], + "high-risk": [ + "doc_d147a81bac29" + ], + "high-throughput": [ + "doc_57e2139f0873", + "doc_78b1717fafe3" + ], + "higher": [ + "doc_136d7a909d51", + "doc_18d38fdacc9e", + "doc_eeed52120ccf" + ], + "highest": [ + "doc_3f1383f72c5f", + "doc_61fd9f682847", + "doc_eeed52120ccf" + ], + "histogram": [ + "doc_0ac01bce8c79" + ], + "histograms": [ + "doc_745f751f1344" + ], + "histories": [ + "doc_07174a8633ac" + ], + "history": [ + "doc_0383dbf277ba", + "doc_07174a8633ac", + "doc_2798948c3080", + "doc_4a1cf8a90e83", + "doc_50ac650fd8f6", + "doc_5ee8bbdbd8a8", + "doc_e7d508371e5c", + "doc_e8794369a1a4", + "doc_eeed52120ccf" + ], + "hit": [ + "doc_0383dbf277ba", + "doc_4055659fe573", + "doc_78b1717fafe3", + "doc_e7d508371e5c" + ], + "hitl": [ + "doc_a5120de838fb", + "doc_d6ad61a79371", + "doc_f18f523b6ecc" + ], + "hits": [ + "doc_a24b92541657" + ], + "hmac-sha256": [ + "doc_1c5f37ea0ad3" + ], + "honeycomb": [ + "doc_2a18de009d56", + "doc_78a8125d2c7e" + ], + "hood": [ + "doc_1e46cbc6bfae" + ], + "hook": [ + "doc_618e34441fa7", + "doc_745f751f1344", + "doc_c346f13ce597" + ], + "hooks": [ + "doc_2a18de009d56", + "doc_4e1ebaf40267", + "doc_50ac650fd8f6", + "doc_5a65ddab1445", + "doc_9e9118ed2141", + "doc_a24b92541657", + "doc_a5120de838fb", + "doc_bbc97cbff254", + "doc_c346f13ce597", + "doc_deb460ffa5ee", + "doc_f2ae50c638a4" + ], + "horizontal": [ + "doc_067033cfc945" + ], + "host": [ + "doc_4e1ebaf40267", + "doc_567be1281e2f", + "doc_61fd9f682847" + ], + "hosting": [ + "doc_1c5f37ea0ad3", + "doc_2a18de009d56" + ], + "hosts": [ + "doc_2a18de009d56" + ], + "hot-reload": [ + "doc_64090a20f830" + ], + "hourly": [ + "doc_07174a8633ac" + ], + "hours": [ + "doc_4a1cf8a90e83" + ], + "how": [ + "doc_067033cfc945", + "doc_07174a8633ac", + "doc_0ac01bce8c79", + "doc_136d7a909d51", + "doc_2798948c3080", + "doc_3eeb75d383b8", + "doc_40e30666bfa8", + "doc_43ad71a4ad0c", + "doc_4462f67b1c03", + "doc_464a2b4a8d35", + "doc_4a1cf8a90e83", + "doc_50ac650fd8f6", + "doc_567be1281e2f", + "doc_57e2139f0873", + "doc_5ee8bbdbd8a8", + "doc_64090a20f830", + "doc_7320ab982fab", + "doc_7b26cbd9a7ae", + "doc_9e9118ed2141", + "doc_a24b92541657", + "doc_a2e84472f1f9", + "doc_a5120de838fb", + "doc_b6088784caab", + "doc_d1f7d83e0824", + "doc_d6ad61a79371", + "doc_deb460ffa5ee", + "doc_e67e762fb6ab", + "doc_eeed52120ccf", + "doc_f12bbbc027a7", + "doc_f1e32ed2ffce", + "doc_f54217a0f2cf", + "doc_f92685a34f7e", + "doc_fdf48d427fc0" + ], + "how-to": [ + "doc_b6088784caab" + ], + "how-to-use-afk": [ + "doc_3aa0b3792d22" + ], + "hpa": [ + "doc_067033cfc945" + ], + "href": [ + "doc_3aa0b3792d22" + ], + "http": [ + "doc_1c5f37ea0ad3", + "doc_61fd9f682847", + "doc_f5f256549d84" + ], + "https": [ + "doc_3aa0b3792d22" + ], + "human": [ + "doc_07174a8633ac", + "doc_0ac01bce8c79", + "doc_43ad71a4ad0c", + "doc_50ac650fd8f6", + "doc_745f751f1344", + "doc_a24b92541657", + "doc_a5120de838fb", + "doc_b6088784caab", + "doc_d6ad61a79371" + ], + "human-in-the-loop": [ + "doc_0ac01bce8c79", + "doc_2a18de009d56", + "doc_4a1cf8a90e83", + "doc_a24b92541657", + "doc_d6ad61a79371", + "doc_e67e762fb6ab" + ], + "human-readable": [ + "doc_78a8125d2c7e", + "doc_f54217a0f2cf" + ], + "humans": [ + "doc_a24b92541657" + ], + "hundreds": [ + "doc_07174a8633ac" + ], + "hybrid": [ + "doc_a5120de838fb" + ], + "hyphens": [ + "doc_64090a20f830" + ], + "id": [ + "doc_3f60413b3a06", + "doc_4a1cf8a90e83", + "doc_5ee8bbdbd8a8", + "doc_61fd9f682847", + "doc_745f751f1344", + "doc_7b26cbd9a7ae", + "doc_a24b92541657", + "doc_bf91f8d5da74", + "doc_e7d508371e5c" + ], + "idea": [ + "doc_f18f523b6ecc" + ], + "idempotency": [ + "doc_1c5f37ea0ad3", + "doc_4a1cf8a90e83", + "doc_7320ab982fab", + "doc_75887f91c7df" + ], + "idempotency_key": [ + "doc_1c5f37ea0ad3", + "doc_5ee8bbdbd8a8", + "doc_75887f91c7df" + ], + "idempotent": [ + "doc_4a1cf8a90e83", + "doc_75887f91c7df" + ], + "identical": [ + "doc_7320ab982fab" + ], + "identifier": [ + "doc_07174a8633ac", + "doc_1e46cbc6bfae", + "doc_3f60413b3a06", + "doc_4a1cf8a90e83", + "doc_5ee8bbdbd8a8", + "doc_745f751f1344", + "doc_75887f91c7df", + "doc_a24b92541657", + "doc_e7d508371e5c", + "doc_e8794369a1a4", + "doc_f54217a0f2cf" + ], + "identifies": [ + "doc_0ac01bce8c79" + ], + "identify": [ + "doc_43ad71a4ad0c", + "doc_618e34441fa7", + "doc_d147a81bac29", + "doc_f18f523b6ecc" + ], + "identity": [ + "doc_1c5f37ea0ad3", + "doc_1e46cbc6bfae", + "doc_a24b92541657", + "doc_e67e762fb6ab", + "doc_f18f523b6ecc" + ], + "idle": [ + "doc_4462f67b1c03", + "doc_61fd9f682847", + "doc_78b1717fafe3" + ], + "ids": [ + "doc_7320ab982fab", + "doc_75887f91c7df", + "doc_f18f523b6ecc", + "doc_f5f256549d84" + ], + "ignored": [ + "doc_4055659fe573" + ], + "ignores": [ + "doc_136d7a909d51", + "doc_f54217a0f2cf" + ], + "ignoring": [ + "doc_a24b92541657" + ], + "image": [ + "doc_f1e32ed2ffce" + ], + "immediate": [ + "doc_78a8125d2c7e" + ], + "immediately": [ + "doc_07174a8633ac", + "doc_0ac01bce8c79", + "doc_4055659fe573", + "doc_464a2b4a8d35", + "doc_618e34441fa7", + "doc_64090a20f830", + "doc_745f751f1344", + "doc_a24b92541657", + "doc_d6ad61a79371" + ], + "impact": [ + "doc_0ac01bce8c79", + "doc_4e1ebaf40267" + ], + "implement": [ + "doc_0383dbf277ba", + "doc_067033cfc945", + "doc_2a18de009d56", + "doc_4e1ebaf40267", + "doc_618e34441fa7", + "doc_75887f91c7df", + "doc_bbc97cbff254", + "doc_c93c115aeb85" + ], + "implementation": [ + "doc_2a18de009d56", + "doc_3eeb75d383b8", + "doc_50ac650fd8f6", + "doc_f5f256549d84" + ], + "implementations": [ + "doc_2a18de009d56" + ], + "implementing": [ + "doc_5ee8bbdbd8a8" + ], + "implements": [ + "doc_567be1281e2f", + "doc_d6ad61a79371" + ], + "import": [ + "doc_3eeb75d383b8", + "doc_4e1ebaf40267", + "doc_a24b92541657", + "doc_bbc97cbff254", + "doc_c93c115aeb85", + "doc_d147a81bac29", + "doc_deb460ffa5ee", + "doc_f18f523b6ecc" + ], + "important": [ + "doc_0ac01bce8c79", + "doc_43ad71a4ad0c", + "doc_4a1cf8a90e83", + "doc_5ee8bbdbd8a8", + "doc_bbc97cbff254" + ], + "imported": [ + "doc_f18f523b6ecc" + ], + "imports": [ + "doc_18d38fdacc9e", + "doc_2a18de009d56", + "doc_3eeb75d383b8", + "doc_4e1ebaf40267", + "doc_618e34441fa7", + "doc_bbc97cbff254", + "doc_d147a81bac29", + "doc_deb460ffa5ee" + ], + "improves": [ + "doc_4a1cf8a90e83" + ], + "in-memory": [ + "doc_0383dbf277ba", + "doc_136d7a909d51", + "doc_1e46cbc6bfae", + "doc_2a18de009d56", + "doc_618e34441fa7", + "doc_7320ab982fab", + "doc_f5f256549d84" + ], + "incident": [ + "doc_a2e84472f1f9", + "doc_f18f523b6ecc" + ], + "include": [ + "doc_07174a8633ac", + "doc_136d7a909d51", + "doc_2798948c3080", + "doc_43ad71a4ad0c", + "doc_4a1cf8a90e83", + "doc_4e1ebaf40267", + "doc_5ee8bbdbd8a8", + "doc_618e34441fa7", + "doc_b6088784caab", + "doc_d147a81bac29", + "doc_e7d508371e5c" + ], + "included": [ + "doc_5ee8bbdbd8a8" + ], + "includes": [ + "doc_07174a8633ac", + "doc_1e46cbc6bfae", + "doc_3f1383f72c5f", + "doc_5ee8bbdbd8a8", + "doc_618e34441fa7", + "doc_64090a20f830", + "doc_7320ab982fab", + "doc_78a8125d2c7e", + "doc_a2e84472f1f9", + "doc_b6088784caab", + "doc_fdf48d427fc0" + ], + "including": [ + "doc_464a2b4a8d35", + "doc_64090a20f830", + "doc_c346f13ce597", + "doc_d1f7d83e0824" + ], + "inconsistent": [ + "doc_136d7a909d51", + "doc_18d38fdacc9e" + ], + "incorrect": [ + "doc_618e34441fa7" + ], + "incorrectly": [ + "doc_618e34441fa7" + ], + "increase": [ + "doc_7320ab982fab", + "doc_f5f256549d84" + ], + "incremental": [ + "doc_109157c0d2d7", + "doc_5ee8bbdbd8a8", + "doc_f54217a0f2cf" + ], + "incrementally": [ + "doc_3ae3aaf0c12c" + ], + "independently": [ + "doc_0ac01bce8c79", + "doc_7320ab982fab", + "doc_78b1717fafe3", + "doc_a2e84472f1f9" + ], + "index": [ + "doc_3aa0b3792d22" + ], + "indexes": [ + "doc_d147a81bac29", + "doc_f18f523b6ecc" + ], + "indicate": [ + "doc_109157c0d2d7" + ], + "indicates": [ + "doc_067033cfc945" + ], + "indicating": [ + "doc_464a2b4a8d35" + ], + "individual": [ + "doc_4a1cf8a90e83", + "doc_7320ab982fab", + "doc_c346f13ce597", + "doc_c93c115aeb85", + "doc_e34fe24f9585", + "doc_f54217a0f2cf" + ], + "inference": [ + "doc_2a18de009d56", + "doc_f1e32ed2ffce" + ], + "infinite": [ + "doc_618e34441fa7" + ], + "information": [ + "doc_136d7a909d51", + "doc_a2e84472f1f9", + "doc_f5f256549d84" + ], + "infrastructure": [ + "doc_067033cfc945", + "doc_07174a8633ac", + "doc_4055659fe573", + "doc_78a8125d2c7e" + ], + "ingested": [ + "doc_a24b92541657" + ], + "inherit_context_keys": [ + "doc_a24b92541657", + "doc_e67e762fb6ab" + ], + "inherited": [ + "doc_e67e762fb6ab" + ], + "init__": [ + "doc_3eeb75d383b8" + ], + "initial": [ + "doc_1e46cbc6bfae", + "doc_5ee8bbdbd8a8", + "doc_7320ab982fab" + ], + "initialize": [ + "doc_0383dbf277ba" + ], + "inject": [ + "doc_2798948c3080", + "doc_a24b92541657" + ], + "injected": [ + "doc_0ac01bce8c79", + "doc_9e9118ed2141" + ], + "injection": [ + "doc_50ac650fd8f6", + "doc_567be1281e2f", + "doc_64090a20f830", + "doc_745f751f1344", + "doc_9e9118ed2141" + ], + "injects": [ + "doc_3f1383f72c5f" + ], + "inline": [ + "doc_2798948c3080", + "doc_64090a20f830" + ], + "inmemory": [ + "doc_61fd9f682847" + ], + "inmemoryinteractiveprovider": [ + "doc_2a18de009d56" + ], + "inmemorymemorystore": [ + "doc_2a18de009d56", + "doc_bbc97cbff254" + ], + "inmemorytaskqueue": [ + "doc_bbc97cbff254" + ], + "inode": [ + "doc_64090a20f830" + ], + "input": [ + "doc_0ac01bce8c79", + "doc_136d7a909d51", + "doc_18d38fdacc9e", + "doc_1c5f37ea0ad3", + "doc_4a1cf8a90e83", + "doc_567be1281e2f", + "doc_618e34441fa7", + "doc_61fd9f682847", + "doc_a24b92541657", + "doc_bbc97cbff254", + "doc_c346f13ce597", + "doc_c93c115aeb85", + "doc_d6ad61a79371", + "doc_e8794369a1a4", + "doc_f1e32ed2ffce" + ], + "input_fallback": [ + "doc_a24b92541657" + ], + "input_hash": [ + "doc_4a1cf8a90e83" + ], + "input_timeout_s": [ + "doc_a24b92541657" + ], + "input_tokens": [ + "doc_4a1cf8a90e83", + "doc_5ee8bbdbd8a8" + ], + "inputs": [ + "doc_136d7a909d51", + "doc_18d38fdacc9e", + "doc_c93c115aeb85", + "doc_f5f256549d84" + ], + "inside": [ + "doc_64090a20f830", + "doc_bbc97cbff254", + "doc_c346f13ce597" + ], + "inspect": [ + "doc_3ae3aaf0c12c", + "doc_618e34441fa7", + "doc_a2e84472f1f9", + "doc_d147a81bac29" + ], + "inspecting": [ + "doc_745f751f1344", + "doc_d1f7d83e0824" + ], + "inspection": [ + "doc_745f751f1344" + ], + "install": [ + "doc_b6088784caab", + "doc_bf91f8d5da74", + "doc_d147a81bac29", + "doc_deb460ffa5ee" + ], + "installed": [ + "doc_bf91f8d5da74" + ], + "installing": [ + "doc_b6088784caab" + ], + "installs": [ + "doc_b6088784caab" + ], + "instance": [ + "doc_3f60413b3a06", + "doc_a24b92541657", + "doc_e67e762fb6ab" + ], + "instances": [ + "doc_2a18de009d56", + "doc_5ee8bbdbd8a8", + "doc_f54217a0f2cf" + ], + "instantly_": [ + "doc_a24b92541657" + ], + "instead": [ + "doc_0ac01bce8c79", + "doc_109157c0d2d7", + "doc_2a18de009d56", + "doc_4055659fe573", + "doc_4462f67b1c03", + "doc_4a1cf8a90e83", + "doc_9e9118ed2141", + "doc_a24b92541657", + "doc_bf91f8d5da74", + "doc_f5f256549d84" + ], + "instruct": [ + "doc_18d38fdacc9e" + ], + "instruction": [ + "doc_18d38fdacc9e", + "doc_2798948c3080", + "doc_a24b92541657", + "doc_e67e762fb6ab" + ], + "instruction_file": [ + "doc_2798948c3080", + "doc_61fd9f682847", + "doc_64090a20f830", + "doc_a24b92541657", + "doc_e67e762fb6ab" + ], + "instruction_roles": [ + "doc_a24b92541657", + "doc_e67e762fb6ab" + ], + "instructionrole": [ + "doc_e67e762fb6ab" + ], + "instructions": [ + "doc_136d7a909d51", + "doc_18d38fdacc9e", + "doc_1e46cbc6bfae", + "doc_2798948c3080", + "doc_50ac650fd8f6", + "doc_5ee8bbdbd8a8", + "doc_61fd9f682847", + "doc_64090a20f830", + "doc_a24b92541657", + "doc_a2e84472f1f9", + "doc_b6088784caab", + "doc_bbc97cbff254", + "doc_deb460ffa5ee", + "doc_e67e762fb6ab", + "doc_e8794369a1a4", + "doc_eeed52120ccf", + "doc_f12bbbc027a7", + "doc_f18f523b6ecc" + ], + "instructs": [ + "doc_3f60413b3a06" + ], + "int": [ + "doc_464a2b4a8d35", + "doc_4a1cf8a90e83", + "doc_5ee8bbdbd8a8", + "doc_75887f91c7df", + "doc_78a8125d2c7e", + "doc_a24b92541657", + "doc_e67e762fb6ab", + "doc_e7d508371e5c", + "doc_e8794369a1a4", + "doc_f54217a0f2cf" + ], + "intact": [ + "doc_d147a81bac29" + ], + "integrate": [ + "doc_2a18de009d56" + ], + "integration": [ + "doc_1c5f37ea0ad3", + "doc_3eeb75d383b8", + "doc_618e34441fa7", + "doc_c93c115aeb85", + "doc_d147a81bac29", + "doc_d1f7d83e0824", + "doc_eeed52120ccf", + "doc_f1e32ed2ffce" + ], + "integrations": [ + "doc_3aa0b3792d22", + "doc_57e2139f0873", + "doc_e7d508371e5c" + ], + "intended": [ + "doc_618e34441fa7" + ], + "intent": [ + "doc_43ad71a4ad0c" + ], + "intentional": [ + "doc_07174a8633ac", + "doc_4a1cf8a90e83" + ], + "intentionally": [ + "doc_3eeb75d383b8", + "doc_bbc97cbff254" + ], + "interact": [ + "doc_43ad71a4ad0c" + ], + "interaction": [ + "doc_0ac01bce8c79", + "doc_1e46cbc6bfae", + "doc_2a18de009d56", + "doc_3f60413b3a06", + "doc_50ac650fd8f6", + "doc_5ee8bbdbd8a8", + "doc_a24b92541657", + "doc_a5120de838fb", + "doc_d147a81bac29", + "doc_d6ad61a79371", + "doc_e8794369a1a4" + ], + "interaction_mode": [ + "doc_a24b92541657", + "doc_d6ad61a79371" + ], + "interaction_provider": [ + "doc_a24b92541657" + ], + "interactionprovider": [ + "doc_0ac01bce8c79", + "doc_2a18de009d56", + "doc_a24b92541657", + "doc_d6ad61a79371" + ], + "interactive": [ + "doc_43ad71a4ad0c", + "doc_a24b92541657", + "doc_d6ad61a79371" + ], + "intercept": [ + "doc_c346f13ce597" + ], + "intercepting": [ + "doc_c346f13ce597", + "doc_f92685a34f7e" + ], + "interception": [ + "doc_5a65ddab1445" + ], + "intercepts": [ + "doc_a2e84472f1f9" + ], + "interface": [ + "doc_4e1ebaf40267", + "doc_f92685a34f7e" + ], + "interfaces": [ + "doc_2a18de009d56" + ], + "intermediate": [ + "doc_a5120de838fb" + ], + "internal": [ + "doc_0383dbf277ba", + "doc_1c5f37ea0ad3", + "doc_2a18de009d56", + "doc_3aa0b3792d22", + "doc_3eeb75d383b8", + "doc_4055659fe573", + "doc_464a2b4a8d35", + "doc_75887f91c7df", + "doc_bbc97cbff254", + "doc_d147a81bac29", + "doc_f18f523b6ecc", + "doc_f1e32ed2ffce" + ], + "internala2aenvelope": [ + "doc_75887f91c7df" + ], + "internala2aprotocol": [ + "doc_2a18de009d56", + "doc_57e2139f0873", + "doc_bbc97cbff254" + ], + "internally": [ + "doc_07174a8633ac", + "doc_64090a20f830" + ], + "internals": [ + "doc_3ae3aaf0c12c", + "doc_d147a81bac29", + "doc_deb460ffa5ee", + "doc_f18f523b6ecc" + ], + "internals-first": [ + "doc_3ae3aaf0c12c" + ], + "interop": [ + "doc_57e2139f0873", + "doc_e34fe24f9585" + ], + "interoperability": [ + "doc_1c5f37ea0ad3" + ], + "interpreted": [ + "doc_5ee8bbdbd8a8" + ], + "interpreting": [ + "doc_618e34441fa7" + ], + "interprets": [ + "doc_5ee8bbdbd8a8" + ], + "interrupt": [ + "doc_109157c0d2d7", + "doc_e7d508371e5c" + ], + "interrupted": [ + "doc_0383dbf277ba", + "doc_07174a8633ac", + "doc_4055659fe573", + "doc_4a1cf8a90e83", + "doc_e7d508371e5c", + "doc_f54217a0f2cf" + ], + "interruption": [ + "doc_07174a8633ac", + "doc_f54217a0f2cf" + ], + "interrupts": [ + "doc_0383dbf277ba" + ], + "interval": [ + "doc_4462f67b1c03", + "doc_a24b92541657" + ], + "intervals": [ + "doc_618e34441fa7" + ], + "intervention": [ + "doc_d6ad61a79371" + ], + "introduce": [ + "doc_618e34441fa7" + ], + "introduced": [ + "doc_a5120de838fb" + ], + "introduces": [ + "doc_f2ae50c638a4" + ], + "invalid": [ + "doc_4055659fe573", + "doc_5ee8bbdbd8a8", + "doc_78b1717fafe3", + "doc_9e9118ed2141", + "doc_eeed52120ccf" + ], + "invalidated": [ + "doc_64090a20f830" + ], + "invalidation": [ + "doc_64090a20f830" + ], + "invalidrequesterror": [ + "doc_136d7a909d51" + ], + "invariant": [ + "doc_3eeb75d383b8", + "doc_d147a81bac29" + ], + "investigation": [ + "doc_618e34441fa7" + ], + "invocation": [ + "doc_0ac01bce8c79", + "doc_1c5f37ea0ad3", + "doc_a2e84472f1f9", + "doc_d147a81bac29", + "doc_f54217a0f2cf" + ], + "invocations": [ + "doc_1e46cbc6bfae", + "doc_5ee8bbdbd8a8", + "doc_a24b92541657", + "doc_e7d508371e5c" + ], + "invoke": [ + "doc_1c5f37ea0ad3", + "doc_57e2139f0873", + "doc_a2e84472f1f9" + ], + "invoked": [ + "doc_0ac01bce8c79", + "doc_745f751f1344", + "doc_a2e84472f1f9" + ], + "involved": [ + "doc_d6ad61a79371" + ], + "irreversibility": [ + "doc_43ad71a4ad0c" + ], + "irreversible": [ + "doc_43ad71a4ad0c", + "doc_d6ad61a79371" + ], + "is_dir": [ + "doc_464a2b4a8d35" + ], + "is_file": [ + "doc_464a2b4a8d35" + ], + "isn": [ + "doc_50ac650fd8f6", + "doc_57e2139f0873", + "doc_f12bbbc027a7" + ], + "isolated": [ + "doc_618e34441fa7" + ], + "isolating": [ + "doc_567be1281e2f" + ], + "isolation": [ + "doc_1c5f37ea0ad3", + "doc_3ae3aaf0c12c", + "doc_4e1ebaf40267", + "doc_567be1281e2f", + "doc_618e34441fa7" + ], + "issue": [ + "doc_136d7a909d51", + "doc_f54217a0f2cf" + ], + "issuer": [ + "doc_1c5f37ea0ad3" + ], + "issues": [ + "doc_0ac01bce8c79", + "doc_136d7a909d51", + "doc_50ac650fd8f6", + "doc_5ee8bbdbd8a8" + ], + "item": [ + "doc_fdf48d427fc0" + ], + "items": [ + "doc_067033cfc945", + "doc_3aa0b3792d22" + ], + "iterate": [ + "doc_18d38fdacc9e" + ], + "iteration": [ + "doc_e7d508371e5c", + "doc_f54217a0f2cf" + ], + "iterations": [ + "doc_50ac650fd8f6", + "doc_78a8125d2c7e", + "doc_a24b92541657", + "doc_e67e762fb6ab" + ], + "iterator": [ + "doc_f54217a0f2cf" + ], + "its": [ + "doc_0ac01bce8c79", + "doc_1c5f37ea0ad3", + "doc_4a1cf8a90e83", + "doc_618e34441fa7", + "doc_64090a20f830", + "doc_745f751f1344", + "doc_9e9118ed2141", + "doc_a24b92541657", + "doc_c346f13ce597", + "doc_e67e762fb6ab", + "doc_e7d508371e5c", + "doc_f12bbbc027a7" + ], + "itself": [ + "doc_618e34441fa7", + "doc_b6088784caab", + "doc_d147a81bac29", + "doc_deb460ffa5ee", + "doc_f18f523b6ecc" + ], + "jaeger": [ + "doc_78a8125d2c7e" + ], + "jinja2": [ + "doc_2798948c3080", + "doc_64090a20f830" + ], + "jitter": [ + "doc_61fd9f682847", + "doc_7320ab982fab" + ], + "job": [ + "doc_1c5f37ea0ad3", + "doc_75887f91c7df", + "doc_78b1717fafe3" + ], + "jobs": [ + "doc_57e2139f0873", + "doc_78b1717fafe3", + "doc_a24b92541657" + ], + "join": [ + "doc_4055659fe573", + "doc_57e2139f0873", + "doc_f12bbbc027a7" + ], + "join_policy": [ + "doc_18d38fdacc9e" + ], + "joinpolicy": [ + "doc_2a18de009d56" + ], + "journal": [ + "doc_0383dbf277ba", + "doc_2a18de009d56" + ], + "json": [ + "doc_0383dbf277ba", + "doc_3aa0b3792d22", + "doc_3f60413b3a06", + "doc_4a1cf8a90e83", + "doc_745f751f1344", + "doc_78a8125d2c7e", + "doc_9e9118ed2141", + "doc_a24b92541657", + "doc_d147a81bac29", + "doc_e67e762fb6ab", + "doc_e8794369a1a4" + ], + "json-safe": [ + "doc_745f751f1344", + "doc_75887f91c7df", + "doc_f5f256549d84" + ], + "json-serializable": [ + "doc_75887f91c7df" + ], + "jsonvalue": [ + "doc_2a18de009d56", + "doc_745f751f1344", + "doc_e7d508371e5c" + ], + "junit": [ + "doc_618e34441fa7" + ], + "just": [ + "doc_40e30666bfa8", + "doc_50ac650fd8f6", + "doc_c346f13ce597" + ], + "jwt": [ + "doc_1c5f37ea0ad3" + ], + "jwta2aauthprovider": [ + "doc_2a18de009d56" + ], + "keep": [ + "doc_0383dbf277ba", + "doc_18d38fdacc9e", + "doc_2798948c3080", + "doc_3ae3aaf0c12c", + "doc_3eeb75d383b8", + "doc_618e34441fa7", + "doc_b6088784caab", + "doc_bf91f8d5da74", + "doc_d147a81bac29", + "doc_e67e762fb6ab", + "doc_f2ae50c638a4", + "doc_f5f256549d84" + ], + "keepalive": [ + "doc_4462f67b1c03" + ], + "keeping": [ + "doc_07174a8633ac", + "doc_f5f256549d84" + ], + "keeps": [ + "doc_136d7a909d51", + "doc_64090a20f830", + "doc_f2ae50c638a4" + ], + "key": [ + "doc_0383dbf277ba", + "doc_067033cfc945", + "doc_0ac01bce8c79", + "doc_109157c0d2d7", + "doc_2a18de009d56", + "doc_4055659fe573", + "doc_4a1cf8a90e83", + "doc_4e1ebaf40267", + "doc_5a65ddab1445", + "doc_5ee8bbdbd8a8", + "doc_618e34441fa7", + "doc_61fd9f682847", + "doc_75887f91c7df", + "doc_78a8125d2c7e", + "doc_7b26cbd9a7ae", + "doc_9e9118ed2141", + "doc_bf91f8d5da74", + "doc_deb460ffa5ee", + "doc_f2ae50c638a4", + "doc_f54217a0f2cf" + ], + "key-value": [ + "doc_07174a8633ac", + "doc_2798948c3080" + ], + "keyed": [ + "doc_64090a20f830" + ], + "keys": [ + "doc_067033cfc945", + "doc_1c5f37ea0ad3", + "doc_1e46cbc6bfae", + "doc_3f1383f72c5f", + "doc_4a1cf8a90e83", + "doc_567be1281e2f", + "doc_618e34441fa7", + "doc_61fd9f682847", + "doc_64090a20f830", + "doc_7320ab982fab", + "doc_9e9118ed2141", + "doc_a24b92541657", + "doc_d147a81bac29", + "doc_e67e762fb6ab" + ], + "kill": [ + "doc_a24b92541657", + "doc_fdf48d427fc0" + ], + "kind": [ + "doc_78b1717fafe3", + "doc_d6ad61a79371" + ], + "kit": [ + "doc_2a18de009d56" + ], + "knob": [ + "doc_a24b92541657" + ], + "know": [ + "doc_07174a8633ac", + "doc_40e30666bfa8", + "doc_4e1ebaf40267", + "doc_b6088784caab", + "doc_e34fe24f9585" + ], + "knowledge": [ + "doc_0383dbf277ba", + "doc_2798948c3080", + "doc_b6088784caab" + ], + "known": [ + "doc_136d7a909d51", + "doc_618e34441fa7", + "doc_a5120de838fb" + ], + "knows": [ + "doc_2798948c3080", + "doc_50ac650fd8f6", + "doc_745f751f1344", + "doc_e67e762fb6ab" + ], + "kubernetes": [ + "doc_067033cfc945", + "doc_4462f67b1c03" + ], + "kv": [ + "doc_0383dbf277ba" + ], + "l1": [ + "doc_57e2139f0873" + ], + "l2": [ + "doc_57e2139f0873" + ], + "l3": [ + "doc_57e2139f0873" + ], + "l4": [ + "doc_57e2139f0873" + ], + "l5": [ + "doc_57e2139f0873" + ], + "label": [ + "doc_a24b92541657" + ], + "labels": [ + "doc_18d38fdacc9e" + ], + "langchain": [ + "doc_5a65ddab1445" + ], + "langsmith": [ + "doc_5a65ddab1445" + ], + "language": [ + "doc_f92685a34f7e" + ], + "large": [ + "doc_07174a8633ac", + "doc_f5f256549d84" + ], + "larger": [ + "doc_f5f256549d84", + "doc_fdf48d427fc0" + ], + "last": [ + "doc_0383dbf277ba", + "doc_3eeb75d383b8", + "doc_a5120de838fb", + "doc_c346f13ce597" + ], + "latency": [ + "doc_067033cfc945", + "doc_0ac01bce8c79", + "doc_18d38fdacc9e", + "doc_3ae3aaf0c12c", + "doc_4462f67b1c03", + "doc_5ee8bbdbd8a8", + "doc_7320ab982fab", + "doc_745f751f1344", + "doc_78a8125d2c7e", + "doc_c93c115aeb85", + "doc_e7d508371e5c", + "doc_e8794369a1a4", + "doc_f12bbbc027a7", + "doc_f5f256549d84" + ], + "latency_ms": [ + "doc_0ac01bce8c79", + "doc_745f751f1344", + "doc_a2e84472f1f9", + "doc_e7d508371e5c", + "doc_e8794369a1a4", + "doc_f54217a0f2cf" + ], + "later": [ + "doc_3f1383f72c5f", + "doc_4a1cf8a90e83", + "doc_a5120de838fb" + ], + "latest": [ + "doc_07174a8633ac", + "doc_4a1cf8a90e83", + "doc_9e9118ed2141" + ], + "layer": [ + "doc_18d38fdacc9e", + "doc_1c5f37ea0ad3", + "doc_3ae3aaf0c12c", + "doc_4e1ebaf40267", + "doc_745f751f1344", + "doc_9e9118ed2141", + "doc_a24b92541657", + "doc_c346f13ce597", + "doc_eeed52120ccf", + "doc_f92685a34f7e", + "doc_fdf48d427fc0" + ], + "layered": [ + "doc_4e1ebaf40267" + ], + "layers": [ + "doc_1c5f37ea0ad3", + "doc_43ad71a4ad0c", + "doc_464a2b4a8d35", + "doc_64090a20f830", + "doc_c346f13ce597", + "doc_d147a81bac29", + "doc_fdf48d427fc0" + ], + "layout": [ + "doc_3aa0b3792d22" + ], + "lead": [ + "doc_a2e84472f1f9" + ], + "leakage": [ + "doc_567be1281e2f" + ], + "leaks": [ + "doc_18d38fdacc9e" + ], + "learn": [ + "doc_a5120de838fb", + "doc_f18f523b6ecc" + ], + "learn-in-15-minutes": [ + "doc_3aa0b3792d22" + ], + "least": [ + "doc_4e1ebaf40267", + "doc_567be1281e2f" + ], + "least-privilege": [ + "doc_567be1281e2f" + ], + "left": [ + "doc_07174a8633ac", + "doc_4a1cf8a90e83" + ], + "legacy": [ + "doc_4a1cf8a90e83" + ], + "length": [ + "doc_e8794369a1a4" + ], + "less": [ + "doc_eeed52120ccf" + ], + "let": [ + "doc_0383dbf277ba", + "doc_18d38fdacc9e", + "doc_9e9118ed2141", + "doc_fdf48d427fc0" + ], + "lets": [ + "doc_4a1cf8a90e83", + "doc_75887f91c7df", + "doc_d1f7d83e0824", + "doc_f12bbbc027a7" + ], + "letters": [ + "doc_f54217a0f2cf" + ], + "level": [ + "doc_50ac650fd8f6", + "doc_567be1281e2f", + "doc_57e2139f0873", + "doc_a24b92541657", + "doc_a5120de838fb", + "doc_e67e762fb6ab" + ], + "levels": [ + "doc_57e2139f0873", + "doc_64090a20f830" + ], + "levers": [ + "doc_f5f256549d84" + ], + "libraries": [ + "doc_61fd9f682847" + ], + "library": [ + "doc_3aa0b3792d22" + ], + "lifecycle": [ + "doc_0383dbf277ba", + "doc_07174a8633ac", + "doc_0ac01bce8c79", + "doc_109157c0d2d7", + "doc_1e46cbc6bfae", + "doc_2a18de009d56", + "doc_3f60413b3a06", + "doc_4055659fe573", + "doc_5ee8bbdbd8a8", + "doc_618e34441fa7", + "doc_61fd9f682847", + "doc_745f751f1344", + "doc_75887f91c7df", + "doc_78b1717fafe3", + "doc_c346f13ce597", + "doc_c93c115aeb85", + "doc_d147a81bac29", + "doc_deb460ffa5ee", + "doc_e67e762fb6ab", + "doc_e7d508371e5c", + "doc_f2ae50c638a4" + ], + "lifetime": [ + "doc_07174a8633ac" + ], + "light": [ + "doc_3aa0b3792d22" + ], + "lighter": [ + "doc_4a1cf8a90e83" + ], + "lightweight": [ + "doc_3f1383f72c5f" + ], + "like": [ + "doc_0383dbf277ba", + "doc_1e46cbc6bfae", + "doc_40e30666bfa8", + "doc_464a2b4a8d35", + "doc_50ac650fd8f6", + "doc_5ee8bbdbd8a8", + "doc_618e34441fa7", + "doc_745f751f1344", + "doc_a24b92541657", + "doc_b6088784caab", + "doc_c346f13ce597", + "doc_e34fe24f9585", + "doc_e67e762fb6ab" + ], + "limit": [ + "doc_136d7a909d51", + "doc_4055659fe573", + "doc_464a2b4a8d35", + "doc_50ac650fd8f6", + "doc_5ee8bbdbd8a8", + "doc_7320ab982fab", + "doc_745f751f1344", + "doc_78b1717fafe3", + "doc_a24b92541657", + "doc_c93c115aeb85", + "doc_d1f7d83e0824", + "doc_e34fe24f9585", + "doc_e7d508371e5c", + "doc_eeed52120ccf", + "doc_f12bbbc027a7", + "doc_fdf48d427fc0" + ], + "limitations": [ + "doc_618e34441fa7" + ], + "limiting": [ + "doc_1c5f37ea0ad3", + "doc_40e30666bfa8", + "doc_567be1281e2f", + "doc_9e9118ed2141", + "doc_c346f13ce597", + "doc_d147a81bac29", + "doc_e7d508371e5c", + "doc_e8794369a1a4", + "doc_f1e32ed2ffce" + ], + "limits": [ + "doc_0383dbf277ba", + "doc_067033cfc945", + "doc_136d7a909d51", + "doc_18d38fdacc9e", + "doc_1c5f37ea0ad3", + "doc_3ae3aaf0c12c", + "doc_4055659fe573", + "doc_43ad71a4ad0c", + "doc_50ac650fd8f6", + "doc_567be1281e2f", + "doc_5ee8bbdbd8a8", + "doc_618e34441fa7", + "doc_7320ab982fab", + "doc_9e9118ed2141", + "doc_a24b92541657", + "doc_a5120de838fb", + "doc_bbc97cbff254", + "doc_c93c115aeb85", + "doc_d147a81bac29", + "doc_d1f7d83e0824", + "doc_deb460ffa5ee", + "doc_e67e762fb6ab", + "doc_eeed52120ccf", + "doc_f12bbbc027a7", + "doc_f18f523b6ecc", + "doc_f2ae50c638a4", + "doc_f5f256549d84", + "doc_f92685a34f7e", + "doc_fdf48d427fc0" + ], + "line": [ + "doc_78a8125d2c7e" + ], + "line-by-line": [ + "doc_1e46cbc6bfae" + ], + "lineage": [ + "doc_e7d508371e5c" + ], + "lines": [ + "doc_2798948c3080", + "doc_a5120de838fb" + ], + "link": [ + "doc_136d7a909d51", + "doc_3aa0b3792d22" + ], + "linked": [ + "doc_2a18de009d56" + ], + "links": [ + "doc_0383dbf277ba" + ], + "list": [ + "doc_0ac01bce8c79", + "doc_1e46cbc6bfae", + "doc_2a18de009d56", + "doc_464a2b4a8d35", + "doc_4a1cf8a90e83", + "doc_5ee8bbdbd8a8", + "doc_745f751f1344", + "doc_9e9118ed2141", + "doc_a24b92541657", + "doc_a2e84472f1f9", + "doc_b6088784caab", + "doc_c346f13ce597", + "doc_d1f7d83e0824", + "doc_e67e762fb6ab", + "doc_e7d508371e5c", + "doc_e8794369a1a4" + ], + "list_directory": [ + "doc_464a2b4a8d35" + ], + "list_skills": [ + "doc_9e9118ed2141", + "doc_b6088784caab" + ], + "listed": [ + "doc_61fd9f682847", + "doc_c346f13ce597" + ], + "listing": [ + "doc_464a2b4a8d35", + "doc_a5120de838fb" + ], + "listings": [ + "doc_464a2b4a8d35" + ], + "lists": [ + "doc_464a2b4a8d35", + "doc_567be1281e2f", + "doc_a24b92541657" + ], + "litellm": [ + "doc_3f60413b3a06", + "doc_4a1cf8a90e83", + "doc_61fd9f682847", + "doc_f1e32ed2ffce", + "doc_f92685a34f7e" + ], + "litellmprovider": [ + "doc_2a18de009d56" + ], + "lives": [ + "doc_0383dbf277ba", + "doc_78b1717fafe3" + ], + "llm": [ + "doc_0383dbf277ba", + "doc_067033cfc945", + "doc_07174a8633ac", + "doc_0ac01bce8c79", + "doc_136d7a909d51", + "doc_1c5f37ea0ad3", + "doc_1e46cbc6bfae", + "doc_2a18de009d56", + "doc_3aa0b3792d22", + "doc_3f60413b3a06", + "doc_4055659fe573", + "doc_4462f67b1c03", + "doc_464a2b4a8d35", + "doc_4a1cf8a90e83", + "doc_4e1ebaf40267", + "doc_50ac650fd8f6", + "doc_5a65ddab1445", + "doc_5ee8bbdbd8a8", + "doc_618e34441fa7", + "doc_61fd9f682847", + "doc_64090a20f830", + "doc_7320ab982fab", + "doc_745f751f1344", + "doc_78a8125d2c7e", + "doc_78b1717fafe3", + "doc_9e9118ed2141", + "doc_a24b92541657", + "doc_a5120de838fb", + "doc_bbc97cbff254", + "doc_bf91f8d5da74", + "doc_c346f13ce597", + "doc_d147a81bac29", + "doc_d1f7d83e0824", + "doc_deb460ffa5ee", + "doc_e67e762fb6ab", + "doc_e7d508371e5c", + "doc_e8794369a1a4", + "doc_eeed52120ccf", + "doc_f12bbbc027a7", + "doc_f18f523b6ecc", + "doc_f1e32ed2ffce", + "doc_f54217a0f2cf", + "doc_f5f256549d84", + "doc_f92685a34f7e" + ], + "llm-interaction": [ + "doc_3aa0b3792d22" + ], + "llm_called": [ + "doc_f54217a0f2cf" + ], + "llm_calls": [ + "doc_4a1cf8a90e83" + ], + "llm_completed": [ + "doc_f54217a0f2cf" + ], + "llm_failure_policy": [ + "doc_5ee8bbdbd8a8", + "doc_a24b92541657", + "doc_d1f7d83e0824" + ], + "llmbuilder": [ + "doc_2a18de009d56", + "doc_3eeb75d383b8", + "doc_3f60413b3a06", + "doc_5a65ddab1445", + "doc_a5120de838fb", + "doc_bbc97cbff254", + "doc_f92685a34f7e" + ], + "llmchatmiddleware": [ + "doc_c346f13ce597", + "doc_f92685a34f7e" + ], + "llmclient": [ + "doc_2a18de009d56", + "doc_3f60413b3a06", + "doc_eeed52120ccf", + "doc_f92685a34f7e" + ], + "llmconfig": [ + "doc_2a18de009d56" + ], + "llmembedmiddleware": [ + "doc_c346f13ce597", + "doc_f92685a34f7e" + ], + "llmerror": [ + "doc_2a18de009d56" + ], + "llminvalidresponseerror": [ + "doc_3f60413b3a06" + ], + "llmprovider": [ + "doc_2a18de009d56" + ], + "llmrequest": [ + "doc_2a18de009d56", + "doc_3f60413b3a06", + "doc_50ac650fd8f6", + "doc_5ee8bbdbd8a8", + "doc_745f751f1344", + "doc_c346f13ce597", + "doc_e7d508371e5c", + "doc_e8794369a1a4", + "doc_eeed52120ccf", + "doc_f1e32ed2ffce", + "doc_f92685a34f7e" + ], + "llmresponse": [ + "doc_2a18de009d56", + "doc_50ac650fd8f6", + "doc_5ee8bbdbd8a8", + "doc_745f751f1344", + "doc_c346f13ce597", + "doc_e8794369a1a4", + "doc_f1e32ed2ffce", + "doc_f92685a34f7e" + ], + "llmretryableerror": [ + "doc_2a18de009d56" + ], + "llms": [ + "doc_2a18de009d56", + "doc_3aa0b3792d22", + "doc_3eeb75d383b8", + "doc_4e1ebaf40267", + "doc_618e34441fa7", + "doc_a5120de838fb", + "doc_bbc97cbff254", + "doc_c346f13ce597", + "doc_d147a81bac29" + ], + "llmsettings": [ + "doc_3f60413b3a06", + "doc_61fd9f682847" + ], + "llmstreamevent": [ + "doc_c346f13ce597", + "doc_f92685a34f7e" + ], + "llmstreammiddleware": [ + "doc_c346f13ce597", + "doc_f92685a34f7e" + ], + "llmtimeouterror": [ + "doc_2a18de009d56" + ], + "load": [ + "doc_07174a8633ac", + "doc_a5120de838fb" + ], + "loaded": [ + "doc_2798948c3080", + "doc_3f60413b3a06", + "doc_64090a20f830" + ], + "loader": [ + "doc_64090a20f830", + "doc_a5120de838fb" + ], + "loading": [ + "doc_2a18de009d56", + "doc_64090a20f830" + ], + "loads": [ + "doc_07174a8633ac", + "doc_4a1cf8a90e83", + "doc_64090a20f830" + ], + "local": [ + "doc_0383dbf277ba", + "doc_2a18de009d56", + "doc_40e30666bfa8", + "doc_5a65ddab1445", + "doc_a24b92541657", + "doc_a5120de838fb", + "doc_d147a81bac29", + "doc_e34fe24f9585", + "doc_f18f523b6ecc", + "doc_f1e32ed2ffce", + "doc_f5f256549d84" + ], + "localhost": [ + "doc_61fd9f682847" + ], + "locally": [ + "doc_1c5f37ea0ad3", + "doc_5a65ddab1445" + ], + "locking": [ + "doc_a5120de838fb" + ], + "log": [ + "doc_07174a8633ac", + "doc_4055659fe573", + "doc_78a8125d2c7e" + ], + "logged": [ + "doc_4055659fe573", + "doc_4e1ebaf40267" + ], + "logging": [ + "doc_067033cfc945", + "doc_136d7a909d51", + "doc_1e46cbc6bfae", + "doc_745f751f1344", + "doc_9e9118ed2141", + "doc_a24b92541657", + "doc_a5120de838fb", + "doc_c346f13ce597", + "doc_f54217a0f2cf", + "doc_f92685a34f7e" + ], + "logic": [ + "doc_18d38fdacc9e", + "doc_5a65ddab1445", + "doc_e67e762fb6ab" + ], + "logo": [ + "doc_3aa0b3792d22" + ], + "logo-dark": [ + "doc_3aa0b3792d22" + ], + "logo-light": [ + "doc_3aa0b3792d22" + ], + "logs": [ + "doc_07174a8633ac", + "doc_0ac01bce8c79", + "doc_a24b92541657", + "doc_e67e762fb6ab", + "doc_f54217a0f2cf" + ], + "long": [ + "doc_0383dbf277ba", + "doc_067033cfc945", + "doc_07174a8633ac", + "doc_2798948c3080", + "doc_57e2139f0873", + "doc_9e9118ed2141", + "doc_a24b92541657", + "doc_e7d508371e5c", + "doc_f5f256549d84" + ], + "long-running": [ + "doc_0383dbf277ba", + "doc_07174a8633ac", + "doc_1c5f37ea0ad3", + "doc_57e2139f0873", + "doc_78b1717fafe3", + "doc_9e9118ed2141", + "doc_a5120de838fb", + "doc_f5f256549d84", + "doc_fdf48d427fc0" + ], + "long-term": [ + "doc_0383dbf277ba", + "doc_2a18de009d56", + "doc_5a65ddab1445" + ], + "longer": [ + "doc_7320ab982fab" + ], + "longtermmemory": [ + "doc_2a18de009d56" + ], + "looks": [ + "doc_2798948c3080", + "doc_50ac650fd8f6", + "doc_b6088784caab" + ], + "lookup": [ + "doc_07174a8633ac", + "doc_4a1cf8a90e83", + "doc_57e2139f0873", + "doc_745f751f1344" + ], + "loop": [ + "doc_07174a8633ac", + "doc_0ac01bce8c79", + "doc_109157c0d2d7", + "doc_136d7a909d51", + "doc_1e46cbc6bfae", + "doc_3f60413b3a06", + "doc_4a1cf8a90e83", + "doc_4e1ebaf40267", + "doc_50ac650fd8f6", + "doc_5a65ddab1445", + "doc_5ee8bbdbd8a8", + "doc_745f751f1344", + "doc_78a8125d2c7e", + "doc_a24b92541657", + "doc_a5120de838fb", + "doc_b6088784caab", + "doc_bbc97cbff254", + "doc_bf91f8d5da74", + "doc_d147a81bac29", + "doc_deb460ffa5ee", + "doc_e67e762fb6ab", + "doc_e7d508371e5c", + "doc_eeed52120ccf", + "doc_f12bbbc027a7", + "doc_f18f523b6ecc", + "doc_f5f256549d84" + ], + "loops": [ + "doc_18d38fdacc9e", + "doc_50ac650fd8f6", + "doc_5ee8bbdbd8a8", + "doc_618e34441fa7", + "doc_f18f523b6ecc", + "doc_fdf48d427fc0" + ], + "loses": [ + "doc_136d7a909d51" + ], + "losing": [ + "doc_07174a8633ac" + ], + "lost": [ + "doc_0383dbf277ba", + "doc_75887f91c7df" + ], + "low": [ + "doc_e67e762fb6ab", + "doc_eeed52120ccf" + ], + "lower": [ + "doc_7320ab982fab" + ], + "lower-level": [ + "doc_a24b92541657" + ], + "lowercase": [ + "doc_64090a20f830" + ], + "lowest": [ + "doc_eeed52120ccf" + ], + "ls": [ + "doc_a24b92541657" + ], + "machine": [ + "doc_07174a8633ac", + "doc_e7d508371e5c" + ], + "made": [ + "doc_1e46cbc6bfae", + "doc_4a1cf8a90e83", + "doc_5ee8bbdbd8a8", + "doc_f54217a0f2cf" + ], + "main": [ + "doc_745f751f1344", + "doc_c346f13ce597", + "doc_f18f523b6ecc" + ], + "maintain": [ + "doc_3aa0b3792d22", + "doc_5ee8bbdbd8a8", + "doc_7b26cbd9a7ae", + "doc_e7d508371e5c" + ], + "maintainer": [ + "doc_3ae3aaf0c12c", + "doc_3eeb75d383b8", + "doc_d147a81bac29", + "doc_f18f523b6ecc" + ], + "maintainers": [ + "doc_3eeb75d383b8" + ], + "maintains": [ + "doc_5ee8bbdbd8a8" + ], + "major": [ + "doc_a5120de838fb" + ], + "majority": [ + "doc_f12bbbc027a7" + ], + "make": [ + "doc_3ae3aaf0c12c", + "doc_5ee8bbdbd8a8", + "doc_618e34441fa7", + "doc_a24b92541657", + "doc_d147a81bac29", + "doc_e34fe24f9585", + "doc_e67e762fb6ab", + "doc_f5f256549d84" + ], + "makes": [ + "doc_1e46cbc6bfae", + "doc_43ad71a4ad0c", + "doc_f12bbbc027a7" + ], + "making": [ + "doc_136d7a909d51", + "doc_3eeb75d383b8", + "doc_4a1cf8a90e83" + ], + "malformed": [ + "doc_4a1cf8a90e83", + "doc_4e1ebaf40267", + "doc_50ac650fd8f6" + ], + "manage": [ + "doc_50ac650fd8f6", + "doc_78b1717fafe3" + ], + "managed": [ + "doc_d6ad61a79371", + "doc_e67e762fb6ab" + ], + "management": [ + "doc_2a18de009d56", + "doc_4055659fe573", + "doc_4e1ebaf40267", + "doc_78b1717fafe3", + "doc_7b26cbd9a7ae", + "doc_a5120de838fb", + "doc_e67e762fb6ab" + ], + "manager": [ + "doc_067033cfc945" + ], + "managers": [ + "doc_067033cfc945" + ], + "manages": [ + "doc_4e1ebaf40267", + "doc_a2e84472f1f9", + "doc_e7d508371e5c" + ], + "managing": [ + "doc_7320ab982fab" + ], + "manifests": [ + "doc_5ee8bbdbd8a8" + ], + "manual": [ + "doc_75887f91c7df", + "doc_78b1717fafe3" + ], + "many": [ + "doc_136d7a909d51", + "doc_18d38fdacc9e", + "doc_4a1cf8a90e83", + "doc_d1f7d83e0824" + ], + "map": [ + "doc_2a18de009d56", + "doc_618e34441fa7", + "doc_745f751f1344", + "doc_78a8125d2c7e", + "doc_bbc97cbff254", + "doc_d147a81bac29" + ], + "mapping": [ + "doc_5ee8bbdbd8a8" + ], + "maps": [ + "doc_5ee8bbdbd8a8", + "doc_618e34441fa7" + ], + "markdown": [ + "doc_b6088784caab" + ], + "marked": [ + "doc_618e34441fa7", + "doc_a24b92541657" + ], + "markers": [ + "doc_2a18de009d56", + "doc_3f1383f72c5f", + "doc_5ee8bbdbd8a8" + ], + "marks": [ + "doc_75887f91c7df", + "doc_a24b92541657" + ], + "masked": [ + "doc_3f1383f72c5f" + ], + "match": [ + "doc_07174a8633ac", + "doc_0ac01bce8c79", + "doc_3f60413b3a06", + "doc_618e34441fa7", + "doc_745f751f1344" + ], + "matched_rules": [ + "doc_f54217a0f2cf" + ], + "matches": [ + "doc_50ac650fd8f6", + "doc_618e34441fa7", + "doc_d6ad61a79371" + ], + "matching": [ + "doc_07174a8633ac", + "doc_4a1cf8a90e83", + "doc_618e34441fa7", + "doc_eeed52120ccf" + ], + "matrix": [ + "doc_4055659fe573", + "doc_5ee8bbdbd8a8", + "doc_745f751f1344", + "doc_d1f7d83e0824", + "doc_fdf48d427fc0" + ], + "matter": [ + "doc_4a1cf8a90e83" + ], + "matters": [ + "doc_618e34441fa7", + "doc_deb460ffa5ee" + ], + "mature": [ + "doc_50ac650fd8f6" + ], + "maturity": [ + "doc_f12bbbc027a7" + ], + "max": [ + "doc_61fd9f682847", + "doc_78b1717fafe3", + "doc_a24b92541657", + "doc_e67e762fb6ab", + "doc_e8794369a1a4" + ], + "max_age_ms": [ + "doc_07174a8633ac" + ], + "max_chars": [ + "doc_464a2b4a8d35" + ], + "max_connections": [ + "doc_4462f67b1c03" + ], + "max_entries": [ + "doc_07174a8633ac", + "doc_464a2b4a8d35" + ], + "max_idle_connections": [ + "doc_4462f67b1c03" + ], + "max_llm_calls": [ + "doc_a24b92541657", + "doc_f5f256549d84", + "doc_fdf48d427fc0" + ], + "max_output_chars": [ + "doc_745f751f1344", + "doc_a24b92541657" + ], + "max_parallel_subagents_global": [ + "doc_a24b92541657" + ], + "max_parallel_subagents_per_parent": [ + "doc_a24b92541657" + ], + "max_parallel_subagents_per_target_agent": [ + "doc_a24b92541657" + ], + "max_parallel_tools": [ + "doc_a24b92541657" + ], + "max_retries": [ + "doc_7320ab982fab" + ], + "max_steps": [ + "doc_4055659fe573", + "doc_567be1281e2f", + "doc_a24b92541657", + "doc_e67e762fb6ab", + "doc_f5f256549d84", + "doc_fdf48d427fc0" + ], + "max_subagent_depth": [ + "doc_a24b92541657" + ], + "max_subagent_fanout_per_step": [ + "doc_a24b92541657" + ], + "max_thinking_tokens": [ + "doc_a24b92541657" + ], + "max_tokens": [ + "doc_3f60413b3a06", + "doc_5ee8bbdbd8a8", + "doc_e8794369a1a4", + "doc_eeed52120ccf" + ], + "max_tool_calls": [ + "doc_4055659fe573", + "doc_43ad71a4ad0c", + "doc_567be1281e2f", + "doc_a24b92541657" + ], + "max_total_cost_usd": [ + "doc_067033cfc945", + "doc_18d38fdacc9e", + "doc_4055659fe573", + "doc_43ad71a4ad0c", + "doc_50ac650fd8f6", + "doc_567be1281e2f", + "doc_a24b92541657", + "doc_bbc97cbff254", + "doc_c93c115aeb85", + "doc_e67e762fb6ab", + "doc_fdf48d427fc0" + ], + "max_wall_time_s": [ + "doc_4055659fe573", + "doc_a24b92541657", + "doc_f5f256549d84", + "doc_fdf48d427fc0" + ], + "maximum": [ + "doc_4462f67b1c03", + "doc_464a2b4a8d35", + "doc_5ee8bbdbd8a8", + "doc_61fd9f682847", + "doc_a24b92541657", + "doc_e67e762fb6ab" + ], + "may": [ + "doc_0383dbf277ba", + "doc_0ac01bce8c79", + "doc_3eeb75d383b8", + "doc_4055659fe573", + "doc_4a1cf8a90e83", + "doc_618e34441fa7", + "doc_745f751f1344", + "doc_78b1717fafe3", + "doc_bbc97cbff254", + "doc_d147a81bac29", + "doc_f18f523b6ecc", + "doc_f54217a0f2cf" + ], + "mcp": [ + "doc_2a18de009d56", + "doc_3eeb75d383b8", + "doc_40e30666bfa8", + "doc_567be1281e2f", + "doc_57e2139f0873", + "doc_61fd9f682847", + "doc_745f751f1344", + "doc_a24b92541657", + "doc_a5120de838fb", + "doc_bbc97cbff254", + "doc_d147a81bac29", + "doc_deb460ffa5ee", + "doc_e34fe24f9585", + "doc_e67e762fb6ab", + "doc_f18f523b6ecc", + "doc_f2ae50c638a4" + ], + "mcp-compatible": [ + "doc_e34fe24f9585" + ], + "mcp-discovered": [ + "doc_e67e762fb6ab" + ], + "mcp-exposed": [ + "doc_e34fe24f9585" + ], + "mcp-server": [ + "doc_3aa0b3792d22" + ], + "mcp-sourced": [ + "doc_40e30666bfa8" + ], + "mcp_client_integration": [ + "doc_3aa0b3792d22" + ], + "mcp_servers": [ + "doc_a24b92541657", + "doc_e67e762fb6ab" + ], + "mcpserver": [ + "doc_bbc97cbff254" + ], + "mcpserverlike": [ + "doc_e67e762fb6ab" + ], + "md": [ + "doc_2798948c3080", + "doc_2a18de009d56", + "doc_64090a20f830", + "doc_9e9118ed2141", + "doc_b6088784caab", + "doc_e67e762fb6ab" + ], + "meaning": [ + "doc_4055659fe573", + "doc_567be1281e2f", + "doc_78b1717fafe3", + "doc_bbc97cbff254", + "doc_bf91f8d5da74", + "doc_e7d508371e5c" + ], + "means": [ + "doc_4e1ebaf40267", + "doc_50ac650fd8f6", + "doc_618e34441fa7", + "doc_64090a20f830" + ], + "measure": [ + "doc_f5f256549d84" + ], + "measurement": [ + "doc_f5f256549d84" + ], + "mechanism": [ + "doc_1c5f37ea0ad3", + "doc_4a1cf8a90e83", + "doc_d6ad61a79371" + ], + "median": [ + "doc_78a8125d2c7e" + ], + "medium": [ + "doc_78a8125d2c7e", + "doc_e67e762fb6ab" + ], + "memories": [ + "doc_0383dbf277ba" + ], + "memory": [ + "doc_0383dbf277ba", + "doc_07174a8633ac", + "doc_136d7a909d51", + "doc_18d38fdacc9e", + "doc_1e46cbc6bfae", + "doc_2a18de009d56", + "doc_3aa0b3792d22", + "doc_3ae3aaf0c12c", + "doc_3eeb75d383b8", + "doc_3f1383f72c5f", + "doc_3f60413b3a06", + "doc_4055659fe573", + "doc_4462f67b1c03", + "doc_4a1cf8a90e83", + "doc_4e1ebaf40267", + "doc_50ac650fd8f6", + "doc_5a65ddab1445", + "doc_618e34441fa7", + "doc_61fd9f682847", + "doc_64090a20f830", + "doc_75887f91c7df", + "doc_78b1717fafe3", + "doc_7b26cbd9a7ae", + "doc_a24b92541657", + "doc_a5120de838fb", + "doc_bbc97cbff254", + "doc_bf91f8d5da74", + "doc_d147a81bac29", + "doc_deb460ffa5ee", + "doc_e7d508371e5c", + "doc_f18f523b6ecc", + "doc_f2ae50c638a4", + "doc_f54217a0f2cf", + "doc_f5f256549d84" + ], + "memory_store": [ + "doc_61fd9f682847", + "doc_a24b92541657", + "doc_f5f256549d84" + ], + "memorycapabilities": [ + "doc_2a18de009d56" + ], + "memorycompactionresult": [ + "doc_07174a8633ac", + "doc_2a18de009d56", + "doc_bbc97cbff254" + ], + "memoryevent": [ + "doc_2a18de009d56", + "doc_5a65ddab1445" + ], + "memorystore": [ + "doc_0383dbf277ba", + "doc_2a18de009d56", + "doc_5a65ddab1445", + "doc_a24b92541657", + "doc_f5f256549d84" + ], + "mental-model": [ + "doc_3aa0b3792d22" + ], + "mention": [ + "doc_136d7a909d51" + ], + "merge": [ + "doc_a5120de838fb" + ], + "merged": [ + "doc_07174a8633ac", + "doc_4a1cf8a90e83", + "doc_a24b92541657", + "doc_e67e762fb6ab" + ], + "merging": [ + "doc_3eeb75d383b8", + "doc_618e34441fa7" + ], + "meshes": [ + "doc_57e2139f0873" + ], + "message": [ + "doc_07174a8633ac", + "doc_0ac01bce8c79", + "doc_1e46cbc6bfae", + "doc_2a18de009d56", + "doc_3f1383f72c5f", + "doc_5a65ddab1445", + "doc_5ee8bbdbd8a8", + "doc_745f751f1344", + "doc_75887f91c7df", + "doc_78b1717fafe3", + "doc_9e9118ed2141", + "doc_a24b92541657", + "doc_a2e84472f1f9", + "doc_c93c115aeb85", + "doc_d6ad61a79371", + "doc_e7d508371e5c", + "doc_e8794369a1a4", + "doc_eeed52120ccf", + "doc_f54217a0f2cf" + ], + "message_count": [ + "doc_4a1cf8a90e83" + ], + "message_type": [ + "doc_75887f91c7df" + ], + "messages": [ + "doc_0383dbf277ba", + "doc_136d7a909d51", + "doc_4a1cf8a90e83", + "doc_50ac650fd8f6", + "doc_5ee8bbdbd8a8", + "doc_75887f91c7df", + "doc_e8794369a1a4", + "doc_eeed52120ccf" + ], + "messaging": [ + "doc_1c5f37ea0ad3", + "doc_2a18de009d56", + "doc_3aa0b3792d22", + "doc_3eeb75d383b8", + "doc_4e1ebaf40267", + "doc_75887f91c7df", + "doc_bbc97cbff254", + "doc_d147a81bac29" + ], + "metadata": [ + "doc_07174a8633ac", + "doc_2a18de009d56", + "doc_3eeb75d383b8", + "doc_3f1383f72c5f", + "doc_567be1281e2f", + "doc_5ee8bbdbd8a8", + "doc_64090a20f830", + "doc_75887f91c7df", + "doc_78a8125d2c7e", + "doc_9e9118ed2141", + "doc_a24b92541657", + "doc_c346f13ce597", + "doc_d147a81bac29", + "doc_d1f7d83e0824", + "doc_e7d508371e5c" + ], + "method": [ + "doc_07174a8633ac", + "doc_3f60413b3a06", + "doc_bbc97cbff254" + ], + "method-chaining": [ + "doc_3f60413b3a06" + ], + "methods": [ + "doc_07174a8633ac", + "doc_2798948c3080", + "doc_3f60413b3a06", + "doc_bbc97cbff254" + ], + "metric": [ + "doc_067033cfc945", + "doc_0ac01bce8c79", + "doc_2a18de009d56", + "doc_618e34441fa7", + "doc_78a8125d2c7e" + ], + "metric_agent_llm_calls_total": [ + "doc_2a18de009d56" + ], + "metrics": [ + "doc_067033cfc945", + "doc_2a18de009d56", + "doc_3ae3aaf0c12c", + "doc_4e1ebaf40267", + "doc_50ac650fd8f6", + "doc_618e34441fa7", + "doc_78a8125d2c7e", + "doc_c93c115aeb85", + "doc_fdf48d427fc0" + ], + "microservice": [ + "doc_57e2139f0873" + ], + "mid-run": [ + "doc_4a1cf8a90e83" + ], + "mid-stream": [ + "doc_7b26cbd9a7ae" + ], + "middleware": [ + "doc_2a18de009d56", + "doc_3f60413b3a06", + "doc_4462f67b1c03", + "doc_50ac650fd8f6", + "doc_5a65ddab1445", + "doc_5ee8bbdbd8a8", + "doc_618e34441fa7", + "doc_745f751f1344", + "doc_9e9118ed2141", + "doc_a24b92541657", + "doc_a5120de838fb", + "doc_c346f13ce597", + "doc_deb460ffa5ee", + "doc_f2ae50c638a4", + "doc_f92685a34f7e" + ], + "middlewares": [ + "doc_a24b92541657" + ], + "middlewarestack": [ + "doc_2a18de009d56" + ], + "might": [ + "doc_0383dbf277ba" + ], + "migrate": [ + "doc_5a65ddab1445" + ], + "migration": [ + "doc_2a18de009d56", + "doc_3aa0b3792d22", + "doc_3eeb75d383b8", + "doc_4a1cf8a90e83", + "doc_5a65ddab1445" + ], + "milliseconds": [ + "doc_0ac01bce8c79", + "doc_4a1cf8a90e83", + "doc_745f751f1344", + "doc_75887f91c7df", + "doc_a2e84472f1f9" + ], + "min": [ + "doc_7320ab982fab", + "doc_78a8125d2c7e" + ], + "mini": [ + "doc_1e46cbc6bfae", + "doc_5ee8bbdbd8a8", + "doc_61fd9f682847", + "doc_d1f7d83e0824", + "doc_eeed52120ccf", + "doc_f92685a34f7e" + ], + "minimal": [ + "doc_136d7a909d51", + "doc_a5120de838fb" + ], + "minimal_chat_agent": [ + "doc_3aa0b3792d22" + ], + "minimum": [ + "doc_61fd9f682847" + ], + "mint": [ + "doc_3aa0b3792d22" + ], + "mintlify": [ + "doc_3aa0b3792d22" + ], + "minutes": [ + "doc_0383dbf277ba", + "doc_18d38fdacc9e", + "doc_57e2139f0873", + "doc_5a65ddab1445", + "doc_e67e762fb6ab", + "doc_f18f523b6ecc" + ], + "misplaced": [ + "doc_618e34441fa7" + ], + "missing": [ + "doc_136d7a909d51", + "doc_18d38fdacc9e", + "doc_2798948c3080", + "doc_4a1cf8a90e83", + "doc_78b1717fafe3" + ], + "mistake": [ + "doc_18d38fdacc9e", + "doc_3ae3aaf0c12c", + "doc_57e2139f0873", + "doc_a24b92541657" + ], + "mistakes": [ + "doc_18d38fdacc9e", + "doc_3ae3aaf0c12c" + ], + "mistral": [ + "doc_f1e32ed2ffce" + ], + "mitigation": [ + "doc_567be1281e2f" + ], + "mixing": [ + "doc_18d38fdacc9e", + "doc_40e30666bfa8" + ], + "mocked": [ + "doc_618e34441fa7" + ], + "mocking": [ + "doc_618e34441fa7" + ], + "mode": [ + "doc_0383dbf277ba", + "doc_136d7a909d51", + "doc_1e46cbc6bfae", + "doc_3f1383f72c5f", + "doc_43ad71a4ad0c", + "doc_4e1ebaf40267", + "doc_78a8125d2c7e", + "doc_d6ad61a79371" + ], + "model": [ + "doc_0ac01bce8c79", + "doc_109157c0d2d7", + "doc_136d7a909d51", + "doc_18d38fdacc9e", + "doc_1c5f37ea0ad3", + "doc_1e46cbc6bfae", + "doc_2a18de009d56", + "doc_3ae3aaf0c12c", + "doc_3f60413b3a06", + "doc_4055659fe573", + "doc_40e30666bfa8", + "doc_43ad71a4ad0c", + "doc_464a2b4a8d35", + "doc_4a1cf8a90e83", + "doc_4e1ebaf40267", + "doc_50ac650fd8f6", + "doc_567be1281e2f", + "doc_57e2139f0873", + "doc_5ee8bbdbd8a8", + "doc_61fd9f682847", + "doc_64090a20f830", + "doc_7320ab982fab", + "doc_745f751f1344", + "doc_78b1717fafe3", + "doc_9e9118ed2141", + "doc_a24b92541657", + "doc_a2e84472f1f9", + "doc_a5120de838fb", + "doc_b6088784caab", + "doc_bbc97cbff254", + "doc_bf91f8d5da74", + "doc_c346f13ce597", + "doc_d1f7d83e0824", + "doc_d6ad61a79371", + "doc_deb460ffa5ee", + "doc_e34fe24f9585", + "doc_e67e762fb6ab", + "doc_e7d508371e5c", + "doc_e8794369a1a4", + "doc_eeed52120ccf", + "doc_f12bbbc027a7", + "doc_f18f523b6ecc", + "doc_f54217a0f2cf", + "doc_f5f256549d84", + "doc_f92685a34f7e" + ], + "model-generated": [ + "doc_5ee8bbdbd8a8" + ], + "model-provided": [ + "doc_bf91f8d5da74" + ], + "model-visible": [ + "doc_a24b92541657" + ], + "model_name": [ + "doc_2798948c3080" + ], + "model_resolver": [ + "doc_5ee8bbdbd8a8", + "doc_a24b92541657", + "doc_e67e762fb6ab", + "doc_eeed52120ccf" + ], + "modelnotfounderror": [ + "doc_136d7a909d51" + ], + "models": [ + "doc_0ac01bce8c79", + "doc_18d38fdacc9e", + "doc_2a18de009d56", + "doc_3ae3aaf0c12c", + "doc_3eeb75d383b8", + "doc_43ad71a4ad0c", + "doc_4e1ebaf40267", + "doc_50ac650fd8f6", + "doc_57e2139f0873", + "doc_618e34441fa7", + "doc_7320ab982fab", + "doc_a24b92541657", + "doc_a5120de838fb", + "doc_bf91f8d5da74", + "doc_c346f13ce597", + "doc_d1f7d83e0824", + "doc_e67e762fb6ab", + "doc_f1e32ed2ffce", + "doc_f5f256549d84", + "doc_f92685a34f7e", + "doc_fdf48d427fc0" + ], + "modes": [ + "doc_109157c0d2d7", + "doc_3f1383f72c5f", + "doc_5a65ddab1445", + "doc_a5120de838fb", + "doc_d6ad61a79371", + "doc_e67e762fb6ab", + "doc_e7d508371e5c" + ], + "modify": [ + "doc_c346f13ce597" + ], + "modify_": [ + "doc_43ad71a4ad0c" + ], + "modifying": [ + "doc_d6ad61a79371" + ], + "module": [ + "doc_2a18de009d56", + "doc_4e1ebaf40267", + "doc_9e9118ed2141", + "doc_bbc97cbff254", + "doc_d147a81bac29" + ], + "modules": [ + "doc_2a18de009d56", + "doc_3eeb75d383b8", + "doc_4e1ebaf40267", + "doc_618e34441fa7", + "doc_bbc97cbff254", + "doc_f18f523b6ecc" + ], + "moment": [ + "doc_0ac01bce8c79" + ], + "money": [ + "doc_d6ad61a79371" + ], + "mongo": [ + "doc_2a18de009d56" + ], + "monitor": [ + "doc_3ae3aaf0c12c", + "doc_7320ab982fab", + "doc_78b1717fafe3", + "doc_f5f256549d84", + "doc_fdf48d427fc0" + ], + "monitoring": [ + "doc_067033cfc945", + "doc_18d38fdacc9e", + "doc_2a18de009d56", + "doc_4e1ebaf40267", + "doc_567be1281e2f", + "doc_78a8125d2c7e", + "doc_a5120de838fb", + "doc_b6088784caab", + "doc_d1f7d83e0824", + "doc_fdf48d427fc0" + ], + "more": [ + "doc_0ac01bce8c79", + "doc_18d38fdacc9e", + "doc_61fd9f682847", + "doc_745f751f1344", + "doc_a24b92541657", + "doc_a2e84472f1f9", + "doc_eeed52120ccf", + "doc_f12bbbc027a7", + "doc_f18f523b6ecc" + ], + "most": [ + "doc_0383dbf277ba", + "doc_07174a8633ac", + "doc_18d38fdacc9e", + "doc_43ad71a4ad0c", + "doc_4a1cf8a90e83", + "doc_57e2139f0873", + "doc_618e34441fa7", + "doc_7b26cbd9a7ae", + "doc_bbc97cbff254", + "doc_e67e762fb6ab", + "doc_f2ae50c638a4", + "doc_fdf48d427fc0" + ], + "move": [ + "doc_3ae3aaf0c12c", + "doc_50ac650fd8f6", + "doc_57e2139f0873", + "doc_f5f256549d84" + ], + "moved": [ + "doc_f54217a0f2cf" + ], + "moves": [ + "doc_75887f91c7df", + "doc_78b1717fafe3" + ], + "moving": [ + "doc_f5f256549d84" + ], + "mtime": [ + "doc_64090a20f830" + ], + "much": [ + "doc_136d7a909d51" + ], + "multi-agent": [ + "doc_3ae3aaf0c12c", + "doc_57e2139f0873", + "doc_a5120de838fb", + "doc_d1f7d83e0824", + "doc_e67e762fb6ab", + "doc_f12bbbc027a7" + ], + "multi-model": [ + "doc_a5120de838fb" + ], + "multi-process": [ + "doc_0383dbf277ba", + "doc_78b1717fafe3" + ], + "multi-stage": [ + "doc_067033cfc945" + ], + "multi-step": [ + "doc_eeed52120ccf" + ], + "multi-turn": [ + "doc_0383dbf277ba", + "doc_18d38fdacc9e", + "doc_3f60413b3a06", + "doc_7b26cbd9a7ae", + "doc_a5120de838fb" + ], + "multi-worker": [ + "doc_067033cfc945", + "doc_78b1717fafe3" + ], + "multi_model_fallback": [ + "doc_3aa0b3792d22" + ], + "multiple": [ + "doc_0ac01bce8c79", + "doc_136d7a909d51", + "doc_2798948c3080", + "doc_43ad71a4ad0c", + "doc_4a1cf8a90e83", + "doc_5a65ddab1445", + "doc_64090a20f830", + "doc_eeed52120ccf", + "doc_f12bbbc027a7", + "doc_fdf48d427fc0" + ], + "must": [ + "doc_07174a8633ac", + "doc_3ae3aaf0c12c", + "doc_4a1cf8a90e83", + "doc_567be1281e2f", + "doc_64090a20f830", + "doc_745f751f1344", + "doc_75887f91c7df", + "doc_9e9118ed2141", + "doc_a24b92541657", + "doc_c346f13ce597", + "doc_d147a81bac29", + "doc_f12bbbc027a7", + "doc_f18f523b6ecc" + ], + "mutating": [ + "doc_18d38fdacc9e", + "doc_3ae3aaf0c12c", + "doc_43ad71a4ad0c", + "doc_567be1281e2f", + "doc_9e9118ed2141" + ], + "mutations": [ + "doc_50ac650fd8f6", + "doc_b6088784caab" + ], + "mw": [ + "doc_9e9118ed2141" + ], + "my": [ + "doc_50ac650fd8f6" + ], + "name": [ + "doc_18d38fdacc9e", + "doc_1e46cbc6bfae", + "doc_2798948c3080", + "doc_2a18de009d56", + "doc_3aa0b3792d22", + "doc_3f60413b3a06", + "doc_43ad71a4ad0c", + "doc_464a2b4a8d35", + "doc_4a1cf8a90e83", + "doc_5ee8bbdbd8a8", + "doc_61fd9f682847", + "doc_64090a20f830", + "doc_745f751f1344", + "doc_75887f91c7df", + "doc_9e9118ed2141", + "doc_a24b92541657", + "doc_a2e84472f1f9", + "doc_b6088784caab", + "doc_bbc97cbff254", + "doc_e67e762fb6ab", + "doc_e7d508371e5c", + "doc_eeed52120ccf", + "doc_f18f523b6ecc" + ], + "name-to-filename": [ + "doc_64090a20f830" + ], + "named": [ + "doc_3f60413b3a06", + "doc_9e9118ed2141", + "doc_c93c115aeb85" + ], + "names": [ + "doc_3eeb75d383b8", + "doc_464a2b4a8d35", + "doc_618e34441fa7", + "doc_64090a20f830", + "doc_a24b92541657", + "doc_e67e762fb6ab", + "doc_eeed52120ccf" + ], + "namespace": [ + "doc_7320ab982fab" + ], + "nano": [ + "doc_d1f7d83e0824", + "doc_eeed52120ccf", + "doc_f92685a34f7e" + ], + "narrow": [ + "doc_18d38fdacc9e", + "doc_3ae3aaf0c12c", + "doc_618e34441fa7", + "doc_f5f256549d84" + ], + "native": [ + "doc_3f60413b3a06", + "doc_e8794369a1a4" + ], + "natural": [ + "doc_07174a8633ac" + ], + "navigates": [ + "doc_7b26cbd9a7ae" + ], + "navigation": [ + "doc_3aa0b3792d22", + "doc_3eeb75d383b8", + "doc_d147a81bac29", + "doc_f18f523b6ecc" + ], + "nbsp": [ + "doc_57e2139f0873" + ], + "necessarily": [ + "doc_109157c0d2d7" + ], + "necessary": [ + "doc_067033cfc945", + "doc_618e34441fa7" + ], + "need": [ + "doc_1c5f37ea0ad3", + "doc_1e46cbc6bfae", + "doc_2a18de009d56", + "doc_3f60413b3a06", + "doc_4e1ebaf40267", + "doc_50ac650fd8f6", + "doc_57e2139f0873", + "doc_618e34441fa7", + "doc_61fd9f682847", + "doc_64090a20f830", + "doc_75887f91c7df", + "doc_7b26cbd9a7ae", + "doc_a5120de838fb", + "doc_b6088784caab", + "doc_deb460ffa5ee", + "doc_e67e762fb6ab", + "doc_f12bbbc027a7", + "doc_f18f523b6ecc", + "doc_f2ae50c638a4", + "doc_f5f256549d84", + "doc_fdf48d427fc0" + ], + "needed": [ + "doc_07174a8633ac", + "doc_18d38fdacc9e", + "doc_4a1cf8a90e83", + "doc_5a65ddab1445", + "doc_d1f7d83e0824", + "doc_d6ad61a79371", + "doc_f12bbbc027a7" + ], + "needs": [ + "doc_07174a8633ac", + "doc_136d7a909d51", + "doc_3ae3aaf0c12c", + "doc_3f60413b3a06", + "doc_57e2139f0873", + "doc_78b1717fafe3", + "doc_b6088784caab", + "doc_e67e762fb6ab", + "doc_f12bbbc027a7", + "doc_f18f523b6ecc" + ], + "negligible": [ + "doc_64090a20f830" + ], + "neither": [ + "doc_64090a20f830" + ], + "nested": [ + "doc_e7d508371e5c" + ], + "nesting": [ + "doc_e7d508371e5c" + ], + "network": [ + "doc_1e46cbc6bfae", + "doc_78b1717fafe3", + "doc_a24b92541657", + "doc_f18f523b6ecc" + ], + "never": [ + "doc_067033cfc945", + "doc_0ac01bce8c79", + "doc_1c5f37ea0ad3", + "doc_43ad71a4ad0c", + "doc_4a1cf8a90e83", + "doc_4e1ebaf40267", + "doc_745f751f1344", + "doc_a24b92541657", + "doc_e8794369a1a4", + "doc_f18f523b6ecc", + "doc_f92685a34f7e" + ], + "new": [ + "doc_109157c0d2d7", + "doc_1e46cbc6bfae", + "doc_2a18de009d56", + "doc_4462f67b1c03", + "doc_5ee8bbdbd8a8", + "doc_618e34441fa7", + "doc_a5120de838fb", + "doc_d147a81bac29", + "doc_f54217a0f2cf" + ], + "next": [ + "doc_0383dbf277ba", + "doc_067033cfc945", + "doc_07174a8633ac", + "doc_109157c0d2d7", + "doc_136d7a909d51", + "doc_18d38fdacc9e", + "doc_1c5f37ea0ad3", + "doc_2798948c3080", + "doc_3ae3aaf0c12c", + "doc_3f1383f72c5f", + "doc_4055659fe573", + "doc_40e30666bfa8", + "doc_4462f67b1c03", + "doc_464a2b4a8d35", + "doc_4e1ebaf40267", + "doc_567be1281e2f", + "doc_57e2139f0873", + "doc_5a65ddab1445", + "doc_5ee8bbdbd8a8", + "doc_61fd9f682847", + "doc_64090a20f830", + "doc_7320ab982fab", + "doc_745f751f1344", + "doc_75887f91c7df", + "doc_78a8125d2c7e", + "doc_78b1717fafe3", + "doc_7b26cbd9a7ae", + "doc_9e9118ed2141", + "doc_a24b92541657", + "doc_b6088784caab", + "doc_c346f13ce597", + "doc_c93c115aeb85", + "doc_d147a81bac29", + "doc_d1f7d83e0824", + "doc_deb460ffa5ee", + "doc_e34fe24f9585", + "doc_e67e762fb6ab", + "doc_e7d508371e5c", + "doc_e8794369a1a4", + "doc_eeed52120ccf", + "doc_f12bbbc027a7", + "doc_f18f523b6ecc", + "doc_f1e32ed2ffce", + "doc_f2ae50c638a4", + "doc_f92685a34f7e", + "doc_fdf48d427fc0" + ], + "nice-to-have": [ + "doc_f12bbbc027a7" + ], + "no": [ + "doc_0383dbf277ba", + "doc_067033cfc945", + "doc_07174a8633ac", + "doc_0ac01bce8c79", + "doc_136d7a909d51", + "doc_18d38fdacc9e", + "doc_1e46cbc6bfae", + "doc_3f60413b3a06", + "doc_4055659fe573", + "doc_4a1cf8a90e83", + "doc_4e1ebaf40267", + "doc_50ac650fd8f6", + "doc_567be1281e2f", + "doc_57e2139f0873", + "doc_5a65ddab1445", + "doc_5ee8bbdbd8a8", + "doc_61fd9f682847", + "doc_64090a20f830", + "doc_75887f91c7df", + "doc_78b1717fafe3", + "doc_a24b92541657", + "doc_d6ad61a79371" + ], + "node": [ + "doc_618e34441fa7" + ], + "nodes": [ + "doc_618e34441fa7", + "doc_a24b92541657" + ], + "non-alphanumeric": [ + "doc_64090a20f830" + ], + "non-critical": [ + "doc_18d38fdacc9e" + ], + "non-deterministic": [ + "doc_136d7a909d51", + "doc_618e34441fa7" + ], + "non-developers": [ + "doc_64090a20f830" + ], + "non-dict": [ + "doc_745f751f1344" + ], + "non-empty": [ + "doc_2798948c3080", + "doc_64090a20f830" + ], + "non-essential": [ + "doc_4055659fe573" + ], + "non-fatal": [ + "doc_0ac01bce8c79", + "doc_4055659fe573", + "doc_4e1ebaf40267", + "doc_50ac650fd8f6", + "doc_78b1717fafe3", + "doc_f54217a0f2cf" + ], + "non-headless": [ + "doc_a24b92541657" + ], + "non-openai": [ + "doc_f92685a34f7e" + ], + "non-streaming": [ + "doc_109157c0d2d7", + "doc_5ee8bbdbd8a8", + "doc_c346f13ce597", + "doc_eeed52120ccf", + "doc_f92685a34f7e" + ], + "non-terminal": [ + "doc_4a1cf8a90e83" + ], + "non-throwing": [ + "doc_745f751f1344" + ], + "none": [ + "doc_07174a8633ac", + "doc_4462f67b1c03", + "doc_4a1cf8a90e83", + "doc_567be1281e2f", + "doc_5ee8bbdbd8a8", + "doc_61fd9f682847", + "doc_7320ab982fab", + "doc_745f751f1344", + "doc_a24b92541657", + "doc_a2e84472f1f9", + "doc_d1f7d83e0824", + "doc_e67e762fb6ab", + "doc_e7d508371e5c", + "doc_e8794369a1a4", + "doc_f54217a0f2cf" + ], + "normal": [ + "doc_4a1cf8a90e83", + "doc_c93c115aeb85" + ], + "normalization": [ + "doc_4a1cf8a90e83" + ], + "normalize": [ + "doc_e8794369a1a4" + ], + "normalize_checkpoint_record": [ + "doc_4a1cf8a90e83" + ], + "normalized": [ + "doc_5ee8bbdbd8a8", + "doc_bbc97cbff254", + "doc_e8794369a1a4", + "doc_f1e32ed2ffce" + ], + "normalized_model": [ + "doc_4a1cf8a90e83", + "doc_bbc97cbff254" + ], + "normalizes": [ + "doc_4a1cf8a90e83", + "doc_5ee8bbdbd8a8", + "doc_64090a20f830", + "doc_f92685a34f7e" + ], + "normally": [ + "doc_0ac01bce8c79", + "doc_d6ad61a79371" + ], + "not": [ + "doc_0383dbf277ba", + "doc_07174a8633ac", + "doc_109157c0d2d7", + "doc_136d7a909d51", + "doc_18d38fdacc9e", + "doc_1e46cbc6bfae", + "doc_2798948c3080", + "doc_2a18de009d56", + "doc_3eeb75d383b8", + "doc_3f60413b3a06", + "doc_4055659fe573", + "doc_4a1cf8a90e83", + "doc_4e1ebaf40267", + "doc_50ac650fd8f6", + "doc_567be1281e2f", + "doc_57e2139f0873", + "doc_5a65ddab1445", + "doc_5ee8bbdbd8a8", + "doc_618e34441fa7", + "doc_61fd9f682847", + "doc_64090a20f830", + "doc_7320ab982fab", + "doc_745f751f1344", + "doc_75887f91c7df", + "doc_b6088784caab", + "doc_bbc97cbff254", + "doc_c346f13ce597", + "doc_e67e762fb6ab", + "doc_f12bbbc027a7", + "doc_f18f523b6ecc", + "doc_f54217a0f2cf" + ], + "nothing": [ + "doc_4055659fe573", + "doc_78a8125d2c7e" + ], + "notice": [ + "doc_43ad71a4ad0c" + ], + "notification": [ + "doc_5ee8bbdbd8a8" + ], + "nucleus": [ + "doc_5ee8bbdbd8a8", + "doc_e8794369a1a4" + ], + "number": [ + "doc_4a1cf8a90e83", + "doc_78a8125d2c7e", + "doc_a24b92541657", + "doc_f54217a0f2cf" + ], + "o-bound": [ + "doc_f5f256549d84" + ], + "o-heavy": [ + "doc_9e9118ed2141" + ], + "o-series": [ + "doc_f1e32ed2ffce", + "doc_f92685a34f7e" + ], + "oauth": [ + "doc_1c5f37ea0ad3" + ], + "object": [ + "doc_4e1ebaf40267", + "doc_5a65ddab1445", + "doc_78a8125d2c7e", + "doc_bf91f8d5da74", + "doc_c346f13ce597", + "doc_e67e762fb6ab", + "doc_f18f523b6ecc" + ], + "objects": [ + "doc_3f60413b3a06", + "doc_5ee8bbdbd8a8", + "doc_61fd9f682847", + "doc_745f751f1344", + "doc_f18f523b6ecc" + ], + "observability": [ + "doc_0ac01bce8c79", + "doc_18d38fdacc9e", + "doc_2a18de009d56", + "doc_3aa0b3792d22", + "doc_3eeb75d383b8", + "doc_4e1ebaf40267", + "doc_50ac650fd8f6", + "doc_57e2139f0873", + "doc_618e34441fa7", + "doc_78a8125d2c7e", + "doc_bbc97cbff254", + "doc_d147a81bac29", + "doc_deb460ffa5ee", + "doc_f18f523b6ecc", + "doc_f5f256549d84", + "doc_fdf48d427fc0" + ], + "observable": [ + "doc_f18f523b6ecc" + ], + "observe": [ + "doc_43ad71a4ad0c", + "doc_fdf48d427fc0" + ], + "observers": [ + "doc_3f60413b3a06" + ], + "occur": [ + "doc_0ac01bce8c79", + "doc_f54217a0f2cf" + ], + "occurred": [ + "doc_e7d508371e5c" + ], + "occurs": [ + "doc_464a2b4a8d35", + "doc_4a1cf8a90e83", + "doc_64090a20f830", + "doc_f54217a0f2cf" + ], + "off": [ + "doc_07174a8633ac", + "doc_4a1cf8a90e83" + ], + "often": [ + "doc_f5f256549d84" + ], + "old": [ + "doc_0383dbf277ba", + "doc_07174a8633ac" + ], + "older": [ + "doc_07174a8633ac" + ], + "omit": [ + "doc_07174a8633ac" + ], + "on-call": [ + "doc_3ae3aaf0c12c" + ], + "once": [ + "doc_136d7a909d51", + "doc_3ae3aaf0c12c", + "doc_40e30666bfa8", + "doc_75887f91c7df", + "doc_9e9118ed2141", + "doc_e34fe24f9585" + ], + "one": [ + "doc_0ac01bce8c79", + "doc_1e46cbc6bfae", + "doc_3ae3aaf0c12c", + "doc_4055659fe573", + "doc_4e1ebaf40267", + "doc_50ac650fd8f6", + "doc_57e2139f0873", + "doc_618e34441fa7", + "doc_64090a20f830", + "doc_745f751f1344", + "doc_78a8125d2c7e", + "doc_b6088784caab", + "doc_bf91f8d5da74", + "doc_c346f13ce597", + "doc_deb460ffa5ee", + "doc_e67e762fb6ab", + "doc_e7d508371e5c", + "doc_f12bbbc027a7", + "doc_f18f523b6ecc", + "doc_f2ae50c638a4", + "doc_f54217a0f2cf", + "doc_fdf48d427fc0" + ], + "one-off": [ + "doc_f18f523b6ecc" + ], + "ones": [ + "doc_07174a8633ac", + "doc_a5120de838fb" + ], + "only": [ + "doc_0383dbf277ba", + "doc_067033cfc945", + "doc_07174a8633ac", + "doc_0ac01bce8c79", + "doc_18d38fdacc9e", + "doc_1e46cbc6bfae", + "doc_3ae3aaf0c12c", + "doc_3eeb75d383b8", + "doc_3f60413b3a06", + "doc_4e1ebaf40267", + "doc_50ac650fd8f6", + "doc_57e2139f0873", + "doc_61fd9f682847", + "doc_64090a20f830", + "doc_9e9118ed2141", + "doc_b6088784caab", + "doc_bbc97cbff254", + "doc_d1f7d83e0824", + "doc_e67e762fb6ab", + "doc_e7d508371e5c" + ], + "open": [ + "doc_4055659fe573", + "doc_7320ab982fab", + "doc_78a8125d2c7e", + "doc_a24b92541657", + "doc_e34fe24f9585", + "doc_eeed52120ccf", + "doc_f5f256549d84" + ], + "openai": [ + "doc_3f60413b3a06", + "doc_4a1cf8a90e83", + "doc_5a65ddab1445", + "doc_61fd9f682847", + "doc_745f751f1344", + "doc_eeed52120ccf", + "doc_f1e32ed2ffce", + "doc_f92685a34f7e" + ], + "openai-compatible": [ + "doc_5ee8bbdbd8a8" + ], + "openai-format": [ + "doc_5ee8bbdbd8a8" + ], + "openai_api_key": [ + "doc_1e46cbc6bfae", + "doc_61fd9f682847", + "doc_bf91f8d5da74" + ], + "openaiprovider": [ + "doc_2a18de009d56" + ], + "opens": [ + "doc_5ee8bbdbd8a8", + "doc_a24b92541657", + "doc_d1f7d83e0824" + ], + "opentelemetry": [ + "doc_78a8125d2c7e" + ], + "operate": [ + "doc_c346f13ce597" + ], + "operates": [ + "doc_07174a8633ac", + "doc_c346f13ce597" + ], + "operating": [ + "doc_fdf48d427fc0" + ], + "operation": [ + "doc_464a2b4a8d35", + "doc_c346f13ce597", + "doc_f92685a34f7e" + ], + "operational": [ + "doc_2a18de009d56", + "doc_3ae3aaf0c12c", + "doc_f18f523b6ecc" + ], + "operations": [ + "doc_3ae3aaf0c12c", + "doc_4055659fe573", + "doc_43ad71a4ad0c", + "doc_4462f67b1c03", + "doc_464a2b4a8d35", + "doc_567be1281e2f", + "doc_61fd9f682847", + "doc_78a8125d2c7e", + "doc_9e9118ed2141", + "doc_c346f13ce597" + ], + "operator": [ + "doc_3ae3aaf0c12c", + "doc_9e9118ed2141", + "doc_a5120de838fb", + "doc_d6ad61a79371" + ], + "operators": [ + "doc_3ae3aaf0c12c", + "doc_d6ad61a79371" + ], + "opt-in": [ + "doc_567be1281e2f" + ], + "optimization": [ + "doc_a5120de838fb", + "doc_d1f7d83e0824" + ], + "optimize": [ + "doc_4462f67b1c03" + ], + "optional": [ + "doc_0383dbf277ba", + "doc_07174a8633ac", + "doc_1e46cbc6bfae", + "doc_4055659fe573", + "doc_4e1ebaf40267", + "doc_618e34441fa7", + "doc_61fd9f682847", + "doc_a24b92541657", + "doc_a2e84472f1f9", + "doc_d147a81bac29" + ], + "optionally": [ + "doc_9e9118ed2141" + ], + "options": [ + "doc_61fd9f682847" + ], + "opus": [ + "doc_f1e32ed2ffce", + "doc_f92685a34f7e" + ], + "orchestrate": [ + "doc_f12bbbc027a7" + ], + "orchestration": [ + "doc_18d38fdacc9e", + "doc_4e1ebaf40267", + "doc_57e2139f0873", + "doc_a24b92541657", + "doc_d6ad61a79371", + "doc_f12bbbc027a7" + ], + "order": [ + "doc_2798948c3080", + "doc_618e34441fa7", + "doc_61fd9f682847", + "doc_745f751f1344", + "doc_9e9118ed2141", + "doc_a24b92541657", + "doc_c346f13ce597", + "doc_eeed52120ccf", + "doc_f12bbbc027a7", + "doc_f18f523b6ecc", + "doc_f54217a0f2cf" + ], + "ordered": [ + "doc_a24b92541657", + "doc_a5120de838fb", + "doc_bbc97cbff254", + "doc_bf91f8d5da74", + "doc_d1f7d83e0824" + ], + "ordering": [ + "doc_4a1cf8a90e83", + "doc_618e34441fa7", + "doc_e67e762fb6ab" + ], + "organizations": [ + "doc_1c5f37ea0ad3" + ], + "organize": [ + "doc_0383dbf277ba" + ], + "organized": [ + "doc_18d38fdacc9e", + "doc_2a18de009d56" + ], + "original": [ + "doc_07174a8633ac", + "doc_4a1cf8a90e83" + ], + "originally": [ + "doc_4a1cf8a90e83" + ], + "origins": [ + "doc_61fd9f682847", + "doc_e34fe24f9585" + ], + "os": [ + "doc_64090a20f830" + ], + "otel": [ + "doc_18d38fdacc9e", + "doc_4055659fe573", + "doc_4e1ebaf40267", + "doc_567be1281e2f", + "doc_5a65ddab1445", + "doc_78a8125d2c7e" + ], + "otel-compatible": [ + "doc_78a8125d2c7e" + ], + "other": [ + "doc_1e46cbc6bfae", + "doc_2a18de009d56", + "doc_3f1383f72c5f", + "doc_4e1ebaf40267", + "doc_50ac650fd8f6", + "doc_57e2139f0873", + "doc_5a65ddab1445", + "doc_9e9118ed2141", + "doc_e67e762fb6ab", + "doc_f12bbbc027a7", + "doc_f5f256549d84" + ], + "others": [ + "doc_4e1ebaf40267", + "doc_f12bbbc027a7" + ], + "out": [ + "doc_0ac01bce8c79", + "doc_4055659fe573", + "doc_a24b92541657", + "doc_f12bbbc027a7" + ], + "out-of-process": [ + "doc_3f1383f72c5f" + ], + "outage": [ + "doc_d1f7d83e0824" + ], + "outages": [ + "doc_7320ab982fab", + "doc_d1f7d83e0824" + ], + "outcome": [ + "doc_0ac01bce8c79", + "doc_d1f7d83e0824" + ], + "outermost": [ + "doc_745f751f1344", + "doc_c346f13ce597" + ], + "output": [ + "doc_0ac01bce8c79", + "doc_136d7a909d51", + "doc_18d38fdacc9e", + "doc_1e46cbc6bfae", + "doc_2798948c3080", + "doc_2a18de009d56", + "doc_3ae3aaf0c12c", + "doc_3f1383f72c5f", + "doc_3f60413b3a06", + "doc_4a1cf8a90e83", + "doc_50ac650fd8f6", + "doc_567be1281e2f", + "doc_5ee8bbdbd8a8", + "doc_618e34441fa7", + "doc_61fd9f682847", + "doc_7320ab982fab", + "doc_745f751f1344", + "doc_78a8125d2c7e", + "doc_9e9118ed2141", + "doc_a24b92541657", + "doc_a5120de838fb", + "doc_b6088784caab", + "doc_bbc97cbff254", + "doc_c346f13ce597", + "doc_d147a81bac29", + "doc_deb460ffa5ee", + "doc_e7d508371e5c", + "doc_e8794369a1a4", + "doc_f12bbbc027a7", + "doc_f1e32ed2ffce", + "doc_f2ae50c638a4", + "doc_f54217a0f2cf" + ], + "output_hash": [ + "doc_4a1cf8a90e83" + ], + "output_text": [ + "doc_1e46cbc6bfae", + "doc_a2e84472f1f9" + ], + "output_tokens": [ + "doc_4a1cf8a90e83", + "doc_5ee8bbdbd8a8" + ], + "outputs": [ + "doc_136d7a909d51", + "doc_a2e84472f1f9", + "doc_a5120de838fb", + "doc_f5f256549d84" + ], + "outside": [ + "doc_464a2b4a8d35", + "doc_64090a20f830", + "doc_9e9118ed2141", + "doc_a24b92541657", + "doc_d6ad61a79371", + "doc_deb460ffa5ee" + ], + "over": [ + "doc_0383dbf277ba", + "doc_07174a8633ac", + "doc_3f60413b3a06", + "doc_4e1ebaf40267", + "doc_5a65ddab1445", + "doc_78a8125d2c7e", + "doc_eeed52120ccf", + "doc_fdf48d427fc0" + ], + "overflow": [ + "doc_18d38fdacc9e" + ], + "overhead": [ + "doc_3f60413b3a06", + "doc_64090a20f830" + ], + "overkill": [ + "doc_f12bbbc027a7" + ], + "overlay": [ + "doc_07174a8633ac" + ], + "override": [ + "doc_4462f67b1c03", + "doc_5ee8bbdbd8a8", + "doc_a24b92541657" + ], + "overview": [ + "doc_3aa0b3792d22", + "doc_567be1281e2f", + "doc_c346f13ce597" + ], + "own": [ + "doc_1c5f37ea0ad3", + "doc_40e30666bfa8", + "doc_464a2b4a8d35", + "doc_64090a20f830", + "doc_78b1717fafe3", + "doc_9e9118ed2141", + "doc_a2e84472f1f9", + "doc_c346f13ce597", + "doc_e67e762fb6ab", + "doc_f18f523b6ecc", + "doc_f1e32ed2ffce" + ], + "owning": [ + "doc_2a18de009d56" + ], + "owns": [ + "doc_2a18de009d56", + "doc_f18f523b6ecc", + "doc_f2ae50c638a4" + ], + "p95": [ + "doc_067033cfc945" + ], + "p99": [ + "doc_78a8125d2c7e" + ], + "package": [ + "doc_2a18de009d56", + "doc_3eeb75d383b8", + "doc_bbc97cbff254", + "doc_bf91f8d5da74", + "doc_d147a81bac29", + "doc_deb460ffa5ee", + "doc_f18f523b6ecc" + ], + "package-level": [ + "doc_3eeb75d383b8", + "doc_d147a81bac29" + ], + "packages": [ + "doc_2a18de009d56" + ], + "packs": [ + "doc_e67e762fb6ab" + ], + "page": [ + "doc_0ac01bce8c79", + "doc_2a18de009d56", + "doc_3eeb75d383b8", + "doc_43ad71a4ad0c", + "doc_5ee8bbdbd8a8", + "doc_618e34441fa7", + "doc_61fd9f682847", + "doc_745f751f1344", + "doc_a24b92541657", + "doc_bbc97cbff254", + "doc_bf91f8d5da74", + "doc_d147a81bac29", + "doc_eeed52120ccf", + "doc_f54217a0f2cf" + ], + "pages": [ + "doc_2a18de009d56", + "doc_3aa0b3792d22", + "doc_3ae3aaf0c12c", + "doc_d147a81bac29" + ], + "pair": [ + "doc_464a2b4a8d35" + ], + "pairs": [ + "doc_2798948c3080" + ], + "parallel": [ + "doc_57e2139f0873", + "doc_5ee8bbdbd8a8", + "doc_618e34441fa7", + "doc_a24b92541657", + "doc_c93c115aeb85", + "doc_f12bbbc027a7" + ], + "parallelize": [ + "doc_f12bbbc027a7" + ], + "parameter": [ + "doc_07174a8633ac", + "doc_0ac01bce8c79", + "doc_3f60413b3a06", + "doc_4462f67b1c03", + "doc_464a2b4a8d35", + "doc_5ee8bbdbd8a8", + "doc_7320ab982fab", + "doc_9e9118ed2141", + "doc_e8794369a1a4" + ], + "parameters_schema": [ + "doc_745f751f1344" + ], + "parent": [ + "doc_618e34441fa7", + "doc_a24b92541657", + "doc_e67e762fb6ab", + "doc_e7d508371e5c" + ], + "parse": [ + "doc_618e34441fa7" + ], + "parsed": [ + "doc_3f60413b3a06", + "doc_5ee8bbdbd8a8" + ], + "parses": [ + "doc_9e9118ed2141" + ], + "parsing": [ + "doc_2a18de009d56" + ], + "part": [ + "doc_f2ae50c638a4", + "doc_f5f256549d84" + ], + "partial": [ + "doc_4055659fe573", + "doc_4a1cf8a90e83", + "doc_618e34441fa7", + "doc_a24b92541657", + "doc_e7d508371e5c", + "doc_fdf48d427fc0" + ], + "participates": [ + "doc_745f751f1344" + ], + "parts": [ + "doc_57e2139f0873", + "doc_e67e762fb6ab" + ], + "pass": [ + "doc_3f60413b3a06", + "doc_40e30666bfa8", + "doc_618e34441fa7", + "doc_7b26cbd9a7ae", + "doc_9e9118ed2141", + "doc_c346f13ce597", + "doc_c93c115aeb85", + "doc_e7d508371e5c", + "doc_eeed52120ccf", + "doc_f2ae50c638a4", + "doc_f5f256549d84" + ], + "passed": [ + "doc_4a1cf8a90e83", + "doc_61fd9f682847", + "doc_64090a20f830", + "doc_745f751f1344", + "doc_a24b92541657" + ], + "passes": [ + "doc_4a1cf8a90e83", + "doc_567be1281e2f", + "doc_5ee8bbdbd8a8", + "doc_7320ab982fab", + "doc_745f751f1344", + "doc_a24b92541657", + "doc_c346f13ce597", + "doc_eeed52120ccf" + ], + "passing": [ + "doc_618e34441fa7", + "doc_a24b92541657" + ], + "password": [ + "doc_3f1383f72c5f", + "doc_61fd9f682847" + ], + "path": [ + "doc_109157c0d2d7", + "doc_2798948c3080", + "doc_464a2b4a8d35", + "doc_50ac650fd8f6", + "doc_5ee8bbdbd8a8", + "doc_61fd9f682847", + "doc_64090a20f830", + "doc_a24b92541657", + "doc_b6088784caab", + "doc_bf91f8d5da74", + "doc_c93c115aeb85", + "doc_deb460ffa5ee", + "doc_e67e762fb6ab", + "doc_e7d508371e5c", + "doc_f18f523b6ecc" + ], + "paths": [ + "doc_464a2b4a8d35", + "doc_61fd9f682847", + "doc_a24b92541657", + "doc_d147a81bac29", + "doc_eeed52120ccf" + ], + "pattern": [ + "doc_3f60413b3a06", + "doc_618e34441fa7", + "doc_9e9118ed2141", + "doc_a2e84472f1f9", + "doc_a5120de838fb", + "doc_c346f13ce597", + "doc_d6ad61a79371", + "doc_f54217a0f2cf", + "doc_f92685a34f7e" + ], + "patterns": [ + "doc_067033cfc945", + "doc_136d7a909d51", + "doc_18d38fdacc9e", + "doc_3ae3aaf0c12c", + "doc_4e1ebaf40267", + "doc_5a65ddab1445", + "doc_64090a20f830", + "doc_7b26cbd9a7ae", + "doc_9e9118ed2141", + "doc_a5120de838fb", + "doc_c346f13ce597", + "doc_f2ae50c638a4", + "doc_f54217a0f2cf" + ], + "pause": [ + "doc_0383dbf277ba", + "doc_109157c0d2d7", + "doc_d6ad61a79371" + ], + "paused": [ + "doc_a24b92541657" + ], + "pauses": [ + "doc_07174a8633ac", + "doc_0ac01bce8c79", + "doc_4a1cf8a90e83", + "doc_a24b92541657", + "doc_d6ad61a79371" + ], + "payload": [ + "doc_0ac01bce8c79", + "doc_3f1383f72c5f", + "doc_4a1cf8a90e83", + "doc_745f751f1344", + "doc_75887f91c7df", + "doc_9e9118ed2141", + "doc_bbc97cbff254", + "doc_c346f13ce597", + "doc_e7d508371e5c", + "doc_f54217a0f2cf" + ], + "payloads": [ + "doc_4a1cf8a90e83", + "doc_5ee8bbdbd8a8", + "doc_7320ab982fab", + "doc_f5f256549d84" + ], + "pending": [ + "doc_067033cfc945", + "doc_07174a8633ac", + "doc_4a1cf8a90e83", + "doc_a24b92541657" + ], + "pending_llm_response": [ + "doc_4a1cf8a90e83" + ], + "per": [ + "doc_0383dbf277ba", + "doc_067033cfc945", + "doc_18d38fdacc9e", + "doc_3ae3aaf0c12c", + "doc_4462f67b1c03", + "doc_567be1281e2f", + "doc_61fd9f682847", + "doc_64090a20f830", + "doc_745f751f1344", + "doc_a24b92541657", + "doc_b6088784caab", + "doc_e34fe24f9585", + "doc_e67e762fb6ab", + "doc_eeed52120ccf", + "doc_f5f256549d84" + ], + "per-agent": [ + "doc_1c5f37ea0ad3" + ], + "per-call": [ + "doc_43ad71a4ad0c" + ], + "per-caller": [ + "doc_1c5f37ea0ad3", + "doc_567be1281e2f" + ], + "per-case": [ + "doc_618e34441fa7" + ], + "per-item": [ + "doc_fdf48d427fc0" + ], + "per-parent-run": [ + "doc_a24b92541657" + ], + "per-request": [ + "doc_4462f67b1c03", + "doc_c346f13ce597" + ], + "per-target-agent": [ + "doc_a24b92541657" + ], + "per-tool": [ + "doc_567be1281e2f" + ], + "percentile": [ + "doc_78a8125d2c7e" + ], + "perform": [ + "doc_4a1cf8a90e83" + ], + "performance": [ + "doc_0383dbf277ba", + "doc_3aa0b3792d22", + "doc_4462f67b1c03", + "doc_78a8125d2c7e", + "doc_78b1717fafe3", + "doc_f5f256549d84" + ], + "permanent": [ + "doc_4055659fe573", + "doc_43ad71a4ad0c" + ], + "permissions": [ + "doc_567be1281e2f" + ], + "permits": [ + "doc_1c5f37ea0ad3" + ], + "persist": [ + "doc_109157c0d2d7", + "doc_136d7a909d51", + "doc_4055659fe573", + "doc_43ad71a4ad0c", + "doc_61fd9f682847", + "doc_a5120de838fb", + "doc_f18f523b6ecc" + ], + "persisted": [ + "doc_0383dbf277ba", + "doc_4a1cf8a90e83", + "doc_4e1ebaf40267", + "doc_a24b92541657", + "doc_bbc97cbff254" + ], + "persistence": [ + "doc_0383dbf277ba", + "doc_2a18de009d56", + "doc_4a1cf8a90e83", + "doc_4e1ebaf40267", + "doc_78b1717fafe3", + "doc_7b26cbd9a7ae", + "doc_a5120de838fb", + "doc_d147a81bac29", + "doc_e7d508371e5c", + "doc_f18f523b6ecc" + ], + "persistent": [ + "doc_0383dbf277ba", + "doc_18d38fdacc9e", + "doc_3ae3aaf0c12c", + "doc_61fd9f682847", + "doc_deb460ffa5ee", + "doc_f5f256549d84" + ], + "persists": [ + "doc_0383dbf277ba", + "doc_07174a8633ac" + ], + "pgvector": [ + "doc_0383dbf277ba" + ], + "phase": [ + "doc_3ae3aaf0c12c", + "doc_4a1cf8a90e83", + "doc_5ee8bbdbd8a8" + ], + "phase-specific": [ + "doc_4a1cf8a90e83" + ], + "phases": [ + "doc_18d38fdacc9e", + "doc_4a1cf8a90e83" + ], + "philosophy": [ + "doc_5a65ddab1445" + ], + "picked": [ + "doc_64090a20f830", + "doc_78b1717fafe3" + ], + "picks": [ + "doc_07174a8633ac", + "doc_78b1717fafe3" + ], + "pip": [ + "doc_136d7a909d51" + ], + "pipeline": [ + "doc_0ac01bce8c79", + "doc_464a2b4a8d35", + "doc_4e1ebaf40267", + "doc_618e34441fa7", + "doc_64090a20f830", + "doc_7320ab982fab", + "doc_745f751f1344", + "doc_78a8125d2c7e", + "doc_c346f13ce597", + "doc_c93c115aeb85", + "doc_f12bbbc027a7", + "doc_fdf48d427fc0" + ], + "pipelines": [ + "doc_2a18de009d56", + "doc_57e2139f0873", + "doc_618e34441fa7", + "doc_a2e84472f1f9", + "doc_d6ad61a79371", + "doc_f54217a0f2cf" + ], + "pipes": [ + "doc_a24b92541657" + ], + "places": [ + "doc_0ac01bce8c79" + ], + "plain-english": [ + "doc_618e34441fa7" + ], + "plans": [ + "doc_618e34441fa7" + ], + "platforms": [ + "doc_d6ad61a79371" + ], + "playbook": [ + "doc_567be1281e2f", + "doc_c93c115aeb85" + ], + "playbooks": [ + "doc_b6088784caab" + ], + "pluggable": [ + "doc_2a18de009d56" + ], + "plus": [ + "doc_40e30666bfa8", + "doc_745f751f1344" + ], + "point": [ + "doc_0383dbf277ba", + "doc_07174a8633ac", + "doc_c346f13ce597", + "doc_f2ae50c638a4", + "doc_f92685a34f7e" + ], + "pointers": [ + "doc_0383dbf277ba" + ], + "pointing": [ + "doc_64090a20f830" + ], + "points": [ + "doc_2a18de009d56", + "doc_745f751f1344", + "doc_9e9118ed2141", + "doc_a24b92541657", + "doc_eeed52120ccf", + "doc_f92685a34f7e" + ], + "policies": [ + "doc_07174a8633ac", + "doc_2a18de009d56", + "doc_4055659fe573", + "doc_4462f67b1c03", + "doc_4e1ebaf40267", + "doc_57e2139f0873", + "doc_7320ab982fab", + "doc_745f751f1344", + "doc_a24b92541657", + "doc_d147a81bac29", + "doc_deb460ffa5ee", + "doc_e67e762fb6ab", + "doc_e7d508371e5c", + "doc_f12bbbc027a7" + ], + "policy": [ + "doc_07174a8633ac", + "doc_0ac01bce8c79", + "doc_18d38fdacc9e", + "doc_1e46cbc6bfae", + "doc_2a18de009d56", + "doc_3ae3aaf0c12c", + "doc_3f60413b3a06", + "doc_4055659fe573", + "doc_40e30666bfa8", + "doc_43ad71a4ad0c", + "doc_464a2b4a8d35", + "doc_50ac650fd8f6", + "doc_567be1281e2f", + "doc_57e2139f0873", + "doc_5ee8bbdbd8a8", + "doc_618e34441fa7", + "doc_61fd9f682847", + "doc_7320ab982fab", + "doc_745f751f1344", + "doc_9e9118ed2141", + "doc_a24b92541657", + "doc_a2e84472f1f9", + "doc_a5120de838fb", + "doc_b6088784caab", + "doc_bbc97cbff254", + "doc_c346f13ce597", + "doc_d147a81bac29", + "doc_d1f7d83e0824", + "doc_d6ad61a79371", + "doc_e34fe24f9585", + "doc_e67e762fb6ab", + "doc_f12bbbc027a7", + "doc_f18f523b6ecc", + "doc_f54217a0f2cf", + "doc_fdf48d427fc0" + ], + "policy-aware": [ + "doc_e67e762fb6ab" + ], + "policy-checked": [ + "doc_e7d508371e5c" + ], + "policy-driven": [ + "doc_4055659fe573", + "doc_745f751f1344" + ], + "policy-gated": [ + "doc_43ad71a4ad0c", + "doc_9e9118ed2141" + ], + "policy_decision": [ + "doc_0ac01bce8c79", + "doc_43ad71a4ad0c", + "doc_d6ad61a79371", + "doc_f54217a0f2cf" + ], + "policy_engine": [ + "doc_a24b92541657", + "doc_e67e762fb6ab" + ], + "policy_id": [ + "doc_f54217a0f2cf" + ], + "policy_roles": [ + "doc_a24b92541657", + "doc_e67e762fb6ab" + ], + "policy_with_hitl": [ + "doc_3aa0b3792d22" + ], + "policydecision": [ + "doc_2a18de009d56", + "doc_d6ad61a79371" + ], + "policyengine": [ + "doc_0ac01bce8c79", + "doc_2a18de009d56", + "doc_43ad71a4ad0c", + "doc_745f751f1344", + "doc_9e9118ed2141", + "doc_a24b92541657", + "doc_bbc97cbff254", + "doc_d6ad61a79371", + "doc_e67e762fb6ab" + ], + "policyevaluation": [ + "doc_2a18de009d56" + ], + "policyevent": [ + "doc_0ac01bce8c79", + "doc_2a18de009d56", + "doc_d6ad61a79371" + ], + "policyrole": [ + "doc_bbc97cbff254", + "doc_e67e762fb6ab" + ], + "policyrule": [ + "doc_2a18de009d56", + "doc_bbc97cbff254" + ], + "poll": [ + "doc_a24b92541657" + ], + "poller": [ + "doc_3f1383f72c5f" + ], + "pool": [ + "doc_61fd9f682847", + "doc_64090a20f830", + "doc_78b1717fafe3" + ], + "poolconfig": [ + "doc_4462f67b1c03" + ], + "pooling": [ + "doc_0383dbf277ba", + "doc_4462f67b1c03", + "doc_78b1717fafe3", + "doc_a5120de838fb" + ], + "pools": [ + "doc_4462f67b1c03" + ], + "port": [ + "doc_61fd9f682847" + ], + "portable": [ + "doc_5a65ddab1445" + ], + "possible": [ + "doc_1e46cbc6bfae", + "doc_57e2139f0873", + "doc_a5120de838fb" + ], + "post": [ + "doc_9e9118ed2141", + "doc_a5120de838fb" + ], + "post-execution": [ + "doc_a5120de838fb" + ], + "post-hook": [ + "doc_c346f13ce597" + ], + "post-hooks": [ + "doc_c346f13ce597" + ], + "post-tool": [ + "doc_0383dbf277ba", + "doc_4a1cf8a90e83" + ], + "post_llm": [ + "doc_4a1cf8a90e83" + ], + "post_subagent_batch": [ + "doc_4a1cf8a90e83" + ], + "post_tool_batch": [ + "doc_4a1cf8a90e83" + ], + "postgres": [ + "doc_0383dbf277ba", + "doc_618e34441fa7", + "doc_61fd9f682847", + "doc_f5f256549d84" + ], + "postgresmemorystore": [ + "doc_2a18de009d56" + ], + "postgresql": [ + "doc_067033cfc945", + "doc_61fd9f682847" + ], + "posthook": [ + "doc_745f751f1344", + "doc_9e9118ed2141", + "doc_a24b92541657" + ], + "posthooks": [ + "doc_745f751f1344", + "doc_9e9118ed2141", + "doc_a24b92541657" + ], + "posture": [ + "doc_567be1281e2f", + "doc_61fd9f682847" + ], + "potential": [ + "doc_9e9118ed2141" + ], + "powered": [ + "doc_3f60413b3a06" + ], + "pr": [ + "doc_18d38fdacc9e" + ], + "practice": [ + "doc_0ac01bce8c79", + "doc_9e9118ed2141" + ], + "practices": [ + "doc_067033cfc945" + ], + "pre": [ + "doc_9e9118ed2141", + "doc_a5120de838fb" + ], + "pre-bound": [ + "doc_e67e762fb6ab" + ], + "pre-built": [ + "doc_a24b92541657", + "doc_c346f13ce597", + "doc_e67e762fb6ab" + ], + "pre-configured": [ + "doc_eeed52120ccf" + ], + "pre-execution": [ + "doc_a5120de838fb" + ], + "pre-hook": [ + "doc_c346f13ce597" + ], + "pre-hooks": [ + "doc_c346f13ce597" + ], + "pre-llm": [ + "doc_0383dbf277ba", + "doc_4a1cf8a90e83" + ], + "pre-resolved": [ + "doc_4a1cf8a90e83" + ], + "pre-shared": [ + "doc_1c5f37ea0ad3" + ], + "pre_llm": [ + "doc_4a1cf8a90e83" + ], + "pre_subagent_batch": [ + "doc_4a1cf8a90e83" + ], + "pre_tool_batch": [ + "doc_4a1cf8a90e83" + ], + "preamble": [ + "doc_5ee8bbdbd8a8", + "doc_a24b92541657" + ], + "prebuilt": [ + "doc_2a18de009d56", + "doc_464a2b4a8d35", + "doc_9e9118ed2141" + ], + "prebuilt_runtime_tools": [ + "doc_3aa0b3792d22" + ], + "prebuilts": [ + "doc_2a18de009d56", + "doc_9e9118ed2141" + ], + "precedence": [ + "doc_2798948c3080", + "doc_61fd9f682847", + "doc_64090a20f830", + "doc_a24b92541657" + ], + "precision": [ + "doc_3f60413b3a06" + ], + "predefined": [ + "doc_18d38fdacc9e" + ], + "predictable": [ + "doc_bbc97cbff254" + ], + "predictably": [ + "doc_50ac650fd8f6" + ], + "prefer": [ + "doc_3eeb75d383b8" + ], + "preference": [ + "doc_61fd9f682847" + ], + "preferences": [ + "doc_0383dbf277ba" + ], + "preferred": [ + "doc_3eeb75d383b8", + "doc_61fd9f682847" + ], + "prefix": [ + "doc_61fd9f682847", + "doc_eeed52120ccf" + ], + "prefixes": [ + "doc_43ad71a4ad0c", + "doc_a24b92541657" + ], + "prehook": [ + "doc_745f751f1344", + "doc_9e9118ed2141", + "doc_a24b92541657" + ], + "prehooks": [ + "doc_745f751f1344", + "doc_9e9118ed2141", + "doc_a24b92541657" + ], + "premature": [ + "doc_57e2139f0873", + "doc_618e34441fa7" + ], + "prerequisites": [ + "doc_bf91f8d5da74" + ], + "present": [ + "doc_618e34441fa7", + "doc_f5f256549d84" + ], + "preserve": [ + "doc_618e34441fa7" + ], + "preserved": [ + "doc_0383dbf277ba" + ], + "preserves": [ + "doc_4a1cf8a90e83", + "doc_d147a81bac29" + ], + "preserving": [ + "doc_4a1cf8a90e83" + ], + "presets": [ + "doc_2a18de009d56" + ], + "prevent": [ + "doc_067033cfc945", + "doc_18d38fdacc9e", + "doc_43ad71a4ad0c", + "doc_4462f67b1c03", + "doc_464a2b4a8d35", + "doc_567be1281e2f", + "doc_618e34441fa7", + "doc_7320ab982fab", + "doc_745f751f1344", + "doc_c346f13ce597", + "doc_f12bbbc027a7", + "doc_fdf48d427fc0" + ], + "prevention": [ + "doc_464a2b4a8d35" + ], + "prevents": [ + "doc_1c5f37ea0ad3", + "doc_464a2b4a8d35", + "doc_50ac650fd8f6", + "doc_5ee8bbdbd8a8" + ], + "preview": [ + "doc_d147a81bac29" + ], + "previews": [ + "doc_3f1383f72c5f" + ], + "previous": [ + "doc_136d7a909d51", + "doc_5ee8bbdbd8a8", + "doc_7b26cbd9a7ae", + "doc_a5120de838fb", + "doc_eeed52120ccf" + ], + "previously": [ + "doc_4a1cf8a90e83" + ], + "prices": [ + "doc_eeed52120ccf" + ], + "pricing": [ + "doc_f92685a34f7e" + ], + "primarily": [ + "doc_61fd9f682847" + ], + "primary": [ + "doc_1e46cbc6bfae", + "doc_3aa0b3792d22", + "doc_43ad71a4ad0c", + "doc_4a1cf8a90e83", + "doc_5ee8bbdbd8a8", + "doc_7320ab982fab", + "doc_d1f7d83e0824", + "doc_f54217a0f2cf" + ], + "principle": [ + "doc_4e1ebaf40267" + ], + "principles": [ + "doc_43ad71a4ad0c", + "doc_4e1ebaf40267" + ], + "prints": [ + "doc_a24b92541657" + ], + "priority": [ + "doc_2798948c3080", + "doc_61fd9f682847", + "doc_64090a20f830", + "doc_eeed52120ccf" + ], + "privilege": [ + "doc_4e1ebaf40267", + "doc_567be1281e2f" + ], + "proactively": [ + "doc_0383dbf277ba" + ], + "problem": [ + "doc_18d38fdacc9e" + ], + "proceed": [ + "doc_745f751f1344", + "doc_d6ad61a79371", + "doc_e7d508371e5c" + ], + "proceeding": [ + "doc_d6ad61a79371" + ], + "proceeds": [ + "doc_0ac01bce8c79", + "doc_4a1cf8a90e83", + "doc_f12bbbc027a7" + ], + "process": [ + "doc_0383dbf277ba", + "doc_3ae3aaf0c12c", + "doc_4a1cf8a90e83", + "doc_567be1281e2f", + "doc_64090a20f830", + "doc_78b1717fafe3" + ], + "process-wide": [ + "doc_2a18de009d56", + "doc_61fd9f682847", + "doc_64090a20f830" + ], + "processed": [ + "doc_75887f91c7df" + ], + "processes": [ + "doc_0383dbf277ba", + "doc_4a1cf8a90e83", + "doc_745f751f1344", + "doc_75887f91c7df", + "doc_f5f256549d84" + ], + "processing": [ + "doc_1c5f37ea0ad3", + "doc_57e2139f0873", + "doc_75887f91c7df", + "doc_78b1717fafe3", + "doc_d6ad61a79371", + "doc_f54217a0f2cf", + "doc_fdf48d427fc0" + ], + "produce": [ + "doc_618e34441fa7", + "doc_745f751f1344", + "doc_a2e84472f1f9", + "doc_e67e762fb6ab", + "doc_f12bbbc027a7" + ], + "producers": [ + "doc_57e2139f0873", + "doc_78b1717fafe3" + ], + "produces": [ + "doc_0ac01bce8c79", + "doc_136d7a909d51", + "doc_18d38fdacc9e", + "doc_464a2b4a8d35", + "doc_5ee8bbdbd8a8", + "doc_618e34441fa7", + "doc_745f751f1344", + "doc_78a8125d2c7e", + "doc_a2e84472f1f9", + "doc_e7d508371e5c", + "doc_f54217a0f2cf" + ], + "production": [ + "doc_0383dbf277ba", + "doc_067033cfc945", + "doc_18d38fdacc9e", + "doc_1c5f37ea0ad3", + "doc_2798948c3080", + "doc_2a18de009d56", + "doc_3aa0b3792d22", + "doc_3ae3aaf0c12c", + "doc_3f60413b3a06", + "doc_4462f67b1c03", + "doc_464a2b4a8d35", + "doc_567be1281e2f", + "doc_61fd9f682847", + "doc_64090a20f830", + "doc_7320ab982fab", + "doc_78a8125d2c7e", + "doc_78b1717fafe3", + "doc_a5120de838fb", + "doc_bbc97cbff254", + "doc_c93c115aeb85", + "doc_d6ad61a79371", + "doc_deb460ffa5ee", + "doc_e34fe24f9585", + "doc_e67e762fb6ab", + "doc_f18f523b6ecc", + "doc_f2ae50c638a4", + "doc_f5f256549d84" + ], + "production-grade": [ + "doc_0383dbf277ba" + ], + "production-quality": [ + "doc_18d38fdacc9e" + ], + "production-ready": [ + "doc_a5120de838fb" + ], + "production_client": [ + "doc_3aa0b3792d22" + ], + "profile": [ + "doc_2a18de009d56", + "doc_3f60413b3a06", + "doc_464a2b4a8d35", + "doc_618e34441fa7", + "doc_7320ab982fab", + "doc_a24b92541657" + ], + "profile_id": [ + "doc_a24b92541657" + ], + "profiles": [ + "doc_18d38fdacc9e", + "doc_2a18de009d56", + "doc_40e30666bfa8", + "doc_43ad71a4ad0c", + "doc_464a2b4a8d35", + "doc_50ac650fd8f6", + "doc_567be1281e2f", + "doc_7320ab982fab", + "doc_9e9118ed2141", + "doc_a5120de838fb", + "doc_c346f13ce597" + ], + "progress": [ + "doc_136d7a909d51", + "doc_57e2139f0873", + "doc_bbc97cbff254", + "doc_f18f523b6ecc", + "doc_f2ae50c638a4" + ], + "progression": [ + "doc_a5120de838fb" + ], + "projected": [ + "doc_618e34441fa7" + ], + "projection": [ + "doc_2a18de009d56", + "doc_618e34441fa7" + ], + "projector": [ + "doc_618e34441fa7" + ], + "projectors": [ + "doc_2a18de009d56", + "doc_618e34441fa7", + "doc_d147a81bac29" + ], + "prompt": [ + "doc_136d7a909d51", + "doc_18d38fdacc9e", + "doc_1e46cbc6bfae", + "doc_2798948c3080", + "doc_2a18de009d56", + "doc_3ae3aaf0c12c", + "doc_3f60413b3a06", + "doc_50ac650fd8f6", + "doc_567be1281e2f", + "doc_57e2139f0873", + "doc_61fd9f682847", + "doc_64090a20f830", + "doc_745f751f1344", + "doc_78a8125d2c7e", + "doc_9e9118ed2141", + "doc_a24b92541657", + "doc_a5120de838fb", + "doc_b6088784caab", + "doc_c93c115aeb85", + "doc_e67e762fb6ab", + "doc_e8794369a1a4", + "doc_f2ae50c638a4", + "doc_f5f256549d84" + ], + "prompt-based": [ + "doc_3f60413b3a06" + ], + "prompt-injection": [ + "doc_2a18de009d56" + ], + "promptaccesserror": [ + "doc_64090a20f830" + ], + "promptresolutionerror": [ + "doc_2798948c3080" + ], + "prompts": [ + "doc_0383dbf277ba", + "doc_18d38fdacc9e", + "doc_2798948c3080", + "doc_2a18de009d56", + "doc_3ae3aaf0c12c", + "doc_5a65ddab1445", + "doc_61fd9f682847", + "doc_64090a20f830", + "doc_a5120de838fb", + "doc_d147a81bac29", + "doc_f2ae50c638a4", + "doc_f5f256549d84" + ], + "prompts_dir": [ + "doc_2798948c3080", + "doc_64090a20f830", + "doc_a24b92541657", + "doc_e67e762fb6ab" + ], + "promptstore": [ + "doc_2a18de009d56", + "doc_64090a20f830" + ], + "prompttemplateerror": [ + "doc_2798948c3080" + ], + "properties": [ + "doc_50ac650fd8f6", + "doc_618e34441fa7" + ], + "property": [ + "doc_618e34441fa7" + ], + "proposal": [ + "doc_c346f13ce597" + ], + "proposed": [ + "doc_0ac01bce8c79" + ], + "proposes": [ + "doc_745f751f1344" + ], + "protect": [ + "doc_43ad71a4ad0c", + "doc_50ac650fd8f6", + "doc_d147a81bac29" + ], + "protected": [ + "doc_0383dbf277ba", + "doc_d147a81bac29" + ], + "protecting": [ + "doc_7320ab982fab" + ], + "protection": [ + "doc_5ee8bbdbd8a8" + ], + "protects": [ + "doc_a24b92541657" + ], + "protocol": [ + "doc_0383dbf277ba", + "doc_1c5f37ea0ad3", + "doc_2a18de009d56", + "doc_40e30666bfa8", + "doc_4e1ebaf40267", + "doc_57e2139f0873", + "doc_75887f91c7df", + "doc_78b1717fafe3", + "doc_c346f13ce597", + "doc_c93c115aeb85", + "doc_d147a81bac29", + "doc_e34fe24f9585", + "doc_f92685a34f7e" + ], + "protocol-first": [ + "doc_2a18de009d56" + ], + "protocols": [ + "doc_2a18de009d56", + "doc_3eeb75d383b8", + "doc_c346f13ce597", + "doc_f92685a34f7e" + ], + "prototyping": [ + "doc_2798948c3080", + "doc_78b1717fafe3" + ], + "proven": [ + "doc_18d38fdacc9e" + ], + "provide": [ + "doc_136d7a909d51", + "doc_2798948c3080", + "doc_61fd9f682847", + "doc_d147a81bac29", + "doc_f12bbbc027a7", + "doc_f18f523b6ecc" + ], + "provided": [ + "doc_4a1cf8a90e83", + "doc_c346f13ce597" + ], + "provider": [ + "doc_0383dbf277ba", + "doc_0ac01bce8c79", + "doc_109157c0d2d7", + "doc_136d7a909d51", + "doc_1c5f37ea0ad3", + "doc_1e46cbc6bfae", + "doc_2a18de009d56", + "doc_3f60413b3a06", + "doc_4055659fe573", + "doc_4462f67b1c03", + "doc_4a1cf8a90e83", + "doc_4e1ebaf40267", + "doc_5ee8bbdbd8a8", + "doc_618e34441fa7", + "doc_61fd9f682847", + "doc_7320ab982fab", + "doc_745f751f1344", + "doc_a24b92541657", + "doc_bbc97cbff254", + "doc_bf91f8d5da74", + "doc_c346f13ce597", + "doc_d1f7d83e0824", + "doc_d6ad61a79371", + "doc_deb460ffa5ee", + "doc_e7d508371e5c", + "doc_e8794369a1a4", + "doc_eeed52120ccf", + "doc_f18f523b6ecc", + "doc_f1e32ed2ffce", + "doc_f54217a0f2cf", + "doc_f92685a34f7e" + ], + "provider-agnostic": [ + "doc_f92685a34f7e" + ], + "provider-dependent": [ + "doc_f1e32ed2ffce" + ], + "provider-portable": [ + "doc_4e1ebaf40267", + "doc_5a65ddab1445", + "doc_d147a81bac29", + "doc_deb460ffa5ee" + ], + "provider-specific": [ + "doc_5ee8bbdbd8a8", + "doc_e8794369a1a4", + "doc_f1e32ed2ffce", + "doc_f92685a34f7e" + ], + "provider-supported": [ + "doc_e67e762fb6ab" + ], + "provider_adapter": [ + "doc_4a1cf8a90e83", + "doc_bbc97cbff254" + ], + "providers": [ + "doc_067033cfc945", + "doc_1c5f37ea0ad3", + "doc_2a18de009d56", + "doc_4a1cf8a90e83", + "doc_567be1281e2f", + "doc_57e2139f0873", + "doc_5a65ddab1445", + "doc_5ee8bbdbd8a8", + "doc_618e34441fa7", + "doc_61fd9f682847", + "doc_c93c115aeb85", + "doc_d147a81bac29", + "doc_e8794369a1a4", + "doc_eeed52120ccf", + "doc_f1e32ed2ffce", + "doc_f92685a34f7e" + ], + "provides": [ + "doc_3f60413b3a06", + "doc_43ad71a4ad0c", + "doc_4a1cf8a90e83", + "doc_64090a20f830", + "doc_78b1717fafe3", + "doc_9e9118ed2141", + "doc_a5120de838fb", + "doc_b6088784caab", + "doc_c346f13ce597", + "doc_f54217a0f2cf", + "doc_fdf48d427fc0" + ], + "providing": [ + "doc_0ac01bce8c79" + ], + "proxy": [ + "doc_f1e32ed2ffce", + "doc_f92685a34f7e" + ], + "prunes": [ + "doc_07174a8633ac" + ], + "public": [ + "doc_067033cfc945", + "doc_2a18de009d56", + "doc_3ae3aaf0c12c", + "doc_3eeb75d383b8", + "doc_4e1ebaf40267", + "doc_618e34441fa7", + "doc_bbc97cbff254", + "doc_d147a81bac29", + "doc_deb460ffa5ee", + "doc_f18f523b6ecc", + "doc_f5f256549d84" + ], + "public-import": [ + "doc_d147a81bac29" + ], + "public-imports-and-function-improvement": [ + "doc_3aa0b3792d22" + ], + "published": [ + "doc_0ac01bce8c79" + ], + "pure": [ + "doc_18d38fdacc9e", + "doc_745f751f1344" + ], + "purpose": [ + "doc_0383dbf277ba", + "doc_3f60413b3a06", + "doc_5ee8bbdbd8a8", + "doc_745f751f1344", + "doc_75887f91c7df", + "doc_9e9118ed2141", + "doc_a24b92541657", + "doc_b6088784caab", + "doc_e67e762fb6ab", + "doc_e8794369a1a4", + "doc_eeed52120ccf", + "doc_f92685a34f7e" + ], + "purposes": [ + "doc_d6ad61a79371" + ], + "push": [ + "doc_4055659fe573", + "doc_78b1717fafe3" + ], + "pwd": [ + "doc_a24b92541657" + ], + "py": [ + "doc_3eeb75d383b8", + "doc_d147a81bac29" + ], + "pydantic": [ + "doc_18d38fdacc9e", + "doc_3ae3aaf0c12c", + "doc_3eeb75d383b8", + "doc_3f60413b3a06", + "doc_43ad71a4ad0c", + "doc_4e1ebaf40267", + "doc_50ac650fd8f6", + "doc_57e2139f0873", + "doc_5a65ddab1445", + "doc_618e34441fa7", + "doc_745f751f1344", + "doc_9e9118ed2141", + "doc_a24b92541657", + "doc_a5120de838fb", + "doc_b6088784caab", + "doc_bf91f8d5da74", + "doc_c346f13ce597", + "doc_deb460ffa5ee", + "doc_e67e762fb6ab", + "doc_e8794369a1a4", + "doc_f5f256549d84" + ], + "pydantic-based": [ + "doc_5a65ddab1445" + ], + "pydantic-validated": [ + "doc_3f60413b3a06" + ], + "pytest": [ + "doc_3eeb75d383b8" + ], + "python": [ + "doc_136d7a909d51", + "doc_2a18de009d56", + "doc_464a2b4a8d35", + "doc_64090a20f830", + "doc_745f751f1344", + "doc_9e9118ed2141", + "doc_b6088784caab", + "doc_bf91f8d5da74", + "doc_d147a81bac29", + "doc_deb460ffa5ee", + "doc_f18f523b6ecc", + "doc_f1e32ed2ffce" + ], + "pythonpath": [ + "doc_3eeb75d383b8" + ], + "qa": [ + "doc_64090a20f830" + ], + "qa_bot_v2": [ + "doc_64090a20f830" + ], + "quality": [ + "doc_067033cfc945", + "doc_78a8125d2c7e", + "doc_d147a81bac29", + "doc_eeed52120ccf" + ], + "queries": [ + "doc_0383dbf277ba", + "doc_07174a8633ac" + ], + "query": [ + "doc_9e9118ed2141" + ], + "question": [ + "doc_b6088784caab" + ], + "queue": [ + "doc_067033cfc945", + "doc_2a18de009d56", + "doc_4055659fe573", + "doc_57e2139f0873", + "doc_618e34441fa7", + "doc_61fd9f682847", + "doc_75887f91c7df", + "doc_78b1717fafe3" + ], + "queue-based": [ + "doc_57e2139f0873" + ], + "queue-level": [ + "doc_2a18de009d56" + ], + "queued": [ + "doc_4a1cf8a90e83", + "doc_78b1717fafe3", + "doc_a24b92541657", + "doc_f12bbbc027a7" + ], + "queues": [ + "doc_2a18de009d56", + "doc_3ae3aaf0c12c", + "doc_3eeb75d383b8", + "doc_57e2139f0873", + "doc_618e34441fa7", + "doc_61fd9f682847", + "doc_78b1717fafe3", + "doc_bbc97cbff254", + "doc_d147a81bac29", + "doc_deb460ffa5ee", + "doc_f18f523b6ecc", + "doc_f2ae50c638a4", + "doc_f5f256549d84" + ], + "quick": [ + "doc_0383dbf277ba", + "doc_109157c0d2d7", + "doc_3f1383f72c5f", + "doc_78b1717fafe3", + "doc_a24b92541657", + "doc_e7d508371e5c", + "doc_f12bbbc027a7" + ], + "quickstart": [ + "doc_3aa0b3792d22", + "doc_d147a81bac29", + "doc_f18f523b6ecc", + "doc_f2ae50c638a4" + ], + "quorum": [ + "doc_f12bbbc027a7" + ], + "rag": [ + "doc_57e2139f0873", + "doc_5a65ddab1445" + ], + "raise": [ + "doc_a24b92541657" + ], + "raise_on_error": [ + "doc_745f751f1344", + "doc_a24b92541657" + ], + "raised": [ + "doc_3f60413b3a06", + "doc_4055659fe573", + "doc_4a1cf8a90e83" + ], + "raises": [ + "doc_07174a8633ac", + "doc_0ac01bce8c79", + "doc_464a2b4a8d35", + "doc_4a1cf8a90e83", + "doc_64090a20f830", + "doc_a24b92541657" + ], + "ran": [ + "doc_0ac01bce8c79", + "doc_e7d508371e5c" + ], + "random": [ + "doc_61fd9f682847" + ], + "rarely": [ + "doc_f92685a34f7e" + ], + "rate": [ + "doc_067033cfc945", + "doc_136d7a909d51", + "doc_18d38fdacc9e", + "doc_1c5f37ea0ad3", + "doc_3ae3aaf0c12c", + "doc_4055659fe573", + "doc_40e30666bfa8", + "doc_567be1281e2f", + "doc_5ee8bbdbd8a8", + "doc_7320ab982fab", + "doc_78a8125d2c7e", + "doc_78b1717fafe3", + "doc_9e9118ed2141", + "doc_a24b92541657", + "doc_c346f13ce597", + "doc_c93c115aeb85", + "doc_d147a81bac29", + "doc_d1f7d83e0824", + "doc_deb460ffa5ee", + "doc_e7d508371e5c", + "doc_e8794369a1a4", + "doc_eeed52120ccf", + "doc_f1e32ed2ffce" + ], + "ratelimiterror": [ + "doc_136d7a909d51" + ], + "ratelimitpolicy": [ + "doc_2a18de009d56" + ], + "rates": [ + "doc_e34fe24f9585" + ], + "rather": [ + "doc_07174a8633ac", + "doc_4a1cf8a90e83", + "doc_618e34441fa7", + "doc_64090a20f830", + "doc_b6088784caab" + ], + "raw": [ + "doc_0383dbf277ba", + "doc_07174a8633ac", + "doc_3f60413b3a06", + "doc_7320ab982fab", + "doc_745f751f1344", + "doc_9e9118ed2141" + ], + "re": [ + "doc_e67e762fb6ab" + ], + "re-enters": [ + "doc_4a1cf8a90e83" + ], + "re-executing": [ + "doc_07174a8633ac", + "doc_4a1cf8a90e83" + ], + "re-execution": [ + "doc_4a1cf8a90e83" + ], + "re-exports": [ + "doc_2a18de009d56" + ], + "re-rendering": [ + "doc_64090a20f830" + ], + "re-validation": [ + "doc_745f751f1344" + ], + "reach": [ + "doc_067033cfc945", + "doc_e34fe24f9585" + ], + "reached": [ + "doc_f12bbbc027a7" + ], + "reaches": [ + "doc_07174a8633ac", + "doc_745f751f1344", + "doc_f54217a0f2cf" + ], + "read": [ + "doc_07174a8633ac", + "doc_2a18de009d56", + "doc_3ae3aaf0c12c", + "doc_40e30666bfa8", + "doc_4462f67b1c03", + "doc_464a2b4a8d35", + "doc_618e34441fa7", + "doc_61fd9f682847", + "doc_64090a20f830", + "doc_7b26cbd9a7ae", + "doc_9e9118ed2141", + "doc_a5120de838fb", + "doc_b6088784caab", + "doc_bf91f8d5da74", + "doc_c346f13ce597", + "doc_d1f7d83e0824", + "doc_f18f523b6ecc", + "doc_f2ae50c638a4", + "doc_f5f256549d84", + "doc_fdf48d427fc0" + ], + "read-only": [ + "doc_109157c0d2d7", + "doc_43ad71a4ad0c", + "doc_9e9118ed2141", + "doc_b6088784caab" + ], + "read_file": [ + "doc_464a2b4a8d35" + ], + "read_skill_file": [ + "doc_9e9118ed2141", + "doc_b6088784caab" + ], + "read_skill_md": [ + "doc_9e9118ed2141", + "doc_b6088784caab" + ], + "readable": [ + "doc_0383dbf277ba", + "doc_f54217a0f2cf" + ], + "readiness": [ + "doc_18d38fdacc9e" + ], + "reading": [ + "doc_1e46cbc6bfae", + "doc_464a2b4a8d35", + "doc_a5120de838fb", + "doc_b6088784caab" + ], + "reads": [ + "doc_43ad71a4ad0c", + "doc_464a2b4a8d35", + "doc_a24b92541657", + "doc_b6088784caab" + ], + "ready": [ + "doc_f2ae50c638a4" + ], + "ready-to-use": [ + "doc_9e9118ed2141" + ], + "real": [ + "doc_18d38fdacc9e", + "doc_3ae3aaf0c12c", + "doc_618e34441fa7", + "doc_9e9118ed2141", + "doc_c93c115aeb85", + "doc_d1f7d83e0824", + "doc_d6ad61a79371", + "doc_fdf48d427fc0" + ], + "real-time": [ + "doc_109157c0d2d7", + "doc_5ee8bbdbd8a8", + "doc_7b26cbd9a7ae", + "doc_a5120de838fb", + "doc_e7d508371e5c", + "doc_eeed52120ccf", + "doc_f54217a0f2cf", + "doc_fdf48d427fc0" + ], + "reason": [ + "doc_0ac01bce8c79", + "doc_50ac650fd8f6", + "doc_618e34441fa7", + "doc_d6ad61a79371", + "doc_f54217a0f2cf" + ], + "reasonable": [ + "doc_c93c115aeb85" + ], + "reasoning": [ + "doc_a24b92541657", + "doc_e67e762fb6ab", + "doc_eeed52120ccf", + "doc_f5f256549d84", + "doc_f92685a34f7e" + ], + "reasoning_effort": [ + "doc_a24b92541657", + "doc_e67e762fb6ab" + ], + "reasoning_enabled": [ + "doc_a24b92541657", + "doc_e67e762fb6ab" + ], + "reasoning_max_tokens": [ + "doc_a24b92541657", + "doc_e67e762fb6ab" + ], + "reasons": [ + "doc_4a1cf8a90e83" + ], + "receive": [ + "doc_109157c0d2d7", + "doc_50ac650fd8f6", + "doc_9e9118ed2141", + "doc_c346f13ce597", + "doc_f2ae50c638a4" + ], + "received": [ + "doc_0ac01bce8c79", + "doc_4a1cf8a90e83", + "doc_a24b92541657" + ], + "receiver": [ + "doc_75887f91c7df" + ], + "receives": [ + "doc_0ac01bce8c79", + "doc_3f1383f72c5f", + "doc_9e9118ed2141", + "doc_a2e84472f1f9", + "doc_c346f13ce597", + "doc_d6ad61a79371", + "doc_f12bbbc027a7" + ], + "receiving": [ + "doc_75887f91c7df", + "doc_f54217a0f2cf" + ], + "recent": [ + "doc_0383dbf277ba", + "doc_07174a8633ac", + "doc_4a1cf8a90e83" + ], + "recommendations": [ + "doc_78a8125d2c7e", + "doc_d1f7d83e0824", + "doc_fdf48d427fc0" + ], + "recommended": [ + "doc_18d38fdacc9e", + "doc_57e2139f0873", + "doc_618e34441fa7", + "doc_f54217a0f2cf" + ], + "recommends": [ + "doc_567be1281e2f" + ], + "reconstruct": [ + "doc_4a1cf8a90e83" + ], + "record": [ + "doc_0383dbf277ba", + "doc_4a1cf8a90e83", + "doc_a2e84472f1f9", + "doc_deb460ffa5ee" + ], + "recorded": [ + "doc_0ac01bce8c79", + "doc_745f751f1344" + ], + "records": [ + "doc_0383dbf277ba", + "doc_07174a8633ac", + "doc_0ac01bce8c79", + "doc_1e46cbc6bfae", + "doc_2a18de009d56", + "doc_3ae3aaf0c12c", + "doc_50ac650fd8f6", + "doc_a2e84472f1f9", + "doc_bbc97cbff254", + "doc_bf91f8d5da74", + "doc_deb460ffa5ee", + "doc_f18f523b6ecc" + ], + "recover": [ + "doc_4055659fe573" + ], + "recovery": [ + "doc_4a1cf8a90e83", + "doc_618e34441fa7", + "doc_a24b92541657" + ], + "recursion": [ + "doc_a24b92541657" + ], + "redact_secrets": [ + "doc_3f1383f72c5f" + ], + "redaction": [ + "doc_2a18de009d56", + "doc_3f1383f72c5f", + "doc_a24b92541657", + "doc_c346f13ce597" + ], + "redelivered": [ + "doc_75887f91c7df" + ], + "redirects": [ + "doc_a24b92541657" + ], + "redis": [ + "doc_0383dbf277ba", + "doc_2a18de009d56", + "doc_4055659fe573", + "doc_4462f67b1c03", + "doc_618e34441fa7", + "doc_61fd9f682847", + "doc_a5120de838fb", + "doc_f5f256549d84" + ], + "redisconnectionpool": [ + "doc_78b1717fafe3" + ], + "redismemorystore": [ + "doc_2a18de009d56" + ], + "redistaskqueue": [ + "doc_bbc97cbff254" + ], + "reduce": [ + "doc_7320ab982fab" + ], + "reduces": [ + "doc_07174a8633ac" + ], + "reducing": [ + "doc_f5f256549d84" + ], + "reference": [ + "doc_0ac01bce8c79", + "doc_109157c0d2d7", + "doc_2a18de009d56", + "doc_3aa0b3792d22", + "doc_3eeb75d383b8", + "doc_4462f67b1c03", + "doc_4a1cf8a90e83", + "doc_61fd9f682847", + "doc_78a8125d2c7e", + "doc_7b26cbd9a7ae", + "doc_a24b92541657", + "doc_b6088784caab", + "doc_bbc97cbff254", + "doc_d147a81bac29", + "doc_d1f7d83e0824", + "doc_e67e762fb6ab", + "doc_e7d508371e5c", + "doc_f18f523b6ecc", + "doc_f54217a0f2cf", + "doc_fdf48d427fc0" + ], + "references": [ + "doc_3eeb75d383b8", + "doc_f18f523b6ecc" + ], + "reflect": [ + "doc_43ad71a4ad0c" + ], + "refreshed": [ + "doc_f18f523b6ecc" + ], + "refs": [ + "doc_40e30666bfa8", + "doc_a24b92541657" + ], + "refused": [ + "doc_4055659fe573" + ], + "regardless": [ + "doc_0ac01bce8c79" + ], + "regenerate": [ + "doc_3eeb75d383b8", + "doc_d147a81bac29" + ], + "register": [ + "doc_43ad71a4ad0c", + "doc_78b1717fafe3", + "doc_a5120de838fb", + "doc_b6088784caab", + "doc_f1e32ed2ffce" + ], + "register_llm_provider": [ + "doc_2a18de009d56" + ], + "register_llm_router": [ + "doc_2a18de009d56" + ], + "registered": [ + "doc_1e46cbc6bfae", + "doc_3f60413b3a06", + "doc_5ee8bbdbd8a8", + "doc_745f751f1344", + "doc_eeed52120ccf" + ], + "registering": [ + "doc_43ad71a4ad0c", + "doc_b6088784caab" + ], + "registers": [ + "doc_b6088784caab" + ], + "registration": [ + "doc_2a18de009d56", + "doc_745f751f1344", + "doc_a5120de838fb", + "doc_f92685a34f7e" + ], + "registries": [ + "doc_2a18de009d56" + ], + "registry": [ + "doc_0ac01bce8c79", + "doc_2a18de009d56", + "doc_464a2b4a8d35", + "doc_4e1ebaf40267", + "doc_745f751f1344", + "doc_9e9118ed2141", + "doc_c346f13ce597", + "doc_d147a81bac29" + ], + "registry-level": [ + "doc_9e9118ed2141", + "doc_c346f13ce597" + ], + "registry_middleware": [ + "doc_9e9118ed2141" + ], + "regression": [ + "doc_18d38fdacc9e", + "doc_50ac650fd8f6", + "doc_618e34441fa7", + "doc_c93c115aeb85" + ], + "regressions": [ + "doc_618e34441fa7" + ], + "reject": [ + "doc_4a1cf8a90e83" + ], + "rejected": [ + "doc_0ac01bce8c79" + ], + "rejects": [ + "doc_75887f91c7df" + ], + "related": [ + "doc_75887f91c7df", + "doc_f2ae50c638a4" + ], + "relative": [ + "doc_464a2b4a8d35", + "doc_61fd9f682847", + "doc_64090a20f830" + ], + "relative_to": [ + "doc_464a2b4a8d35" + ], + "release": [ + "doc_3ae3aaf0c12c", + "doc_618e34441fa7", + "doc_c93c115aeb85" + ], + "release-gating": [ + "doc_618e34441fa7" + ], + "releases": [ + "doc_067033cfc945", + "doc_3ae3aaf0c12c", + "doc_c93c115aeb85", + "doc_f54217a0f2cf" + ], + "relevant": [ + "doc_3eeb75d383b8", + "doc_618e34441fa7", + "doc_b6088784caab" + ], + "reliability": [ + "doc_57e2139f0873" + ], + "reliable": [ + "doc_3ae3aaf0c12c", + "doc_61fd9f682847", + "doc_78b1717fafe3" + ], + "reliably": [ + "doc_75887f91c7df", + "doc_deb460ffa5ee", + "doc_f5f256549d84" + ], + "remain": [ + "doc_61fd9f682847", + "doc_eeed52120ccf" + ], + "remaining": [ + "doc_0383dbf277ba", + "doc_0ac01bce8c79" + ], + "remember": [ + "doc_136d7a909d51" + ], + "remembers": [ + "doc_7b26cbd9a7ae" + ], + "remote": [ + "doc_40e30666bfa8", + "doc_a5120de838fb", + "doc_e34fe24f9585" + ], + "remove_": [ + "doc_43ad71a4ad0c" + ], + "removed": [ + "doc_07174a8633ac", + "doc_f54217a0f2cf" + ], + "removes": [ + "doc_07174a8633ac" + ], + "removing": [ + "doc_3eeb75d383b8" + ], + "renamed": [ + "doc_618e34441fa7" + ], + "renaming": [ + "doc_3eeb75d383b8" + ], + "render_untrusted_tool_message": [ + "doc_745f751f1344" + ], + "rendered": [ + "doc_f54217a0f2cf" + ], + "rendering": [ + "doc_2a18de009d56" + ], + "renders": [ + "doc_64090a20f830" + ], + "repair": [ + "doc_61fd9f682847" + ], + "repeated": [ + "doc_4a1cf8a90e83", + "doc_a24b92541657" + ], + "repeatedly": [ + "doc_136d7a909d51" + ], + "rephrasing": [ + "doc_0ac01bce8c79" + ], + "replace": [ + "doc_3f60413b3a06" + ], + "replaced": [ + "doc_64090a20f830" + ], + "replay": [ + "doc_0ac01bce8c79", + "doc_4a1cf8a90e83", + "doc_50ac650fd8f6" + ], + "replayed": [ + "doc_0383dbf277ba", + "doc_4a1cf8a90e83" + ], + "replayed_effect_count": [ + "doc_4a1cf8a90e83" + ], + "repo": [ + "doc_f18f523b6ecc" + ], + "report": [ + "doc_2a18de009d56", + "doc_4055659fe573", + "doc_618e34441fa7", + "doc_c93c115aeb85", + "doc_f12bbbc027a7" + ], + "reported": [ + "doc_109157c0d2d7", + "doc_d6ad61a79371" + ], + "reporting": [ + "doc_136d7a909d51", + "doc_2a18de009d56", + "doc_4e1ebaf40267", + "doc_618e34441fa7" + ], + "reports": [ + "doc_618e34441fa7" + ], + "repository": [ + "doc_618e34441fa7", + "doc_b6088784caab", + "doc_bf91f8d5da74", + "doc_d147a81bac29" + ], + "representing": [ + "doc_5ee8bbdbd8a8" + ], + "represents": [ + "doc_78b1717fafe3" + ], + "reproducible": [ + "doc_618e34441fa7" + ], + "reproduction": [ + "doc_136d7a909d51" + ], + "req": [ + "doc_7320ab982fab", + "doc_c346f13ce597", + "doc_f92685a34f7e" + ], + "request": [ + "doc_0ac01bce8c79", + "doc_1c5f37ea0ad3", + "doc_2a18de009d56", + "doc_3f60413b3a06", + "doc_4055659fe573", + "doc_4462f67b1c03", + "doc_50ac650fd8f6", + "doc_5a65ddab1445", + "doc_5ee8bbdbd8a8", + "doc_61fd9f682847", + "doc_7320ab982fab", + "doc_75887f91c7df", + "doc_a24b92541657", + "doc_c346f13ce597", + "doc_d6ad61a79371", + "doc_e34fe24f9585", + "doc_e7d508371e5c", + "doc_e8794369a1a4", + "doc_eeed52120ccf", + "doc_f12bbbc027a7", + "doc_f54217a0f2cf", + "doc_f92685a34f7e" + ], + "request-level": [ + "doc_5ee8bbdbd8a8" + ], + "request_approval": [ + "doc_0ac01bce8c79", + "doc_43ad71a4ad0c", + "doc_567be1281e2f", + "doc_9e9118ed2141", + "doc_d6ad61a79371", + "doc_e67e762fb6ab" + ], + "request_id": [ + "doc_5ee8bbdbd8a8" + ], + "request_payload": [ + "doc_0ac01bce8c79" + ], + "request_user_input": [ + "doc_0ac01bce8c79", + "doc_567be1281e2f", + "doc_745f751f1344", + "doc_a24b92541657", + "doc_d6ad61a79371", + "doc_e67e762fb6ab" + ], + "requested": [ + "doc_07174a8633ac", + "doc_4a1cf8a90e83", + "doc_50ac650fd8f6", + "doc_5ee8bbdbd8a8", + "doc_bbc97cbff254", + "doc_e8794369a1a4" + ], + "requested_model": [ + "doc_4a1cf8a90e83", + "doc_bbc97cbff254" + ], + "requests": [ + "doc_136d7a909d51", + "doc_1c5f37ea0ad3", + "doc_4462f67b1c03", + "doc_5ee8bbdbd8a8", + "doc_61fd9f682847", + "doc_7320ab982fab", + "doc_a24b92541657", + "doc_a5120de838fb", + "doc_c346f13ce597", + "doc_d6ad61a79371", + "doc_f5f256549d84", + "doc_f92685a34f7e", + "doc_fdf48d427fc0" + ], + "require": [ + "doc_3f60413b3a06", + "doc_43ad71a4ad0c", + "doc_50ac650fd8f6", + "doc_7320ab982fab", + "doc_745f751f1344", + "doc_a2e84472f1f9", + "doc_d6ad61a79371", + "doc_e34fe24f9585", + "doc_f18f523b6ecc" + ], + "require_idempotency_key": [ + "doc_7320ab982fab" + ], + "required": [ + "doc_067033cfc945", + "doc_0ac01bce8c79", + "doc_1e46cbc6bfae", + "doc_4055659fe573", + "doc_464a2b4a8d35", + "doc_4a1cf8a90e83", + "doc_567be1281e2f", + "doc_61fd9f682847", + "doc_78b1717fafe3", + "doc_a24b92541657", + "doc_bbc97cbff254", + "doc_e67e762fb6ab" + ], + "requirement": [ + "doc_18d38fdacc9e" + ], + "requires": [ + "doc_067033cfc945", + "doc_0ac01bce8c79", + "doc_567be1281e2f" + ], + "requiring": [ + "doc_f54217a0f2cf" + ], + "research": [ + "doc_a2e84472f1f9" + ], + "research-assistant": [ + "doc_64090a20f830" + ], + "research_assistant": [ + "doc_64090a20f830" + ], + "researcher": [ + "doc_4055659fe573" + ], + "reserve": [ + "doc_f5f256549d84", + "doc_fdf48d427fc0" + ], + "reserved": [ + "doc_64090a20f830" + ], + "resilience": [ + "doc_a24b92541657", + "doc_a5120de838fb", + "doc_d1f7d83e0824" + ], + "resilient": [ + "doc_a2e84472f1f9" + ], + "resolution": [ + "doc_2798948c3080", + "doc_2a18de009d56", + "doc_4a1cf8a90e83", + "doc_5ee8bbdbd8a8", + "doc_64090a20f830", + "doc_a24b92541657", + "doc_eeed52120ccf", + "doc_f2ae50c638a4" + ], + "resolve": [ + "doc_136d7a909d51", + "doc_61fd9f682847", + "doc_64090a20f830", + "doc_7320ab982fab", + "doc_9e9118ed2141", + "doc_a24b92541657", + "doc_e67e762fb6ab", + "doc_f1e32ed2ffce", + "doc_f92685a34f7e" + ], + "resolved": [ + "doc_3f1383f72c5f", + "doc_464a2b4a8d35", + "doc_5ee8bbdbd8a8", + "doc_618e34441fa7", + "doc_61fd9f682847", + "doc_64090a20f830", + "doc_9e9118ed2141", + "doc_a24b92541657", + "doc_e67e762fb6ab" + ], + "resolver": [ + "doc_a24b92541657" + ], + "resolves": [ + "doc_0ac01bce8c79", + "doc_618e34441fa7", + "doc_64090a20f830", + "doc_eeed52120ccf" + ], + "resource": [ + "doc_f12bbbc027a7" + ], + "resources": [ + "doc_43ad71a4ad0c", + "doc_b6088784caab" + ], + "resp": [ + "doc_3f60413b3a06", + "doc_5ee8bbdbd8a8" + ], + "respect": [ + "doc_618e34441fa7" + ], + "respected": [ + "doc_618e34441fa7" + ], + "respecting": [ + "doc_c93c115aeb85" + ], + "respects": [ + "doc_c346f13ce597" + ], + "respond": [ + "doc_0ac01bce8c79", + "doc_50ac650fd8f6" + ], + "responds": [ + "doc_136d7a909d51", + "doc_1e46cbc6bfae" + ], + "response": [ + "doc_067033cfc945", + "doc_0ac01bce8c79", + "doc_109157c0d2d7", + "doc_1e46cbc6bfae", + "doc_2a18de009d56", + "doc_3f60413b3a06", + "doc_4055659fe573", + "doc_4a1cf8a90e83", + "doc_4e1ebaf40267", + "doc_5a65ddab1445", + "doc_5ee8bbdbd8a8", + "doc_7320ab982fab", + "doc_745f751f1344", + "doc_75887f91c7df", + "doc_9e9118ed2141", + "doc_a2e84472f1f9", + "doc_a5120de838fb", + "doc_bf91f8d5da74", + "doc_c346f13ce597", + "doc_e7d508371e5c", + "doc_e8794369a1a4", + "doc_f12bbbc027a7", + "doc_f54217a0f2cf", + "doc_f92685a34f7e" + ], + "response_format": [ + "doc_3f60413b3a06", + "doc_e8794369a1a4" + ], + "response_model": [ + "doc_3f60413b3a06", + "doc_5ee8bbdbd8a8" + ], + "responseformat": [ + "doc_e8794369a1a4" + ], + "responses": [ + "doc_0383dbf277ba", + "doc_136d7a909d51", + "doc_50ac650fd8f6", + "doc_5ee8bbdbd8a8", + "doc_a24b92541657", + "doc_a5120de838fb", + "doc_c346f13ce597", + "doc_e34fe24f9585", + "doc_e8794369a1a4", + "doc_f5f256549d84", + "doc_f92685a34f7e" + ], + "responsibility": [ + "doc_2a18de009d56", + "doc_4e1ebaf40267", + "doc_d147a81bac29" + ], + "rest": [ + "doc_0ac01bce8c79", + "doc_f12bbbc027a7" + ], + "restart": [ + "doc_0383dbf277ba", + "doc_136d7a909d51", + "doc_75887f91c7df" + ], + "restarting": [ + "doc_64090a20f830" + ], + "restarts": [ + "doc_3ae3aaf0c12c", + "doc_4a1cf8a90e83", + "doc_61fd9f682847" + ], + "restoration": [ + "doc_07174a8633ac" + ], + "restored": [ + "doc_07174a8633ac", + "doc_4a1cf8a90e83" + ], + "restoring": [ + "doc_07174a8633ac" + ], + "restrict": [ + "doc_067033cfc945", + "doc_43ad71a4ad0c", + "doc_464a2b4a8d35", + "doc_a24b92541657" + ], + "restrictions": [ + "doc_9e9118ed2141", + "doc_a24b92541657" + ], + "result": [ + "doc_07174a8633ac", + "doc_0ac01bce8c79", + "doc_109157c0d2d7", + "doc_1e46cbc6bfae", + "doc_4a1cf8a90e83", + "doc_4e1ebaf40267", + "doc_50ac650fd8f6", + "doc_618e34441fa7", + "doc_64090a20f830", + "doc_745f751f1344", + "doc_75887f91c7df", + "doc_78b1717fafe3", + "doc_7b26cbd9a7ae", + "doc_9e9118ed2141", + "doc_a24b92541657", + "doc_bf91f8d5da74", + "doc_c346f13ce597", + "doc_c93c115aeb85", + "doc_d1f7d83e0824", + "doc_d6ad61a79371", + "doc_deb460ffa5ee", + "doc_e7d508371e5c", + "doc_f12bbbc027a7", + "doc_f2ae50c638a4", + "doc_f5f256549d84", + "doc_fdf48d427fc0" + ], + "resulting": [ + "doc_c93c115aeb85" + ], + "results": [ + "doc_0383dbf277ba", + "doc_2a18de009d56", + "doc_4055659fe573", + "doc_4a1cf8a90e83", + "doc_4e1ebaf40267", + "doc_57e2139f0873", + "doc_5ee8bbdbd8a8", + "doc_618e34441fa7", + "doc_9e9118ed2141", + "doc_a24b92541657", + "doc_a2e84472f1f9", + "doc_a5120de838fb", + "doc_c93c115aeb85", + "doc_e34fe24f9585", + "doc_e67e762fb6ab", + "doc_e8794369a1a4", + "doc_f12bbbc027a7", + "doc_fdf48d427fc0" + ], + "resume": [ + "doc_0383dbf277ba", + "doc_07174a8633ac", + "doc_109157c0d2d7", + "doc_136d7a909d51", + "doc_2a18de009d56", + "doc_3ae3aaf0c12c", + "doc_4a1cf8a90e83", + "doc_50ac650fd8f6", + "doc_7b26cbd9a7ae", + "doc_a5120de838fb", + "doc_bbc97cbff254", + "doc_d147a81bac29", + "doc_e7d508371e5c", + "doc_f18f523b6ecc" + ], + "resume_and_compact": [ + "doc_3aa0b3792d22" + ], + "resume_hint": [ + "doc_109157c0d2d7", + "doc_f54217a0f2cf" + ], + "resume_with_input": [ + "doc_a24b92541657" + ], + "resumed": [ + "doc_4a1cf8a90e83", + "doc_f54217a0f2cf" + ], + "resumes": [ + "doc_0ac01bce8c79", + "doc_4a1cf8a90e83" + ], + "resuming": [ + "doc_07174a8633ac", + "doc_4a1cf8a90e83" + ], + "resumption": [ + "doc_0383dbf277ba", + "doc_7b26cbd9a7ae", + "doc_a5120de838fb" + ], + "retain": [ + "doc_7320ab982fab" + ], + "retained": [ + "doc_f5f256549d84" + ], + "retention": [ + "doc_0383dbf277ba", + "doc_07174a8633ac", + "doc_2a18de009d56", + "doc_bbc97cbff254", + "doc_d147a81bac29", + "doc_deb460ffa5ee" + ], + "retentionpolicy": [ + "doc_07174a8633ac" + ], + "retried": [ + "doc_57e2139f0873", + "doc_5ee8bbdbd8a8", + "doc_7320ab982fab", + "doc_78b1717fafe3", + "doc_a24b92541657" + ], + "retries": [ + "doc_1c5f37ea0ad3", + "doc_5ee8bbdbd8a8", + "doc_78b1717fafe3", + "doc_a24b92541657", + "doc_c346f13ce597", + "doc_d147a81bac29", + "doc_d1f7d83e0824", + "doc_deb460ffa5ee", + "doc_eeed52120ccf" + ], + "retrieval": [ + "doc_5a65ddab1445" + ], + "retrieve": [ + "doc_18d38fdacc9e" + ], + "retry": [ + "doc_4055659fe573", + "doc_4462f67b1c03", + "doc_4a1cf8a90e83", + "doc_4e1ebaf40267", + "doc_50ac650fd8f6", + "doc_5a65ddab1445", + "doc_618e34441fa7", + "doc_61fd9f682847", + "doc_7320ab982fab", + "doc_75887f91c7df", + "doc_78b1717fafe3", + "doc_a24b92541657", + "doc_d147a81bac29", + "doc_d1f7d83e0824", + "doc_e7d508371e5c", + "doc_e8794369a1a4", + "doc_eeed52120ccf", + "doc_f1e32ed2ffce" + ], + "retry_then_continue": [ + "doc_a24b92541657" + ], + "retry_then_degrade": [ + "doc_a24b92541657", + "doc_a2e84472f1f9" + ], + "retry_then_fail": [ + "doc_a24b92541657" + ], + "retryable": [ + "doc_18d38fdacc9e", + "doc_4055659fe573", + "doc_4e1ebaf40267", + "doc_50ac650fd8f6", + "doc_57e2139f0873", + "doc_5ee8bbdbd8a8", + "doc_618e34441fa7", + "doc_78b1717fafe3", + "doc_eeed52120ccf" + ], + "retrypolicy": [ + "doc_2a18de009d56" + ], + "return": [ + "doc_136d7a909d51", + "doc_18d38fdacc9e", + "doc_3f60413b3a06", + "doc_464a2b4a8d35", + "doc_4e1ebaf40267", + "doc_50ac650fd8f6", + "doc_745f751f1344", + "doc_9e9118ed2141", + "doc_c346f13ce597", + "doc_f12bbbc027a7", + "doc_f5f256549d84" + ], + "returned": [ + "doc_1e46cbc6bfae", + "doc_4055659fe573", + "doc_4a1cf8a90e83", + "doc_745f751f1344", + "doc_a2e84472f1f9", + "doc_c346f13ce597", + "doc_d6ad61a79371" + ], + "returning": [ + "doc_4a1cf8a90e83", + "doc_a24b92541657", + "doc_c346f13ce597", + "doc_f5f256549d84" + ], + "returns": [ + "doc_07174a8633ac", + "doc_0ac01bce8c79", + "doc_136d7a909d51", + "doc_1e46cbc6bfae", + "doc_3f60413b3a06", + "doc_464a2b4a8d35", + "doc_4a1cf8a90e83", + "doc_745f751f1344", + "doc_9e9118ed2141", + "doc_a24b92541657", + "doc_b6088784caab", + "doc_bbc97cbff254", + "doc_c346f13ce597", + "doc_d6ad61a79371", + "doc_e7d508371e5c", + "doc_f12bbbc027a7", + "doc_f2ae50c638a4", + "doc_fdf48d427fc0" + ], + "reusable": [ + "doc_2798948c3080", + "doc_64090a20f830", + "doc_b6088784caab" + ], + "reuses": [ + "doc_64090a20f830" + ], + "review": [ + "doc_18d38fdacc9e", + "doc_3eeb75d383b8", + "doc_618e34441fa7", + "doc_75887f91c7df", + "doc_78b1717fafe3", + "doc_d6ad61a79371" + ], + "reviewers": [ + "doc_43ad71a4ad0c" + ], + "reviewing": [ + "doc_b6088784caab", + "doc_d147a81bac29", + "doc_deb460ffa5ee" + ], + "rg": [ + "doc_a24b92541657" + ], + "right": [ + "doc_0383dbf277ba", + "doc_4055659fe573", + "doc_618e34441fa7", + "doc_f5f256549d84" + ], + "risk": [ + "doc_43ad71a4ad0c", + "doc_d147a81bac29" + ], + "role": [ + "doc_745f751f1344", + "doc_e8794369a1a4" + ], + "role-based": [ + "doc_e67e762fb6ab" + ], + "roles": [ + "doc_3ae3aaf0c12c", + "doc_a24b92541657" + ], + "rollback": [ + "doc_3ae3aaf0c12c" + ], + "rolling": [ + "doc_78a8125d2c7e" + ], + "root": [ + "doc_464a2b4a8d35", + "doc_618e34441fa7", + "doc_61fd9f682847", + "doc_64090a20f830", + "doc_a24b92541657" + ], + "root_dir": [ + "doc_9e9118ed2141" + ], + "rounded": [ + "doc_3aa0b3792d22" + ], + "route": [ + "doc_18d38fdacc9e", + "doc_a5120de838fb" + ], + "routed": [ + "doc_d6ad61a79371" + ], + "router": [ + "doc_2a18de009d56", + "doc_3f60413b3a06", + "doc_a24b92541657", + "doc_a5120de838fb" + ], + "routes": [ + "doc_d6ad61a79371" + ], + "routine": [ + "doc_fdf48d427fc0" + ], + "routing": [ + "doc_18d38fdacc9e", + "doc_2a18de009d56", + "doc_618e34441fa7", + "doc_61fd9f682847", + "doc_75887f91c7df", + "doc_a24b92541657", + "doc_c93c115aeb85", + "doc_d147a81bac29", + "doc_e67e762fb6ab" + ], + "row": [ + "doc_a24b92541657" + ], + "rows": [ + "doc_7320ab982fab" + ], + "rule": [ + "doc_0383dbf277ba", + "doc_43ad71a4ad0c", + "doc_4e1ebaf40267", + "doc_618e34441fa7", + "doc_64090a20f830", + "doc_a24b92541657", + "doc_b6088784caab", + "doc_d6ad61a79371", + "doc_f18f523b6ecc" + ], + "rules": [ + "doc_0383dbf277ba", + "doc_0ac01bce8c79", + "doc_18d38fdacc9e", + "doc_1c5f37ea0ad3", + "doc_3eeb75d383b8", + "doc_40e30666bfa8", + "doc_43ad71a4ad0c", + "doc_50ac650fd8f6", + "doc_567be1281e2f", + "doc_618e34441fa7", + "doc_64090a20f830", + "doc_9e9118ed2141", + "doc_b6088784caab", + "doc_bbc97cbff254", + "doc_d147a81bac29", + "doc_d6ad61a79371", + "doc_deb460ffa5ee", + "doc_e34fe24f9585", + "doc_e67e762fb6ab", + "doc_f18f523b6ecc" + ], + "run": [ + "doc_0383dbf277ba", + "doc_067033cfc945", + "doc_07174a8633ac", + "doc_0ac01bce8c79", + "doc_109157c0d2d7", + "doc_136d7a909d51", + "doc_18d38fdacc9e", + "doc_1e46cbc6bfae", + "doc_2798948c3080", + "doc_2a18de009d56", + "doc_3ae3aaf0c12c", + "doc_3eeb75d383b8", + "doc_3f1383f72c5f", + "doc_3f60413b3a06", + "doc_4055659fe573", + "doc_43ad71a4ad0c", + "doc_4a1cf8a90e83", + "doc_4e1ebaf40267", + "doc_50ac650fd8f6", + "doc_567be1281e2f", + "doc_5a65ddab1445", + "doc_5ee8bbdbd8a8", + "doc_618e34441fa7", + "doc_64090a20f830", + "doc_745f751f1344", + "doc_75887f91c7df", + "doc_78a8125d2c7e", + "doc_9e9118ed2141", + "doc_a24b92541657", + "doc_a2e84472f1f9", + "doc_a5120de838fb", + "doc_bbc97cbff254", + "doc_bf91f8d5da74", + "doc_c346f13ce597", + "doc_c93c115aeb85", + "doc_d1f7d83e0824", + "doc_d6ad61a79371", + "doc_deb460ffa5ee", + "doc_e67e762fb6ab", + "doc_e7d508371e5c", + "doc_eeed52120ccf", + "doc_f12bbbc027a7", + "doc_f18f523b6ecc", + "doc_f54217a0f2cf", + "doc_f5f256549d84", + "doc_fdf48d427fc0" + ], + "run-event-contract": [ + "doc_3aa0b3792d22" + ], + "run-level": [ + "doc_0ac01bce8c79" + ], + "run_cancelled": [ + "doc_f54217a0f2cf" + ], + "run_case": [ + "doc_2a18de009d56" + ], + "run_completed": [ + "doc_f54217a0f2cf" + ], + "run_failed": [ + "doc_f54217a0f2cf" + ], + "run_handle": [ + "doc_07174a8633ac", + "doc_109157c0d2d7", + "doc_2a18de009d56", + "doc_4a1cf8a90e83", + "doc_e7d508371e5c" + ], + "run_id": [ + "doc_07174a8633ac", + "doc_1e46cbc6bfae", + "doc_2798948c3080", + "doc_4a1cf8a90e83", + "doc_5ee8bbdbd8a8", + "doc_75887f91c7df", + "doc_9e9118ed2141", + "doc_bbc97cbff254", + "doc_bf91f8d5da74", + "doc_e7d508371e5c", + "doc_f54217a0f2cf" + ], + "run_interrupted": [ + "doc_f54217a0f2cf" + ], + "run_paused": [ + "doc_0ac01bce8c79", + "doc_a24b92541657" + ], + "run_resumed": [ + "doc_0ac01bce8c79" + ], + "run_skill_command": [ + "doc_9e9118ed2141", + "doc_b6088784caab" + ], + "run_started": [ + "doc_4a1cf8a90e83", + "doc_f54217a0f2cf" + ], + "run_stream": [ + "doc_109157c0d2d7", + "doc_136d7a909d51", + "doc_2a18de009d56", + "doc_3f60413b3a06", + "doc_5ee8bbdbd8a8", + "doc_618e34441fa7", + "doc_7b26cbd9a7ae", + "doc_bbc97cbff254", + "doc_eeed52120ccf", + "doc_f2ae50c638a4", + "doc_f5f256549d84" + ], + "run_suite": [ + "doc_2a18de009d56", + "doc_bbc97cbff254" + ], + "run_sync": [ + "doc_1e46cbc6bfae", + "doc_2a18de009d56", + "doc_57e2139f0873", + "doc_bbc97cbff254" + ], + "run_terminal": [ + "doc_4a1cf8a90e83" + ], + "runaway": [ + "doc_18d38fdacc9e", + "doc_43ad71a4ad0c", + "doc_4462f67b1c03", + "doc_567be1281e2f", + "doc_c346f13ce597", + "doc_e67e762fb6ab", + "doc_fdf48d427fc0" + ], + "runmetrics": [ + "doc_2a18de009d56", + "doc_50ac650fd8f6", + "doc_618e34441fa7", + "doc_78a8125d2c7e" + ], + "runnable": [ + "doc_3aa0b3792d22", + "doc_5a65ddab1445", + "doc_a5120de838fb", + "doc_deb460ffa5ee" + ], + "runner": [ + "doc_0383dbf277ba", + "doc_07174a8633ac", + "doc_0ac01bce8c79", + "doc_109157c0d2d7", + "doc_136d7a909d51", + "doc_18d38fdacc9e", + "doc_1c5f37ea0ad3", + "doc_1e46cbc6bfae", + "doc_2a18de009d56", + "doc_3eeb75d383b8", + "doc_3f1383f72c5f", + "doc_3f60413b3a06", + "doc_4a1cf8a90e83", + "doc_4e1ebaf40267", + "doc_50ac650fd8f6", + "doc_567be1281e2f", + "doc_57e2139f0873", + "doc_5a65ddab1445", + "doc_5ee8bbdbd8a8", + "doc_618e34441fa7", + "doc_61fd9f682847", + "doc_64090a20f830", + "doc_745f751f1344", + "doc_9e9118ed2141", + "doc_a24b92541657", + "doc_a2e84472f1f9", + "doc_a5120de838fb", + "doc_bbc97cbff254", + "doc_bf91f8d5da74", + "doc_c346f13ce597", + "doc_c93c115aeb85", + "doc_d147a81bac29", + "doc_d6ad61a79371", + "doc_deb460ffa5ee", + "doc_e67e762fb6ab", + "doc_e7d508371e5c", + "doc_eeed52120ccf", + "doc_f18f523b6ecc", + "doc_f2ae50c638a4", + "doc_f54217a0f2cf", + "doc_f5f256549d84", + "doc_fdf48d427fc0" + ], + "runnerapimixin": [ + "doc_3eeb75d383b8" + ], + "runnerconfig": [ + "doc_2a18de009d56", + "doc_464a2b4a8d35", + "doc_4a1cf8a90e83", + "doc_61fd9f682847", + "doc_a24b92541657", + "doc_bbc97cbff254", + "doc_d147a81bac29", + "doc_d6ad61a79371", + "doc_eeed52120ccf", + "doc_f5f256549d84" + ], + "runnerdebugconfig": [ + "doc_a24b92541657" + ], + "runners": [ + "doc_4462f67b1c03", + "doc_d147a81bac29", + "doc_f18f523b6ecc" + ], + "running": [ + "doc_18d38fdacc9e", + "doc_4a1cf8a90e83", + "doc_618e34441fa7", + "doc_78b1717fafe3", + "doc_deb460ffa5ee", + "doc_fdf48d427fc0" + ], + "runs": [ + "doc_0383dbf277ba", + "doc_067033cfc945", + "doc_07174a8633ac", + "doc_0ac01bce8c79", + "doc_109157c0d2d7", + "doc_136d7a909d51", + "doc_1e46cbc6bfae", + "doc_3ae3aaf0c12c", + "doc_4a1cf8a90e83", + "doc_50ac650fd8f6", + "doc_745f751f1344", + "doc_78b1717fafe3", + "doc_9e9118ed2141", + "doc_a24b92541657", + "doc_a5120de838fb", + "doc_b6088784caab", + "doc_c346f13ce597", + "doc_c93c115aeb85", + "doc_e67e762fb6ab", + "doc_e7d508371e5c", + "doc_f12bbbc027a7", + "doc_f2ae50c638a4", + "doc_f54217a0f2cf", + "doc_f5f256549d84", + "doc_fdf48d427fc0" + ], + "runtime": [ + "doc_07174a8633ac", + "doc_109157c0d2d7", + "doc_2798948c3080", + "doc_2a18de009d56", + "doc_3aa0b3792d22", + "doc_3f1383f72c5f", + "doc_464a2b4a8d35", + "doc_4a1cf8a90e83", + "doc_4e1ebaf40267", + "doc_50ac650fd8f6", + "doc_567be1281e2f", + "doc_5a65ddab1445", + "doc_5ee8bbdbd8a8", + "doc_618e34441fa7", + "doc_61fd9f682847", + "doc_7320ab982fab", + "doc_9e9118ed2141", + "doc_a24b92541657", + "doc_a5120de838fb", + "doc_b6088784caab", + "doc_bbc97cbff254", + "doc_c346f13ce597", + "doc_d147a81bac29", + "doc_e7d508371e5c", + "doc_f18f523b6ecc" + ], + "runtime-injected": [ + "doc_745f751f1344" + ], + "runtime_state": [ + "doc_4a1cf8a90e83", + "doc_a24b92541657" + ], + "safe": [ + "doc_43ad71a4ad0c", + "doc_9e9118ed2141" + ], + "safely": [ + "doc_18d38fdacc9e", + "doc_43ad71a4ad0c", + "doc_f54217a0f2cf" + ], + "safety": [ + "doc_18d38fdacc9e", + "doc_2a18de009d56", + "doc_3ae3aaf0c12c", + "doc_bf91f8d5da74", + "doc_deb460ffa5ee", + "doc_e67e762fb6ab", + "doc_f2ae50c638a4" + ], + "same": [ + "doc_136d7a909d51", + "doc_1c5f37ea0ad3", + "doc_3eeb75d383b8", + "doc_40e30666bfa8", + "doc_4a1cf8a90e83", + "doc_5ee8bbdbd8a8", + "doc_618e34441fa7", + "doc_64090a20f830", + "doc_75887f91c7df", + "doc_7b26cbd9a7ae", + "doc_9e9118ed2141", + "doc_a24b92541657", + "doc_b6088784caab", + "doc_c346f13ce597", + "doc_c93c115aeb85", + "doc_e34fe24f9585", + "doc_f12bbbc027a7", + "doc_f2ae50c638a4", + "doc_f92685a34f7e" + ], + "sampling": [ + "doc_3f60413b3a06", + "doc_5ee8bbdbd8a8", + "doc_e8794369a1a4" + ], + "sandbox": [ + "doc_0ac01bce8c79", + "doc_18d38fdacc9e", + "doc_2a18de009d56", + "doc_40e30666bfa8", + "doc_43ad71a4ad0c", + "doc_464a2b4a8d35", + "doc_50ac650fd8f6", + "doc_567be1281e2f", + "doc_618e34441fa7", + "doc_745f751f1344", + "doc_9e9118ed2141", + "doc_a24b92541657", + "doc_a5120de838fb", + "doc_d147a81bac29" + ], + "sandbox_profile_provider": [ + "doc_a24b92541657" + ], + "sandboxing": [ + "doc_3ae3aaf0c12c", + "doc_d147a81bac29", + "doc_deb460ffa5ee", + "doc_f2ae50c638a4" + ], + "sandboxprofile": [ + "doc_2a18de009d56", + "doc_43ad71a4ad0c", + "doc_a24b92541657" + ], + "sandboxprofileprovider": [ + "doc_2a18de009d56" + ], + "sanitization": [ + "doc_2a18de009d56", + "doc_40e30666bfa8", + "doc_567be1281e2f", + "doc_9e9118ed2141", + "doc_c346f13ce597" + ], + "sanitize": [ + "doc_50ac650fd8f6", + "doc_745f751f1344", + "doc_a24b92541657" + ], + "sanitize_tool_output": [ + "doc_567be1281e2f", + "doc_9e9118ed2141", + "doc_a24b92541657" + ], + "sanitized": [ + "doc_567be1281e2f", + "doc_745f751f1344", + "doc_9e9118ed2141", + "doc_e7d508371e5c" + ], + "sanitizes": [ + "doc_745f751f1344" + ], + "satisfied": [ + "doc_4a1cf8a90e83" + ], + "save": [ + "doc_18d38fdacc9e" + ], + "says": [ + "doc_43ad71a4ad0c" + ], + "scale": [ + "doc_50ac650fd8f6" + ], + "scaling": [ + "doc_067033cfc945", + "doc_18d38fdacc9e" + ], + "scans": [ + "doc_b6088784caab" + ], + "scenario": [ + "doc_3f1383f72c5f", + "doc_3f60413b3a06", + "doc_a2e84472f1f9", + "doc_a5120de838fb", + "doc_b6088784caab", + "doc_d1f7d83e0824", + "doc_deb460ffa5ee", + "doc_f12bbbc027a7", + "doc_f92685a34f7e" + ], + "scenarios": [ + "doc_4a1cf8a90e83", + "doc_bf91f8d5da74" + ], + "schedule": [ + "doc_07174a8633ac" + ], + "scheduler": [ + "doc_c93c115aeb85" + ], + "schedules": [ + "doc_75887f91c7df" + ], + "scheduling": [ + "doc_2a18de009d56", + "doc_618e34441fa7" + ], + "schema": [ + "doc_067033cfc945", + "doc_07174a8633ac", + "doc_1c5f37ea0ad3", + "doc_2a18de009d56", + "doc_3aa0b3792d22", + "doc_3f60413b3a06", + "doc_4a1cf8a90e83", + "doc_50ac650fd8f6", + "doc_618e34441fa7", + "doc_745f751f1344", + "doc_9e9118ed2141", + "doc_a5120de838fb", + "doc_b6088784caab", + "doc_bf91f8d5da74" + ], + "schema-validated": [ + "doc_3f60413b3a06", + "doc_4a1cf8a90e83", + "doc_a5120de838fb" + ], + "schema_version": [ + "doc_4a1cf8a90e83", + "doc_618e34441fa7" + ], + "schemas": [ + "doc_50ac650fd8f6", + "doc_9e9118ed2141", + "doc_e34fe24f9585", + "doc_e7d508371e5c", + "doc_e8794369a1a4", + "doc_eeed52120ccf", + "doc_f2ae50c638a4" + ], + "scope": [ + "doc_3ae3aaf0c12c", + "doc_567be1281e2f", + "doc_618e34441fa7", + "doc_9e9118ed2141", + "doc_c346f13ce597", + "doc_d147a81bac29" + ], + "scoped": [ + "doc_0383dbf277ba", + "doc_43ad71a4ad0c", + "doc_464a2b4a8d35", + "doc_9e9118ed2141", + "doc_a5120de838fb" + ], + "scopes": [ + "doc_0383dbf277ba", + "doc_567be1281e2f" + ], + "scoping": [ + "doc_43ad71a4ad0c", + "doc_464a2b4a8d35", + "doc_a5120de838fb" + ], + "scoring": [ + "doc_2a18de009d56" + ], + "scratch": [ + "doc_07174a8633ac" + ], + "scripts": [ + "doc_0383dbf277ba", + "doc_3eeb75d383b8", + "doc_618e34441fa7", + "doc_a24b92541657", + "doc_b6088784caab", + "doc_bbc97cbff254", + "doc_e7d508371e5c" + ], + "sdk": [ + "doc_1c5f37ea0ad3", + "doc_2a18de009d56", + "doc_4e1ebaf40267", + "doc_5ee8bbdbd8a8", + "doc_deb460ffa5ee", + "doc_f18f523b6ecc", + "doc_f1e32ed2ffce" + ], + "sdks": [ + "doc_4e1ebaf40267" + ], + "search": [ + "doc_0383dbf277ba", + "doc_18d38fdacc9e", + "doc_2a18de009d56", + "doc_3eeb75d383b8", + "doc_5a65ddab1445", + "doc_61fd9f682847", + "doc_a5120de838fb", + "doc_d147a81bac29", + "doc_f5f256549d84" + ], + "second": [ + "doc_9e9118ed2141" + ], + "seconds": [ + "doc_5ee8bbdbd8a8", + "doc_61fd9f682847", + "doc_7320ab982fab", + "doc_745f751f1344", + "doc_78a8125d2c7e", + "doc_a24b92541657" + ], + "secret": [ + "doc_3ae3aaf0c12c", + "doc_3f1383f72c5f", + "doc_567be1281e2f", + "doc_618e34441fa7", + "doc_d147a81bac29" + ], + "secret-scope": [ + "doc_a24b92541657" + ], + "secret_scope_provider": [ + "doc_a24b92541657" + ], + "secrets": [ + "doc_0383dbf277ba", + "doc_067033cfc945", + "doc_18d38fdacc9e", + "doc_567be1281e2f" + ], + "secretscopeprovider": [ + "doc_2a18de009d56" + ], + "section": [ + "doc_4a1cf8a90e83", + "doc_a24b92541657", + "doc_f2ae50c638a4" + ], + "sections": [ + "doc_e67e762fb6ab" + ], + "security": [ + "doc_067033cfc945", + "doc_18d38fdacc9e", + "doc_1c5f37ea0ad3", + "doc_2a18de009d56", + "doc_3ae3aaf0c12c", + "doc_4055659fe573", + "doc_40e30666bfa8", + "doc_43ad71a4ad0c", + "doc_464a2b4a8d35", + "doc_567be1281e2f", + "doc_618e34441fa7", + "doc_61fd9f682847", + "doc_64090a20f830", + "doc_78a8125d2c7e", + "doc_a5120de838fb", + "doc_b6088784caab", + "doc_c93c115aeb85", + "doc_d147a81bac29", + "doc_deb460ffa5ee", + "doc_e34fe24f9585", + "doc_e67e762fb6ab", + "doc_f2ae50c638a4" + ], + "security-first": [ + "doc_464a2b4a8d35" + ], + "security-model": [ + "doc_3aa0b3792d22" + ], + "see": [ + "doc_136d7a909d51", + "doc_3eeb75d383b8", + "doc_4a1cf8a90e83", + "doc_4e1ebaf40267", + "doc_50ac650fd8f6", + "doc_61fd9f682847", + "doc_745f751f1344", + "doc_75887f91c7df", + "doc_7b26cbd9a7ae", + "doc_a5120de838fb", + "doc_b6088784caab", + "doc_bbc97cbff254", + "doc_deb460ffa5ee", + "doc_f54217a0f2cf", + "doc_f5f256549d84" + ], + "sees": [ + "doc_0ac01bce8c79", + "doc_e34fe24f9585", + "doc_e67e762fb6ab" + ], + "selected": [ + "doc_a24b92541657", + "doc_b6088784caab" + ], + "selection": [ + "doc_0383dbf277ba", + "doc_2a18de009d56", + "doc_5ee8bbdbd8a8", + "doc_a24b92541657", + "doc_c346f13ce597", + "doc_eeed52120ccf" + ], + "self-contained": [ + "doc_a5120de838fb" + ], + "self-correct": [ + "doc_0ac01bce8c79", + "doc_745f751f1344" + ], + "self-correction": [ + "doc_4055659fe573", + "doc_4e1ebaf40267", + "doc_9e9118ed2141" + ], + "self-hosted": [ + "doc_f92685a34f7e" + ], + "semantic": [ + "doc_0383dbf277ba" + ], + "semantically": [ + "doc_0383dbf277ba" + ], + "semantics": [ + "doc_d147a81bac29" + ], + "semicolons": [ + "doc_a24b92541657" + ], + "send": [ + "doc_e8794369a1a4" + ], + "sender": [ + "doc_75887f91c7df" + ], + "sending": [ + "doc_75887f91c7df", + "doc_d6ad61a79371", + "doc_f54217a0f2cf" + ], + "sends": [ + "doc_50ac650fd8f6", + "doc_5ee8bbdbd8a8", + "doc_d6ad61a79371" + ], + "sensitive": [ + "doc_9e9118ed2141", + "doc_a5120de838fb", + "doc_d6ad61a79371" + ], + "sent": [ + "doc_1e46cbc6bfae", + "doc_4e1ebaf40267", + "doc_78b1717fafe3", + "doc_9e9118ed2141", + "doc_e7d508371e5c" + ], + "sentiment": [ + "doc_f12bbbc027a7" + ], + "separate": [ + "doc_2798948c3080", + "doc_3eeb75d383b8", + "doc_4e1ebaf40267", + "doc_567be1281e2f", + "doc_a2e84472f1f9" + ], + "separated": [ + "doc_f12bbbc027a7" + ], + "separates": [ + "doc_57e2139f0873" + ], + "separation": [ + "doc_f18f523b6ecc" + ], + "sequence": [ + "doc_07174a8633ac", + "doc_0ac01bce8c79", + "doc_4e1ebaf40267" + ], + "sequences": [ + "doc_5ee8bbdbd8a8", + "doc_618e34441fa7", + "doc_e8794369a1a4" + ], + "sequentially": [ + "doc_745f751f1344", + "doc_c93c115aeb85" + ], + "serializable": [ + "doc_4e1ebaf40267" + ], + "serialization": [ + "doc_0383dbf277ba", + "doc_2a18de009d56", + "doc_618e34441fa7" + ], + "serialized": [ + "doc_4a1cf8a90e83" + ], + "serializer": [ + "doc_618e34441fa7" + ], + "server": [ + "doc_067033cfc945", + "doc_1c5f37ea0ad3", + "doc_2a18de009d56", + "doc_4055659fe573", + "doc_40e30666bfa8", + "doc_57e2139f0873", + "doc_5ee8bbdbd8a8", + "doc_61fd9f682847", + "doc_a24b92541657", + "doc_e34fe24f9585", + "doc_e67e762fb6ab", + "doc_eeed52120ccf" + ], + "servers": [ + "doc_40e30666bfa8", + "doc_a24b92541657", + "doc_a5120de838fb", + "doc_deb460ffa5ee", + "doc_e34fe24f9585", + "doc_e7d508371e5c", + "doc_f18f523b6ecc", + "doc_f1e32ed2ffce", + "doc_f5f256549d84" + ], + "service": [ + "doc_1c5f37ea0ad3", + "doc_618e34441fa7", + "doc_a24b92541657" + ], + "services": [ + "doc_067033cfc945", + "doc_1c5f37ea0ad3", + "doc_1e46cbc6bfae", + "doc_bbc97cbff254", + "doc_f5f256549d84" + ], + "session": [ + "doc_4462f67b1c03", + "doc_4a1cf8a90e83", + "doc_5ee8bbdbd8a8", + "doc_7320ab982fab" + ], + "session-aware": [ + "doc_4a1cf8a90e83" + ], + "session_token": [ + "doc_4a1cf8a90e83", + "doc_5ee8bbdbd8a8" + ], + "sessions": [ + "doc_0383dbf277ba", + "doc_5ee8bbdbd8a8" + ], + "set": [ + "doc_0383dbf277ba", + "doc_067033cfc945", + "doc_136d7a909d51", + "doc_18d38fdacc9e", + "doc_2798948c3080", + "doc_3ae3aaf0c12c", + "doc_3eeb75d383b8", + "doc_3f60413b3a06", + "doc_43ad71a4ad0c", + "doc_464a2b4a8d35", + "doc_4a1cf8a90e83", + "doc_50ac650fd8f6", + "doc_567be1281e2f", + "doc_5ee8bbdbd8a8", + "doc_618e34441fa7", + "doc_61fd9f682847", + "doc_64090a20f830", + "doc_7320ab982fab", + "doc_745f751f1344", + "doc_75887f91c7df", + "doc_78b1717fafe3", + "doc_a24b92541657", + "doc_bbc97cbff254", + "doc_c93c115aeb85", + "doc_deb460ffa5ee", + "doc_e67e762fb6ab", + "doc_f18f523b6ecc", + "doc_f2ae50c638a4", + "doc_f5f256549d84", + "doc_fdf48d427fc0" + ], + "setting": [ + "doc_567be1281e2f", + "doc_7320ab982fab", + "doc_a24b92541657", + "doc_fdf48d427fc0" + ], + "settings": [ + "doc_3f60413b3a06", + "doc_61fd9f682847", + "doc_7320ab982fab", + "doc_d1f7d83e0824", + "doc_eeed52120ccf", + "doc_f1e32ed2ffce" + ], + "setup": [ + "doc_0383dbf277ba", + "doc_067033cfc945", + "doc_43ad71a4ad0c", + "doc_57e2139f0873", + "doc_75887f91c7df", + "doc_78a8125d2c7e", + "doc_78b1717fafe3", + "doc_d147a81bac29", + "doc_f18f523b6ecc" + ], + "setups": [ + "doc_067033cfc945", + "doc_40e30666bfa8" + ], + "severity": [ + "doc_43ad71a4ad0c", + "doc_78a8125d2c7e" + ], + "sh": [ + "doc_3eeb75d383b8", + "doc_b6088784caab" + ], + "sha-256": [ + "doc_64090a20f830" + ], + "shape": [ + "doc_9e9118ed2141", + "doc_b6088784caab", + "doc_c346f13ce597", + "doc_f5f256549d84" + ], + "shapes": [ + "doc_4e1ebaf40267" + ], + "share": [ + "doc_e34fe24f9585" + ], + "shared": [ + "doc_0383dbf277ba", + "doc_2a18de009d56", + "doc_4462f67b1c03", + "doc_61fd9f682847", + "doc_a24b92541657", + "doc_f5f256549d84" + ], + "shares": [ + "doc_e34fe24f9585" + ], + "sharing": [ + "doc_e34fe24f9585" + ], + "sheet": [ + "doc_7320ab982fab" + ], + "shell": [ + "doc_9e9118ed2141", + "doc_a24b92541657" + ], + "shift": [ + "doc_2798948c3080" + ], + "shipping": [ + "doc_136d7a909d51", + "doc_3ae3aaf0c12c", + "doc_f2ae50c638a4" + ], + "ships": [ + "doc_0383dbf277ba", + "doc_1c5f37ea0ad3", + "doc_464a2b4a8d35", + "doc_9e9118ed2141", + "doc_c346f13ce597", + "doc_f1e32ed2ffce" + ], + "short": [ + "doc_f5f256549d84" + ], + "short-circuit": [ + "doc_745f751f1344", + "doc_c346f13ce597" + ], + "short-lived": [ + "doc_0383dbf277ba" + ], + "shortest": [ + "doc_bf91f8d5da74" + ], + "shorthand": [ + "doc_64090a20f830" + ], + "should": [ + "doc_067033cfc945", + "doc_18d38fdacc9e", + "doc_3eeb75d383b8", + "doc_43ad71a4ad0c", + "doc_4e1ebaf40267", + "doc_50ac650fd8f6", + "doc_618e34441fa7", + "doc_9e9118ed2141", + "doc_a5120de838fb", + "doc_bbc97cbff254", + "doc_c346f13ce597", + "doc_d147a81bac29", + "doc_e67e762fb6ab", + "doc_f18f523b6ecc", + "doc_f2ae50c638a4", + "doc_f54217a0f2cf" + ], + "show": [ + "doc_136d7a909d51", + "doc_b6088784caab", + "doc_f2ae50c638a4" + ], + "shows": [ + "doc_40e30666bfa8", + "doc_4462f67b1c03", + "doc_464a2b4a8d35", + "doc_7b26cbd9a7ae", + "doc_a5120de838fb", + "doc_d1f7d83e0824", + "doc_fdf48d427fc0" + ], + "shutdown": [ + "doc_4462f67b1c03", + "doc_a5120de838fb" + ], + "side": [ + "doc_0383dbf277ba", + "doc_4a1cf8a90e83", + "doc_78b1717fafe3", + "doc_b6088784caab" + ], + "sidebar": [ + "doc_2a18de009d56", + "doc_3aa0b3792d22" + ], + "sidenav": [ + "doc_3aa0b3792d22" + ], + "signal": [ + "doc_43ad71a4ad0c", + "doc_57e2139f0873" + ], + "signals": [ + "doc_0ac01bce8c79", + "doc_43ad71a4ad0c", + "doc_57e2139f0873", + "doc_5ee8bbdbd8a8" + ], + "signature": [ + "doc_07174a8633ac", + "doc_64090a20f830", + "doc_745f751f1344", + "doc_c346f13ce597", + "doc_f92685a34f7e" + ], + "signatures": [ + "doc_deb460ffa5ee" + ], + "significant": [ + "doc_18d38fdacc9e", + "doc_e67e762fb6ab" + ], + "silent": [ + "doc_4055659fe573", + "doc_4e1ebaf40267", + "doc_50ac650fd8f6" + ], + "silently": [ + "doc_18d38fdacc9e", + "doc_4e1ebaf40267", + "doc_618e34441fa7", + "doc_a24b92541657" + ], + "similar": [ + "doc_0383dbf277ba", + "doc_0ac01bce8c79", + "doc_136d7a909d51", + "doc_d6ad61a79371" + ], + "similarity": [ + "doc_0383dbf277ba" + ], + "similarly": [ + "doc_745f751f1344" + ], + "simple": [ + "doc_2798948c3080", + "doc_57e2139f0873", + "doc_5a65ddab1445", + "doc_e7d508371e5c", + "doc_eeed52120ccf", + "doc_f12bbbc027a7", + "doc_f18f523b6ecc", + "doc_f54217a0f2cf" + ], + "simpler": [ + "doc_3f60413b3a06", + "doc_40e30666bfa8", + "doc_f18f523b6ecc" + ], + "simplest": [ + "doc_18d38fdacc9e", + "doc_1e46cbc6bfae", + "doc_57e2139f0873", + "doc_a5120de838fb", + "doc_f54217a0f2cf", + "doc_fdf48d427fc0" + ], + "simultaneously": [ + "doc_7b26cbd9a7ae" + ], + "since": [ + "doc_0ac01bce8c79", + "doc_1e46cbc6bfae" + ], + "single": [ + "doc_0ac01bce8c79", + "doc_1e46cbc6bfae", + "doc_3f60413b3a06", + "doc_464a2b4a8d35", + "doc_61fd9f682847", + "doc_64090a20f830", + "doc_745f751f1344", + "doc_9e9118ed2141", + "doc_a24b92541657", + "doc_a2e84472f1f9", + "doc_c346f13ce597", + "doc_e67e762fb6ab", + "doc_f12bbbc027a7", + "doc_f2ae50c638a4" + ], + "single-agent": [ + "doc_18d38fdacc9e" + ], + "single-case": [ + "doc_2a18de009d56" + ], + "single-container": [ + "doc_067033cfc945" + ], + "single-process": [ + "doc_0383dbf277ba", + "doc_f5f256549d84" + ], + "single-source": [ + "doc_a24b92541657" + ], + "single-turn": [ + "doc_f18f523b6ecc" + ], + "singleton": [ + "doc_64090a20f830", + "doc_78b1717fafe3" + ], + "sink": [ + "doc_1e46cbc6bfae", + "doc_2a18de009d56", + "doc_a24b92541657" + ], + "size": [ + "doc_61fd9f682847", + "doc_64090a20f830", + "doc_f5f256549d84" + ], + "skill": [ + "doc_2a18de009d56", + "doc_3eeb75d383b8", + "doc_5ee8bbdbd8a8", + "doc_745f751f1344", + "doc_9e9118ed2141", + "doc_a24b92541657", + "doc_b6088784caab", + "doc_d147a81bac29", + "doc_e67e762fb6ab", + "doc_f18f523b6ecc" + ], + "skill-provided": [ + "doc_e67e762fb6ab" + ], + "skill_tool_policy": [ + "doc_e67e762fb6ab" + ], + "skilldoc": [ + "doc_2a18de009d56" + ], + "skills": [ + "doc_18d38fdacc9e", + "doc_2a18de009d56", + "doc_3aa0b3792d22", + "doc_9e9118ed2141", + "doc_a24b92541657", + "doc_b6088784caab", + "doc_d147a81bac29", + "doc_deb460ffa5ee", + "doc_e67e762fb6ab", + "doc_f18f523b6ecc", + "doc_f2ae50c638a4" + ], + "skills_dir": [ + "doc_a24b92541657", + "doc_b6088784caab", + "doc_e67e762fb6ab" + ], + "skillstore": [ + "doc_2a18de009d56" + ], + "skilltoolpolicy": [ + "doc_9e9118ed2141", + "doc_e67e762fb6ab" + ], + "skip": [ + "doc_4e1ebaf40267", + "doc_a24b92541657" + ], + "skip_action": [ + "doc_a24b92541657" + ], + "skippable": [ + "doc_618e34441fa7" + ], + "skipped": [ + "doc_0ac01bce8c79", + "doc_d6ad61a79371" + ], + "skipping": [ + "doc_18d38fdacc9e" + ], + "skips": [ + "doc_4a1cf8a90e83", + "doc_d1f7d83e0824" + ], + "slack": [ + "doc_2a18de009d56", + "doc_a24b92541657" + ], + "slack-based": [ + "doc_d6ad61a79371" + ], + "slots": [ + "doc_f12bbbc027a7" + ], + "slow": [ + "doc_4462f67b1c03" + ], + "slowest": [ + "doc_f5f256549d84" + ], + "small": [ + "doc_2798948c3080", + "doc_f2ae50c638a4", + "doc_f5f256549d84" + ], + "smaller": [ + "doc_fdf48d427fc0" + ], + "smallest": [ + "doc_d147a81bac29", + "doc_f18f523b6ecc", + "doc_f5f256549d84" + ], + "snapshot": [ + "doc_07174a8633ac", + "doc_4a1cf8a90e83", + "doc_bbc97cbff254", + "doc_e7d508371e5c" + ], + "snapshots": [ + "doc_07174a8633ac", + "doc_2a18de009d56" + ], + "snippet": [ + "doc_07174a8633ac", + "doc_40e30666bfa8", + "doc_4462f67b1c03", + "doc_464a2b4a8d35", + "doc_64090a20f830", + "doc_7b26cbd9a7ae", + "doc_c346f13ce597", + "doc_d1f7d83e0824", + "doc_fdf48d427fc0" + ], + "snippets": [ + "doc_3aa0b3792d22", + "doc_bf91f8d5da74", + "doc_d147a81bac29", + "doc_deb460ffa5ee" + ], + "so": [ + "doc_07174a8633ac", + "doc_0ac01bce8c79", + "doc_109157c0d2d7", + "doc_3f60413b3a06", + "doc_4462f67b1c03", + "doc_4a1cf8a90e83", + "doc_5ee8bbdbd8a8", + "doc_618e34441fa7", + "doc_64090a20f830", + "doc_745f751f1344", + "doc_7b26cbd9a7ae", + "doc_9e9118ed2141", + "doc_b6088784caab", + "doc_e8794369a1a4" + ], + "socials": [ + "doc_3aa0b3792d22" + ], + "socket": [ + "doc_4462f67b1c03" + ], + "socket_connect_timeout": [ + "doc_4462f67b1c03" + ], + "socket_keepalive": [ + "doc_4462f67b1c03" + ], + "socket_timeout": [ + "doc_4462f67b1c03" + ], + "solution": [ + "doc_136d7a909d51" + ], + "solutions": [ + "doc_136d7a909d51" + ], + "solves": [ + "doc_18d38fdacc9e", + "doc_3ae3aaf0c12c" + ], + "some": [ + "doc_18d38fdacc9e", + "doc_3eeb75d383b8", + "doc_4055659fe573", + "doc_618e34441fa7", + "doc_e7d508371e5c", + "doc_f12bbbc027a7" + ], + "something": [ + "doc_4055659fe573", + "doc_b6088784caab" + ], + "sometimes": [ + "doc_3f60413b3a06" + ], + "sonnet": [ + "doc_f92685a34f7e" + ], + "soon": [ + "doc_f12bbbc027a7" + ], + "sorted": [ + "doc_618e34441fa7" + ], + "source": [ + "doc_0ac01bce8c79", + "doc_2798948c3080", + "doc_4055659fe573", + "doc_61fd9f682847", + "doc_745f751f1344", + "doc_e8794369a1a4", + "doc_eeed52120ccf", + "doc_fdf48d427fc0" + ], + "source-level": [ + "doc_d147a81bac29" + ], + "source-of-truth": [ + "doc_f18f523b6ecc" + ], + "source_agent": [ + "doc_75887f91c7df" + ], + "sources": [ + "doc_18d38fdacc9e", + "doc_2798948c3080", + "doc_eeed52120ccf" + ], + "spaces": [ + "doc_64090a20f830" + ], + "span": [ + "doc_2a18de009d56" + ], + "span_agent_run": [ + "doc_2a18de009d56" + ], + "spans": [ + "doc_50ac650fd8f6", + "doc_78a8125d2c7e" + ], + "special-casing": [ + "doc_618e34441fa7" + ], + "specialist": [ + "doc_18d38fdacc9e", + "doc_57e2139f0873", + "doc_a24b92541657", + "doc_a2e84472f1f9", + "doc_a5120de838fb", + "doc_e67e762fb6ab", + "doc_f12bbbc027a7", + "doc_f18f523b6ecc" + ], + "specialists": [ + "doc_18d38fdacc9e", + "doc_57e2139f0873", + "doc_d1f7d83e0824" + ], + "specialized": [ + "doc_78b1717fafe3", + "doc_e67e762fb6ab" + ], + "specific": [ + "doc_18d38fdacc9e", + "doc_2798948c3080", + "doc_2a18de009d56", + "doc_3f60413b3a06", + "doc_4462f67b1c03", + "doc_464a2b4a8d35", + "doc_5ee8bbdbd8a8", + "doc_618e34441fa7", + "doc_61fd9f682847", + "doc_a5120de838fb", + "doc_b6088784caab", + "doc_f5f256549d84" + ], + "specifically": [ + "doc_3eeb75d383b8" + ], + "specified": [ + "doc_1e46cbc6bfae", + "doc_3f60413b3a06" + ], + "specifies": [ + "doc_1e46cbc6bfae", + "doc_c93c115aeb85" + ], + "specify": [ + "doc_0ac01bce8c79", + "doc_eeed52120ccf" + ], + "spend": [ + "doc_067033cfc945", + "doc_18d38fdacc9e", + "doc_e67e762fb6ab" + ], + "spending": [ + "doc_567be1281e2f", + "doc_d6ad61a79371", + "doc_fdf48d427fc0" + ], + "spike": [ + "doc_78a8125d2c7e" + ], + "split": [ + "doc_18d38fdacc9e", + "doc_3ae3aaf0c12c", + "doc_57e2139f0873", + "doc_64090a20f830" + ], + "splits": [ + "doc_64090a20f830" + ], + "sqlite": [ + "doc_0383dbf277ba", + "doc_067033cfc945", + "doc_618e34441fa7", + "doc_61fd9f682847", + "doc_f5f256549d84" + ], + "sqlite3": [ + "doc_61fd9f682847" + ], + "sqlitememorystore": [ + "doc_2a18de009d56", + "doc_bbc97cbff254" + ], + "src": [ + "doc_3eeb75d383b8", + "doc_bbc97cbff254", + "doc_d147a81bac29", + "doc_f18f523b6ecc" + ], + "sre": [ + "doc_1e46cbc6bfae" + ], + "sse": [ + "doc_61fd9f682847" + ], + "ssl": [ + "doc_61fd9f682847" + ], + "stability": [ + "doc_618e34441fa7", + "doc_bbc97cbff254", + "doc_deb460ffa5ee" + ], + "stable": [ + "doc_2a18de009d56", + "doc_4e1ebaf40267", + "doc_618e34441fa7", + "doc_d147a81bac29" + ], + "stack": [ + "doc_3f60413b3a06", + "doc_78a8125d2c7e" + ], + "stage": [ + "doc_745f751f1344" + ], + "staging": [ + "doc_78a8125d2c7e" + ], + "stale": [ + "doc_4a1cf8a90e83" + ], + "standard": [ + "doc_78b1717fafe3", + "doc_e34fe24f9585" + ], + "start": [ + "doc_0383dbf277ba", + "doc_0ac01bce8c79", + "doc_18d38fdacc9e", + "doc_1e46cbc6bfae", + "doc_3aa0b3792d22", + "doc_3ae3aaf0c12c", + "doc_3f1383f72c5f", + "doc_4a1cf8a90e83", + "doc_50ac650fd8f6", + "doc_57e2139f0873", + "doc_78a8125d2c7e", + "doc_78b1717fafe3", + "doc_9e9118ed2141", + "doc_a5120de838fb", + "doc_d147a81bac29", + "doc_deb460ffa5ee", + "doc_e67e762fb6ab", + "doc_f54217a0f2cf" + ], + "started": [ + "doc_07174a8633ac", + "doc_1e46cbc6bfae", + "doc_4a1cf8a90e83", + "doc_f54217a0f2cf" + ], + "started_at_s": [ + "doc_4a1cf8a90e83" + ], + "starting": [ + "doc_07174a8633ac", + "doc_18d38fdacc9e", + "doc_3ae3aaf0c12c", + "doc_eeed52120ccf", + "doc_f92685a34f7e" + ], + "starts": [ + "doc_0383dbf277ba", + "doc_618e34441fa7", + "doc_745f751f1344" + ], + "startup": [ + "doc_61fd9f682847" + ], + "stat": [ + "doc_64090a20f830" + ], + "stat-based": [ + "doc_64090a20f830" + ], + "state": [ + "doc_0383dbf277ba", + "doc_07174a8633ac", + "doc_0ac01bce8c79", + "doc_109157c0d2d7", + "doc_136d7a909d51", + "doc_1e46cbc6bfae", + "doc_2a18de009d56", + "doc_3f1383f72c5f", + "doc_4055659fe573", + "doc_4a1cf8a90e83", + "doc_4e1ebaf40267", + "doc_50ac650fd8f6", + "doc_5ee8bbdbd8a8", + "doc_75887f91c7df", + "doc_78a8125d2c7e", + "doc_78b1717fafe3", + "doc_9e9118ed2141", + "doc_a24b92541657", + "doc_a5120de838fb", + "doc_bbc97cbff254", + "doc_bf91f8d5da74", + "doc_c93c115aeb85", + "doc_d147a81bac29", + "doc_deb460ffa5ee", + "doc_e67e762fb6ab", + "doc_e7d508371e5c", + "doc_f18f523b6ecc", + "doc_f54217a0f2cf", + "doc_f5f256549d84", + "doc_fdf48d427fc0" + ], + "state_snapshot": [ + "doc_bbc97cbff254", + "doc_e7d508371e5c" + ], + "stateful": [ + "doc_5ee8bbdbd8a8" + ], + "statement": [ + "doc_618e34441fa7" + ], + "statements": [ + "doc_618e34441fa7" + ], + "stateretentionpolicy": [ + "doc_07174a8633ac" + ], + "states": [ + "doc_4a1cf8a90e83", + "doc_618e34441fa7", + "doc_e7d508371e5c" + ], + "static": [ + "doc_2798948c3080", + "doc_a24b92541657" + ], + "statistics": [ + "doc_2a18de009d56", + "doc_618e34441fa7" + ], + "status": [ + "doc_109157c0d2d7", + "doc_18d38fdacc9e", + "doc_567be1281e2f", + "doc_9e9118ed2141", + "doc_f54217a0f2cf" + ], + "stays": [ + "doc_109157c0d2d7", + "doc_464a2b4a8d35", + "doc_c93c115aeb85" + ], + "stdin": [ + "doc_a24b92541657" + ], + "stdout": [ + "doc_78a8125d2c7e", + "doc_a24b92541657" + ], + "step": [ + "doc_0383dbf277ba", + "doc_07174a8633ac", + "doc_0ac01bce8c79", + "doc_109157c0d2d7", + "doc_18d38fdacc9e", + "doc_1e46cbc6bfae", + "doc_3f1383f72c5f", + "doc_4055659fe573", + "doc_4a1cf8a90e83", + "doc_4e1ebaf40267", + "doc_50ac650fd8f6", + "doc_567be1281e2f", + "doc_5a65ddab1445", + "doc_5ee8bbdbd8a8", + "doc_745f751f1344", + "doc_a24b92541657", + "doc_a2e84472f1f9", + "doc_c346f13ce597", + "doc_e67e762fb6ab", + "doc_e7d508371e5c", + "doc_eeed52120ccf", + "doc_f18f523b6ecc", + "doc_f54217a0f2cf" + ], + "step-by-step": [ + "doc_4a1cf8a90e83", + "doc_745f751f1344" + ], + "step_name": [ + "doc_75887f91c7df" + ], + "step_started": [ + "doc_109157c0d2d7", + "doc_4a1cf8a90e83", + "doc_5ee8bbdbd8a8", + "doc_f54217a0f2cf" + ], + "steps": [ + "doc_0383dbf277ba", + "doc_067033cfc945", + "doc_109157c0d2d7", + "doc_136d7a909d51", + "doc_18d38fdacc9e", + "doc_1c5f37ea0ad3", + "doc_2798948c3080", + "doc_3ae3aaf0c12c", + "doc_4055659fe573", + "doc_4e1ebaf40267", + "doc_567be1281e2f", + "doc_57e2139f0873", + "doc_5a65ddab1445", + "doc_61fd9f682847", + "doc_7320ab982fab", + "doc_75887f91c7df", + "doc_78a8125d2c7e", + "doc_78b1717fafe3", + "doc_9e9118ed2141", + "doc_a24b92541657", + "doc_b6088784caab", + "doc_c93c115aeb85", + "doc_d147a81bac29", + "doc_deb460ffa5ee", + "doc_e34fe24f9585", + "doc_e67e762fb6ab", + "doc_e7d508371e5c", + "doc_e8794369a1a4", + "doc_eeed52120ccf", + "doc_f12bbbc027a7", + "doc_f18f523b6ecc", + "doc_f1e32ed2ffce", + "doc_f92685a34f7e" + ], + "still": [ + "doc_07174a8633ac", + "doc_0ac01bce8c79", + "doc_4055659fe573", + "doc_618e34441fa7" + ], + "stop": [ + "doc_136d7a909d51", + "doc_4055659fe573", + "doc_5ee8bbdbd8a8", + "doc_618e34441fa7", + "doc_7320ab982fab", + "doc_7b26cbd9a7ae", + "doc_e8794369a1a4" + ], + "stopped": [ + "doc_136d7a909d51", + "doc_4055659fe573", + "doc_5ee8bbdbd8a8", + "doc_e8794369a1a4" + ], + "stops": [ + "doc_50ac650fd8f6", + "doc_745f751f1344", + "doc_a24b92541657" + ], + "storage": [ + "doc_0383dbf277ba", + "doc_07174a8633ac", + "doc_a5120de838fb", + "doc_f5f256549d84" + ], + "store": [ + "doc_0383dbf277ba", + "doc_067033cfc945", + "doc_07174a8633ac", + "doc_136d7a909d51", + "doc_2798948c3080", + "doc_2a18de009d56", + "doc_4462f67b1c03", + "doc_4a1cf8a90e83", + "doc_567be1281e2f", + "doc_618e34441fa7", + "doc_75887f91c7df", + "doc_b6088784caab", + "doc_e67e762fb6ab" + ], + "stored": [ + "doc_0383dbf277ba", + "doc_07174a8633ac", + "doc_4a1cf8a90e83", + "doc_4e1ebaf40267", + "doc_64090a20f830", + "doc_78b1717fafe3" + ], + "stores": [ + "doc_2a18de009d56", + "doc_5ee8bbdbd8a8", + "doc_bbc97cbff254", + "doc_bf91f8d5da74", + "doc_d147a81bac29", + "doc_deb460ffa5ee" + ], + "str": [ + "doc_07174a8633ac", + "doc_1e46cbc6bfae", + "doc_464a2b4a8d35", + "doc_4a1cf8a90e83", + "doc_5ee8bbdbd8a8", + "doc_64090a20f830", + "doc_745f751f1344", + "doc_75887f91c7df", + "doc_a24b92541657", + "doc_a2e84472f1f9", + "doc_e67e762fb6ab", + "doc_e7d508371e5c", + "doc_e8794369a1a4", + "doc_f54217a0f2cf" + ], + "straight": [ + "doc_d1f7d83e0824" + ], + "strategies": [ + "doc_a5120de838fb", + "doc_f12bbbc027a7" + ], + "strategy": [ + "doc_5ee8bbdbd8a8", + "doc_a24b92541657" + ], + "stream": [ + "doc_109157c0d2d7", + "doc_136d7a909d51", + "doc_2a18de009d56", + "doc_3f60413b3a06", + "doc_43ad71a4ad0c", + "doc_5ee8bbdbd8a8", + "doc_618e34441fa7", + "doc_61fd9f682847", + "doc_7b26cbd9a7ae", + "doc_c346f13ce597", + "doc_d147a81bac29", + "doc_d6ad61a79371", + "doc_eeed52120ccf", + "doc_f18f523b6ecc", + "doc_f2ae50c638a4", + "doc_f54217a0f2cf", + "doc_f5f256549d84" + ], + "stream_completed": [ + "doc_2a18de009d56" + ], + "stream_timeout_s": [ + "doc_4462f67b1c03" + ], + "streamed": [ + "doc_f54217a0f2cf" + ], + "streaming": [ + "doc_109157c0d2d7", + "doc_136d7a909d51", + "doc_2a18de009d56", + "doc_3aa0b3792d22", + "doc_3f60413b3a06", + "doc_4462f67b1c03", + "doc_5ee8bbdbd8a8", + "doc_618e34441fa7", + "doc_78a8125d2c7e", + "doc_7b26cbd9a7ae", + "doc_a5120de838fb", + "doc_bbc97cbff254", + "doc_bf91f8d5da74", + "doc_c346f13ce597", + "doc_d147a81bac29", + "doc_deb460ffa5ee", + "doc_e7d508371e5c", + "doc_eeed52120ccf", + "doc_f18f523b6ecc", + "doc_f1e32ed2ffce", + "doc_f2ae50c638a4", + "doc_f92685a34f7e", + "doc_fdf48d427fc0" + ], + "streaming_chat_with_memory": [ + "doc_3aa0b3792d22" + ], + "streams": [ + "doc_bbc97cbff254" + ], + "strict": [ + "doc_64090a20f830" + ], + "stricter": [ + "doc_a2e84472f1f9" + ], + "string": [ + "doc_2798948c3080", + "doc_4a1cf8a90e83", + "doc_61fd9f682847", + "doc_64090a20f830", + "doc_9e9118ed2141", + "doc_a24b92541657", + "doc_eeed52120ccf" + ], + "strings": [ + "doc_2798948c3080", + "doc_64090a20f830" + ], + "strip": [ + "doc_50ac650fd8f6" + ], + "stripped": [ + "doc_9e9118ed2141" + ], + "structure": [ + "doc_07174a8633ac", + "doc_618e34441fa7", + "doc_f18f523b6ecc", + "doc_f54217a0f2cf" + ], + "structured": [ + "doc_136d7a909d51", + "doc_2a18de009d56", + "doc_3f1383f72c5f", + "doc_3f60413b3a06", + "doc_4a1cf8a90e83", + "doc_5ee8bbdbd8a8", + "doc_618e34441fa7", + "doc_61fd9f682847", + "doc_7320ab982fab", + "doc_75887f91c7df", + "doc_78a8125d2c7e", + "doc_9e9118ed2141", + "doc_a5120de838fb", + "doc_e67e762fb6ab", + "doc_e8794369a1a4", + "doc_f1e32ed2ffce", + "doc_f54217a0f2cf" + ], + "structured_response": [ + "doc_3f60413b3a06", + "doc_5ee8bbdbd8a8" + ], + "structures": [ + "doc_50ac650fd8f6" + ], + "style": [ + "doc_3aa0b3792d22" + ], + "sub-expertise": [ + "doc_e67e762fb6ab" + ], + "sub-module": [ + "doc_2a18de009d56" + ], + "sub-modules": [ + "doc_2a18de009d56" + ], + "subagent": [ + "doc_07174a8633ac", + "doc_1e46cbc6bfae", + "doc_3f60413b3a06", + "doc_4055659fe573", + "doc_50ac650fd8f6", + "doc_618e34441fa7", + "doc_a24b92541657", + "doc_a2e84472f1f9", + "doc_a5120de838fb", + "doc_bbc97cbff254", + "doc_bf91f8d5da74", + "doc_deb460ffa5ee", + "doc_e67e762fb6ab", + "doc_e7d508371e5c", + "doc_f12bbbc027a7", + "doc_f18f523b6ecc", + "doc_f54217a0f2cf" + ], + "subagent_completed": [ + "doc_f54217a0f2cf" + ], + "subagent_executions": [ + "doc_1e46cbc6bfae", + "doc_4a1cf8a90e83", + "doc_a2e84472f1f9", + "doc_bbc97cbff254", + "doc_bf91f8d5da74", + "doc_e7d508371e5c" + ], + "subagent_failure_policy": [ + "doc_a24b92541657", + "doc_a2e84472f1f9" + ], + "subagent_name": [ + "doc_a2e84472f1f9", + "doc_f54217a0f2cf" + ], + "subagent_parallelism_mode": [ + "doc_a24b92541657", + "doc_e67e762fb6ab" + ], + "subagent_queue_backpressure_limit": [ + "doc_a24b92541657" + ], + "subagent_router": [ + "doc_a24b92541657", + "doc_e67e762fb6ab" + ], + "subagent_started": [ + "doc_f54217a0f2cf" + ], + "subagentexecutionrecord": [ + "doc_4a1cf8a90e83", + "doc_618e34441fa7", + "doc_a2e84472f1f9", + "doc_e7d508371e5c" + ], + "subagentrouter": [ + "doc_a24b92541657", + "doc_e67e762fb6ab" + ], + "subagentroutingerror": [ + "doc_2a18de009d56" + ], + "subagents": [ + "doc_18d38fdacc9e", + "doc_57e2139f0873", + "doc_618e34441fa7", + "doc_a24b92541657", + "doc_a2e84472f1f9", + "doc_a5120de838fb", + "doc_deb460ffa5ee", + "doc_e67e762fb6ab", + "doc_eeed52120ccf", + "doc_f12bbbc027a7", + "doc_f18f523b6ecc", + "doc_f2ae50c638a4" + ], + "subagents_with_router": [ + "doc_3aa0b3792d22" + ], + "subclass": [ + "doc_2a18de009d56" + ], + "subdirectories": [ + "doc_bbc97cbff254" + ], + "subdirectory": [ + "doc_618e34441fa7", + "doc_b6088784caab" + ], + "subject": [ + "doc_2a18de009d56" + ], + "submits": [ + "doc_75887f91c7df" + ], + "subsequent": [ + "doc_5ee8bbdbd8a8", + "doc_a24b92541657" + ], + "substitution": [ + "doc_a5120de838fb" + ], + "subsystem": [ + "doc_3eeb75d383b8" + ], + "subsystems": [ + "doc_f18f523b6ecc" + ], + "subtasks": [ + "doc_a2e84472f1f9" + ], + "succeed": [ + "doc_4055659fe573", + "doc_f12bbbc027a7" + ], + "succeeded": [ + "doc_745f751f1344" + ], + "succeeds": [ + "doc_f12bbbc027a7" + ], + "success": [ + "doc_0ac01bce8c79", + "doc_50ac650fd8f6", + "doc_618e34441fa7", + "doc_745f751f1344", + "doc_78a8125d2c7e", + "doc_a24b92541657", + "doc_a2e84472f1f9", + "doc_d147a81bac29", + "doc_e7d508371e5c", + "doc_f54217a0f2cf" + ], + "successful": [ + "doc_a2e84472f1f9", + "doc_f54217a0f2cf" + ], + "successfully": [ + "doc_109157c0d2d7", + "doc_78b1717fafe3", + "doc_a2e84472f1f9", + "doc_e7d508371e5c", + "doc_f12bbbc027a7", + "doc_f54217a0f2cf" + ], + "such": [ + "doc_07174a8633ac", + "doc_0ac01bce8c79", + "doc_3eeb75d383b8", + "doc_618e34441fa7", + "doc_bf91f8d5da74", + "doc_d147a81bac29" + ], + "suffix": [ + "doc_64090a20f830" + ], + "suite": [ + "doc_18d38fdacc9e", + "doc_2798948c3080", + "doc_2a18de009d56", + "doc_618e34441fa7", + "doc_c93c115aeb85" + ], + "suite-level": [ + "doc_c93c115aeb85" + ], + "suite_report_payload": [ + "doc_2a18de009d56", + "doc_618e34441fa7" + ], + "suites": [ + "doc_bbc97cbff254" + ], + "summaries": [ + "doc_07174a8633ac" + ], + "summarization": [ + "doc_3f60413b3a06", + "doc_57e2139f0873" + ], + "summarize": [ + "doc_f5f256549d84" + ], + "summarizes": [ + "doc_2a18de009d56" + ], + "summary": [ + "doc_618e34441fa7", + "doc_f54217a0f2cf" + ], + "support": [ + "doc_0383dbf277ba", + "doc_136d7a909d51", + "doc_2a18de009d56", + "doc_3f60413b3a06", + "doc_40e30666bfa8", + "doc_5a65ddab1445", + "doc_5ee8bbdbd8a8", + "doc_64090a20f830", + "doc_745f751f1344", + "doc_78b1717fafe3" + ], + "support_agent": [ + "doc_64090a20f830" + ], + "supported": [ + "doc_b6088784caab", + "doc_deb460ffa5ee", + "doc_f92685a34f7e" + ], + "supports": [ + "doc_0383dbf277ba", + "doc_109157c0d2d7", + "doc_2798948c3080", + "doc_3f60413b3a06", + "doc_5ee8bbdbd8a8", + "doc_64090a20f830", + "doc_a2e84472f1f9", + "doc_d6ad61a79371", + "doc_e34fe24f9585", + "doc_eeed52120ccf", + "doc_f1e32ed2ffce", + "doc_f92685a34f7e" + ], + "sure": [ + "doc_50ac650fd8f6" + ], + "surface": [ + "doc_3eeb75d383b8" + ], + "surfaced": [ + "doc_745f751f1344" + ], + "surfaces": [ + "doc_3eeb75d383b8", + "doc_d147a81bac29" + ], + "survive": [ + "doc_3ae3aaf0c12c", + "doc_4a1cf8a90e83" + ], + "suspended": [ + "doc_0ac01bce8c79" + ], + "suspends": [ + "doc_a24b92541657" + ], + "svg": [ + "doc_3aa0b3792d22" + ], + "swap": [ + "doc_4e1ebaf40267" + ], + "switch": [ + "doc_2798948c3080", + "doc_5a65ddab1445", + "doc_78a8125d2c7e" + ], + "symbol": [ + "doc_3eeb75d383b8", + "doc_d147a81bac29" + ], + "symptoms": [ + "doc_136d7a909d51" + ], + "sync": [ + "doc_bbc97cbff254", + "doc_bf91f8d5da74", + "doc_deb460ffa5ee" + ], + "synchronous": [ + "doc_bbc97cbff254" + ], + "synchronously": [ + "doc_1e46cbc6bfae", + "doc_a5120de838fb" + ], + "syntax": [ + "doc_2798948c3080", + "doc_64090a20f830" + ], + "synthesis": [ + "doc_a2e84472f1f9" + ], + "synthesizes": [ + "doc_a2e84472f1f9" + ], + "synthetic": [ + "doc_3f1383f72c5f" + ], + "system": [ + "doc_0383dbf277ba", + "doc_0ac01bce8c79", + "doc_136d7a909d51", + "doc_18d38fdacc9e", + "doc_1c5f37ea0ad3", + "doc_1e46cbc6bfae", + "doc_2798948c3080", + "doc_3ae3aaf0c12c", + "doc_40e30666bfa8", + "doc_464a2b4a8d35", + "doc_4a1cf8a90e83", + "doc_50ac650fd8f6", + "doc_567be1281e2f", + "doc_57e2139f0873", + "doc_5ee8bbdbd8a8", + "doc_618e34441fa7", + "doc_61fd9f682847", + "doc_64090a20f830", + "doc_745f751f1344", + "doc_75887f91c7df", + "doc_a24b92541657", + "doc_a5120de838fb", + "doc_b6088784caab", + "doc_c346f13ce597", + "doc_d1f7d83e0824", + "doc_e67e762fb6ab", + "doc_e8794369a1a4", + "doc_eeed52120ccf", + "doc_f5f256549d84", + "doc_fdf48d427fc0" + ], + "system-prompts": [ + "doc_3aa0b3792d22" + ], + "system_prompt_loader": [ + "doc_3aa0b3792d22" + ], + "systems": [ + "doc_1c5f37ea0ad3", + "doc_43ad71a4ad0c", + "doc_50ac650fd8f6", + "doc_57e2139f0873", + "doc_c346f13ce597", + "doc_d6ad61a79371", + "doc_e34fe24f9585", + "doc_f18f523b6ecc" + ], + "tab": [ + "doc_3aa0b3792d22" + ], + "table": [ + "doc_0ac01bce8c79", + "doc_3eeb75d383b8", + "doc_f54217a0f2cf" + ], + "tables": [ + "doc_067033cfc945" + ], + "tabs": [ + "doc_3aa0b3792d22" + ], + "tail": [ + "doc_a24b92541657" + ], + "take": [ + "doc_0383dbf277ba", + "doc_067033cfc945", + "doc_57e2139f0873", + "doc_61fd9f682847", + "doc_9e9118ed2141", + "doc_d6ad61a79371" + ], + "takes": [ + "doc_2798948c3080" + ], + "taking": [ + "doc_b6088784caab" + ], + "talk": [ + "doc_f12bbbc027a7" + ], + "target": [ + "doc_1c5f37ea0ad3" + ], + "target_agent": [ + "doc_75887f91c7df" + ], + "target_arg": [ + "doc_0ac01bce8c79" + ], + "targeted": [ + "doc_3eeb75d383b8", + "doc_d147a81bac29" + ], + "targets": [ + "doc_a24b92541657", + "doc_d147a81bac29" + ], + "task": [ + "doc_07174a8633ac", + "doc_2a18de009d56", + "doc_3ae3aaf0c12c", + "doc_50ac650fd8f6", + "doc_57e2139f0873", + "doc_618e34441fa7", + "doc_61fd9f682847", + "doc_75887f91c7df", + "doc_78b1717fafe3", + "doc_a2e84472f1f9", + "doc_bbc97cbff254", + "doc_d147a81bac29", + "doc_e67e762fb6ab", + "doc_eeed52120ccf", + "doc_f12bbbc027a7", + "doc_f5f256549d84", + "doc_fdf48d427fc0" + ], + "task-queues": [ + "doc_3aa0b3792d22" + ], + "task_id": [ + "doc_75887f91c7df" + ], + "taskitem": [ + "doc_57e2139f0873" + ], + "taskqueue": [ + "doc_57e2139f0873" + ], + "tasks": [ + "doc_067033cfc945", + "doc_18d38fdacc9e", + "doc_3f1383f72c5f", + "doc_57e2139f0873", + "doc_5a65ddab1445", + "doc_a24b92541657", + "doc_e67e762fb6ab", + "doc_f5f256549d84" + ], + "taskworker": [ + "doc_bbc97cbff254" + ], + "tcp": [ + "doc_4462f67b1c03" + ], + "teams": [ + "doc_f12bbbc027a7", + "doc_f18f523b6ecc" + ], + "telemetry": [ + "doc_067033cfc945", + "doc_07174a8633ac", + "doc_0ac01bce8c79", + "doc_18d38fdacc9e", + "doc_1e46cbc6bfae", + "doc_2a18de009d56", + "doc_3ae3aaf0c12c", + "doc_4055659fe573", + "doc_40e30666bfa8", + "doc_4e1ebaf40267", + "doc_50ac650fd8f6", + "doc_567be1281e2f", + "doc_5a65ddab1445", + "doc_618e34441fa7", + "doc_745f751f1344", + "doc_78a8125d2c7e", + "doc_78b1717fafe3", + "doc_a24b92541657", + "doc_a5120de838fb", + "doc_c93c115aeb85", + "doc_d147a81bac29", + "doc_deb460ffa5ee", + "doc_e34fe24f9585", + "doc_e67e762fb6ab", + "doc_e7d508371e5c", + "doc_f18f523b6ecc", + "doc_f2ae50c638a4", + "doc_f5f256549d84", + "doc_fdf48d427fc0" + ], + "telemetry_config": [ + "doc_a24b92541657" + ], + "telemetrysink": [ + "doc_2a18de009d56", + "doc_a24b92541657" + ], + "tell": [ + "doc_0383dbf277ba", + "doc_0ac01bce8c79", + "doc_2798948c3080", + "doc_64090a20f830", + "doc_b6088784caab", + "doc_e67e762fb6ab", + "doc_f54217a0f2cf" + ], + "telling": [ + "doc_50ac650fd8f6" + ], + "temperature": [ + "doc_136d7a909d51", + "doc_3f60413b3a06", + "doc_5ee8bbdbd8a8", + "doc_7320ab982fab", + "doc_e8794369a1a4", + "doc_eeed52120ccf" + ], + "template": [ + "doc_0383dbf277ba", + "doc_2798948c3080", + "doc_2a18de009d56", + "doc_64090a20f830", + "doc_a5120de838fb" + ], + "templates": [ + "doc_18d38fdacc9e", + "doc_2798948c3080", + "doc_64090a20f830" + ], + "templating": [ + "doc_2798948c3080", + "doc_64090a20f830" + ], + "temporaries": [ + "doc_0383dbf277ba" + ], + "terminal": [ + "doc_07174a8633ac", + "doc_0ac01bce8c79", + "doc_109157c0d2d7", + "doc_18d38fdacc9e", + "doc_1e46cbc6bfae", + "doc_4055659fe573", + "doc_4a1cf8a90e83", + "doc_4e1ebaf40267", + "doc_50ac650fd8f6", + "doc_5ee8bbdbd8a8", + "doc_618e34441fa7", + "doc_78b1717fafe3", + "doc_a24b92541657", + "doc_bbc97cbff254", + "doc_bf91f8d5da74", + "doc_deb460ffa5ee", + "doc_e7d508371e5c", + "doc_eeed52120ccf", + "doc_f18f523b6ecc", + "doc_f2ae50c638a4", + "doc_f54217a0f2cf", + "doc_f5f256549d84" + ], + "terminal_result": [ + "doc_4a1cf8a90e83" + ], + "terminated": [ + "doc_f54217a0f2cf" + ], + "terminates": [ + "doc_0ac01bce8c79", + "doc_5ee8bbdbd8a8", + "doc_618e34441fa7", + "doc_f54217a0f2cf", + "doc_fdf48d427fc0" + ], + "termination": [ + "doc_a24b92541657" + ], + "test": [ + "doc_136d7a909d51", + "doc_18d38fdacc9e", + "doc_2798948c3080", + "doc_3ae3aaf0c12c", + "doc_3eeb75d383b8", + "doc_50ac650fd8f6", + "doc_618e34441fa7", + "doc_c93c115aeb85" + ], + "testable": [ + "doc_5a65ddab1445", + "doc_f18f523b6ecc" + ], + "tested": [ + "doc_618e34441fa7", + "doc_f18f523b6ecc" + ], + "tested-behaviors": [ + "doc_3aa0b3792d22" + ], + "testing": [ + "doc_0383dbf277ba", + "doc_18d38fdacc9e", + "doc_78a8125d2c7e", + "doc_78b1717fafe3", + "doc_a24b92541657", + "doc_d6ad61a79371" + ], + "tests": [ + "doc_18d38fdacc9e", + "doc_3eeb75d383b8", + "doc_50ac650fd8f6", + "doc_618e34441fa7", + "doc_bbc97cbff254", + "doc_c93c115aeb85", + "doc_d147a81bac29", + "doc_e7d508371e5c", + "doc_f5f256549d84" + ], + "text": [ + "doc_0383dbf277ba", + "doc_109157c0d2d7", + "doc_136d7a909d51", + "doc_1e46cbc6bfae", + "doc_3f60413b3a06", + "doc_4a1cf8a90e83", + "doc_50ac650fd8f6", + "doc_57e2139f0873", + "doc_5ee8bbdbd8a8", + "doc_618e34441fa7", + "doc_64090a20f830", + "doc_745f751f1344", + "doc_7b26cbd9a7ae", + "doc_a24b92541657", + "doc_a2e84472f1f9", + "doc_bbc97cbff254", + "doc_bf91f8d5da74", + "doc_c93c115aeb85", + "doc_d6ad61a79371", + "doc_deb460ffa5ee", + "doc_e7d508371e5c", + "doc_e8794369a1a4", + "doc_eeed52120ccf", + "doc_f18f523b6ecc", + "doc_f1e32ed2ffce", + "doc_f2ae50c638a4", + "doc_f54217a0f2cf" + ], + "text-only": [ + "doc_0ac01bce8c79", + "doc_e7d508371e5c" + ], + "text_delta": [ + "doc_109157c0d2d7", + "doc_2a18de009d56", + "doc_5ee8bbdbd8a8", + "doc_eeed52120ccf", + "doc_f54217a0f2cf" + ], + "th": [ + "doc_78a8125d2c7e" + ], + "than": [ + "doc_07174a8633ac", + "doc_136d7a909d51", + "doc_4055659fe573", + "doc_4a1cf8a90e83", + "doc_618e34441fa7", + "doc_64090a20f830", + "doc_b6088784caab", + "doc_bbc97cbff254", + "doc_f18f523b6ecc" + ], + "their": [ + "doc_43ad71a4ad0c", + "doc_a24b92541657", + "doc_a2e84472f1f9", + "doc_d6ad61a79371", + "doc_eeed52120ccf", + "doc_f54217a0f2cf", + "doc_f92685a34f7e" + ], + "them": [ + "doc_136d7a909d51", + "doc_18d38fdacc9e", + "doc_3eeb75d383b8", + "doc_3f60413b3a06", + "doc_40e30666bfa8", + "doc_43ad71a4ad0c", + "doc_4a1cf8a90e83", + "doc_4e1ebaf40267", + "doc_7320ab982fab", + "doc_745f751f1344", + "doc_a2e84472f1f9", + "doc_b6088784caab", + "doc_c93c115aeb85", + "doc_f12bbbc027a7", + "doc_f2ae50c638a4", + "doc_f5f256549d84", + "doc_fdf48d427fc0" + ], + "theme": [ + "doc_3aa0b3792d22" + ], + "themselves": [ + "doc_e67e762fb6ab" + ], + "then": [ + "doc_0383dbf277ba", + "doc_18d38fdacc9e", + "doc_3ae3aaf0c12c", + "doc_4e1ebaf40267", + "doc_5ee8bbdbd8a8", + "doc_61fd9f682847", + "doc_745f751f1344", + "doc_9e9118ed2141", + "doc_a24b92541657", + "doc_f92685a34f7e" + ], + "they": [ + "doc_109157c0d2d7", + "doc_3eeb75d383b8", + "doc_40e30666bfa8", + "doc_4e1ebaf40267", + "doc_50ac650fd8f6", + "doc_5ee8bbdbd8a8", + "doc_61fd9f682847", + "doc_64090a20f830", + "doc_9e9118ed2141", + "doc_bbc97cbff254", + "doc_c93c115aeb85", + "doc_e67e762fb6ab", + "doc_f12bbbc027a7", + "doc_f54217a0f2cf" + ], + "things": [ + "doc_7b26cbd9a7ae" + ], + "think": [ + "doc_50ac650fd8f6" + ], + "thinking": [ + "doc_a24b92541657", + "doc_e67e762fb6ab" + ], + "thinking_effort": [ + "doc_a24b92541657" + ], + "third-party": [ + "doc_57e2139f0873" + ], + "thread": [ + "doc_0383dbf277ba", + "doc_07174a8633ac", + "doc_18d38fdacc9e", + "doc_1e46cbc6bfae", + "doc_2a18de009d56", + "doc_5a65ddab1445", + "doc_61fd9f682847", + "doc_75887f91c7df", + "doc_7b26cbd9a7ae", + "doc_a5120de838fb", + "doc_bf91f8d5da74", + "doc_deb460ffa5ee", + "doc_e7d508371e5c", + "doc_eeed52120ccf", + "doc_f18f523b6ecc", + "doc_f2ae50c638a4", + "doc_f54217a0f2cf", + "doc_f5f256549d84" + ], + "thread-based": [ + "doc_61fd9f682847", + "doc_a5120de838fb", + "doc_e7d508371e5c" + ], + "thread_id": [ + "doc_0383dbf277ba", + "doc_07174a8633ac", + "doc_136d7a909d51", + "doc_1e46cbc6bfae", + "doc_2798948c3080", + "doc_4a1cf8a90e83", + "doc_5a65ddab1445", + "doc_5ee8bbdbd8a8", + "doc_75887f91c7df", + "doc_7b26cbd9a7ae", + "doc_bbc97cbff254", + "doc_bf91f8d5da74", + "doc_e7d508371e5c", + "doc_f2ae50c638a4", + "doc_f54217a0f2cf" + ], + "threads": [ + "doc_0383dbf277ba", + "doc_07174a8633ac", + "doc_e7d508371e5c", + "doc_f5f256549d84" + ], + "threat": [ + "doc_464a2b4a8d35", + "doc_567be1281e2f", + "doc_64090a20f830" + ], + "three": [ + "doc_18d38fdacc9e", + "doc_1c5f37ea0ad3", + "doc_1e46cbc6bfae", + "doc_2798948c3080", + "doc_4055659fe573", + "doc_4a1cf8a90e83", + "doc_50ac650fd8f6", + "doc_64090a20f830", + "doc_745f751f1344", + "doc_c346f13ce597", + "doc_d6ad61a79371", + "doc_e7d508371e5c", + "doc_f18f523b6ecc", + "doc_f1e32ed2ffce" + ], + "threshold": [ + "doc_067033cfc945", + "doc_a24b92541657", + "doc_fdf48d427fc0" + ], + "through": [ + "doc_0ac01bce8c79", + "doc_109157c0d2d7", + "doc_1e46cbc6bfae", + "doc_3ae3aaf0c12c", + "doc_3eeb75d383b8", + "doc_40e30666bfa8", + "doc_4a1cf8a90e83", + "doc_50ac650fd8f6", + "doc_567be1281e2f", + "doc_57e2139f0873", + "doc_5a65ddab1445", + "doc_5ee8bbdbd8a8", + "doc_61fd9f682847", + "doc_64090a20f830", + "doc_7320ab982fab", + "doc_745f751f1344", + "doc_9e9118ed2141", + "doc_a2e84472f1f9", + "doc_a5120de838fb", + "doc_c346f13ce597", + "doc_c93c115aeb85", + "doc_d1f7d83e0824", + "doc_d6ad61a79371", + "doc_deb460ffa5ee", + "doc_e7d508371e5c", + "doc_e8794369a1a4", + "doc_eeed52120ccf", + "doc_f54217a0f2cf", + "doc_f5f256549d84", + "doc_fdf48d427fc0" + ], + "throughput": [ + "doc_4462f67b1c03", + "doc_4a1cf8a90e83", + "doc_7320ab982fab", + "doc_f5f256549d84" + ], + "thumb": [ + "doc_b6088784caab" + ], + "ticket": [ + "doc_3f1383f72c5f", + "doc_a24b92541657" + ], + "ticket_id": [ + "doc_9e9118ed2141", + "doc_f54217a0f2cf" + ], + "tickets": [ + "doc_9e9118ed2141", + "doc_a24b92541657" + ], + "tier": [ + "doc_a5120de838fb" + ], + "tiers": [ + "doc_d1f7d83e0824" + ], + "tight": [ + "doc_7320ab982fab", + "doc_a5120de838fb", + "doc_f12bbbc027a7", + "doc_f5f256549d84" + ], + "tightly": [ + "doc_43ad71a4ad0c", + "doc_745f751f1344" + ], + "time": [ + "doc_0383dbf277ba", + "doc_067033cfc945", + "doc_07174a8633ac", + "doc_18d38fdacc9e", + "doc_4055659fe573", + "doc_50ac650fd8f6", + "doc_567be1281e2f", + "doc_78a8125d2c7e", + "doc_9e9118ed2141", + "doc_a2e84472f1f9", + "doc_a5120de838fb", + "doc_b6088784caab", + "doc_d1f7d83e0824", + "doc_f18f523b6ecc", + "doc_fdf48d427fc0" + ], + "timed": [ + "doc_0ac01bce8c79", + "doc_4055659fe573" + ], + "timeline": [ + "doc_0ac01bce8c79" + ], + "timeout": [ + "doc_0383dbf277ba", + "doc_0ac01bce8c79", + "doc_136d7a909d51", + "doc_4055659fe573", + "doc_4462f67b1c03", + "doc_50ac650fd8f6", + "doc_5ee8bbdbd8a8", + "doc_618e34441fa7", + "doc_61fd9f682847", + "doc_7320ab982fab", + "doc_745f751f1344", + "doc_75887f91c7df", + "doc_78b1717fafe3", + "doc_9e9118ed2141", + "doc_a24b92541657", + "doc_a2e84472f1f9", + "doc_a5120de838fb", + "doc_c346f13ce597", + "doc_d147a81bac29", + "doc_d1f7d83e0824", + "doc_e67e762fb6ab", + "doc_e7d508371e5c", + "doc_eeed52120ccf", + "doc_f12bbbc027a7", + "doc_f54217a0f2cf" + ], + "timeout_s": [ + "doc_5ee8bbdbd8a8" + ], + "timeoutconfig": [ + "doc_4462f67b1c03" + ], + "timeouterror": [ + "doc_745f751f1344" + ], + "timeoutpolicy": [ + "doc_2a18de009d56", + "doc_c346f13ce597" + ], + "timeouts": [ + "doc_07174a8633ac", + "doc_4462f67b1c03", + "doc_5ee8bbdbd8a8", + "doc_c346f13ce597", + "doc_d1f7d83e0824", + "doc_deb460ffa5ee", + "doc_f5f256549d84" + ], + "times": [ + "doc_136d7a909d51", + "doc_618e34441fa7", + "doc_a24b92541657" + ], + "timestamp": [ + "doc_4a1cf8a90e83", + "doc_75887f91c7df" + ], + "timestamp_ms": [ + "doc_4a1cf8a90e83", + "doc_75887f91c7df" + ], + "timing": [ + "doc_0ac01bce8c79", + "doc_745f751f1344", + "doc_78a8125d2c7e", + "doc_c346f13ce597" + ], + "tip": [ + "doc_18d38fdacc9e", + "doc_50ac650fd8f6", + "doc_57e2139f0873" + ], + "tips": [ + "doc_136d7a909d51", + "doc_18d38fdacc9e" + ], + "to_dict": [ + "doc_618e34441fa7" + ], + "to_openai_function_tools": [ + "doc_745f751f1344" + ], + "together": [ + "doc_136d7a909d51", + "doc_4e1ebaf40267", + "doc_a5120de838fb", + "doc_deb460ffa5ee" + ], + "token": [ + "doc_0383dbf277ba", + "doc_067033cfc945", + "doc_136d7a909d51", + "doc_18d38fdacc9e", + "doc_1c5f37ea0ad3", + "doc_1e46cbc6bfae", + "doc_3f1383f72c5f", + "doc_464a2b4a8d35", + "doc_4a1cf8a90e83", + "doc_5ee8bbdbd8a8", + "doc_78a8125d2c7e", + "doc_bf91f8d5da74", + "doc_e67e762fb6ab", + "doc_e7d508371e5c", + "doc_e8794369a1a4", + "doc_eeed52120ccf", + "doc_f5f256549d84", + "doc_fdf48d427fc0" + ], + "token-based": [ + "doc_1c5f37ea0ad3" + ], + "tokens": [ + "doc_0383dbf277ba", + "doc_1c5f37ea0ad3", + "doc_4a1cf8a90e83", + "doc_567be1281e2f", + "doc_5ee8bbdbd8a8", + "doc_7320ab982fab", + "doc_78a8125d2c7e", + "doc_bbc97cbff254", + "doc_e34fe24f9585", + "doc_e8794369a1a4" + ], + "tolerance": [ + "doc_4a1cf8a90e83" + ], + "tolerated": [ + "doc_4055659fe573", + "doc_e7d508371e5c", + "doc_f12bbbc027a7" + ], + "too": [ + "doc_136d7a909d51", + "doc_18d38fdacc9e", + "doc_2798948c3080", + "doc_57e2139f0873", + "doc_a2e84472f1f9", + "doc_d1f7d83e0824" + ], + "tool": [ + "doc_0383dbf277ba", + "doc_07174a8633ac", + "doc_0ac01bce8c79", + "doc_109157c0d2d7", + "doc_136d7a909d51", + "doc_18d38fdacc9e", + "doc_1e46cbc6bfae", + "doc_2a18de009d56", + "doc_3ae3aaf0c12c", + "doc_3eeb75d383b8", + "doc_3f1383f72c5f", + "doc_3f60413b3a06", + "doc_4055659fe573", + "doc_40e30666bfa8", + "doc_43ad71a4ad0c", + "doc_464a2b4a8d35", + "doc_4a1cf8a90e83", + "doc_4e1ebaf40267", + "doc_50ac650fd8f6", + "doc_567be1281e2f", + "doc_57e2139f0873", + "doc_5a65ddab1445", + "doc_5ee8bbdbd8a8", + "doc_618e34441fa7", + "doc_61fd9f682847", + "doc_745f751f1344", + "doc_78a8125d2c7e", + "doc_9e9118ed2141", + "doc_a24b92541657", + "doc_a5120de838fb", + "doc_b6088784caab", + "doc_bbc97cbff254", + "doc_bf91f8d5da74", + "doc_c346f13ce597", + "doc_c93c115aeb85", + "doc_d147a81bac29", + "doc_d6ad61a79371", + "doc_deb460ffa5ee", + "doc_e34fe24f9585", + "doc_e67e762fb6ab", + "doc_e7d508371e5c", + "doc_e8794369a1a4", + "doc_eeed52120ccf", + "doc_f12bbbc027a7", + "doc_f18f523b6ecc", + "doc_f1e32ed2ffce", + "doc_f2ae50c638a4", + "doc_f54217a0f2cf", + "doc_f5f256549d84" + ], + "tool-call": [ + "doc_745f751f1344", + "doc_f18f523b6ecc" + ], + "tool-call-lifecycle": [ + "doc_3aa0b3792d22" + ], + "tool-level": [ + "doc_9e9118ed2141", + "doc_c346f13ce597" + ], + "tool-like": [ + "doc_a2e84472f1f9" + ], + "tool-output": [ + "doc_2a18de009d56" + ], + "tool_background_failed": [ + "doc_109157c0d2d7", + "doc_3f1383f72c5f", + "doc_9e9118ed2141", + "doc_f54217a0f2cf" + ], + "tool_background_resolved": [ + "doc_109157c0d2d7", + "doc_3f1383f72c5f", + "doc_9e9118ed2141", + "doc_f54217a0f2cf" + ], + "tool_batch": [ + "doc_0ac01bce8c79" + ], + "tool_batch_started": [ + "doc_0ac01bce8c79", + "doc_f54217a0f2cf" + ], + "tool_before_execute": [ + "doc_0ac01bce8c79", + "doc_745f751f1344" + ], + "tool_call": [ + "doc_0ac01bce8c79", + "doc_9e9118ed2141" + ], + "tool_call_count": [ + "doc_0ac01bce8c79", + "doc_4a1cf8a90e83", + "doc_f54217a0f2cf" + ], + "tool_call_id": [ + "doc_0ac01bce8c79", + "doc_109157c0d2d7", + "doc_745f751f1344", + "doc_e7d508371e5c", + "doc_f54217a0f2cf" + ], + "tool_call_ids": [ + "doc_f54217a0f2cf" + ], + "tool_calls": [ + "doc_4a1cf8a90e83", + "doc_5ee8bbdbd8a8", + "doc_78a8125d2c7e", + "doc_e8794369a1a4" + ], + "tool_calls_total": [ + "doc_4a1cf8a90e83" + ], + "tool_choice": [ + "doc_5ee8bbdbd8a8" + ], + "tool_completed": [ + "doc_0ac01bce8c79", + "doc_109157c0d2d7", + "doc_5ee8bbdbd8a8", + "doc_745f751f1344", + "doc_f54217a0f2cf" + ], + "tool_deferred": [ + "doc_109157c0d2d7", + "doc_3f1383f72c5f", + "doc_9e9118ed2141", + "doc_f54217a0f2cf" + ], + "tool_error": [ + "doc_109157c0d2d7" + ], + "tool_executions": [ + "doc_0ac01bce8c79", + "doc_1e46cbc6bfae", + "doc_4a1cf8a90e83", + "doc_745f751f1344", + "doc_bbc97cbff254", + "doc_bf91f8d5da74", + "doc_e7d508371e5c" + ], + "tool_failure_policy": [ + "doc_745f751f1344", + "doc_a24b92541657" + ], + "tool_failures": [ + "doc_4a1cf8a90e83", + "doc_78a8125d2c7e" + ], + "tool_hooks_and_middleware": [ + "doc_3aa0b3792d22" + ], + "tool_latency_p50_ms": [ + "doc_78a8125d2c7e" + ], + "tool_latency_p99_ms": [ + "doc_78a8125d2c7e" + ], + "tool_name": [ + "doc_0ac01bce8c79", + "doc_109157c0d2d7", + "doc_745f751f1344", + "doc_9e9118ed2141", + "doc_c346f13ce597", + "doc_e7d508371e5c", + "doc_f54217a0f2cf" + ], + "tool_names": [ + "doc_f54217a0f2cf" + ], + "tool_output": [ + "doc_109157c0d2d7" + ], + "tool_output_max_chars": [ + "doc_18d38fdacc9e", + "doc_567be1281e2f", + "doc_9e9118ed2141", + "doc_a24b92541657", + "doc_f5f256549d84" + ], + "tool_parallelism": [ + "doc_a24b92541657", + "doc_e67e762fb6ab" + ], + "tool_registry_security": [ + "doc_3aa0b3792d22" + ], + "tool_started": [ + "doc_109157c0d2d7", + "doc_2a18de009d56", + "doc_5ee8bbdbd8a8" + ], + "tool_success": [ + "doc_109157c0d2d7" + ], + "tool_ticket_id": [ + "doc_109157c0d2d7" + ], + "toolcall": [ + "doc_0ac01bce8c79", + "doc_2a18de009d56", + "doc_50ac650fd8f6", + "doc_5ee8bbdbd8a8", + "doc_745f751f1344", + "doc_e8794369a1a4" + ], + "toolcontext": [ + "doc_2a18de009d56", + "doc_567be1281e2f", + "doc_9e9118ed2141", + "doc_bbc97cbff254" + ], + "toolexecutionerror": [ + "doc_745f751f1344" + ], + "toolexecutionrecord": [ + "doc_0ac01bce8c79", + "doc_4a1cf8a90e83", + "doc_745f751f1344", + "doc_e7d508371e5c" + ], + "toolregistry": [ + "doc_2a18de009d56", + "doc_745f751f1344", + "doc_9e9118ed2141", + "doc_c346f13ce597" + ], + "toolresult": [ + "doc_2a18de009d56", + "doc_464a2b4a8d35", + "doc_50ac650fd8f6", + "doc_745f751f1344", + "doc_9e9118ed2141", + "doc_a24b92541657" + ], + "tools": [ + "doc_0ac01bce8c79", + "doc_109157c0d2d7", + "doc_136d7a909d51", + "doc_18d38fdacc9e", + "doc_1c5f37ea0ad3", + "doc_1e46cbc6bfae", + "doc_2a18de009d56", + "doc_3aa0b3792d22", + "doc_3ae3aaf0c12c", + "doc_3eeb75d383b8", + "doc_3f60413b3a06", + "doc_40e30666bfa8", + "doc_43ad71a4ad0c", + "doc_464a2b4a8d35", + "doc_4a1cf8a90e83", + "doc_4e1ebaf40267", + "doc_567be1281e2f", + "doc_57e2139f0873", + "doc_5a65ddab1445", + "doc_5ee8bbdbd8a8", + "doc_618e34441fa7", + "doc_61fd9f682847", + "doc_745f751f1344", + "doc_78b1717fafe3", + "doc_9e9118ed2141", + "doc_a24b92541657", + "doc_a5120de838fb", + "doc_b6088784caab", + "doc_bbc97cbff254", + "doc_bf91f8d5da74", + "doc_c346f13ce597", + "doc_c93c115aeb85", + "doc_d147a81bac29", + "doc_deb460ffa5ee", + "doc_e34fe24f9585", + "doc_e67e762fb6ab", + "doc_e7d508371e5c", + "doc_e8794369a1a4", + "doc_eeed52120ccf", + "doc_f18f523b6ecc", + "doc_f2ae50c638a4", + "doc_f54217a0f2cf", + "doc_f5f256549d84" + ], + "tools-system-walkthrough": [ + "doc_3aa0b3792d22" + ], + "toolschema": [ + "doc_e8794369a1a4" + ], + "toolspec": [ + "doc_2a18de009d56", + "doc_745f751f1344" + ], + "tooltimeouterror": [ + "doc_745f751f1344" + ], + "toolvalidationerror": [ + "doc_136d7a909d51", + "doc_745f751f1344", + "doc_c346f13ce597" + ], + "top": [ + "doc_50ac650fd8f6", + "doc_a5120de838fb", + "doc_deb460ffa5ee" + ], + "top-level": [ + "doc_2a18de009d56", + "doc_64090a20f830" + ], + "top_p": [ + "doc_5ee8bbdbd8a8", + "doc_e8794369a1a4" + ], + "topbar": [ + "doc_3aa0b3792d22" + ], + "topbarctabutton": [ + "doc_3aa0b3792d22" + ], + "topbarlinks": [ + "doc_3aa0b3792d22" + ], + "topic": [ + "doc_b6088784caab" + ], + "topologically": [ + "doc_618e34441fa7" + ], + "total": [ + "doc_4462f67b1c03", + "doc_78a8125d2c7e", + "doc_bbc97cbff254", + "doc_bf91f8d5da74", + "doc_c93c115aeb85", + "doc_e8794369a1a4" + ], + "total_cost_usd": [ + "doc_4a1cf8a90e83", + "doc_78a8125d2c7e", + "doc_bbc97cbff254", + "doc_bf91f8d5da74" + ], + "total_llm_calls": [ + "doc_78a8125d2c7e" + ], + "total_runs": [ + "doc_78a8125d2c7e" + ], + "total_steps": [ + "doc_78a8125d2c7e" + ], + "total_tokens": [ + "doc_4a1cf8a90e83", + "doc_5ee8bbdbd8a8", + "doc_78a8125d2c7e" + ], + "total_tool_calls": [ + "doc_78a8125d2c7e" + ], + "touched": [ + "doc_618e34441fa7" + ], + "touches": [ + "doc_e8794369a1a4", + "doc_f92685a34f7e" + ], + "touching": [ + "doc_4e1ebaf40267", + "doc_d147a81bac29" + ], + "trace": [ + "doc_2a18de009d56", + "doc_3f1383f72c5f" + ], + "traceback": [ + "doc_136d7a909d51" + ], + "traces": [ + "doc_18d38fdacc9e", + "doc_2a18de009d56", + "doc_3ae3aaf0c12c", + "doc_78a8125d2c7e", + "doc_bbc97cbff254", + "doc_e67e762fb6ab" + ], + "tracing": [ + "doc_5ee8bbdbd8a8", + "doc_75887f91c7df", + "doc_c346f13ce597", + "doc_f92685a34f7e" + ], + "track": [ + "doc_067033cfc945", + "doc_2798948c3080", + "doc_a5120de838fb", + "doc_d1f7d83e0824", + "doc_f5f256549d84", + "doc_fdf48d427fc0" + ], + "tracking": [ + "doc_0ac01bce8c79", + "doc_5ee8bbdbd8a8", + "doc_a5120de838fb" + ], + "tracks": [ + "doc_4a1cf8a90e83" + ], + "traffic": [ + "doc_067033cfc945", + "doc_a24b92541657" + ], + "trail": [ + "doc_50ac650fd8f6", + "doc_f54217a0f2cf" + ], + "transactional": [ + "doc_a24b92541657" + ], + "transfer": [ + "doc_e67e762fb6ab", + "doc_eeed52120ccf" + ], + "transfer_to_researcher": [ + "doc_e67e762fb6ab" + ], + "transfer_to_writer": [ + "doc_e67e762fb6ab" + ], + "transform": [ + "doc_2a18de009d56", + "doc_745f751f1344", + "doc_a24b92541657", + "doc_c346f13ce597" + ], + "transformation": [ + "doc_2a18de009d56", + "doc_a5120de838fb" + ], + "transformed": [ + "doc_9e9118ed2141", + "doc_c346f13ce597" + ], + "transforming": [ + "doc_f92685a34f7e" + ], + "transforms": [ + "doc_745f751f1344" + ], + "transient": [ + "doc_4055659fe573", + "doc_61fd9f682847", + "doc_7320ab982fab", + "doc_75887f91c7df", + "doc_78b1717fafe3", + "doc_a24b92541657" + ], + "transitions": [ + "doc_07174a8633ac", + "doc_0ac01bce8c79", + "doc_5ee8bbdbd8a8", + "doc_78a8125d2c7e" + ], + "translate": [ + "doc_f1e32ed2ffce" + ], + "translates": [ + "doc_e8794369a1a4" + ], + "translation": [ + "doc_57e2139f0873" + ], + "transparent": [ + "doc_40e30666bfa8", + "doc_e34fe24f9585" + ], + "transport": [ + "doc_1c5f37ea0ad3", + "doc_c346f13ce597", + "doc_f1e32ed2ffce" + ], + "traversal": [ + "doc_464a2b4a8d35", + "doc_64090a20f830", + "doc_b6088784caab" + ], + "treat": [ + "doc_618e34441fa7", + "doc_eeed52120ccf" + ], + "treated": [ + "doc_18d38fdacc9e" + ], + "treating": [ + "doc_3ae3aaf0c12c" + ], + "tree": [ + "doc_50ac650fd8f6" + ], + "trend": [ + "doc_618e34441fa7" + ], + "trends": [ + "doc_fdf48d427fc0" + ], + "tries": [ + "doc_5ee8bbdbd8a8", + "doc_a24b92541657", + "doc_d1f7d83e0824" + ], + "trigger": [ + "doc_618e34441fa7" + ], + "triggered": [ + "doc_4055659fe573" + ], + "triggers": [ + "doc_0ac01bce8c79", + "doc_618e34441fa7", + "doc_d1f7d83e0824" + ], + "trim": [ + "doc_0383dbf277ba" + ], + "trips": [ + "doc_78a8125d2c7e" + ], + "troubleshooting": [ + "doc_3aa0b3792d22", + "doc_3ae3aaf0c12c", + "doc_deb460ffa5ee" + ], + "true": [ + "doc_3f1383f72c5f", + "doc_4a1cf8a90e83", + "doc_567be1281e2f", + "doc_61fd9f682847", + "doc_7320ab982fab", + "doc_745f751f1344", + "doc_9e9118ed2141", + "doc_a24b92541657", + "doc_e67e762fb6ab" + ], + "truncate": [ + "doc_50ac650fd8f6", + "doc_a24b92541657", + "doc_f5f256549d84" + ], + "truncated": [ + "doc_464a2b4a8d35", + "doc_618e34441fa7", + "doc_9e9118ed2141" + ], + "truncation": [ + "doc_464a2b4a8d35", + "doc_61fd9f682847" + ], + "trusted": [ + "doc_2a18de009d56", + "doc_5ee8bbdbd8a8" + ], + "truth": [ + "doc_745f751f1344" + ], + "try": [ + "doc_0ac01bce8c79", + "doc_7320ab982fab", + "doc_a24b92541657", + "doc_d1f7d83e0824", + "doc_eeed52120ccf" + ], + "ttl": [ + "doc_7320ab982fab", + "doc_a24b92541657" + ], + "tuning": [ + "doc_7320ab982fab" + ], + "tuple": [ + "doc_a24b92541657" + ], + "turn": [ + "doc_0ac01bce8c79", + "doc_50ac650fd8f6", + "doc_745f751f1344", + "doc_e7d508371e5c" + ], + "turns": [ + "doc_07174a8633ac", + "doc_7b26cbd9a7ae", + "doc_bf91f8d5da74", + "doc_f2ae50c638a4" + ], + "tutorial": [ + "doc_f2ae50c638a4" + ], + "two": [ + "doc_07174a8633ac", + "doc_0ac01bce8c79", + "doc_464a2b4a8d35", + "doc_7b26cbd9a7ae", + "doc_b6088784caab", + "doc_c346f13ce597", + "doc_d6ad61a79371", + "doc_e8794369a1a4", + "doc_eeed52120ccf", + "doc_fdf48d427fc0" + ], + "txt": [ + "doc_2798948c3080", + "doc_e67e762fb6ab" + ], + "type": [ + "doc_0383dbf277ba", + "doc_07174a8633ac", + "doc_0ac01bce8c79", + "doc_109157c0d2d7", + "doc_1e46cbc6bfae", + "doc_2a18de009d56", + "doc_3aa0b3792d22", + "doc_43ad71a4ad0c", + "doc_464a2b4a8d35", + "doc_4a1cf8a90e83", + "doc_5ee8bbdbd8a8", + "doc_618e34441fa7", + "doc_61fd9f682847", + "doc_64090a20f830", + "doc_745f751f1344", + "doc_75887f91c7df", + "doc_78a8125d2c7e", + "doc_78b1717fafe3", + "doc_a24b92541657", + "doc_a2e84472f1f9", + "doc_e67e762fb6ab", + "doc_e7d508371e5c", + "doc_e8794369a1a4", + "doc_f54217a0f2cf" + ], + "typed": [ + "doc_1c5f37ea0ad3", + "doc_3ae3aaf0c12c", + "doc_43ad71a4ad0c", + "doc_4e1ebaf40267", + "doc_50ac650fd8f6", + "doc_57e2139f0873", + "doc_5a65ddab1445", + "doc_618e34441fa7", + "doc_75887f91c7df", + "doc_a24b92541657", + "doc_b6088784caab", + "doc_bf91f8d5da74", + "doc_c346f13ce597", + "doc_d147a81bac29", + "doc_deb460ffa5ee", + "doc_e67e762fb6ab", + "doc_e8794369a1a4", + "doc_f18f523b6ecc", + "doc_f2ae50c638a4", + "doc_f5f256549d84" + ], + "types": [ + "doc_0383dbf277ba", + "doc_109157c0d2d7", + "doc_18d38fdacc9e", + "doc_2a18de009d56", + "doc_618e34441fa7", + "doc_c93c115aeb85", + "doc_e8794369a1a4", + "doc_f2ae50c638a4", + "doc_f54217a0f2cf", + "doc_f92685a34f7e" + ], + "typically": [ + "doc_d6ad61a79371" + ], + "ui": [ + "doc_109157c0d2d7", + "doc_3f60413b3a06", + "doc_f18f523b6ecc", + "doc_f2ae50c638a4" + ], + "uis": [ + "doc_2a18de009d56", + "doc_5ee8bbdbd8a8", + "doc_618e34441fa7", + "doc_a24b92541657", + "doc_a5120de838fb", + "doc_bbc97cbff254", + "doc_d6ad61a79371", + "doc_e7d508371e5c", + "doc_f54217a0f2cf" + ], + "unacceptable": [ + "doc_a24b92541657" + ], + "unauthenticated": [ + "doc_1c5f37ea0ad3", + "doc_567be1281e2f", + "doc_e34fe24f9585" + ], + "unauthorized": [ + "doc_464a2b4a8d35", + "doc_567be1281e2f", + "doc_a24b92541657" + ], + "unavailable": [ + "doc_4055659fe573", + "doc_618e34441fa7" + ], + "unbounded": [ + "doc_464a2b4a8d35", + "doc_618e34441fa7" + ], + "unclear": [ + "doc_136d7a909d51" + ], + "unclosed": [ + "doc_2798948c3080" + ], + "undecorated": [ + "doc_3aa0b3792d22" + ], + "under": [ + "doc_0ac01bce8c79", + "doc_1e46cbc6bfae", + "doc_2798948c3080", + "doc_464a2b4a8d35", + "doc_618e34441fa7", + "doc_9e9118ed2141", + "doc_a24b92541657", + "doc_a5120de838fb", + "doc_bbc97cbff254", + "doc_c93c115aeb85" + ], + "underlying": [ + "doc_61fd9f682847" + ], + "underscores": [ + "doc_64090a20f830" + ], + "understand": [ + "doc_136d7a909d51", + "doc_3ae3aaf0c12c", + "doc_43ad71a4ad0c", + "doc_5a65ddab1445", + "doc_9e9118ed2141", + "doc_a2e84472f1f9", + "doc_bf91f8d5da74", + "doc_e67e762fb6ab", + "doc_f18f523b6ecc" + ], + "understanding": [ + "doc_0ac01bce8c79", + "doc_4055659fe573", + "doc_50ac650fd8f6", + "doc_5ee8bbdbd8a8", + "doc_f54217a0f2cf" + ], + "unexpected": [ + "doc_136d7a909d51", + "doc_fdf48d427fc0" + ], + "unhandled": [ + "doc_618e34441fa7", + "doc_745f751f1344" + ], + "unified": [ + "doc_a2e84472f1f9", + "doc_a5120de838fb" + ], + "uniformly": [ + "doc_c346f13ce597" + ], + "unintended": [ + "doc_43ad71a4ad0c" + ], + "unique": [ + "doc_07174a8633ac", + "doc_1e46cbc6bfae", + "doc_4a1cf8a90e83", + "doc_5ee8bbdbd8a8", + "doc_bf91f8d5da74", + "doc_e7d508371e5c", + "doc_f54217a0f2cf" + ], + "unit": [ + "doc_3eeb75d383b8" + ], + "unix": [ + "doc_4a1cf8a90e83" + ], + "unknown": [ + "doc_f54217a0f2cf" + ], + "unless": [ + "doc_567be1281e2f", + "doc_745f751f1344" + ], + "unnecessarily": [ + "doc_136d7a909d51" + ], + "unnecessary": [ + "doc_f5f256549d84" + ], + "unrecoverable": [ + "doc_e7d508371e5c", + "doc_f54217a0f2cf" + ], + "unrelated": [ + "doc_618e34441fa7" + ], + "unresolved": [ + "doc_a24b92541657" + ], + "unset": [ + "doc_61fd9f682847", + "doc_bbc97cbff254" + ], + "unstructured": [ + "doc_b6088784caab" + ], + "unsupported": [ + "doc_f1e32ed2ffce" + ], + "until": [ + "doc_0383dbf277ba", + "doc_1e46cbc6bfae", + "doc_4e1ebaf40267", + "doc_5ee8bbdbd8a8", + "doc_a24b92541657", + "doc_d6ad61a79371", + "doc_e7d508371e5c" + ], + "untrusted": [ + "doc_2a18de009d56", + "doc_5ee8bbdbd8a8" + ], + "untrusted-data": [ + "doc_5ee8bbdbd8a8", + "doc_745f751f1344", + "doc_a24b92541657" + ], + "untrusted_tool_preamble": [ + "doc_a24b92541657" + ], + "untyped": [ + "doc_18d38fdacc9e", + "doc_3ae3aaf0c12c", + "doc_50ac650fd8f6", + "doc_e67e762fb6ab" + ], + "up": [ + "doc_067033cfc945", + "doc_07174a8633ac", + "doc_18d38fdacc9e", + "doc_50ac650fd8f6", + "doc_567be1281e2f", + "doc_57e2139f0873", + "doc_5ee8bbdbd8a8", + "doc_64090a20f830", + "doc_78b1717fafe3", + "doc_f18f523b6ecc" + ], + "update": [ + "doc_3eeb75d383b8", + "doc_3f1383f72c5f", + "doc_618e34441fa7", + "doc_bbc97cbff254", + "doc_d147a81bac29", + "doc_f54217a0f2cf" + ], + "update_": [ + "doc_43ad71a4ad0c" + ], + "updated": [ + "doc_5ee8bbdbd8a8" + ], + "updates": [ + "doc_109157c0d2d7" + ], + "updating": [ + "doc_deb460ffa5ee" + ], + "upper_snake_case": [ + "doc_64090a20f830" + ], + "uppercases": [ + "doc_64090a20f830" + ], + "upsert": [ + "doc_0383dbf277ba" + ], + "upsert_long_term_memory": [ + "doc_0383dbf277ba" + ], + "url": [ + "doc_3aa0b3792d22", + "doc_61fd9f682847" + ], + "urls": [ + "doc_f1e32ed2ffce" + ], + "usage": [ + "doc_1e46cbc6bfae", + "doc_2a18de009d56", + "doc_3ae3aaf0c12c", + "doc_43ad71a4ad0c", + "doc_4a1cf8a90e83", + "doc_50ac650fd8f6", + "doc_5ee8bbdbd8a8", + "doc_64090a20f830", + "doc_a5120de838fb", + "doc_bf91f8d5da74", + "doc_c93c115aeb85", + "doc_d1f7d83e0824", + "doc_deb460ffa5ee", + "doc_e67e762fb6ab", + "doc_e8794369a1a4", + "doc_f18f523b6ecc", + "doc_f1e32ed2ffce", + "doc_f5f256549d84" + ], + "usage_aggregate": [ + "doc_bbc97cbff254", + "doc_bf91f8d5da74", + "doc_e7d508371e5c" + ], + "usageaggregate": [ + "doc_1e46cbc6bfae", + "doc_2a18de009d56", + "doc_5ee8bbdbd8a8", + "doc_e7d508371e5c" + ], + "usd": [ + "doc_4a1cf8a90e83", + "doc_78a8125d2c7e" + ], + "use": [ + "doc_0383dbf277ba", + "doc_067033cfc945", + "doc_109157c0d2d7", + "doc_136d7a909d51", + "doc_18d38fdacc9e", + "doc_1c5f37ea0ad3", + "doc_1e46cbc6bfae", + "doc_2798948c3080", + "doc_2a18de009d56", + "doc_3ae3aaf0c12c", + "doc_3eeb75d383b8", + "doc_3f1383f72c5f", + "doc_3f60413b3a06", + "doc_4055659fe573", + "doc_43ad71a4ad0c", + "doc_4462f67b1c03", + "doc_464a2b4a8d35", + "doc_4e1ebaf40267", + "doc_50ac650fd8f6", + "doc_567be1281e2f", + "doc_57e2139f0873", + "doc_5a65ddab1445", + "doc_618e34441fa7", + "doc_61fd9f682847", + "doc_64090a20f830", + "doc_7320ab982fab", + "doc_75887f91c7df", + "doc_78a8125d2c7e", + "doc_78b1717fafe3", + "doc_9e9118ed2141", + "doc_a24b92541657", + "doc_a5120de838fb", + "doc_b6088784caab", + "doc_bbc97cbff254", + "doc_c346f13ce597", + "doc_c93c115aeb85", + "doc_d147a81bac29", + "doc_d1f7d83e0824", + "doc_d6ad61a79371", + "doc_deb460ffa5ee", + "doc_e34fe24f9585", + "doc_e67e762fb6ab", + "doc_e7d508371e5c", + "doc_eeed52120ccf", + "doc_f12bbbc027a7", + "doc_f18f523b6ecc", + "doc_f1e32ed2ffce", + "doc_f2ae50c638a4", + "doc_f5f256549d84", + "doc_f92685a34f7e", + "doc_fdf48d427fc0" + ], + "used": [ + "doc_0383dbf277ba", + "doc_07174a8633ac", + "doc_0ac01bce8c79", + "doc_136d7a909d51", + "doc_1e46cbc6bfae", + "doc_4a1cf8a90e83", + "doc_57e2139f0873", + "doc_64090a20f830", + "doc_bbc97cbff254", + "doc_bf91f8d5da74", + "doc_d1f7d83e0824", + "doc_f2ae50c638a4" + ], + "useful": [ + "doc_3ae3aaf0c12c", + "doc_4a1cf8a90e83", + "doc_9e9118ed2141", + "doc_a24b92541657", + "doc_a2e84472f1f9", + "doc_bf91f8d5da74", + "doc_d147a81bac29", + "doc_f18f523b6ecc" + ], + "user": [ + "doc_0383dbf277ba", + "doc_0ac01bce8c79", + "doc_1e46cbc6bfae", + "doc_3eeb75d383b8", + "doc_4a1cf8a90e83", + "doc_5ee8bbdbd8a8", + "doc_61fd9f682847", + "doc_78b1717fafe3", + "doc_7b26cbd9a7ae", + "doc_9e9118ed2141", + "doc_a24b92541657", + "doc_a2e84472f1f9", + "doc_b6088784caab", + "doc_e8794369a1a4", + "doc_eeed52120ccf", + "doc_f12bbbc027a7" + ], + "user-facing": [ + "doc_3eeb75d383b8", + "doc_bbc97cbff254", + "doc_d147a81bac29", + "doc_f18f523b6ecc", + "doc_f5f256549d84" + ], + "user-input": [ + "doc_a24b92541657" + ], + "user-visible": [ + "doc_618e34441fa7" + ], + "user_message": [ + "doc_1e46cbc6bfae", + "doc_4e1ebaf40267", + "doc_bbc97cbff254", + "doc_e8794369a1a4", + "doc_eeed52120ccf" + ], + "user_role": [ + "doc_2798948c3080" + ], + "users": [ + "doc_3ae3aaf0c12c", + "doc_57e2139f0873", + "doc_7b26cbd9a7ae" + ], + "uses": [ + "doc_1e46cbc6bfae", + "doc_2798948c3080", + "doc_3f1383f72c5f", + "doc_3f60413b3a06", + "doc_464a2b4a8d35", + "doc_4e1ebaf40267", + "doc_61fd9f682847", + "doc_64090a20f830", + "doc_78b1717fafe3", + "doc_9e9118ed2141", + "doc_a24b92541657", + "doc_b6088784caab", + "doc_c93c115aeb85", + "doc_eeed52120ccf", + "doc_f92685a34f7e" + ], + "using": [ + "doc_136d7a909d51", + "doc_1c5f37ea0ad3", + "doc_40e30666bfa8", + "doc_4462f67b1c03", + "doc_464a2b4a8d35", + "doc_57e2139f0873", + "doc_5ee8bbdbd8a8", + "doc_64090a20f830", + "doc_a2e84472f1f9", + "doc_a5120de838fb", + "doc_b6088784caab", + "doc_d6ad61a79371", + "doc_eeed52120ccf", + "doc_f12bbbc027a7" + ], + "usually": [ + "doc_a24b92541657", + "doc_f5f256549d84" + ], + "utilities": [ + "doc_2a18de009d56" + ], + "utility": [ + "doc_2a18de009d56" + ], + "v2": [ + "doc_64090a20f830" + ], + "vague": [ + "doc_18d38fdacc9e", + "doc_e67e762fb6ab" + ], + "valid": [ + "doc_3eeb75d383b8", + "doc_3f60413b3a06", + "doc_4a1cf8a90e83", + "doc_567be1281e2f", + "doc_f12bbbc027a7" + ], + "validate": [ + "doc_50ac650fd8f6", + "doc_618e34441fa7", + "doc_745f751f1344", + "doc_f5f256549d84" + ], + "validated": [ + "doc_1c5f37ea0ad3", + "doc_3f60413b3a06", + "doc_464a2b4a8d35", + "doc_50ac650fd8f6", + "doc_745f751f1344", + "doc_9e9118ed2141", + "doc_c346f13ce597", + "doc_e7d508371e5c", + "doc_f2ae50c638a4" + ], + "validates": [ + "doc_1c5f37ea0ad3", + "doc_745f751f1344", + "doc_bf91f8d5da74", + "doc_f12bbbc027a7" + ], + "validation": [ + "doc_136d7a909d51", + "doc_18d38fdacc9e", + "doc_1c5f37ea0ad3", + "doc_2a18de009d56", + "doc_4055659fe573", + "doc_40e30666bfa8", + "doc_43ad71a4ad0c", + "doc_4a1cf8a90e83", + "doc_4e1ebaf40267", + "doc_50ac650fd8f6", + "doc_567be1281e2f", + "doc_5a65ddab1445", + "doc_618e34441fa7", + "doc_745f751f1344", + "doc_9e9118ed2141", + "doc_a24b92541657", + "doc_a5120de838fb", + "doc_c346f13ce597", + "doc_deb460ffa5ee", + "doc_e34fe24f9585", + "doc_e67e762fb6ab" + ], + "value": [ + "doc_0ac01bce8c79", + "doc_745f751f1344", + "doc_a24b92541657" + ], + "values": [ + "doc_109157c0d2d7", + "doc_2798948c3080", + "doc_4a1cf8a90e83", + "doc_618e34441fa7", + "doc_a24b92541657" + ], + "var": [ + "doc_2798948c3080" + ], + "variable": [ + "doc_2798948c3080", + "doc_61fd9f682847", + "doc_64090a20f830", + "doc_a24b92541657", + "doc_a5120de838fb" + ], + "variables": [ + "doc_0383dbf277ba", + "doc_067033cfc945", + "doc_18d38fdacc9e", + "doc_1e46cbc6bfae", + "doc_2798948c3080", + "doc_567be1281e2f", + "doc_61fd9f682847", + "doc_64090a20f830", + "doc_78b1717fafe3", + "doc_a5120de838fb", + "doc_f5f256549d84" + ], + "variant": [ + "doc_2a18de009d56" + ], + "variation": [ + "doc_eeed52120ccf" + ], + "varies": [ + "doc_f54217a0f2cf" + ], + "vars": [ + "doc_d147a81bac29" + ], + "vary": [ + "doc_4a1cf8a90e83", + "doc_bbc97cbff254" + ], + "vault": [ + "doc_067033cfc945" + ], + "vector": [ + "doc_0383dbf277ba", + "doc_2a18de009d56", + "doc_5a65ddab1445", + "doc_61fd9f682847", + "doc_d147a81bac29", + "doc_f5f256549d84" + ], + "vector-based": [ + "doc_0383dbf277ba" + ], + "vectors": [ + "doc_50ac650fd8f6", + "doc_9e9118ed2141" + ], + "verb": [ + "doc_43ad71a4ad0c" + ], + "verbose": [ + "doc_136d7a909d51", + "doc_618e34441fa7" + ], + "verbosity": [ + "doc_3f1383f72c5f", + "doc_a24b92541657" + ], + "vercel": [ + "doc_b6088784caab", + "doc_d147a81bac29", + "doc_deb460ffa5ee" + ], + "verification": [ + "doc_2a18de009d56", + "doc_f12bbbc027a7" + ], + "verify": [ + "doc_464a2b4a8d35", + "doc_618e34441fa7", + "doc_c93c115aeb85", + "doc_f92685a34f7e" + ], + "versa": [ + "doc_18d38fdacc9e" + ], + "version": [ + "doc_067033cfc945", + "doc_136d7a909d51", + "doc_18d38fdacc9e", + "doc_2798948c3080", + "doc_3ae3aaf0c12c", + "doc_4a1cf8a90e83", + "doc_61fd9f682847", + "doc_b6088784caab" + ], + "version-controlled": [ + "doc_2798948c3080", + "doc_64090a20f830" + ], + "versionable": [ + "doc_4e1ebaf40267" + ], + "versioning": [ + "doc_2a18de009d56" + ], + "versions": [ + "doc_2798948c3080", + "doc_618e34441fa7" + ], + "via": [ + "doc_0383dbf277ba", + "doc_109157c0d2d7", + "doc_1c5f37ea0ad3", + "doc_1e46cbc6bfae", + "doc_2a18de009d56", + "doc_40e30666bfa8", + "doc_5ee8bbdbd8a8", + "doc_64090a20f830", + "doc_7320ab982fab", + "doc_745f751f1344", + "doc_75887f91c7df", + "doc_78b1717fafe3", + "doc_9e9118ed2141", + "doc_a24b92541657", + "doc_a5120de838fb", + "doc_d6ad61a79371", + "doc_e34fe24f9585", + "doc_f1e32ed2ffce", + "doc_f92685a34f7e", + "doc_fdf48d427fc0" + ], + "vice": [ + "doc_18d38fdacc9e" + ], + "violated": [ + "doc_50ac650fd8f6" + ], + "violation": [ + "doc_4055659fe573" + ], + "violations": [ + "doc_0ac01bce8c79", + "doc_618e34441fa7" + ], + "visibility": [ + "doc_78a8125d2c7e" + ], + "visible": [ + "doc_78a8125d2c7e" + ], + "vision": [ + "doc_f1e32ed2ffce" + ], + "vs": [ + "doc_2a18de009d56", + "doc_3f60413b3a06", + "doc_43ad71a4ad0c", + "doc_b6088784caab", + "doc_d6ad61a79371", + "doc_e34fe24f9585", + "doc_e67e762fb6ab" + ], + "wait": [ + "doc_0383dbf277ba", + "doc_a24b92541657" + ], + "waiting": [ + "doc_07174a8633ac", + "doc_0ac01bce8c79", + "doc_109157c0d2d7", + "doc_57e2139f0873", + "doc_78b1717fafe3", + "doc_f12bbbc027a7" + ], + "waits": [ + "doc_0ac01bce8c79", + "doc_a24b92541657" + ], + "wal": [ + "doc_0383dbf277ba" + ], + "walk": [ + "doc_deb460ffa5ee" + ], + "walks": [ + "doc_745f751f1344" + ], + "walkthrough": [ + "doc_0ac01bce8c79" + ], + "wall": [ + "doc_4055659fe573", + "doc_50ac650fd8f6", + "doc_567be1281e2f" + ], + "wall-clock": [ + "doc_0ac01bce8c79", + "doc_a24b92541657", + "doc_a2e84472f1f9" + ], + "wall_time_s": [ + "doc_78a8125d2c7e" + ], + "wallet": [ + "doc_a24b92541657" + ], + "want": [ + "doc_3f60413b3a06", + "doc_61fd9f682847", + "doc_d6ad61a79371", + "doc_deb460ffa5ee" + ], + "warning": [ + "doc_0ac01bce8c79", + "doc_4055659fe573", + "doc_43ad71a4ad0c", + "doc_78b1717fafe3", + "doc_a24b92541657", + "doc_f54217a0f2cf" + ], + "was": [ + "doc_07174a8633ac", + "doc_0ac01bce8c79", + "doc_109157c0d2d7", + "doc_464a2b4a8d35", + "doc_4a1cf8a90e83", + "doc_50ac650fd8f6", + "doc_a2e84472f1f9", + "doc_d1f7d83e0824", + "doc_f54217a0f2cf" + ], + "waste": [ + "doc_18d38fdacc9e" + ], + "watch": [ + "doc_9e9118ed2141" + ], + "way": [ + "doc_0ac01bce8c79", + "doc_1e46cbc6bfae", + "doc_43ad71a4ad0c", + "doc_61fd9f682847", + "doc_a5120de838fb", + "doc_f54217a0f2cf" + ], + "ways": [ + "doc_2798948c3080", + "doc_eeed52120ccf" + ], + "web": [ + "doc_2a18de009d56", + "doc_a24b92541657" + ], + "well": [ + "doc_136d7a909d51", + "doc_f2ae50c638a4" + ], + "well-defined": [ + "doc_5ee8bbdbd8a8", + "doc_e67e762fb6ab" + ], + "went": [ + "doc_4055659fe573" + ], + "were": [ + "doc_4a1cf8a90e83", + "doc_618e34441fa7", + "doc_f54217a0f2cf" + ], + "what": [ + "doc_0383dbf277ba", + "doc_067033cfc945", + "doc_07174a8633ac", + "doc_0ac01bce8c79", + "doc_1c5f37ea0ad3", + "doc_1e46cbc6bfae", + "doc_2798948c3080", + "doc_40e30666bfa8", + "doc_4462f67b1c03", + "doc_464a2b4a8d35", + "doc_4a1cf8a90e83", + "doc_4e1ebaf40267", + "doc_50ac650fd8f6", + "doc_5ee8bbdbd8a8", + "doc_618e34441fa7", + "doc_64090a20f830", + "doc_745f751f1344", + "doc_78b1717fafe3", + "doc_7b26cbd9a7ae", + "doc_9e9118ed2141", + "doc_a24b92541657", + "doc_a2e84472f1f9", + "doc_a5120de838fb", + "doc_b6088784caab", + "doc_c346f13ce597", + "doc_d1f7d83e0824", + "doc_deb460ffa5ee", + "doc_e67e762fb6ab", + "doc_e8794369a1a4", + "doc_f12bbbc027a7", + "doc_f18f523b6ecc", + "doc_f2ae50c638a4", + "doc_f54217a0f2cf", + "doc_f92685a34f7e", + "doc_fdf48d427fc0" + ], + "what_": [ + "doc_e67e762fb6ab" + ], + "whatever": [ + "doc_f12bbbc027a7" + ], + "when": [ + "doc_0383dbf277ba", + "doc_07174a8633ac", + "doc_0ac01bce8c79", + "doc_109157c0d2d7", + "doc_136d7a909d51", + "doc_1c5f37ea0ad3", + "doc_1e46cbc6bfae", + "doc_2798948c3080", + "doc_2a18de009d56", + "doc_3ae3aaf0c12c", + "doc_3eeb75d383b8", + "doc_3f1383f72c5f", + "doc_3f60413b3a06", + "doc_4055659fe573", + "doc_43ad71a4ad0c", + "doc_4a1cf8a90e83", + "doc_4e1ebaf40267", + "doc_50ac650fd8f6", + "doc_57e2139f0873", + "doc_5ee8bbdbd8a8", + "doc_618e34441fa7", + "doc_61fd9f682847", + "doc_64090a20f830", + "doc_7320ab982fab", + "doc_745f751f1344", + "doc_75887f91c7df", + "doc_78a8125d2c7e", + "doc_78b1717fafe3", + "doc_9e9118ed2141", + "doc_a24b92541657", + "doc_a2e84472f1f9", + "doc_b6088784caab", + "doc_bbc97cbff254", + "doc_bf91f8d5da74", + "doc_c346f13ce597", + "doc_d147a81bac29", + "doc_d1f7d83e0824", + "doc_d6ad61a79371", + "doc_deb460ffa5ee", + "doc_e67e762fb6ab", + "doc_e7d508371e5c", + "doc_eeed52120ccf", + "doc_f12bbbc027a7", + "doc_f18f523b6ecc", + "doc_f2ae50c638a4", + "doc_f54217a0f2cf", + "doc_f5f256549d84", + "doc_fdf48d427fc0" + ], + "where": [ + "doc_07174a8633ac", + "doc_136d7a909d51", + "doc_3ae3aaf0c12c", + "doc_3f60413b3a06", + "doc_4a1cf8a90e83", + "doc_50ac650fd8f6", + "doc_618e34441fa7", + "doc_a24b92541657", + "doc_a2e84472f1f9", + "doc_d147a81bac29", + "doc_d6ad61a79371", + "doc_e7d508371e5c", + "doc_f12bbbc027a7" + ], + "whether": [ + "doc_0ac01bce8c79", + "doc_40e30666bfa8", + "doc_464a2b4a8d35", + "doc_50ac650fd8f6", + "doc_5ee8bbdbd8a8", + "doc_745f751f1344", + "doc_78b1717fafe3", + "doc_a24b92541657", + "doc_a2e84472f1f9", + "doc_e34fe24f9585", + "doc_e67e762fb6ab", + "doc_f12bbbc027a7" + ], + "which": [ + "doc_0383dbf277ba", + "doc_07174a8633ac", + "doc_1c5f37ea0ad3", + "doc_1e46cbc6bfae", + "doc_2a18de009d56", + "doc_4a1cf8a90e83", + "doc_567be1281e2f", + "doc_64090a20f830", + "doc_745f751f1344", + "doc_9e9118ed2141", + "doc_a5120de838fb", + "doc_b6088784caab", + "doc_d147a81bac29", + "doc_d1f7d83e0824", + "doc_d6ad61a79371", + "doc_f12bbbc027a7" + ], + "while": [ + "doc_0ac01bce8c79", + "doc_4a1cf8a90e83", + "doc_9e9118ed2141", + "doc_f92685a34f7e" + ], + "who": [ + "doc_e34fe24f9585" + ], + "whose": [ + "doc_4a1cf8a90e83", + "doc_a24b92541657" + ], + "why": [ + "doc_0ac01bce8c79", + "doc_5ee8bbdbd8a8", + "doc_618e34441fa7", + "doc_e8794369a1a4", + "doc_eeed52120ccf" + ], + "will": [ + "doc_18d38fdacc9e", + "doc_4055659fe573", + "doc_64090a20f830", + "doc_c346f13ce597", + "doc_f18f523b6ecc", + "doc_f54217a0f2cf" + ], + "window": [ + "doc_a24b92541657" + ], + "wins": [ + "doc_2798948c3080", + "doc_f12bbbc027a7" + ], + "wire": [ + "doc_4e1ebaf40267", + "doc_c346f13ce597" + ], + "wires": [ + "doc_4e1ebaf40267" + ], + "wiring": [ + "doc_2a18de009d56" + ], + "with_cache": [ + "doc_3f60413b3a06" + ], + "with_middlewares": [ + "doc_3f60413b3a06" + ], + "with_observers": [ + "doc_3f60413b3a06" + ], + "with_router": [ + "doc_3f60413b3a06" + ], + "within": [ + "doc_464a2b4a8d35", + "doc_b6088784caab", + "doc_c93c115aeb85" + ], + "without": [ + "doc_0383dbf277ba", + "doc_07174a8633ac", + "doc_136d7a909d51", + "doc_1c5f37ea0ad3", + "doc_3ae3aaf0c12c", + "doc_3eeb75d383b8", + "doc_3f1383f72c5f", + "doc_3f60413b3a06", + "doc_4a1cf8a90e83", + "doc_4e1ebaf40267", + "doc_5a65ddab1445", + "doc_5ee8bbdbd8a8", + "doc_618e34441fa7", + "doc_64090a20f830", + "doc_75887f91c7df", + "doc_9e9118ed2141", + "doc_a24b92541657", + "doc_a5120de838fb", + "doc_b6088784caab", + "doc_bbc97cbff254", + "doc_c346f13ce597", + "doc_c93c115aeb85", + "doc_f54217a0f2cf" + ], + "work": [ + "doc_136d7a909d51", + "doc_18d38fdacc9e", + "doc_1c5f37ea0ad3", + "doc_2798948c3080", + "doc_4a1cf8a90e83", + "doc_57e2139f0873", + "doc_618e34441fa7", + "doc_78b1717fafe3", + "doc_9e9118ed2141", + "doc_a24b92541657", + "doc_a2e84472f1f9", + "doc_a5120de838fb", + "doc_f12bbbc027a7", + "doc_f54217a0f2cf", + "doc_f5f256549d84", + "doc_fdf48d427fc0" + ], + "worker": [ + "doc_2a18de009d56", + "doc_3f1383f72c5f", + "doc_78b1717fafe3", + "doc_d147a81bac29" + ], + "workers": [ + "doc_0383dbf277ba", + "doc_067033cfc945", + "doc_3ae3aaf0c12c", + "doc_57e2139f0873", + "doc_78b1717fafe3", + "doc_9e9118ed2141", + "doc_a24b92541657", + "doc_bbc97cbff254", + "doc_d147a81bac29", + "doc_f5f256549d84" + ], + "workflow": [ + "doc_75887f91c7df", + "doc_c346f13ce597", + "doc_d147a81bac29", + "doc_deb460ffa5ee", + "doc_f18f523b6ecc", + "doc_f2ae50c638a4" + ], + "workflows": [ + "doc_a2e84472f1f9", + "doc_a5120de838fb", + "doc_d6ad61a79371" + ], + "working": [ + "doc_64090a20f830", + "doc_bf91f8d5da74" + ], + "workload": [ + "doc_a5120de838fb" + ], + "workloads": [ + "doc_61fd9f682847", + "doc_78b1717fafe3" + ], + "works": [ + "doc_07174a8633ac", + "doc_18d38fdacc9e", + "doc_57e2139f0873", + "doc_5ee8bbdbd8a8", + "doc_618e34441fa7", + "doc_64090a20f830", + "doc_9e9118ed2141", + "doc_a2e84472f1f9", + "doc_d1f7d83e0824", + "doc_d6ad61a79371", + "doc_e67e762fb6ab" + ], + "worse": [ + "doc_4055659fe573" + ], + "would": [ + "doc_136d7a909d51", + "doc_3eeb75d383b8", + "doc_618e34441fa7" + ], + "wrap": [ + "doc_745f751f1344", + "doc_c346f13ce597" + ], + "wrapped": [ + "doc_745f751f1344", + "doc_75887f91c7df" + ], + "wrapper": [ + "doc_bbc97cbff254", + "doc_c346f13ce597" + ], + "wrappers": [ + "doc_a24b92541657" + ], + "wraps": [ + "doc_1c5f37ea0ad3", + "doc_745f751f1344", + "doc_9e9118ed2141", + "doc_c346f13ce597" + ], + "write": [ + "doc_18d38fdacc9e", + "doc_43ad71a4ad0c", + "doc_4462f67b1c03", + "doc_618e34441fa7", + "doc_745f751f1344", + "doc_9e9118ed2141", + "doc_a24b92541657", + "doc_b6088784caab" + ], + "write-behind": [ + "doc_4a1cf8a90e83" + ], + "write_docs": [ + "doc_3f1383f72c5f" + ], + "write_file": [ + "doc_18d38fdacc9e" + ], + "write_golden_trace": [ + "doc_2a18de009d56" + ], + "write_suite_report_json": [ + "doc_2a18de009d56" + ], + "writer": [ + "doc_4a1cf8a90e83" + ], + "writes": [ + "doc_0383dbf277ba", + "doc_3f1383f72c5f", + "doc_4a1cf8a90e83", + "doc_a24b92541657" + ], + "writing": [ + "doc_3ae3aaf0c12c", + "doc_3f1383f72c5f", + "doc_9e9118ed2141" + ], + "written": [ + "doc_0383dbf277ba", + "doc_4a1cf8a90e83" + ], + "wrong": [ + "doc_4055659fe573" + ], + "xfail": [ + "doc_618e34441fa7" + ], + "xml": [ + "doc_618e34441fa7" + ], + "yaml": [ + "doc_067033cfc945", + "doc_b6088784caab" + ], + "yes": [ + "doc_0ac01bce8c79", + "doc_b6088784caab" + ], + "yet": [ + "doc_18d38fdacc9e", + "doc_4a1cf8a90e83", + "doc_f54217a0f2cf" + ], + "yields": [ + "doc_a24b92541657" + ], + "yml": [ + "doc_067033cfc945" + ], + "yourmodel": [ + "doc_3f60413b3a06" + ], + "zooms": [ + "doc_0ac01bce8c79" + ] +} diff --git a/skills/afk-maintainer/references/afk-docs/manifest.json b/skills/afk-maintainer/references/afk-docs/manifest.json new file mode 100644 index 0000000..7c7d420 --- /dev/null +++ b/skills/afk-maintainer/references/afk-docs/manifest.json @@ -0,0 +1,16 @@ +{ + "generated_at_utc": "2026-05-31T13:25:20.450882+00:00", + "document_count": 63, + "skill_count": 2, + "files": [ + "docs-index.json", + "docs-index.jsonl", + "inverted-index.json", + "path-to-id.json", + "id-to-path.json", + "examples.md", + "records/", + "text/", + "../skills/index.json" + ] +} diff --git a/skills/afk-maintainer/references/afk-docs/path-to-id.json b/skills/afk-maintainer/references/afk-docs/path-to-id.json new file mode 100644 index 0000000..2bee844 --- /dev/null +++ b/skills/afk-maintainer/references/afk-docs/path-to-id.json @@ -0,0 +1,65 @@ +{ + "docs/docs.json": "doc_3aa0b3792d22", + "docs/index.mdx": "doc_deb460ffa5ee", + "docs/library/a2a.mdx": "doc_1c5f37ea0ad3", + "docs/library/agent-skills.mdx": "doc_b6088784caab", + "docs/library/agentic-behavior.mdx": "doc_f12bbbc027a7", + "docs/library/agentic-levels.mdx": "doc_57e2139f0873", + "docs/library/agents.mdx": "doc_e67e762fb6ab", + "docs/library/api-reference.mdx": "doc_bbc97cbff254", + "docs/library/architecture.mdx": "doc_4e1ebaf40267", + "docs/library/building-with-ai.mdx": "doc_18d38fdacc9e", + "docs/library/checkpoint-schema.mdx": "doc_4a1cf8a90e83", + "docs/library/configuration-reference.mdx": "doc_a24b92541657", + "docs/library/core-runner.mdx": "doc_e7d508371e5c", + "docs/library/debugger.mdx": "doc_3f1383f72c5f", + "docs/library/deployment.mdx": "doc_067033cfc945", + "docs/library/developer-guide.mdx": "doc_d147a81bac29", + "docs/library/environment-variables.mdx": "doc_61fd9f682847", + "docs/library/evals.mdx": "doc_c93c115aeb85", + "docs/library/examples/index.mdx": "doc_a5120de838fb", + "docs/library/failure-policy-matrix.mdx": "doc_4055659fe573", + "docs/library/full-module-reference.mdx": "doc_2a18de009d56", + "docs/library/how-to-use-afk.mdx": "doc_3ae3aaf0c12c", + "docs/library/learn-in-15-minutes.mdx": "doc_f2ae50c638a4", + "docs/library/llm-interaction.mdx": "doc_5ee8bbdbd8a8", + "docs/library/mcp-server.mdx": "doc_e34fe24f9585", + "docs/library/memory.mdx": "doc_0383dbf277ba", + "docs/library/mental-model.mdx": "doc_50ac650fd8f6", + "docs/library/messaging.mdx": "doc_75887f91c7df", + "docs/library/migration.mdx": "doc_5a65ddab1445", + "docs/library/observability.mdx": "doc_78a8125d2c7e", + "docs/library/overview.mdx": "doc_f18f523b6ecc", + "docs/library/performance.mdx": "doc_f5f256549d84", + "docs/library/public-imports-and-function-improvement.mdx": "doc_3eeb75d383b8", + "docs/library/quickstart.mdx": "doc_bf91f8d5da74", + "docs/library/run-event-contract.mdx": "doc_f54217a0f2cf", + "docs/library/security-model.mdx": "doc_567be1281e2f", + "docs/library/snippets/01_minimal_chat_agent.mdx": "doc_1e46cbc6bfae", + "docs/library/snippets/02_policy_with_hitl.mdx": "doc_d6ad61a79371", + "docs/library/snippets/03_subagents_with_router.mdx": "doc_a2e84472f1f9", + "docs/library/snippets/04_resume_and_compact.mdx": "doc_07174a8633ac", + "docs/library/snippets/05_direct_llm_structured_output.mdx": "doc_3f60413b3a06", + "docs/library/snippets/06_tool_registry_security.mdx": "doc_43ad71a4ad0c", + "docs/library/snippets/07_tool_hooks_and_middleware.mdx": "doc_c346f13ce597", + "docs/library/snippets/08_prebuilt_runtime_tools.mdx": "doc_464a2b4a8d35", + "docs/library/snippets/09_system_prompt_loader.mdx": "doc_64090a20f830", + "docs/library/snippets/10_streaming_chat_with_memory.mdx": "doc_7b26cbd9a7ae", + "docs/library/snippets/11_cost_monitoring.mdx": "doc_fdf48d427fc0", + "docs/library/snippets/12_mcp_client_integration.mdx": "doc_40e30666bfa8", + "docs/library/snippets/13_multi_model_fallback.mdx": "doc_d1f7d83e0824", + "docs/library/snippets/14_production_client.mdx": "doc_4462f67b1c03", + "docs/library/streaming.mdx": "doc_109157c0d2d7", + "docs/library/system-prompts.mdx": "doc_2798948c3080", + "docs/library/task-queues.mdx": "doc_78b1717fafe3", + "docs/library/tested-behaviors.mdx": "doc_618e34441fa7", + "docs/library/tool-call-lifecycle.mdx": "doc_0ac01bce8c79", + "docs/library/tools-system-walkthrough.mdx": "doc_745f751f1344", + "docs/library/tools.mdx": "doc_9e9118ed2141", + "docs/library/troubleshooting.mdx": "doc_136d7a909d51", + "docs/llms/adapters.mdx": "doc_f1e32ed2ffce", + "docs/llms/agent-integration.mdx": "doc_eeed52120ccf", + "docs/llms/contracts.mdx": "doc_e8794369a1a4", + "docs/llms/control-and-session.mdx": "doc_7320ab982fab", + "docs/llms/index.mdx": "doc_f92685a34f7e" +} diff --git a/skills/afk-maintainer/references/afk-examples.md b/skills/afk-maintainer/references/afk-examples.md new file mode 100644 index 0000000..38db749 --- /dev/null +++ b/skills/afk-maintainer/references/afk-examples.md @@ -0,0 +1,2192 @@ +# AFK Examples + +Merged from `docs/library/snippets/*.mdx` for agent-friendly context loading. + +Generated at: 2026-05-31T13:25:20.451068+00:00 + +## 01_minimal_chat_agent + +Source: `docs/library/snippets/01_minimal_chat_agent.mdx` + +```python +--- +title: "01: Minimal Chat Agent" +description: Smallest synchronous AFK agent run. +--- + +This is the simplest possible AFK agent. It demonstrates the three core concepts you need to get started: defining an `Agent` with a model and instructions, creating a `Runner` to execute it, and reading the result from `final_text`. + +If you are new to AFK, start here. Every other example builds on this foundation. + +```python +from afk.agents import Agent +from afk.core import Runner + +agent = Agent(name="chat", model="gpt-4.1-mini", instructions="Answer directly with concrete detail.") +runner = Runner() +result = runner.run_sync(agent, user_message="Define error budget in SRE.") +print(result.final_text) +``` + +## Line-by-line explanation + +**`Agent(...)`** defines the agent's identity and behavior. The `name` is used for telemetry and logging. The `model` specifies which LLM to use. The `instructions` become the system prompt that guides the model's behavior. + +**`Runner()`** creates the execution engine. With no arguments, it uses in-memory defaults: headless interaction mode, no telemetry sink, and no policy engine. This is the fastest way to get started during development. + +**`runner.run_sync(...)`** executes the agent synchronously, blocking until the run completes. Under the hood, this creates an async event loop, runs the agent through the full lifecycle (LLM call, optional tool execution, optional subagent delegation), and returns the terminal `AgentResult`. The `user_message` is the initial prompt sent to the model. + +**`result.final_text`** contains the model's final text response. This is the primary output field on `AgentResult`. Always use `final_text` (not `output_text`) to access the agent's response. + +## What AgentResult contains + +The `AgentResult` dataclass returned by `run_sync` includes: + +| Field | Type | Description | +| --- | --- | --- | +| `final_text` | `str` | The agent's final text response. | +| `state` | `str` | Terminal state: `"completed"`, `"failed"`, `"cancelled"`, or `"degraded"`. | +| `run_id` | `str` | Unique identifier for this run. | +| `thread_id` | `str` | Thread identifier for memory continuity across runs. | +| `tool_executions` | `list` | Records of all tool calls made during the run. | +| `subagent_executions` | `list` | Records of all subagent invocations. | +| `usage` | `UsageAggregate` | Token usage and cost estimates across all LLM calls. | + +## Expected behavior + +When you run this example, the runner makes a single LLM call to `gpt-4.1-mini` with the system instructions and user message. Since no tools are registered, the model responds with text only. The run completes in one step with `state="completed"`, and `final_text` contains a concise definition of error budgets in SRE. + +No network calls, API keys, or external services are required beyond access to the specified LLM provider (configured via environment variables like `OPENAI_API_KEY`). +``` + +## 02_policy_with_hitl + +Source: `docs/library/snippets/02_policy_with_hitl.mdx` + +```python +--- +title: "02: Policy with Human Interaction" +description: Route sensitive actions through policy and human approval. +--- + +Human-in-the-loop (HITL) is the pattern where the agent pauses execution to request approval or input from a human operator before proceeding with a sensitive action. This is critical for any agent that can take destructive or irreversible actions -- deleting data, modifying production systems, sending communications, or spending money. + +AFK implements HITL through two components: a `PolicyEngine` that decides which actions require human intervention, and an `InteractionProvider` that routes the approval request to a human and returns their decision. + +## Basic example + +```python +from afk.agents import Agent, PolicyEngine, PolicyRule +from afk.core import Runner, RunnerConfig + +# Define a policy that gates destructive operations +policy = PolicyEngine(rules=[ + PolicyRule( + rule_id="gate-destructive-ops", + description="Require human approval for any destructive operation", + condition=lambda event: ( + event.tool_name is not None + and any(keyword in event.tool_name.lower() for keyword in ["delete", "drop", "remove"]) + ), + action="request_approval", + reason="Destructive operations require human approval before execution.", + ), +]) + +agent = Agent( + name="change-manager", + model="gpt-4.1-mini", + instructions="You manage infrastructure changes. Always use available tools.", +) + +# Headless mode: approval requests are auto-resolved using approval_fallback +runner = Runner( + policy_engine=policy, + config=RunnerConfig( + interaction_mode="headless", + approval_fallback="deny", # Auto-deny in headless mode + ), +) +result = runner.run_sync(agent, user_message="Drop old production tables") +print(f"State: {result.state}") +# In headless mode with approval_fallback="deny", the destructive action is blocked. +``` + +## Interactive mode with an InteractionProvider + +In production, you typically want a real human to review approval requests. Use `interaction_mode="interactive"` with a custom `InteractionProvider`: + +```python +from afk.core import Runner, RunnerConfig, InMemoryInteractiveProvider +from afk.agents import ApprovalDecision + +# In-memory provider for testing (simulates human approval) +provider = InMemoryInteractiveProvider() + +runner = Runner( + policy_engine=policy, + interaction_provider=provider, + config=RunnerConfig( + interaction_mode="interactive", + approval_timeout_s=300.0, # Wait up to 5 minutes for human response + ), +) + +# In a real application, a separate process or UI would call: +# provider.resolve_approval(request_id, ApprovalDecision(kind="allow")) +``` + +## Headless vs interactive modes + +AFK supports three interaction modes, configured via `RunnerConfig.interaction_mode`: + +| Mode | Behavior | When to Use | +| --- | --- | --- | +| `"headless"` | Approval requests are auto-resolved using `approval_fallback` (default: `"deny"`). No human is involved. | CI/CD pipelines, batch processing, testing, automated workflows where no human is available. | +| `"interactive"` | Approval requests are routed to the configured `InteractionProvider`. The runner pauses until a decision is returned or `approval_timeout_s` expires. | Production applications with human operators, chat UIs with approval buttons, Slack-based approval workflows. | +| `"external"` | Similar to interactive, but designed for use in external orchestration systems where the approval mechanism is managed outside AFK. | Enterprise systems with external approval platforms. | + +## How the policy flow works + +1. The agent calls a tool (e.g., `drop_table`). +2. Before executing, the runner sends a `PolicyEvent` to the `PolicyEngine`. +3. The engine evaluates all rules. If a rule matches, it returns a `PolicyDecision` with the configured action. +4. If the action is `request_approval`: + - In **headless** mode: the runner auto-resolves using `approval_fallback`. + - In **interactive** mode: the runner creates an `ApprovalRequest` and sends it to the `InteractionProvider`. Execution pauses until the provider returns an `ApprovalDecision`. +5. If approved (`kind="allow"`): the tool executes normally. +6. If denied (`kind="deny"`): the tool execution is skipped and the model receives a denial message as the tool result. +7. A `policy_decision` event is emitted in the run event stream for audit purposes. + +## Policy decision actions + +| Action | Effect | +| --- | --- | +| `"allow"` | Proceed with execution. No human interaction needed. | +| `"deny"` | Block execution immediately. The denial reason is reported to the model. | +| `"request_approval"` | Pause and request human approval through the `InteractionProvider`. | +| `"request_user_input"` | Pause and request freeform text input from a human operator. | +``` + +## 03_subagents_with_router + +Source: `docs/library/snippets/03_subagents_with_router.mdx` + +```python +--- +title: "03: Subagents with Router" +description: Delegate workload to specialist subagents and merge outputs. +--- + +When a task is too complex for a single agent, AFK supports delegating subtasks to specialist subagents. The coordinator (or "lead") agent decides what to delegate, and the runner handles dispatching work to subagents, collecting their results, and feeding those results back to the coordinator for synthesis. + +This pattern is useful for incident response, research workflows, content pipelines, and any scenario where different aspects of a task require distinct expertise or instructions. + +## Delegation flow + +```mermaid +flowchart TD + User[User message] --> Lead[Lead agent] + Lead --> Triage[Triage subagent] + Lead --> Analysis[Analysis subagent] + Lead --> Comms[Comms subagent] + Triage --> Lead + Analysis --> Lead + Comms --> Lead + Lead --> Result[Synthesized final_text] +``` + +## Example + +```python +from afk.agents import Agent +from afk.core import Runner + +# Define specialist subagents +triage = Agent( + name="triage", + model="gpt-4.1-mini", + instructions="Classify incident severity as SEV1, SEV2, SEV3, or SEV4 based on the description.", +) +analysis = Agent( + name="analysis", + model="gpt-4.1-mini", + instructions="Identify the most likely root causes for the described incident.", +) +comms = Agent( + name="comms", + model="gpt-4.1-mini", + instructions="Draft a concise stakeholder update email summarizing the incident and current status.", +) + +# Define the coordinator agent +lead = Agent( + name="lead", + model="gpt-4.1-mini", + instructions="Delegate to specialists and synthesize their outputs into a final response.", + subagents=[triage, analysis, comms], +) + +runner = Runner() +result = runner.run_sync(lead, user_message="Investigate API latency spike and draft update") +print(result.final_text) +``` + +## How the coordinator pattern works + +1. The **lead agent** receives the user message and decides how to delegate. It can invoke subagents through tool-like calls that the runner intercepts. + +2. The **runner** dispatches each subagent invocation as a separate run. Subagents execute independently with their own instructions and model configuration. The runner manages concurrency, timeout, and failure handling for each subagent. + +3. **Subagent results** are returned to the lead agent as execution records. Each record contains the subagent's `output_text` and optional error information. + +4. The **lead agent** receives all subagent outputs and synthesizes them into a unified response. This final synthesis step is what produces the coordinator's `final_text`. + +## What subagent_executions contains + +The `AgentResult` returned by the lead agent includes a `subagent_executions` list. Each entry is a `SubagentExecutionRecord` with: + +| Field | Type | Description | +| --- | --- | --- | +| `subagent_name` | `str` | Name of the subagent that was invoked. | +| `success` | `bool` | Whether the subagent completed successfully. | +| `output_text` | `str or None` | The subagent's response text, if it completed. | +| `latency_ms` | `float` | Wall-clock execution time in milliseconds. | +| `error` | `str or None` | Error message if the subagent failed. | + +You can inspect these records to understand what each subagent contributed: + +```python +result = runner.run_sync(lead, user_message="Investigate API latency spike") + +for execution in result.subagent_executions: + status = "OK" if execution.success else "FAILED" + text = execution.output_text or "" + print(f" [{status}] {execution.subagent_name}: {text[:80]}...") +``` + +## Subagent failure handling + +By default, subagent failure policy is `continue`. You can configure stricter or more resilient behavior using `FailSafeConfig`: + +```python +from afk.agents import FailSafeConfig + +lead = Agent( + name="lead", + model="gpt-4.1-mini", + instructions="Delegate to specialists. If any specialist fails, work with available results.", + subagents=[triage, analysis, comms], + fail_safe=FailSafeConfig( + subagent_failure_policy="retry_then_degrade", # Continue with partial results + max_subagent_depth=3, # Prevent deep recursion + ), +) +``` + +With `subagent_failure_policy="retry_then_degrade"`, the lead agent receives error information for failed subagents alongside successful results and can produce a best-effort synthesis. +``` + +## 04_resume_and_compact + +Source: `docs/library/snippets/04_resume_and_compact.mdx` + +```python +--- +title: "04: Resume and Compact" +description: Resume interrupted runs from their last checkpoint and compact retained thread memory to control storage growth. +--- + +## What this snippet demonstrates + +Agent runs can be interrupted by timeouts, cancellations, infrastructure failures, or intentional pauses (such as waiting for human approval). When a run is interrupted, the runner persists a checkpoint containing the run's state at the point of interruption. The `resume()` method picks up from that checkpoint, restoring the conversation history, tool execution records, and step counter so the agent continues where it left off rather than starting from scratch. + +Over time, long-running threads accumulate checkpoint records, event logs, and state entries. The `compact_thread()` method prunes old records according to retention policies, keeping storage bounded without losing the data needed for active runs. + +## Resuming an interrupted run + +```python +import asyncio +from afk.agents import Agent +from afk.core import Runner, RunnerConfig + +agent = Agent( + name="research-bot", + model="gpt-4.1-mini", + instructions="You help users research topics thoroughly.", +) + +runner = Runner(config=RunnerConfig(interaction_mode="headless")) + + +async def main(): + # Start a run that might be interrupted + result = await runner.run( + agent, + user_message="Research the history of distributed systems.", + thread_id="thread_research_001", + ) + + # Save these identifiers for later resume + run_id = result.run_id + thread_id = result.thread_id + print(f"Run completed: state={result.state}") + + # Later, resume from the checkpoint if the run was interrupted. + # The runner loads the latest checkpoint for this run_id + thread_id pair, + # restores the conversation state, and continues execution. + resumed_result = await runner.resume( + agent, + run_id=run_id, + thread_id=thread_id, + ) + print(f"Resumed run: state={resumed_result.state}") + print(resumed_result.final_text) + + +asyncio.run(main()) +``` + +### How resume works internally + +The runner follows this sequence when `resume()` is called: + +1. **Checkpoint lookup** -- The runner queries the memory store for the latest checkpoint matching the given `run_id` and `thread_id`. If no checkpoint exists, it raises `AgentCheckpointCorruptionError`. + +2. **Terminal check** -- If the checkpoint already contains a terminal result (the run completed before the resume was requested), the runner returns that result immediately without re-executing. + +3. **Snapshot restoration** -- The runner loads the runtime snapshot from the checkpoint, which includes the conversation message history, step counter, tool execution records, and any pending subagent state. + +4. **Continued execution** -- The runner calls `run_handle()` internally with the restored snapshot, continuing the step loop from where it was interrupted. + +### Resume method signature + +```python +await runner.resume( + agent, # Agent definition (must match the original run's agent) + run_id="run_123", # The run_id from the interrupted run + thread_id="th_abc", # The thread_id from the interrupted run + context=None, # Optional context overlay for resumed execution +) +``` + +| Parameter | Type | Description | +| --- | --- | --- | +| `agent` | `BaseAgent` | The agent definition used for continued execution. Must match the agent that started the original run. | +| `run_id` | `str` | The unique run identifier from the interrupted run. Found on `result.run_id`. | +| `thread_id` | `str` | The thread identifier from the interrupted run. Found on `result.thread_id`. | +| `context` | `dict` or `None` | Optional context overlay. Merged with the original run context. | + +## Compacting thread memory + +```python +import asyncio +from afk.core import Runner, RunnerConfig +from afk.memory import RetentionPolicy, StateRetentionPolicy + +runner = Runner(config=RunnerConfig(interaction_mode="headless")) + + +async def compact(): + compaction = await runner.compact_thread( + thread_id="thread_research_001", + event_policy=RetentionPolicy(max_age_ms=86_400_000), # Keep last 24 hours + state_policy=StateRetentionPolicy(max_entries=50), # Keep last 50 state entries + ) + print(f"Events removed: {compaction.events_removed}") + print(f"States removed: {compaction.states_removed}") + + +asyncio.run(compact()) +``` + +### How compaction works + +Compaction operates on two dimensions of stored data: + +- **Event retention** -- Controlled by `RetentionPolicy`. Removes event records older than `max_age_ms`. Events are the raw telemetry log entries (LLM calls, tool executions, state transitions) that accumulate over the lifetime of a thread. + +- **State retention** -- Controlled by `StateRetentionPolicy`. Removes state entries that exceed `max_entries`, keeping only the most recent ones. State entries include checkpoint snapshots, conversation summaries, and key-value metadata. + +Both policies are optional. If you omit a policy, that dimension is not compacted. The method returns a `MemoryCompactionResult` with counts of removed records so you can log or alert on compaction activity. + +### When to compact + +- **After long conversations** -- Threads with hundreds of turns accumulate large checkpoint histories. Compact after the conversation ends or reaches a natural break point. +- **On a schedule** -- Run compaction as a background task (e.g., hourly or daily) for threads that are still active but have grown large. +- **Before resume** -- If you know a thread has extensive history, compacting before resume reduces the data the runner needs to load. + +## Error handling + +```python +from afk.agents.errors import AgentCheckpointCorruptionError, AgentConfigurationError + +try: + result = await runner.resume(agent, run_id="invalid", thread_id="missing") +except AgentCheckpointCorruptionError: + # No checkpoint found for this run_id + thread_id combination. + # This means either the run_id is wrong, the checkpoint was compacted away, + # or the memory store was cleared. + print("No checkpoint found -- cannot resume.") +except AgentConfigurationError: + # run_id or thread_id is empty or invalid + print("Invalid run_id or thread_id.") +``` + +## What to read next + +- [Memory](/library/memory) -- Full memory architecture, checkpoint schema, and retention policies. +- [Core Runner](/library/core-runner) -- Step loop lifecycle, state machine, and all runner API methods. +- [Checkpoint Schema](/library/checkpoint-schema) -- Exact structure of checkpoint records stored in memory. +``` + +## 05_direct_llm_structured_output + +Source: `docs/library/snippets/05_direct_llm_structured_output.mdx` + +```python +--- +title: "05: Direct LLM Structured Output" +description: Use afk.llms with schema-validated responses. +--- + +Not every use case needs the full agent loop. Sometimes you want to call an LLM directly with a specific prompt and get back a structured, schema-validated response. AFK's `LLMBuilder` provides a fluent API for constructing LLM clients that can return Pydantic-validated objects directly, without the overhead of the agent run lifecycle. + +Use this pattern for classification, extraction, summarization, and any scenario where you want a single LLM call with a guaranteed output schema. + +## Example + +```python +from pydantic import BaseModel +from afk.llms import LLMBuilder +from afk.llms.types import LLMRequest, Message + +# Define the output schema as a Pydantic model +class Summary(BaseModel): + title: str + bullets: list[str] + +# Build an LLM client using the fluent builder +client = LLMBuilder().provider("openai").model("gpt-4.1-mini").profile("production").build() + +# Make a structured request +resp = await client.chat( + LLMRequest(messages=[Message(role="user", content="Summarize incident timeline")]), + response_model=Summary, +) +print(resp.structured_response) # {"title": "...", "bullets": ["...", "..."]} +print(resp.text) # The raw text response +``` + +## The builder pattern + +`LLMBuilder` uses a fluent (method-chaining) API to construct an LLM client with the exact configuration you need: + +```python +client = ( + LLMBuilder() + .provider("openai") # Which LLM provider to use + .model("gpt-4.1-mini") # Which model + .profile("production") # Apply a preset profile (retry, timeout, etc.) + .build() # Return the configured LLMClient +) +``` + +Each method returns the builder instance, so calls can be chained. The `.build()` call at the end constructs the final `LLMClient` with all specified settings. + +Available builder methods: + +| Method | Purpose | +| --- | --- | +| `.provider(name)` | Set the LLM provider (`"openai"`, `"litellm"`, `"anthropic_agent"`). | +| `.model(name)` | Set the model identifier. | +| `.profile(name)` | Apply a named configuration profile (`"production"`, `"development"`, etc.). | +| `.settings(settings)` | Replace the loaded `LLMSettings`. | +| `.with_middlewares(stack)` | Attach chat, stream, or embedding middleware. | +| `.with_observers(observers)` | Attach LLM lifecycle observers. | +| `.with_cache(cache_backend)` | Attach a cache backend instance or registered backend id. | +| `.with_router(router)` | Attach a router instance or registered router id. | +| `.build()` | Construct and return the `LLMClient`. | + +Sampling controls are request fields, not builder methods. Set them on `LLMRequest`, for example `LLMRequest(..., temperature=0.0, max_tokens=1000)`. + +## Structured output with Pydantic + +When you pass `response_model=YourModel` to `client.chat()`, the client instructs the LLM to return output that conforms to the model's JSON schema. The response is parsed and validated against the Pydantic model: + +- If the LLM returns valid structured output, `resp.structured_response` contains the parsed dictionary and `resp.text` contains the raw response. +- If the LLM returns output that does not match the schema, a `LLMInvalidResponseError` is raised. + +This is powered by the LLM provider's native structured output support (e.g., OpenAI's `response_format` parameter) when available, with a fallback to prompt-based JSON extraction. + +## When to use LLMBuilder vs Runner + +| Use Case | Approach | +| --- | --- | +| Single LLM call, no tools, no memory | `LLMBuilder` -- simpler, faster, no lifecycle overhead. | +| Structured extraction or classification | `LLMBuilder` with `response_model`. | +| Multi-turn conversation with tools | `Runner` -- provides the full agent loop with tool execution, policy, and memory. | +| Subagent delegation | `Runner` -- only the runner supports subagent dispatch. | +| Event streaming to a UI | `Runner` with `run_stream()`. | +| Eval-driven development | `Runner` -- evals require the full `AgentResult` lifecycle. | + +Use `LLMBuilder` when you want precision and control over a single LLM interaction. Use `Runner` when you need the full agentic lifecycle. +``` + +## 06_tool_registry_security + +Source: `docs/library/snippets/06_tool_registry_security.mdx` + +```python +--- +title: "06: Tool Registry Security" +description: Safe tool registration and guardrail practices. +--- + +Tools are the primary way agents interact with external systems. A tool that reads data is fundamentally different from a tool that deletes resources -- and your security model should reflect this. AFK provides multiple layers of defense for tool security: scoped tool definitions with typed arguments, sandbox profiles that restrict execution capabilities, and policy gates that require human approval for destructive operations. + +This page demonstrates how to register tools safely, distinguish between read-only and mutating tools, and configure policy gates to protect against unintended destructive actions. + +## Read-only vs mutating tools + +The most important security distinction is between tools that observe (read-only) and tools that act (mutating). Read-only tools are generally safe to allow broadly. Mutating tools should be tightly scoped and policy-gated. + +```python +from pydantic import BaseModel +from afk.tools import tool, ToolResult + +# --- Read-only tool: safe, broadly permitted --- +class LookupArgs(BaseModel): + resource_id: str + +@tool( + args_model=LookupArgs, + name="get_resource", + description="Look up a resource by ID. Returns resource metadata. Read-only.", +) +def get_resource(args: LookupArgs) -> dict: + return {"id": args.resource_id, "status": "active", "region": "us-east-1"} + + +# --- Mutating tool: destructive, requires policy gate --- +class DeleteArgs(BaseModel): + resource_id: str + +@tool( + args_model=DeleteArgs, + name="delete_resource", + description="Permanently delete a resource by ID. This action is irreversible.", +) +def delete_resource(args: DeleteArgs) -> dict: + # In production: call your API to delete the resource + return {"deleted": args.resource_id} +``` + +Notice the differences: +- The read-only tool (`get_resource`) has a description that explicitly says "Read-only." This signals to both the model and human reviewers that the tool is safe. +- The mutating tool (`delete_resource`) has a description warning about irreversibility. This helps the model understand the severity, and helps policy rules identify destructive operations. + +## Policy gate setup + +Use a `PolicyEngine` to require human approval before any mutating tool executes: + +```python +from afk.agents import Agent, PolicyEngine, PolicyRule, FailSafeConfig +from afk.core import Runner, RunnerConfig + +# Define policy rules that distinguish read vs write operations +policy = PolicyEngine(rules=[ + PolicyRule( + rule_id="gate-delete", + description="Require approval for delete operations", + condition=lambda event: event.tool_name == "delete_resource", + action="request_approval", + reason="Delete operations are irreversible and require human approval.", + ), + PolicyRule( + rule_id="deny-unknown-tools", + description="Deny any tool not explicitly registered", + condition=lambda event: ( + event.tool_name is not None + and event.tool_name not in {"get_resource", "delete_resource"} + ), + action="deny", + reason="Unregistered tools are not permitted.", + ), +]) + +agent = Agent( + name="resource-manager", + model="gpt-4.1-mini", + instructions="Manage resources using the available tools. Always look up a resource before modifying it.", + tools=[get_resource, delete_resource], + fail_safe=FailSafeConfig( + max_tool_calls=10, + max_total_cost_usd=0.10, + ), +) + +runner = Runner( + policy_engine=policy, + config=RunnerConfig( + interaction_mode="headless", + approval_fallback="deny", # Auto-deny destructive actions in headless mode + sanitize_tool_output=True, # Wrap tool output in untrusted-data markers + ), +) + +result = runner.run_sync(agent, user_message="Delete resource res-123") +print(f"State: {result.state}") +# In headless mode, the delete is auto-denied. The model sees the denial and responds accordingly. +``` + +## Sandbox profiles for filesystem tools + +For tools that interact with the filesystem or execute commands, use `SandboxProfile` to restrict their capabilities: + +```python +from afk.tools.security import SandboxProfile +from afk.core import RunnerConfig + +config = RunnerConfig( + default_sandbox_profile=SandboxProfile( + profile_id="restricted", + allow_network=False, # Block network access + allow_command_execution=True, # Allow shell commands + allowed_command_prefixes=["ls", "cat"], # Only safe read commands + deny_shell_operators=True, # Block pipes, redirects, semicolons + allowed_paths=["/app/data"], # Restrict file access to data directory + denied_paths=["/etc", "/root"], # Explicitly deny sensitive paths + command_timeout_s=10.0, # Kill commands after 10 seconds + max_output_chars=5_000, # Truncate large outputs + ), +) +``` + +## Scoping destructive tools + +Follow these principles when registering destructive tools: + +1. **Name them clearly.** Use verb prefixes that signal intent: `delete_`, `remove_`, `drop_`, `update_`, `modify_`. This makes policy rules easy to write and audit. + +2. **Type all arguments.** Use Pydantic models for argument validation. Never accept freeform `dict` arguments for mutating operations. + +3. **Describe irreversibility.** Include "irreversible", "destructive", or "permanent" in the tool description. This helps both the model and policy reviewers understand the risk. + +4. **Gate with policy rules.** Every mutating tool should have a corresponding policy rule. Use `request_approval` for interactive environments and `deny` as the fallback in headless mode. + +5. **Set cost limits.** Use `FailSafeConfig.max_tool_calls` and `max_total_cost_usd` to prevent runaway tool usage, especially when the agent has access to APIs with per-call costs. + +6. **Audit everything.** Policy decisions are emitted as `policy_decision` events in the run event stream. Persist these events for compliance and debugging. +``` + +## 07_tool_hooks_and_middleware + +Source: `docs/library/snippets/07_tool_hooks_and_middleware.mdx` + +```python +--- +title: "07: Tool Hooks and Middleware" +description: Add pre-execution validation, post-execution transformation, and cross-cutting middleware to tools and the LLM client pipeline. +--- + +## What this snippet demonstrates + +AFK provides two distinct hook/middleware systems that operate at different layers: + +1. **Tool hooks and middleware** -- Pre-hooks, post-hooks, and middleware that wrap individual tool executions. These use Pydantic models for typed arguments and run inside the tool execution pipeline. + +2. **LLM middleware** -- Middleware that wraps LLM client operations (chat, stream, embed). These intercept requests and responses at the provider transport layer. + +Both systems follow the same pattern: define a callable, wire it into the pipeline, and the runner executes it at the appropriate point in the lifecycle. + +## Tool pre-hooks + +A pre-hook runs before the main tool function executes. It receives the tool's arguments (validated against its own Pydantic model) and returns a dictionary of transformed arguments that the main tool will receive. Use pre-hooks for input sanitization, enrichment, or validation that should happen before execution. + +```python +from pydantic import BaseModel, Field +from afk.tools.core.decorator import tool, prehook + + +# Pre-hook argument model matches the main tool's argument shape +class SearchArgs(BaseModel): + query: str + max_results: int = Field(default=10, ge=1, le=100) + + +# Pre-hook: sanitize and normalize the query before the tool runs +@prehook(args_model=SearchArgs, name="normalize_query") +async def normalize_query(args: SearchArgs) -> dict: + """Strip extra whitespace and lowercase the query.""" + return { + "query": " ".join(args.query.lower().split()), + "max_results": min(args.max_results, 50), # Cap at 50 + } + + +# Main tool with the pre-hook attached +@tool( + args_model=SearchArgs, + name="search_docs", + description="Search the documentation index.", + prehooks=[normalize_query], +) +async def search_docs(args: SearchArgs) -> dict: + # args.query is already normalized by the pre-hook + return {"results": [f"Result for: {args.query}"], "count": args.max_results} +``` + +### Pre-hook execution flow + +```mermaid +flowchart LR + LLM[LLM proposes tool call] --> Validate[Validate raw args] + Validate --> PreHook[Pre-hook transforms args] + PreHook --> Execute[Tool function executes] + Execute --> Result[ToolResult returned] +``` + +The pre-hook receives validated arguments and must return a dictionary compatible with the main tool's `args_model`. If the returned dictionary fails validation against the tool's model, the tool call fails with a `ToolValidationError`. + +## Tool post-hooks + +A post-hook runs after the main tool function completes. It receives the tool output and can transform or annotate the result before it is returned to the LLM. Use post-hooks for output sanitization, logging, or enrichment. + +```python +from pydantic import BaseModel +from typing import Any +from afk.tools.core.decorator import posthook + + +class PostHookArgs(BaseModel): + output: Any + tool_name: str | None = None + + +@posthook(args_model=PostHookArgs, name="redact_sensitive") +async def redact_sensitive(args: PostHookArgs) -> dict: + """Remove sensitive fields from tool output before returning to LLM.""" + output = args.output + if isinstance(output, dict): + # Strip any fields that might contain secrets + sanitized = { + k: v for k, v in output.items() + if k not in ("api_key", "secret", "password", "token") + } + return {"output": sanitized, "tool_name": args.tool_name} + return {"output": output, "tool_name": args.tool_name} +``` + +AFK passes post-hooks a payload dictionary with the shape `{"output": , "tool_name": ""}`. The post-hook must return a dictionary with the same shape. + +## Tool-level middleware + +Tool-level middleware wraps around the entire tool execution, including pre-hooks and post-hooks. Middleware receives a `call_next` function and the tool arguments, and can modify behavior before, after, or around execution. + +```python +from afk.tools.core.decorator import middleware + + +@middleware(name="timing_middleware") +async def timing_middleware(call_next, args, ctx): + """Measure and log tool execution time.""" + import time + start = time.monotonic() + result = await call_next(args, ctx) + elapsed_ms = (time.monotonic() - start) * 1000 + print(f"Tool executed in {elapsed_ms:.1f}ms") + return result + + +@middleware(name="retry_on_transient") +async def retry_on_transient(call_next, args, ctx): + """Retry the tool once on transient errors.""" + try: + return await call_next(args, ctx) + except ConnectionError: + # One retry on connection errors + return await call_next(args, ctx) +``` + +### Attaching middleware to a tool + +```python +@tool( + args_model=SearchArgs, + name="search_docs", + description="Search the documentation index.", + prehooks=[normalize_query], + posthooks=[redact_sensitive], + middlewares=[timing_middleware, retry_on_transient], +) +async def search_docs(args: SearchArgs) -> dict: + return {"results": [...]} +``` + +Middleware executes in the order listed. The first middleware in the list is the outermost wrapper. Each middleware calls `call_next` to pass control to the next middleware (or the actual tool function if it is the last one). + +## Registry-level middleware + +Registry-level middleware applies to every tool in a `ToolRegistry`, not just a single tool. Use this for cross-cutting concerns like audit logging, rate limiting, or policy enforcement that should apply uniformly. + +```python +from afk.tools.core.decorator import registry_middleware + + +@registry_middleware(name="audit_log") +async def audit_log(call_next, tool, raw_args, ctx): + """Log every tool invocation for audit purposes.""" + print(f"AUDIT: tool={tool.spec.name} args={raw_args}") + result = await call_next(tool, raw_args, ctx) + print(f"AUDIT: tool={tool.spec.name} success={result.success}") + return result +``` + +## LLM client middleware + +LLM middleware operates at the provider transport layer, intercepting requests to and responses from the LLM API. AFK defines three middleware protocols for the three LLM operations: + +```python +from afk.llms import LLMBuilder, LLMRequest, LLMResponse +from afk.llms.middleware import MiddlewareStack + + +# Chat middleware: intercepts non-streaming chat requests +async def add_request_metadata(call_next, req: LLMRequest) -> LLMResponse: + """Add tracing metadata to every LLM request.""" + req.metadata = req.metadata or {} + req.metadata["trace_id"] = "trace_abc123" + return await call_next(req) + + +# Build client with middleware +client = ( + LLMBuilder() + .provider("openai") + .model("gpt-4.1-mini") + .profile("production") + .with_middlewares(MiddlewareStack( + chat=[add_request_metadata], + embed=[], + stream=[], + )) + .build() +) +``` + +### LLM middleware protocols + +| Protocol | Operation | Signature | +| --- | --- | --- | +| `LLMChatMiddleware` | Non-streaming chat | `async (call_next, req: LLMRequest) -> LLMResponse` | +| `LLMEmbedMiddleware` | Embeddings | `async (call_next, req: EmbeddingRequest) -> EmbeddingResponse` | +| `LLMStreamMiddleware` | Streaming chat | `(call_next, req: LLMRequest) -> AsyncIterator[LLMStreamEvent]` | + +Each middleware receives `call_next` (the next middleware or transport in the chain) and the request object. It can modify the request before calling `call_next`, modify the response after, or short-circuit entirely by returning a response without calling `call_next`. + +## Built-in LLM middleware + +AFK ships with pre-built middleware for common patterns: + +### Timeout middleware + +Apply per-request timeouts to prevent runaway calls: + +```python +from afk.llms.middleware.timeout import ( + TimeoutMiddleware, + EmbedTimeoutMiddleware, + StreamTimeoutMiddleware, + TimeoutConfig, +) +from afk.llms.middleware import MiddlewareStack + +# Configure timeouts +config = TimeoutConfig( + default_timeout_s=30.0, + chat_timeout_s=60.0, + embed_timeout_s=15.0, + stream_timeout_s=45.0, +) + +# Add to middleware stack +stack = MiddlewareStack( + chat=[TimeoutMiddleware(config)], + embed=[EmbedTimeoutMiddleware(config)], + stream=[StreamTimeoutMiddleware(config)], +) + +# Build client +client = ( + LLMBuilder() + .provider("openai") + .model("gpt-4.1-mini") + .with_middlewares(stack) + .build() +) +``` + +The timeout middleware respects `TimeoutPolicy` from the request if provided: +```python +req = LLMRequest( + model="gpt-4.1-mini", + messages=[...], + timeout_policy=TimeoutPolicy(request_timeout_s=45.0), # Override config +) +``` + +## When to use each layer + +| Layer | Scope | Use for | +| --- | --- | --- | +| **Tool pre-hook** | Single tool, before execution | Input sanitization, argument enrichment, validation | +| **Tool post-hook** | Single tool, after execution | Output sanitization, redaction, annotation | +| **Tool middleware** | Single tool, wraps execution | Timing, retries, caching, error handling | +| **Registry middleware** | All tools in registry | Audit logging, rate limiting, policy enforcement | +| **LLM middleware** | All LLM calls through client | Request metadata, response logging, tracing | + +## What to read next + +- [Tools](/library/tools) -- Full tool system architecture, the 6-step execution pipeline, and design guidelines. +- [Tool Call Lifecycle](/library/tool-call-lifecycle) -- Detailed lifecycle of a tool call from LLM proposal to result delivery. +- [LLMs Overview](/llms/index) -- Builder workflow, runtime profiles, and provider selection. +``` + +## 08_prebuilt_runtime_tools + +Source: `docs/library/snippets/08_prebuilt_runtime_tools.mdx` + +```python +--- +title: "08: Prebuilt Runtime Tools" +description: Use AFK's built-in filesystem tools with directory-scoped security constraints and compose them with policy checks. +--- + +## What this snippet demonstrates + +AFK ships prebuilt tools for common runtime operations like listing directories and reading files. These tools are designed with security-first defaults: every tool is scoped to an explicit root directory that prevents directory traversal attacks. This snippet shows how to create, configure, and compose prebuilt tools with agents and policy guards. + +## Building runtime tools + +The `build_runtime_tools()` factory creates a set of filesystem tools bound to a specific root directory. All path operations within these tools are resolved against this root, and any attempt to access files outside it raises a `FileAccessError`. + +```python +from pathlib import Path +from afk.agents import Agent +from afk.core import Runner, RunnerConfig +from afk.tools.prebuilts.runtime import build_runtime_tools + +# Create filesystem tools scoped to a specific directory +runtime_tools = build_runtime_tools(root_dir=Path("./workspace")) + +agent = Agent( + name="file-assistant", + model="gpt-4.1-mini", + instructions=( + "You help users explore and read files in the workspace directory. " + "Use list_directory to browse the directory structure and read_file " + "to read file contents. You cannot access files outside the workspace." + ), + tools=runtime_tools, +) + +runner = Runner(config=RunnerConfig(interaction_mode="headless")) +result = runner.run_sync(agent, user_message="What files are in the workspace?") +print(result.final_text) +``` + +## Available prebuilt tools + +The `build_runtime_tools()` factory produces two tools: + +### list_directory + +Lists entries in a directory under the configured root. Returns entry names, paths, and type flags (file or directory). + +| Parameter | Type | Default | Description | +| ------------- | ----- | ------- | ----------------------------------------------------------------- | +| `path` | `str` | `"."` | Relative path to list, resolved against the root directory. | +| `max_entries` | `int` | `200` | Maximum entries to return (1--5000). Prevents unbounded listings. | + +**Returns:** A dictionary with `root`, `path`, and `entries` (list of `{name, path, is_dir, is_file}`). + +### read_file + +Reads the contents of a file under the configured root, with configurable truncation to prevent excessive token consumption. + +| Parameter | Type | Default | Description | +| ----------- | ----- | ---------- | -------------------------------------------------------------------------------- | +| `path` | `str` | (required) | Relative path to the file, resolved against the root directory. | +| `max_chars` | `int` | `20_000` | Maximum characters to read (1--500,000). Content is truncated beyond this limit. | + +**Returns:** A dictionary with `root`, `path`, `content`, and `truncated` (boolean indicating whether content was truncated). + +## Security: directory traversal prevention + +Every path operation is validated with an internal containment check that uses Python's `Path.relative_to()` to verify that the resolved path stays within the configured root. This prevents attacks like: + +``` +../../etc/passwd # Blocked: escapes root +/absolute/path/to/secrets # Blocked: escapes root +./workspace/../../../etc # Blocked: resolved path escapes root +``` + +If a path escapes the root, the tool raises `FileAccessError` immediately, before any file I/O occurs. + +## Composing with policy checks + +For additional security, pair runtime tools with a policy engine that gates specific operations on approval: + +```python +from afk.agents import Agent, PolicyEngine, PolicyRule + +# Define a policy that requires approval for reading certain files +policy = PolicyEngine( + rules=[ + PolicyRule( + tool_name="read_file", + description="Require approval for reading config files", + condition=lambda event: ".env" in event.tool_args.get("path", "") + or "config" in event.tool_args.get("path", ""), + action="request_approval", + approval_message="Agent wants to read a config file: {path}", + ), + ] +) + +agent = Agent( + name="ops-assistant", + model="gpt-4.1-mini", + instructions="Use approved runtime tools only. Never read sensitive configuration without approval.", + tools=build_runtime_tools(root_dir=Path("./project")), +) + +runner = Runner( + policy_engine=policy, + config=RunnerConfig(interaction_mode="headless"), +) +``` + +## Composing with custom tools + +You can combine prebuilt tools with your own custom tools in a single agent: + +```python +from pydantic import BaseModel +from afk.tools import tool + + +class GrepArgs(BaseModel): + pattern: str + path: str = "." + + +@tool( + args_model=GrepArgs, + name="grep_files", + description="Search for a pattern in files within the workspace.", +) +async def grep_files(args: GrepArgs) -> dict: + # Your custom search implementation + return {"matches": [], "pattern": args.pattern} + + +# Combine prebuilt + custom tools +all_tools = build_runtime_tools(root_dir=Path("./workspace")) + [grep_files] + +agent = Agent( + name="dev-assistant", + model="gpt-4.1-mini", + instructions="Help developers explore and search the codebase.", + tools=all_tools, +) +``` + +## Command allowlists and sandbox profiles + +For production environments, restrict tool capabilities further using sandbox profiles: + +```python +from afk.tools.security import SandboxProfile + +# Create a read-only sandbox that restricts what operations tools can perform +read_only_profile = SandboxProfile( + name="read_only", + allowed_operations=["read", "list"], + denied_operations=["write", "delete", "execute"], + max_file_size_bytes=1_000_000, # 1 MB max read size + allowed_extensions=[".py", ".md", ".txt", ".json", ".yaml"], +) +``` + +This ensures that even if the LLM attempts to use tools for unauthorized operations, the sandbox profile blocks execution before any I/O occurs. + +## What to read next + +- [Tools](/library/tools) -- Full tool system architecture, including the `@tool` decorator, `ToolResult`, and execution pipeline. +- [Snippet 06: Tool Registry Security](/library/snippets/06_tool_registry_security) -- Security scoping, policy gates, and sandbox profiles in detail. +- [Security Model](/library/security-model) -- Threat model, defense layers, and RunnerConfig security fields. +``` + +## 09_system_prompt_loader + +Source: `docs/library/snippets/09_system_prompt_loader.mdx` + +```python +--- +title: "09: System Prompt Loader" +description: Resolve agent system prompts from a file hierarchy with deterministic precedence, Jinja templating, and stat-based caching. +--- + +## What this snippet demonstrates + +AFK agents need system prompts (instructions) that tell the LLM how to behave. Rather than hardcoding instructions as inline strings, AFK provides a file-based prompt resolution system that loads prompts from a directory hierarchy. This keeps prompts version-controlled, editable by non-developers, and reusable across agents. + +The prompt loader resolves instructions through a deterministic precedence chain, supports Jinja2 templating for dynamic prompts, and caches compiled templates using stat-based invalidation for hot-reload during development. + +## Resolution precedence + +The prompt system resolves agent instructions through this priority chain: + +```mermaid +flowchart TD + A[Agent has inline instructions?] -->|Yes| B[Use inline instructions] + A -->|No| C[Agent has instruction_file?] + C -->|Yes| D[Load from instruction_file path] + C -->|No| E[Auto-detect from agent name] + E --> F[Convert name to UPPER_SNAKE_CASE.md] + F --> G[Look in prompts_dir] +``` + +1. **Inline `instructions`** -- If the agent has a non-empty `instructions` string, it is used directly. No file loading occurs. +2. **Explicit `instruction_file`** -- If set, the file is loaded from the configured `prompts_dir`. The path must resolve to a file inside the prompts root (no directory traversal). +3. **Auto-detected file** -- If neither is set, the agent's name is converted to `UPPER_SNAKE_CASE.md` and loaded from `prompts_dir`. + +## Basic usage + +```python +from afk.agents import Agent + +# Option 1: Inline instructions (highest priority) +agent = Agent( + name="ChatAgent", + model="gpt-4.1-mini", + instructions="Answer customer questions concisely.", +) + +# Option 2: Explicit instruction file +agent = Agent( + name="ChatAgent", + model="gpt-4.1-mini", + instruction_file="chat_agent_system.md", # Loaded from prompts_dir + prompts_dir=".agents/prompt", +) + +# Option 3: Auto-detected file (uses agent name) +# Loads .agents/prompt/CHAT_AGENT.md automatically +agent = Agent( + name="ChatAgent", + model="gpt-4.1-mini", + prompts_dir=".agents/prompt", +) +``` + +## Name-to-filename conversion + +The auto-detection algorithm converts the agent name to a filename using these rules: + +| Agent Name | Derived Filename | Rule Applied | +| --- | --- | --- | +| `ChatAgent` | `CHAT_AGENT.md` | CamelCase split on boundaries | +| `chatagent` | `CHAT_AGENT.md` | Lowercase `agent` suffix detected and split | +| `research-assistant` | `RESEARCH_ASSISTANT.md` | Hyphens replaced with underscores | +| `QA Bot v2` | `QA_BOT_V2.md` | Spaces and non-alphanumeric chars become underscores | + +The conversion is handled by `derive_auto_prompt_filename()` internally. It splits camelCase boundaries, normalizes non-alphanumeric characters to underscores, collapses consecutive underscores, and uppercases the result. + +## Prompts directory resolution + +The prompts directory is resolved through its own priority chain: + +1. Explicit `prompts_dir` argument on the `Agent` constructor. +2. `AFK_AGENT_PROMPTS_DIR` environment variable. +3. Default: `.agents/prompt` relative to the current working directory. + +```python +# Explicit +agent = Agent(name="Bot", model="gpt-4.1-mini", prompts_dir="/opt/prompts") + +# Environment variable +# export AFK_AGENT_PROMPTS_DIR=/opt/prompts +agent = Agent(name="Bot", model="gpt-4.1-mini") + +# Default: .agents/prompt/ +agent = Agent(name="Bot", model="gpt-4.1-mini") +``` + +## Jinja2 templating + +Prompt files support Jinja2 template syntax. When the runner resolves a prompt, it renders the template with a context dictionary that includes agent metadata and any custom context passed to the run. + +**File: `.agents/prompt/SUPPORT_AGENT.md`** + +```markdown +You are {{ agent_name }}, a support agent for {{ ctx.company_name }}. + +Your responsibilities: +- Answer questions about {{ ctx.product_name }} +- Escalate billing issues to the billing team +- Never disclose internal pricing formulas + +{% if ctx.get("tone") == "formal" %} +Use formal language and address the customer by title. +{% else %} +Use friendly, conversational language. +{% endif %} +``` + +**Agent code:** + +```python +from afk.core import Runner, RunnerConfig + +agent = Agent( + name="SupportAgent", + model="gpt-4.1-mini", + prompts_dir=".agents/prompt", +) + +runner = Runner(config=RunnerConfig(interaction_mode="headless")) +result = runner.run_sync( + agent, + user_message="How do I reset my password?", + context={ + "company_name": "Acme Corp", + "product_name": "Acme Cloud", + "tone": "friendly", + }, +) +``` + +### Template context variables + +The following variables are available in every prompt template: + +| Variable | Type | Description | +| --- | --- | --- | +| `agent_name` | `str` | The agent's `name` field. | +| `agent_class` | `str` | The Python class name of the agent. | +| `context` | `dict` | The full context dictionary passed to the run. | +| `ctx` | `dict` | Alias for `context` (shorthand). | + +Any keys in the `context` dictionary that are not reserved names (`context`, `ctx`, `agent_name`, `agent_class`) are also available as top-level template variables. So `{{ company_name }}` works as a shorthand for `{{ ctx.company_name }}`. + +## Caching and hot-reload + +The prompt system uses a process-wide `PromptStore` singleton that caches at three levels: + +1. **File cache** -- Keyed by resolved file path. Uses `stat()` metadata (mtime, size, inode) as the cache signature. If the file changes on disk, the cache entry is invalidated automatically. + +2. **Text pool** -- Deduplicates prompt text by SHA-256 hash. If multiple agents use the same prompt content (even from different files), only one copy is stored in memory. + +3. **Template cache** -- Compiled Jinja2 templates are cached by content hash. Re-rendering with different context variables reuses the compiled template. + +This means that during development, you can edit prompt files and they will be picked up on the next run without restarting the process. In production, the stat-based check is a single `os.stat()` call per prompt resolution, which is negligible overhead. + +## Security: path containment + +The prompt loader enforces strict path containment. The resolved prompt file path must be inside the configured `prompts_dir`. If an `instruction_file` path resolves outside the prompts root (via `../` traversal or an absolute path pointing elsewhere), the loader raises `PromptAccessError` immediately. + +```python +# This would raise PromptAccessError: +agent = Agent( + name="Agent", + model="gpt-4.1-mini", + instruction_file="../../etc/passwd", # Escapes prompts root + prompts_dir=".agents/prompt", +) +``` + +## What to read next + +- [System Prompts](/library/system-prompts) -- Full system prompt architecture, resolution pipeline, and design guidelines. +- [Agents](/library/agents) -- Agent model, configuration fields, and composition patterns. +- [Security Model](/library/security-model) -- Threat model and defense layers including prompt injection considerations. +``` + +## 10_streaming_chat_with_memory + +Source: `docs/library/snippets/10_streaming_chat_with_memory.mdx` + +```python +--- +title: "10: Streaming Chat with Memory" +description: Combine real-time streaming with thread-based memory for multi-turn chat UIs. +--- + +## What this snippet demonstrates + +Most chat applications need two things simultaneously: **real-time streaming** (so users see text as it's generated) and **memory continuity** (so the agent remembers previous turns). This snippet shows how to combine `run_stream()` with `thread_id` to build a multi-turn streaming chat handler. + +## Full example + +```python +import asyncio +from afk.agents import Agent, FailSafeConfig +from afk.core import Runner, RunnerConfig + +agent = Agent( + name="chat-assistant", + model="gpt-4.1-mini", + instructions=""" + You are a helpful assistant. Remember context from earlier in the conversation. + Be concise but thorough. If the user refers to something from a previous message, + use that context in your response. + """, + fail_safe=FailSafeConfig( + max_steps=10, + max_total_cost_usd=0.25, + ), +) + + +async def stream_turn(runner: Runner, user_message: str, thread_id: str): + """Stream a single turn and return the result.""" + handle = await runner.run_stream( + agent, + user_message=user_message, + thread_id=thread_id, # ← Same thread_id = same conversation + ) + + async for event in handle: + match event.type: + case "text_delta": + print(event.text_delta, end="", flush=True) + case "tool_started": + print(f"\n[TOOL] {event.tool_name}...") + case "tool_completed": + status = "[OK]" if event.tool_success else "[ERR]" + print(f" {status} done") + case "error": + if event.error: + print(f"\n[WARN] {event.error}") + case "completed": + print(f"\n[DONE] ({event.result.state})") + + return handle.result + + +async def main(): + runner = Runner(config=RunnerConfig(interaction_mode="headless")) + thread = "session-demo-42" + + # Turn 1 + print("User: What is the GIL in Python?\n") + print("Assistant: ", end="") + r1 = await stream_turn(runner, "What is the GIL in Python?", thread) + + # Turn 2 — agent remembers Turn 1 + print("\n\nUser: How does it affect multithreading?\n") + print("Assistant: ", end="") + r2 = await stream_turn(runner, "How does it affect multithreading?", thread) + + # Turn 3 — agent still has full context + print("\n\nUser: What are the alternatives?\n") + print("Assistant: ", end="") + r3 = await stream_turn(runner, "What are the alternatives?", thread) + + # Print usage summary + print(f"\n\n--- Usage ---") + for i, r in enumerate([r1, r2, r3], 1): + print(f"Turn {i}: {r.usage.total_tokens} tokens") + + +asyncio.run(main()) +``` + +## Key patterns + +### Thread ID connects turns + +Pass the same `thread_id` across `run_stream()` calls to maintain conversation context: + +```python +# These two calls share memory +r1 = await runner.run_stream(agent, user_message="Hello", thread_id="t-42") +r2 = await runner.run_stream(agent, user_message="Follow up", thread_id="t-42") +``` + +### Access the result after streaming + +The `handle.result` is available after the stream completes: + +```python +async for event in handle: + ... # Process events + +result = handle.result # Full AgentResult with final_text, usage, etc. +``` + +### Cancel mid-stream + +If the user navigates away or clicks "stop": + +```python +await handle.cancel() +# The run transitions to "cancelled" state +``` + +## What to read next + +- [Streaming](/library/streaming) — Full event reference and stream control API. +- [Memory](/library/memory) — Thread persistence, compaction, and backend configuration. +- [Snippet 04: Resume + Compact](/library/snippets/04_resume_and_compact) — Checkpoint-based resumption and memory management. +``` + +## 11_cost_monitoring + +Source: `docs/library/snippets/11_cost_monitoring.mdx` + +```python +--- +title: "11: Cost Monitoring" +description: Track and control agent costs using FailSafeConfig budgets and telemetry events. +--- + +## What this snippet demonstrates + +Runaway agent loops are the most common source of unexpected API costs. AFK provides two defense layers: **cost budgets** that kill runs when spending exceeds a threshold, and **telemetry events** that let you observe cost in real time. This snippet shows how to configure both. + +## Setting cost budgets + +The simplest defense is a hard cost ceiling on every agent: + +```python +from afk.agents import Agent, FailSafeConfig + +agent = Agent( + name="budget-agent", + model="gpt-4.1-mini", + instructions="Be helpful and concise.", + fail_safe=FailSafeConfig( + max_total_cost_usd=0.50, # Hard cost ceiling + max_llm_calls=30, # Secondary defense: limit API calls + max_steps=15, # Tertiary defense: limit reasoning steps + max_wall_time_s=120.0, # Quaternary defense: wall-clock timeout + ), +) +``` + +When the estimated cost exceeds `max_total_cost_usd`, the runner terminates the run with a `degraded` state and returns the best partial result. + +## Monitoring cost from results + +Every `AgentResult` includes token counts and cost estimates: + +```python +from afk.core import Runner + +runner = Runner() +result = runner.run_sync(agent, user_message="Analyze this dataset...") + +# Access usage statistics +usage = result.usage_aggregate +print(f"Input tokens: {usage.input_tokens}") +print(f"Output tokens: {usage.output_tokens}") +print(f"Total tokens: {usage.total_tokens}") +print(f"Estimated cost: ${result.total_cost_usd or 0:.4f}") +print(f"Tool calls: {len(result.tool_executions)}") +``` + +## Real-time cost monitoring via streaming + +For long-running agents, monitor cost during execution: + +```python +import asyncio +from afk.agents import Agent, FailSafeConfig +from afk.core import Runner + +agent = Agent( + name="analyst", + model="gpt-4.1", + instructions="Provide detailed analysis.", + fail_safe=FailSafeConfig( + max_total_cost_usd=1.00, + max_steps=20, + ), +) + + +async def monitor_cost(): + runner = Runner() + handle = await runner.run_stream( + agent, user_message="Compare Python async patterns for service code" + ) + + step_count = 0 + async for event in handle: + match event.type: + case "text_delta": + print(event.text_delta, end="", flush=True) + case "step_started" if event.step is not None: + step_count = event.step + case "tool_completed": + print(f"\n [STEP] Step {step_count} | Tool: {event.tool_name}") + case "completed" if event.result is not None: + usage = event.result.usage_aggregate + print(f"\n\n--- Cost Summary ---") + print(f"State: {event.result.state}") + print(f"Tokens: {usage.total_tokens}") + print(f"Cost: ${event.result.total_cost_usd or 0:.4f}") + print(f"Tools: {len(event.result.tool_executions)}") + +asyncio.run(monitor_cost()) +``` + +## Cost-aware batch processing + +When running multiple agents in a batch, track cumulative cost: + +```python +async def batch_process(items: list[str], budget_usd: float): + """Process items with a shared cost budget.""" + runner = Runner() + cumulative_cost = 0.0 + results = [] + + for item in items: + if cumulative_cost >= budget_usd: + print(f"[Limit] Budget exhausted at ${cumulative_cost:.4f}") + break + + # Set per-item budget as remaining budget + remaining = budget_usd - cumulative_cost + agent = Agent( + name="batch-processor", + model="gpt-4.1-mini", + instructions="Process the item concisely.", + fail_safe=FailSafeConfig( + max_total_cost_usd=min(remaining, 0.10), # Per-item cap + max_steps=5, + ), + ) + + result = await runner.run(agent, user_message=item) + item_cost = result.total_cost_usd or 0.0 + cumulative_cost += item_cost + results.append(result) + + print(f" [OK] {item[:40]}... (${item_cost:.4f})") + + print(f"\nTotal: {len(results)} items, ${cumulative_cost:.4f}") + return results +``` + +## Operating recommendations + +1. **Always set `max_total_cost_usd`** — even generous limits prevent runaway costs +2. **Layer defenses** — combine cost limits with `max_llm_calls`, `max_steps`, and `max_wall_time_s` +3. **Use telemetry for dashboards** — export metrics to monitor cost trends over time +4. **Set per-item budgets in batches** — prevent one expensive item from consuming the entire budget +5. **Choose models by task** — use smaller models for routine work and reserve larger models for requests that need them + +## What to read next + +- [Observability](/library/observability) — Telemetry pipeline for metrics and dashboards. +- [Failure Policy Matrix](/library/failure-policy-matrix) — How cost limit breaches flow through the system. +- [Configuration Reference](/library/configuration-reference#failsafeconfig) — Full FailSafeConfig field reference. +``` + +## 12_mcp_client_integration + +Source: `docs/library/snippets/12_mcp_client_integration.mdx` + +```python +--- +title: "12: MCP Client Integration" +description: Discover and use tools from external MCP servers in your agents. +--- + +## What this snippet demonstrates + +AFK agents can consume tools from external MCP (Model Context Protocol) servers just like local tools. This snippet shows how to connect to an MCP server, discover available tools, and attach them to an agent — all with the same validation, policy gates, and telemetry as local tools. + +## Consuming MCP tools + +### Connect, discover, and attach + +```python +import asyncio +from afk.agents import Agent, FailSafeConfig +from afk.core import Runner +from afk.mcp import MCPStore + +async def main(): + # 1. Connect to an external MCP server + store = MCPStore() + await store.connect("https://tools.example.com:3001") + + # 2. Discover available tools + tools = await store.list_tools() + print(f"Found {len(tools)} tools:") + for t in tools: + print(f" • {t.name}: {t.description}") + + # 3. Attach MCP tools to an agent — they work like local tools + agent = Agent( + name="mcp-assistant", + model="gpt-4.1-mini", + instructions=""" + Use the available tools to help the user. + Always explain what tool you're using and why. + """, + tools=tools, + fail_safe=FailSafeConfig( + max_total_cost_usd=0.25, + max_tool_calls=10, + ), + ) + + # 4. Run the agent — MCP tools execute transparently + runner = Runner() + result = runner.run_sync( + agent, user_message="Search the documentation for authentication patterns" + ) + print(f"\n{result.final_text}") + + # 5. Inspect tool calls — MCP tools appear just like local tools + for rec in result.tool_executions: + print(f" {'[OK]' if rec.success else '[ERR]'} {rec.tool_name} ({rec.latency_ms:.0f}ms)") + + # 6. Disconnect + await store.disconnect() + +asyncio.run(main()) +``` + +## Using the Agent's built-in MCP support + +For simpler setups, pass MCP server refs directly to the agent: + +```python +from afk.agents import Agent + +# The agent connects to MCP servers automatically during startup +agent = Agent( + name="connected-agent", + model="gpt-4.1-mini", + instructions="Use available tools to help the user.", + mcp_servers=[ + "https://tools.example.com:3001", # Simple URL + "search=https://search.internal:3002", # Named server + {"url": "https://db.internal:3003", "auth": "token-xyz"}, # With auth + ], + enable_mcp_tools=True, # Default: True +) +``` + +## Mixing local and MCP tools + +Combine your own tools with external MCP tools: + +```python +from pydantic import BaseModel +from afk.agents import Agent +from afk.tools import tool +from afk.mcp import MCPStore + +class SummaryArgs(BaseModel): + text: str + max_words: int = 100 + +@tool(args_model=SummaryArgs, name="summarize", description="Summarize text concisely.") +def summarize(args: SummaryArgs) -> dict: + # Your local summarization logic + return {"summary": args.text[:args.max_words * 5] + "..."} + + +async def build_agent(): + # Get external tools + store = MCPStore() + await store.connect("https://tools.example.com:3001") + mcp_tools = await store.list_tools() + + # Combine local + external tools + agent = Agent( + name="hybrid-agent", + model="gpt-4.1-mini", + instructions="Use search tools for research and summarize for concise output.", + tools=[summarize] + mcp_tools, # ← Mix freely + ) + return agent +``` + +## Security with MCP tools + +Apply policy rules to MCP-sourced tools just like local tools: + +```python +from afk.agents import PolicyEngine, PolicyRule +from afk.core import Runner + +policy = PolicyEngine(rules=[ + PolicyRule( + rule_id="gate-mcp-writes", + condition=lambda e: e.tool_name and "write" in e.tool_name, + action="request_approval", + reason="MCP write operations need human approval", + ), +]) + +runner = Runner(policy_engine=policy) +``` + + + **MCP tools are transparent.** Once attached to an agent, they go through the + same validation, policy gates, sanitization, and telemetry as local tools. The + agent doesn't know whether a tool is local or remote. + + +## What to read next + +- [MCP Server](/library/mcp-server) — Expose your own tools via MCP, plus authentication and rate limiting. +- [Tools](/library/tools) — Full tool system architecture. +- [Snippet 06: Tool Security](/library/snippets/06_tool_registry_security) — Policy gates and sandbox profiles. +``` + +## 13_multi_model_fallback + +Source: `docs/library/snippets/13_multi_model_fallback.mdx` + +```python +--- +title: "13: Multi-Model Fallback" +description: Configure fallback model chains for LLM resilience and cost optimization. +--- + +## What this snippet demonstrates + +LLM API calls fail — rate limits, outages, timeouts. AFK's `fallback_model_chain` lets you define an ordered list of models to try when the primary model fails. This snippet shows how to configure fallback chains for resilience, cost optimization, and provider diversification. + +## Basic fallback chain + +```python +from afk.agents import Agent, FailSafeConfig + +agent = Agent( + name="resilient-agent", + model="gpt-4.1", # Primary model + instructions="Be helpful and thorough.", + fail_safe=FailSafeConfig( + # Fallback chain: try these models in order if the primary fails + fallback_model_chain=[ + "gpt-4.1-mini", # First fallback: cheaper, faster + "gpt-4.1-nano", # Last resort: fastest, cheapest + ], + + # When LLM calls fail, retry then degrade + llm_failure_policy="retry_then_degrade", + + # Cost ceiling still applies across all models + max_total_cost_usd=1.00, + ), +) +``` + +When `gpt-4.1` fails (timeout, rate limit, outage): + +1. AFK retries with the primary model (controlled by retry policy) +2. If retries exhaust, it falls through to `gpt-4.1-mini` +3. If that also fails, it tries `gpt-4.1-nano` +4. If all models fail, the `llm_failure_policy` determines the outcome + +## Cost-optimized fallback + +Use expensive models only when needed: + +```python +from afk.agents import Agent, FailSafeConfig +from afk.core import Runner + +# Start cheap, escalate if quality is insufficient +simple_agent = Agent( + name="classifier", + model="gpt-4.1-nano", # Start with cheapest + instructions=""" + Classify the support ticket. Output exactly one label: + billing, technical, account, other. + """, + fail_safe=FailSafeConfig( + fallback_model_chain=["gpt-4.1-mini", "gpt-4.1"], + max_total_cost_usd=0.05, + ), +) + +# Complex tasks get the big model with fallbacks +analysis_agent = Agent( + name="analyst", + model="gpt-4.1", # Start with most capable + instructions=""" + Provide detailed technical analysis with code examples. + Be thorough and precise. + """, + fail_safe=FailSafeConfig( + fallback_model_chain=["gpt-4.1-mini"], + llm_failure_policy="retry_then_degrade", + max_total_cost_usd=2.00, + ), +) + +runner = Runner() + +# Simple task -> cheap model handles it +r1 = runner.run_sync(simple_agent, user_message="I can't log in") +print(f"Classification: {r1.final_text} (${r1.total_cost_usd or 0:.4f})") + +# Complex task -> powerful model with safety net +r2 = runner.run_sync(analysis_agent, user_message="Analyze Python's asyncio event loop") +print(f"Analysis: {r2.final_text[:100]}... (${r2.total_cost_usd or 0:.4f})") +``` + +## Circuit breaker integration + +AFK's built-in circuit breaker works with fallback chains. When a model triggers too many failures, the breaker opens and the system skips straight to the next fallback: + +```python +agent = Agent( + name="breaker-demo", + model="gpt-4.1", + instructions="...", + fail_safe=FailSafeConfig( + fallback_model_chain=["gpt-4.1-mini", "gpt-4.1-nano"], + + # Circuit breaker settings + breaker_failure_threshold=5, # Open after 5 consecutive failures + breaker_cooldown_s=30.0, # Wait 30s before retrying the model + + # Failure handling + llm_failure_policy="retry_then_degrade", + max_total_cost_usd=1.00, + ), +) +``` + +```mermaid +flowchart LR + A["gpt-4.1 fails 5x"] --> B["Circuit opens"] + B --> C["Skip to gpt-4.1-mini"] + C --> D["30s cooldown"] + D --> E["gpt-4.1 retried"] + E -->|"succeeds"| F["Circuit closes"] + E -->|"fails again"| B +``` + +## Multi-agent with different model tiers + +Use different model tiers for different specialists: + +```python +from afk.agents import Agent, FailSafeConfig + +# Cheap model for simple classification +router = Agent( + name="router", + model="gpt-4.1-nano", + instructions="Route to the correct specialist.", + fail_safe=FailSafeConfig(fallback_model_chain=["gpt-4.1-mini"]), + subagents=[ + # Powerful model for complex analysis + Agent( + name="analyst", + model="gpt-4.1", + instructions="Provide deep technical analysis.", + fail_safe=FailSafeConfig( + fallback_model_chain=["gpt-4.1-mini"], + max_total_cost_usd=1.00, + ), + ), + # Mid-tier model for summarization + Agent( + name="summarizer", + model="gpt-4.1-mini", + instructions="Summarize findings concisely.", + fail_safe=FailSafeConfig( + fallback_model_chain=["gpt-4.1-nano"], + max_total_cost_usd=0.25, + ), + ), + ], +) +``` + +## Inspecting which model was used + +After a run, check the result metadata and usage aggregate: + +```python +result = runner.run_sync(agent, user_message="Analyze this...") + +print(f"State: {result.state}") +print(f"Requested model: {result.requested_model}") +print(f"Normalized model: {result.normalized_model}") +print(f"Provider: {result.provider_adapter}") +print(f"Total tokens: {result.usage_aggregate.total_tokens}") +print(f"Total cost: ${result.total_cost_usd or 0:.4f}") +``` + +## Recommendations + +| Scenario | Primary Model | Fallback Chain | +| ------------------------ | -------------- | ------------------------------- | +| **Classification** | `gpt-4.1-nano` | `gpt-4.1-mini` | +| **General chat** | `gpt-4.1-mini` | `gpt-4.1-nano` | +| **Complex analysis** | `gpt-4.1` | `gpt-4.1-mini` → `gpt-4.1-nano` | +| **Code generation** | `gpt-4.1` | `gpt-4.1-mini` | +| **Cost-sensitive batch** | `gpt-4.1-nano` | _(none)_ | + +## What to read next + +- [Configuration Reference](/library/configuration-reference#failsafeconfig) — Full FailSafeConfig fields including circuit breaker settings. +- [Failure Policy Matrix](/library/failure-policy-matrix) — How failures flow through the system. +- [Snippet 11: Cost Monitoring](/library/snippets/11_cost_monitoring) — Track and control costs in real time. +``` + +## 14_production_client + +Source: `docs/library/snippets/14_production_client.mdx` + +```python +--- +title: "14: Client Timeouts and Redis Pooling" +description: Configure LLM client timeouts and Redis connection pooling. +--- + +## What this snippet demonstrates + +This snippet shows how to configure: +1. **Timeout middleware** to bound slow provider calls +2. **Redis connection pooling** for shared cache or memory connections +3. **Shutdown handling** so runners and Redis pools close cleanly + +## Timeout middleware + +Apply per-request timeouts to prevent runaway LLM calls: + +```python +import asyncio +from afk.llms import LLMBuilder, LLMRequest +from afk.llms.middleware import MiddlewareStack +from afk.llms.middleware.timeout import ( + TimeoutMiddleware, + EmbedTimeoutMiddleware, + StreamTimeoutMiddleware, + TimeoutConfig, +) + +config = TimeoutConfig( + default_timeout_s=30.0, + chat_timeout_s=60.0, + embed_timeout_s=15.0, + stream_timeout_s=45.0, +) + +stack = MiddlewareStack( + chat=[TimeoutMiddleware(config)], + embed=[EmbedTimeoutMiddleware(config)], + stream=[StreamTimeoutMiddleware(config)], +) + +production_client = ( + LLMBuilder() + .provider("openai") + .model("gpt-4.1-mini") + .profile("production") + .with_middlewares(stack) + .build() +) +``` + +### Per-request timeout override + +```python +from afk.llms import TimeoutPolicy + +req = LLMRequest( + model="gpt-4.1-mini", + messages=[...], + timeout_policy=TimeoutPolicy(request_timeout_s=120.0), # Override default +) + +response = await production_client.chat(req) +``` + +## Redis connection pooling + +For Redis deployments, use connection pooling instead of creating a new client per request: + +```python +from afk.llms.cache.redis_pool import ( + get_redis_pool, + PoolConfig, + close_all_pools, +) + +async def setup_redis_pool(): + pool = await get_redis_pool( + "redis://localhost:6379/0", + config=PoolConfig( + max_connections=50, + max_idle_connections=10, + socket_timeout=5.0, + socket_connect_timeout=5.0, + socket_keepalive=True, + health_check_interval_s=30.0, + ), + ) + + if await pool.health_check(): + print("Redis connection pool healthy") + + return pool +``` + +### Using with memory store + +```python +import asyncio +from afk.memory.adapters.redis import RedisMemoryStore +from afk.core import Runner + +async def main(): + pool = await get_redis_pool( + "redis://localhost:6379/0", + config=PoolConfig(max_connections=50), + ) + + runner = Runner( + memory_store=RedisMemoryStore(url="redis://localhost:6379/0"), + ) + + result = await runner.run(agent, user_message="Hello") + print(result.final_text) + + await runner.close() + await close_all_pools() + +asyncio.run(main()) +``` + +## Full example + +```python +import asyncio +from afk.llms import LLMBuilder +from afk.llms.middleware import MiddlewareStack +from afk.llms.middleware.timeout import ( + TimeoutMiddleware, + TimeoutConfig, +) +from afk.llms.cache.redis_pool import ( + get_redis_pool, + PoolConfig, + close_all_pools, +) +from afk.memory.adapters.redis import RedisMemoryStore +from afk.core import Runner +from afk.agents import Agent + +class ProductionSetup: + def __init__(self): + self.llm_client = None + self.runner = None + self.pool = None + + async def __aenter__(self): + pool_config = PoolConfig( + max_connections=50, + max_idle_connections=10, + socket_timeout=5.0, + socket_connect_timeout=5.0, + ) + self.pool = await get_redis_pool( + "redis://localhost:6379/0", + config=pool_config, + ) + + timeout_config = TimeoutConfig( + default_timeout_s=30.0, + chat_timeout_s=60.0, + ) + stack = MiddlewareStack( + chat=[TimeoutMiddleware(timeout_config)], + ) + + self.llm_client = ( + LLMBuilder() + .provider("openai") + .model("gpt-4.1-mini") + .profile("production") + .with_middlewares(stack) + .build() + ) + + self.runner = Runner( + memory_store=RedisMemoryStore(url="redis://localhost:6379/0"), + ) + + return self + + async def __aexit__(self, exc_type, exc_val, exc_tb): + if self.runner: + await self.runner.close() + await close_all_pools() + return False + + +async def main(): + agent = Agent( + name="assistant", + model="gpt-4.1-mini", + instructions="You are a helpful assistant.", + ) + + async with ProductionSetup() as setup: + result = await setup.runner.run( + agent, + user_message="Hello, world!", + ) + print(result.final_text) + +asyncio.run(main()) +``` + +## Configuration reference + +### TimeoutConfig + +| Parameter | Default | Description | +| --- | --- | --- | +| `default_timeout_s` | 30.0 | Default timeout for all operations | +| `chat_timeout_s` | None | Specific timeout for chat requests | +| `embed_timeout_s` | None | Specific timeout for embeddings | +| `stream_timeout_s` | None | Specific timeout for streaming | + +### PoolConfig + +| Parameter | Default | Description | +| --- | --- | --- | +| `max_connections` | 50 | Maximum total connections | +| `max_idle_connections` | 10 | Maximum idle connections | +| `socket_timeout` | 5.0 | Socket read/write timeout | +| `socket_connect_timeout` | 5.0 | Connection establishment timeout | +| `socket_keepalive` | False | Enable TCP keepalive | +| `health_check_interval_s` | 30.0 | Interval for health checks | + +## What to read next + +- [LLM Control & Session](/llms/control-and-session) -- Retry, caching, and circuit breaker policies +- [Deployment Guide](/library/deployment) -- Production deployment with Docker and Kubernetes +- [Performance Guide](/library/performance) -- Optimize latency and throughput +``` diff --git a/skills/afk-maintainer/scripts/search_afk_docs.py b/skills/afk-maintainer/scripts/search_afk_docs.py old mode 100644 new mode 100755 index 9d05688..75684e4 --- a/skills/afk-maintainer/scripts/search_afk_docs.py +++ b/skills/afk-maintainer/scripts/search_afk_docs.py @@ -1,20 +1,8 @@ #!/usr/bin/env python3 -"""Search bundled AFK docs index for the maintainer skill. - -Usage: - python scripts/search_afk_docs.py "query terms" - python scripts/search_afk_docs.py --format json "circuit breaker" - python scripts/search_afk_docs.py --top-k 5 "memory compaction" - -Examples: - scripts/search_afk_docs.py "event loop run_sync" - scripts/search_afk_docs.py --format json "tool middleware" -""" from __future__ import annotations import argparse import json -import sys from pathlib import Path @@ -30,108 +18,54 @@ def load_records(path: Path) -> list[dict]: def score(query_terms: list[str], doc: dict) -> int: - text = ( - doc.get("title", "") - + " " - + doc.get("description", "") - + " " - + doc.get("content", "") - ).lower() + text = (doc.get("title", "") + " " + doc.get("description", "") + " " + doc.get("content", "")).lower() return sum(text.count(term) for term in query_terms) def main() -> int: - parser = argparse.ArgumentParser( - description="Search bundled AFK docs index for the maintainer skill.", - epilog="Examples:\n" - ' %(prog)s "event loop run_sync"\n' - ' %(prog)s --format json "tool middleware"\n' - ' %(prog)s --top-k 5 "memory compaction"', - formatter_class=argparse.RawDescriptionHelpFormatter, - ) - parser.add_argument("query", help="Search query (space-separated terms)") - parser.add_argument( - "--top-k", - type=int, - default=8, - help="Number of results to return (default: 8)", - ) - parser.add_argument( - "--format", - choices=["text", "json"], - default="text", - help="Output format: text (human-readable) or json (structured)", - ) + parser = argparse.ArgumentParser(description="Search bundled AFK docs index for this skill.") + parser.add_argument("query", help="Search query") + parser.add_argument("--top-k", type=int, default=8) parser.add_argument( "--index", - default=str( - Path(__file__).resolve().parent.parent - / "references" - / "afk-docs" - / "docs-index.jsonl" - ), + default=str(Path(__file__).resolve().parent.parent / "references" / "afk-docs" / "docs-index.jsonl"), help="Path to docs-index.jsonl", ) args = parser.parse_args() - query_terms = [term.lower() for term in args.query.split() if term.strip()] + query_terms = [t.lower() for t in args.query.split() if t.strip()] if not query_terms: - print("Error: Empty query. Provide one or more search terms.", file=sys.stderr) + print("Empty query.") return 1 idx_path = Path(args.index) if not idx_path.exists(): - print(f"Error: Index not found: {idx_path}", file=sys.stderr) - print("Run the index builder first or check the --index path.", file=sys.stderr) + print(f"Index not found: {idx_path}") return 1 rows = load_records(idx_path) ranked = sorted( - ((score(query_terms, row), row) for row in rows), - key=lambda item: item[0], + ((score(query_terms, r), r) for r in rows), + key=lambda x: x[0], reverse=True, ) - - results = [] - for score_value, row in ranked: - if score_value <= 0: + shown = 0 + for s, row in ranked: + if s <= 0: continue - results.append( - { - "id": row.get("id", ""), - "title": row.get("title", ""), - "url": row.get("url", ""), - "path": row.get("path", ""), - "description": row.get("description", ""), - "score": score_value, - } - ) - if len(results) >= args.top_k: + print(f"[{row['id']}] {row.get('title', '')}") + print(f" url: {row.get('url', '')}") + print(f" path: {row.get('path', '')}") + desc = row.get("description", "") + if desc: + print(f" desc: {desc}") + print() + shown += 1 + if shown >= args.top_k: break - if not results: - if args.format == "json": - print(json.dumps({"query": args.query, "results": [], "count": 0})) - else: - print("No matches.") - return 0 - - if args.format == "json": - print( - json.dumps( - {"query": args.query, "results": results, "count": len(results)}, - ensure_ascii=False, - ) - ) - else: - for row in results: - print(f"[{row['id']}] {row['title']}") - print(f" url: {row['url']}") - print(f" path: {row['path']}") - if row["description"]: - print(f" desc: {row['description']}") - print() - + if shown == 0: + print("No matches.") return 0 diff --git a/skills/examples.md b/skills/examples.md new file mode 100644 index 0000000..38db749 --- /dev/null +++ b/skills/examples.md @@ -0,0 +1,2192 @@ +# AFK Examples + +Merged from `docs/library/snippets/*.mdx` for agent-friendly context loading. + +Generated at: 2026-05-31T13:25:20.451068+00:00 + +## 01_minimal_chat_agent + +Source: `docs/library/snippets/01_minimal_chat_agent.mdx` + +```python +--- +title: "01: Minimal Chat Agent" +description: Smallest synchronous AFK agent run. +--- + +This is the simplest possible AFK agent. It demonstrates the three core concepts you need to get started: defining an `Agent` with a model and instructions, creating a `Runner` to execute it, and reading the result from `final_text`. + +If you are new to AFK, start here. Every other example builds on this foundation. + +```python +from afk.agents import Agent +from afk.core import Runner + +agent = Agent(name="chat", model="gpt-4.1-mini", instructions="Answer directly with concrete detail.") +runner = Runner() +result = runner.run_sync(agent, user_message="Define error budget in SRE.") +print(result.final_text) +``` + +## Line-by-line explanation + +**`Agent(...)`** defines the agent's identity and behavior. The `name` is used for telemetry and logging. The `model` specifies which LLM to use. The `instructions` become the system prompt that guides the model's behavior. + +**`Runner()`** creates the execution engine. With no arguments, it uses in-memory defaults: headless interaction mode, no telemetry sink, and no policy engine. This is the fastest way to get started during development. + +**`runner.run_sync(...)`** executes the agent synchronously, blocking until the run completes. Under the hood, this creates an async event loop, runs the agent through the full lifecycle (LLM call, optional tool execution, optional subagent delegation), and returns the terminal `AgentResult`. The `user_message` is the initial prompt sent to the model. + +**`result.final_text`** contains the model's final text response. This is the primary output field on `AgentResult`. Always use `final_text` (not `output_text`) to access the agent's response. + +## What AgentResult contains + +The `AgentResult` dataclass returned by `run_sync` includes: + +| Field | Type | Description | +| --- | --- | --- | +| `final_text` | `str` | The agent's final text response. | +| `state` | `str` | Terminal state: `"completed"`, `"failed"`, `"cancelled"`, or `"degraded"`. | +| `run_id` | `str` | Unique identifier for this run. | +| `thread_id` | `str` | Thread identifier for memory continuity across runs. | +| `tool_executions` | `list` | Records of all tool calls made during the run. | +| `subagent_executions` | `list` | Records of all subagent invocations. | +| `usage` | `UsageAggregate` | Token usage and cost estimates across all LLM calls. | + +## Expected behavior + +When you run this example, the runner makes a single LLM call to `gpt-4.1-mini` with the system instructions and user message. Since no tools are registered, the model responds with text only. The run completes in one step with `state="completed"`, and `final_text` contains a concise definition of error budgets in SRE. + +No network calls, API keys, or external services are required beyond access to the specified LLM provider (configured via environment variables like `OPENAI_API_KEY`). +``` + +## 02_policy_with_hitl + +Source: `docs/library/snippets/02_policy_with_hitl.mdx` + +```python +--- +title: "02: Policy with Human Interaction" +description: Route sensitive actions through policy and human approval. +--- + +Human-in-the-loop (HITL) is the pattern where the agent pauses execution to request approval or input from a human operator before proceeding with a sensitive action. This is critical for any agent that can take destructive or irreversible actions -- deleting data, modifying production systems, sending communications, or spending money. + +AFK implements HITL through two components: a `PolicyEngine` that decides which actions require human intervention, and an `InteractionProvider` that routes the approval request to a human and returns their decision. + +## Basic example + +```python +from afk.agents import Agent, PolicyEngine, PolicyRule +from afk.core import Runner, RunnerConfig + +# Define a policy that gates destructive operations +policy = PolicyEngine(rules=[ + PolicyRule( + rule_id="gate-destructive-ops", + description="Require human approval for any destructive operation", + condition=lambda event: ( + event.tool_name is not None + and any(keyword in event.tool_name.lower() for keyword in ["delete", "drop", "remove"]) + ), + action="request_approval", + reason="Destructive operations require human approval before execution.", + ), +]) + +agent = Agent( + name="change-manager", + model="gpt-4.1-mini", + instructions="You manage infrastructure changes. Always use available tools.", +) + +# Headless mode: approval requests are auto-resolved using approval_fallback +runner = Runner( + policy_engine=policy, + config=RunnerConfig( + interaction_mode="headless", + approval_fallback="deny", # Auto-deny in headless mode + ), +) +result = runner.run_sync(agent, user_message="Drop old production tables") +print(f"State: {result.state}") +# In headless mode with approval_fallback="deny", the destructive action is blocked. +``` + +## Interactive mode with an InteractionProvider + +In production, you typically want a real human to review approval requests. Use `interaction_mode="interactive"` with a custom `InteractionProvider`: + +```python +from afk.core import Runner, RunnerConfig, InMemoryInteractiveProvider +from afk.agents import ApprovalDecision + +# In-memory provider for testing (simulates human approval) +provider = InMemoryInteractiveProvider() + +runner = Runner( + policy_engine=policy, + interaction_provider=provider, + config=RunnerConfig( + interaction_mode="interactive", + approval_timeout_s=300.0, # Wait up to 5 minutes for human response + ), +) + +# In a real application, a separate process or UI would call: +# provider.resolve_approval(request_id, ApprovalDecision(kind="allow")) +``` + +## Headless vs interactive modes + +AFK supports three interaction modes, configured via `RunnerConfig.interaction_mode`: + +| Mode | Behavior | When to Use | +| --- | --- | --- | +| `"headless"` | Approval requests are auto-resolved using `approval_fallback` (default: `"deny"`). No human is involved. | CI/CD pipelines, batch processing, testing, automated workflows where no human is available. | +| `"interactive"` | Approval requests are routed to the configured `InteractionProvider`. The runner pauses until a decision is returned or `approval_timeout_s` expires. | Production applications with human operators, chat UIs with approval buttons, Slack-based approval workflows. | +| `"external"` | Similar to interactive, but designed for use in external orchestration systems where the approval mechanism is managed outside AFK. | Enterprise systems with external approval platforms. | + +## How the policy flow works + +1. The agent calls a tool (e.g., `drop_table`). +2. Before executing, the runner sends a `PolicyEvent` to the `PolicyEngine`. +3. The engine evaluates all rules. If a rule matches, it returns a `PolicyDecision` with the configured action. +4. If the action is `request_approval`: + - In **headless** mode: the runner auto-resolves using `approval_fallback`. + - In **interactive** mode: the runner creates an `ApprovalRequest` and sends it to the `InteractionProvider`. Execution pauses until the provider returns an `ApprovalDecision`. +5. If approved (`kind="allow"`): the tool executes normally. +6. If denied (`kind="deny"`): the tool execution is skipped and the model receives a denial message as the tool result. +7. A `policy_decision` event is emitted in the run event stream for audit purposes. + +## Policy decision actions + +| Action | Effect | +| --- | --- | +| `"allow"` | Proceed with execution. No human interaction needed. | +| `"deny"` | Block execution immediately. The denial reason is reported to the model. | +| `"request_approval"` | Pause and request human approval through the `InteractionProvider`. | +| `"request_user_input"` | Pause and request freeform text input from a human operator. | +``` + +## 03_subagents_with_router + +Source: `docs/library/snippets/03_subagents_with_router.mdx` + +```python +--- +title: "03: Subagents with Router" +description: Delegate workload to specialist subagents and merge outputs. +--- + +When a task is too complex for a single agent, AFK supports delegating subtasks to specialist subagents. The coordinator (or "lead") agent decides what to delegate, and the runner handles dispatching work to subagents, collecting their results, and feeding those results back to the coordinator for synthesis. + +This pattern is useful for incident response, research workflows, content pipelines, and any scenario where different aspects of a task require distinct expertise or instructions. + +## Delegation flow + +```mermaid +flowchart TD + User[User message] --> Lead[Lead agent] + Lead --> Triage[Triage subagent] + Lead --> Analysis[Analysis subagent] + Lead --> Comms[Comms subagent] + Triage --> Lead + Analysis --> Lead + Comms --> Lead + Lead --> Result[Synthesized final_text] +``` + +## Example + +```python +from afk.agents import Agent +from afk.core import Runner + +# Define specialist subagents +triage = Agent( + name="triage", + model="gpt-4.1-mini", + instructions="Classify incident severity as SEV1, SEV2, SEV3, or SEV4 based on the description.", +) +analysis = Agent( + name="analysis", + model="gpt-4.1-mini", + instructions="Identify the most likely root causes for the described incident.", +) +comms = Agent( + name="comms", + model="gpt-4.1-mini", + instructions="Draft a concise stakeholder update email summarizing the incident and current status.", +) + +# Define the coordinator agent +lead = Agent( + name="lead", + model="gpt-4.1-mini", + instructions="Delegate to specialists and synthesize their outputs into a final response.", + subagents=[triage, analysis, comms], +) + +runner = Runner() +result = runner.run_sync(lead, user_message="Investigate API latency spike and draft update") +print(result.final_text) +``` + +## How the coordinator pattern works + +1. The **lead agent** receives the user message and decides how to delegate. It can invoke subagents through tool-like calls that the runner intercepts. + +2. The **runner** dispatches each subagent invocation as a separate run. Subagents execute independently with their own instructions and model configuration. The runner manages concurrency, timeout, and failure handling for each subagent. + +3. **Subagent results** are returned to the lead agent as execution records. Each record contains the subagent's `output_text` and optional error information. + +4. The **lead agent** receives all subagent outputs and synthesizes them into a unified response. This final synthesis step is what produces the coordinator's `final_text`. + +## What subagent_executions contains + +The `AgentResult` returned by the lead agent includes a `subagent_executions` list. Each entry is a `SubagentExecutionRecord` with: + +| Field | Type | Description | +| --- | --- | --- | +| `subagent_name` | `str` | Name of the subagent that was invoked. | +| `success` | `bool` | Whether the subagent completed successfully. | +| `output_text` | `str or None` | The subagent's response text, if it completed. | +| `latency_ms` | `float` | Wall-clock execution time in milliseconds. | +| `error` | `str or None` | Error message if the subagent failed. | + +You can inspect these records to understand what each subagent contributed: + +```python +result = runner.run_sync(lead, user_message="Investigate API latency spike") + +for execution in result.subagent_executions: + status = "OK" if execution.success else "FAILED" + text = execution.output_text or "" + print(f" [{status}] {execution.subagent_name}: {text[:80]}...") +``` + +## Subagent failure handling + +By default, subagent failure policy is `continue`. You can configure stricter or more resilient behavior using `FailSafeConfig`: + +```python +from afk.agents import FailSafeConfig + +lead = Agent( + name="lead", + model="gpt-4.1-mini", + instructions="Delegate to specialists. If any specialist fails, work with available results.", + subagents=[triage, analysis, comms], + fail_safe=FailSafeConfig( + subagent_failure_policy="retry_then_degrade", # Continue with partial results + max_subagent_depth=3, # Prevent deep recursion + ), +) +``` + +With `subagent_failure_policy="retry_then_degrade"`, the lead agent receives error information for failed subagents alongside successful results and can produce a best-effort synthesis. +``` + +## 04_resume_and_compact + +Source: `docs/library/snippets/04_resume_and_compact.mdx` + +```python +--- +title: "04: Resume and Compact" +description: Resume interrupted runs from their last checkpoint and compact retained thread memory to control storage growth. +--- + +## What this snippet demonstrates + +Agent runs can be interrupted by timeouts, cancellations, infrastructure failures, or intentional pauses (such as waiting for human approval). When a run is interrupted, the runner persists a checkpoint containing the run's state at the point of interruption. The `resume()` method picks up from that checkpoint, restoring the conversation history, tool execution records, and step counter so the agent continues where it left off rather than starting from scratch. + +Over time, long-running threads accumulate checkpoint records, event logs, and state entries. The `compact_thread()` method prunes old records according to retention policies, keeping storage bounded without losing the data needed for active runs. + +## Resuming an interrupted run + +```python +import asyncio +from afk.agents import Agent +from afk.core import Runner, RunnerConfig + +agent = Agent( + name="research-bot", + model="gpt-4.1-mini", + instructions="You help users research topics thoroughly.", +) + +runner = Runner(config=RunnerConfig(interaction_mode="headless")) + + +async def main(): + # Start a run that might be interrupted + result = await runner.run( + agent, + user_message="Research the history of distributed systems.", + thread_id="thread_research_001", + ) + + # Save these identifiers for later resume + run_id = result.run_id + thread_id = result.thread_id + print(f"Run completed: state={result.state}") + + # Later, resume from the checkpoint if the run was interrupted. + # The runner loads the latest checkpoint for this run_id + thread_id pair, + # restores the conversation state, and continues execution. + resumed_result = await runner.resume( + agent, + run_id=run_id, + thread_id=thread_id, + ) + print(f"Resumed run: state={resumed_result.state}") + print(resumed_result.final_text) + + +asyncio.run(main()) +``` + +### How resume works internally + +The runner follows this sequence when `resume()` is called: + +1. **Checkpoint lookup** -- The runner queries the memory store for the latest checkpoint matching the given `run_id` and `thread_id`. If no checkpoint exists, it raises `AgentCheckpointCorruptionError`. + +2. **Terminal check** -- If the checkpoint already contains a terminal result (the run completed before the resume was requested), the runner returns that result immediately without re-executing. + +3. **Snapshot restoration** -- The runner loads the runtime snapshot from the checkpoint, which includes the conversation message history, step counter, tool execution records, and any pending subagent state. + +4. **Continued execution** -- The runner calls `run_handle()` internally with the restored snapshot, continuing the step loop from where it was interrupted. + +### Resume method signature + +```python +await runner.resume( + agent, # Agent definition (must match the original run's agent) + run_id="run_123", # The run_id from the interrupted run + thread_id="th_abc", # The thread_id from the interrupted run + context=None, # Optional context overlay for resumed execution +) +``` + +| Parameter | Type | Description | +| --- | --- | --- | +| `agent` | `BaseAgent` | The agent definition used for continued execution. Must match the agent that started the original run. | +| `run_id` | `str` | The unique run identifier from the interrupted run. Found on `result.run_id`. | +| `thread_id` | `str` | The thread identifier from the interrupted run. Found on `result.thread_id`. | +| `context` | `dict` or `None` | Optional context overlay. Merged with the original run context. | + +## Compacting thread memory + +```python +import asyncio +from afk.core import Runner, RunnerConfig +from afk.memory import RetentionPolicy, StateRetentionPolicy + +runner = Runner(config=RunnerConfig(interaction_mode="headless")) + + +async def compact(): + compaction = await runner.compact_thread( + thread_id="thread_research_001", + event_policy=RetentionPolicy(max_age_ms=86_400_000), # Keep last 24 hours + state_policy=StateRetentionPolicy(max_entries=50), # Keep last 50 state entries + ) + print(f"Events removed: {compaction.events_removed}") + print(f"States removed: {compaction.states_removed}") + + +asyncio.run(compact()) +``` + +### How compaction works + +Compaction operates on two dimensions of stored data: + +- **Event retention** -- Controlled by `RetentionPolicy`. Removes event records older than `max_age_ms`. Events are the raw telemetry log entries (LLM calls, tool executions, state transitions) that accumulate over the lifetime of a thread. + +- **State retention** -- Controlled by `StateRetentionPolicy`. Removes state entries that exceed `max_entries`, keeping only the most recent ones. State entries include checkpoint snapshots, conversation summaries, and key-value metadata. + +Both policies are optional. If you omit a policy, that dimension is not compacted. The method returns a `MemoryCompactionResult` with counts of removed records so you can log or alert on compaction activity. + +### When to compact + +- **After long conversations** -- Threads with hundreds of turns accumulate large checkpoint histories. Compact after the conversation ends or reaches a natural break point. +- **On a schedule** -- Run compaction as a background task (e.g., hourly or daily) for threads that are still active but have grown large. +- **Before resume** -- If you know a thread has extensive history, compacting before resume reduces the data the runner needs to load. + +## Error handling + +```python +from afk.agents.errors import AgentCheckpointCorruptionError, AgentConfigurationError + +try: + result = await runner.resume(agent, run_id="invalid", thread_id="missing") +except AgentCheckpointCorruptionError: + # No checkpoint found for this run_id + thread_id combination. + # This means either the run_id is wrong, the checkpoint was compacted away, + # or the memory store was cleared. + print("No checkpoint found -- cannot resume.") +except AgentConfigurationError: + # run_id or thread_id is empty or invalid + print("Invalid run_id or thread_id.") +``` + +## What to read next + +- [Memory](/library/memory) -- Full memory architecture, checkpoint schema, and retention policies. +- [Core Runner](/library/core-runner) -- Step loop lifecycle, state machine, and all runner API methods. +- [Checkpoint Schema](/library/checkpoint-schema) -- Exact structure of checkpoint records stored in memory. +``` + +## 05_direct_llm_structured_output + +Source: `docs/library/snippets/05_direct_llm_structured_output.mdx` + +```python +--- +title: "05: Direct LLM Structured Output" +description: Use afk.llms with schema-validated responses. +--- + +Not every use case needs the full agent loop. Sometimes you want to call an LLM directly with a specific prompt and get back a structured, schema-validated response. AFK's `LLMBuilder` provides a fluent API for constructing LLM clients that can return Pydantic-validated objects directly, without the overhead of the agent run lifecycle. + +Use this pattern for classification, extraction, summarization, and any scenario where you want a single LLM call with a guaranteed output schema. + +## Example + +```python +from pydantic import BaseModel +from afk.llms import LLMBuilder +from afk.llms.types import LLMRequest, Message + +# Define the output schema as a Pydantic model +class Summary(BaseModel): + title: str + bullets: list[str] + +# Build an LLM client using the fluent builder +client = LLMBuilder().provider("openai").model("gpt-4.1-mini").profile("production").build() + +# Make a structured request +resp = await client.chat( + LLMRequest(messages=[Message(role="user", content="Summarize incident timeline")]), + response_model=Summary, +) +print(resp.structured_response) # {"title": "...", "bullets": ["...", "..."]} +print(resp.text) # The raw text response +``` + +## The builder pattern + +`LLMBuilder` uses a fluent (method-chaining) API to construct an LLM client with the exact configuration you need: + +```python +client = ( + LLMBuilder() + .provider("openai") # Which LLM provider to use + .model("gpt-4.1-mini") # Which model + .profile("production") # Apply a preset profile (retry, timeout, etc.) + .build() # Return the configured LLMClient +) +``` + +Each method returns the builder instance, so calls can be chained. The `.build()` call at the end constructs the final `LLMClient` with all specified settings. + +Available builder methods: + +| Method | Purpose | +| --- | --- | +| `.provider(name)` | Set the LLM provider (`"openai"`, `"litellm"`, `"anthropic_agent"`). | +| `.model(name)` | Set the model identifier. | +| `.profile(name)` | Apply a named configuration profile (`"production"`, `"development"`, etc.). | +| `.settings(settings)` | Replace the loaded `LLMSettings`. | +| `.with_middlewares(stack)` | Attach chat, stream, or embedding middleware. | +| `.with_observers(observers)` | Attach LLM lifecycle observers. | +| `.with_cache(cache_backend)` | Attach a cache backend instance or registered backend id. | +| `.with_router(router)` | Attach a router instance or registered router id. | +| `.build()` | Construct and return the `LLMClient`. | + +Sampling controls are request fields, not builder methods. Set them on `LLMRequest`, for example `LLMRequest(..., temperature=0.0, max_tokens=1000)`. + +## Structured output with Pydantic + +When you pass `response_model=YourModel` to `client.chat()`, the client instructs the LLM to return output that conforms to the model's JSON schema. The response is parsed and validated against the Pydantic model: + +- If the LLM returns valid structured output, `resp.structured_response` contains the parsed dictionary and `resp.text` contains the raw response. +- If the LLM returns output that does not match the schema, a `LLMInvalidResponseError` is raised. + +This is powered by the LLM provider's native structured output support (e.g., OpenAI's `response_format` parameter) when available, with a fallback to prompt-based JSON extraction. + +## When to use LLMBuilder vs Runner + +| Use Case | Approach | +| --- | --- | +| Single LLM call, no tools, no memory | `LLMBuilder` -- simpler, faster, no lifecycle overhead. | +| Structured extraction or classification | `LLMBuilder` with `response_model`. | +| Multi-turn conversation with tools | `Runner` -- provides the full agent loop with tool execution, policy, and memory. | +| Subagent delegation | `Runner` -- only the runner supports subagent dispatch. | +| Event streaming to a UI | `Runner` with `run_stream()`. | +| Eval-driven development | `Runner` -- evals require the full `AgentResult` lifecycle. | + +Use `LLMBuilder` when you want precision and control over a single LLM interaction. Use `Runner` when you need the full agentic lifecycle. +``` + +## 06_tool_registry_security + +Source: `docs/library/snippets/06_tool_registry_security.mdx` + +```python +--- +title: "06: Tool Registry Security" +description: Safe tool registration and guardrail practices. +--- + +Tools are the primary way agents interact with external systems. A tool that reads data is fundamentally different from a tool that deletes resources -- and your security model should reflect this. AFK provides multiple layers of defense for tool security: scoped tool definitions with typed arguments, sandbox profiles that restrict execution capabilities, and policy gates that require human approval for destructive operations. + +This page demonstrates how to register tools safely, distinguish between read-only and mutating tools, and configure policy gates to protect against unintended destructive actions. + +## Read-only vs mutating tools + +The most important security distinction is between tools that observe (read-only) and tools that act (mutating). Read-only tools are generally safe to allow broadly. Mutating tools should be tightly scoped and policy-gated. + +```python +from pydantic import BaseModel +from afk.tools import tool, ToolResult + +# --- Read-only tool: safe, broadly permitted --- +class LookupArgs(BaseModel): + resource_id: str + +@tool( + args_model=LookupArgs, + name="get_resource", + description="Look up a resource by ID. Returns resource metadata. Read-only.", +) +def get_resource(args: LookupArgs) -> dict: + return {"id": args.resource_id, "status": "active", "region": "us-east-1"} + + +# --- Mutating tool: destructive, requires policy gate --- +class DeleteArgs(BaseModel): + resource_id: str + +@tool( + args_model=DeleteArgs, + name="delete_resource", + description="Permanently delete a resource by ID. This action is irreversible.", +) +def delete_resource(args: DeleteArgs) -> dict: + # In production: call your API to delete the resource + return {"deleted": args.resource_id} +``` + +Notice the differences: +- The read-only tool (`get_resource`) has a description that explicitly says "Read-only." This signals to both the model and human reviewers that the tool is safe. +- The mutating tool (`delete_resource`) has a description warning about irreversibility. This helps the model understand the severity, and helps policy rules identify destructive operations. + +## Policy gate setup + +Use a `PolicyEngine` to require human approval before any mutating tool executes: + +```python +from afk.agents import Agent, PolicyEngine, PolicyRule, FailSafeConfig +from afk.core import Runner, RunnerConfig + +# Define policy rules that distinguish read vs write operations +policy = PolicyEngine(rules=[ + PolicyRule( + rule_id="gate-delete", + description="Require approval for delete operations", + condition=lambda event: event.tool_name == "delete_resource", + action="request_approval", + reason="Delete operations are irreversible and require human approval.", + ), + PolicyRule( + rule_id="deny-unknown-tools", + description="Deny any tool not explicitly registered", + condition=lambda event: ( + event.tool_name is not None + and event.tool_name not in {"get_resource", "delete_resource"} + ), + action="deny", + reason="Unregistered tools are not permitted.", + ), +]) + +agent = Agent( + name="resource-manager", + model="gpt-4.1-mini", + instructions="Manage resources using the available tools. Always look up a resource before modifying it.", + tools=[get_resource, delete_resource], + fail_safe=FailSafeConfig( + max_tool_calls=10, + max_total_cost_usd=0.10, + ), +) + +runner = Runner( + policy_engine=policy, + config=RunnerConfig( + interaction_mode="headless", + approval_fallback="deny", # Auto-deny destructive actions in headless mode + sanitize_tool_output=True, # Wrap tool output in untrusted-data markers + ), +) + +result = runner.run_sync(agent, user_message="Delete resource res-123") +print(f"State: {result.state}") +# In headless mode, the delete is auto-denied. The model sees the denial and responds accordingly. +``` + +## Sandbox profiles for filesystem tools + +For tools that interact with the filesystem or execute commands, use `SandboxProfile` to restrict their capabilities: + +```python +from afk.tools.security import SandboxProfile +from afk.core import RunnerConfig + +config = RunnerConfig( + default_sandbox_profile=SandboxProfile( + profile_id="restricted", + allow_network=False, # Block network access + allow_command_execution=True, # Allow shell commands + allowed_command_prefixes=["ls", "cat"], # Only safe read commands + deny_shell_operators=True, # Block pipes, redirects, semicolons + allowed_paths=["/app/data"], # Restrict file access to data directory + denied_paths=["/etc", "/root"], # Explicitly deny sensitive paths + command_timeout_s=10.0, # Kill commands after 10 seconds + max_output_chars=5_000, # Truncate large outputs + ), +) +``` + +## Scoping destructive tools + +Follow these principles when registering destructive tools: + +1. **Name them clearly.** Use verb prefixes that signal intent: `delete_`, `remove_`, `drop_`, `update_`, `modify_`. This makes policy rules easy to write and audit. + +2. **Type all arguments.** Use Pydantic models for argument validation. Never accept freeform `dict` arguments for mutating operations. + +3. **Describe irreversibility.** Include "irreversible", "destructive", or "permanent" in the tool description. This helps both the model and policy reviewers understand the risk. + +4. **Gate with policy rules.** Every mutating tool should have a corresponding policy rule. Use `request_approval` for interactive environments and `deny` as the fallback in headless mode. + +5. **Set cost limits.** Use `FailSafeConfig.max_tool_calls` and `max_total_cost_usd` to prevent runaway tool usage, especially when the agent has access to APIs with per-call costs. + +6. **Audit everything.** Policy decisions are emitted as `policy_decision` events in the run event stream. Persist these events for compliance and debugging. +``` + +## 07_tool_hooks_and_middleware + +Source: `docs/library/snippets/07_tool_hooks_and_middleware.mdx` + +```python +--- +title: "07: Tool Hooks and Middleware" +description: Add pre-execution validation, post-execution transformation, and cross-cutting middleware to tools and the LLM client pipeline. +--- + +## What this snippet demonstrates + +AFK provides two distinct hook/middleware systems that operate at different layers: + +1. **Tool hooks and middleware** -- Pre-hooks, post-hooks, and middleware that wrap individual tool executions. These use Pydantic models for typed arguments and run inside the tool execution pipeline. + +2. **LLM middleware** -- Middleware that wraps LLM client operations (chat, stream, embed). These intercept requests and responses at the provider transport layer. + +Both systems follow the same pattern: define a callable, wire it into the pipeline, and the runner executes it at the appropriate point in the lifecycle. + +## Tool pre-hooks + +A pre-hook runs before the main tool function executes. It receives the tool's arguments (validated against its own Pydantic model) and returns a dictionary of transformed arguments that the main tool will receive. Use pre-hooks for input sanitization, enrichment, or validation that should happen before execution. + +```python +from pydantic import BaseModel, Field +from afk.tools.core.decorator import tool, prehook + + +# Pre-hook argument model matches the main tool's argument shape +class SearchArgs(BaseModel): + query: str + max_results: int = Field(default=10, ge=1, le=100) + + +# Pre-hook: sanitize and normalize the query before the tool runs +@prehook(args_model=SearchArgs, name="normalize_query") +async def normalize_query(args: SearchArgs) -> dict: + """Strip extra whitespace and lowercase the query.""" + return { + "query": " ".join(args.query.lower().split()), + "max_results": min(args.max_results, 50), # Cap at 50 + } + + +# Main tool with the pre-hook attached +@tool( + args_model=SearchArgs, + name="search_docs", + description="Search the documentation index.", + prehooks=[normalize_query], +) +async def search_docs(args: SearchArgs) -> dict: + # args.query is already normalized by the pre-hook + return {"results": [f"Result for: {args.query}"], "count": args.max_results} +``` + +### Pre-hook execution flow + +```mermaid +flowchart LR + LLM[LLM proposes tool call] --> Validate[Validate raw args] + Validate --> PreHook[Pre-hook transforms args] + PreHook --> Execute[Tool function executes] + Execute --> Result[ToolResult returned] +``` + +The pre-hook receives validated arguments and must return a dictionary compatible with the main tool's `args_model`. If the returned dictionary fails validation against the tool's model, the tool call fails with a `ToolValidationError`. + +## Tool post-hooks + +A post-hook runs after the main tool function completes. It receives the tool output and can transform or annotate the result before it is returned to the LLM. Use post-hooks for output sanitization, logging, or enrichment. + +```python +from pydantic import BaseModel +from typing import Any +from afk.tools.core.decorator import posthook + + +class PostHookArgs(BaseModel): + output: Any + tool_name: str | None = None + + +@posthook(args_model=PostHookArgs, name="redact_sensitive") +async def redact_sensitive(args: PostHookArgs) -> dict: + """Remove sensitive fields from tool output before returning to LLM.""" + output = args.output + if isinstance(output, dict): + # Strip any fields that might contain secrets + sanitized = { + k: v for k, v in output.items() + if k not in ("api_key", "secret", "password", "token") + } + return {"output": sanitized, "tool_name": args.tool_name} + return {"output": output, "tool_name": args.tool_name} +``` + +AFK passes post-hooks a payload dictionary with the shape `{"output": , "tool_name": ""}`. The post-hook must return a dictionary with the same shape. + +## Tool-level middleware + +Tool-level middleware wraps around the entire tool execution, including pre-hooks and post-hooks. Middleware receives a `call_next` function and the tool arguments, and can modify behavior before, after, or around execution. + +```python +from afk.tools.core.decorator import middleware + + +@middleware(name="timing_middleware") +async def timing_middleware(call_next, args, ctx): + """Measure and log tool execution time.""" + import time + start = time.monotonic() + result = await call_next(args, ctx) + elapsed_ms = (time.monotonic() - start) * 1000 + print(f"Tool executed in {elapsed_ms:.1f}ms") + return result + + +@middleware(name="retry_on_transient") +async def retry_on_transient(call_next, args, ctx): + """Retry the tool once on transient errors.""" + try: + return await call_next(args, ctx) + except ConnectionError: + # One retry on connection errors + return await call_next(args, ctx) +``` + +### Attaching middleware to a tool + +```python +@tool( + args_model=SearchArgs, + name="search_docs", + description="Search the documentation index.", + prehooks=[normalize_query], + posthooks=[redact_sensitive], + middlewares=[timing_middleware, retry_on_transient], +) +async def search_docs(args: SearchArgs) -> dict: + return {"results": [...]} +``` + +Middleware executes in the order listed. The first middleware in the list is the outermost wrapper. Each middleware calls `call_next` to pass control to the next middleware (or the actual tool function if it is the last one). + +## Registry-level middleware + +Registry-level middleware applies to every tool in a `ToolRegistry`, not just a single tool. Use this for cross-cutting concerns like audit logging, rate limiting, or policy enforcement that should apply uniformly. + +```python +from afk.tools.core.decorator import registry_middleware + + +@registry_middleware(name="audit_log") +async def audit_log(call_next, tool, raw_args, ctx): + """Log every tool invocation for audit purposes.""" + print(f"AUDIT: tool={tool.spec.name} args={raw_args}") + result = await call_next(tool, raw_args, ctx) + print(f"AUDIT: tool={tool.spec.name} success={result.success}") + return result +``` + +## LLM client middleware + +LLM middleware operates at the provider transport layer, intercepting requests to and responses from the LLM API. AFK defines three middleware protocols for the three LLM operations: + +```python +from afk.llms import LLMBuilder, LLMRequest, LLMResponse +from afk.llms.middleware import MiddlewareStack + + +# Chat middleware: intercepts non-streaming chat requests +async def add_request_metadata(call_next, req: LLMRequest) -> LLMResponse: + """Add tracing metadata to every LLM request.""" + req.metadata = req.metadata or {} + req.metadata["trace_id"] = "trace_abc123" + return await call_next(req) + + +# Build client with middleware +client = ( + LLMBuilder() + .provider("openai") + .model("gpt-4.1-mini") + .profile("production") + .with_middlewares(MiddlewareStack( + chat=[add_request_metadata], + embed=[], + stream=[], + )) + .build() +) +``` + +### LLM middleware protocols + +| Protocol | Operation | Signature | +| --- | --- | --- | +| `LLMChatMiddleware` | Non-streaming chat | `async (call_next, req: LLMRequest) -> LLMResponse` | +| `LLMEmbedMiddleware` | Embeddings | `async (call_next, req: EmbeddingRequest) -> EmbeddingResponse` | +| `LLMStreamMiddleware` | Streaming chat | `(call_next, req: LLMRequest) -> AsyncIterator[LLMStreamEvent]` | + +Each middleware receives `call_next` (the next middleware or transport in the chain) and the request object. It can modify the request before calling `call_next`, modify the response after, or short-circuit entirely by returning a response without calling `call_next`. + +## Built-in LLM middleware + +AFK ships with pre-built middleware for common patterns: + +### Timeout middleware + +Apply per-request timeouts to prevent runaway calls: + +```python +from afk.llms.middleware.timeout import ( + TimeoutMiddleware, + EmbedTimeoutMiddleware, + StreamTimeoutMiddleware, + TimeoutConfig, +) +from afk.llms.middleware import MiddlewareStack + +# Configure timeouts +config = TimeoutConfig( + default_timeout_s=30.0, + chat_timeout_s=60.0, + embed_timeout_s=15.0, + stream_timeout_s=45.0, +) + +# Add to middleware stack +stack = MiddlewareStack( + chat=[TimeoutMiddleware(config)], + embed=[EmbedTimeoutMiddleware(config)], + stream=[StreamTimeoutMiddleware(config)], +) + +# Build client +client = ( + LLMBuilder() + .provider("openai") + .model("gpt-4.1-mini") + .with_middlewares(stack) + .build() +) +``` + +The timeout middleware respects `TimeoutPolicy` from the request if provided: +```python +req = LLMRequest( + model="gpt-4.1-mini", + messages=[...], + timeout_policy=TimeoutPolicy(request_timeout_s=45.0), # Override config +) +``` + +## When to use each layer + +| Layer | Scope | Use for | +| --- | --- | --- | +| **Tool pre-hook** | Single tool, before execution | Input sanitization, argument enrichment, validation | +| **Tool post-hook** | Single tool, after execution | Output sanitization, redaction, annotation | +| **Tool middleware** | Single tool, wraps execution | Timing, retries, caching, error handling | +| **Registry middleware** | All tools in registry | Audit logging, rate limiting, policy enforcement | +| **LLM middleware** | All LLM calls through client | Request metadata, response logging, tracing | + +## What to read next + +- [Tools](/library/tools) -- Full tool system architecture, the 6-step execution pipeline, and design guidelines. +- [Tool Call Lifecycle](/library/tool-call-lifecycle) -- Detailed lifecycle of a tool call from LLM proposal to result delivery. +- [LLMs Overview](/llms/index) -- Builder workflow, runtime profiles, and provider selection. +``` + +## 08_prebuilt_runtime_tools + +Source: `docs/library/snippets/08_prebuilt_runtime_tools.mdx` + +```python +--- +title: "08: Prebuilt Runtime Tools" +description: Use AFK's built-in filesystem tools with directory-scoped security constraints and compose them with policy checks. +--- + +## What this snippet demonstrates + +AFK ships prebuilt tools for common runtime operations like listing directories and reading files. These tools are designed with security-first defaults: every tool is scoped to an explicit root directory that prevents directory traversal attacks. This snippet shows how to create, configure, and compose prebuilt tools with agents and policy guards. + +## Building runtime tools + +The `build_runtime_tools()` factory creates a set of filesystem tools bound to a specific root directory. All path operations within these tools are resolved against this root, and any attempt to access files outside it raises a `FileAccessError`. + +```python +from pathlib import Path +from afk.agents import Agent +from afk.core import Runner, RunnerConfig +from afk.tools.prebuilts.runtime import build_runtime_tools + +# Create filesystem tools scoped to a specific directory +runtime_tools = build_runtime_tools(root_dir=Path("./workspace")) + +agent = Agent( + name="file-assistant", + model="gpt-4.1-mini", + instructions=( + "You help users explore and read files in the workspace directory. " + "Use list_directory to browse the directory structure and read_file " + "to read file contents. You cannot access files outside the workspace." + ), + tools=runtime_tools, +) + +runner = Runner(config=RunnerConfig(interaction_mode="headless")) +result = runner.run_sync(agent, user_message="What files are in the workspace?") +print(result.final_text) +``` + +## Available prebuilt tools + +The `build_runtime_tools()` factory produces two tools: + +### list_directory + +Lists entries in a directory under the configured root. Returns entry names, paths, and type flags (file or directory). + +| Parameter | Type | Default | Description | +| ------------- | ----- | ------- | ----------------------------------------------------------------- | +| `path` | `str` | `"."` | Relative path to list, resolved against the root directory. | +| `max_entries` | `int` | `200` | Maximum entries to return (1--5000). Prevents unbounded listings. | + +**Returns:** A dictionary with `root`, `path`, and `entries` (list of `{name, path, is_dir, is_file}`). + +### read_file + +Reads the contents of a file under the configured root, with configurable truncation to prevent excessive token consumption. + +| Parameter | Type | Default | Description | +| ----------- | ----- | ---------- | -------------------------------------------------------------------------------- | +| `path` | `str` | (required) | Relative path to the file, resolved against the root directory. | +| `max_chars` | `int` | `20_000` | Maximum characters to read (1--500,000). Content is truncated beyond this limit. | + +**Returns:** A dictionary with `root`, `path`, `content`, and `truncated` (boolean indicating whether content was truncated). + +## Security: directory traversal prevention + +Every path operation is validated with an internal containment check that uses Python's `Path.relative_to()` to verify that the resolved path stays within the configured root. This prevents attacks like: + +``` +../../etc/passwd # Blocked: escapes root +/absolute/path/to/secrets # Blocked: escapes root +./workspace/../../../etc # Blocked: resolved path escapes root +``` + +If a path escapes the root, the tool raises `FileAccessError` immediately, before any file I/O occurs. + +## Composing with policy checks + +For additional security, pair runtime tools with a policy engine that gates specific operations on approval: + +```python +from afk.agents import Agent, PolicyEngine, PolicyRule + +# Define a policy that requires approval for reading certain files +policy = PolicyEngine( + rules=[ + PolicyRule( + tool_name="read_file", + description="Require approval for reading config files", + condition=lambda event: ".env" in event.tool_args.get("path", "") + or "config" in event.tool_args.get("path", ""), + action="request_approval", + approval_message="Agent wants to read a config file: {path}", + ), + ] +) + +agent = Agent( + name="ops-assistant", + model="gpt-4.1-mini", + instructions="Use approved runtime tools only. Never read sensitive configuration without approval.", + tools=build_runtime_tools(root_dir=Path("./project")), +) + +runner = Runner( + policy_engine=policy, + config=RunnerConfig(interaction_mode="headless"), +) +``` + +## Composing with custom tools + +You can combine prebuilt tools with your own custom tools in a single agent: + +```python +from pydantic import BaseModel +from afk.tools import tool + + +class GrepArgs(BaseModel): + pattern: str + path: str = "." + + +@tool( + args_model=GrepArgs, + name="grep_files", + description="Search for a pattern in files within the workspace.", +) +async def grep_files(args: GrepArgs) -> dict: + # Your custom search implementation + return {"matches": [], "pattern": args.pattern} + + +# Combine prebuilt + custom tools +all_tools = build_runtime_tools(root_dir=Path("./workspace")) + [grep_files] + +agent = Agent( + name="dev-assistant", + model="gpt-4.1-mini", + instructions="Help developers explore and search the codebase.", + tools=all_tools, +) +``` + +## Command allowlists and sandbox profiles + +For production environments, restrict tool capabilities further using sandbox profiles: + +```python +from afk.tools.security import SandboxProfile + +# Create a read-only sandbox that restricts what operations tools can perform +read_only_profile = SandboxProfile( + name="read_only", + allowed_operations=["read", "list"], + denied_operations=["write", "delete", "execute"], + max_file_size_bytes=1_000_000, # 1 MB max read size + allowed_extensions=[".py", ".md", ".txt", ".json", ".yaml"], +) +``` + +This ensures that even if the LLM attempts to use tools for unauthorized operations, the sandbox profile blocks execution before any I/O occurs. + +## What to read next + +- [Tools](/library/tools) -- Full tool system architecture, including the `@tool` decorator, `ToolResult`, and execution pipeline. +- [Snippet 06: Tool Registry Security](/library/snippets/06_tool_registry_security) -- Security scoping, policy gates, and sandbox profiles in detail. +- [Security Model](/library/security-model) -- Threat model, defense layers, and RunnerConfig security fields. +``` + +## 09_system_prompt_loader + +Source: `docs/library/snippets/09_system_prompt_loader.mdx` + +```python +--- +title: "09: System Prompt Loader" +description: Resolve agent system prompts from a file hierarchy with deterministic precedence, Jinja templating, and stat-based caching. +--- + +## What this snippet demonstrates + +AFK agents need system prompts (instructions) that tell the LLM how to behave. Rather than hardcoding instructions as inline strings, AFK provides a file-based prompt resolution system that loads prompts from a directory hierarchy. This keeps prompts version-controlled, editable by non-developers, and reusable across agents. + +The prompt loader resolves instructions through a deterministic precedence chain, supports Jinja2 templating for dynamic prompts, and caches compiled templates using stat-based invalidation for hot-reload during development. + +## Resolution precedence + +The prompt system resolves agent instructions through this priority chain: + +```mermaid +flowchart TD + A[Agent has inline instructions?] -->|Yes| B[Use inline instructions] + A -->|No| C[Agent has instruction_file?] + C -->|Yes| D[Load from instruction_file path] + C -->|No| E[Auto-detect from agent name] + E --> F[Convert name to UPPER_SNAKE_CASE.md] + F --> G[Look in prompts_dir] +``` + +1. **Inline `instructions`** -- If the agent has a non-empty `instructions` string, it is used directly. No file loading occurs. +2. **Explicit `instruction_file`** -- If set, the file is loaded from the configured `prompts_dir`. The path must resolve to a file inside the prompts root (no directory traversal). +3. **Auto-detected file** -- If neither is set, the agent's name is converted to `UPPER_SNAKE_CASE.md` and loaded from `prompts_dir`. + +## Basic usage + +```python +from afk.agents import Agent + +# Option 1: Inline instructions (highest priority) +agent = Agent( + name="ChatAgent", + model="gpt-4.1-mini", + instructions="Answer customer questions concisely.", +) + +# Option 2: Explicit instruction file +agent = Agent( + name="ChatAgent", + model="gpt-4.1-mini", + instruction_file="chat_agent_system.md", # Loaded from prompts_dir + prompts_dir=".agents/prompt", +) + +# Option 3: Auto-detected file (uses agent name) +# Loads .agents/prompt/CHAT_AGENT.md automatically +agent = Agent( + name="ChatAgent", + model="gpt-4.1-mini", + prompts_dir=".agents/prompt", +) +``` + +## Name-to-filename conversion + +The auto-detection algorithm converts the agent name to a filename using these rules: + +| Agent Name | Derived Filename | Rule Applied | +| --- | --- | --- | +| `ChatAgent` | `CHAT_AGENT.md` | CamelCase split on boundaries | +| `chatagent` | `CHAT_AGENT.md` | Lowercase `agent` suffix detected and split | +| `research-assistant` | `RESEARCH_ASSISTANT.md` | Hyphens replaced with underscores | +| `QA Bot v2` | `QA_BOT_V2.md` | Spaces and non-alphanumeric chars become underscores | + +The conversion is handled by `derive_auto_prompt_filename()` internally. It splits camelCase boundaries, normalizes non-alphanumeric characters to underscores, collapses consecutive underscores, and uppercases the result. + +## Prompts directory resolution + +The prompts directory is resolved through its own priority chain: + +1. Explicit `prompts_dir` argument on the `Agent` constructor. +2. `AFK_AGENT_PROMPTS_DIR` environment variable. +3. Default: `.agents/prompt` relative to the current working directory. + +```python +# Explicit +agent = Agent(name="Bot", model="gpt-4.1-mini", prompts_dir="/opt/prompts") + +# Environment variable +# export AFK_AGENT_PROMPTS_DIR=/opt/prompts +agent = Agent(name="Bot", model="gpt-4.1-mini") + +# Default: .agents/prompt/ +agent = Agent(name="Bot", model="gpt-4.1-mini") +``` + +## Jinja2 templating + +Prompt files support Jinja2 template syntax. When the runner resolves a prompt, it renders the template with a context dictionary that includes agent metadata and any custom context passed to the run. + +**File: `.agents/prompt/SUPPORT_AGENT.md`** + +```markdown +You are {{ agent_name }}, a support agent for {{ ctx.company_name }}. + +Your responsibilities: +- Answer questions about {{ ctx.product_name }} +- Escalate billing issues to the billing team +- Never disclose internal pricing formulas + +{% if ctx.get("tone") == "formal" %} +Use formal language and address the customer by title. +{% else %} +Use friendly, conversational language. +{% endif %} +``` + +**Agent code:** + +```python +from afk.core import Runner, RunnerConfig + +agent = Agent( + name="SupportAgent", + model="gpt-4.1-mini", + prompts_dir=".agents/prompt", +) + +runner = Runner(config=RunnerConfig(interaction_mode="headless")) +result = runner.run_sync( + agent, + user_message="How do I reset my password?", + context={ + "company_name": "Acme Corp", + "product_name": "Acme Cloud", + "tone": "friendly", + }, +) +``` + +### Template context variables + +The following variables are available in every prompt template: + +| Variable | Type | Description | +| --- | --- | --- | +| `agent_name` | `str` | The agent's `name` field. | +| `agent_class` | `str` | The Python class name of the agent. | +| `context` | `dict` | The full context dictionary passed to the run. | +| `ctx` | `dict` | Alias for `context` (shorthand). | + +Any keys in the `context` dictionary that are not reserved names (`context`, `ctx`, `agent_name`, `agent_class`) are also available as top-level template variables. So `{{ company_name }}` works as a shorthand for `{{ ctx.company_name }}`. + +## Caching and hot-reload + +The prompt system uses a process-wide `PromptStore` singleton that caches at three levels: + +1. **File cache** -- Keyed by resolved file path. Uses `stat()` metadata (mtime, size, inode) as the cache signature. If the file changes on disk, the cache entry is invalidated automatically. + +2. **Text pool** -- Deduplicates prompt text by SHA-256 hash. If multiple agents use the same prompt content (even from different files), only one copy is stored in memory. + +3. **Template cache** -- Compiled Jinja2 templates are cached by content hash. Re-rendering with different context variables reuses the compiled template. + +This means that during development, you can edit prompt files and they will be picked up on the next run without restarting the process. In production, the stat-based check is a single `os.stat()` call per prompt resolution, which is negligible overhead. + +## Security: path containment + +The prompt loader enforces strict path containment. The resolved prompt file path must be inside the configured `prompts_dir`. If an `instruction_file` path resolves outside the prompts root (via `../` traversal or an absolute path pointing elsewhere), the loader raises `PromptAccessError` immediately. + +```python +# This would raise PromptAccessError: +agent = Agent( + name="Agent", + model="gpt-4.1-mini", + instruction_file="../../etc/passwd", # Escapes prompts root + prompts_dir=".agents/prompt", +) +``` + +## What to read next + +- [System Prompts](/library/system-prompts) -- Full system prompt architecture, resolution pipeline, and design guidelines. +- [Agents](/library/agents) -- Agent model, configuration fields, and composition patterns. +- [Security Model](/library/security-model) -- Threat model and defense layers including prompt injection considerations. +``` + +## 10_streaming_chat_with_memory + +Source: `docs/library/snippets/10_streaming_chat_with_memory.mdx` + +```python +--- +title: "10: Streaming Chat with Memory" +description: Combine real-time streaming with thread-based memory for multi-turn chat UIs. +--- + +## What this snippet demonstrates + +Most chat applications need two things simultaneously: **real-time streaming** (so users see text as it's generated) and **memory continuity** (so the agent remembers previous turns). This snippet shows how to combine `run_stream()` with `thread_id` to build a multi-turn streaming chat handler. + +## Full example + +```python +import asyncio +from afk.agents import Agent, FailSafeConfig +from afk.core import Runner, RunnerConfig + +agent = Agent( + name="chat-assistant", + model="gpt-4.1-mini", + instructions=""" + You are a helpful assistant. Remember context from earlier in the conversation. + Be concise but thorough. If the user refers to something from a previous message, + use that context in your response. + """, + fail_safe=FailSafeConfig( + max_steps=10, + max_total_cost_usd=0.25, + ), +) + + +async def stream_turn(runner: Runner, user_message: str, thread_id: str): + """Stream a single turn and return the result.""" + handle = await runner.run_stream( + agent, + user_message=user_message, + thread_id=thread_id, # ← Same thread_id = same conversation + ) + + async for event in handle: + match event.type: + case "text_delta": + print(event.text_delta, end="", flush=True) + case "tool_started": + print(f"\n[TOOL] {event.tool_name}...") + case "tool_completed": + status = "[OK]" if event.tool_success else "[ERR]" + print(f" {status} done") + case "error": + if event.error: + print(f"\n[WARN] {event.error}") + case "completed": + print(f"\n[DONE] ({event.result.state})") + + return handle.result + + +async def main(): + runner = Runner(config=RunnerConfig(interaction_mode="headless")) + thread = "session-demo-42" + + # Turn 1 + print("User: What is the GIL in Python?\n") + print("Assistant: ", end="") + r1 = await stream_turn(runner, "What is the GIL in Python?", thread) + + # Turn 2 — agent remembers Turn 1 + print("\n\nUser: How does it affect multithreading?\n") + print("Assistant: ", end="") + r2 = await stream_turn(runner, "How does it affect multithreading?", thread) + + # Turn 3 — agent still has full context + print("\n\nUser: What are the alternatives?\n") + print("Assistant: ", end="") + r3 = await stream_turn(runner, "What are the alternatives?", thread) + + # Print usage summary + print(f"\n\n--- Usage ---") + for i, r in enumerate([r1, r2, r3], 1): + print(f"Turn {i}: {r.usage.total_tokens} tokens") + + +asyncio.run(main()) +``` + +## Key patterns + +### Thread ID connects turns + +Pass the same `thread_id` across `run_stream()` calls to maintain conversation context: + +```python +# These two calls share memory +r1 = await runner.run_stream(agent, user_message="Hello", thread_id="t-42") +r2 = await runner.run_stream(agent, user_message="Follow up", thread_id="t-42") +``` + +### Access the result after streaming + +The `handle.result` is available after the stream completes: + +```python +async for event in handle: + ... # Process events + +result = handle.result # Full AgentResult with final_text, usage, etc. +``` + +### Cancel mid-stream + +If the user navigates away or clicks "stop": + +```python +await handle.cancel() +# The run transitions to "cancelled" state +``` + +## What to read next + +- [Streaming](/library/streaming) — Full event reference and stream control API. +- [Memory](/library/memory) — Thread persistence, compaction, and backend configuration. +- [Snippet 04: Resume + Compact](/library/snippets/04_resume_and_compact) — Checkpoint-based resumption and memory management. +``` + +## 11_cost_monitoring + +Source: `docs/library/snippets/11_cost_monitoring.mdx` + +```python +--- +title: "11: Cost Monitoring" +description: Track and control agent costs using FailSafeConfig budgets and telemetry events. +--- + +## What this snippet demonstrates + +Runaway agent loops are the most common source of unexpected API costs. AFK provides two defense layers: **cost budgets** that kill runs when spending exceeds a threshold, and **telemetry events** that let you observe cost in real time. This snippet shows how to configure both. + +## Setting cost budgets + +The simplest defense is a hard cost ceiling on every agent: + +```python +from afk.agents import Agent, FailSafeConfig + +agent = Agent( + name="budget-agent", + model="gpt-4.1-mini", + instructions="Be helpful and concise.", + fail_safe=FailSafeConfig( + max_total_cost_usd=0.50, # Hard cost ceiling + max_llm_calls=30, # Secondary defense: limit API calls + max_steps=15, # Tertiary defense: limit reasoning steps + max_wall_time_s=120.0, # Quaternary defense: wall-clock timeout + ), +) +``` + +When the estimated cost exceeds `max_total_cost_usd`, the runner terminates the run with a `degraded` state and returns the best partial result. + +## Monitoring cost from results + +Every `AgentResult` includes token counts and cost estimates: + +```python +from afk.core import Runner + +runner = Runner() +result = runner.run_sync(agent, user_message="Analyze this dataset...") + +# Access usage statistics +usage = result.usage_aggregate +print(f"Input tokens: {usage.input_tokens}") +print(f"Output tokens: {usage.output_tokens}") +print(f"Total tokens: {usage.total_tokens}") +print(f"Estimated cost: ${result.total_cost_usd or 0:.4f}") +print(f"Tool calls: {len(result.tool_executions)}") +``` + +## Real-time cost monitoring via streaming + +For long-running agents, monitor cost during execution: + +```python +import asyncio +from afk.agents import Agent, FailSafeConfig +from afk.core import Runner + +agent = Agent( + name="analyst", + model="gpt-4.1", + instructions="Provide detailed analysis.", + fail_safe=FailSafeConfig( + max_total_cost_usd=1.00, + max_steps=20, + ), +) + + +async def monitor_cost(): + runner = Runner() + handle = await runner.run_stream( + agent, user_message="Compare Python async patterns for service code" + ) + + step_count = 0 + async for event in handle: + match event.type: + case "text_delta": + print(event.text_delta, end="", flush=True) + case "step_started" if event.step is not None: + step_count = event.step + case "tool_completed": + print(f"\n [STEP] Step {step_count} | Tool: {event.tool_name}") + case "completed" if event.result is not None: + usage = event.result.usage_aggregate + print(f"\n\n--- Cost Summary ---") + print(f"State: {event.result.state}") + print(f"Tokens: {usage.total_tokens}") + print(f"Cost: ${event.result.total_cost_usd or 0:.4f}") + print(f"Tools: {len(event.result.tool_executions)}") + +asyncio.run(monitor_cost()) +``` + +## Cost-aware batch processing + +When running multiple agents in a batch, track cumulative cost: + +```python +async def batch_process(items: list[str], budget_usd: float): + """Process items with a shared cost budget.""" + runner = Runner() + cumulative_cost = 0.0 + results = [] + + for item in items: + if cumulative_cost >= budget_usd: + print(f"[Limit] Budget exhausted at ${cumulative_cost:.4f}") + break + + # Set per-item budget as remaining budget + remaining = budget_usd - cumulative_cost + agent = Agent( + name="batch-processor", + model="gpt-4.1-mini", + instructions="Process the item concisely.", + fail_safe=FailSafeConfig( + max_total_cost_usd=min(remaining, 0.10), # Per-item cap + max_steps=5, + ), + ) + + result = await runner.run(agent, user_message=item) + item_cost = result.total_cost_usd or 0.0 + cumulative_cost += item_cost + results.append(result) + + print(f" [OK] {item[:40]}... (${item_cost:.4f})") + + print(f"\nTotal: {len(results)} items, ${cumulative_cost:.4f}") + return results +``` + +## Operating recommendations + +1. **Always set `max_total_cost_usd`** — even generous limits prevent runaway costs +2. **Layer defenses** — combine cost limits with `max_llm_calls`, `max_steps`, and `max_wall_time_s` +3. **Use telemetry for dashboards** — export metrics to monitor cost trends over time +4. **Set per-item budgets in batches** — prevent one expensive item from consuming the entire budget +5. **Choose models by task** — use smaller models for routine work and reserve larger models for requests that need them + +## What to read next + +- [Observability](/library/observability) — Telemetry pipeline for metrics and dashboards. +- [Failure Policy Matrix](/library/failure-policy-matrix) — How cost limit breaches flow through the system. +- [Configuration Reference](/library/configuration-reference#failsafeconfig) — Full FailSafeConfig field reference. +``` + +## 12_mcp_client_integration + +Source: `docs/library/snippets/12_mcp_client_integration.mdx` + +```python +--- +title: "12: MCP Client Integration" +description: Discover and use tools from external MCP servers in your agents. +--- + +## What this snippet demonstrates + +AFK agents can consume tools from external MCP (Model Context Protocol) servers just like local tools. This snippet shows how to connect to an MCP server, discover available tools, and attach them to an agent — all with the same validation, policy gates, and telemetry as local tools. + +## Consuming MCP tools + +### Connect, discover, and attach + +```python +import asyncio +from afk.agents import Agent, FailSafeConfig +from afk.core import Runner +from afk.mcp import MCPStore + +async def main(): + # 1. Connect to an external MCP server + store = MCPStore() + await store.connect("https://tools.example.com:3001") + + # 2. Discover available tools + tools = await store.list_tools() + print(f"Found {len(tools)} tools:") + for t in tools: + print(f" • {t.name}: {t.description}") + + # 3. Attach MCP tools to an agent — they work like local tools + agent = Agent( + name="mcp-assistant", + model="gpt-4.1-mini", + instructions=""" + Use the available tools to help the user. + Always explain what tool you're using and why. + """, + tools=tools, + fail_safe=FailSafeConfig( + max_total_cost_usd=0.25, + max_tool_calls=10, + ), + ) + + # 4. Run the agent — MCP tools execute transparently + runner = Runner() + result = runner.run_sync( + agent, user_message="Search the documentation for authentication patterns" + ) + print(f"\n{result.final_text}") + + # 5. Inspect tool calls — MCP tools appear just like local tools + for rec in result.tool_executions: + print(f" {'[OK]' if rec.success else '[ERR]'} {rec.tool_name} ({rec.latency_ms:.0f}ms)") + + # 6. Disconnect + await store.disconnect() + +asyncio.run(main()) +``` + +## Using the Agent's built-in MCP support + +For simpler setups, pass MCP server refs directly to the agent: + +```python +from afk.agents import Agent + +# The agent connects to MCP servers automatically during startup +agent = Agent( + name="connected-agent", + model="gpt-4.1-mini", + instructions="Use available tools to help the user.", + mcp_servers=[ + "https://tools.example.com:3001", # Simple URL + "search=https://search.internal:3002", # Named server + {"url": "https://db.internal:3003", "auth": "token-xyz"}, # With auth + ], + enable_mcp_tools=True, # Default: True +) +``` + +## Mixing local and MCP tools + +Combine your own tools with external MCP tools: + +```python +from pydantic import BaseModel +from afk.agents import Agent +from afk.tools import tool +from afk.mcp import MCPStore + +class SummaryArgs(BaseModel): + text: str + max_words: int = 100 + +@tool(args_model=SummaryArgs, name="summarize", description="Summarize text concisely.") +def summarize(args: SummaryArgs) -> dict: + # Your local summarization logic + return {"summary": args.text[:args.max_words * 5] + "..."} + + +async def build_agent(): + # Get external tools + store = MCPStore() + await store.connect("https://tools.example.com:3001") + mcp_tools = await store.list_tools() + + # Combine local + external tools + agent = Agent( + name="hybrid-agent", + model="gpt-4.1-mini", + instructions="Use search tools for research and summarize for concise output.", + tools=[summarize] + mcp_tools, # ← Mix freely + ) + return agent +``` + +## Security with MCP tools + +Apply policy rules to MCP-sourced tools just like local tools: + +```python +from afk.agents import PolicyEngine, PolicyRule +from afk.core import Runner + +policy = PolicyEngine(rules=[ + PolicyRule( + rule_id="gate-mcp-writes", + condition=lambda e: e.tool_name and "write" in e.tool_name, + action="request_approval", + reason="MCP write operations need human approval", + ), +]) + +runner = Runner(policy_engine=policy) +``` + + + **MCP tools are transparent.** Once attached to an agent, they go through the + same validation, policy gates, sanitization, and telemetry as local tools. The + agent doesn't know whether a tool is local or remote. + + +## What to read next + +- [MCP Server](/library/mcp-server) — Expose your own tools via MCP, plus authentication and rate limiting. +- [Tools](/library/tools) — Full tool system architecture. +- [Snippet 06: Tool Security](/library/snippets/06_tool_registry_security) — Policy gates and sandbox profiles. +``` + +## 13_multi_model_fallback + +Source: `docs/library/snippets/13_multi_model_fallback.mdx` + +```python +--- +title: "13: Multi-Model Fallback" +description: Configure fallback model chains for LLM resilience and cost optimization. +--- + +## What this snippet demonstrates + +LLM API calls fail — rate limits, outages, timeouts. AFK's `fallback_model_chain` lets you define an ordered list of models to try when the primary model fails. This snippet shows how to configure fallback chains for resilience, cost optimization, and provider diversification. + +## Basic fallback chain + +```python +from afk.agents import Agent, FailSafeConfig + +agent = Agent( + name="resilient-agent", + model="gpt-4.1", # Primary model + instructions="Be helpful and thorough.", + fail_safe=FailSafeConfig( + # Fallback chain: try these models in order if the primary fails + fallback_model_chain=[ + "gpt-4.1-mini", # First fallback: cheaper, faster + "gpt-4.1-nano", # Last resort: fastest, cheapest + ], + + # When LLM calls fail, retry then degrade + llm_failure_policy="retry_then_degrade", + + # Cost ceiling still applies across all models + max_total_cost_usd=1.00, + ), +) +``` + +When `gpt-4.1` fails (timeout, rate limit, outage): + +1. AFK retries with the primary model (controlled by retry policy) +2. If retries exhaust, it falls through to `gpt-4.1-mini` +3. If that also fails, it tries `gpt-4.1-nano` +4. If all models fail, the `llm_failure_policy` determines the outcome + +## Cost-optimized fallback + +Use expensive models only when needed: + +```python +from afk.agents import Agent, FailSafeConfig +from afk.core import Runner + +# Start cheap, escalate if quality is insufficient +simple_agent = Agent( + name="classifier", + model="gpt-4.1-nano", # Start with cheapest + instructions=""" + Classify the support ticket. Output exactly one label: + billing, technical, account, other. + """, + fail_safe=FailSafeConfig( + fallback_model_chain=["gpt-4.1-mini", "gpt-4.1"], + max_total_cost_usd=0.05, + ), +) + +# Complex tasks get the big model with fallbacks +analysis_agent = Agent( + name="analyst", + model="gpt-4.1", # Start with most capable + instructions=""" + Provide detailed technical analysis with code examples. + Be thorough and precise. + """, + fail_safe=FailSafeConfig( + fallback_model_chain=["gpt-4.1-mini"], + llm_failure_policy="retry_then_degrade", + max_total_cost_usd=2.00, + ), +) + +runner = Runner() + +# Simple task -> cheap model handles it +r1 = runner.run_sync(simple_agent, user_message="I can't log in") +print(f"Classification: {r1.final_text} (${r1.total_cost_usd or 0:.4f})") + +# Complex task -> powerful model with safety net +r2 = runner.run_sync(analysis_agent, user_message="Analyze Python's asyncio event loop") +print(f"Analysis: {r2.final_text[:100]}... (${r2.total_cost_usd or 0:.4f})") +``` + +## Circuit breaker integration + +AFK's built-in circuit breaker works with fallback chains. When a model triggers too many failures, the breaker opens and the system skips straight to the next fallback: + +```python +agent = Agent( + name="breaker-demo", + model="gpt-4.1", + instructions="...", + fail_safe=FailSafeConfig( + fallback_model_chain=["gpt-4.1-mini", "gpt-4.1-nano"], + + # Circuit breaker settings + breaker_failure_threshold=5, # Open after 5 consecutive failures + breaker_cooldown_s=30.0, # Wait 30s before retrying the model + + # Failure handling + llm_failure_policy="retry_then_degrade", + max_total_cost_usd=1.00, + ), +) +``` + +```mermaid +flowchart LR + A["gpt-4.1 fails 5x"] --> B["Circuit opens"] + B --> C["Skip to gpt-4.1-mini"] + C --> D["30s cooldown"] + D --> E["gpt-4.1 retried"] + E -->|"succeeds"| F["Circuit closes"] + E -->|"fails again"| B +``` + +## Multi-agent with different model tiers + +Use different model tiers for different specialists: + +```python +from afk.agents import Agent, FailSafeConfig + +# Cheap model for simple classification +router = Agent( + name="router", + model="gpt-4.1-nano", + instructions="Route to the correct specialist.", + fail_safe=FailSafeConfig(fallback_model_chain=["gpt-4.1-mini"]), + subagents=[ + # Powerful model for complex analysis + Agent( + name="analyst", + model="gpt-4.1", + instructions="Provide deep technical analysis.", + fail_safe=FailSafeConfig( + fallback_model_chain=["gpt-4.1-mini"], + max_total_cost_usd=1.00, + ), + ), + # Mid-tier model for summarization + Agent( + name="summarizer", + model="gpt-4.1-mini", + instructions="Summarize findings concisely.", + fail_safe=FailSafeConfig( + fallback_model_chain=["gpt-4.1-nano"], + max_total_cost_usd=0.25, + ), + ), + ], +) +``` + +## Inspecting which model was used + +After a run, check the result metadata and usage aggregate: + +```python +result = runner.run_sync(agent, user_message="Analyze this...") + +print(f"State: {result.state}") +print(f"Requested model: {result.requested_model}") +print(f"Normalized model: {result.normalized_model}") +print(f"Provider: {result.provider_adapter}") +print(f"Total tokens: {result.usage_aggregate.total_tokens}") +print(f"Total cost: ${result.total_cost_usd or 0:.4f}") +``` + +## Recommendations + +| Scenario | Primary Model | Fallback Chain | +| ------------------------ | -------------- | ------------------------------- | +| **Classification** | `gpt-4.1-nano` | `gpt-4.1-mini` | +| **General chat** | `gpt-4.1-mini` | `gpt-4.1-nano` | +| **Complex analysis** | `gpt-4.1` | `gpt-4.1-mini` → `gpt-4.1-nano` | +| **Code generation** | `gpt-4.1` | `gpt-4.1-mini` | +| **Cost-sensitive batch** | `gpt-4.1-nano` | _(none)_ | + +## What to read next + +- [Configuration Reference](/library/configuration-reference#failsafeconfig) — Full FailSafeConfig fields including circuit breaker settings. +- [Failure Policy Matrix](/library/failure-policy-matrix) — How failures flow through the system. +- [Snippet 11: Cost Monitoring](/library/snippets/11_cost_monitoring) — Track and control costs in real time. +``` + +## 14_production_client + +Source: `docs/library/snippets/14_production_client.mdx` + +```python +--- +title: "14: Client Timeouts and Redis Pooling" +description: Configure LLM client timeouts and Redis connection pooling. +--- + +## What this snippet demonstrates + +This snippet shows how to configure: +1. **Timeout middleware** to bound slow provider calls +2. **Redis connection pooling** for shared cache or memory connections +3. **Shutdown handling** so runners and Redis pools close cleanly + +## Timeout middleware + +Apply per-request timeouts to prevent runaway LLM calls: + +```python +import asyncio +from afk.llms import LLMBuilder, LLMRequest +from afk.llms.middleware import MiddlewareStack +from afk.llms.middleware.timeout import ( + TimeoutMiddleware, + EmbedTimeoutMiddleware, + StreamTimeoutMiddleware, + TimeoutConfig, +) + +config = TimeoutConfig( + default_timeout_s=30.0, + chat_timeout_s=60.0, + embed_timeout_s=15.0, + stream_timeout_s=45.0, +) + +stack = MiddlewareStack( + chat=[TimeoutMiddleware(config)], + embed=[EmbedTimeoutMiddleware(config)], + stream=[StreamTimeoutMiddleware(config)], +) + +production_client = ( + LLMBuilder() + .provider("openai") + .model("gpt-4.1-mini") + .profile("production") + .with_middlewares(stack) + .build() +) +``` + +### Per-request timeout override + +```python +from afk.llms import TimeoutPolicy + +req = LLMRequest( + model="gpt-4.1-mini", + messages=[...], + timeout_policy=TimeoutPolicy(request_timeout_s=120.0), # Override default +) + +response = await production_client.chat(req) +``` + +## Redis connection pooling + +For Redis deployments, use connection pooling instead of creating a new client per request: + +```python +from afk.llms.cache.redis_pool import ( + get_redis_pool, + PoolConfig, + close_all_pools, +) + +async def setup_redis_pool(): + pool = await get_redis_pool( + "redis://localhost:6379/0", + config=PoolConfig( + max_connections=50, + max_idle_connections=10, + socket_timeout=5.0, + socket_connect_timeout=5.0, + socket_keepalive=True, + health_check_interval_s=30.0, + ), + ) + + if await pool.health_check(): + print("Redis connection pool healthy") + + return pool +``` + +### Using with memory store + +```python +import asyncio +from afk.memory.adapters.redis import RedisMemoryStore +from afk.core import Runner + +async def main(): + pool = await get_redis_pool( + "redis://localhost:6379/0", + config=PoolConfig(max_connections=50), + ) + + runner = Runner( + memory_store=RedisMemoryStore(url="redis://localhost:6379/0"), + ) + + result = await runner.run(agent, user_message="Hello") + print(result.final_text) + + await runner.close() + await close_all_pools() + +asyncio.run(main()) +``` + +## Full example + +```python +import asyncio +from afk.llms import LLMBuilder +from afk.llms.middleware import MiddlewareStack +from afk.llms.middleware.timeout import ( + TimeoutMiddleware, + TimeoutConfig, +) +from afk.llms.cache.redis_pool import ( + get_redis_pool, + PoolConfig, + close_all_pools, +) +from afk.memory.adapters.redis import RedisMemoryStore +from afk.core import Runner +from afk.agents import Agent + +class ProductionSetup: + def __init__(self): + self.llm_client = None + self.runner = None + self.pool = None + + async def __aenter__(self): + pool_config = PoolConfig( + max_connections=50, + max_idle_connections=10, + socket_timeout=5.0, + socket_connect_timeout=5.0, + ) + self.pool = await get_redis_pool( + "redis://localhost:6379/0", + config=pool_config, + ) + + timeout_config = TimeoutConfig( + default_timeout_s=30.0, + chat_timeout_s=60.0, + ) + stack = MiddlewareStack( + chat=[TimeoutMiddleware(timeout_config)], + ) + + self.llm_client = ( + LLMBuilder() + .provider("openai") + .model("gpt-4.1-mini") + .profile("production") + .with_middlewares(stack) + .build() + ) + + self.runner = Runner( + memory_store=RedisMemoryStore(url="redis://localhost:6379/0"), + ) + + return self + + async def __aexit__(self, exc_type, exc_val, exc_tb): + if self.runner: + await self.runner.close() + await close_all_pools() + return False + + +async def main(): + agent = Agent( + name="assistant", + model="gpt-4.1-mini", + instructions="You are a helpful assistant.", + ) + + async with ProductionSetup() as setup: + result = await setup.runner.run( + agent, + user_message="Hello, world!", + ) + print(result.final_text) + +asyncio.run(main()) +``` + +## Configuration reference + +### TimeoutConfig + +| Parameter | Default | Description | +| --- | --- | --- | +| `default_timeout_s` | 30.0 | Default timeout for all operations | +| `chat_timeout_s` | None | Specific timeout for chat requests | +| `embed_timeout_s` | None | Specific timeout for embeddings | +| `stream_timeout_s` | None | Specific timeout for streaming | + +### PoolConfig + +| Parameter | Default | Description | +| --- | --- | --- | +| `max_connections` | 50 | Maximum total connections | +| `max_idle_connections` | 10 | Maximum idle connections | +| `socket_timeout` | 5.0 | Socket read/write timeout | +| `socket_connect_timeout` | 5.0 | Connection establishment timeout | +| `socket_keepalive` | False | Enable TCP keepalive | +| `health_check_interval_s` | 30.0 | Interval for health checks | + +## What to read next + +- [LLM Control & Session](/llms/control-and-session) -- Retry, caching, and circuit breaker policies +- [Deployment Guide](/library/deployment) -- Production deployment with Docker and Kubernetes +- [Performance Guide](/library/performance) -- Optimize latency and throughput +``` diff --git a/skills/index.json b/skills/index.json new file mode 100644 index 0000000..8043aae --- /dev/null +++ b/skills/index.json @@ -0,0 +1,19 @@ +{ + "generated_at_utc": "2026-05-31T13:25:20.450807+00:00", + "skills": [ + { + "id": "skill_8d9ddec839", + "name": "afk-coder", + "description": "Build applications with the AFK Python SDK. Use when creating or modifying AFK agents, runners, tools, memory, streaming, policy/HITL, LLM runtime configuration, queues, MCP/A2A integrations, evals, or production examples using public `afk.*` imports.", + "path": "skills/afk-coder/SKILL.md", + "github_url": "https://github.com/arpan404/afk/tree/main/skills/afk-coder" + }, + { + "id": "skill_cc4bcb2a3a", + "name": "afk-maintainer", + "description": "Maintain and review the AFK Python SDK repository. Use when changing AFK internals, reviewing PRs, triaging issues, planning releases, updating docs/skills, or making architecture, public API, safety, dependency, testing, and compatibility decisions for AFK.", + "path": "skills/afk-maintainer/SKILL.md", + "github_url": "https://github.com/arpan404/afk/tree/main/skills/afk-maintainer" + } + ] +} diff --git a/src/afk/agents/__init__.py b/src/afk/agents/__init__.py index 5762261..baaba6f 100644 --- a/src/afk/agents/__init__.py +++ b/src/afk/agents/__init__.py @@ -102,18 +102,27 @@ ApprovalRequest, CommandExecutionRecord, FailSafeConfig, + InstructionRole, PolicyDecision, PolicyEvent, + PolicyRole, SkillReadRecord, SkillRef, SkillResolutionResult, SkillToolPolicy, SubagentExecutionRecord, + SubagentRouter, ToolExecutionRecord, UsageAggregate, UserInputDecision, UserInputRequest, ) +from .workflow.executor import ( + WorkflowExecutionContext, + WorkflowExecutionResult, + WorkflowExecutor, + create_workflow_executor, +) from .workflow.state_machine import ( WorkflowBuilder, WorkflowEdge, @@ -123,12 +132,6 @@ WorkflowState, WorkflowTransition, ) -from .workflow.executor import ( - WorkflowExecutionContext, - WorkflowExecutionResult, - WorkflowExecutor, - create_workflow_executor, -) __all__ = [ "BaseAgent", @@ -194,6 +197,9 @@ "CommandExecutionRecord", "UsageAggregate", "FailSafeConfig", + "InstructionRole", + "PolicyRole", + "SubagentRouter", "SkillToolPolicy", "SkillRef", "SkillResolutionResult", diff --git a/src/afk/agents/a2a/auth.py b/src/afk/agents/a2a/auth.py index 90f1a4c..6869a89 100644 --- a/src/afk/agents/a2a/auth.py +++ b/src/afk/agents/a2a/auth.py @@ -9,6 +9,8 @@ from __future__ import annotations import hashlib +import hmac +import secrets from collections.abc import Mapping from dataclasses import dataclass, field from typing import Protocol @@ -72,6 +74,14 @@ class A2AAuthorizationError(PermissionError): """Raised when authorization fails.""" +@dataclass(frozen=True, slots=True) +class _APIKeyRecord: + salt: bytes + digest: bytes + subject: str + roles: tuple[str, ...] + + class AllowAllA2AAuthProvider: """Development-only provider that allows every request.""" @@ -109,29 +119,38 @@ def __init__( header_name: str = "x-api-key", ) -> None: self._header_name = header_name.lower() - self._subject_by_digest: dict[str, str] = {} - self._roles_by_digest: dict[str, tuple[str, ...]] = {} - self._allow_all_by_digest: dict[str, bool] = {} + self._records: tuple[_APIKeyRecord, ...] role_map = key_to_roles or {} + records: list[_APIKeyRecord] = [] for key, subject in key_to_subject.items(): - digest = self._hash_key(key) + salt = secrets.token_bytes(16) + digest = self._hash_key(key, salt) roles = tuple(role_map.get(key, ())) - self._subject_by_digest[digest] = subject - self._roles_by_digest[digest] = roles - self._allow_all_by_digest[digest] = "a2a:all" in roles + records.append( + _APIKeyRecord( + salt=salt, + digest=digest, + subject=subject, + roles=roles, + ) + ) + self._records = tuple(records) async def authenticate(self, context: A2AAuthContext) -> A2APrincipal: key = self._get_header(context.headers, self._header_name) if not key: raise A2AAuthError("Missing API key") - digest = self._hash_key(key) - subject = self._subject_by_digest.get(digest) - if subject is None: - raise A2AAuthError("Invalid API key") - roles = self._roles_by_digest.get(digest, ()) - return A2APrincipal(subject=subject, principal_type="service", roles=roles) + for record in self._records: + digest = self._hash_key(key, record.salt) + if hmac.compare_digest(digest, record.digest): + return A2APrincipal( + subject=record.subject, + principal_type="service", + roles=record.roles, + ) + raise A2AAuthError("Invalid API key") async def authorize( self, @@ -157,8 +176,13 @@ def _get_header(self, headers: Mapping[str, str], target: str) -> str | None: return value return None - def _hash_key(self, key: str) -> str: - return hashlib.sha256(key.encode("utf-8")).hexdigest() + def _hash_key(self, key: str, salt: bytes) -> bytes: + return hashlib.pbkdf2_hmac( + "sha256", + key.encode("utf-8"), + salt, + 120_000, + ) class JWTA2AAuthProvider: @@ -205,7 +229,7 @@ async def authenticate(self, context: A2AAuthContext) -> A2APrincipal: options={"verify_signature": True}, ) except Exception as exc: - raise A2AAuthError(f"Invalid bearer token: {exc}") from exc + raise A2AAuthError("Invalid bearer token") from exc subject = claims.get(self._subject_claim) if not isinstance(subject, str) or not subject.strip(): diff --git a/src/afk/agents/a2a/server.py b/src/afk/agents/a2a/server.py index d404009..16b3f6e 100644 --- a/src/afk/agents/a2a/server.py +++ b/src/afk/agents/a2a/server.py @@ -8,6 +8,7 @@ from __future__ import annotations +import json from dataclasses import asdict from typing import Any @@ -51,12 +52,30 @@ def create_app(self): """Create and return FastAPI app exposing A2A endpoints.""" try: from fastapi import FastAPI, HTTPException + from fastapi.responses import StreamingResponse except Exception as exc: # pragma: no cover - optional runtime path raise A2AServiceHostError( "FastAPI is required to host A2A service endpoints" ) from exc - app = FastAPI(title=self.service_name) + app = FastAPI( + title=self.service_name, + docs_url=None if self.production_mode else "/docs", + redoc_url=None if self.production_mode else "/redoc", + openapi_url=None if self.production_mode else "/openapi.json", + ) + + def _auth_detail(default: str, exc: Exception | None = None) -> str: + if self.production_mode: + return default + if exc is None: + return default + return str(exc) + + def _validation_detail(operation: str, exc: Exception) -> str: + if self.production_mode: + return f"Invalid {operation} payload" + return f"Invalid {operation} payload: {exc}" async def _authorize( request: Request, @@ -72,7 +91,10 @@ async def _authorize( try: principal = await self.auth_provider.authenticate(auth_context) except A2AAuthError as exc: - raise HTTPException(status_code=401, detail=str(exc)) from exc + raise HTTPException( + status_code=401, + detail=_auth_detail("Authentication failed", exc), + ) from exc decision = await self.auth_provider.authorize( principal, @@ -83,7 +105,11 @@ async def _authorize( if not decision.allowed: raise HTTPException( status_code=403, - detail=decision.reason or "Forbidden", + detail=( + "Forbidden" + if self.production_mode + else decision.reason or "Forbidden" + ), ) @app.get("/.well-known/agent-card") @@ -106,7 +132,7 @@ async def invoke(payload: dict[str, Any], request: Request) -> dict[str, Any]: invocation = AgentInvocationRequest(**payload) except Exception as exc: raise HTTPException( - status_code=422, detail=f"Invalid invoke payload: {exc}" + status_code=422, detail=_validation_detail("invoke", exc) ) from exc response = await self.protocol.invoke(invocation) return asdict(response) @@ -125,13 +151,18 @@ async def invoke_stream( invocation = AgentInvocationRequest(**payload) except Exception as exc: raise HTTPException( - status_code=422, detail=f"Invalid invoke payload: {exc}" + status_code=422, detail=_validation_detail("invoke", exc) ) from exc - events: list[dict[str, Any]] = [] - async for event in self.protocol.invoke_stream(invocation): - events.append(asdict(event)) - return {"events": events} + async def _iter_events(): + async for event in self.protocol.invoke_stream(invocation): + yield json.dumps(asdict(event), ensure_ascii=True, default=str) + yield "\n" + + return StreamingResponse( + _iter_events(), + media_type="application/x-ndjson", + ) @app.get("/a2a/tasks/{task_id}") async def get_task(task_id: str, request: Request) -> dict[str, Any]: diff --git a/src/afk/agents/agent_contract.py b/src/afk/agents/agent_contract.py deleted file mode 100644 index f12661a..0000000 --- a/src/afk/agents/agent_contract.py +++ /dev/null @@ -1,253 +0,0 @@ -""" -MIT License -Copyright (c) 2026 arpan404 -See LICENSE file for full license text. - -Formal agent contract specification and verification. -""" - -from __future__ import annotations - -import asyncio -import inspect -from dataclasses import dataclass, field -from typing import Any, Callable, Protocol - -from ..llms.types import JSONValue - - -class Predicate(Protocol): - """A callable that returns True if the predicate is satisfied.""" - - def __call__(self, context: dict[str, Any]) -> bool: ... - - -@dataclass(frozen=True, slots=True) -class AgentContract: - """ - Formal specification of an agent's behavior contract. - - AgentContract allows verification that an agent behaves according to - a specified contract with preconditions, postconditions, and invariants. - - Attributes: - name: Contract name for identification. - description: Human-readable contract description. - preconditions: Conditions that must be true before execution. - postconditions: Conditions that must be true after execution. - invariants: Conditions that must remain true during execution. - max_execution_time_s: Maximum allowed execution time. - allowed_tools: Set of tool names this agent is allowed to call. - forbidden_tools: Set of tool names this agent must never call. - max_cost_usd: Maximum allowed cost in USD. - metadata: Additional contract metadata. - """ - - name: str - description: str = "" - preconditions: list[Callable[[dict[str, Any]], bool]] = field(default_factory=list) - postconditions: list[Callable[[dict[str, Any]], bool]] = field(default_factory=list) - invariants: list[Callable[[dict[str, Any]], bool]] = field(default_factory=list) - max_execution_time_s: float | None = None - allowed_tools: list[str] | None = None - forbidden_tools: list[str] = field(default_factory=list) - max_cost_usd: float | None = None - metadata: dict[str, JSONValue] = field(default_factory=dict) - - -@dataclass(frozen=True, slots=True) -class ContractViolation: - """ - Record of a contract violation. - - Attributes: - contract_name: Name of the violated contract. - violation_type: Type of violation (precondition, postcondition, invariant). - predicate_name: Name of the predicate that failed. - message: Human-readable violation message. - context_snapshot: Context at time of violation. - timestamp_ms: When violation occurred. - """ - - contract_name: str - violation_type: str - predicate_name: str - message: str - context_snapshot: dict[str, Any] = field(default_factory=dict) - timestamp_ms: int = field(default_factory=lambda: int(__import__("time").time() * 1000)) - - -@dataclass -class ContractVerifier: - """ - Verifies agent behavior against an AgentContract. - - Provides both pre-execution validation and post-execution verification, - with support for collecting violations for later analysis. - """ - - def __init__(self, contract: AgentContract) -> None: - self._contract = contract - self._violations: list[ContractViolation] = [] - - def validate_preconditions(self, context: dict[str, Any]) -> list[ContractViolation]: - """ - Check all preconditions against the given context. - - Args: - context: Execution context to validate against. - - Returns: - List of violations found (empty if all pass). - """ - violations = [] - for pred in self._contract.preconditions: - pred_name = getattr(pred, "__name__", repr(pred)) - try: - if not pred(context): - violations.append( - ContractViolation( - contract_name=self._contract.name, - violation_type="precondition", - predicate_name=pred_name, - message=f"Precondition '{pred_name}' was not satisfied", - context_snapshot=dict(context), - ) - ) - except Exception as e: - violations.append( - ContractViolation( - contract_name=self._contract.name, - violation_type="precondition", - predicate_name=pred_name, - message=f"Precondition '{pred_name}' raised error: {e}", - context_snapshot=dict(context), - ) - ) - self._violations.extend(violations) - return violations - - def validate_postconditions( - self, context: dict[str, Any] - ) -> list[ContractViolation]: - """ - Check all postconditions against the given context. - - Args: - context: Execution context to validate against. - - Returns: - List of violations found (empty if all pass). - """ - violations = [] - for pred in self._contract.postconditions: - pred_name = getattr(pred, "__name__", repr(pred)) - try: - if not pred(context): - violations.append( - ContractViolation( - contract_name=self._contract.name, - violation_type="postcondition", - predicate_name=pred_name, - message=f"Postcondition '{pred_name}' was not satisfied", - context_snapshot=dict(context), - ) - ) - except Exception as e: - violations.append( - ContractViolation( - contract_name=self._contract.name, - violation_type="postcondition", - predicate_name=pred_name, - message=f"Postcondition '{pred_name}' raised error: {e}", - context_snapshot=dict(context), - ) - ) - self._violations.extend(violations) - return violations - - def validate_invariants( - self, context: dict[str, Any] - ) -> list[ContractViolation]: - """ - Check all invariants against the given context. - - Args: - context: Execution context to validate against. - - Returns: - List of violations found (empty if all pass). - """ - violations = [] - for pred in self._contract.invariants: - pred_name = getattr(pred, "__name__", repr(pred)) - try: - if not pred(context): - violations.append( - ContractViolation( - contract_name=self._contract.name, - violation_type="invariant", - predicate_name=pred_name, - message=f"Invariant '{pred_name}' was violated", - context_snapshot=dict(context), - ) - ) - except Exception as e: - violations.append( - ContractViolation( - contract_name=self._contract.name, - violation_type="invariant", - predicate_name=pred_name, - message=f"Invariant '{pred_name}' raised error: {e}", - context_snapshot=dict(context), - ) - ) - self._violations.extend(violations) - return violations - - def validate_tool_allowed(self, tool_name: str) -> bool: - """ - Check if a tool is allowed by this contract. - - Args: - tool_name: Name of the tool to check. - - Returns: - True if tool is allowed. - """ - if self._contract.forbidden_tools and tool_name in self._contract.forbidden_tools: - return False - if self._contract.allowed_tools is not None: - return tool_name in self._contract.allowed_tools - return True - - def get_violations(self) -> list[ContractViolation]: - """Get all collected violations.""" - return list(self._violations) - - def clear_violations(self) -> None: - """Clear collected violations.""" - self._violations.clear() - - -def contract_to_predicate(contract: AgentContract) -> Callable[[dict[str, Any]], bool]: - """ - Convert an AgentContract into a single predicate function. - - This is useful for composing contracts together or using them - in policy engines. - - Args: - contract: The contract to convert. - - Returns: - A predicate that returns True if all preconditions and invariants pass. - """ - - def predicate(context: dict[str, Any]) -> bool: - verifier = ContractVerifier(contract) - pre_violations = verifier.validate_preconditions(context) - inv_violations = verifier.validate_invariants(context) - return len(pre_violations) == 0 and len(inv_violations) == 0 - - return predicate \ No newline at end of file diff --git a/src/afk/agents/core/registry.py b/src/afk/agents/core/registry.py deleted file mode 100644 index 2c26b94..0000000 --- a/src/afk/agents/core/registry.py +++ /dev/null @@ -1,248 +0,0 @@ -""" -MIT License -Copyright (c) 2026 arpan404 -See LICENSE file for full license text. - -Agent registry and discovery for runtime agent management. -""" - -from __future__ import annotations - -import asyncio -from dataclasses import dataclass, field -from typing import Any - -from .base import BaseAgent - - -@dataclass -class AgentRegistration: - """ - Registration entry for an agent in the registry. - - Attributes: - agent: The agent instance. - name: Agent name (unique identifier). - description: Human-readable description. - tags: Tags for categorization and discovery. - created_at_ms: Registration timestamp. - metadata: Additional metadata. - """ - - agent: BaseAgent - name: str - description: str = "" - tags: list[str] = field(default_factory=list) - created_at_ms: int = field(default_factory=lambda: int(__import__("time").time() * 1000)) - metadata: dict[str, Any] = field(default_factory=dict) - - -@dataclass -class AgentRegistry: - """ - Thread-safe registry for agent instances. - - Provides registration, lookup, and discovery of agents at runtime. - Supports tags for categorical lookup and metadata for extensibility. - """ - - def __init__(self) -> None: - self._agents: dict[str, AgentRegistration] = {} - self._tags_index: dict[str, set[str]] = {} # tag -> agent names - self._lock = asyncio.Lock() - - async def register( - self, - agent: BaseAgent, - name: str | None = None, - description: str = "", - tags: list[str] | None = None, - metadata: dict[str, Any] | None = None, - ) -> str: - """ - Register an agent with the registry. - - Args: - agent: Agent instance to register. - name: Optional name override (defaults to agent.name). - description: Human-readable description. - tags: Tags for categorization. - metadata: Additional metadata. - - Returns: - The registered agent name. - - Raises: - ValueError: If agent has no name and no name provided. - """ - agent_name = name or agent.name - if not agent_name: - raise ValueError("Agent must have a name or a name must be provided") - - async with self._lock: - reg = AgentRegistration( - agent=agent, - name=agent_name, - description=description, - tags=tags or [], - metadata=metadata or {}, - ) - self._agents[agent_name] = reg - - # Update tags index - for tag in reg.tags: - if tag not in self._tags_index: - self._tags_index[tag] = set() - self._tags_index[tag].add(agent_name) - - return agent_name - - async def unregister(self, name: str) -> bool: - """ - Remove an agent from the registry. - - Args: - name: Agent name to remove. - - Returns: - True if agent was removed, False if not found. - """ - async with self._lock: - if name not in self._agents: - return False - - reg = self._agents.pop(name) - - # Remove from tags index - for tag in reg.tags: - if tag in self._tags_index: - self._tags_index[tag].discard(name) - if not self._tags_index[tag]: - del self._tags_index[tag] - - return True - - async def get(self, name: str) -> BaseAgent | None: - """ - Get an agent by name. - - Args: - name: Agent name. - - Returns: - Agent instance or None if not found. - """ - async with self._lock: - reg = self._agents.get(name) - return reg.agent if reg else None - - async def find_by_tag(self, tag: str) -> list[BaseAgent]: - """ - Find all agents with a specific tag. - - Args: - tag: Tag to search for. - - Returns: - List of matching agent instances. - """ - async with self._lock: - names = self._tags_index.get(tag, set()) - return [self._agents[name].agent for name in names if name in self._agents] - - async def find_by_tags(self, tags: list[str], match_all: bool = False) -> list[BaseAgent]: - """ - Find agents matching tag criteria. - - Args: - tags: Tags to search for. - match_all: If True, agents must have all tags. If False, any tag matches. - - Returns: - List of matching agent instances. - """ - async with self._lock: - if match_all: - matching_names = None - for tag in tags: - tag_names = self._tags_index.get(tag, set()) - if matching_names is None: - matching_names = tag_names - else: - matching_names = matching_names.intersection(tag_names) - if matching_names is None: - return [] - else: - matching_names: set[str] = set() - for tag in tags: - matching_names.update(self._tags_index.get(tag, set())) - - return [ - self._agents[name].agent - for name in matching_names - if name in self._agents - ] - - async def list_agents(self) -> list[str]: - """ - List all registered agent names. - - Returns: - List of agent names. - """ - async with self._lock: - return list(self._agents.keys()) - - async def list_tags(self) -> list[str]: - """ - List all known tags. - - Returns: - List of tag names. - """ - async with self._lock: - return list(self._tags_index.keys()) - - async def get_metadata(self, name: str) -> dict[str, Any] | None: - """ - Get metadata for a registered agent. - - Args: - name: Agent name. - - Returns: - Metadata dict or None if not found. - """ - async with self._lock: - reg = self._agents.get(name) - return dict(reg.metadata) if reg else None - - async def update_metadata(self, name: str, metadata: dict[str, Any]) -> bool: - """ - Update metadata for a registered agent. - - Args: - name: Agent name. - metadata: Metadata to merge. - - Returns: - True if updated, False if agent not found. - """ - async with self._lock: - reg = self._agents.get(name) - if not reg: - return False - reg.metadata.update(metadata) - return True - - -# Global registry instance -_global_registry: AgentRegistry | None = None - - -def get_agent_registry() -> AgentRegistry: - """Get the global agent registry instance.""" - global _global_registry - if _global_registry is None: - _global_registry = AgentRegistry() - return _global_registry \ No newline at end of file diff --git a/src/afk/agents/delegation_planner.py b/src/afk/agents/delegation_planner.py deleted file mode 100644 index 353f65c..0000000 --- a/src/afk/agents/delegation_planner.py +++ /dev/null @@ -1,312 +0,0 @@ -""" -MIT License -Copyright (c) 2026 arpan404 -See LICENSE file for full license text. - -Goal decomposition planner using LLM-assisted HTN-style planning. -""" - -from __future__ import annotations - -import asyncio -import uuid -from dataclasses import dataclass, field -from typing import Any - -from ..llms.types import JSONValue - - -@dataclass(frozen=True, slots=True) -class DecomposedTask: - """ - A single task from goal decomposition. - - Attributes: - id: Unique task identifier. - description: Human-readable task description. - dependencies: Task IDs that must complete before this task. - estimated_cost: Estimated LLM cost in USD. - estimated_duration_s: Estimated duration in seconds. - optional: Whether task failure should block completion. - metadata: Additional task metadata. - """ - - id: str - description: str - dependencies: list[str] = field(default_factory=list) - estimated_cost: float = 0.0 - estimated_duration_s: float = 0.0 - optional: bool = False - metadata: dict[str, Any] = field(default_factory=dict) - - -@dataclass(frozen=True, slots=True) -class DecompositionResult: - """ - Result of goal decomposition into tasks. - - Attributes: - original_goal: The goal that was decomposed. - tasks: Ordered list of decomposed tasks (topologically sorted). - estimated_total_cost: Sum of all task estimated costs. - estimated_total_duration_s: Sum of all task durations. - execution_order: Task IDs in execution order. - parallel_groups: Tasks that can run in parallel. - metadata: Additional decomposition metadata. - """ - - original_goal: str - tasks: list[DecomposedTask] = field(default_factory=list) - estimated_total_cost: float = 0.0 - estimated_total_duration_s: float = 0.0 - execution_order: list[str] = field(default_factory=list) - parallel_groups: list[list[str]] = field(default_factory=list) - metadata: dict[str, Any] = field(default_factory=dict) - - def to_delegation_plan(self) -> dict[str, Any]: - """ - Convert decomposition result to a delegation plan dict. - - Returns: - Dictionary suitable for passing to DelegationPlan.from_dict(). - """ - edges = [] - for task in self.tasks: - for dep in task.dependencies: - edges.append({"from": dep, "to": task.id}) - - return { - "nodes": [ - { - "node_id": task.id, - "target_agent": task.metadata.get("agent", "default"), - "description": task.description, - "optional": task.optional, - "timeout_s": task.estimated_duration_s, - } - for task in self.tasks - ], - "edges": edges, - "execution_order": self.execution_order, - "parallel_groups": self.parallel_groups, - } - - -@dataclass -class GoalDecomposer: - """ - LLM-assisted goal decomposition planner. - - Takes a high-level goal and decomposes it into a DAG of tasks - using HTN-style planning with LLM assistance. - """ - - def __init__(self, llm_client: Any | None = None, max_depth: int = 5) -> None: - """ - Initialize goal decomposer. - - Args: - llm_client: LLM client for decomposition assistance. - max_depth: Maximum decomposition depth. - """ - self._llm = llm_client - self._max_depth = max_depth - - async def decompose( - self, - goal: str, - context: dict[str, Any] | None = None, - ) -> DecompositionResult: - """ - Decompose a goal into tasks. - - Args: - goal: High-level goal to decompose. - context: Optional context for decomposition. - - Returns: - DecompositionResult with tasks and execution plan. - """ - ctx = context or {} - - if self._llm: - tasks = await self._llm_decompose(goal, ctx) - else: - tasks = self._rule_based_decompose(goal, ctx) - - # Compute execution order via topological sort - execution_order = self._topological_sort(tasks) - - # Identify parallel groups - parallel_groups = self._identify_parallel_groups(tasks, execution_order) - - # Compute estimates - total_cost = sum(t.estimated_cost for t in tasks) - total_duration = sum(t.estimated_duration_s for t in tasks) - - return DecompositionResult( - original_goal=goal, - tasks=tasks, - estimated_total_cost=total_cost, - estimated_total_duration_s=total_duration, - execution_order=execution_order, - parallel_groups=parallel_groups, - metadata={"decomposer": "goal_decomposer", "depth": self._max_depth}, - ) - - async def _llm_decompose( - self, goal: str, context: dict[str, Any] - ) -> list[DecomposedTask]: - """Use LLM to decompose goal into tasks.""" - prompt = f"""Decompose the following goal into specific, actionable tasks. - -Goal: {goal} - -Context: {context} - -Return a JSON list of tasks, each with: -- id: unique identifier (e.g., "task-1") -- description: what this task entails -- dependencies: list of task IDs this depends on -- estimated_cost: approximate USD cost -- estimated_duration_s: approximate seconds to complete -- optional: whether task failure should block completion - -Format: JSON array of task objects.""" - - try: - response = await self._llm.chat( - messages=[{"role": "user", "content": prompt}], - model=context.get("model", "gpt-4.1-mini"), - ) - # Parse response and convert to DecomposedTask objects - import json - - task_data = json.loads(response.text) - tasks = [] - for item in task_data: - tasks.append( - DecomposedTask( - id=item.get("id", f"task-{len(tasks)+1}"), - description=item.get("description", ""), - dependencies=item.get("dependencies", []), - estimated_cost=float(item.get("estimated_cost", 0.0)), - estimated_duration_s=float(item.get("estimated_duration_s", 60.0)), - optional=bool(item.get("optional", False)), - metadata=item.get("metadata", {}), - ) - ) - return tasks - except Exception: - # Fallback to rule-based on LLM failure - return self._rule_based_decompose(goal, context) - - def _rule_based_decompose( - self, goal: str, context: dict[str, Any] - ) -> list[DecomposedTask]: - """Simple rule-based decomposition fallback.""" - tasks = [] - - # Simple heuristic: split by common conjunctions - import re - - segments = re.split(r"\s+(?:and|then|also|plus|additionally)\s+", goal, flags=re.IGNORECASE) - - for i, segment in enumerate(segments): - segment = segment.strip() - if not segment: - continue - - task_id = f"task-{i+1}" - deps = [f"task-{j}" for j in range(1, i + 1)] - - tasks.append( - DecomposedTask( - id=task_id, - description=segment, - dependencies=deps, - estimated_cost=0.01 * (i + 1), - estimated_duration_s=30.0 * (i + 1), - metadata={"source": "rule_based"}, - ) - ) - - return tasks - - def _topological_sort(self, tasks: list[DecomposedTask]) -> list[str]: - """Compute topological sort of tasks.""" - # Build adjacency list - in_degree: dict[str, int] = {t.id: 0 for t in tasks} - adj: dict[str, list[str]] = {t.id: [] for t in tasks} - - for task in tasks: - for dep in task.dependencies: - if dep in adj: - adj[dep].append(task.id) - in_degree[task.id] += 1 - - # Kahn's algorithm - queue = [tid for tid, deg in in_degree.items() if deg == 0] - result = [] - - while queue: - # Sort for deterministic ordering - queue.sort() - tid = queue.pop(0) - result.append(tid) - - for neighbor in adj[tid]: - in_degree[neighbor] -= 1 - if in_degree[neighbor] == 0: - queue.append(neighbor) - - return result - - def _identify_parallel_groups( - self, tasks: list[DecomposedTask], execution_order: list[str] - ) -> list[list[str]]: - """Identify tasks that can execute in parallel.""" - task_map = {t.id: t for t in tasks} - completed = set() - groups = [] - - remaining = list(execution_order) - while remaining: - # Find tasks with all dependencies satisfied - ready = [] - for tid in remaining: - task = task_map[tid] - if all(dep in completed for dep in task.dependencies): - ready.append(tid) - - if not ready: - # Cycle detected, just add remaining - groups.append(remaining) - break - - groups.append(ready) - completed.update(ready) - remaining = [t for t in remaining if t not in ready] - - return groups - - async def refine_plan( - self, plan: DecompositionResult, feedback: str - ) -> DecompositionResult: - """ - Refine an existing plan based on feedback. - - Args: - plan: Existing plan to refine. - feedback: Feedback for improvement. - - Returns: - New refined DecompositionResult. - """ - # Simple refinement: re-decompose with context - context = { - "original_goal": plan.original_goal, - "feedback": feedback, - "current_tasks": [t.description for t in plan.tasks], - } - return await self.decompose(plan.original_goal, context) \ No newline at end of file diff --git a/src/afk/agents/eval/harness.py b/src/afk/agents/eval/harness.py index 5a86d1d..1f771a3 100644 --- a/src/afk/agents/eval/harness.py +++ b/src/afk/agents/eval/harness.py @@ -10,11 +10,12 @@ import asyncio import time +from collections.abc import Callable from dataclasses import dataclass, field -from typing import Any, Callable +from typing import Any -from ..types import AgentResult from ..core.base import BaseAgent +from ..types import AgentResult @dataclass @@ -281,7 +282,7 @@ async def _run_single_task( validation_details=validation_details, ) - except asyncio.TimeoutError: + except TimeoutError: return EvalResult( task_id=task.id, success=False, @@ -335,10 +336,7 @@ def validate_safety(result: AgentResult) -> bool: if not result or not result.final_text: return False text = result.final_text.lower() - for pattern in forbidden_patterns: - if pattern.lower() in text: - return False - return True + return all(pattern.lower() not in text for pattern in forbidden_patterns) return EvalTask( id=task_id, @@ -380,4 +378,4 @@ def validate_correctness(result: AgentResult) -> bool: prompt=prompt, validation_fn=validate_correctness, tags=["correctness", "quality"], - ) \ No newline at end of file + ) diff --git a/src/afk/agents/lifecycle/__init__.py b/src/afk/agents/lifecycle/__init__.py index 304525a..768265e 100644 --- a/src/afk/agents/lifecycle/__init__.py +++ b/src/afk/agents/lifecycle/__init__.py @@ -1,3 +1,17 @@ +from .replay import ( + CheckpointInfo, + CheckpointReplayHandle, + CheckpointReplayManager, +) +from .replay_api import ( + InteractiveReplayHandler, + ReplayAPI, + ReplayDecisionPoint, + ReplaySession, + ReplayTimeline, + ReplayTimelineEvent, + create_replay_api, +) from .runtime import ( CircuitBreaker, EffectJournal, @@ -23,20 +37,6 @@ migrate_checkpoint_record, migrate_event_record, ) -from .replay import ( - CheckpointInfo, - CheckpointReplayHandle, - CheckpointReplayManager, -) -from .replay_api import ( - ReplayAPI, - ReplayDecisionPoint, - ReplaySession, - ReplayTimeline, - ReplayTimelineEvent, - InteractiveReplayHandler, - create_replay_api, -) __all__ = [ "CircuitBreaker", diff --git a/src/afk/agents/lifecycle/replay.py b/src/afk/agents/lifecycle/replay.py index ab1c299..9a10bad 100644 --- a/src/afk/agents/lifecycle/replay.py +++ b/src/afk/agents/lifecycle/replay.py @@ -8,7 +8,6 @@ from __future__ import annotations -import asyncio from dataclasses import dataclass from typing import Any diff --git a/src/afk/agents/lifecycle/replay_api.py b/src/afk/agents/lifecycle/replay_api.py index e2ae2dd..b824bbf 100644 --- a/src/afk/agents/lifecycle/replay_api.py +++ b/src/afk/agents/lifecycle/replay_api.py @@ -8,15 +8,13 @@ from __future__ import annotations -import asyncio import time from dataclasses import dataclass, field from datetime import datetime -from typing import Any, Callable +from typing import Any from ..errors import AgentCheckpointCorruptionError from .replay import ( - CheckpointInfo, CheckpointReplayHandle, CheckpointReplayManager, ) diff --git a/src/afk/agents/policy/__init__.py b/src/afk/agents/policy/__init__.py index 42cb52f..3cfe529 100644 --- a/src/afk/agents/policy/__init__.py +++ b/src/afk/agents/policy/__init__.py @@ -1,13 +1,3 @@ -from .engine import ( - PolicyEngine, - PolicyEvaluation, - PolicyRule, - PolicyRuleCondition, - PolicySubject, - infer_policy_subject, - normalize_policy_payload, -) - from .audit import ( AuditAction, AuditConfig, @@ -19,6 +9,15 @@ PolicyAuditLogger, create_policy_audit_logger, ) +from .engine import ( + PolicyEngine, + PolicyEvaluation, + PolicyRule, + PolicyRuleCondition, + PolicySubject, + infer_policy_subject, + normalize_policy_payload, +) __all__ = [ "PolicyEngine", diff --git a/src/afk/agents/policy/audit.py b/src/afk/agents/policy/audit.py index 5526473..2e4a587 100644 --- a/src/afk/agents/policy/audit.py +++ b/src/afk/agents/policy/audit.py @@ -11,16 +11,27 @@ import asyncio import json import time +import os +from dataclasses import asdict, is_dataclass +import dataclasses +from collections.abc import Callable from dataclasses import dataclass, field -from datetime import datetime -from enum import Enum +from datetime import UTC, datetime +from enum import StrEnum from pathlib import Path -from typing import Any, Callable +from typing import Any + +from pydantic import BaseModel from ..types import JSONValue, PolicyDecision, PolicyEvent -class AuditAction(str, Enum): +def _utc_timestamp_iso() -> str: + """Return a timezone-aware UTC timestamp for audit records.""" + return datetime.now(UTC).isoformat() + + +class AuditAction(StrEnum): """Actions captured in audit log.""" POLICY_EVALUATED = "policy_evaluated" @@ -36,7 +47,7 @@ class AuditAction(str, Enum): APPROVAL_DENIED = "approval_denied" -class AuditLevel(str, Enum): +class AuditLevel(StrEnum): """Audit log levels.""" DEBUG = "debug" @@ -77,7 +88,7 @@ class AuditConfig: enabled: bool = True min_level: str = "info" # Capture info and above - include_payloads: bool = True + include_payloads: bool = False max_payload_size: int = 10_000 retention_days: int = 90 sink: str = "console" # console, file, syslog, custom @@ -156,8 +167,15 @@ async def _flush(self) -> None: path = Path(self._config.file_path) path.parent.mkdir(parents=True, exist_ok=True) + if not path.exists(): + fd = os.open(path, os.O_CREAT | os.O_APPEND | os.O_WRONLY, 0o600) + os.close(fd) + try: + path.chmod(0o600) + except Exception: + pass - with open(path, "a") as f: + with open(path, "a", encoding="utf-8") as f: for record in self._buffer: line = json.dumps( { @@ -187,19 +205,54 @@ async def _flush(self) -> None: def _redact(self, data: dict[str, JSONValue]) -> dict[str, JSONValue]: """Redact sensitive fields.""" + return { + str(key): self._redact_value(key, value) + for key, value in data.items() + } + + def _redact_value(self, key: str, value: Any) -> JSONValue: import re - def _redact_value(key: str, value: Any) -> Any: - key_lower = key.lower() - for pattern in self._config.redact_patterns: - if re.search(pattern, key_lower): - return "[REDACTED]" + if isinstance(value, str): + if self._is_sensitive_key(key) or self._is_sensitive_value(value): + return "[REDACTED]" + if len(value) > self._config.max_payload_size: + return ( + value[: self._config.max_payload_size] + + f"... [truncated {len(value) - self._config.max_payload_size} chars]" + ) return value - result = {} - for key, value in data.items(): - result[key] = _redact_value(key, value) - return result + if isinstance(value, dict): + return { + str(k): self._redact_value(str(k), v) + for k, v in value.items() + } + + if isinstance(value, list): + return [self._redact_value(key, item) for item in value] + + if is_dataclass(value): + return self._redact_value(key, dataclasses.asdict(value)) + + if isinstance(value, BaseModel): + return self._redact_value(key, value.model_dump(mode="python")) + + return value + + def _is_sensitive_key(self, key: str) -> bool: + lowered = key.lower() + import re + + return any(re.search(pattern, lowered) for pattern in self._config.redact_patterns) + + def _is_sensitive_value(self, value: str) -> bool: + import re + + if len(value) < 16: + return False + patterns = (r"eyJhbGci", r"sk_live", r"AKIA[0-9A-Z]{16}") + return any(re.search(pattern, value, flags=re.IGNORECASE) for pattern in patterns) async def close(self) -> None: """Flush and close.""" @@ -275,7 +328,7 @@ async def log_policy_decision( record = AuditRecord( id=f"audit-{int(time.time() * 1000)}-{len(self._records)}", timestamp_ms=int(time.time() * 1000), - timestamp_iso=datetime.utcnow().isoformat(), + timestamp_iso=_utc_timestamp_iso(), level=level.value, action=action.value, actor=actor, @@ -316,7 +369,7 @@ async def log_tool_execution( record = AuditRecord( id=f"audit-{int(time.time() * 1000)}-{len(self._records)}", timestamp_ms=int(time.time() * 1000), - timestamp_iso=datetime.utcnow().isoformat(), + timestamp_iso=_utc_timestamp_iso(), level=level.value, action=action.value, actor=None, @@ -354,7 +407,7 @@ async def log_approval( record = AuditRecord( id=f"audit-{int(time.time() * 1000)}-{len(self._records)}", timestamp_ms=int(time.time() * 1000), - timestamp_iso=datetime.utcnow().isoformat(), + timestamp_iso=_utc_timestamp_iso(), level=level.value, action=action.value, actor=None, diff --git a/src/afk/agents/prompts/store.py b/src/afk/agents/prompts/store.py index c86e617..24b3d3d 100644 --- a/src/afk/agents/prompts/store.py +++ b/src/afk/agents/prompts/store.py @@ -103,8 +103,8 @@ def resolve_prompt_file_path( """ Resolve the target prompt file path with root-constrained access checks. - Absolute paths bypass the security check but still require the file to exist. - Relative paths are resolved against prompt_root and checked for directory escape. + Both absolute and relative paths must resolve inside prompt_root. + Relative paths are resolved against prompt_root before the containment check. """ if instruction_file is not None: candidate = Path(instruction_file) @@ -113,19 +113,11 @@ def resolve_prompt_file_path( candidate = Path(derive_auto_prompt_filename(agent_name)) source = "auto_prompt" - # Absolute paths bypass security check but must exist if candidate.is_absolute(): resolved = candidate.resolve() - if not resolved.exists() or not resolved.is_file(): - raise PromptResolutionError( - f"prompt file not found for {source} " - f"(path='{resolved}')" - ) - return resolved + else: + resolved = (prompt_root / candidate).resolve() - # Relative paths: resolve against root and check for escape - target = prompt_root / candidate - resolved = target.resolve() if not _is_under(resolved, prompt_root): raise PromptAccessError( f"{source} path escapes configured prompts root " diff --git a/src/afk/agents/types/memory.py b/src/afk/agents/types/memory.py index 0baaa66..0c5f39a 100644 --- a/src/afk/agents/types/memory.py +++ b/src/afk/agents/types/memory.py @@ -11,8 +11,6 @@ from dataclasses import dataclass, field from typing import Any -from .common import AgentState - @dataclass(frozen=True, slots=True) class ScoredMemoryEvent: diff --git a/src/afk/agents/workflow/__init__.py b/src/afk/agents/workflow/__init__.py index fb09e1e..8657af5 100644 --- a/src/afk/agents/workflow/__init__.py +++ b/src/afk/agents/workflow/__init__.py @@ -6,6 +6,12 @@ Workflow module exports. """ +from .executor import ( + WorkflowExecutionContext, + WorkflowExecutionResult, + WorkflowExecutor, + create_workflow_executor, +) from .state_machine import ( WorkflowBuilder, WorkflowEdge, @@ -15,12 +21,6 @@ WorkflowState, WorkflowTransition, ) -from .executor import ( - WorkflowExecutionContext, - WorkflowExecutionResult, - WorkflowExecutor, - create_workflow_executor, -) __all__ = [ "WorkflowBuilder", diff --git a/src/afk/agents/workflow/executor.py b/src/afk/agents/workflow/executor.py index 57140fb..e47d289 100644 --- a/src/afk/agents/workflow/executor.py +++ b/src/afk/agents/workflow/executor.py @@ -10,12 +10,11 @@ import asyncio import time +from collections.abc import Callable from dataclasses import dataclass, field -from typing import Any, Callable +from typing import Any from .state_machine import ( - WorkflowEdge, - WorkflowEvent, WorkflowNode, WorkflowSpec, WorkflowState, @@ -188,7 +187,7 @@ async def _execute_node( try: async with asyncio.timeout(node.timeout_s): return await self._run_node_executor(context, node) - except asyncio.TimeoutError: + except TimeoutError: # Check retry if node.can_retry(): node.increment_retry() diff --git a/src/afk/agents/workflow/state_machine.py b/src/afk/agents/workflow/state_machine.py index f9d34fa..882404c 100644 --- a/src/afk/agents/workflow/state_machine.py +++ b/src/afk/agents/workflow/state_machine.py @@ -9,11 +9,11 @@ from __future__ import annotations from dataclasses import dataclass, field -from enum import Enum -from typing import Any, Callable +from enum import StrEnum +from typing import Any -class WorkflowState(str, Enum): +class WorkflowState(StrEnum): """States in a workflow state machine.""" PENDING = "pending" @@ -25,7 +25,7 @@ class WorkflowState(str, Enum): CANCELLED = "cancelled" -class WorkflowEvent(str, Enum): +class WorkflowEvent(StrEnum): """Events that transition workflow state.""" START = "start" diff --git a/src/afk/config.py b/src/afk/config.py index 8bf003e..221d4f6 100644 --- a/src/afk/config.py +++ b/src/afk/config.py @@ -12,7 +12,8 @@ from __future__ import annotations import os -from typing import Any, Callable, TypeVar +from collections.abc import Callable +from typing import Any, ClassVar, TypeVar T = TypeVar("T") @@ -137,7 +138,7 @@ class MCPServerEnv(Settings): ) AFK_MCP_NAME: str = EnvVarField("AFK_MCP_NAME", default="afk-mcp-server") AFK_MCP_VERSION: str = EnvVarField("AFK_MCP_VERSION", default="1.0.0") - AFK_MCP_HOST: str = EnvVarField("AFK_MCP_HOST", default="0.0.0.0") + AFK_MCP_HOST: str = EnvVarField("AFK_MCP_HOST", default="127.0.0.1") AFK_MCP_PORT: int = EnvVarField("AFK_MCP_PORT", default=8000, parser=_int) AFK_MCP_INSTRUCTIONS: str | None = EnvVarField( "AFK_MCP_INSTRUCTIONS", default=None @@ -167,7 +168,7 @@ class RunnerEnv(Settings): class MemoryEnv(Settings): """Environment variable bindings for memory stores and queues.""" - AFK_MEMORY_BACKEND: str = EnvVarField("AFK_MEMORY_BACKEND", default="sqlite") + AFK_MEMORY_BACKEND: str = EnvVarField("AFK_MEMORY_BACKEND", default="memory") AFK_SQLITE_PATH: str = EnvVarField("AFK_SQLITE_PATH", default="afk_memory.sqlite3") AFK_REDIS_URL: str | None = EnvVarField("AFK_REDIS_URL", default=None) AFK_REDIS_HOST: str = EnvVarField("AFK_REDIS_HOST", default="localhost") diff --git a/src/afk/core/runner/execution.py b/src/afk/core/runner/execution.py index bccd3ca..8a7f23a 100644 --- a/src/afk/core/runner/execution.py +++ b/src/afk/core/runner/execution.py @@ -344,7 +344,21 @@ async def _execute( raise AgentExecutionError( f"Failed to load MCP tools for agent '{agent.name}': {e}" ) from e - extra_tools.extend(build_runtime_tools(root_dir=Path.cwd())) + + runtime_tool_names: set[str] = set() + runtime_tool_root: Path | None = None + + if self.config.enable_runtime_tools and self.config.runtime_tool_root.strip(): + try: + runtime_tool_root = Path(self.config.runtime_tool_root).expanduser().resolve() + runtime_tools = build_runtime_tools(root_dir=runtime_tool_root) + runtime_tool_names = {tool.spec.name for tool in runtime_tools} + extra_tools.extend(runtime_tools) + except Exception as e: + raise AgentExecutionError( + f"Failed to initialize runtime tools for agent '{agent.name}': {e}" + ) from e + registry = agent.build_tool_registry( extra_tools=extra_tools, ) @@ -1264,19 +1278,25 @@ async def _ingest_background_rows( if decision.updated_tool_args is not None: raw_args = dict(decision.updated_tool_args) + is_runtime_tool = tool_name in runtime_tool_names + default_profile = self.config.default_sandbox_profile + if is_runtime_tool and self.config.runtime_tool_sandbox_profile is not None: + default_profile = self.config.runtime_tool_sandbox_profile + effective_sandbox_profile = resolve_sandbox_profile( tool_name=tool_name, tool_args=raw_args, run_context=ctx, - default_profile=self.config.default_sandbox_profile, + default_profile=default_profile, provider=self.config.sandbox_profile_provider, ) if effective_sandbox_profile is not None: + cwd = runtime_tool_root or Path.cwd() sandbox_violation = validate_tool_args_against_sandbox( tool_name=tool_name, tool_args=raw_args, profile=effective_sandbox_profile, - cwd=Path.cwd(), + cwd=cwd, ) if sandbox_violation is not None: record = ToolExecutionRecord( diff --git a/src/afk/core/runner/types.py b/src/afk/core/runner/types.py index 00fbeac..ff8294b 100644 --- a/src/afk/core/runner/types.py +++ b/src/afk/core/runner/types.py @@ -106,6 +106,9 @@ class RunnerConfig: default_sandbox_profile: SandboxProfile | None = None sandbox_profile_provider: SandboxProfileProvider | None = None secret_scope_provider: SecretScopeProvider | None = None + enable_runtime_tools: bool = False + runtime_tool_root: str = "" + runtime_tool_sandbox_profile: SandboxProfile | None = None default_allowlisted_commands: tuple[str, ...] = ( "ls", "cat", diff --git a/src/afk/llms/__init__.py b/src/afk/llms/__init__.py index d30f0bc..78bdc76 100644 --- a/src/afk/llms/__init__.py +++ b/src/afk/llms/__init__.py @@ -30,7 +30,7 @@ from .llm import LLM from .middleware import MiddlewareStack from .observability import LLMLifecycleEvent, LLMObserver -from .profiles import LLMProfile, PROFILES +from .profiles import PROFILES, LLMProfile from .providers import ( AnthropicAgentProvider, LiteLLMProvider, @@ -99,13 +99,13 @@ def create_llm_client( middlewares: MiddlewareStack | None = None, observers: list[LLMObserver] | None = None, router=None, - retry_policy: "RetryPolicy | None" = None, - timeout_policy: "TimeoutPolicy | None" = None, - rate_limit_policy: "RateLimitPolicy | None" = None, - circuit_breaker_policy: "CircuitBreakerPolicy | None" = None, - hedging_policy: "HedgingPolicy | None" = None, - cache_policy: "CachePolicy | None" = None, - coalescing_policy: "CoalescingPolicy | None" = None, + retry_policy: RetryPolicy | None = None, + timeout_policy: TimeoutPolicy | None = None, + rate_limit_policy: RateLimitPolicy | None = None, + circuit_breaker_policy: CircuitBreakerPolicy | None = None, + hedging_policy: HedgingPolicy | None = None, + cache_policy: CachePolicy | None = None, + coalescing_policy: CoalescingPolicy | None = None, ) -> LLMClient: """Create enterprise runtime client with explicit provider selection. diff --git a/src/afk/llms/cache/base.py b/src/afk/llms/cache/base.py index f0df653..22c37c5 100644 --- a/src/afk/llms/cache/base.py +++ b/src/afk/llms/cache/base.py @@ -8,7 +8,7 @@ from __future__ import annotations -from dataclasses import dataclass, field +from dataclasses import dataclass, field, replace from typing import Protocol from ..types import JSONValue, LLMResponse @@ -33,3 +33,15 @@ async def get(self, key: str) -> LLMResponse | None: ... async def set(self, key: str, value: LLMResponse, *, ttl_s: float) -> None: ... async def delete(self, key: str) -> None: ... + + +def cache_safe_response(value: LLMResponse) -> LLMResponse: + """Strip request-scoped provider metadata before storing a reusable cache row.""" + return replace( + value, + request_id=None, + provider_request_id=None, + session_token=None, + checkpoint_token=None, + raw={}, + ) diff --git a/src/afk/llms/cache/inmemory.py b/src/afk/llms/cache/inmemory.py index 464ec82..9c0585e 100644 --- a/src/afk/llms/cache/inmemory.py +++ b/src/afk/llms/cache/inmemory.py @@ -12,7 +12,7 @@ from dataclasses import dataclass from ..types import LLMResponse -from .base import CacheEntry, LLMCacheBackend +from .base import CacheEntry, LLMCacheBackend, cache_safe_response @dataclass(slots=True) @@ -46,7 +46,10 @@ async def set(self, key: str, value: LLMResponse, *, ttl_s: float) -> None: to_remove = len(self._rows) - self.max_size + 1 for k, _ in by_expiry[:to_remove]: del self._rows[k] - self._rows[key] = CacheEntry(value=value, expires_at_s=now + ttl_s) + self._rows[key] = CacheEntry( + value=cache_safe_response(value), + expires_at_s=now + ttl_s, + ) async def delete(self, key: str) -> None: self._rows.pop(key, None) diff --git a/src/afk/llms/cache/redis.py b/src/afk/llms/cache/redis.py index 18939f0..d791b5c 100644 --- a/src/afk/llms/cache/redis.py +++ b/src/afk/llms/cache/redis.py @@ -12,7 +12,7 @@ from dataclasses import dataclass from ..types import LLMResponse, ToolCall, Usage -from .base import LLMCacheBackend +from .base import LLMCacheBackend, cache_safe_response @dataclass(slots=True) @@ -62,25 +62,26 @@ async def get(self, key: str) -> LLMResponse | None: ) async def set(self, key: str, value: LLMResponse, *, ttl_s: float) -> None: + safe_value = cache_safe_response(value) payload = { - "text": value.text, - "request_id": value.request_id, - "provider_request_id": value.provider_request_id, - "session_token": value.session_token, - "checkpoint_token": value.checkpoint_token, - "structured_response": value.structured_response, + "text": safe_value.text, + "request_id": safe_value.request_id, + "provider_request_id": safe_value.provider_request_id, + "session_token": safe_value.session_token, + "checkpoint_token": safe_value.checkpoint_token, + "structured_response": safe_value.structured_response, "tool_calls": [ {"id": tc.id, "tool_name": tc.tool_name, "arguments": tc.arguments} - for tc in value.tool_calls + for tc in safe_value.tool_calls ], - "finish_reason": value.finish_reason, + "finish_reason": safe_value.finish_reason, "usage": { - "input_tokens": value.usage.input_tokens, - "output_tokens": value.usage.output_tokens, - "total_tokens": value.usage.total_tokens, + "input_tokens": safe_value.usage.input_tokens, + "output_tokens": safe_value.usage.output_tokens, + "total_tokens": safe_value.usage.total_tokens, }, - "raw": value.raw, - "model": value.model, + "raw": safe_value.raw, + "model": safe_value.model, } await self._redis.setex( key, int(max(1, ttl_s)), json.dumps(payload, ensure_ascii=True) diff --git a/src/afk/llms/cache/redis_pool.py b/src/afk/llms/cache/redis_pool.py new file mode 100644 index 0000000..6d945ba --- /dev/null +++ b/src/afk/llms/cache/redis_pool.py @@ -0,0 +1,205 @@ +""" +MIT License +Copyright (c) 2026 arpan404 +See LICENSE file for full license text. + +Module: llms/cache/redis_pool.py +""" + +from __future__ import annotations + +import asyncio +import logging +from collections.abc import AsyncIterator +from contextlib import asynccontextmanager +from dataclasses import dataclass, field +from typing import Any + +import redis.asyncio as redis + +logger = logging.getLogger("afk.redis.pool") + + +@dataclass +class PoolConfig: + """Configuration for Redis connection pool.""" + + max_connections: int = 50 + max_idle_connections: int = 10 + socket_keepalive: bool = True + socket_keepalive_options: dict[int, int] = field(default_factory=dict) + socket_connect_timeout: float = 5.0 + socket_timeout: float = 5.0 + retry_on_timeout: bool = True + health_check_interval: float = 30.0 + + +class RedisConnectionPool: + """ + Managed Redis connection pool with lifecycle control. + + Provides centralized connection pooling for high-throughput scenarios, + reducing connection overhead and preventing connection exhaustion. + + Usage: + pool = RedisConnectionPool("redis://localhost:6379/0") + async with pool.client() as redis_client: + await redis_client.get("key") + await pool.disconnect() + """ + + def __init__( + self, + url: str, + *, + config: PoolConfig | None = None, + ) -> None: + self._url = url + self._config = config or PoolConfig() + self._pool: redis.ConnectionPool | None = None + self._client: redis.Redis | None = None + self._lock = asyncio.Lock() + self._connected = False + + @property + def is_connected(self) -> bool: + return self._connected + + async def connect(self) -> None: + """Initialize the connection pool.""" + async with self._lock: + if self._connected: + return + + self._pool = redis.ConnectionPool.from_url( + self._url, + max_connections=self._config.max_connections, + max_idle_connections=self._config.max_idle_connections, + socket_keepalive=self._config.socket_keepalive, + socket_keepalive_options=self._config.socket_keepalive_options, + socket_connect_timeout=self._config.socket_connect_timeout, + socket_timeout=self._config.socket_timeout, + retry_on_timeout=self._config.retry_on_timeout, + health_check_interval=int(self._config.health_check_interval), + ) + self._client = redis.Redis(connection_pool=self._pool) + self._connected = True + logger.info( + "Redis pool connected (url=%s, max_connections=%d)", + self._url, + self._config.max_connections, + ) + + async def disconnect(self) -> None: + """Close the connection pool gracefully.""" + async with self._lock: + if not self._connected: + return + + if self._client: + await self._client.aclose() + if self._pool: + await self._pool.aclose() + + self._client = None + self._pool = None + self._connected = False + logger.info("Redis pool disconnected (url=%s)", self._url) + + @asynccontextmanager + async def client(self) -> AsyncIterator[redis.Redis]: + """ + Acquire a Redis client from the pool. + + Yields: + An async Redis client instance. + + Example: + pool = RedisConnectionPool("redis://localhost:6379/0") + async with pool.client() as redis: + await redis.set("key", "value") + value = await redis.get("key") + """ + if not self._connected: + await self.connect() + + client = self._client + if client is None: + raise RuntimeError("Redis client not initialized") + + yield client + + async def get_client(self) -> redis.Redis: + """ + Get a Redis client (non-context manager usage). + + Returns: + The shared Redis client instance. + """ + if not self._connected: + await self.connect() + client = self._client + if client is None: + raise RuntimeError("Redis client not initialized") + return client + + async def health_check(self) -> bool: + """ + Perform a health check on the connection. + + Returns: + True if the connection is healthy, False otherwise. + """ + try: + client = await self.get_client() + await client.ping() # type: ignore[await-only] + return True + except Exception as e: + logger.warning("Redis health check failed: %s", e) + return False + + @property + def pool_stats(self) -> dict[str, Any] | None: + """Return current pool statistics if available.""" + if self._pool is None: + return None + return { + "max_connections": self._config.max_connections, + "max_idle_connections": self._config.max_idle_connections, + "connected": self._connected, + } + + +_POOLS: dict[str, RedisConnectionPool] = {} +_POOLS_LOCK = asyncio.Lock() + + +async def get_redis_pool( + url: str, + *, + config: PoolConfig | None = None, +) -> RedisConnectionPool: + """ + Get or create a singleton Redis connection pool by URL. + + Args: + url: Redis connection URL. + config: Optional pool configuration. + + Returns: + The Redis connection pool instance. + """ + async with _POOLS_LOCK: + if url not in _POOLS: + _POOLS[url] = RedisConnectionPool(url, config=config) + await _POOLS[url].connect() + return _POOLS[url] + + +async def close_all_pools() -> None: + """Close all registered Redis connection pools.""" + async with _POOLS_LOCK: + for _, pool in _POOLS.items(): + await pool.disconnect() + _POOLS.clear() + logger.info("All Redis pools closed") diff --git a/src/afk/llms/factory.py b/src/afk/llms/factory.py index cb96a71..df1b43f 100644 --- a/src/afk/llms/factory.py +++ b/src/afk/llms/factory.py @@ -8,6 +8,8 @@ from __future__ import annotations +from typing import Any + from .errors import LLMConfigurationError @@ -19,21 +21,22 @@ def _removed() -> None: ) -def register_llm_adapter(*args, **kwargs): # type: ignore[no-untyped-def] +def register_llm_adapter(*args: Any, **kwargs: Any) -> None: """Removed legacy API shim.""" _removed() -def available_llm_adapters(*args, **kwargs): # type: ignore[no-untyped-def] +def available_llm_adapters(*args: Any, **kwargs: Any) -> list[str]: """Removed legacy API shim.""" _removed() + return [] -def create_llm(*args, **kwargs): # type: ignore[no-untyped-def] +def create_llm(*args: Any, **kwargs: Any) -> Any: """Removed legacy API shim.""" _removed() -def create_llm_from_env(*args, **kwargs): # type: ignore[no-untyped-def] +def create_llm_from_env(*args: Any, **kwargs: Any) -> Any: """Removed legacy API shim.""" _removed() diff --git a/src/afk/llms/middleware.py b/src/afk/llms/middleware/__init__.py similarity index 98% rename from src/afk/llms/middleware.py rename to src/afk/llms/middleware/__init__.py index 1d81b3b..debf1c4 100644 --- a/src/afk/llms/middleware.py +++ b/src/afk/llms/middleware/__init__.py @@ -12,7 +12,7 @@ from dataclasses import dataclass from typing import Protocol -from .types import ( +from ..types import ( EmbeddingRequest, EmbeddingResponse, LLMRequest, diff --git a/src/afk/llms/middleware/timeout.py b/src/afk/llms/middleware/timeout.py new file mode 100644 index 0000000..01a4ef1 --- /dev/null +++ b/src/afk/llms/middleware/timeout.py @@ -0,0 +1,185 @@ +""" +MIT License +Copyright (c) 2026 arpan404 +See LICENSE file for full license text. + +Module: llms/middleware/timeout.py + +Request timeout middleware for LLM client. +""" + +from __future__ import annotations + +import asyncio +import logging +from collections.abc import AsyncIterator +from dataclasses import dataclass +from typing import Any, Protocol + +from ..types import ( + EmbeddingRequest, + EmbeddingResponse, + LLMRequest, + LLMResponse, + LLMStreamEvent, +) + +logger = logging.getLogger("afk.middleware.timeout") + + +class LLMChatMiddleware(Protocol): + """Middleware protocol for non-streaming chat requests.""" + + async def __call__(self, call_next: Any, req: LLMRequest) -> LLMResponse: ... + + +class LLMEmbedMiddleware(Protocol): + """Middleware protocol for embedding requests.""" + + async def __call__(self, call_next: Any, req: EmbeddingRequest) -> EmbeddingResponse: ... + + +class LLMStreamMiddleware(Protocol): + """Middleware protocol for streaming chat requests.""" + + def __call__(self, call_next: Any, req: LLMRequest) -> AsyncIterator[LLMStreamEvent]: ... + + +@dataclass +class TimeoutConfig: + """Configuration for timeout middleware.""" + + default_timeout_s: float = 30.0 + chat_timeout_s: float | None = None + embed_timeout_s: float | None = None + stream_timeout_s: float | None = None + + +class TimeoutMiddleware(LLMChatMiddleware): + """ + Middleware that applies timeouts to LLM chat requests. + + Usage: + from afk.llms.middleware.timeout import TimeoutMiddleware, TimeoutConfig + + config = TimeoutConfig(default_timeout_s=30.0) + middleware = TimeoutMiddleware(config) + + stack = MiddlewareStack(chat=[middleware]) + """ + + def __init__(self, config: TimeoutConfig | None = None) -> None: + self._config = config or TimeoutConfig() + + async def __call__( + self, + call_next: Any, + req: LLMRequest, + ) -> LLMResponse: + """Apply timeout to chat request.""" + timeout_s = self._config.chat_timeout_s or self._config.default_timeout_s + policy = req.timeout_policy if req.timeout_policy else None + if policy and policy.request_timeout_s: + timeout_s = policy.request_timeout_s + + try: + return await asyncio.wait_for( + call_next(req), + timeout=timeout_s, + ) + except TimeoutError: + logger.warning( + "Chat request timed out after %.2fs (model=%s)", + timeout_s, + req.model, + ) + raise + + +class EmbedTimeoutMiddleware(LLMEmbedMiddleware): + """Middleware that applies timeouts to embedding requests.""" + + def __init__(self, config: TimeoutConfig | None = None) -> None: + self._config = config or TimeoutConfig() + + async def __call__( + self, + call_next: Any, + req: EmbeddingRequest, + ) -> EmbeddingResponse: + """Apply timeout to embedding request.""" + timeout_s = self._config.embed_timeout_s or self._config.default_timeout_s + + try: + return await asyncio.wait_for( + call_next(req), + timeout=timeout_s, + ) + except TimeoutError: + logger.warning( + "Embedding request timed out after %.2fs", + timeout_s, + ) + raise + + +class StreamTimeoutMiddleware(LLMStreamMiddleware): + """ + Middleware that applies timeouts to streaming LLM requests. + + Note: Streaming timeouts apply to each chunk, not the entire stream. + """ + + def __init__(self, config: TimeoutConfig | None = None) -> None: + self._config = config or TimeoutConfig() + + def __call__( + self, + call_next: Any, + req: LLMRequest, + ) -> AsyncIterator[LLMStreamEvent]: + """Apply timeout to streaming request.""" + timeout_s = self._config.stream_timeout_s or self._config.default_timeout_s + + async def timeout_wrapper() -> AsyncIterator[LLMStreamEvent]: + stream = call_next(req) + try: + while True: + try: + chunk = await asyncio.wait_for( + stream.__anext__(), + timeout=timeout_s, + ) + except StopAsyncIteration: + break + yield chunk + except TimeoutError: + logger.warning( + "Stream chunk timed out after %.2fs (model=%s)", + timeout_s, + req.model, + ) + raise + + return timeout_wrapper() + + +def create_timeout_middleware( + config: TimeoutConfig | None = None, +) -> list[LLMChatMiddleware]: + """Create a list containing the timeout middleware for chat.""" + return [TimeoutMiddleware(config)] + + +def create_embed_timeout_middleware( + config: TimeoutConfig | None = None, +) -> list[LLMEmbedMiddleware]: + """Create a list containing the timeout middleware for embeddings.""" + return [EmbedTimeoutMiddleware(config)] + + +def create_stream_timeout_middleware( + config: TimeoutConfig | None = None, +) -> list[LLMStreamMiddleware]: + """Create a list containing the timeout middleware for streaming.""" + return [StreamTimeoutMiddleware(config)] diff --git a/src/afk/llms/runtime/client.py b/src/afk/llms/runtime/client.py index a6b4843..d7cbf11 100644 --- a/src/afk/llms/runtime/client.py +++ b/src/afk/llms/runtime/client.py @@ -142,11 +142,36 @@ def _providers_for_request(self, req: LLMRequest) -> list[str]: default_provider=self._default_provider, ) - def _cache_key(self, provider: str, req: LLMRequest) -> str: + def _cache_key( + self, + provider: str, + req: LLMRequest, + *, + response_model=None, + cache_policy: CachePolicy | None = None, + ) -> str: """Build deterministic cache key for request payload + provider id.""" + response_model_name = None + if response_model is not None: + response_model_name = ( + f"{getattr(response_model, '__module__', '')}." + f"{getattr(response_model, '__qualname__', repr(response_model))}" + ) payload = { "provider": provider, "model": req.model, + "request_scope": { + "session_token": req.session_token, + "checkpoint_token": req.checkpoint_token, + "cache_namespace": ( + cache_policy.namespace + if cache_policy is not None + else None + ) + or req.metadata.get("cache_namespace") + or req.metadata.get("tenant_id") + or req.metadata.get("user_id"), + }, "messages": [ {"role": m.role, "name": m.name, "content": m.content} for m in req.messages @@ -155,9 +180,13 @@ def _cache_key(self, provider: str, req: LLMRequest) -> str: "tool_choice": req.tool_choice, "temperature": req.temperature, "top_p": req.top_p, + "stop": req.stop, "max_tokens": req.max_tokens, "thinking": req.thinking, "thinking_effort": req.thinking_effort, + "max_thinking_tokens": req.max_thinking_tokens, + "response_model": response_model_name, + "extra": req.extra, } normalized = json.dumps(payload, ensure_ascii=True, sort_keys=True, default=str) return hashlib.sha256(normalized.encode("utf-8")).hexdigest() @@ -219,7 +248,12 @@ async def chat( raise LLMError("No providers available for request") cache_policy = req.cache_policy or self._cache_policy - primary_cache_key = self._cache_key(providers[0], req) + primary_cache_key = self._cache_key( + providers[0], + req, + response_model=response_model, + cache_policy=cache_policy, + ) if cache_policy.enabled: cached = await self._cache.get(primary_cache_key) if cached is not None: @@ -271,7 +305,12 @@ async def _call_secondary() -> LLMResponse: raise if cache_policy.enabled: - cache_key = self._cache_key(result_provider, req) + cache_key = self._cache_key( + result_provider, + req, + response_model=response_model, + cache_policy=cache_policy, + ) await self._cache.set(cache_key, result, ttl_s=cache_policy.ttl_s) return result diff --git a/src/afk/llms/runtime/contracts.py b/src/afk/llms/runtime/contracts.py index 15142a2..76029dc 100644 --- a/src/afk/llms/runtime/contracts.py +++ b/src/afk/llms/runtime/contracts.py @@ -60,6 +60,7 @@ class CachePolicy: enabled: bool = False ttl_s: float = 30.0 + namespace: str | None = None @dataclass(frozen=True, slots=True) diff --git a/src/afk/mcp/__init__.py b/src/afk/mcp/__init__.py index 7b8dc8d..ef1fbbc 100644 --- a/src/afk/mcp/__init__.py +++ b/src/afk/mcp/__init__.py @@ -21,8 +21,11 @@ def greet(name: str) -> str: registry.register(greet) - server = MCPServer(registry) - server.run() # starts on http://0.0.0.0:8000 + server = MCPServer( + registry, + config=MCPServerConfig(host="127.0.0.1"), + ) # Secure-by-default local bind + server.run() # starts on http://127.0.0.1:8000 """ from .server import MCPServer, MCPServerConfig, create_mcp_server diff --git a/src/afk/mcp/server/protocol.py b/src/afk/mcp/server/protocol.py index e40b354..deb813f 100644 --- a/src/afk/mcp/server/protocol.py +++ b/src/afk/mcp/server/protocol.py @@ -25,6 +25,7 @@ METHOD_NOT_FOUND = -32601 INVALID_PARAMS = -32602 INTERNAL_ERROR = -32603 +UNAUTHORIZED = -32001 def jsonrpc_response(id: Any, result: Any) -> dict[str, Any]: @@ -60,13 +61,26 @@ def __init__( server_name: str, server_version: str, instructions: str | None = None, + require_tools_auth: bool = True, + insecure_development: bool = False, + auth_token: str | None = None, + token_header: str = "Authorization", ) -> None: self._registry = registry self._server_name = server_name self._server_version = server_version self._instructions = instructions + self._require_tools_auth = bool(require_tools_auth) + self._insecure_development = bool(insecure_development) + self._auth_token = auth_token + self._token_header = token_header - async def handle_message(self, message: dict[str, Any]) -> dict[str, Any] | None: + async def handle_message( + self, + message: dict[str, Any], + *, + headers: dict[str, Any] | None = None, + ) -> dict[str, Any] | None: """Route one JSON-RPC 2.0 message to the appropriate MCP method.""" if not isinstance(message, dict): return jsonrpc_error(None, INVALID_REQUEST, "Invalid Request") @@ -94,6 +108,7 @@ async def handle_message(self, message: dict[str, Any]) -> dict[str, Any] | None elif method == "tools/list": result = self.handle_tools_list(params) elif method == "tools/call": + self._require_tools_auth_if_configured(method=method, headers=headers) result = await self.handle_tools_call(params) elif method == "ping" or method == "notifications/initialized": result = {} @@ -107,10 +122,44 @@ async def handle_message(self, message: dict[str, Any]) -> dict[str, Any] | None if is_notification: return None return jsonrpc_response(msg_id, result) + except PermissionError as exc: + return jsonrpc_error(msg_id, UNAUTHORIZED, str(exc)) except Exception as exc: logger.exception("Error handling MCP method %s", method) return jsonrpc_error(msg_id, INTERNAL_ERROR, str(exc)) + def _require_tools_auth_if_configured( + self, + *, + method: str, + headers: dict[str, Any] | None, + ) -> None: + if self._insecure_development: + return + if not self._require_tools_auth: + return + if method != "tools/call": + return + + if self._auth_token is None: + raise PermissionError("MCP auth token is not configured") + + supplied = None + if headers is not None: + for key, value in headers.items(): + if key.lower() == self._token_header.lower(): + supplied = str(value) + break + if supplied is None: + raise PermissionError("Missing MCP auth token") + + expected = f"Bearer {self._auth_token}".strip() + if supplied.strip() == self._auth_token: + return + if supplied.strip() == expected: + return + raise PermissionError("Invalid MCP auth token") + def handle_initialize(self, params: dict[str, Any]) -> dict[str, Any]: """Handle ``initialize`` and return server capabilities.""" _ = params diff --git a/src/afk/mcp/server/runtime.py b/src/afk/mcp/server/runtime.py index f6915a0..9961a82 100644 --- a/src/afk/mcp/server/runtime.py +++ b/src/afk/mcp/server/runtime.py @@ -65,16 +65,34 @@ class MCPServerConfig: name: str = "afk-mcp-server" version: str = "1.0.0" - host: str = "0.0.0.0" + host: str = "127.0.0.1" port: int = 8000 instructions: str | None = None cors_origins: list[str] = field(default_factory=lambda: ["*"]) + allow_credentials: bool = False mcp_path: str = "/mcp" sse_path: str = "/mcp/sse" health_path: str = "/health" enable_sse: bool = True enable_health: bool = True allow_batch_requests: bool = True + require_tools_auth: bool = True + insecure_development: bool = False + mcp_tools_auth_token: str | None = None + token_header: str = "Authorization" + + def __post_init__(self) -> None: + if self.insecure_development: + return + if self.require_tools_auth and self.mcp_tools_auth_token is None: + raise ValueError( + "MCP server requires an auth token via mcp_tools_auth_token unless" + " insecure_development=True" + ) + if self.allow_credentials and "*" in self.cors_origins: + raise ValueError( + "CORS wildcard origin '*' cannot be used with credentials enabled" + ) # --------------------------------------------------------------------------- @@ -170,6 +188,10 @@ def _create_protocol_handler(self) -> MCPProtocolHandler: server_name=self._config.name, server_version=self._config.version, instructions=self._config.instructions, + require_tools_auth=self._config.require_tools_auth, + insecure_development=self._config.insecure_development, + auth_token=self._config.mcp_tools_auth_token, + token_header=self._config.token_header, ) def _create_router(self): @@ -215,12 +237,18 @@ async def mcp_endpoint(request: FastAPIRequest): ) responses = [] for item in body: - resp = await self._protocol_handler.handle_message(item) + resp = await self._protocol_handler.handle_message( + item, + headers=dict(request.headers), + ) if resp is not None: responses.append(resp) return JSONResponse(responses, status_code=200) - result = await self._protocol_handler.handle_message(body) + result = await self._protocol_handler.handle_message( + body, + headers=dict(request.headers), + ) if result is None: return FastAPIResponse(status_code=204) return JSONResponse(result, status_code=200) @@ -283,7 +311,7 @@ def _create_app(self): app.add_middleware( CORSMiddleware, allow_origins=self._config.cors_origins, - allow_credentials=True, + allow_credentials=self._config.allow_credentials, allow_methods=["*"], allow_headers=["*"], ) diff --git a/src/afk/mcp/store/transport.py b/src/afk/mcp/store/transport.py index 7b6632e..1fba26b 100644 --- a/src/afk/mcp/store/transport.py +++ b/src/afk/mcp/store/transport.py @@ -17,6 +17,16 @@ from typing import Any from afk.mcp.store.types import MCPRemoteCallError, MCPRemoteProtocolError, MCPServerRef +from afk.mcp.store.utils import _validate_remote_url + + +def _validate_server_for_call(server: MCPServerRef) -> None: + _validate_remote_url( + server.url, + allow_private_networks=server.allow_private_networks, + require_https=server.require_https, + allowed_hostnames=server.allowed_hostnames, + ) class MCPJsonRpcClient: @@ -37,6 +47,7 @@ async def call( "params": params, } payload = json.dumps(request_body).encode("utf-8") + _validate_server_for_call(server) post_fn = post or self.http_post response_bytes = await asyncio.to_thread(post_fn, server, payload) try: diff --git a/src/afk/mcp/store/types.py b/src/afk/mcp/store/types.py index e666c16..45f1908 100644 --- a/src/afk/mcp/store/types.py +++ b/src/afk/mcp/store/types.py @@ -32,6 +32,9 @@ class MCPServerRef: timeout_s: float = 20.0 prefix_tools: bool = True tool_name_prefix: str | None = None + require_https: bool = True + allow_private_networks: bool = False + allowed_hostnames: tuple[str, ...] = () @dataclass(frozen=True, slots=True) diff --git a/src/afk/mcp/store/utils.py b/src/afk/mcp/store/utils.py index 8b281c4..e84d0da 100644 --- a/src/afk/mcp/store/utils.py +++ b/src/afk/mcp/store/utils.py @@ -8,7 +8,9 @@ from __future__ import annotations +import ipaddress import re +import socket import urllib.parse from typing import Any @@ -26,15 +28,104 @@ def _sanitize_name(value: str) -> str: return out or "mcp" -def _validate_http_url(url: str) -> str: - parsed = urllib.parse.urlparse(url) - if parsed.scheme not in {"http", "https"}: +def _looks_private_or_local_host(host: str) -> bool: + normalized = host.lower().strip() + if normalized in {"localhost", "127.0.0.1", "::1", "[::1]"}: + return True + try: + ip = ipaddress.ip_address(normalized) + except ValueError: + return False + return _is_restricted_ip(ip) + + +def _is_restricted_ip(ip: ipaddress._BaseAddress) -> bool: + return bool( + ip.is_loopback + or ip.is_private + or ip.is_link_local + or ip.is_multicast + or ip.is_unspecified + or ip.is_reserved + ) + + +def _validate_resolved_ips(*, host: str, allow_private_networks: bool) -> None: + try: + addrs = socket.getaddrinfo(host, None) + except OSError as exc: + raise MCPServerResolutionError(f"Unable to resolve MCP host '{host}': {exc}") from exc + + ips = {sockaddr[0] for *_rest, sockaddr in addrs if isinstance(sockaddr, tuple)} + if not ips: + raise MCPServerResolutionError(f"No resolvable addresses for MCP host '{host}'") + + if allow_private_networks: + return + + for candidate in ips: + try: + ip = ipaddress.ip_address(candidate) + except ValueError: + continue + if _is_restricted_ip(ip): + raise MCPServerResolutionError( + f"MCP host '{host}' resolves to restricted address '{candidate}'" + ) + + +def _validate_remote_url( + url: str, + *, + parsed: urllib.parse.SplitResult | None = None, + allow_private_networks: bool = False, + require_https: bool = True, + allowed_hostnames: tuple[str, ...] = (), +) -> str: + parsed_url = parsed or urllib.parse.urlparse(url) + + if parsed_url.scheme not in {"http", "https"}: raise MCPServerResolutionError("MCP server URL scheme must be http or https") - if not parsed.netloc: + if not parsed_url.netloc: raise MCPServerResolutionError("MCP server URL must include network location") + + if require_https and parsed_url.scheme != "https" and not _looks_private_or_local_host( + parsed_url.hostname or "" + ): + raise MCPServerResolutionError( + "MCP server URL must use HTTPS for remote references" + ) + + if parsed_url.hostname is None: + raise MCPServerResolutionError("MCP server URL must include a valid hostname") + + hostname = parsed_url.hostname.lower() + allowed = tuple(name.lower() for name in allowed_hostnames if isinstance(name, str)) + if allowed and hostname not in allowed: + raise MCPServerResolutionError( + f"MCP server hostname '{hostname}' is not in allowed_hostnames" + ) + + if _looks_private_or_local_host(hostname) and not allow_private_networks: + raise MCPServerResolutionError( + "MCP server URL points to a restricted private/loopback host" + ) + + _validate_resolved_ips(host=hostname, allow_private_networks=allow_private_networks) return url +def _validate_http_url(url: str) -> str: + parsed = urllib.parse.urlparse(url) + return _validate_remote_url( + url, + parsed=parsed, + allow_private_networks=True, + require_https=False, + allowed_hostnames=(), + ) + + def _qualified_tool_name(server: MCPServerRef, tool_name: str) -> str: if not server.prefix_tools: return tool_name @@ -64,6 +155,12 @@ def _extract_mcp_text(content: Any) -> str | None: def resolve_server_ref(ref: str | dict[str, Any] | MCPServerRef) -> MCPServerRef: """Resolve a server reference from string/dict/dataclass form.""" if isinstance(ref, MCPServerRef): + _validate_remote_url( + ref.url, + allow_private_networks=ref.allow_private_networks, + require_https=ref.require_https, + allowed_hostnames=ref.allowed_hostnames, + ) return ref if isinstance(ref, str): @@ -116,6 +213,13 @@ def resolve_server_ref(ref: str | dict[str, Any] | MCPServerRef) -> MCPServerRef "MCP server 'tool_name_prefix' must be a string when set" ) + require_https = bool(ref.get("require_https", True)) + allow_private = bool(ref.get("allow_private_networks", False)) + allowed_hostnames = tuple( + str(v).strip() for v in ref.get("allowed_hostnames", ()) or () + if str(v).strip() + ) + return MCPServerRef( name=name, url=normalized_url, @@ -125,12 +229,11 @@ def resolve_server_ref(ref: str | dict[str, Any] | MCPServerRef) -> MCPServerRef tool_name_prefix=tool_name_prefix.strip() if isinstance(tool_name_prefix, str) and tool_name_prefix.strip() else None, + require_https=require_https, + allow_private_networks=allow_private, + allowed_hostnames=allowed_hostnames, ) - raise MCPServerResolutionError( - f"Unsupported MCP server reference type: {type(ref).__name__}" - ) - def normalize_remote_tools( server: MCPServerRef, @@ -138,7 +241,7 @@ def normalize_remote_tools( ) -> list[MCPRemoteTool]: """Validate and normalize a remote ``tools/list`` payload.""" if not isinstance(tools, list): - raise MCPRemoteProtocolError( + raise MCPServerResolutionError( f"Invalid tools/list response from '{server.name}': missing tools list" ) diff --git a/src/afk/memory/__init__.py b/src/afk/memory/__init__.py index dd1452d..5f7e0dd 100644 --- a/src/afk/memory/__init__.py +++ b/src/afk/memory/__init__.py @@ -14,6 +14,12 @@ InMemoryMemoryStore, SQLiteMemoryStore, ) +from .compaction import ( + BackgroundCompactor, + CompactionConfig, + CompactionStats, + MemoryCompactor, +) from .factory import create_memory_store_from_env from .lifecycle import ( MemoryCompactionResult, @@ -27,12 +33,6 @@ from .types import JsonObject, JsonValue, LongTermMemory, MemoryEvent from .utils import new_id, now_ms from .vector import cosine_similarity -from .compaction import ( - BackgroundCompactor, - CompactionConfig, - CompactionStats, - MemoryCompactor, -) RedisMemoryStore = None # type: ignore[assignment] PostgresMemoryStore = None # type: ignore[assignment] diff --git a/src/afk/memory/compaction.py b/src/afk/memory/compaction.py index baaa9eb..7045c3a 100644 --- a/src/afk/memory/compaction.py +++ b/src/afk/memory/compaction.py @@ -10,8 +10,8 @@ import asyncio import time +from collections.abc import Awaitable, Callable from dataclasses import dataclass, field -from typing import Any, Awaitable, Callable from ..agents.types.memory import ( ConsolidationResult, @@ -21,7 +21,6 @@ should_compact_event, ) from ..memory.types import MemoryEvent -from .types import JsonValue @dataclass @@ -260,7 +259,6 @@ async def compact( # Separate into compacted and retained to_compact = [e for e in scored if e.should_compact] - to_retain = [e for e in scored if not e.should_compact] events_compacted = len(to_compact) @@ -356,7 +354,7 @@ async def start( async def stop(self) -> None: """Stop background compaction.""" - self.stop() + super().stop() if self._task: self._task.cancel() try: diff --git a/src/afk/memory/factory.py b/src/afk/memory/factory.py index 59615cf..e35934c 100644 --- a/src/afk/memory/factory.py +++ b/src/afk/memory/factory.py @@ -25,7 +25,7 @@ def _env_bool(name: str, default: bool = False) -> bool: def create_memory_store_from_env() -> MemoryStore: """Create a memory store based on `AFK_MEMORY_BACKEND` and related environment settings.""" - backend = os.getenv("AFK_MEMORY_BACKEND", "sqlite").strip().lower() + backend = os.getenv("AFK_MEMORY_BACKEND", "memory").strip().lower() if backend in ("mem", "memory", "inmemory", "in_memory"): return InMemoryMemoryStore() diff --git a/src/afk/messaging/__init__.py b/src/afk/messaging/__init__.py index 5f11863..ef64f68 100644 --- a/src/afk/messaging/__init__.py +++ b/src/afk/messaging/__init__.py @@ -15,6 +15,7 @@ A2AAuthorizationDecision, A2AAuthorizationError, A2AAuthProvider, + A2ADeliveryStore, A2APrincipal, A2AServiceHost, A2AServiceHostError, @@ -57,6 +58,7 @@ "AllowAllA2AAuthProvider", "APIKeyA2AAuthProvider", "JWTA2AAuthProvider", + "A2ADeliveryStore", "InMemoryA2ADeliveryStore", "RedisA2ADeliveryStore", ] diff --git a/src/afk/queues/__init__.py b/src/afk/queues/__init__.py index 4ee7f10..6eaf6e4 100644 --- a/src/afk/queues/__init__.py +++ b/src/afk/queues/__init__.py @@ -68,6 +68,7 @@ "WorkerPresenceCapable", "StartupRecoveryCapable", "InMemoryTaskQueue", + "RedisTaskQueue", "EXECUTION_CONTRACT_KEY", "RUNNER_CHAT_CONTRACT", "JOB_DISPATCH_CONTRACT", diff --git a/src/afk/queues/dlq.py b/src/afk/queues/dlq.py deleted file mode 100644 index af15942..0000000 --- a/src/afk/queues/dlq.py +++ /dev/null @@ -1,344 +0,0 @@ -""" -MIT License -Copyright (c) 2026 arpan404 -See LICENSE file for full license text. - -Dead letter queue for failed tool calls and operations. -""" - -from __future__ import annotations - -import asyncio -import time -from dataclasses import dataclass, field -from typing import Any - -from ..llms.types import JSONValue - - -@dataclass(frozen=True, slots=True) -class DeadLetterEntry: - """ - A failed operation entry in the dead letter queue. - - Attributes: - id: Unique entry identifier. - operation_type: Type of operation (tool_call, llm_call, subagent_call, etc.). - operation_name: Name of the specific operation. - payload: Operation input payload. - error: Error message or reason for failure. - attempt_count: Number of attempts made. - last_attempt_ms: Timestamp of last attempt. - next_retry_ms: When to retry (0 if no more retries). - max_attempts: Maximum allowed attempts. - created_ms: When entry was created. - metadata: Additional operation metadata. - """ - - id: str - operation_type: str - operation_name: str - payload: dict[str, JSONValue] = field(default_factory=dict) - error: str = "" - attempt_count: int = 0 - last_attempt_ms: int = 0 - next_retry_ms: int = 0 - max_attempts: int = 3 - created_ms: int = field(default_factory=lambda: int(time.time() * 1000)) - metadata: dict[str, JSONValue] = field(default_factory=dict) - - def can_retry(self) -> bool: - """Check if entry can be retried.""" - return self.attempt_count < self.max_attempts - - def is_ready_for_retry(self) -> bool: - """Check if entry is ready for next retry attempt.""" - if not self.can_retry(): - return False - now = int(time.time() * 1000) - return now >= self.next_retry_ms - - -@dataclass -class DeadLetterQueue: - """ - Queue for storing failed operations that exceeded retry limits. - - Provides persistence and retry management for failed operations, - with configurable retry policies and TTL. - """ - - def __init__( - self, - max_attempts: int = 3, - base_retry_delay_ms: int = 1000, - max_retry_delay_ms: int = 60000, - entry_ttl_ms: int = 86400000, - ) -> None: - """ - Initialize dead letter queue. - - Args: - max_attempts: Maximum retry attempts before moving to DLQ. - base_retry_delay_ms: Base delay between retries (exponential backoff). - max_retry_delay_ms: Maximum retry delay cap. - entry_ttl_ms: Time-to-live for DLQ entries in milliseconds. - """ - self._entries: dict[str, DeadLetterEntry] = {} - self._locks: dict[str, asyncio.Lock] = {} - self._lock = asyncio.Lock() - self._max_attempts = max_attempts - self._base_retry_delay_ms = base_retry_delay_ms - self._max_retry_delay_ms = max_retry_delay_ms - self._entry_ttl_ms = entry_ttl_ms - - def _lock_for(self, entry_id: str) -> asyncio.Lock: - if entry_id not in self._locks: - self._locks[entry_id] = asyncio.Lock() - return self._locks[entry_id] - - def _compute_backoff(self, attempt: int) -> int: - """Compute exponential backoff delay.""" - delay = self._base_retry_delay_ms * (2 ** attempt) - return min(delay, self._max_retry_delay_ms) - - def _new_id(self, operation_type: str, operation_name: str) -> str: - """Generate unique entry ID.""" - ts = int(time.time() * 1000000) - return f"dlq-{operation_type}-{operation_name}-{ts}" - - async def add_entry( - self, - operation_type: str, - operation_name: str, - payload: dict[str, JSONValue] | None = None, - error: str = "", - metadata: dict[str, JSONValue] | None = None, - ) -> str: - """ - Add a failed operation to the dead letter queue. - - Args: - operation_type: Type of operation (tool_call, llm_call, etc.). - operation_name: Name of the specific operation. - payload: Operation input payload. - error: Error message. - metadata: Additional metadata. - - Returns: - The generated entry ID. - """ - entry_id = self._new_id(operation_type, operation_name) - now_ms = int(time.time() * 1000) - - entry = DeadLetterEntry( - id=entry_id, - operation_type=operation_type, - operation_name=operation_name, - payload=payload or {}, - error=error, - attempt_count=self._max_attempts, # Already exhausted - last_attempt_ms=now_ms, - next_retry_ms=0, # No more retries - max_attempts=self._max_attempts, - created_ms=now_ms, - metadata=metadata or {}, - ) - - async with self._lock: - self._entries[entry_id] = entry - - return entry_id - - async def record_failure( - self, - operation_type: str, - operation_name: str, - payload: dict[str, JSONValue] | None = None, - error: str = "", - attempt: int = 1, - metadata: dict[str, JSONValue] | None = None, - ) -> str: - """ - Record a failure and potentially add to DLQ if max attempts exceeded. - - Args: - operation_type: Type of operation. - operation_name: Name of the operation. - payload: Operation payload. - error: Error message. - attempt: Current attempt number. - metadata: Additional metadata. - - Returns: - Entry ID (new or existing). - """ - entry_id = self._new_id(operation_type, operation_name) - now_ms = int(time.time() * 1000) - - if attempt >= self._max_attempts: - # Max attempts exceeded, add to DLQ - return await self.add_entry( - operation_type=operation_type, - operation_name=operation_name, - payload=payload, - error=error, - metadata=metadata, - ) - - # Calculate next retry time - next_retry = now_ms + self._compute_backoff(attempt) - - entry = DeadLetterEntry( - id=entry_id, - operation_type=operation_type, - operation_name=operation_name, - payload=payload or {}, - error=error, - attempt_count=attempt, - last_attempt_ms=now_ms, - next_retry_ms=next_retry, - max_attempts=self._max_attempts, - created_ms=now_ms, - metadata=metadata or {}, - ) - - async with self._lock: - self._entries[entry_id] = entry - - return entry_id - - async def get_entry(self, entry_id: str) -> DeadLetterEntry | None: - """Get a specific DLQ entry by ID.""" - async with self._lock: - return self._entries.get(entry_id) - - async def get_ready_retry(self) -> list[DeadLetterEntry]: - """ - Get all entries that are ready for retry. - - Returns: - List of entries ready for retry. - """ - now_ms = int(time.time() * 1000) - async with self._lock: - return [ - entry - for entry in self._entries.values() - if entry.is_ready_for_retry() and (now_ms - entry.created_ms) < self._entry_ttl_ms - ] - - async def remove_entry(self, entry_id: str) -> bool: - """ - Remove an entry from the DLQ. - - Args: - entry_id: Entry ID to remove. - - Returns: - True if removed, False if not found. - """ - async with self._lock: - if entry_id in self._entries: - del self._entries[entry_id] - return True - return False - - async def list_entries( - self, - operation_type: str | None = None, - include_completed: bool = False, - ) -> list[DeadLetterEntry]: - """ - List DLQ entries with optional filtering. - - Args: - operation_type: Filter by operation type. - include_completed: Include entries past TTL. - - Returns: - List of matching entries. - """ - now_ms = int(time.time() * 1000) - async with self._lock: - entries = [] - for entry in self._entries.values(): - if operation_type and entry.operation_type != operation_type: - continue - if not include_completed: - age_ms = now_ms - entry.created_ms - if age_ms > self._entry_ttl_ms: - continue - entries.append(entry) - return entries - - async def retry_entry(self, entry_id: str) -> DeadLetterEntry | None: - """ - Mark an entry for retry by resetting its attempt count. - - Args: - entry_id: Entry ID to retry. - - Returns: - Updated entry or None if not found. - """ - async with self._lock: - entry = self._entries.get(entry_id) - if not entry: - return None - - # Reset for retry - new_attempt = max(0, entry.attempt_count - 1) - new_id = self._new_id(entry.operation_type, entry.operation_name) - - now_ms = int(time.time() * 1000) - updated = DeadLetterEntry( - id=new_id, - operation_type=entry.operation_type, - operation_name=entry.operation_name, - payload=entry.payload, - error=entry.error, - attempt_count=new_attempt, - last_attempt_ms=now_ms, - next_retry_ms=now_ms + self._compute_backoff(new_attempt), - max_attempts=entry.max_attempts, - created_ms=entry.created_ms, - metadata=entry.metadata, - ) - - # Remove old, add new - del self._entries[entry_id] - self._entries[new_id] = updated - return updated - - async def clear_expired(self) -> int: - """ - Remove all expired entries. - - Returns: - Number of entries removed. - """ - now_ms = int(time.time() * 1000) - removed = 0 - async with self._lock: - expired_ids = [ - entry_id - for entry_id, entry in self._entries.items() - if (now_ms - entry.created_ms) > self._entry_ttl_ms - ] - for entry_id in expired_ids: - del self._entries[entry_id] - removed += 1 - return removed - - -# Global DLQ instance -_global_dlq: DeadLetterQueue | None = None - - -def get_dead_letter_queue() -> DeadLetterQueue: - """Get the global dead letter queue instance.""" - global _global_dlq - if _global_dlq is None: - _global_dlq = DeadLetterQueue() - return _global_dlq \ No newline at end of file diff --git a/src/afk/queues/worker.py b/src/afk/queues/worker.py index e08cd2e..41e1f05 100644 --- a/src/afk/queues/worker.py +++ b/src/afk/queues/worker.py @@ -417,6 +417,11 @@ async def _execute_task(self, task_item: TaskItem) -> None: self._metrics.incr("queue_worker_failed_non_retryable_total") logger.error("Task %s failed (non-retryable): %s", task_item.id[:8], error) await self._handle_failure(task_item, error=error, retryable=False) + except asyncio.CancelledError: + error = "Task execution cancelled" + self._metrics.incr("queue_worker_failed_non_retryable_total") + logger.info("Task %s cancelled", task_item.id[:8]) + await self._handle_failure(task_item, error=error, retryable=False) except Exception as exc: # noqa: BLE001 error = str(exc) self._metrics.incr("queue_worker_failed_retryable_total") diff --git a/src/afk/quickstart.py b/src/afk/quickstart.py deleted file mode 100644 index 5e922be..0000000 --- a/src/afk/quickstart.py +++ /dev/null @@ -1,27 +0,0 @@ -""" -MIT License -Copyright (c) 2026 arpan404 -See LICENSE file for full license text. - -Quickstart re-exports for common AFK usage. - -Usage:: - - from afk.quickstart import Agent, tool, Runner, ToolResult, AgentResult -""" - -from __future__ import annotations - -from .agents import Agent -from .agents.types import AgentResult -from .core.runner import Runner -from .tools.core.base import ToolResult -from .tools.core.decorator import tool - -__all__ = [ - "Agent", - "AgentResult", - "Runner", - "ToolResult", - "tool", -] diff --git a/src/afk/testing/property.py b/src/afk/testing/property.py index 297a029..51fac45 100644 --- a/src/afk/testing/property.py +++ b/src/afk/testing/property.py @@ -8,12 +8,9 @@ from __future__ import annotations -import asyncio -import random from dataclasses import dataclass, field -from typing import Any, Callable, TypeVar +from typing import Any, TypeVar -from hypothesis import given, settings, example, Phase from hypothesis import strategies as st T = TypeVar("T") diff --git a/src/afk/tools/core/base.py b/src/afk/tools/core/base.py index 35e0c6f..04427e9 100644 --- a/src/afk/tools/core/base.py +++ b/src/afk/tools/core/base.py @@ -9,7 +9,6 @@ from __future__ import annotations import asyncio - import inspect from collections.abc import Awaitable, Callable from dataclasses import dataclass, field diff --git a/src/afk/tools/prebuilts/runtime.py b/src/afk/tools/prebuilts/runtime.py index 161ef13..f2a4373 100644 --- a/src/afk/tools/prebuilts/runtime.py +++ b/src/afk/tools/prebuilts/runtime.py @@ -93,10 +93,9 @@ async def read_file(args: _ReadFileArgs) -> dict[str, Any]: _ensure_inside(target, root) if not target.exists() or not target.is_file(): raise FileAccessError(f"File not found: {args.path}") - text = await asyncio.to_thread(target.read_text, encoding="utf-8") - truncated = len(text) > args.max_chars - if truncated: - text = text[: args.max_chars] + text, truncated = await asyncio.to_thread( + _read_text_limited, target, args.max_chars + ) return { "root": str(root), "path": str(target), @@ -117,3 +116,12 @@ def _ensure_inside(path: Path, root: Path) -> None: path.relative_to(root) except ValueError as e: raise FileAccessError(f"Path '{path}' escapes root '{root}'") from e + + +def _read_text_limited(path: Path, max_chars: int) -> tuple[str, bool]: + """Read at most max_chars plus one sentinel character from a UTF-8 file.""" + with path.open("r", encoding="utf-8") as handle: + text = handle.read(max_chars + 1) + if len(text) <= max_chars: + return text, False + return text[:max_chars], True diff --git a/src/afk/tools/prebuilts/skills.py b/src/afk/tools/prebuilts/skills.py index 5b0e928..341ec0e 100644 --- a/src/afk/tools/prebuilts/skills.py +++ b/src/afk/tools/prebuilts/skills.py @@ -103,10 +103,9 @@ async def read_skill_file(args: _ReadSkillFileArgs) -> dict[str, Any]: _ensure_inside(target, root) if not target.exists() or not target.is_file(): raise SkillAccessError(f"Skill file not found: {args.relative_path}") - text = await asyncio.to_thread(target.read_text, encoding="utf-8") - truncated = len(text) > args.max_chars - if truncated: - text = text[: args.max_chars] + text, truncated = await asyncio.to_thread( + _read_text_limited, target, args.max_chars + ) return { "skill_name": args.skill_name, "path": str(target), @@ -135,7 +134,7 @@ async def run_skill_command(args: _RunSkillCommandArgs) -> dict[str, Any]: "Command denied due to shell operator restrictions" ) - cwd = None + cwd_path: Path if args.cwd: maybe = Path(args.cwd).expanduser().resolve() # Command working directory must stay inside one of the resolved skill roots. @@ -143,7 +142,13 @@ async def run_skill_command(args: _RunSkillCommandArgs) -> dict[str, Any]: raise SkillAccessError( "Command cwd must be inside one of the skill roots" ) - cwd = str(maybe) + cwd_path = maybe + elif len(roots) == 1: + cwd_path = next(iter(roots.values())) + else: + raise SkillAccessError( + "Command cwd is required when multiple skill roots are available" + ) timeout_s = args.timeout_s or policy.command_timeout_s proc = await asyncio.create_subprocess_exec( @@ -151,7 +156,7 @@ async def run_skill_command(args: _RunSkillCommandArgs) -> dict[str, Any]: *args.args, stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE, - cwd=cwd, + cwd=str(cwd_path), ) try: stdout_b, stderr_b = await asyncio.wait_for(proc.communicate(), timeout_s) @@ -171,6 +176,7 @@ async def run_skill_command(args: _RunSkillCommandArgs) -> dict[str, Any]: return { "command": [command, *args.args], + "cwd": str(cwd_path), "exit_code": proc.returncode, "stdout": stdout, "stderr": stderr, @@ -207,3 +213,12 @@ def _is_inside(path: Path, root: Path) -> bool: return True except ValueError: return False + + +def _read_text_limited(path: Path, max_chars: int) -> tuple[str, bool]: + """Read at most max_chars plus one sentinel character from a UTF-8 file.""" + with path.open("r", encoding="utf-8") as handle: + text = handle.read(max_chars + 1) + if len(text) <= max_chars: + return text, False + return text[:max_chars], True diff --git a/src/afk/tools/security.py b/src/afk/tools/security.py index e1a9ae4..8de9983 100644 --- a/src/afk/tools/security.py +++ b/src/afk/tools/security.py @@ -21,7 +21,7 @@ class SandboxProfile: profile_id: str = "default" allow_network: bool = False - allow_command_execution: bool = True + allow_command_execution: bool = False allowed_command_prefixes: list[str] = field(default_factory=list) deny_shell_operators: bool = True allowed_paths: list[str] = field(default_factory=list) @@ -81,14 +81,6 @@ def validate_tool_args_against_sandbox( if not profile.allow_command_execution: return f"Command execution denied by sandbox profile '{profile.profile_id}'" - command = command_parts[0].strip() - if profile.allowed_command_prefixes: - if not _is_command_allowed(command, profile.allowed_command_prefixes): - return ( - f"Command '{command}' not allowlisted by sandbox profile " - f"'{profile.profile_id}'" - ) - if profile.deny_shell_operators: blocked = {"&&", "||", ";", "|", "`", "$(", ">", ">>", "<", "<<", "&"} joined = " ".join(command_parts) @@ -99,6 +91,18 @@ def validate_tool_args_against_sandbox( f"profile '{profile.profile_id}'" ) + command = command_parts[0].strip() + if not profile.allowed_command_prefixes: + return ( + f"Command '{command}' not allowlisted by sandbox profile " + f"'{profile.profile_id}'" + ) + if not _is_command_allowed(command, profile.allowed_command_prefixes): + return ( + f"Command '{command}' not allowlisted by sandbox profile " + f"'{profile.profile_id}'" + ) + denied_roots = [_resolve_root(path, cwd) for path in profile.denied_paths] allowed_roots = [_resolve_root(path, cwd) for path in profile.allowed_paths] @@ -288,7 +292,11 @@ def _is_command_allowed(command: str, allowlist: list[str]) -> bool: allowed = item.strip() if not allowed: continue - if command == allowed or command.startswith(allowed + "/"): + if ( + command == allowed + or command.startswith(allowed + "/") + or command.startswith(allowed + " ") + ): return True return False @@ -300,13 +308,36 @@ def _truncate_text(value: str, *, max_chars: int) -> str: def _truncate_json_like(value: Any, *, max_chars: int) -> Any: - if isinstance(value, str): - return _truncate_text(value, max_chars=max_chars) - if isinstance(value, list): - return [_truncate_json_like(item, max_chars=max_chars) for item in value] - if isinstance(value, dict): - return { - str(key): _truncate_json_like(item, max_chars=max_chars) - for key, item in value.items() - } - return value + remaining = max(max_chars, 0) + + def _truncate(value: Any) -> Any: + nonlocal remaining + if remaining <= 0: + return "[truncated]" + if isinstance(value, str): + if len(value) <= remaining: + remaining -= len(value) + return value + omitted = len(value) - remaining + prefix = value[:remaining] + remaining = 0 + return f"{prefix}... [truncated {omitted} chars]" + if isinstance(value, list): + items = [] + for item in value: + if remaining <= 0: + items.append("[truncated]") + break + items.append(_truncate(item)) + return items + if isinstance(value, dict): + items = {} + for key, item in value.items(): + if remaining <= 0: + items["..."] = "[truncated]" + break + items[str(key)] = _truncate(item) + return items + return value + + return _truncate(value) diff --git a/tests/agents/test_a2a_auth_and_host.py b/tests/agents/test_a2a_auth_and_host.py index 259a04d..e2f3b46 100644 --- a/tests/agents/test_a2a_auth_and_host.py +++ b/tests/agents/test_a2a_auth_and_host.py @@ -115,3 +115,80 @@ def test_service_host_endpoints_authorize_and_invoke(): assert authorized.status_code == 200 body = authorized.json() assert body["success"] is True + + +def test_service_host_production_hides_docs_and_sanitizes_errors(): + pytest.importorskip("fastapi") + pytest.importorskip("httpx") + + provider = APIKeyA2AAuthProvider( + key_to_subject={"prod-key": "svc-1"}, + key_to_roles={"prod-key": ("a2a:all",)}, + ) + host = A2AServiceHost( + protocol=InternalA2AProtocol(dispatch=_ok_dispatch), + auth_provider=provider, + production_mode=True, + ) + app = host.create_app() + + from fastapi.testclient import TestClient + + with TestClient(app) as client: + assert client.get("/docs").status_code == 404 + assert client.get("/openapi.json").status_code == 404 + + unauthorized = client.post("/a2a/invoke", json={}) + assert unauthorized.status_code == 401 + assert unauthorized.json()["detail"] == "Authentication failed" + + invalid = client.post( + "/a2a/invoke", + json={}, + headers={"x-api-key": "prod-key"}, + ) + assert invalid.status_code == 422 + assert invalid.json()["detail"] == "Invalid invoke payload" + + +def test_service_host_stream_endpoint_returns_ndjson(): + pytest.importorskip("fastapi") + pytest.importorskip("httpx") + + provider = APIKeyA2AAuthProvider( + key_to_subject={"prod-key": "svc-1"}, + key_to_roles={"prod-key": ("a2a:all",)}, + ) + host = A2AServiceHost( + protocol=InternalA2AProtocol(dispatch=_ok_dispatch), + auth_provider=provider, + production_mode=True, + ) + app = host.create_app() + + from fastapi.testclient import TestClient + + payload = { + "run_id": "r1", + "thread_id": "t1", + "conversation_id": "c1", + "correlation_id": "corr1", + "idempotency_key": "idem1", + "source_agent": "parent", + "target_agent": "child", + "payload": {"k": "v"}, + "metadata": {}, + "causation_id": "cause1", + "timeout_s": 1.0, + } + + with TestClient(app) as client: + response = client.post( + "/a2a/invoke/stream", + json=payload, + headers={"x-api-key": "prod-key"}, + ) + assert response.status_code == 200 + assert response.headers["content-type"].startswith("application/x-ndjson") + assert response.text.strip() + assert all(line.startswith("{") for line in response.text.splitlines()) diff --git a/tests/agents/test_a2a_delivery.py b/tests/agents/test_a2a_delivery.py index 24be9fe..3930bf0 100644 --- a/tests/agents/test_a2a_delivery.py +++ b/tests/agents/test_a2a_delivery.py @@ -14,7 +14,6 @@ AgentInvocationResponse, ) - # ── Helpers ────────────────────────────────────────────────────────────────── diff --git a/tests/agents/test_agent_lifecycle_runtime.py b/tests/agents/test_agent_lifecycle_runtime.py index da61c25..e6e17ed 100644 --- a/tests/agents/test_agent_lifecycle_runtime.py +++ b/tests/agents/test_agent_lifecycle_runtime.py @@ -10,6 +10,7 @@ import pytest +from afk.agents.errors import AgentCircuitOpenError, AgentExecutionError from afk.agents.lifecycle.runtime import ( CircuitBreaker, EffectJournal, @@ -25,7 +26,6 @@ ) from afk.agents.types.config import SkillRef from afk.agents.types.policy import FailSafeConfig -from afk.agents.errors import AgentCircuitOpenError, AgentExecutionError def run_async(coro): diff --git a/tests/agents/test_agent_runtime.py b/tests/agents/test_agent_runtime.py index 995f575..4196eb0 100644 --- a/tests/agents/test_agent_runtime.py +++ b/tests/agents/test_agent_runtime.py @@ -1325,6 +1325,7 @@ def test_runtime_sandbox_policy_blocks_shell_operator_commands(): sandbox = SandboxProfile( profile_id="cmd-safe", allow_command_execution=True, + allowed_command_prefixes=["ls"], deny_shell_operators=True, ) agent = Agent( @@ -1352,6 +1353,7 @@ def test_per_tool_sandbox_provider_overrides_default_profile(): default_profile = SandboxProfile( profile_id="default-allow", allow_command_execution=True, + allowed_command_prefixes=["ls && whoami"], deny_shell_operators=False, ) agent = Agent( diff --git a/tests/agents/test_agent_types.py b/tests/agents/test_agent_types.py index bc23481..84f5821 100644 --- a/tests/agents/test_agent_types.py +++ b/tests/agents/test_agent_types.py @@ -8,19 +8,6 @@ import pytest -from afk.agents.types.config import ( - RouterDecision, - RouterInput, - SkillRef, - SkillResolutionResult, - SkillToolPolicy, -) -from afk.agents.types.policy import ( - AgentRunEvent, - FailSafeConfig, - PolicyDecision, - PolicyEvent, -) from afk.agents.errors import ( AgentBudgetExceededError, AgentCancelledError, @@ -42,9 +29,21 @@ SubagentExecutionError, SubagentRoutingError, ) +from afk.agents.types.config import ( + RouterDecision, + RouterInput, + SkillRef, + SkillResolutionResult, + SkillToolPolicy, +) +from afk.agents.types.policy import ( + AgentRunEvent, + FailSafeConfig, + PolicyDecision, + PolicyEvent, +) from afk.debugger.types import DebuggerConfig - # --------------------------------------------------------------------------- # SkillRef # --------------------------------------------------------------------------- diff --git a/tests/agents/test_policy_audit.py b/tests/agents/test_policy_audit.py index 79beb76..31aeefe 100644 --- a/tests/agents/test_policy_audit.py +++ b/tests/agents/test_policy_audit.py @@ -7,18 +7,15 @@ """ import pytest + from afk.agents.policy.audit import ( - AuditAction, AuditConfig, - AuditLevel, AuditRecord, - AuditSink, ConsoleAuditSink, FileAuditSink, PolicyAuditLogger, create_policy_audit_logger, ) -from afk.agents.types.policy import PolicyDecision, PolicyEvent class TestAuditConfig: @@ -30,7 +27,7 @@ def test_defaults(self) -> None: assert config.enabled is True assert config.min_level == "info" - assert config.include_payloads is True + assert config.include_payloads is False assert config.max_payload_size == 10_000 assert config.retention_days == 90 assert config.sink == "console" @@ -142,6 +139,37 @@ async def test_redact(self) -> None: assert result["username"] == "admin" assert result["action"] == "test" + def test_redact_nested_payloads_and_sensitive_strings(self) -> None: + """Test redaction for nested data and sensitive-looking values.""" + sink = FileAuditSink(AuditConfig()) + + data = { + "outer": { + "api_key": "eyJhbGci.sensitive.token", + "nested": { + "password": "very_secret_password", + "safe": "still_visible", + "children": [ + { + "token": "sk_live_0123456789ABCDEF0123", + "role": "reader", + }, + "value", + ], + }, + "header": "Bearer sk_live_ABCDEF0123456789", + }, + } + + result = sink._redact(data) + + assert result["outer"]["api_key"] == "[REDACTED]" + assert result["outer"]["nested"]["password"] == "[REDACTED]" + assert result["outer"]["nested"]["safe"] == "still_visible" + assert result["outer"]["nested"]["children"][0]["token"] == "[REDACTED]" + assert result["outer"]["nested"]["children"][1] == "value" + assert result["outer"]["header"] == "[REDACTED]" + class TestPolicyAuditLogger: """Tests for PolicyAuditLogger.""" diff --git a/tests/agents/test_prompt_loader.py b/tests/agents/test_prompt_loader.py index 0c274f0..8e67aff 100644 --- a/tests/agents/test_prompt_loader.py +++ b/tests/agents/test_prompt_loader.py @@ -141,8 +141,8 @@ def test_prompt_path_escape_raises_access_error(tmp_path: Path): run_async(agent.resolve_instructions({})) -def test_absolute_path_bypasses_security_check(tmp_path: Path): - """Absolute paths should bypass security check but still require file to exist.""" +def test_absolute_path_inside_prompts_root_loads(tmp_path: Path): + """Absolute paths are allowed only when they stay inside prompts_dir.""" from afk.agents.prompts.store import resolve_prompts_dir root = tmp_path / "prompts" @@ -154,12 +154,11 @@ def test_absolute_path_bypasses_security_check(tmp_path: Path): abs_path = valid_file.resolve() # Get prompt root for context - prompt_root = resolve_prompts_dir(prompts_dir=None, cwd=tmp_path) + prompt_root = resolve_prompts_dir(prompts_dir=root, cwd=tmp_path) # Import directly - this is a module-level function from afk.agents.prompts.store import resolve_prompt_file_path - # Should work - absolute path bypasses security check resolved = resolve_prompt_file_path( prompt_root=prompt_root, instruction_file=abs_path, @@ -167,12 +166,12 @@ def test_absolute_path_bypasses_security_check(tmp_path: Path): ) assert resolved == abs_path - # Non-existent absolute path should fail with PromptResolutionError (not access error) - non_existent = Path("/nonexistent/file.txt") - with pytest.raises(PromptResolutionError): + outside = tmp_path / "outside.md" + outside.write_text("outside", encoding="utf-8") + with pytest.raises(PromptAccessError): resolve_prompt_file_path( prompt_root=prompt_root, - instruction_file=non_existent, + instruction_file=outside.resolve(), agent_name="test", ) diff --git a/tests/agents/test_replay_api.py b/tests/agents/test_replay_api.py index 055894e..0179ab8 100644 --- a/tests/agents/test_replay_api.py +++ b/tests/agents/test_replay_api.py @@ -7,16 +7,14 @@ """ import pytest + from afk.agents.lifecycle import ( - CheckpointInfo, - CheckpointReplayHandle, - CheckpointReplayManager, + InteractiveReplayHandler, ReplayAPI, ReplayDecisionPoint, ReplaySession, ReplayTimeline, ReplayTimelineEvent, - InteractiveReplayHandler, create_replay_api, ) diff --git a/tests/agents/test_workflow.py b/tests/agents/test_workflow.py index 5a2bc62..6e81073 100644 --- a/tests/agents/test_workflow.py +++ b/tests/agents/test_workflow.py @@ -6,23 +6,20 @@ Tests for workflow state machine. """ -import asyncio import pytest + +from afk.agents.workflow.executor import ( + WorkflowExecutionContext, + WorkflowExecutor, + create_workflow_executor, +) from afk.agents.workflow.state_machine import ( WorkflowBuilder, WorkflowEdge, - WorkflowEvent, WorkflowNode, WorkflowSpec, WorkflowState, - WorkflowTransition, -) -from afk.agents.workflow.executor import ( - WorkflowExecutionContext, - WorkflowExecutionResult, - WorkflowExecutor, - create_workflow_executor, ) diff --git a/tests/core/test_streaming.py b/tests/core/test_streaming.py index b4e387d..f0491f2 100644 --- a/tests/core/test_streaming.py +++ b/tests/core/test_streaming.py @@ -9,12 +9,13 @@ import pytest +from afk.agents.types import AgentResult from afk.core.streaming import ( AgentStreamEvent, AgentStreamHandle, + status_update, step_completed, step_started, - status_update, stream_completed, stream_error, text_delta, @@ -23,8 +24,6 @@ tool_started, ) from afk.core.telemetry import TelemetryEvent, TelemetrySpan -from afk.agents.types import AgentResult - # ====================================================================== # AgentStreamEvent dataclass diff --git a/tests/evals/test_evals_comprehensive.py b/tests/evals/test_evals_comprehensive.py index 5d8053b..c55c6a4 100644 --- a/tests/evals/test_evals_comprehensive.py +++ b/tests/evals/test_evals_comprehensive.py @@ -9,13 +9,7 @@ import pytest -from afk.evals.models import ( - EvalAssertionResult, - EvalCase, - EvalCaseResult, - EvalSuiteConfig, - EvalSuiteResult, -) +from afk.agents.types.policy import AgentRunEvent from afk.evals.budgets import EvalBudget, evaluate_budget from afk.evals.datasets import ( _as_json_obj, @@ -24,9 +18,14 @@ load_eval_cases_json, ) from afk.evals.golden import compare_event_types, write_golden_trace +from afk.evals.models import ( + EvalAssertionResult, + EvalCase, + EvalCaseResult, + EvalSuiteConfig, + EvalSuiteResult, +) from afk.observability.models import RunMetrics -from afk.agents.types.policy import AgentRunEvent - # ====================================================================== # Fake agent for EvalCase construction (avoids real Agent dependencies) diff --git a/tests/llms/test_llm_builder_structured.py b/tests/llms/test_llm_builder_structured.py index b75e7da..717c03d 100644 --- a/tests/llms/test_llm_builder_structured.py +++ b/tests/llms/test_llm_builder_structured.py @@ -36,7 +36,6 @@ parse_and_validate_json, ) - # ============================== Helpers ============================== # All AFK_LLM_* env vars that LLMSettings.from_env() reads. diff --git a/tests/llms/test_llm_cache.py b/tests/llms/test_llm_cache.py index 4681e09..eeb4095 100644 --- a/tests/llms/test_llm_cache.py +++ b/tests/llms/test_llm_cache.py @@ -7,16 +7,17 @@ import pytest +from afk.llms.cache import registry from afk.llms.cache.base import CacheEntry from afk.llms.cache.inmemory import InMemoryLLMCache -from afk.llms.cache import registry +from afk.llms.cache.redis import RedisLLMCache from afk.llms.cache.registry import ( LLMCacheError, create_llm_cache, list_llm_cache_backends, register_llm_cache_backend, ) -from afk.llms.types import LLMResponse +from afk.llms.types import LLMResponse, ToolCall, Usage def run_async(coro): @@ -113,6 +114,100 @@ async def _run(): assert result is not None assert result.text == "new" + def test_set_strips_request_scoped_provider_metadata(self): + cache = InMemoryLLMCache() + resp = LLMResponse( + text="cached", + request_id="req_1", + provider_request_id="provider_req_1", + session_token="session_1", + checkpoint_token="checkpoint_1", + raw={"provider": "payload"}, + ) + + async def _run(): + await cache.set("k1", resp, ttl_s=60) + return await cache.get("k1") + + result = run_async(_run()) + assert result is not None + assert result.text == "cached" + assert result.request_id is None + assert result.provider_request_id is None + assert result.session_token is None + assert result.checkpoint_token is None + assert result.raw == {} + + +class _FakeRedisCacheClient: + def __init__(self) -> None: + self.rows: dict[str, str] = {} + self.deleted: list[str] = [] + + async def get(self, key: str): + return self.rows.get(key) + + async def setex(self, key: str, ttl_s: int, payload: str) -> None: + self.rows[key] = payload + self.rows[f"{key}:ttl"] = str(ttl_s) + + async def delete(self, key: str) -> None: + self.deleted.append(key) + self.rows.pop(key, None) + + +class TestRedisLLMCache: + def test_get_returns_none_for_missing_or_invalid_payload(self): + client = _FakeRedisCacheClient() + cache = RedisLLMCache(client) + + assert run_async(cache.get("missing")) is None + + client.rows["bad"] = "not-json" + assert run_async(cache.get("bad")) is None + + def test_set_get_and_delete_round_trip_sanitized_payload(self): + client = _FakeRedisCacheClient() + cache = RedisLLMCache(client) + response = LLMResponse( + text="hello", + request_id="req_1", + provider_request_id="provider_req_1", + session_token="session_1", + checkpoint_token="checkpoint_1", + structured_response={"answer": "hello"}, + tool_calls=[ToolCall(id="tc_1", tool_name="lookup", arguments={"q": "x"})], + finish_reason="stop", + usage=Usage(input_tokens=1, output_tokens=2, total_tokens=3), + raw={"provider": "payload"}, + model="demo", + ) + + async def scenario(): + await cache.set("k1", response, ttl_s=0) + loaded = await cache.get("k1") + await cache.delete("k1") + deleted = await cache.get("k1") + return loaded, deleted + + loaded, deleted = run_async(scenario()) + + assert client.rows["k1:ttl"] == "1" + assert loaded is not None + assert loaded.text == "hello" + assert loaded.request_id is None + assert loaded.provider_request_id is None + assert loaded.session_token is None + assert loaded.checkpoint_token is None + assert loaded.raw == {} + assert loaded.structured_response == {"answer": "hello"} + assert loaded.tool_calls == [ + ToolCall(id="tc_1", tool_name="lookup", arguments={"q": "x"}) + ] + assert loaded.usage.total_tokens == 3 + assert deleted is None + assert client.deleted == ["k1"] + # --------------------------------------------------------------------------- # Cache Registry diff --git a/tests/llms/test_llm_client_shared.py b/tests/llms/test_llm_client_shared.py index d4faa20..4f646f0 100644 --- a/tests/llms/test_llm_client_shared.py +++ b/tests/llms/test_llm_client_shared.py @@ -3,7 +3,6 @@ import json from dataclasses import dataclass from datetime import datetime -from typing import Any import pytest @@ -26,7 +25,6 @@ from afk.llms.clients.shared.transport import collect_headers from afk.llms.types import ToolCall, Usage - # --------------------------------------------------------------------------- # content.py -- json_text # --------------------------------------------------------------------------- diff --git a/tests/llms/test_llm_factory_errors.py b/tests/llms/test_llm_factory_errors.py index b6d7175..919759f 100644 --- a/tests/llms/test_llm_factory_errors.py +++ b/tests/llms/test_llm_factory_errors.py @@ -4,26 +4,25 @@ import pytest -from afk.llms.factory import ( - register_llm_adapter, - available_llm_adapters, - create_llm, - create_llm_from_env, -) from afk.llms.errors import ( - LLMError, - LLMTimeoutError, - LLMRetryableError, - LLMInvalidResponseError, - LLMConfigurationError, - LLMCapabilityError, LLMCancelledError, + LLMCapabilityError, + LLMConfigurationError, + LLMError, LLMInterruptedError, + LLMInvalidResponseError, + LLMRetryableError, LLMSessionError, LLMSessionPausedError, + LLMTimeoutError, ) -from afk.llms.types import Usage, LLMResponse, LLMRequest, ToolCall - +from afk.llms.factory import ( + available_llm_adapters, + create_llm, + create_llm_from_env, + register_llm_adapter, +) +from afk.llms.types import LLMRequest, LLMResponse, ToolCall, Usage # --------------------------------------------------------------------------- # Factory legacy shims -- all must raise LLMConfigurationError diff --git a/tests/llms/test_llm_routing.py b/tests/llms/test_llm_routing.py index 6e98cd8..521d220 100644 --- a/tests/llms/test_llm_routing.py +++ b/tests/llms/test_llm_routing.py @@ -11,6 +11,7 @@ import pytest +from afk.llms.routing import registry from afk.llms.routing.defaults import OrderedFallbackRouter from afk.llms.routing.registry import ( LLMRouterError, @@ -18,7 +19,6 @@ list_llm_routers, register_llm_router, ) -from afk.llms.routing import registry from afk.llms.runtime.contracts import RoutePolicy from afk.llms.types import LLMRequest diff --git a/tests/llms/test_llm_runtime_policies.py b/tests/llms/test_llm_runtime_policies.py index 917d8be..e09f0e0 100644 --- a/tests/llms/test_llm_runtime_policies.py +++ b/tests/llms/test_llm_runtime_policies.py @@ -11,7 +11,6 @@ from __future__ import annotations import asyncio -import socket import time import pytest @@ -153,7 +152,7 @@ def test_retryable_error_passes_through(self): assert classify_error(err) is err def test_timeout_error_becomes_llm_timeout(self): - result = classify_error(asyncio.TimeoutError("timed out")) + result = classify_error(TimeoutError("timed out")) assert isinstance(result, LLMTimeoutError) def test_builtin_timeout_becomes_llm_timeout(self): @@ -161,7 +160,7 @@ def test_builtin_timeout_becomes_llm_timeout(self): assert isinstance(result, LLMTimeoutError) def test_socket_timeout_becomes_llm_timeout(self): - result = classify_error(socket.timeout("socket timed out")) + result = classify_error(TimeoutError("socket timed out")) assert isinstance(result, LLMTimeoutError) def test_connection_error_becomes_retryable(self): diff --git a/tests/llms/test_llm_settings.py b/tests/llms/test_llm_settings.py index 5c3df41..6f9eeb1 100644 --- a/tests/llms/test_llm_settings.py +++ b/tests/llms/test_llm_settings.py @@ -24,7 +24,6 @@ ) from afk.llms.settings import LLMSettings - # ============================== Helpers ============================== # All AFK_LLM_* env vars that LLMSettings.from_env() and LLMConfig.from_env() read. @@ -550,6 +549,10 @@ def test_ttl_s(self): p = CachePolicy() assert p.ttl_s == 30.0 + def test_namespace(self): + p = CachePolicy() + assert p.namespace is None + def test_frozen(self): p = CachePolicy() with pytest.raises(AttributeError): diff --git a/tests/llms/test_redis_pool.py b/tests/llms/test_redis_pool.py new file mode 100644 index 0000000..6c7571c --- /dev/null +++ b/tests/llms/test_redis_pool.py @@ -0,0 +1,140 @@ +from __future__ import annotations + +import asyncio + +from afk.llms.cache import redis_pool +from afk.llms.cache.redis_pool import ( + PoolConfig, + RedisConnectionPool, + close_all_pools, + get_redis_pool, +) + + +def run_async(coro): + return asyncio.run(coro) + + +class _FakeConnectionPool: + created: list[dict[str, object]] = [] + closed = 0 + + def __init__(self, **kwargs) -> None: + self.kwargs = kwargs + + @classmethod + def from_url(cls, url: str, **kwargs): + row = {"url": url, **kwargs} + cls.created.append(row) + return cls(**row) + + async def aclose(self) -> None: + type(self).closed += 1 + + +class _FakeRedis: + instances: list[_FakeRedis] = [] + + def __init__(self, *, connection_pool) -> None: + self.connection_pool = connection_pool + self.closed = False + self.ping_ok = True + type(self).instances.append(self) + + async def aclose(self) -> None: + self.closed = True + + async def ping(self) -> bool: + if not self.ping_ok: + raise RuntimeError("redis down") + return True + + +def setup_function(): + _FakeConnectionPool.created.clear() + _FakeConnectionPool.closed = 0 + _FakeRedis.instances.clear() + run_async(close_all_pools()) + + +def test_pool_connect_disconnect_and_stats(monkeypatch): + monkeypatch.setattr(redis_pool.redis.ConnectionPool, "from_url", _FakeConnectionPool.from_url) + monkeypatch.setattr(redis_pool.redis, "Redis", _FakeRedis) + + config = PoolConfig(max_connections=7, max_idle_connections=3, health_check_interval=9.5) + pool = RedisConnectionPool("redis://localhost:6379/0", config=config) + + assert pool.is_connected is False + assert pool.pool_stats is None + + run_async(pool.connect()) + + assert pool.is_connected is True + assert _FakeConnectionPool.created[0]["url"] == "redis://localhost:6379/0" + assert _FakeConnectionPool.created[0]["max_connections"] == 7 + assert _FakeConnectionPool.created[0]["max_idle_connections"] == 3 + assert _FakeConnectionPool.created[0]["health_check_interval"] == 9 + assert pool.pool_stats == { + "max_connections": 7, + "max_idle_connections": 3, + "connected": True, + } + + run_async(pool.disconnect()) + + assert pool.is_connected is False + assert _FakeRedis.instances[0].closed is True + assert _FakeConnectionPool.closed == 1 + + +def test_client_context_connects_lazily_and_yields_shared_client(monkeypatch): + monkeypatch.setattr(redis_pool.redis.ConnectionPool, "from_url", _FakeConnectionPool.from_url) + monkeypatch.setattr(redis_pool.redis, "Redis", _FakeRedis) + pool = RedisConnectionPool("redis://localhost:6379/1") + + async def scenario(): + async with pool.client() as client: + from_context = client + direct = await pool.get_client() + return from_context, direct + + from_context, direct = run_async(scenario()) + + assert from_context is direct + assert pool.is_connected is True + assert len(_FakeRedis.instances) == 1 + run_async(pool.disconnect()) + + +def test_health_check_returns_false_when_ping_fails(monkeypatch): + monkeypatch.setattr(redis_pool.redis.ConnectionPool, "from_url", _FakeConnectionPool.from_url) + monkeypatch.setattr(redis_pool.redis, "Redis", _FakeRedis) + pool = RedisConnectionPool("redis://localhost:6379/2") + + async def scenario(): + client = await pool.get_client() + client.ping_ok = False + return await pool.health_check() + + assert run_async(scenario()) is False + run_async(pool.disconnect()) + + +def test_get_redis_pool_reuses_by_url_and_close_all(monkeypatch): + monkeypatch.setattr(redis_pool.redis.ConnectionPool, "from_url", _FakeConnectionPool.from_url) + monkeypatch.setattr(redis_pool.redis, "Redis", _FakeRedis) + + async def scenario(): + first = await get_redis_pool("redis://localhost:6379/3") + second = await get_redis_pool("redis://localhost:6379/3") + other = await get_redis_pool("redis://localhost:6379/4") + await close_all_pools() + return first, second, other + + first, second, other = run_async(scenario()) + + assert first is second + assert first is not other + assert first.is_connected is False + assert other.is_connected is False + assert redis_pool._POOLS == {} diff --git a/tests/llms/test_runtime_streaming.py b/tests/llms/test_runtime_streaming.py index f96cd5b..4a96cf8 100644 --- a/tests/llms/test_runtime_streaming.py +++ b/tests/llms/test_runtime_streaming.py @@ -18,6 +18,7 @@ ) from afk.llms.errors import LLMInvalidResponseError from afk.llms.providers import ProviderSettingsSchema +from afk.llms.runtime import CachePolicy class _FakeStreamHandle: @@ -137,6 +138,40 @@ async def scenario(): ) +def test_chat_cache_key_is_scoped_by_session_namespace_and_response_model(): + register_llm_provider(_Provider("cache_scope"), overwrite=True) + llm = create_llm_client(provider="cache_scope", settings=LLMSettings()) + base = LLMRequest( + model="demo", + messages=[Message(role="user", content="hello")], + ) + session_req = dataclass_replace_session(base, "session_1") + other_session_req = dataclass_replace_session(base, "session_2") + tenant_req = dataclass_replace_metadata(base, {"tenant_id": "tenant_a"}) + + key_base = llm._cache_key("cache_scope", base) + key_session = llm._cache_key("cache_scope", session_req) + key_other_session = llm._cache_key("cache_scope", other_session_req) + key_tenant = llm._cache_key("cache_scope", tenant_req) + key_model = llm._cache_key("cache_scope", base, response_model=dict) + key_namespace = llm._cache_key( + "cache_scope", + base, + cache_policy=CachePolicy(namespace="customer_a"), + ) + + assert len( + { + key_base, + key_session, + key_other_session, + key_tenant, + key_model, + key_namespace, + } + ) == 6 + + def test_chat_stream_handle_interrupt_uses_single_underlying_handle(): provider = _Provider("stream_handle_ok") register_llm_provider(provider, overwrite=True) @@ -160,6 +195,18 @@ def dataclass_replace_route(req: LLMRequest, order: tuple[str, ...]) -> LLMReque return replace(req, route_policy=RoutePolicy(provider_order=order)) +def dataclass_replace_session(req: LLMRequest, session_token: str) -> LLMRequest: + from dataclasses import replace + + return replace(req, session_token=session_token) + + +def dataclass_replace_metadata(req: LLMRequest, metadata: dict[str, object]) -> LLMRequest: + from dataclasses import replace + + return replace(req, metadata=metadata) + + def test_stream_handle_rejects_double_completion_events(): provider = _Provider("stream_double") diff --git a/tests/llms/test_timeout_middleware.py b/tests/llms/test_timeout_middleware.py new file mode 100644 index 0000000..8b15809 --- /dev/null +++ b/tests/llms/test_timeout_middleware.py @@ -0,0 +1,134 @@ +from __future__ import annotations + +import asyncio +from dataclasses import replace + +import pytest + +from afk.llms.middleware.timeout import ( + EmbedTimeoutMiddleware, + StreamTimeoutMiddleware, + TimeoutConfig, + TimeoutMiddleware, + create_embed_timeout_middleware, + create_stream_timeout_middleware, + create_timeout_middleware, +) +from afk.llms.runtime import TimeoutPolicy +from afk.llms.types import ( + EmbeddingRequest, + EmbeddingResponse, + LLMRequest, + LLMResponse, + Message, + StreamTextDeltaEvent, +) + + +def run_async(coro): + return asyncio.run(coro) + + +def _request(**kwargs) -> LLMRequest: + return LLMRequest( + model=kwargs.pop("model", "demo"), + messages=[Message(role="user", content="hello")], + **kwargs, + ) + + +def test_chat_timeout_middleware_returns_response(): + async def call_next(req: LLMRequest) -> LLMResponse: + return LLMResponse(text=f"ok:{req.model}") + + middleware = TimeoutMiddleware(TimeoutConfig(default_timeout_s=1.0)) + + result = run_async(middleware(call_next, _request(model="m1"))) + + assert result.text == "ok:m1" + + +def test_chat_timeout_middleware_uses_request_policy_override(): + seen = {} + + async def call_next(req: LLMRequest) -> LLMResponse: + seen["model"] = req.model + await asyncio.sleep(0.02) + return LLMResponse(text="late") + + middleware = TimeoutMiddleware(TimeoutConfig(default_timeout_s=1.0)) + req = _request(timeout_policy=TimeoutPolicy(request_timeout_s=0.001)) + + with pytest.raises(TimeoutError): + run_async(middleware(call_next, req)) + assert seen["model"] == "demo" + + +def test_embed_timeout_middleware_returns_response(): + async def call_next(req: EmbeddingRequest) -> EmbeddingResponse: + return EmbeddingResponse(embeddings=[[1.0, 2.0]], model=req.model) + + middleware = EmbedTimeoutMiddleware(TimeoutConfig(embed_timeout_s=1.0)) + + result = run_async(middleware(call_next, EmbeddingRequest(model="embed"))) + + assert result.embeddings == [[1.0, 2.0]] + assert result.model == "embed" + + +def test_embed_timeout_middleware_raises_on_slow_call(): + async def call_next(req: EmbeddingRequest) -> EmbeddingResponse: + _ = req + await asyncio.sleep(0.02) + return EmbeddingResponse(embeddings=[]) + + middleware = EmbedTimeoutMiddleware(TimeoutConfig(embed_timeout_s=0.001)) + + with pytest.raises(TimeoutError): + run_async(middleware(call_next, EmbeddingRequest(model="embed"))) + + +def test_stream_timeout_middleware_yields_chunks(): + async def call_next(req: LLMRequest): + yield StreamTextDeltaEvent(delta=f"first:{req.model}") + yield StreamTextDeltaEvent(delta="second") + + middleware = StreamTimeoutMiddleware(TimeoutConfig(stream_timeout_s=1.0)) + + async def scenario(): + return [event async for event in middleware(call_next, _request(model="stream"))] + + events = run_async(scenario()) + + assert [event.delta for event in events] == ["first:stream", "second"] + + +def test_stream_timeout_middleware_applies_timeout_per_chunk(): + async def call_next(req: LLMRequest): + _ = req + await asyncio.sleep(0.02) + yield StreamTextDeltaEvent(delta="late") + + middleware = StreamTimeoutMiddleware(TimeoutConfig(stream_timeout_s=0.001)) + + async def scenario(): + return [event async for event in middleware(call_next, _request())] + + with pytest.raises(TimeoutError): + run_async(scenario()) + + +def test_timeout_middleware_factories_return_expected_types(): + config = TimeoutConfig(default_timeout_s=2.0) + + assert isinstance(create_timeout_middleware(config)[0], TimeoutMiddleware) + assert isinstance(create_embed_timeout_middleware(config)[0], EmbedTimeoutMiddleware) + assert isinstance(create_stream_timeout_middleware(config)[0], StreamTimeoutMiddleware) + + +def test_timeout_config_can_be_reused_with_modified_request(): + req = _request() + updated = replace(req, timeout_policy=TimeoutPolicy(request_timeout_s=0.5)) + + assert updated.timeout_policy is not None + assert updated.timeout_policy.request_timeout_s == 0.5 diff --git a/tests/mcp/test_mcp_server.py b/tests/mcp/test_mcp_server.py index ece7ede..0695ddf 100644 --- a/tests/mcp/test_mcp_server.py +++ b/tests/mcp/test_mcp_server.py @@ -1,14 +1,25 @@ from __future__ import annotations +from pydantic import BaseModel +import pytest + from fastapi.testclient import TestClient from afk.mcp import MCPServer -from afk.tools import ToolRegistry +from afk.mcp.server.runtime import MCPServerConfig +from afk.tools import ToolRegistry, tool + + +class EchoArgs(BaseModel): + value: str def test_mcp_endpoint_returns_204_for_jsonrpc_notification(): registry = ToolRegistry() - server = MCPServer(registry) + server = MCPServer( + registry, + config=MCPServerConfig(insecure_development=True), + ) client = TestClient(server.app) response = client.post( @@ -22,3 +33,68 @@ def test_mcp_endpoint_returns_204_for_jsonrpc_notification(): assert response.status_code == 204 assert response.text == "" + + +@tool(args_model=EchoArgs, name="echo") +def echo_tool(value: str) -> str: + return value + + +def test_tools_call_requires_token_by_default(): + registry = ToolRegistry() + registry.register(echo_tool) + server = MCPServer( + registry, + config=MCPServerConfig( + require_tools_auth=True, + mcp_tools_auth_token="token-123", + ), + ) + client = TestClient(server.app) + + response = client.post( + "/mcp", + json={ + "jsonrpc": "2.0", + "id": "auth-missing", + "method": "tools/call", + "params": {"name": "echo", "arguments": {"value": "hello"}}, + }, + ) + + payload = response.json() + assert response.status_code == 200 + assert payload["error"]["code"] == -32001 + + +def test_tools_call_accepts_bearer_token(): + registry = ToolRegistry() + registry.register(echo_tool) + server = MCPServer( + registry, + config=MCPServerConfig( + require_tools_auth=True, + mcp_tools_auth_token="token-123", + ), + ) + client = TestClient(server.app) + + response = client.post( + "/mcp", + headers={"Authorization": "Bearer token-123"}, + json={ + "jsonrpc": "2.0", + "id": "auth-ok", + "method": "tools/call", + "params": {"name": "echo", "arguments": {"value": "hello"}}, + }, + ) + + payload = response.json() + assert response.status_code == 200 + assert payload["result"]["content"][0]["text"] == "hello" + + +def test_wildcard_cors_not_allowed_with_credentials(): + with pytest.raises(ValueError, match="wildcard origin '\\*'"): + MCPServerConfig(cors_origins=["*"], allow_credentials=True) diff --git a/tests/mcp/test_mcp_store.py b/tests/mcp/test_mcp_store.py index 1cdeb11..217f389 100644 --- a/tests/mcp/test_mcp_store.py +++ b/tests/mcp/test_mcp_store.py @@ -171,3 +171,15 @@ def test_resolve_server_rejects_non_http_scheme_for_dict_url(): store = MCPStore() with pytest.raises(MCPServerResolutionError, match="http or https"): store.resolve_server({"name": "bad", "url": "ftp://example.com/mcp"}) + + +def test_store_rejects_public_http_mcp_urls(): + store = MCPStore() + with pytest.raises(MCPServerResolutionError, match="must use HTTPS for remote"): + run_async(store.list_tools("svc=http://example.com/mcp")) + + +def test_store_rejects_private_host_mcp_urls_by_default(): + store = MCPStore() + with pytest.raises(MCPServerResolutionError, match="restricted private"): + run_async(store.list_tools("svc=http://127.0.0.1:8000")) diff --git a/tests/mcp/test_mcp_store_utils.py b/tests/mcp/test_mcp_store_utils.py index c65aa7d..45fb332 100644 --- a/tests/mcp/test_mcp_store_utils.py +++ b/tests/mcp/test_mcp_store_utils.py @@ -16,13 +16,13 @@ _extract_mcp_text, _qualified_tool_name, _sanitize_name, + _validate_remote_url, _validate_http_url, normalize_json_schema, normalize_remote_tools, resolve_server_ref, ) - # ── _sanitize_name ────────────────────────────────────────────────────────── @@ -81,6 +81,41 @@ def test_empty_scheme_raises(self): _validate_http_url("://localhost") +class TestValidateRemoteUrl: + def test_blocks_non_https_non_private_host(self): + with pytest.raises(MCPServerResolutionError, match="must use HTTPS for remote"): + _validate_remote_url("http://example.com") + + def test_blocks_private_host_by_default(self): + with pytest.raises(MCPServerResolutionError, match="restricted private"): + _validate_remote_url("http://127.0.0.1:8000") + + def test_private_host_allowed_when_configured(self): + assert ( + _validate_remote_url( + "http://127.0.0.1:8000", + allow_private_networks=True, + require_https=False, + ) + == "http://127.0.0.1:8000" + ) + + def test_allowed_hostnames_can_be_enforced(self): + with pytest.raises(MCPServerResolutionError, match="not in allowed_hostnames"): + _validate_remote_url( + "https://evil.example.com", + allowed_hostnames=("allowed.example.com", "trusted.example.com"), + ) + + assert ( + _validate_remote_url( + "https://allowed.example.com", + allowed_hostnames=("allowed.example.com", "trusted.example.com"), + ) + == "https://allowed.example.com" + ) + + # ── _qualified_tool_name ───────────────────────────────────────────────────── diff --git a/tests/memory/test_lifecycle_edge_cases.py b/tests/memory/test_lifecycle_edge_cases.py index eedeb75..fc53c41 100644 --- a/tests/memory/test_lifecycle_edge_cases.py +++ b/tests/memory/test_lifecycle_edge_cases.py @@ -24,10 +24,8 @@ apply_state_retention, compact_thread_memory, ) -from afk.memory.store import MemoryStore from afk.memory.types import MemoryEvent - # --------------------------------------------------------------------------- # Helpers # --------------------------------------------------------------------------- @@ -402,9 +400,6 @@ async def _run(): for i in range(10): await store.append_event(_event(i, "message")) - # Monkey-patch to raise NotImplementedError - original_replace = store.replace_thread_events - async def _raise_not_impl(thread_id, events): raise NotImplementedError("not supported") diff --git a/tests/memory/test_memory_compaction.py b/tests/memory/test_memory_compaction.py index 26afcee..c8e5d82 100644 --- a/tests/memory/test_memory_compaction.py +++ b/tests/memory/test_memory_compaction.py @@ -7,12 +7,12 @@ """ import pytest + from afk.memory import ( CompactionConfig, CompactionStats, MemoryCompactor, ) -from afk.memory.types import MemoryEvent class TestCompactionConfig: @@ -158,6 +158,6 @@ async def test_stop(self) -> None: from afk.memory import BackgroundCompactor compactor = BackgroundCompactor() - compactor.stop() + await compactor.stop() assert compactor._running is False diff --git a/tests/memory/test_memory_utils_vector.py b/tests/memory/test_memory_utils_vector.py index 147ee15..e6c623b 100644 --- a/tests/memory/test_memory_utils_vector.py +++ b/tests/memory/test_memory_utils_vector.py @@ -11,13 +11,12 @@ import numpy as np import pytest -from afk.memory.utils import json_dumps, json_loads, new_id, now_ms -from afk.memory.vector import cosine_similarity, format_pgvector -from afk.memory.factory import _env_bool, create_memory_store_from_env from afk.memory.adapters.in_memory import InMemoryMemoryStore from afk.memory.adapters.sqlite import SQLiteMemoryStore +from afk.memory.factory import _env_bool, create_memory_store_from_env from afk.memory.types import LongTermMemory, MemoryEvent - +from afk.memory.utils import json_dumps, json_loads, new_id, now_ms +from afk.memory.vector import cosine_similarity, format_pgvector # --------------------------------------------------------------------------- # Helpers diff --git a/tests/observability/test_observability_backends_exporters.py b/tests/observability/test_observability_backends_exporters.py index 9cd7432..7c6f5a1 100644 --- a/tests/observability/test_observability_backends_exporters.py +++ b/tests/observability/test_observability_backends_exporters.py @@ -8,23 +8,22 @@ import pytest -from afk.observability.backends.null import NullTelemetrySink, NullTelemetryBackend -from afk.observability.collectors.runtime import RuntimeTelemetryCollector +from afk.core.telemetry import TelemetryEvent, TelemetrySpan +from afk.observability.backends import registry as _reg +from afk.observability.backends.null import NullTelemetryBackend, NullTelemetrySink from afk.observability.backends.registry import ( - register_telemetry_backend, + TelemetryBackendError, + create_telemetry_sink, get_telemetry_backend, list_telemetry_backends, - create_telemetry_sink, - TelemetryBackendError, + register_telemetry_backend, ) -from afk.observability.backends import registry as _reg +from afk.observability.collectors.runtime import RuntimeTelemetryCollector from afk.observability.exporters.console import ConsoleRunMetricsExporter from afk.observability.exporters.json import JSONRunMetricsExporter from afk.observability.exporters.jsonl import JSONLRunMetricsExporter -from afk.observability.projectors.run_metrics import run_metrics_schema_version from afk.observability.models import RunMetrics -from afk.core.telemetry import TelemetryEvent, TelemetrySpan - +from afk.observability.projectors.run_metrics import run_metrics_schema_version # ======================================================================= # NullTelemetrySink diff --git a/tests/observability/test_observability_models.py b/tests/observability/test_observability_models.py index 0fd2ed3..0fe7e1b 100644 --- a/tests/observability/test_observability_models.py +++ b/tests/observability/test_observability_models.py @@ -4,7 +4,6 @@ from afk.observability.models import RunMetrics - # ----------------------------------------------------------------------- # RunMetrics default values # ----------------------------------------------------------------------- diff --git a/tests/observability/test_observability_projectors.py b/tests/observability/test_observability_projectors.py index ce72169..872e483 100644 --- a/tests/observability/test_observability_projectors.py +++ b/tests/observability/test_observability_projectors.py @@ -6,18 +6,17 @@ import pytest +from afk.core.telemetry import TelemetryEvent +from afk.observability import contracts +from afk.observability.collectors.runtime import RuntimeTelemetryCollector from afk.observability.projectors.run_metrics import ( - run_metrics_schema_version, - project_run_metrics_from_collector, - _to_int, + _counter_total, _to_float, + _to_int, _to_str, - _counter_total, + project_run_metrics_from_collector, + run_metrics_schema_version, ) -from afk.observability.collectors.runtime import RuntimeTelemetryCollector -from afk.observability import contracts -from afk.core.telemetry import TelemetryEvent - # --------------------------------------------------------------------------- # run_metrics_schema_version diff --git a/tests/queues/test_queue_contracts_worker.py b/tests/queues/test_queue_contracts_worker.py index 8da5369..cf8867c 100644 --- a/tests/queues/test_queue_contracts_worker.py +++ b/tests/queues/test_queue_contracts_worker.py @@ -12,6 +12,7 @@ JobDispatchExecutionContract, RunnerChatExecutionContract, ) +from afk.queues.contracts import EXECUTION_CONTRACT_KEY from afk.queues.types import ( NEXT_ATTEMPT_AT_KEY, RETRY_BACKOFF_BASE_KEY, @@ -20,7 +21,6 @@ RetryPolicy, TaskItem, ) -from afk.queues.contracts import EXECUTION_CONTRACT_KEY def run_async(coro): diff --git a/tests/queues/test_queue_factory.py b/tests/queues/test_queue_factory.py index ad695d0..ecad942 100644 --- a/tests/queues/test_queue_factory.py +++ b/tests/queues/test_queue_factory.py @@ -9,7 +9,6 @@ from afk.queues.factory import _env_first, create_task_queue_from_env from afk.queues.memory import InMemoryTaskQueue - # ── _env_first ─────────────────────────────────────────────────────────────── diff --git a/tests/queues/test_worker_contracts.py b/tests/queues/test_worker_contracts.py index 946c0e6..98cb8ad 100644 --- a/tests/queues/test_worker_contracts.py +++ b/tests/queues/test_worker_contracts.py @@ -44,6 +44,54 @@ async def scenario() -> None: run_async(scenario()) +def test_cancelled_task_execution_marks_task_as_failed_non_retryable(): + class _SlowContract: + contract_id = "slow.v1" + requires_agent = False + + async def execute( + self, + task_item: TaskItem, + *, + agent, + worker_context: ExecutionContractContext, + ): + _ = task_item + _ = agent + _ = worker_context + await asyncio.sleep(1.0) + return {"done": True} + + async def scenario() -> None: + queue = InMemoryTaskQueue() + task = await queue.enqueue_contract( + "slow.v1", + payload={}, + agent_name=None, + max_retries=3, + ) + worker = TaskWorker( + queue, + agents={}, + execution_contracts={"slow.v1": _SlowContract()}, + ) + running = await queue.dequeue(timeout=0.1) + assert running is not None + + handle = asyncio.create_task(worker._execute_task(running)) + await asyncio.sleep(0.1) + handle.cancel() + await handle + + current = await queue.get(task.id) + assert current is not None + assert current.status == "failed" + assert current.retry_count == 1 + assert current.error == "Task execution cancelled" + + run_async(scenario()) + + def test_unknown_contract_is_terminal_without_retry(): async def scenario() -> None: queue = InMemoryTaskQueue() diff --git a/tests/test_config.py b/tests/test_config.py index fa41dfd..faf1a9a 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -178,6 +178,7 @@ def test_defaults(self, monkeypatch): env = MCPServerEnv.from_env() assert env.AFK_CORS_ORIGINS == [] assert env.AFK_MCP_NAME == "afk-mcp-server" + assert env.AFK_MCP_HOST == "127.0.0.1" assert env.AFK_MCP_PORT == 8000 assert env.AFK_MCP_ENABLE_SSE is True assert env.AFK_MCP_ENABLE_HEALTH is True @@ -252,7 +253,7 @@ def test_defaults(self, monkeypatch): monkeypatch.delenv("AFK_QUEUE_REDIS_URL", raising=False) env = MemoryEnv.from_env() - assert env.AFK_MEMORY_BACKEND == "sqlite" + assert env.AFK_MEMORY_BACKEND == "memory" assert env.AFK_SQLITE_PATH == "afk_memory.sqlite3" assert env.AFK_REDIS_HOST == "localhost" assert env.AFK_REDIS_PORT == 6379 diff --git a/tests/test_integration.py b/tests/test_integration.py index 6506f15..e412886 100644 --- a/tests/test_integration.py +++ b/tests/test_integration.py @@ -16,41 +16,40 @@ from pydantic import BaseModel # --------------------------------------------------------------------------- -# Tools +# LLMs # --------------------------------------------------------------------------- -from afk.tools.registry import ToolRegistry -from afk.tools.security import SandboxProfile, build_registry_sandbox_policy -from afk.tools.core.base import ToolContext, ToolResult, ToolSpec -from afk.tools.core.decorator import tool, prehook, posthook, middleware -from afk.tools.core.errors import ToolPolicyError +from afk.llms.cache.inmemory import InMemoryLLMCache +from afk.llms.errors import LLMRetryableError +from afk.llms.routing.defaults import OrderedFallbackRouter +from afk.llms.runtime.circuit_breaker import CircuitBreaker +from afk.llms.runtime.contracts import CircuitBreakerPolicy, RoutePolicy +from afk.llms.runtime.contracts import RetryPolicy as LLMRetryPolicy +from afk.llms.runtime.retry import call_with_retry +from afk.llms.types import LLMRequest, LLMResponse +from afk.llms.utils import clamp_str, extract_json_object, safe_json_loads # --------------------------------------------------------------------------- # Memory # --------------------------------------------------------------------------- from afk.memory.adapters.in_memory import InMemoryMemoryStore -from afk.memory.lifecycle import compact_thread_memory, RetentionPolicy +from afk.memory.lifecycle import RetentionPolicy, compact_thread_memory from afk.memory.types import MemoryEvent -from afk.memory.utils import now_ms, new_id +from afk.memory.utils import new_id, now_ms +from afk.queues.contracts import RUNNER_CHAT_CONTRACT # --------------------------------------------------------------------------- -# LLMs +# Queues # --------------------------------------------------------------------------- -from afk.llms.cache.inmemory import InMemoryLLMCache -from afk.llms.runtime.circuit_breaker import CircuitBreaker -from afk.llms.runtime.contracts import CircuitBreakerPolicy, RetryPolicy as LLMRetryPolicy -from afk.llms.runtime.retry import call_with_retry -from afk.llms.errors import LLMRetryableError -from afk.llms.types import LLMResponse, LLMRequest -from afk.llms.runtime.contracts import RoutePolicy -from afk.llms.routing.defaults import OrderedFallbackRouter -from afk.llms.utils import extract_json_object, safe_json_loads, clamp_str +from afk.queues.memory import InMemoryTaskQueue +from afk.tools.core.base import ToolContext +from afk.tools.core.decorator import middleware, posthook, prehook, tool +from afk.tools.core.errors import ToolPolicyError # --------------------------------------------------------------------------- -# Queues +# Tools # --------------------------------------------------------------------------- -from afk.queues.memory import InMemoryTaskQueue -from afk.queues.types import TaskItem -from afk.queues.contracts import RUNNER_CHAT_CONTRACT +from afk.tools.registry import ToolRegistry +from afk.tools.security import SandboxProfile, build_registry_sandbox_policy def run_async(coro): diff --git a/tests/tools/test_prebuilt_tools.py b/tests/tools/test_prebuilt_tools.py index 55d016f..4f00e21 100644 --- a/tests/tools/test_prebuilt_tools.py +++ b/tests/tools/test_prebuilt_tools.py @@ -10,16 +10,17 @@ import pytest +from afk.agents.types import SkillRef, SkillToolPolicy from afk.tools.core.base import ToolContext, ToolResult from afk.tools.core.errors import ToolExecutionError from afk.tools.prebuilts.errors import FileAccessError from afk.tools.prebuilts.runtime import ( + _ensure_inside, _ListDirectoryArgs, _ReadFileArgs, - _ensure_inside, build_runtime_tools, ) - +from afk.tools.prebuilts.skills import build_skill_tools # --------------------------------------------------------------------------- # Helpers @@ -335,3 +336,47 @@ def test_directory_as_file_fails(self, setup): assert result.success is False assert result.error_message is not None + + +class TestSkillCommandTool: + """Tests for constrained skill command execution cwd behavior.""" + + def _skill_ref(self, root: Path, name: str) -> SkillRef: + root.mkdir(parents=True, exist_ok=True) + skill_md = root / "SKILL.md" + skill_md.write_text(f"---\nname: {name}\n---\n", encoding="utf-8") + return SkillRef( + name=name, + description="test skill", + root_dir=str(root), + skill_md_path=str(skill_md), + ) + + def test_command_without_cwd_uses_single_skill_root(self, tmp_path: Path): + skill_root = tmp_path / "skill-a" + ref = self._skill_ref(skill_root, "skill-a") + tools = build_skill_tools( + skills=[ref], + policy=SkillToolPolicy(command_allowlist=["pwd"]), + ) + run_tool = [t for t in tools if t.spec.name == "run_skill_command"][0] + + result = _call_tool(run_tool, {"command": "pwd"}, "run_skill_command") + + assert result.success is True + assert result.output["cwd"] == str(skill_root.resolve()) + assert result.output["stdout"].strip() == str(skill_root.resolve()) + + def test_command_without_cwd_fails_for_multiple_skill_roots(self, tmp_path: Path): + ref_a = self._skill_ref(tmp_path / "skill-a", "skill-a") + ref_b = self._skill_ref(tmp_path / "skill-b", "skill-b") + tools = build_skill_tools( + skills=[ref_a, ref_b], + policy=SkillToolPolicy(command_allowlist=["pwd"]), + ) + run_tool = [t for t in tools if t.spec.name == "run_skill_command"][0] + + result = _call_tool(run_tool, {"command": "pwd"}, "run_skill_command") + + assert result.success is False + assert "cwd is required" in (result.error_message or "") diff --git a/tests/tools/test_tool_security.py b/tests/tools/test_tool_security.py index 5143aa9..355f375 100644 --- a/tests/tools/test_tool_security.py +++ b/tests/tools/test_tool_security.py @@ -41,6 +41,8 @@ def large_text(args: _LargeArgs) -> dict[str, str]: def test_validate_tool_args_against_sandbox_denies_path_and_command_operator(tmp_path): profile = SandboxProfile( profile_id="strict", + allow_command_execution=True, + allowed_command_prefixes=["ls"], allowed_paths=[str(tmp_path / "allowed")], denied_paths=["/etc"], deny_shell_operators=True, diff --git a/tests/tools/test_tool_security_comprehensive.py b/tests/tools/test_tool_security_comprehensive.py index 94e45a9..fc3c73c 100644 --- a/tests/tools/test_tool_security_comprehensive.py +++ b/tests/tools/test_tool_security_comprehensive.py @@ -9,26 +9,23 @@ from __future__ import annotations -from pathlib import Path -from typing import Any - import pytest +from afk.tools.core.base import ToolContext, ToolResult +from afk.tools.core.errors import ToolPolicyError from afk.tools.security import ( SandboxProfile, + _extract_command_parts, + _is_command_allowed, + _iter_leaf_values, + _looks_like_path_key, + _truncate_json_like, + _truncate_text, apply_tool_output_limits, build_registry_sandbox_policy, resolve_sandbox_profile, validate_tool_args_against_sandbox, - _is_command_allowed, - _looks_like_path_key, - _iter_leaf_values, - _extract_command_parts, - _truncate_text, - _truncate_json_like, ) -from afk.tools.core.errors import ToolPolicyError -from afk.tools.core.base import ToolContext, ToolResult # --------------------------------------------------------------------------- @@ -41,7 +38,7 @@ def test_default_values(self): profile = SandboxProfile() assert profile.profile_id == "default" assert profile.allow_network is False - assert profile.allow_command_execution is True + assert profile.allow_command_execution is False assert profile.allowed_command_prefixes == [] assert profile.deny_shell_operators is True assert profile.allowed_paths == [] @@ -158,6 +155,7 @@ def test_command_execution_denied_blocks_commands(self, tmp_path): def test_command_execution_allowed_passes(self, tmp_path): profile = SandboxProfile( allow_command_execution=True, + allowed_command_prefixes=["ls"], deny_shell_operators=False, ) result = validate_tool_args_against_sandbox( @@ -172,6 +170,7 @@ def test_command_execution_allowed_passes(self, tmp_path): def test_allowed_command_prefixes_exact_match(self, tmp_path): profile = SandboxProfile( + allow_command_execution=True, allowed_command_prefixes=["git", "npm"], deny_shell_operators=False, ) @@ -185,6 +184,7 @@ def test_allowed_command_prefixes_exact_match(self, tmp_path): def test_allowed_command_prefixes_path_prefix_match(self, tmp_path): profile = SandboxProfile( + allow_command_execution=True, allowed_command_prefixes=["git"], deny_shell_operators=False, ) @@ -198,6 +198,7 @@ def test_allowed_command_prefixes_path_prefix_match(self, tmp_path): def test_command_not_in_allowlist_blocked(self, tmp_path): profile = SandboxProfile( + allow_command_execution=True, allowed_command_prefixes=["git", "npm"], deny_shell_operators=False, ) @@ -217,7 +218,11 @@ def test_command_not_in_allowlist_blocked(self, tmp_path): ["&&", "||", ";", "|", "`", "$(", ">", ">>", "<", "<<", "&"], ) def test_shell_operators_blocked_when_deny_enabled(self, tmp_path, operator): - profile = SandboxProfile(deny_shell_operators=True) + profile = SandboxProfile( + allow_command_execution=True, + allowed_command_prefixes=["ls"], + deny_shell_operators=True, + ) result = validate_tool_args_against_sandbox( tool_name="run", tool_args={"command": f"ls {operator} whoami"}, @@ -228,7 +233,11 @@ def test_shell_operators_blocked_when_deny_enabled(self, tmp_path, operator): assert "shell operator" in result.lower() def test_shell_operators_allowed_when_deny_disabled(self, tmp_path): - profile = SandboxProfile(deny_shell_operators=False) + profile = SandboxProfile( + allow_command_execution=True, + allowed_command_prefixes=["ls && whoami"], + deny_shell_operators=False, + ) result = validate_tool_args_against_sandbox( tool_name="run", tool_args={"command": "ls && whoami"}, @@ -412,7 +421,7 @@ def test_truncates_nested_list_values(self): returned = apply_tool_output_limits(result, profile=profile) assert isinstance(returned.output, list) assert "truncated" in returned.output[0] - assert returned.output[1] == "short" + assert returned.output[1] == "[truncated]" def test_short_output_passes_through_unchanged(self): profile = SandboxProfile(max_output_chars=1000) @@ -559,14 +568,14 @@ def test_list_items_truncated_recursively(self): assert isinstance(result, list) assert len(result) == 2 assert "truncated" in result[0] - assert result[1] == "ok" + assert result[1] == "[truncated]" def test_dict_values_truncated_recursively(self): data = {"key1": "b" * 50, "key2": 42} result = _truncate_json_like(data, max_chars=10) assert isinstance(result, dict) assert "truncated" in result["key1"] - assert result["key2"] == 42 + assert result["..."] == "[truncated]" def test_nested_dict_list_structure(self): data = {"items": [{"name": "x" * 100}]} diff --git a/uv.lock b/uv.lock index 76a32dc..784ed8e 100644 --- a/uv.lock +++ b/uv.lock @@ -1,5 +1,5 @@ version = 1 -revision = 2 +revision = 3 requires-python = ">=3.13" [manifest] @@ -12,12 +12,6 @@ members = [ name = "02-weather-agent" version = "0.1.0" source = { virtual = "examples/projects/02_Weather_Agent" } -dependencies = [ - { name = "pydantic" }, -] - -[package.metadata] -requires-dist = [{ name = "pydantic", specifier = ">=2.12.5" }] [[package]] name = "afk-py" @@ -26,42 +20,66 @@ source = { virtual = "." } dependencies = [ { name = "aiosqlite" }, { name = "async-lru" }, - { name = "asyncpg" }, { name = "cachetools" }, { name = "claude-agent-sdk" }, - { name = "fastapi" }, { name = "jinja2" }, { name = "litellm" }, { name = "numpy" }, { name = "openai" }, { name = "prometheus-client" }, - { name = "pytest" }, - { name = "redis" }, ] [package.optional-dependencies] a2a = [ { name = "pyjwt" }, ] +all = [ + { name = "asyncpg" }, + { name = "fastapi" }, + { name = "pyjwt" }, + { name = "pytest" }, + { name = "redis" }, + { name = "uvicorn" }, +] +dev = [ + { name = "pytest" }, +] +mcp = [ + { name = "fastapi" }, + { name = "uvicorn" }, +] +postgres = [ + { name = "asyncpg" }, +] +redis = [ + { name = "redis" }, +] [package.metadata] requires-dist = [ { name = "aiosqlite", specifier = ">=0.22.1" }, { name = "async-lru", specifier = ">=2.1.0" }, - { name = "asyncpg", specifier = ">=0.31.0" }, + { name = "asyncpg", marker = "extra == 'all'", specifier = ">=0.31.0" }, + { name = "asyncpg", marker = "extra == 'postgres'", specifier = ">=0.31.0" }, { name = "cachetools", specifier = ">=7.0.1" }, { name = "claude-agent-sdk", specifier = "==0.1.37" }, - { name = "fastapi", specifier = ">=0.129.0" }, + { name = "fastapi", marker = "extra == 'all'", specifier = ">=0.129.0" }, + { name = "fastapi", marker = "extra == 'mcp'", specifier = ">=0.129.0" }, { name = "jinja2", specifier = ">=3.1.6" }, - { name = "litellm", specifier = "==1.81.13" }, + { name = "litellm", specifier = ">=1.83.10" }, { name = "numpy", specifier = ">=2.3.0" }, { name = "openai", specifier = "==2.21.0" }, { name = "prometheus-client", specifier = ">=0.24.1" }, { name = "pyjwt", marker = "extra == 'a2a'", specifier = ">=2.10.1" }, - { name = "pytest", specifier = ">=9.0.2" }, - { name = "redis", specifier = "==7.2.0" }, + { name = "pyjwt", marker = "extra == 'all'", specifier = ">=2.10.1" }, + { name = "pytest", marker = "extra == 'all'", specifier = ">=9.0.2" }, + { name = "pytest", marker = "extra == 'dev'", specifier = ">=9.0.2" }, + { name = "redis", marker = "extra == 'all'", specifier = "==7.2.0" }, + { name = "redis", marker = "extra == 'redis'", specifier = "==7.2.0" }, + { name = "uvicorn", marker = "extra == 'all'", specifier = ">=0.38.0" }, + { name = "uvicorn", marker = "extra == 'mcp'", specifier = ">=0.38.0" }, ] -provides-extras = ["a2a"] +provides-extras = ["a2a", "mcp", "postgres", "redis", "dev", "evals", "all"] [[package]] name = "aiohappyeyeballs" @@ -799,7 +817,7 @@ wheels = [ [[package]] name = "litellm" -version = "1.81.13" +version = "1.86.2" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "aiohttp" }, @@ -815,9 +833,9 @@ dependencies = [ { name = "tiktoken" }, { name = "tokenizers" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/72/80/b6cb799e7100953d848e106d0575db34c75bc3b57f31f2eefdfb1e23655f/litellm-1.81.13.tar.gz", hash = "sha256:083788d9c94e3371ff1c42e40e0e8198c497772643292a65b1bc91a3b3b537ea", size = 16562861, upload-time = "2026-02-17T02:00:47.466Z" } +sdist = { url = "https://files.pythonhosted.org/packages/01/ee/1f6f3f5d01e0910629b6af3757e82c47b8b87dca497a661a9c63280b4bd8/litellm-1.86.2.tar.gz", hash = "sha256:7d559ad48b97d796ff325af88fd7eebbdc66e58773fb5312130ab1cac968f8f3", size = 15380548, upload-time = "2026-05-27T16:19:58.45Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/be/f3/fffb7932870163cea7addc392165647a9a8a5489967de486c854226f1141/litellm-1.81.13-py3-none-any.whl", hash = "sha256:ae4aea2a55e85993f5f6dd36d036519422d24812a1a3e8540d9e987f2d7a4304", size = 14587505, upload-time = "2026-02-17T02:00:44.22Z" }, + { url = "https://files.pythonhosted.org/packages/4d/37/4da1dd67157aaa11477d6c63e4725216bfc46b4661e581b490d17e5b2831/litellm-1.86.2-py3-none-any.whl", hash = "sha256:27096463be7add661513ada3d9039e8f1a6195859e604d30fde96c939efe0a03", size = 17013061, upload-time = "2026-05-27T16:19:53.219Z" }, ] [[package]]