diff --git a/.github/workflows/pull_request.yml b/.github/workflows/pull_request.yml index 9fe1d4906..15056aeb7 100644 --- a/.github/workflows/pull_request.yml +++ b/.github/workflows/pull_request.yml @@ -32,7 +32,7 @@ jobs: run: | python -m pip install --upgrade pip pip install -e . - pip install pytest pytest-cov coverage + pip install pytest pytest-cov coverage jsonschema - name: Verify agents import without extras installed id: agent_base_import diff --git a/.gitignore b/.gitignore index 0dd6548f0..34da67052 100644 --- a/.gitignore +++ b/.gitignore @@ -173,3 +173,4 @@ tests/unit/automator/_trial_temp/_trial_marker # agent e2e bundle staging output (scripts/package-e2e-bundle.sh) scripts/e2e-bundle-dist/ +/design/openspec diff --git a/README.md b/README.md index 6daa47290..9869f5507 100644 --- a/README.md +++ b/README.md @@ -1,599 +1,186 @@ # Python SDK for Conductor -[![CI](https://github.com/conductor-sdk/conductor-python/actions/workflows/pull_request.yml/badge.svg)](https://github.com/conductor-sdk/conductor-python/actions/workflows/pull_request.yml) +[![CI](https://github.com/conductor-oss/python-sdk/actions/workflows/pull_request.yml/badge.svg)](https://github.com/conductor-oss/python-sdk/actions/workflows/pull_request.yml) [![PyPI](https://img.shields.io/pypi/v/conductor-python.svg)](https://pypi.org/project/conductor-python/) [![Python Versions](https://img.shields.io/pypi/pyversions/conductor-python.svg)](https://pypi.org/project/conductor-python/) [![License](https://img.shields.io/pypi/l/conductor-python.svg)](LICENSE) -Python SDK for [Conductor](https://www.conductor-oss.org/) (OSS and Orkes Conductor) — an orchestration platform for building distributed applications, AI agents, and workflow-driven microservices. Define workflows as code, run workers anywhere, and let Conductor handle retries, state management, and observability. - -If you find [Conductor](https://github.com/conductor-oss/conductor) useful, please consider giving it a star on GitHub — it helps the project grow. - -[![GitHub stars](https://img.shields.io/github/stars/conductor-oss/conductor.svg?style=social&label=Star&maxAge=)](https://GitHub.com/conductor-oss/conductor/) - - -* [Start Conductor Server](#start-conductor-server) -* [Install the SDK](#install-the-sdk) -* [60-Second Quickstart](#60-second-quickstart) -* [Feature Showcase](#feature-showcase) - * [Workers: Sync and Async](#workers-sync-and-async) - * [Workflows with HTTP Calls and Waits](#workflows-with-http-calls-and-waits) - * [Long-Running Tasks with TaskContext](#long-running-tasks-with-taskcontext) - * [Lease Extension for Long-Running Tasks](#lease-extension-for-long-running-tasks) - * [Monitoring with Metrics](#monitoring-with-metrics) - * [Managing Workflow Executions](#managing-workflow-executions) -* [AI & LLM Workflows](#ai--llm-workflows) -* [AI Agents](#ai-agents) -* [Why Conductor?](#why-conductor) -* [Examples](#examples) -* [Documentation](#documentation) -* [Frequently Asked Questions](#frequently-asked-questions) -* [Support](#support) -* [License](#license) - - -## Start Conductor Server - -#### Conductor CLI -```shell -# Installs conductor cli -npm install -g @conductor-oss/conductor-cli - -# Start the open source conductor server on port 8080 -conductor server start -# see conductor server --help for all the available commands -``` +The Python SDK for [Conductor](https://www.conductor-oss.org/) lets you build durable Conductor agents, workflows, and workers. Conductor coordinates retries, state, and observability while your Python code runs wherever you deploy it. +**Get involved:** [⭐ Conductor OSS](https://github.com/conductor-oss/conductor) · [Choose a Conductor OSS contribution](https://github.com/conductor-oss/conductor/contribute) · [Contribution guide](https://github.com/conductor-oss/conductor/blob/main/CONTRIBUTING.md) -Alternatively, if you want to use docker - -**Docker Compose** +**Using an AI coding agent?** Load [Conductor Skills](https://github.com/conductor-oss/conductor-skills) so it can create, run, and operate Conductor workflows and Conductor agents: ```shell -docker run -p 8080:8080 conductoross/conductor:latest +npm install -g @conductor-oss/conductor-skills && conductor-skills --all ``` -The UI will be available at `http://localhost:8080` and the API at `http://localhost:8080/api` -**MacOS / Linux (one-liner):** (If you don't want to use docker, you can install and run the binary directly) -```shell -curl -sSL https://raw.githubusercontent.com/conductor-oss/conductor/main/conductor_server.sh | sh -``` +## Choose your path +| I want to… | Start here | +|---|---| +| Build a durable Conductor agent with tools and human approval | [Run an AI agent example](#ai-agent-quickstart) | +| Bring an existing Google ADK, LangChain, LangGraph, OpenAI Agents, or Claude Agent SDK agent | [Use framework bridges](#framework-bridges) | +| Build a durable workflow and Python worker | [Run the core hello-world example](#workflow-and-worker-quickstart) | +| Browse all examples | [AI agent guide](docs/agents/README.md) · [Core examples](docs/examples.md) | +| Navigate the SDK documentation | [Documentation hub](docs/README.md) | -## Install the SDK +## Choose your Conductor server -```shell -python3 -m venv conductor-env -source conductor-env/bin/activate # Windows: conductor-env\Scripts\activate -pip install conductor-python -``` +Connect to a server before following either quickstart. Use the hosted Developer Edition by default, or run Conductor locally when you need a self-managed development environment. -> **Already in a virtual environment?** Skip the `venv` step and run `pip install conductor-python` directly. On macOS, Windows, or in containers where system Python is not locked down, you can also install globally. +### Recommended: Orkes Developer Edition -Building durable AI agents instead? Install the `agents` extra (or a per-framework extra — -see [AI Agents](#ai-agents)): +[Orkes Developer Edition](https://developer.orkescloud.com/) is the default hosted option. Create an application and access key in the Developer Edition UI, then configure this SDK with its API endpoint. Keep the key and secret out of source control. ```shell -pip install 'conductor-python[agents]' +export CONDUCTOR_SERVER_URL=https://developer.orkescloud.com/api +export CONDUCTOR_AUTH_KEY= +export CONDUCTOR_AUTH_SECRET= ``` -## 60-Second Quickstart - -**Step 1: Create a workflow** - -Workflows are definitions that reference task types (e.g. a SIMPLE task called `greet`). We'll build a workflow called -`greetings` that runs one task and returns its output. - -Assuming you have a `WorkflowExecutor` (`executor`) and a worker task (`greet`): - -```python -from conductor.client.workflow.conductor_workflow import ConductorWorkflow - -workflow = ConductorWorkflow(name='greetings', version=1, executor=executor) -greet_task = greet(task_ref_name='greet_ref', name=workflow.input('name')) -workflow >> greet_task -workflow.output_parameters({'result': greet_task.output('result')}) -workflow.register(overwrite=True) -``` - -**Step 2: Write a worker** - -Workers are just Python functions decorated with `@worker_task` that poll Conductor for tasks and execute them. - -```python -from conductor.client.worker.worker_task import worker_task - -# register_task_def=True is convenient for local dev quickstarts; in production, manage task definitions separately. -@worker_task(task_definition_name='greet', register_task_def=True) -def greet(name: str) -> str: - return f'Hello {name}' -``` - -**Step 3: Run your first workflow app** - -Create a `quickstart.py` with the following: - -```python -from conductor.client.automator.task_handler import TaskHandler -from conductor.client.configuration.configuration import Configuration -from conductor.client.orkes_clients import ConductorClients # OrkesClients is an alias for the same class -from conductor.client.workflow.conductor_workflow import ConductorWorkflow -from conductor.client.worker.worker_task import worker_task - - -# A worker is any Python function. -@worker_task(task_definition_name='greet', register_task_def=True) -def greet(name: str) -> str: - return f'Hello {name}' - - -def main(): - # Configure the SDK (reads CONDUCTOR_SERVER_URL / CONDUCTOR_AUTH_* from env). - config = Configuration() - - clients = ConductorClients(configuration=config) - executor = clients.get_workflow_executor() +For another hosted or self-managed remote cluster, use that cluster's `/api` URL and its application credentials instead. See [server setup](docs/server-setup.md) for details. - # Build a workflow with the >> operator. - workflow = ConductorWorkflow(name='greetings', version=1, executor=executor) - greet_task = greet(task_ref_name='greet_ref', name=workflow.input('name')) - workflow >> greet_task - workflow.output_parameters({'result': greet_task.output('result')}) - workflow.register(overwrite=True) +### Local alternative: Conductor CLI - # Start polling for tasks (one worker subprocess per worker function). - # Note: scan_for_annotated_workers=True only discovers @worker_task functions that have - # already been imported. If workers are in a separate module, import it first. - with TaskHandler(configuration=config, scan_for_annotated_workers=True) as task_handler: - task_handler.start_processes() - - # Run the workflow and get the result. - run = executor.execute(name='greetings', version=1, workflow_input={'name': 'Conductor'}) - print(f'result: {run.output["result"]}') - print(f'execution: {config.ui_host}/execution/{run.workflow_id}') - - -if __name__ == '__main__': - main() -``` - -Run it: +The CLI is the preferred local-server path. Install the CLI, start the server, then point the SDK at its API endpoint. ```shell -python quickstart.py -``` - -> **About `OrkesClients`:** This is the standard client factory for Conductor. The `Orkes` prefix reflects the implementing organization — it works identically with self-hosted OSS Conductor. No Orkes account or paid service is required. - -> ### Using Orkes Conductor / Remote Server? -> Export your authentication credentials as well: -> -> ```shell -> export CONDUCTOR_SERVER_URL="https://your-cluster.orkesconductor.io/api" -> -> # If using Orkes Conductor that requires auth key/secret -> export CONDUCTOR_AUTH_KEY="your-key" -> export CONDUCTOR_AUTH_SECRET="your-secret" -> -> # Optional — set to false to force HTTP/1.1 if your network environment has unstable long-lived HTTP/2 connections (default: true) -> # export CONDUCTOR_HTTP2_ENABLED=false -> ``` -> See [Configuration](#configuration) for details. - -That's it — you just defined a worker, built a workflow, and executed it. Open the Conductor UI (default: -[http://localhost:8080](http://localhost:8080)) to see the execution. - ---- - -## Feature Showcase - -### Workers: Sync and Async - -The SDK automatically selects the right runner based on your function signature — `TaskRunner` (thread pool) for sync functions, `AsyncTaskRunner` (event loop) for async. - -```python -from conductor.client.worker.worker_task import worker_task - -# Sync worker — for CPU-bound work (uses ThreadPoolExecutor) -@worker_task(task_definition_name='process_image', thread_count=4) -def process_image(image_url: str) -> dict: - import PIL.Image, io, requests - img = PIL.Image.open(io.BytesIO(requests.get(image_url).content)) - img.thumbnail((256, 256)) - return {'width': img.width, 'height': img.height} - - -# Async worker — for I/O-bound work (uses AsyncTaskRunner, no thread overhead) -@worker_task(task_definition_name='fetch_data', thread_count=50) -async def fetch_data(url: str) -> dict: - import httpx - async with httpx.AsyncClient() as client: - resp = await client.get(url) - return resp.json() -``` - -Start workers with `TaskHandler` — it auto-discovers `@worker_task` functions and spawns one subprocess per worker: - -```python -from conductor.client.automator.task_handler import TaskHandler -from conductor.client.configuration.configuration import Configuration - -config = Configuration() -with TaskHandler(configuration=config, scan_for_annotated_workers=True) as task_handler: - task_handler.start_processes() - task_handler.join_processes() # blocks forever (workers poll continuously) -``` - -See [examples/worker_example.py](examples/worker_example.py) and [examples/workers_e2e.py](examples/workers_e2e.py) for complete examples. - -### Workflows with HTTP Calls and Waits - -Chain custom workers with built-in system tasks — HTTP calls, waits, JavaScript, JQ transforms — all in one workflow: - -```python -from conductor.client.workflow.conductor_workflow import ConductorWorkflow -from conductor.client.workflow.task.http_task import HttpTask -from conductor.client.workflow.task.wait_task import WaitTask - -workflow = ConductorWorkflow(name='order_pipeline', version=1, executor=executor) - -# Custom worker task -validate = validate_order(task_ref_name='validate', order_id=workflow.input('order_id')) - -# Built-in HTTP task — call any API, no worker needed -charge_payment = HttpTask(task_ref_name='charge_payment', http_input={ - 'uri': 'https://api.stripe.com/v1/charges', - 'method': 'POST', - 'headers': {'Authorization': ['Bearer ${workflow.input.stripe_key}']}, - 'body': {'amount': '${validate.output.amount}'} -}) - -# Built-in Wait task — pause the workflow for 10 seconds -cool_down = WaitTask(task_ref_name='cool_down', wait_for_seconds=10) - -# Another custom worker task -notify = send_notification(task_ref_name='notify', message='Order complete') - -# Chain with >> operator -workflow >> validate >> charge_payment >> cool_down >> notify - -# Execute synchronously and wait for the result -result = workflow.execute(workflow_input={'order_id': 'ORD-123', 'stripe_key': 'sk_test_...'}) -print(result.output) -``` - -See [examples/kitchensink.py](examples/kitchensink.py) for all task types (HTTP, JavaScript, JQ, Switch, Terminate) and [examples/workflow_ops.py](examples/workflow_ops.py) for lifecycle operations. - -### Long-Running Tasks with TaskContext - -For tasks that take minutes or hours (batch processing, ML training, external approvals), use `TaskContext` to report progress and poll incrementally: - -```python -from typing import Union -from conductor.client.worker.worker_task import worker_task -from conductor.client.context.task_context import get_task_context, TaskInProgress - -@worker_task(task_definition_name='batch_job') -def batch_job(batch_id: str) -> Union[dict, TaskInProgress]: - ctx = get_task_context() - ctx.add_log(f"Processing batch {batch_id}, poll #{ctx.get_poll_count()}") - - if ctx.get_poll_count() < 3: - # Not done yet — re-queue and check again in 30 seconds - return TaskInProgress(callback_after_seconds=30, output={'progress': ctx.get_poll_count() * 33}) - - # Done after 3 polls - return {'status': 'completed', 'batch_id': batch_id} -``` - -`TaskContext` also provides access to task metadata, retry counts, workflow IDs, and the ability to add logs visible in the Conductor UI. - -See [examples/task_context_example.py](examples/task_context_example.py) for all patterns (polling, retry-aware logic, async context, input access). - -### Lease Extension for Long-Running Tasks - -For tasks that run longer than `responseTimeoutSeconds` (e.g., LLM inference, data pipelines, batch jobs), enable automatic lease extension. The SDK sends heartbeats at 80% of `responseTimeoutSeconds`, resetting the server's timeout timer so the task stays alive: - -```python -from conductor.client.worker.worker_task import worker_task - -@worker_task( - task_definition_name='train_model', - lease_extend_enabled=True, # Automatic heartbeat — keeps task alive -) -def train_model(dataset_id: str) -> dict: - """Runs for 10 minutes, but responseTimeoutSeconds is only 60s. - Heartbeats at 48s intervals keep the lease alive.""" - model = train(dataset_id) - return {'model_id': model.id, 'accuracy': model.accuracy} +npm install -g @conductor-oss/conductor-cli +conductor server start +conductor server status +export CONDUCTOR_SERVER_URL=http://localhost:8080/api ``` -Disabled by default. Enable per-worker via decorator, constructor, or environment variable (`conductor_worker__lease_extend_enabled=true`). See [LEASE_EXTENSION.md](docs/LEASE_EXTENSION.md) for the full guide. - -### Monitoring with Metrics - -Enable Prometheus metrics with a single setting — the SDK exposes poll counts, execution times, error rates, and HTTP latency: - -```python -from conductor.client.automator.task_handler import TaskHandler -from conductor.client.configuration.configuration import Configuration -from conductor.client.configuration.settings.metrics_settings import MetricsSettings +### Docker fallback -config = Configuration() -metrics = MetricsSettings(directory='/tmp/conductor-metrics', http_port=8000) - -with TaskHandler(configuration=config, metrics_settings=metrics, scan_for_annotated_workers=True) as task_handler: - task_handler.start_processes() - task_handler.join_processes() -``` +Use Docker when you need a containerized local server instead of the CLI: ```shell -# Prometheus-compatible endpoint -curl http://localhost:8000/metrics +docker run --rm -p 8080:8080 -p 1234:5000 conductoross/conductor:latest +export CONDUCTOR_SERVER_URL=http://localhost:8080/api ``` -Legacy metrics are emitted by default. Set `WORKER_CANONICAL_METRICS=true` before starting workers to use the canonical metric catalog. See [examples/metrics_example.py](examples/metrics_example.py) and [METRICS.md](METRICS.md) for the full legacy and canonical reference. - -### Managing Workflow Executions - -Full lifecycle control — start, execute, pause, resume, terminate, retry, restart, rerun, signal, and search: - -```python -from conductor.client.configuration.configuration import Configuration -from conductor.client.http.models import StartWorkflowRequest, RerunWorkflowRequest, TaskResult -from conductor.client.orkes_clients import ConductorClients - -config = Configuration() -clients = ConductorClients(configuration=config) -workflow_client = clients.get_workflow_client() -task_client = clients.get_task_client() -executor = clients.get_workflow_executor() - -# Start async (returns workflow ID immediately) -workflow_id = executor.start_workflow(StartWorkflowRequest(name='my_workflow', input={'key': 'value'})) - -# Execute sync (blocks until workflow completes) -result = executor.execute(name='my_workflow', version=1, workflow_input={'key': 'value'}) - -# Lifecycle management -workflow_client.pause_workflow(workflow_id) -workflow_client.resume_workflow(workflow_id) -workflow_client.terminate_workflow(workflow_id, reason='no longer needed') -workflow_client.retry_workflow(workflow_id) # retry from last failed task -workflow_client.restart_workflow(workflow_id) # restart from the beginning -workflow_client.rerun_workflow(workflow_id, # rerun from a specific task - RerunWorkflowRequest(re_run_from_task_id=task_id)) +The Docker server UI is available at [http://localhost:1234](http://localhost:1234). See [server setup](docs/server-setup.md) for full local, remote, and authentication guidance. -# Send a signal to a waiting workflow (complete a WAIT task externally) -task_client.update_task(TaskResult( - workflow_instance_id=workflow_id, - task_id=wait_task_id, - status='COMPLETED', - output_data={'approved': True} -)) - -# Search workflows -results = workflow_client.search(query='status IN (RUNNING) AND correlationId = "order-123"') -``` - -See [examples/workflow_ops.py](examples/workflow_ops.py) for a complete walkthrough of every operation. - ---- - -## AI & LLM Workflows +## Why Conductor? -Conductor supports AI-native workflows including agentic tool calling, RAG pipelines, and multi-agent orchestration. +- **Survive process failures:** execution state is durable, so Conductor agents and workflows resume from completed work. +- **Build dynamic agent graphs:** define workflow graphs in Python or let an LLM plan them at runtime. Conductor executes plans as durable sub-workflows rather than transient in-process loops. +- **Run tools as distributed tasks:** scale Python workers independently while Conductor manages retries and delivery. +- **Orchestrate long-running work:** combine AI, schedules, events, and human approval without holding application threads open. +- **See every execution:** inspect inputs, outputs, tool calls, retries, and status through one execution model. -**Agentic Workflows** +See the maintained [planner-context example](examples/agents/115_plan_execute_planner_context.py) for a durable plan-and-execute graph, or start with the [agent examples](examples/agents/README.md). -Build AI agents where LLMs dynamically select and call Python workers as tools. See [examples/agentic_workflows/](examples/agentic_workflows/) for all examples. +## Requirements and compatibility -| Example | Description | -|---------|-------------| -| [llm_chat.py](examples/agentic_workflows/llm_chat.py) | Automated multi-turn science Q&A between two LLMs | -| [llm_chat_human_in_loop.py](examples/agentic_workflows/llm_chat_human_in_loop.py) | Interactive chat with WAIT task pauses for user input | -| [multiagent_chat.py](examples/agentic_workflows/multiagent_chat.py) | Multi-agent debate with moderator routing between panelists | -| [function_calling_example.py](examples/agentic_workflows/function_calling_example.py) | LLM picks which Python function to call based on user queries | -| [mcp_weather_agent.py](examples/agentic_workflows/mcp_weather_agent.py) | AI agent using MCP tools for weather queries | +- Python 3.10+ +- A running OSS or Orkes Conductor server selected in [Choose your Conductor server](#choose-your-conductor-server) +- Docker when using the Docker local-server option +- Node.js/npm only when using the optional Conductor CLI -**LLM and RAG Workflows** +The CI workflows are the source of truth for the server versions exercised by this SDK. See the [agent E2E matrix](.github/workflows/agent-e2e.yml) for its pinned server version. -| Example | Description | -|---------|-------------| -| [rag_workflow.py](examples/rag_workflow.py) | End-to-end RAG: document conversion (PDF/Word/Excel), pgvector indexing, semantic search, answer generation | -| [vector_db_helloworld.py](examples/orkes/vector_db_helloworld.py) | Vector database operations: text indexing, embedding generation, and semantic search with Pinecone | +## Install the SDK ```shell -# Automated multi-turn chat -python examples/agentic_workflows/llm_chat.py - -# Multi-agent debate -python examples/agentic_workflows/multiagent_chat.py --topic "renewable energy" - -# RAG pipeline -pip install "markitdown[pdf]" -python examples/rag_workflow.py document.pdf "What are the key findings?" +python3 -m venv conductor-env +source conductor-env/bin/activate # Windows: conductor-env\Scripts\activate +pip install conductor-python ``` ---- - -## AI Agents +### AI agents -Beyond the workflow-embedded LLM calls above, `conductor-python` also ships a durable -agent-authoring layer — `Agent`, `AgentRuntime`, `tool`, guardrails, handoffs, and multi-agent -strategies — where the agent itself compiles into a Conductor workflow that runs on the server, -with retries, durable state, streaming, and human-in-the-loop pauses built in. +Install the complete Conductor agent surface, including supported framework bridges: ```shell pip install 'conductor-python[agents]' ``` -```python -from conductor.ai.agents import Agent, AgentRuntime +### Workflows and workers -agent = Agent(name="greeter", model="anthropic/claude-sonnet-4-6", - instructions="You are a friendly assistant.") +The base package includes the workflow, task, worker, metadata, scheduler, and metrics clients: -with AgentRuntime() as runtime: - result = runtime.run(agent, "Say hello.") - print(result.output) +```shell +pip install conductor-python ``` -Framework integrations (LangChain, LangGraph, Google ADK, the OpenAI Agents SDK, Claude Agent SDK) -are optional extras — see [docs/agents/getting-started.md](docs/agents/getting-started.md) for -per-framework install instructions, and [examples/agents/](examples/agents/) for 270+ runnable -examples. Full docs: [docs/agents/](docs/agents/). +### Modules ---- - -## Why Conductor? - -| | | +| Package or extra | Use it for | |---|---| -| **Language agnostic** | Workers in Python, Java, Go, JS, C# — all in one workflow | -| **Durable execution** | Survives crashes, retries automatically, never loses state | -| **Built-in HTTP/Wait/JS tasks** | No code needed for common operations | -| **Horizontal scaling** | Built at Netflix for millions of workflows | -| **Full visibility** | UI shows every execution, every task, every retry | -| **Sync + Async execution** | Start-and-forget OR wait-for-result | -| **Human-in-the-loop** | WAIT tasks pause until an external signal | -| **AI-native** | LLM chat, RAG pipelines, function calling, MCP tools built-in | - ---- - -## Examples - -See the [Examples Guide](examples/README.md) for the full catalog. Key examples: - -| Example | Description | Run | -|---------|-------------|-----| -| [workers_e2e.py](examples/workers_e2e.py) | End-to-end: sync + async workers, metrics | `python examples/workers_e2e.py` | -| [kitchensink.py](examples/kitchensink.py) | All task types (HTTP, JS, JQ, Switch) | `python examples/kitchensink.py` | -| [workflow_ops.py](examples/workflow_ops.py) | Pause, resume, terminate, retry, restart, rerun, signal | `python examples/workflow_ops.py` | -| [task_context_example.py](examples/task_context_example.py) | Long-running tasks with TaskInProgress | `python examples/task_context_example.py` | -| [lease_extension_example.py](examples/lease_extension_example.py) | Automatic heartbeat for long-running tasks | `python examples/lease_extension_example.py` | -| [metrics_example.py](examples/metrics_example.py) | Prometheus metrics collection | `python examples/metrics_example.py` | -| [fastapi_worker_service.py](examples/fastapi_worker_service.py) | FastAPI: expose a workflow as an API (+ workers) | `uvicorn examples.fastapi_worker_service:app --port 8081 --workers 1` | -| [helloworld.py](examples/helloworld/helloworld.py) | Minimal hello world | `python examples/helloworld/helloworld.py` | -| [dynamic_workflow.py](examples/dynamic_workflow.py) | Build workflows programmatically | `python examples/dynamic_workflow.py` | -| [test_workflows.py](examples/test_workflows.py) | Unit testing workflows | `python -m unittest examples.test_workflows` | - -**API Journey Examples** - -End-to-end examples covering all APIs for each domain: - -| Example | APIs | Run | -|---------|------|-----| -| [authorization_journey.py](examples/authorization_journey.py) | Authorization APIs | `python examples/authorization_journey.py` | -| [metadata_journey.py](examples/metadata_journey.py) | Metadata APIs | `python examples/metadata_journey.py` | -| [schedule_journey.py](examples/schedule_journey.py) | Schedule APIs | `python examples/schedule_journey.py` | -| [prompt_journey.py](examples/prompt_journey.py) | Prompt APIs | `python examples/prompt_journey.py` | - -## Documentation - -| Document | Description | -|----------|-------------| -| [Worker Design](docs/design/WORKER_DESIGN.md) | Architecture: AsyncTaskRunner vs TaskRunner, discovery, lifecycle | -| [Worker Guide](docs/WORKER.md) | All worker patterns (function, class, annotation, async) | -| [Worker Configuration](WORKER_CONFIGURATION.md) | Hierarchical environment variable configuration | -| [Lease Extension](docs/LEASE_EXTENSION.md) | Automatic heartbeat for long-running tasks | -| [Workflow Management](docs/WORKFLOW.md) | Start, pause, resume, terminate, retry, search | -| [Workflow Testing](docs/WORKFLOW_TESTING.md) | Unit testing with mock outputs | -| [Task Management](docs/TASK_MANAGEMENT.md) | Task operations | -| [Metadata](docs/METADATA.md) | Task & workflow definitions | -| [Authorization](docs/AUTHORIZATION.md) | Users, groups, applications, permissions | -| [Schedules](docs/SCHEDULE.md) | Workflow scheduling | -| [Secrets](docs/SECRET_MANAGEMENT.md) | Secret storage | -| [Prompts](docs/PROMPT.md) | AI/LLM prompt templates | -| [Integrations](docs/INTEGRATION.md) | AI/LLM provider integrations | -| [AI Agents](docs/agents/README.md) | Durable agent authoring: `Agent`, `AgentRuntime`, tools, guardrails, handoffs | -| [Metrics](METRICS.md) | Prometheus metrics collection | -| [Examples](examples/README.md) | Complete examples catalog | - -## Frequently Asked Questions - -**Is this the same as Netflix Conductor?** - -Yes. Conductor OSS is the continuation of the original [Netflix Conductor](https://github.com/Netflix/conductor) repository after Netflix contributed the project to the open-source foundation. - -**Is this project actively maintained?** - -Yes. [Orkes](https://orkes.io) is the primary maintainer and offers an enterprise SaaS platform for Conductor across all major cloud providers. - -**Can Conductor scale to handle my workload?** - -Conductor was built at Netflix to handle massive scale and has been battle-tested in production environments processing millions of workflows. It scales horizontally to meet virtually any demand. +| `conductor-python` | Workflow, task, worker, metadata, scheduler, and metrics clients | +| `conductor-python[agents]` | Durable Conductor agents, tools, guardrails, handoffs, and all supported framework bridges | +| `conductor-python[adk]` | Google ADK bridge | +| `conductor-python[langchain]` | LangChain bridge | +| `conductor-python[langgraph]` | LangGraph bridge | +| `conductor-python[openai-agents]` | OpenAI Agents bridge | +| `conductor-python[claude]` | Claude Agent SDK bridge | -**Does Conductor support durable code execution?** +## AI agent quickstart -Yes. Conductor ensures workflows complete reliably even in the face of infrastructure failures, process crashes, or network issues. +Use this path when your Conductor agent needs LLM reasoning, tools, guardrails, handoffs, or human approval. Select a server above first. For a local server, configure the LLM provider credential in the server environment before starting it. For Developer Edition, configure the provider integration in the hosted cluster. The [agent getting-started guide](docs/agents/getting-started.md) covers both paths. -**Are workflows always asynchronous?** - -No. While Conductor excels at asynchronous orchestration, it also supports synchronous workflow execution when immediate results are required. - -**Why did `execute()` return `status: RUNNING` with no output?** - -`execute()` blocks until the workflow finishes **or** `wait_for_seconds` elapses (default: 10 s), -whichever comes first. If it times out, you get `status='RUNNING'` — that is correct behavior, -not a bug. - -The most common cause: your worker raised an exception. Conductor marks the task FAILED and -schedules a retry after `retryDelaySeconds` (default: **60 s**). The default 10 s wait expires -while the retry is pending, so `execute()` returns before the workflow completes. - -**To fix**: increase `wait_for_seconds` to outlast the retry cycle: - -```python -# default retryDelaySeconds is 60 — wait long enough to cover one retry -run = executor.execute(name='my_workflow', version=1, workflow_input={...}, wait_for_seconds=70) -``` - -**To debug** when a workflow is stuck: - -```python -# Inspect task statuses and failure reasons -wf = executor.get_workflow(run.workflow_id, include_tasks=True) -for task in wf.tasks: - if task.status in ('FAILED', 'FAILED_WITH_TERMINAL_ERROR'): - print(task.reference_task_name, task.reason_for_incompletion) +```shell +export CONDUCTOR_AGENT_LLM_MODEL=openai/gpt-4o-mini +cd examples/agents +python 01_basic_agent.py ``` -You can also open the Conductor UI at `/execution/` — it shows each task's -status, retry count, and the worker exception message directly. Worker tracebacks are also logged -at ERROR level by the SDK in the `TaskHandler` process. - -**Do I need to use a Conductor-specific framework?** - -No. Conductor is language and framework agnostic. Use your preferred language and framework — the [SDKs](https://github.com/conductor-oss/conductor#conductor-sdks) provide native integration for Python, Java, JavaScript, Go, C#, and more. - -**Can I mix workers written in different languages?** +Expected outcome: the example prints an `AgentResult` containing the model response. Continue with the [AI agent guide](docs/agents/README.md), [tools guide](docs/agents/concepts/tools.md), and [agent examples](examples/agents/README.md). -Yes. A single workflow can have workers written in Python, Java, Go, or any other supported language. Workers communicate through the Conductor server, not directly with each other. +### Framework bridges -**What Python versions are supported?** +Keep using the Python agent framework your team already knows. The SDK bridges native [Google ADK](docs/agents/frameworks/google-adk.md), [LangChain](docs/agents/frameworks/langchain.md), [LangGraph](docs/agents/frameworks/langgraph.md), [OpenAI Agents](docs/agents/frameworks/openai.md), and [Claude Agent SDK](docs/agents/frameworks/claude-agent-sdk.md) agents into durable Conductor agents. -Python 3.9 and above. +## Workflow and worker quickstart -**Should I use `def` or `async def` for my workers?** +With a server selected above, this maintained example registers a workflow, starts a Python worker, executes the workflow, and prints its result. -Use `async def` for I/O-bound tasks (API calls, database queries) — the SDK uses `AsyncTaskRunner` with a single event loop for high concurrency with low overhead. Use regular `def` for CPU-bound or blocking work — the SDK uses `TaskRunner` with a thread pool. The SDK selects the right runner automatically based on your function signature. +```shell +cd examples/helloworld +python helloworld.py +``` -**How do I run workers in production?** +Expected outcome: the workflow finishes with `COMPLETED` and prints its greeting output. For worker patterns, workflow definitions, and testing, continue with the [core examples catalog](docs/examples.md), [worker guide](docs/workers.md), and [workflow guide](docs/workflows.md). -Workers are standard Python processes. Deploy them as you would any Python application — in containers, VMs, or bare metal. Workers poll the Conductor server for tasks, so no inbound ports need to be opened. See [Worker Design](docs/design/WORKER_DESIGN.md) for architecture details. +## Common tasks -**How do I test workflows without running a full Conductor server?** +| Need | Start with | +|---|---| +| Build Python Conductor agents | [Agent concepts](docs/agents/concepts/agents.md) | +| Add tools and human approval | [Agent tools](docs/agents/concepts/tools.md) | +| Use another agent framework | [Google ADK](docs/agents/frameworks/google-adk.md) · [LangChain](docs/agents/frameworks/langchain.md) · [LangGraph](docs/agents/frameworks/langgraph.md) · [OpenAI Agents](docs/agents/frameworks/openai.md) | +| Deploy, serve, and run Conductor agents | [Agent runtime modes](docs/agents/concepts/deploy-serve-run.md) | +| Implement and scale Python workers | [Workers guide](docs/workers.md) · [reliability](docs/reliability.md) | +| Define and evolve workflows | [Workflows guide](docs/workflows.md) · [lifecycle/versioning](docs/workflow-lifecycle.md) | +| Upload/download workflow-scoped files | [Python compatibility](docs/compatibility.md#workflow-scoped-files) | +| Test workflows and workers | [Workflow testing](docs/workflow-testing.md) | +| Expose worker metrics | [Observability](docs/observability.md) | +| Host Python workers in an application | [FastAPI worker example](examples/fastapi_worker_service.py) · [deployment/scaling](docs/deployment-scaling.md) | +| Manage schedules and events | [Schedules/events guide](docs/schedules-events.md) | +| Find typed clients and API references | [Core API map](docs/api-map.md) | + +## Troubleshooting + +| Symptom | Check | +|---|---| +| Connection refused | The server is healthy at `http://localhost:8080/health`; `CONDUCTOR_SERVER_URL` ends in `/api`. | +| Task remains `SCHEDULED` | A worker is polling the exact task type and has enough worker capacity. | +| Authentication failure | `CONDUCTOR_AUTH_KEY` and `CONDUCTOR_AUTH_SECRET` are set for the target server. | +| Conductor agent cannot call a model | The server—not only the Python process—has a configured LLM provider and model. | -The SDK provides a test framework that uses Conductor's `POST /api/workflow/test` endpoint to evaluate workflows with mock task outputs. See [Workflow Testing](docs/WORKFLOW_TESTING.md) for details. +## Support and project policies -## Support +**Contribute upstream:** [Choose a Conductor OSS contribution](https://github.com/conductor-oss/conductor/contribute) · [Read the Conductor OSS contribution guide](https://github.com/conductor-oss/conductor/blob/main/CONTRIBUTING.md) -- [Open an issue (SDK)](https://github.com/conductor-sdk/conductor-python/issues) for SDK bugs, questions, and feature requests -- [Open an issue (Conductor server)](https://github.com/conductor-oss/conductor/issues) for Conductor OSS server issues -- [Join the Conductor Slack](https://join.slack.com/t/orkes-conductor/shared_invite/zt-2vdbx239s-Eacdyqya9giNLHfrCavfaA) for community discussion and help -- [Orkes Community Forum](https://community.orkes.io/) for Q&A +- [SDK issues](https://github.com/conductor-oss/python-sdk/issues) for Python SDK bugs and feature requests +- [Conductor server issues](https://github.com/conductor-oss/conductor/issues) for OSS server behavior +- [Conductor Code of Conduct](https://github.com/conductor-oss/conductor/blob/main/CODE_OF_CONDUCT.md) for community expectations and conduct reporting +- [Conductor security policy](https://github.com/conductor-oss/conductor/security/policy) for private vulnerability reporting +- [Conductor Slack](https://join.slack.com/t/orkes-conductor/shared_invite/zt-2vdbx239s-Eacdyqya9giNLHfrCavfaA) and the [Orkes Community Forum](https://community.orkes.io/) for questions ## License -Apache 2.0 +Apache 2.0. See [LICENSE](LICENSE). diff --git a/SDK_UPDATE_v1.md b/SDK_UPDATE_v1.md index 99f8afd4f..8b40865c5 100644 --- a/SDK_UPDATE_v1.md +++ b/SDK_UPDATE_v1.md @@ -192,7 +192,7 @@ The exact semantics of the runtime verbs. Implementations must match this table: ### `run` — start, wait, return the result `run(agent, prompt, *, version?, media?, session_id?, idempotency_key?, on_event?, timeout?, credentials?, context?, run_settings?) -> AgentResult` -Serializes the agent (+ `run_settings` overrides), calls `start_agent`, starts the required tool workers, then polls status to a terminal state. Returns the rich result — the output is `result.output`, **not** a bare value. `credentials=[names]` asks the server to resolve those secrets for this run. +Serializes the agent (+ `run_settings` overrides), calls `start_agent`, starts the required tool workers, then polls status to a terminal state. Returns the rich result — the output is `result.output`, **not** a bare value. Credential names configure worker `TaskDef.runtimeMetadata` locally and are not sent as workflow input. ### `start` — fire-and-forget `start(agent, prompt, *, version?, media?, session_id?, idempotency_key?, context?, run_settings?) -> AgentHandle` @@ -212,7 +212,7 @@ Per agent: (1) **deploy it to the server** (same helper `deploy` uses — new on config_json = serialize(agent) if run_settings: config_json.update(run_settings.to_config_overrides()) payload = {agentConfig: config_json, prompt, sessionId: session_id ?? "", media: media ?? []} -+ optional keys only when set: context, idempotencyKey, timeoutSeconds, credentials, runId, static_plan ++ optional keys only when set: context, idempotencyKey, timeoutSeconds, runId, static_plan data = agent_client.start_agent(payload) execution_id, required_workers = data.executionId, data.requiredWorkers? prepare_workers(agent, required_workers) # WorkerManager start — serve the tools diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 000000000..ad6ef0888 --- /dev/null +++ b/docs/README.md @@ -0,0 +1,36 @@ +# Python SDK documentation + +Build durable workflow workers and Conductor agents with Python. These guides +cover OSS and Orkes; pages call out capabilities that require Orkes. + +## Start here + +| Goal | Guide | Expected result | +|---|---|---| +| Connect to a server | [Server setup](server-setup.md) and [connection/authentication](connection-authentication.md) | The SDK can reach an OSS or Orkes API endpoint. | +| Build a workflow and worker | [Core quickstart](core-quickstart.md) | The hello-world workflow prints its result. | +| Build a Conductor agent | [Agent quickstart](agents/getting-started.md) | An LLM-backed agent completes through Conductor. | + +## Build + +- [Workflows](workflows.md), [workflow lifecycle](workflow-lifecycle.md), and [workers](workers.md) +- [Workflow testing](workflow-testing.md), [schemas](schema-client.md), and [schedules/events](schedules-events.md) +- [Conductor agents](agents/README.md), [tools](agents/concepts/tools.md), and [framework bridges](agents/README.md#framework-bridges) +- [Recommended examples](examples.md); [examples/README.md](../examples/README.md) is the full catalog. + +## Operate + +- [Reliability](reliability.md), [security](security.md), and [deployment/scaling](deployment-scaling.md) +- [Metrics and logging](observability.md) and [debugging](debugging.md) + +## Reference and upgrades + +- [Core API map](api-map.md), [compatibility](compatibility.md), and [upgrading](upgrading.md) +- [Agent runtime](agents/reference/runtime.md), [control plane](agents/reference/client.md), and [agent definition](agents/reference/agent-definition.md) +- [Java/Python documentation parity](documentation-parity.md) — intentional Python mappings and unsupported Java-only surfaces. + +## Documentation conventions + +Primary guides follow the [documentation standard](documentation-standard.md). +Provider credentials belong on the Conductor server or its secret provider, not +in workflow input, example source, or a client-side `.env` committed to Git. diff --git a/docs/agents/README.md b/docs/agents/README.md index 0d7982897..37a7b67a2 100644 --- a/docs/agents/README.md +++ b/docs/agents/README.md @@ -1,39 +1,46 @@ # Conductor Agent Python SDK -> Ships as part of [`conductor-python`](https://pypi.org/project/conductor-python/) — install with the `agents` extra -> (`pip install 'conductor-python[agents]'`) — you're in the right place. +Build durable Python AI agents on Conductor. Agents can use local Python tools, +wait for people, execute dynamic plans, and recover after process restarts because +Conductor persists execution state. -Long-running, dynamic plan-execute, and event-driven AI agents in Python. You write plain Python; Conductor Agent compiles your agent into a Conductor workflow that runs on a server — with automatic retries, durable state, human-in-the-loop pauses, streaming, scheduling, dynamic plan-execute, and full execution history. +## Install -```python -from conductor.ai.agents import Agent, AgentRuntime +```shell +pip install 'conductor-python[agents]' +``` -agent = Agent(name="greeter", model="anthropic/claude-sonnet-4-6", - instructions="You are a friendly assistant.") +Requirements: Python 3.10+ and a Conductor server with an LLM provider configured +server-side. Replace example model names with a model enabled on that server. -with AgentRuntime() as runtime: - result = runtime.run(agent, "Say hello.") - print(result.output) -``` +## Start here -## Docs +- [Getting started](getting-started.md) — configure a server and run a basic agent. +- [Deploy · Serve · Run · Plan](concepts/deploy-serve-run.md) — choose a runtime mode. +- [Scheduling](concepts/scheduling.md) — manage deployed-agent schedules. -- [Getting started](getting-started.md) — install, env vars, and a running agent in under 30 seconds. -- [Writing agents](writing-agents.md) — the `Agent` class and `@agent`, tools, multi-agent strategies, handoffs, guardrails, termination, callbacks, streaming + HITL, schedules, stateful and instance agents. -- [Framework agents](framework-agents.md) — run agents authored in the OpenAI Agents SDK, LangChain, LangGraph, or the Claude Agent SDK. -- [Advanced](advanced.md) — runtime config, the control-plane `AgentClient`, deploy vs serve vs run vs plan, structured output, credentials, plans (`PLAN_EXECUTE`), skills. -- [API reference](api-reference.md) — the public API surface in one place. +## Build agents -## Import surface +- [Agents](concepts/agents.md), [tools](concepts/tools.md), and [multi-agent](concepts/multi-agent.md) +- [Guardrails](concepts/guardrails.md), [termination](concepts/termination.md), [callbacks](concepts/callbacks.md) +- [Stateful agents](concepts/stateful.md), [streaming and HITL](concepts/streaming-hitl.md), and [structured output](concepts/structured-output.md) -Everything public is importable from `conductor.ai.agents`: +## Framework bridges -```python -from conductor.ai.agents import Agent, AgentRuntime, tool, agent -``` +- [Google ADK](frameworks/google-adk.md), [LangChain](frameworks/langchain.md), and [LangGraph](frameworks/langgraph.md) +- [OpenAI Agents SDK](frameworks/openai.md) and [Claude Agent SDK](frameworks/claude-agent-sdk.md) -A small OpenAI-Agents-compatible shim is also exposed at the top level: +## Operate and inspect -```python -from conductor.ai import Runner, function_tool # drop-in for `agents.Runner` -``` +- [Runtime reference](reference/runtime.md), [control-plane reference](reference/client.md), and [API map](reference/api.md) +- [Agent-definition fields](reference/agent-definition.md) and [configuration contract](reference/agent-schema.md) + +## What Conductor adds + +| Capability | Conductor agent runtime | +|---|---| +| Process recovery | Durable workflow state resumes from completed work. | +| Python tools | Tools run as independently scalable Conductor worker tasks. | +| Long-running work | Human approval, schedules, and events do not occupy application threads. | +| Dynamic execution | Plans become durable, inspectable sub-workflows. | +| Observability | Inputs, outputs, tool calls, retries, and status share one execution record. | diff --git a/docs/agents/advanced.md b/docs/agents/advanced.md index 807c131b1..c8e43aec9 100644 --- a/docs/agents/advanced.md +++ b/docs/agents/advanced.md @@ -1,326 +1,8 @@ -# Advanced +# Advanced Conductor-agent usage -- [Runtime init and config](#runtime-init-and-config) -- [run vs start vs stream vs deploy vs serve vs plan](#run-vs-start-vs-stream-vs-deploy-vs-serve-vs-plan) -- [The control-plane AgentClient](#the-control-plane-agentclient) -- [Structured output](#structured-output) -- [Credentials and secrets](#credentials-and-secrets) -- [Plans and PLAN_EXECUTE](#plans-and-plan_execute) -- [Schedules](#schedules) -- [Skills](#skills) +This compatibility page has moved to: -## Runtime init and config - -`AgentRuntime` is the entry point. Use it as a context manager so workers shut down -cleanly. Server connection comes from the standard Conductor `Configuration` — -the same object every other client uses — which resolves `CONDUCTOR_SERVER_URL` -(falling back to `AGENTSPAN_SERVER_URL`) and `CONDUCTOR_AUTH_KEY`/`CONDUCTOR_AUTH_SECRET` -from the environment when not passed explicitly. - -```python -from conductor.ai.agents import AgentRuntime, AgentConfig -from conductor.client.configuration.configuration import Configuration - -# From env (CONDUCTOR_SERVER_URL → AGENTSPAN_SERVER_URL, CONDUCTOR_AUTH_*) -with AgentRuntime() as runtime: - runtime.run(agent, "hi") - -# Explicit Configuration -with AgentRuntime(Configuration(server_api_url="https://prod:8080/api")) as runtime: - ... - -# Runtime behaviour knobs (workers, streaming, log level) via AgentConfig -settings = AgentConfig.from_env() -settings.log_level = "DEBUG" -with AgentRuntime(settings=settings) as runtime: - ... -``` - -`AgentConfig` is a dataclass; `from_env()` reads the `AGENTSPAN_*` environment -variables (full list in [Getting started](getting-started.md#environment-variables)). -It carries runtime *behaviour* settings only — server connection always comes from -the `Configuration`. - -### Module-level convenience functions - -For one-off scripts, top-level functions use a shared singleton runtime: - -```python -import conductor.ai.agents as ag -from conductor.client.configuration.configuration import Configuration - -# Connection/auth via a Configuration; agent-behaviour knobs via config=AgentConfig(...). -ag.configure(configuration=Configuration(server_api_url="https://prod:8080/api")) # before first run -result = ag.run(agent, "Hello!") -ag.shutdown() # explicit cleanup; not required for simple scripts -``` - -`configure(...)` must be called before the first `run`/`start`/`stream`. Available: -`run`, `run_async`, `start`, `start_async`, `stream`, `stream_async`, `resume`, -`resume_async`, `deploy`, `deploy_async`, `serve`, `plan`, `configure`, `shutdown`. - -## run vs start vs stream vs deploy vs serve vs plan - -| Call | Blocks? | Returns | When | -|---|---|---|---| -| `runtime.run(agent, prompt)` | yes | `AgentResult` | Simplest case — run and get the answer | -| `runtime.start(agent, prompt)` | no | `AgentHandle` | Fire-and-forget; poll/control later | -| `runtime.stream(agent, prompt)` | iterates | `AgentStream` | Watch events live; drive HITL | -| `runtime.deploy(*agents)` | yes | `list[DeploymentInfo]` | CI/CD: compile + register, no workers, no execution | -| `runtime.serve(*agents)` | yes (blocks) | — | Register agent(s) **and** serve workers; long-lived, polls until interrupted (`serve` = `deploy` + serve) | -| `runtime.plan(agent)` | yes | `dict` | Compile to a workflow def without running anything | - -`run`/`start`/`stream` accept `media=`, `session_id=`, `idempotency_key=`, -`credentials=`, and extra `**kwargs` as workflow input. `run`/`run_async` also accept -`on_event=` to stream while running synchronously, `timeout=`, and `context=`. -`run`/`start` (and their async variants) also accept `run_settings=` — a `RunSettings` -(or dict) that overrides the agent's `model`/`temperature`/`max_tokens`/… for that one -call. See [API reference](api-reference.md#per-run-overrides--runsettings). - -`plan(agent)` returns `{"workflowDef": ..., "requiredWorkers": ...}` — useful to -inspect the compiled Conductor workflow: - -```python -result = runtime.plan(agent) -print(result["workflowDef"]["name"]) -print(result["workflowDef"]["tasks"]) -``` - -### Deploy once, serve separately (production) - -```python -# CI/CD step: -runtime.deploy(agent) -# CLI alternative: -# agentspan deploy --package my_pkg.my_module -# agentspan deploy --path ./agents --agents greeter,support - -# Long-lived worker process: -runtime.serve(agent) # registers the agent (idempotent) + blocks, polling for tool tasks -``` - -`serve` also registers the agent on the server, so the explicit `deploy` step above -is optional — it's still useful to register/upgrade the workflow from CI independently -of the worker rollout. - -`resume(execution_id, agent)` re-attaches to a previously `start`ed execution and -re-registers its tool workers (e.g. after a process restart): - -```python -handle = runtime.start(agent, "Long job") -eid = handle.execution_id -# later, even after a restart: -handle = runtime.resume(eid, agent) -result = handle.join(timeout=120) -``` - -## The control-plane AgentClient - -`runtime.client` is the **control-plane** `AgentClient` (formerly `AgentHttpClient` — -the old name is kept as an alias). It talks to the `/agent/*` HTTP endpoints directly: -compile, deploy, start, run, schedule, status, respond, stop, signal, SSE. It is -control-plane only — its `run`/`start` do **not** register or poll local `@tool` -workers, so use it for agents whose tools are all server-side (HTTP/MCP/built-in) or -already deployed. - -```python -with AgentRuntime() as runtime: - client = runtime.client - - result = client.run(agent, "Hello") # compile + start + poll - handle = client.start(agent, "Long job") - infos = client.deploy(agent) # compile + register - - # Cron lifecycle (same surface as runtime.schedules_client()): - client.schedule(agent, [nightly]) # reconcile schedules - client.schedules.pause("agent-nightly") -``` - -Key methods: `run`/`run_async`, `start`/`start_async`, `deploy`/`deploy_async`, -`schedule(agent, schedules)`, `get_status`, `respond`, `stop`, `signal`, -`stream_sse`, and `.schedules` (the schedule lifecycle — `pause`/`resume`/`delete`/ -`run_now`/`preview_next`/`reconcile`, now carried by `SchedulerClient` itself). Both -sync and async forms exist. Most users call `runtime.run/start/deploy` instead, -which add local-worker management on top of this client. - -The raw `/agent/*` HTTP transport behind this client is -`conductor.client.ai.AgentApiClient` — also reachable without the agents layer via -`OrkesClients.get_agent_client()` (and `get_scheduler_client()` for the cron -lifecycle). `AgentClient` composes that transport and adds the agent-level surface. - -## Structured output - -Pass `output_type=` a Pydantic model (or dataclass) to get a typed, validated result. -Pydantic is only needed when you use this feature. - -```python -from pydantic import BaseModel -from conductor.ai.agents import Agent, AgentRuntime, tool - -class WeatherReport(BaseModel): - city: str - temperature: float - condition: str - recommendation: str - -@tool -def get_weather(city: str) -> dict: - """Get weather data.""" - return {"city": city, "temp_f": 72, "condition": "Sunny"} - -agent = Agent(name="reporter", model="openai/gpt-4o", - tools=[get_weather], output_type=WeatherReport, - instructions="Report the weather with a recommendation.") - -with AgentRuntime() as runtime: - result = runtime.run(agent, "What's the weather in NYC?") - print(result.output) # conforms to WeatherReport's schema -``` - -## Credentials and secrets - -Store secrets in the server's credential store (never in code), then declare them per -tool with `credentials=[...]`. Inside the tool, read the injected value with -`get_secret(name)`. - -```python -from conductor.ai.agents import tool, get_secret - -@tool(credentials=["OPENAI_API_KEY"]) -def call_openai(prompt: str) -> str: - """Call OpenAI directly using a stored credential.""" - key = get_secret("OPENAI_API_KEY") # only works inside a credentials-aware tool - ... -``` - -You can also declare credentials at the agent level (`Agent(..., credentials=[...])`), -and HTTP/built-in tools resolve `${CRED_NAME}` placeholders in headers from the same -store at execution time. Pass `credentials=[...]` to `runtime.run(...)` to supply -credential names for a specific execution. - -`get_secret` raises `CredentialNotFoundError` when the credential is absent. Other -credential errors: `CredentialAuthError`, `CredentialRateLimitError`, -`CredentialServiceError`. Store a credential via the CLI: - -```bash -agentspan credentials set OPENAI_API_KEY sk-... -``` - -## Plans and PLAN_EXECUTE - -`Strategy.PLAN_EXECUTE` runs a planner agent that emits a JSON plan, which is then -executed deterministically against a fixed tool set. Build the harness with the -`plan_execute` helper, or the `Agent` named-slot API. - -```python -from conductor.ai.agents import plan_execute - -harness = plan_execute( - "report_builder", - tools=[create_directory, write_file, check_word_count], - planner_instructions="Plan a multi-section report, then write each section.", - model="openai/gpt-4o", -) -result = runtime.run(harness, "Write a report on Rust adoption.") -``` - -Or directly: - -```python -from conductor.ai.agents import Agent, Strategy - -planner = Agent(name="rb_planner", model="openai/gpt-4o", instructions="Plan it.") -harness = Agent(name="report_builder", strategy=Strategy.PLAN_EXECUTE, - planner=planner, tools=[write_file, check_word_count]) -``` - -`PLAN_EXECUTE` requires `planner=` (the agent that emits the plan) and `tools=` on the -parent (the canonical executable tools); `fallback=` is optional. - -### Static plans (skip the planner) - -Build a deterministic plan in Python with the typed builders and pass it to `run`: - -```python -from conductor.ai.agents.plans import Plan, Step, Op, Generate, Validation, Ref - -plan = Plan( - steps=[ - Step("setup", operations=[Op("create_directory", args={"path": "out"})]), - Step("write", depends_on=["setup"], parallel=True, operations=[ - Op("write_file", generate=Generate( - instructions="Write the introduction.", - output_schema='{"path": "out/intro.md", "content": "..."}')), - ]), - Step("summarize", depends_on=["write"], operations=[ - Op("summarize", args={"document": Ref("write")}), # wire a prior step's output - ]), - ], - validation=[Validation("check_word_count", args={"path": "out/intro.md", "min_words": 200})], -) - -runtime.run(harness, "build it", plan=plan) -``` - -`Op` takes either `args=` (literal) or `generate=` (LLM-generated args). `Ref("step")` -injects an upstream step's output (the step must be in `depends_on`). `Step.parallel` -runs a step's operations concurrently; `depends_on` expresses cross-step concurrency. - -### Planner context - -Ground the planner with reference documents via `planner_context=` — inline text or a -URL fetched at planner-run time: - -```python -from conductor.ai.agents.plans import Context - -harness = plan_execute( - "kyc", tools=[...], - planner_instructions="Follow the KYC process.", - planner_context=[ - "Tier-1 customers skip manual review.", # inline string - Context(url="https://wiki/kyc-rules", headers={"Authorization": "Bearer ${KYC_TOKEN}"}), - ], -) -``` - -## Schedules - -Attach cron schedules at deploy time, or manage them through the schedule client. - -```python -from conductor.ai.agents import Schedule - -nightly = Schedule(name="nightly", cron="0 0 * * *", timezone="UTC", - input={"prompt": "Daily summary."}) - -runtime.deploy(agent, schedules=[nightly]) # upsert; [] purges; omit leaves as-is - -sc = runtime.schedules_client() # or runtime.client.schedules -sc.get_all_schedules(workflow_name=agent.name) # list — source-of-truth read -sc.pause("greeter-nightly", reason="ship freeze") -print(sc.preview_next("0 0 * * *", n=5)) # next 5 fire times (epoch ms) - -from conductor.ai.agents.schedule import schedules -schedules.run_now("greeter-nightly", runtime=runtime) # fire once -> execution id -``` - -## Skills - -Load an agentskills.io skill directory (with a `SKILL.md`) as an `Agent`: - -```python -from conductor.ai.agents import skill, load_skills - -researcher = skill("./skills/deep-research", model="openai/gpt-4o", - params={"rounds": 3}) -all_skills = load_skills("./skills", model="openai/gpt-4o") # dict: name -> Agent - -runtime.run(researcher, "Research durable execution engines.") -``` - -`skill(path, model="", agent_models=None, search_path=None, params=None)` returns an -ordinary `Agent` you can run, compose (e.g. via `agent_tool`), deploy, and serve. -Sub-agent files (`*-agent.md`), `scripts/`, and resource files are discovered -automatically; cross-skill references resolve from sibling and `~/.agents/skills` -directories plus any `search_path`. +- [Deploy · Serve · Run · Plan](concepts/deploy-serve-run.md) +- [Runtime reference](reference/runtime.md) and [control plane](reference/client.md) +- [Structured output](concepts/structured-output.md), [scheduling](concepts/scheduling.md), and [stateful agents](concepts/stateful.md) +- [Agent configuration contract](reference/agent-schema.md) diff --git a/docs/agents/api-reference.md b/docs/agents/api-reference.md index b1579b185..133763213 100644 --- a/docs/agents/api-reference.md +++ b/docs/agents/api-reference.md @@ -1,348 +1,9 @@ -# API reference +# Conductor-agent API reference -The public surface, importable from `conductor.ai.agents` unless noted. This is a -reference; for usage see [Writing agents](writing-agents.md), [Framework -agents](framework-agents.md), and [Advanced](advanced.md). +The reference is now split for easier navigation: -- [AgentRuntime](#agentruntime) -- [Agent / @agent](#agent) -- [Tools](#tools) and [built-in tools](#built-in-tools) -- [Guardrails](#guardrails) -- [Termination](#termination) -- [Handoffs](#handoffs) -- [TextGate](#textgate) -- [Schedules](#schedules) -- [Results, handles, streams, events](#results-handles-streams-events) -- [CallbackHandler](#callbackhandler) -- [AgentClient](#agentclient) -- [Config and credentials](#config-and-credentials) - -## AgentRuntime - -`AgentRuntime(configuration=None, *, settings=None)` — `configuration` is the -standard Conductor `Configuration` (host + auth; defaults to `Configuration()`, -which resolves `CONDUCTOR_SERVER_URL` → `AGENTSPAN_SERVER_URL` and -`CONDUCTOR_AUTH_*` from the environment); `settings` is an optional `AgentConfig` -with runtime behaviour knobs (its connection fields are ignored). - -Context manager (sync and async: `with` / `async with`). - -| Method | Signature | Purpose | -|---|---|---| -| `run` | `(agent, prompt=None, *, version=None, media=None, session_id=None, idempotency_key=None, on_event=None, timeout=None, credentials=None, context=None, run_settings=None, **kwargs) -> AgentResult` | Start, wait for completion, return the result (also starts workers) | -| `run_async` | same as `run` | Async run | -| `start` | `(agent, prompt=None, *, version=None, media=None, session_id=None, idempotency_key=None, context=None, run_settings=None, **kwargs) -> AgentHandle` | Fire-and-forget; returns a handle (also starts workers) | -| `start_async` | same as `start` | Async start | -| `stream` | `(agent=None, prompt=None, *, version=None, handle=None, media=None, session_id=None, **kwargs) -> AgentStream` | Stream events | -| `stream_async` | same as `stream` | `-> AsyncAgentStream` | -| `deploy` | `(*agents, packages=None, schedules=_UNSET) -> list[DeploymentInfo]` | Compile + register agent(s) on the server; does **not** start workers | -| `deploy_async` | same | Async deploy | -| `serve` | `(*agents, packages=None, blocking=True) -> None` | Register agent(s) on the server **and** serve/poll their workers (`serve` = `deploy` + serve) | -| `plan` | `(agent) -> dict` | Compile to workflow def | -| `resume` | `(execution_id, agent, *, timeout=None) -> AgentHandle` | Re-attach + re-register workers | -| `resume_async` | same | Async resume | -| `prepare` | `(agent) -> None` | Pre-register workers, no execution | -| `get_status` | `(execution_id) -> AgentStatus` | Execution status | -| `respond` | `(execution_id, output) -> None` | Complete a human task | -| `approve` / `reject` | `(execution_id)` / `(execution_id, reason="")` | HITL approve / reject | -| `send_message` | `(execution_id, message) -> None` | Push to workflow message queue | -| `pause` / `cancel` / `stop` | `(execution_id[, reason])` | Lifecycle control | -| `signal` | `(execution_id, message) -> None` | Inject persistent context | -| `shutdown` | `() -> None` | Stop all workers | -| `client` (property) | `-> AgentClient` | Control-plane client | -| `schedules_client` | `() -> SchedulerClient` | Shared schedule client | - -Async variants exist for status/respond/approve/reject/send/stop/shutdown -(`*_async`). Module-level wrappers using a singleton runtime: `run`, `run_async`, -`start`, `start_async`, `stream`, `stream_async`, `resume`, `resume_async`, `deploy`, -`deploy_async`, `serve`, `plan`, `configure`, `shutdown`. - -### Per-run overrides — `RunSettings` - -`run` / `start` (and their async variants and the module-level wrappers) accept -`run_settings=` to override an agent's LLM settings for a single invocation -without rebuilding the `Agent`: - -```python -from conductor.ai.agents import RunSettings - -runtime.run( - agent, - "Summarize this.", - run_settings=RunSettings(model="anthropic/claude-sonnet-4-6", temperature=0.0, max_tokens=512), -) -``` - -`RunSettings(model=None, temperature=None, max_tokens=None, reasoning_effort=None, -thinking_budget_tokens=None)` — only the fields you set override; unset fields keep -the agent's own values (so `temperature=0.0` is honored). A plain `dict` with the -same keys is also accepted. Overrides apply to the root agent's config; sub-agents -keep their own settings. - -## Agent - -`Agent(name, model="", instructions="", tools=None, agents=None, -strategy=Strategy.HANDOFF, router=None, output_type=None, guardrails=None, -memory=None, dependencies=None, max_turns=25, max_tokens=None, timeout_seconds=0, -temperature=None, reasoning_effort=None, stop_when=None, termination=None, -handoffs=None, allowed_transitions=None, introduction=None, metadata=None, -local_code_execution=False, allowed_languages=None, allowed_commands=None, -code_execution=None, cli_commands=False, cli_allowed_commands=None, cli_config=None, -enable_planning=False, callbacks=None, include_contents=None, -thinking_budget_tokens=None, required_tools=None, gate=None, base_url=None, -credentials=None, stateful=False, context_window_budget=None, prefill_tools=None, -fallback_max_turns=None, synthesize=True, masked_fields=None, planner=None, -fallback=None, planner_context=None)` - -- `name` must match `[a-zA-Z_][a-zA-Z0-9_-]*`. -- `model` is `"provider/model"`; empty means inherit from parent or treat as an - external workflow reference. -- `instructions` may be a string, a callable returning a string, or a `PromptTemplate`. -- `strategy` accepts a `Strategy` value or a string. -- Properties: `.is_claude_code`, `.external`. `a >> b` builds a sequential pipeline. - -Classmethod: `Agent.from_instance(instance, name=None)` — resolve `@agent` methods on -an object into one `Agent` (by `name`) or `list[Agent]` (all). `@tool`/`@guardrail` -methods on the instance are auto-attached. - -`@agent(func=None, *, name=None, model="", tools=None, guardrails=None, agents=None, -strategy=Strategy.HANDOFF, max_turns=25, max_tokens=None, temperature=None, -metadata=None, credentials=None, context_window_budget=None, ...)` — register a -function as an agent. The docstring is the instructions; returning a string gives -dynamic instructions. - -`Strategy` enum: `HANDOFF`, `SEQUENTIAL`, `PARALLEL`, `ROUTER`, `ROUND_ROBIN`, -`RANDOM`, `SWARM`, `MANUAL`, `PLAN_EXECUTE`. - -`PromptTemplate(name, variables={}, version=None)` — reference a server-side template. - -`scatter_gather(name, worker, *, model=None, instructions="", tools=None, -retry_count=None, retry_delay_seconds=None, fail_fast=False, **kwargs) -> Agent`. - -## Tools - -`@tool(func=None, *, name=None, external=False, approval_required=False, -timeout_seconds=None, guardrails=None, credentials=None, stateful=False, -max_calls=None, retry_count=2, retry_delay_seconds=2, -retry_policy="linear_backoff")` — register a function as a tool. Type hints + -docstring produce the schema. Attaches `_tool_def`. - -`ToolDef` fields: `name`, `description=""`, `input_schema={}`, `output_schema={}`, -`func`, `approval_required=False`, `timeout_seconds=None`, `tool_type="worker"`, -`config={}`, `guardrails=[]`, `credentials=[]`, `stateful=False`, `max_calls=None`, -`retry_count=2`, `retry_delay_seconds=2`, `retry_policy="linear_backoff"`. Method -`ToolDef.call(**kwargs) -> PrefillToolCall`. - -`ToolContext` fields: `session_id`, `execution_id`, `agent_name`, `metadata`, -`dependencies`, `state`. Declare a `context: ToolContext` parameter to receive it. - -`PrefillToolCall(tool_name, arguments, tool_def=None)` — a pre-declared tool call for -`Agent(prefill_tools=[...])`, created via `tool_def.call(...)`. - -Helpers: `get_tool_def(obj) -> ToolDef`, `get_tool_defs(tools) -> list[ToolDef]`. -`ToolRegistry.register_tool_workers(tools, agent_name, domain=None, -agent_stateful=False)` (used internally by the runtime). - -### Built-in tools - -- `http_tool(name, description, url, method="GET", headers=None, input_schema=None, accept=["application/json"], content_type="application/json", credentials=None)` -- `api_tool(url, name=None, description=None, headers=None, tool_names=None, max_tools=64, credentials=None)` -- `mcp_tool(server_url, name=None, description=None, headers=None, tool_names=None, max_tools=64, credentials=None)` -- `human_tool(name, description, input_schema=None)` -- `image_tool(name, description, llm_provider, model, input_schema=None, **defaults)` -- `audio_tool(name, description, llm_provider, model, input_schema=None, **defaults)` -- `video_tool(name, description, llm_provider, model, input_schema=None, **defaults)` -- `pdf_tool(name="generate_pdf", description="...", input_schema=None, **defaults)` -- `index_tool(name, description, vector_db, index, embedding_model_provider, embedding_model, namespace="default_ns", chunk_size=None, chunk_overlap=None, dimensions=None, input_schema=None)` -- `search_tool(name, description, vector_db, index, embedding_model_provider, embedding_model, namespace="default_ns", max_results=5, dimensions=None, input_schema=None)` -- `wait_for_message_tool(name, description, batch_size=1, blocking=True)` -- `agent_tool(agent, name=None, description=None, retry_count=None, retry_delay_seconds=None, optional=None)` - -OCG (from `conductor.ai.agents.ocg`): -`ocg_agent(*, model, url, name="ocg_agent", credential=None, instructions=None, -max_turns=10, query=True, entities=True, memory=True) -> Agent`; -`ocg_tools(*, url, credential=None, query=True, entities=True, memory=True) -> -list[ToolDef]`; `OCG_SYSTEM_PROMPT`. - -## Guardrails - -`@guardrail(func=None, *, name=None)` — register a `(str) -> GuardrailResult` function. - -`Guardrail(func=None, position=Position.OUTPUT, on_fail=OnFail.RETRY, name=None, -max_retries=3)`. `func=None` + `name=` makes an external guardrail. - -`RegexGuardrail(patterns, *, mode="block", position=Position.OUTPUT, -on_fail=OnFail.RETRY, name=None, message=None, max_retries=3)` — `mode="block"` fails -on match, `"allow"` fails on no match. - -`LLMGuardrail(model, policy, *, position=Position.OUTPUT, on_fail=OnFail.RETRY, -name=None, max_retries=3, max_tokens=None)` — LLM judges content against `policy` -(requires `litellm` at evaluation time). - -`GuardrailResult(passed, message="", fixed_output=None)`. -`OnFail`: `RETRY`, `RAISE`, `FIX`, `HUMAN`. `Position`: `INPUT`, `OUTPUT`. -`GuardrailDef(name, description, func)`. - -## Termination - -Composable with `&` (all) and `|` (any). All take a context dict and return a -`TerminationResult(should_terminate, reason="")`. - -- `TextMentionTermination(text, *, case_sensitive=False)` -- `StopMessageTermination(stop_message="TERMINATE")` -- `MaxMessageTermination(max_messages)` -- `TokenUsageTermination(max_total_tokens=None, max_prompt_tokens=None, max_completion_tokens=None)` -- `TerminationCondition` (base) - -## Handoffs - -For `strategy="swarm"`, in `handoffs=[...]`. All carry `target`. - -- `OnToolResult(target, tool_name="", result_contains=None)` — after a named tool runs (optionally only if the result contains a substring). -- `OnTextMention(target, text="")` — LLM output contains `text` (case-insensitive). -- `OnCondition(target, condition=...)` — `condition(context) -> bool`. -- `HandoffCondition` (base). - -## TextGate - -From `conductor.ai.agents.gate`: `TextGate(text, case_sensitive=True)` — stop a `>>` -pipeline after this agent when its output contains `text`. Compiled server-side. - -## Schedules - -`Schedule(name, cron, timezone="UTC", input={}, catchup=False, paused=False, -start_at=None, end_at=None, description=None)` — `cron` is a 5- or 6-field expression. - -`ScheduleInfo` (read model) fields include `name`, `short_name`, `agent`, `cron`, -`timezone`, `input`, `paused`, `catchup`, `next_run`, `create_time`, `update_time`, ... - -The schedule lifecycle lives on `SchedulerClient` itself (via -`runtime.schedules_client()`, `runtime.client.schedules`, or -`OrkesClients.get_scheduler_client()`): - -| Method | Signature | -|---|---| -| `pause` / `resume` | `(wire_name[, reason])` / `(wire_name)` | -| `delete` | `(wire_name) -> None` | -| `run_now` | `(info: ScheduleInfo) -> str` (execution_id) | -| `preview_next` | `(cron, n=5, start_at=None, end_at=None) -> list[int]` | -| `reconcile` | `(agent_name, desired: list[Schedule] | None) -> None` | - -Reads/writes/lists use the native source-of-truth methods: `get_schedule(wire) -> -WorkflowSchedule | None`, `save_schedule(SaveScheduleRequest)`, -`get_all_schedules(workflow_name=...) -> list[WorkflowSchedule]`. The mapped -`ScheduleInfo` view is returned by the module-level `schedules.list/get`. - -Errors: `ScheduleError`, `ScheduleNameConflict`, `ScheduleNotFound`, -`InvalidCronExpression`. - -## Results, handles, streams, events - -### AgentResult - -Fields: `output`, `execution_id`, `correlation_id`, `messages`, `tool_calls`, -`status` (`Status`), `token_usage` (`TokenUsage`), `metadata`, `finish_reason` -(`FinishReason`), `error`, `events`, `sub_results`. Properties: `is_success()`, -`is_failed()`, `is_rejected()`. Method: `print_result()`. - -`Status`: `COMPLETED`, `FAILED`, `TERMINATED`, `TIMED_OUT`. -`FinishReason`: `STOP`, `LENGTH`, `TOOL_CALLS`, `ERROR`, `CANCELLED`, `TIMEOUT`, -`GUARDRAIL`, `REJECTED`, `STOPPED`. -`TokenUsage`: `prompt_tokens`, `completion_tokens`, `total_tokens`, `reasoning_tokens`. -`DeploymentInfo`: `registered_name`, `agent_name`. - -### AgentHandle - -Fields: `execution_id`, `correlation_id`, `run_id`, `is_resumed`. - -| Method | Signature | Notes | -|---|---|---| -| `get_status` | `() -> AgentStatus` | | -| `stream` | `() -> AgentStream` | | -| `join` | `(timeout=None) -> AgentResult` | block until terminal | -| `respond` | `(output: dict, *, event=None) -> None` | answer a `human_tool` | -| `approve` | `(*, event=None) -> None` | approve pending tool | -| `reject` | `(reason="", *, event=None) -> None` | reject pending tool | -| `send` | `(message: str, *, event=None) -> None` | multi-turn message | -| `pause` / `resume` / `cancel` / `stop` | `()` / `()` / `(reason="")` / `()` | lifecycle | - -The `event=` parameter targets a specific pending pause (event-targeted HITL). Every -method has an `*_async` counterpart (e.g. `approve_async`, `join_async`). - -`AgentStatus` fields: `execution_id`, `is_complete`, `is_running`, `is_waiting`, -`output`, `status`, `reason`, `current_task`, `messages`, `pending_tool`. - -### AgentStream / AsyncAgentStream - -Iterable (sync `for` / async `for`) yielding `AgentEvent`. Fields: `handle`, `events`, -`result`, `execution_id`. Methods: `get_result()`, and HITL `respond`/`approve`/ -`reject`/`send` (each with `*, event=None`). `AsyncAgentStream`'s methods are async. - -### AgentEvent / EventType - -`AgentEvent` fields: `type`, `content`, `tool_name`, `args`, `result`, `target`, -`output`, `execution_id`, `guardrail_name`. - -`EventType`: `THINKING`, `TOOL_CALL`, `TOOL_RESULT`, `HANDOFF`, `WAITING`, `MESSAGE`, -`ERROR`, `DONE`, `GUARDRAIL_PASS`, `GUARDRAIL_FAIL`. - -## CallbackHandler - -Subclass and override any of: `on_agent_start`, `on_agent_end`, `on_model_start`, -`on_model_end`, `on_tool_start`, `on_tool_end`. Each is `(self, **kwargs) -> -Optional[dict]`: return `None` to continue, a non-empty dict to short-circuit and -override. Pass instances via `Agent(callbacks=[...])`; they chain in list order. - -## AgentClient - -The `/agent/*` control-plane client. `AgentClient` is an interface -(`conductor.client.agent_client`) implemented by `OrkesAgentClient` -(`conductor.client.orkes.orkes_agent_client`), following the same pattern as -`WorkflowClient`/`OrkesWorkflowClient` and built on the shared `ApiClient` -token machinery. Reach it via `runtime.client` or -`OrkesClients(configuration).get_agent_client()`. Every method has an `*_async` -counterpart. - -| Method | Signature | Purpose | -|---|---|---| -| `start_agent` | `(payload) -> dict` | POST /agent/start | -| `deploy_agent` | `(payload) -> dict` | POST /agent/deploy | -| `compile_agent` | `(payload) -> dict` | POST /agent/compile | -| `get_status` | `(execution_id) -> dict` | GET /agent/{id}/status | -| `get_execution` | `(execution_id) -> dict` | GET /agent/execution/{id} | -| `list_executions` | `(params=None) -> dict` | GET /agent/executions | -| `respond` | `(execution_id, body) -> None` | POST /agent/{id}/respond | -| `stop` | `(execution_id) -> None` | POST /agent/{id}/stop | -| `signal` | `(execution_id, message) -> None` | POST /agent/{id}/signal | -| `stream_sse` | `(execution_id, last_event_id=None) -> Iterator[dict]` | GET /agent/stream/{id} (SSE) | -| `schedules` (property) | `-> SchedulerClient` | Cron schedule lifecycle | -| `close` / `close_async` | `() -> None` | Release transport resources | - -## Config and credentials - -`AgentConfig` (dataclass) fields: `server_url="http://localhost:8080/api"`, -`api_key=None`, `auth_key=None`, `auth_secret=None`, `llm_retry_count=3`, -`worker_poll_interval_ms=100`, `worker_thread_count=1`, `auto_start_workers=True`, -`daemon_workers=True`, `auto_register_integrations=False`, -`streaming_enabled=True`, `secret_strict_mode=False`, `log_level="INFO"`. Classmethod -`AgentConfig.from_env()` reads the `AGENTSPAN_*` variables (see [Getting -started](getting-started.md#environment-variables)). Property `api_secret` aliases -`auth_secret`. `AgentRuntime` uses it only for runtime *behaviour* knobs — server -connection always comes from the Conductor `Configuration`. - -`get_secret(name) -> str` — read a credential inside a `@tool(credentials=[...])` -function. `resolve_credentials(task, names) -> dict` — for external workers; reads -the host-resolved values the server delivers on `Task.runtimeMetadata` (declared via -the tool/agent `credentials`, resolved at poll time). Errors: -`CredentialNotFoundError`, `CredentialAuthError`, `CredentialRateLimitError`, -`CredentialServiceError`. - -`ClaudeCode(model_name="", permission_mode=PermissionMode.ACCEPT_EDITS)` with -`PermissionMode` ∈ {`DEFAULT`, `ACCEPT_EDITS`, `PLAN`, `BYPASS`}; `to_model_string()`. - -Skills: `skill(path, model="", agent_models=None, search_path=None, params=None) -> -Agent`; `load_skills(path, model="", agent_models=None) -> dict[str, Agent]`; -`SkillLoadError`. - -Exceptions: `AgentspanError`, `AgentAPIError`, `AgentNotFoundError`, -`ConfigurationError`. +- [API map](reference/api.md) +- [AgentRuntime](reference/runtime.md) +- [AgentClient](reference/client.md) +- [Agent definition fields](reference/agent-definition.md) +- [Agent configuration contract](reference/agent-schema.md) diff --git a/docs/agents/concepts/agents.md b/docs/agents/concepts/agents.md new file mode 100644 index 000000000..ceaf58554 --- /dev/null +++ b/docs/agents/concepts/agents.md @@ -0,0 +1,55 @@ +# Conductor agents + +**Audience:** Python developers authoring durable, LLM-backed Conductor agents. + +## Prerequisites + +Install `conductor-python[agents]`, configure a reachable Conductor server, and +configure the selected provider on that server. Keep provider credentials out of +Python source and workflow input. + +## Define an agent + +Create an `Agent` with a stable name, `provider/model` identifier, instructions, +and optional tools or sub-agents. `@agent` turns a function docstring or return +value into instructions; `Agent.from_instance()` discovers decorated methods on +an object. + +```python +from conductor.ai.agents import Agent, AgentRuntime, tool + +@tool +def get_weather(city: str) -> str: + return f"Weather for {city}" + +agent = Agent(name="weather", model="openai/gpt-4o-mini", + instructions="Answer concisely.", tools=[get_weather]) +with AgentRuntime() as runtime: + print(runtime.run(agent, "Weather in Seattle?").output) +``` + +## Instructions and runtime overrides + +`instructions` may be a string, a callable evaluated during compilation, or a +`PromptTemplate` stored on the server. Use `RunSettings` for one execution's +model, temperature, token, or reasoning override; do not mutate a shared agent +definition per request. An omitted model is valid only for inherited-model or +external-agent designs. + +## Expected result + +`runtime.run()` compiles the agent, starts required local tool workers, and +returns an `AgentResult`. The Conductor UI shows the durable execution and its +tool calls. + +## Common failures + +- A model error normally means the provider credential or model is missing on + the **server**, not merely in the Python process. +- A name that does not match `^[a-zA-Z_][a-zA-Z0-9_-]*$` is rejected. +- A closure or non-importable tool cannot be recovered by a worker process. + +## Next steps + +Use [tools](tools.md) for capabilities, [multi-agent](multi-agent.md) for +composition, and [runtime modes](deploy-serve-run.md) for deployment. diff --git a/docs/agents/concepts/callbacks.md b/docs/agents/concepts/callbacks.md new file mode 100644 index 000000000..59751af30 --- /dev/null +++ b/docs/agents/concepts/callbacks.md @@ -0,0 +1,26 @@ +# Callbacks + +**Audience:** applications that need local observation or lightweight reactions to +Conductor-agent lifecycle events. + +## Prerequisites + +Register callback handlers before starting the runtime. Treat callback payloads as +potentially sensitive execution data. + +`CallbackHandler` observes agent lifecycle events and supports hooks for messages, +tool calls, results, and failures. Keep callback work fast and non-blocking; move +durable business effects into a tool or workflow task. Use structured logging and +redact credentials and sensitive user data. + +## Expected result and failures + +Callbacks receive lifecycle notifications without changing the durable workflow +unless they explicitly fail the local process. Do not use them as the only record +of an audit, notification, or external write: process restarts can interrupt local +observers. + +## Next steps + +Use [streaming](streaming-hitl.md) for caller-visible events and [tools](tools.md) +for durable side effects. diff --git a/docs/agents/concepts/deploy-serve-run.md b/docs/agents/concepts/deploy-serve-run.md new file mode 100644 index 000000000..a45d0f719 --- /dev/null +++ b/docs/agents/concepts/deploy-serve-run.md @@ -0,0 +1,38 @@ +# Deploy · Serve · Run · Plan + +**Audience:** developers choosing a local, CI/CD, or long-lived runtime mode. + +## Prerequisites + +Create an `AgentRuntime` with the target Conductor configuration. Ensure every +Python `@tool` is importable in the process that will call `serve()` or `run()`. + +| Operation | Effect | +|---|---| +| `runtime.plan(agent)` | Compile only; does not register or execute. | +| `runtime.deploy(agent)` | Compile and register; does not execute. | +| `runtime.serve(agent)` | Deploy, start local tool workers, and block. | +| `runtime.run(agent, prompt)` | Start and wait for an `AgentResult`. | +| `runtime.start(agent, prompt)` | Start and return an `AgentHandle`. | +| `runtime.resume(execution_id, agent)` | Reattach workers to a prior execution. | + +Use `deploy` in CI/CD and `serve` in long-lived worker processes. `run` is the +best local quickstart. Always call `shutdown()` or use `with AgentRuntime()` for +short-lived programs. See the [runtime reference](../reference/runtime.md). + +## Production pattern + +Compile/register with `deploy()` during release, then run `serve()` in one or more +long-lived worker services. Use `plan()` in CI to inspect the compiled workflow +before deployment. Use `start()` for asynchronous callers and `resume()` after a +local worker process restart. + +## Expected result and cleanup + +`deploy()` returns deployment information without executing an agent; `serve()` +blocks until interrupted; `run()` returns a terminal `AgentResult`. Use the +runtime context manager or `shutdown()` to stop local polling cleanly. + +## Next steps + +See [deployment and scaling](../../deployment-scaling.md), [runtime reference](../reference/runtime.md), and [agent client](../reference/client.md). diff --git a/docs/agents/concepts/guardrails.md b/docs/agents/concepts/guardrails.md new file mode 100644 index 000000000..82d945d86 --- /dev/null +++ b/docs/agents/concepts/guardrails.md @@ -0,0 +1,36 @@ +# Guardrails + +**Audience:** authors defining validation and safety controls for Conductor-agent +input, output, or tool work. + +## Prerequisites + +Decide whether failure should block, retry, repair, or require human review. Test +both a passing and failing value before deploying a guardrail. + +Guardrails validate input or output before it affects the next agent step. Use +`RegexGuardrail`, `LLMGuardrail`, a `Guardrail`, or `@guardrail` for custom logic. +`OnFail` can retry, raise, fix, or request a human decision. + +Attach guardrails to an agent or tool, keep custom functions importable by worker +processes, and avoid sending secrets to model-based validation. Test both pass and +failure paths. See [API reference](../reference/api.md). + +## Patterns + +Use `RegexGuardrail` for deterministic format checks, `LLMGuardrail` for semantic +policy checks, and a custom `@guardrail` only when the rule needs application +state. Apply a tool guardrail closest to the side effect; use an agent guardrail +for broad input/output policy. A retry policy is appropriate only when a new model +response can plausibly pass. + +## Expected result and failures + +Guardrail decisions appear in the execution history. If a model-based guardrail +receives a secret or raw sensitive record, remove that input and validate a +redacted representation instead. + +## Next steps + +Pair guardrails with [human approval](streaming-hitl.md), [tool policy](tools.md), +and [security guidance](../../security.md). diff --git a/docs/agents/concepts/multi-agent.md b/docs/agents/concepts/multi-agent.md new file mode 100644 index 000000000..43e6b8a6e --- /dev/null +++ b/docs/agents/concepts/multi-agent.md @@ -0,0 +1,41 @@ +# Multi-agent systems + +**Audience:** authors composing specialists into a durable Conductor-agent graph. + +## Prerequisites + +Each participating agent needs a unique stable name and bounded execution policy. +Start with a single agent and a tested tool before introducing delegation. + +`Agent(agents=[...], strategy=...)` supports `SEQUENTIAL`, `PARALLEL`, `HANDOFF`, +`ROUTER`, `ROUND_ROBIN`, `RANDOM`, `SWARM`, `MANUAL`, and `PLAN_EXECUTE`. + +- Sequential and parallel strategies compose deterministic work. +- Handoff, router, and swarm transfer control between agents; restrict transitions + and define an empty-safe fallback. +- `PLAN_EXECUTE` compiles a typed `Plan` into a durable sub-workflow and can replan. +- `agent_tool(child)` calls a child agent as a tool instead of transferring control. + +Set a termination condition and a turn/token limit for every open-ended design. +See [termination](termination.md), [plans](../reference/agent-definition.md), and +[writing examples](../../../examples/agents/README.md). + +## Handoffs, routers, and plans + +Use `HANDOFF` when the model should select a specialist, `ROUTER` when an explicit +router chooses one, and `agent_tool(child)` when a parent needs a child's result +without transferring control. `PLAN_EXECUTE` is for typed, inspectable plans that +must survive restarts; provide a planner and an optional fallback/replan policy. +Restrict allowed transitions so an unexpected model output cannot reach an unsafe +specialist. + +## Expected result and failures + +The compiled parent contains durable child/sub-workflow work and each child is +visible in execution history. If a graph loops, add `max_turns`, termination, and +a bounded planner/replan policy before retrying. + +## Next steps + +Read [termination](termination.md), [stateful agents](stateful.md), and the +[agent definition reference](../reference/agent-definition.md). diff --git a/docs/agents/concepts/scheduling.md b/docs/agents/concepts/scheduling.md new file mode 100644 index 000000000..93cbbb274 --- /dev/null +++ b/docs/agents/concepts/scheduling.md @@ -0,0 +1,24 @@ +# Scheduling + +**Audience:** operators scheduling recurring Conductor-agent executions. + +## Prerequisites + +Deploy the agent, choose a stable schedule name, and decide the correlation or +idempotency key that prevents duplicate business work. + +Deploy an agent before scheduling it, then use the shared scheduler client from +`runtime.schedules_client()` or `OrkesClients.get_scheduler_client()`. Use stable +schedule names, timezone-aware cron expressions, and idempotent workflow input. + +The schedule API is the core workflow scheduler, not a separate agent-only facade. +See [SCHEDULE.md](../../SCHEDULE.md) for request fields and lifecycle operations. + +## Expected result and cleanup + +A saved schedule starts the deployed agent on its cron cadence. Pause or delete +the schedule before removing the agent definition or retiring the worker service. + +## Next steps + +Read [schedules and events](../../schedules-events.md) and [runtime modes](deploy-serve-run.md). diff --git a/docs/agents/concepts/stateful.md b/docs/agents/concepts/stateful.md new file mode 100644 index 000000000..a59e3c183 --- /dev/null +++ b/docs/agents/concepts/stateful.md @@ -0,0 +1,27 @@ +# Stateful agents + +**Audience:** applications that need durable, session-scoped conversation state. + +## Prerequisites + +Choose a stable `session_id`, define retention/deletion policy, and classify which +user data may be persisted before enabling state. + +Set `stateful=True` when a session needs durable conversation state and worker +isolation. Pass a stable session identifier when resuming a conversation, use +`ConversationMemory` or `SemanticMemory` only for data appropriate to persist, +and define retention rules for user data. + +Stateful runs use liveness monitoring by default. Configure it with +`CONDUCTOR_AGENT_LIVENESS_*` and use `resume()` after a process restart. + +## Expected result and failures + +Runs with the same session identifier can resume the intended durable state. +Unexpected context growth should be handled with retention, memory limits, or +context condensation—not by placing unbounded history in prompts. + +## Next steps + +Read [streaming and approval](streaming-hitl.md), [security](../../security.md), +and [runtime modes](deploy-serve-run.md). diff --git a/docs/agents/concepts/streaming-hitl.md b/docs/agents/concepts/streaming-hitl.md new file mode 100644 index 000000000..761defbf8 --- /dev/null +++ b/docs/agents/concepts/streaming-hitl.md @@ -0,0 +1,34 @@ +# Streaming and human-in-the-loop + +**Audience:** interactive applications that display progress or require a human +decision before a tool executes. + +## Prerequisites + +Keep the execution ID returned by the runtime or handle. Define who may approve, +reject, or respond and validate every human-supplied value. + +`runtime.stream()` yields `AgentEvent` values while an execution runs; when SSE is +unavailable, the runtime can fall back to status polling. Use `human_tool` or a +wait tool to pause work, then approve, reject, or respond through `AgentHandle`. + +Do not treat streamed content as a final result until the terminal event arrives. +Use event execution IDs when approving nested or sub-agent work. + +## Approval pattern + +Use `human_tool` or a wait-for-message tool for an explicit durable pause. Resume +through the handle/client control plane rather than relying on an in-memory web +request. Make approval actions idempotent because callers may retry a network +request. + +## Expected result and cleanup + +The stream yields progress followed by a terminal event, while an approval task +remains visible as waiting work in Conductor. Close stream consumers and stop +short-lived runtimes after the terminal event. + +## Next steps + +See [tools](tools.md), [agent client](../reference/client.md), and +[callbacks](callbacks.md). diff --git a/docs/agents/concepts/structured-output.md b/docs/agents/concepts/structured-output.md new file mode 100644 index 000000000..747c341bf --- /dev/null +++ b/docs/agents/concepts/structured-output.md @@ -0,0 +1,26 @@ +# Structured output + +**Audience:** callers requiring a validated, typed final answer from a +Conductor agent. + +## Prerequisites + +Define a small dataclass or Pydantic model with explicit optional fields. Ensure +the prompt asks for the same shape the model declares. + +Set `output_type` to a dataclass or Pydantic model to ask the server to validate +the final result against a schema. Keep the model small, make optional fields +explicit, and handle validation failures as retryable only when the prompt can +produce a different result. The returned `AgentResult.output` is the parsed value +when validation succeeds. + +## Expected result and failures + +Successful runs return the parsed typed value. A validation error means the model +did not satisfy the requested contract; improve instructions or use a bounded +retry policy rather than silently accepting malformed data. + +## Next steps + +See [agent schema](../reference/agent-schema.md), [guardrails](guardrails.md), and +[runtime reference](../reference/runtime.md). diff --git a/docs/agents/concepts/termination.md b/docs/agents/concepts/termination.md new file mode 100644 index 000000000..669cd7e35 --- /dev/null +++ b/docs/agents/concepts/termination.md @@ -0,0 +1,26 @@ +# Termination conditions + +**Audience:** authors bounding cost, time, and unsafe open-ended delegation. + +## Prerequisites + +Set a meaningful `max_turns` and decide which completion signal is safe for the +business operation before selecting an additional termination condition. + +Use `MaxMessageTermination`, `StopMessageTermination`, `TextMentionTermination`, +or `TokenUsageTermination` to bound agent execution. Conditions compose with `&` +and `|`; pair them with `max_turns` for defense in depth. + +Stop a live execution through `AgentHandle` or `AgentClient.stop()`. A stop should +be safe to repeat and must not assume an in-flight external tool call is reversible. + +## Expected result and failures + +A terminal condition ends the durable execution with its recorded reason. If a +tool has already started, stopping the agent does not undo its external side +effect; design compensating work where necessary. + +## Next steps + +Continue with [multi-agent](multi-agent.md), [reliability](../../reliability.md), +and [agent client control](../reference/client.md). diff --git a/docs/agents/concepts/tools.md b/docs/agents/concepts/tools.md new file mode 100644 index 000000000..b68d595fa --- /dev/null +++ b/docs/agents/concepts/tools.md @@ -0,0 +1,62 @@ +# Tools + +**Audience:** authors exposing Python or server-native capabilities to a +Conductor agent. + +## Prerequisites + +Install `conductor-python[agents]`. Tool functions must be importable by worker +processes and safe to receive more than once. + +## Define a Python tool + +`@tool` converts Python type hints and docstrings into a tool schema. Each call is +a durable, retryable Conductor task. + +```python +from conductor.ai.agents import ToolContext, tool + +@tool(credentials=["GITHUB_TOKEN"]) +def create_issue(title: str, context: ToolContext) -> str: + token = context.get_credential("GITHUB_TOKEN") + return f"created: {title}" +``` + +## Choose the right tool + +| Need | Factory or pattern | +|---|---| +| Python business logic | `@tool` | +| HTTP endpoint | `http_tool` | +| OpenAPI/Postman discovery | `api_tool` | +| MCP server | `mcp_tool` | +| Human decision | `human_tool` | +| PDF, media, or vector retrieval | built-in PDF/media/index/search factories | +| Another Conductor agent | `agent_tool` | + +The built-in factories compile to Conductor system tasks where possible; prefer +them to hand-written wrapper workers. Declare credentials on the tool or agent so +the server resolves them into task runtime metadata. Do not read credentials from +ambient environment variables or store them in workflow input. + +Use command/code tools only with an allowlist. See [security](../../security.md) +and the complete Python signatures in [API reference](../reference/api.md). + +## Reliability and approval + +Use `retry_count`, `retry_delay_seconds`, `timeout_seconds`, and an idempotency +key appropriate to the external system. Mark destructive operations with +`approval_required=True` or model them with `human_tool`. A tool may accept +`ToolContext` for execution ID, session state, and resolved credentials. + +## Expected result and failures + +A successful tool appears as a named task in the agent execution. A task that +remains `SCHEDULED` has no compatible worker polling; a failed credential lookup +must be fixed in the server credential store rather than by adding a secret to +the prompt. + +## Next steps + +Continue with [guardrails](guardrails.md), [streaming and approval](streaming-hitl.md), +or [security](../../security.md). diff --git a/docs/agents/framework-agents.md b/docs/agents/framework-agents.md index 53e060849..563082b58 100644 --- a/docs/agents/framework-agents.md +++ b/docs/agents/framework-agents.md @@ -1,160 +1,9 @@ -# Framework agents +# Conductor-agent framework bridges -Conductor Agent can run agents authored in other frameworks by bridging them onto its -durable runtime. You keep your framework's authoring API; Conductor Agent handles -durability, retries, streaming, and observability. +This compatibility page has moved to individual framework guides: -Supported bridges: **OpenAI Agents SDK**, **LangChain**, **LangGraph**, **Claude -Agent SDK**. The runtime auto-detects the framework from the object you pass to -`runtime.run(...)`. - -- [OpenAI Agents SDK](#openai-agents-sdk) -- [LangChain](#langchain) -- [LangGraph](#langgraph) -- [Claude Agent SDK](#claude-agent-sdk) - -## OpenAI Agents SDK - -Two ways. Either keep your existing `agents.Agent` and swap the runner, or use the -SDK's `Runner` with a native `Agent`. - -### Drop-in `Runner` - -Change one import — `from conductor.ai import Runner` instead of `from agents import -Runner` — and run your existing OpenAI-Agents agent on Conductor Agent: - -```python -from conductor.ai import Runner # the one line that changes -from agents import Agent, function_tool - -@function_tool -def get_weather(city: str) -> str: - return f"72F and sunny in {city}" - -agent = Agent( - name="weather_assistant", - model="gpt-4o", - tools=[get_weather], - instructions="You are a helpful assistant.", -) - -result = Runner.run_sync(agent, "What's the weather in NYC?") -print(result.final_output) -``` - -`Runner` methods (all classmethods, accept an OpenAI-Agents `Agent` or a native -`Agent`): - -- `Runner.run_sync(starting_agent, input, *, context=None, max_turns=10, **kwargs) -> RunResult` -- `await Runner.run(starting_agent, input, *, context=None, max_turns=10, **kwargs) -> RunResult` -- `await Runner.run_streamed(starting_agent, input, *, context=None, max_turns=10, **kwargs) -> AsyncAgentStream` - -`RunResult` exposes `.final_output` and `.execution_id`. (`context` is accepted for -compatibility and ignored.) - -```python -import asyncio -from conductor.ai import Runner -from agents import Agent - -agent = Agent(name="Assistant", instructions="You only respond in haikus.") -result = asyncio.run(Runner.run(agent, "Tell me about recursion.")) -print(result.final_output) -``` - -`from conductor.ai import function_tool` is an alias of `@tool` for source compatibility. - -## LangChain - -Build a LangChain agent, then hand it to `runtime.run(...)`: - -```python -from conductor.ai.agents import AgentRuntime -from langchain.agents import create_agent -from langchain_core.tools import tool as lc_tool - -@lc_tool -def check_token() -> str: - """Check a token.""" - return "available" - -agent = create_agent("openai:gpt-4o", tools=[check_token], - system_prompt="You are a helpful assistant.") - -with AgentRuntime() as runtime: - result = runtime.run(agent, "Is the token set?", credentials=["GITHUB_TOKEN"]) - result.print_result() -``` - -Conductor Agent also provides a thin wrapper, `conductor.ai.agents.langchain.create_agent`, -that captures the model, tools, and system prompt up front so they compile to native -server-side model + tool tasks (rather than running the whole agent in one opaque -worker). - -## LangGraph - -Pass a compiled graph (e.g. from `create_react_agent` or your own -`StateGraph().compile()`) to `runtime.run(...)`: - -```python -import math -from langchain_core.tools import tool -from langchain_openai import ChatOpenAI -from langgraph.prebuilt import create_react_agent -from conductor.ai.agents import AgentRuntime - -@tool -def calculate(expression: str) -> str: - """Evaluate a math expression.""" - return str(eval(expression, {"__builtins__": {}}, {"sqrt": math.sqrt, "pi": math.pi})) - -llm = ChatOpenAI(model="gpt-4o-mini", temperature=0) -graph = create_react_agent(llm, tools=[calculate], name="math_agent") - -with AgentRuntime() as runtime: - result = runtime.run(graph, "What is sqrt(256) + 2**10?") - result.print_result() -``` - -The bridge tries, in order, full extraction (model + `ToolNode` tools), then a -graph-structure compilation (nodes/edges become tasks), then passthrough. To mark a -node as requiring human input, decorate it with `human_task`: - -```python -from conductor.ai.agents.frameworks.langgraph import human_task - -@human_task(prompt="Review and approve before continuing.") -def approval_node(state): ... -``` - -## Claude Agent SDK - -Run a Claude Agent SDK / Claude Code agent. The simplest path is a native `Agent` -configured with `ClaudeCode`: - -```python -from conductor.ai.agents import Agent, AgentRuntime, ClaudeCode - -fixer = Agent( - name="claude_code_fixer", - model=ClaudeCode("sonnet", - permission_mode=ClaudeCode.PermissionMode.ACCEPT_EDITS), - credentials=["GITHUB_TOKEN"], - instructions="You are a senior developer fixing a GitHub issue.", - tools=["Bash", "Read", "Write", "Edit", "Glob", "Grep"], # built-in string tools only - max_turns=50, -) - -with AgentRuntime() as rt: - result = rt.run(fixer, "Pick an open issue and open a PR.", timeout=600000) - result.print_result() -``` - -`ClaudeCode(model_name="", permission_mode=PermissionMode.ACCEPT_EDITS)`. -`permission_mode` is one of `DEFAULT`, `ACCEPT_EDITS`, `PLAN`, `BYPASS`. Claude Code -agents support the built-in string tools (`Read`, `Edit`, `Bash`, ...); custom `@tool` -functions are not yet supported there. - -You can also bring `ClaudeCodeOptions` / a Claude Agent SDK agent directly; the bridge -runs the full `query()` in one durable worker with instrumentation hooks that stream -tool-use and lifecycle events back to Conductor Agent. +- [Google ADK](frameworks/google-adk.md) +- [LangChain](frameworks/langchain.md) +- [LangGraph](frameworks/langgraph.md) +- [OpenAI Agents SDK](frameworks/openai.md) +- [Claude Agent SDK](frameworks/claude-agent-sdk.md) diff --git a/docs/agents/frameworks/claude-agent-sdk.md b/docs/agents/frameworks/claude-agent-sdk.md new file mode 100644 index 000000000..1c94f684a --- /dev/null +++ b/docs/agents/frameworks/claude-agent-sdk.md @@ -0,0 +1,14 @@ +# Claude Agent SDK + +**Prerequisites:** install `conductor-python[claude]`, configure the provider on +the Conductor server, and review any repository or shell access before use. + +Install `conductor-python[claude]` and the supported Claude Agent SDK dependency. +Pass a native Claude agent/options object to `AgentRuntime`; the bridge adapts it +to a durable Conductor-agent execution and preserves tool lifecycle events. + +Review CLI/code tool allowlists before running against real repositories or +credentials. See [tools](../concepts/tools.md) and the framework examples. + +**Expected result:** the native Claude agent executes through the durable runtime +while preserving tool lifecycle events. **Next:** read [security](../../security.md). diff --git a/docs/agents/frameworks/google-adk.md b/docs/agents/frameworks/google-adk.md new file mode 100644 index 000000000..65828cb17 --- /dev/null +++ b/docs/agents/frameworks/google-adk.md @@ -0,0 +1,30 @@ +# Google ADK + +**Prerequisites:** install the `adk` extra and configure the selected provider on +the Conductor server. + +Install the optional dependency: + +```shell +pip install 'conductor-python[adk]' +``` + +Pass a standard Google ADK `Agent` to `AgentRuntime.run()`. The runtime detects +the framework object, serializes its instructions, tools, and sub-agent graph, +and starts a durable Conductor-agent execution. + +```python +from conductor.ai.agents import AgentRuntime +from google.adk.agents import Agent + +agent = Agent(name="adk_greeter", model="gemini-2.0-flash", + instruction="Be concise.") +with AgentRuntime() as runtime: + print(runtime.run(agent, "Say hello.").output) +``` + +See [runnable ADK examples](../../../examples/agents/adk/README.md). Provider +credentials must be configured on the Conductor server. + +**Expected result:** the native ADK agent runs as a durable Conductor-agent +execution. **Next:** review [tools](../concepts/tools.md) and [runtime modes](../concepts/deploy-serve-run.md). diff --git a/docs/agents/frameworks/langchain.md b/docs/agents/frameworks/langchain.md new file mode 100644 index 000000000..2ce19dab0 --- /dev/null +++ b/docs/agents/frameworks/langchain.md @@ -0,0 +1,14 @@ +# LangChain + +**Prerequisites:** install `conductor-python[langchain]`; keep callback and tool +functions importable by worker processes. + +Install `conductor-python[langchain]`, then pass supported LangChain agents or +tools to the runtime bridge. The bridge retains framework authoring while +Conductor supplies durable execution, worker-backed tools, and observability. + +Keep callbacks and tool callables importable by worker processes. See the +[framework examples](../../../examples/agents/README.md) and [tools](../concepts/tools.md). + +**Expected result:** the bridge preserves LangChain authoring while Conductor owns +durable execution. **Next:** use [runtime modes](../concepts/deploy-serve-run.md). diff --git a/docs/agents/frameworks/langgraph.md b/docs/agents/frameworks/langgraph.md new file mode 100644 index 000000000..e7689cfbf --- /dev/null +++ b/docs/agents/frameworks/langgraph.md @@ -0,0 +1,14 @@ +# LangGraph + +**Prerequisites:** install `conductor-python[langgraph]` and keep recoverable +graph nodes free of process-local state. + +Install `conductor-python[langgraph]` and pass a supported LangGraph graph to +`AgentRuntime`. The bridge executes graph work through the durable runtime and +preserves streaming and human-task integration where the graph exposes it. + +Start with the [LangGraph examples](../../../examples/agents/langgraph/README.md). +Avoid closures and process-local state in nodes that must run after recovery. + +**Expected result:** graph work and emitted events are recorded in a durable +Conductor-agent execution. **Next:** read [streaming](../concepts/streaming-hitl.md). diff --git a/docs/agents/frameworks/openai.md b/docs/agents/frameworks/openai.md new file mode 100644 index 000000000..8006e24c6 --- /dev/null +++ b/docs/agents/frameworks/openai.md @@ -0,0 +1,19 @@ +# OpenAI Agents SDK + +**Prerequisites:** install `conductor-python[openai-agents]` and configure the +provider credential on the Conductor server. + +Install `conductor-python[openai-agents]`. The top-level `Runner` and +`function_tool` compatibility surface lets OpenAI Agents SDK-style applications +run through Conductor while retaining familiar agent, tool, handoff, and streaming +shapes. + +```python +from conductor.ai import Runner, function_tool +``` + +The runtime still requires a Conductor server and server-side provider credential. +See the [OpenAI examples](../../../examples/agents/openai/README.md). + +**Expected result:** familiar OpenAI Agents-style calls execute through Conductor. +**Next:** see [tools](../concepts/tools.md) and [runtime modes](../concepts/deploy-serve-run.md). diff --git a/docs/agents/getting-started.md b/docs/agents/getting-started.md index b9518efae..52f011554 100644 --- a/docs/agents/getting-started.md +++ b/docs/agents/getting-started.md @@ -1,84 +1,42 @@ -# Getting started +# Run your first durable Conductor agent -## Under 30 seconds +**Prerequisites:** Python 3.10+, a reachable Conductor server, and an LLM provider +credential configured on that server. -The agents API ships as an extra of `conductor-python` (see `pyproject.toml`). +## 1. Configure the client -```bash +```shell pip install 'conductor-python[agents]' +export CONDUCTOR_SERVER_URL=http://localhost:8080/api +export CONDUCTOR_AGENT_LLM_MODEL=openai/gpt-4o-mini ``` -Or, per framework, install just what you need — e.g. `conductor-python[langchain]`, -`conductor-python[adk]`, `conductor-python[claude]`. +For authenticated servers, also set `CONDUCTOR_AUTH_KEY` and +`CONDUCTOR_AUTH_SECRET`. Do not export provider secrets into application source; +configure the provider integration on the server. -Point the SDK at a running Conductor Agent server (defaults to `http://localhost:8080/api`): +## 2. Run the maintained example -```bash -export AGENTSPAN_SERVER_URL=http://localhost:8080/api -export OPENAI_API_KEY= -export AGENTSPAN_LLM_MODEL=openai/gpt-4o-mini +```shell +cd examples/agents +python 01_basic_agent.py ``` -Write `hello.py`: +Expected result: an `AgentResult` with the model response. If the request cannot +reach the server, check [server setup](../server-setup.md). If the agent cannot +call a model, check the server-side provider integration. + +## 3. Create the same agent ```python from conductor.ai.agents import Agent, AgentRuntime -agent = Agent( - name="greeter", - model="anthropic/claude-sonnet-4-6", - instructions="You are a friendly assistant. Keep responses brief.", -) - +agent = Agent(name="greeter", model="openai/gpt-4o-mini", + instructions="You are a friendly assistant.") with AgentRuntime() as runtime: - result = runtime.run(agent, "Say hello and tell me a fun fact about Python.") + result = runtime.run(agent, "Say hello.") print(result.output) ``` -Run it: - -```bash -uv run python hello.py -``` - -That is the whole loop: define an `Agent`, open an `AgentRuntime`, call `run`. The -runtime compiles the agent to a workflow, starts it, and blocks until it returns an -[`AgentResult`](api-reference.md#agentresult). `result.print_result()` pretty-prints -the output if you prefer. - -## Environment variables - -`AgentConfig.from_env()` reads these (all optional — defaults shown): - -| Variable | Default | Purpose | -|---|---|---| -| `AGENTSPAN_SERVER_URL` | `http://localhost:8080/api` | Server base URL | -| `AGENTSPAN_API_KEY` | — | API key auth | -| `AGENTSPAN_AUTH_KEY` | — | Key/secret auth — key | -| `AGENTSPAN_AUTH_SECRET` | — | Key/secret auth — secret | -| `AGENTSPAN_LLM_RETRY_COUNT` | `3` | LLM call retries | -| `AGENTSPAN_WORKER_POLL_INTERVAL` | `100` | Worker poll interval (ms) | -| `AGENTSPAN_WORKER_THREADS` | `1` | Worker thread count | -| `AGENTSPAN_AUTO_START_WORKERS` | `true` | Auto-start local tool workers | -| `AGENTSPAN_DAEMON_WORKERS` | `true` | Run workers as daemon threads | -| `AGENTSPAN_INTEGRATIONS_AUTO_REGISTER` | `false` | Auto-register provider integrations | -| `AGENTSPAN_STREAMING_ENABLED` | `true` | Enable SSE streaming | -| `AGENTSPAN_SECRET_STRICT_MODE` | `false` | Fail hard on missing secrets | -| `AGENTSPAN_LOG_LEVEL` | `INFO` | Log level | - -The model string is `"provider/model"`, e.g. `anthropic/claude-sonnet-4-6`, -`anthropic/claude-sonnet-4-20250514`, `google_gemini/gemini-2.0-flash`. Set the -matching provider API key in the environment of whoever runs the agent's workers. - -## What `model` looks like - -```python -Agent(name="a", model="openai/gpt-4o") # OpenAI -Agent(name="b", model="anthropic/claude-sonnet-4-20250514") -Agent(name="c", model="google_gemini/gemini-2.0-flash") -``` - -## Next - -- Add tools, sub-agents, and human-in-the-loop: [Writing agents](writing-agents.md). -- Deploy once and serve workers separately for production: [Advanced](advanced.md). +Next: [tools](concepts/tools.md), [runtime modes](concepts/deploy-serve-run.md), +or [framework bridges](README.md#framework-bridges). diff --git a/docs/agents/reference/agent-definition.md b/docs/agents/reference/agent-definition.md new file mode 100644 index 000000000..fe630aa93 --- /dev/null +++ b/docs/agents/reference/agent-definition.md @@ -0,0 +1,12 @@ +# Agent definition fields + +`Agent` accepts a name, `provider/model`, instructions, tools, sub-agents, and +runtime policy. Important fields include `strategy`, `max_turns`, `max_tokens`, +`temperature`, `timeout_seconds`, `output_type`, `guardrails`, `termination`, +`handoffs`, `credentials`, `stateful`, `enable_planning`, `callbacks`, and +`fallback`. + +Names must match `^[a-zA-Z_][a-zA-Z0-9_-]*$`. Empty models represent inherited or +external-agent behavior. The complete constructor and serialization semantics are +maintained in [api-reference.md](../api-reference.md) and +`AgentConfigSerializer`; use those sources when adding a newly supported field. diff --git a/docs/agents/reference/agent-schema.json b/docs/agents/reference/agent-schema.json new file mode 100644 index 000000000..12656b475 --- /dev/null +++ b/docs/agents/reference/agent-schema.json @@ -0,0 +1,87 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://github.com/conductor-oss/python-sdk/blob/main/docs/agents/reference/agent-schema.json", + "title": "Conductor Python AgentConfig", + "description": "The public wire shape emitted by AgentConfigSerializer under agentConfig.", + "$ref": "#/$defs/agentConfig", + "$defs": { + "jsonValue": { + "anyOf": [ + { "type": "string" }, { "type": "number" }, { "type": "integer" }, + { "type": "boolean" }, { "type": "null" }, + { "type": "array", "items": { "$ref": "#/$defs/jsonValue" } }, + { "type": "object", "additionalProperties": { "$ref": "#/$defs/jsonValue" } } + ] + }, + "taskReference": { + "type": "object", + "required": ["taskName"], + "properties": { "taskName": { "type": "string", "minLength": 1 } }, + "additionalProperties": false + }, + "tool": { + "type": "object", + "required": ["name"], + "properties": { + "name": { "type": "string", "minLength": 1 }, + "description": { "type": "string" }, + "toolType": { "type": "string" }, + "taskName": { "type": "string" }, + "inputSchema": { "type": "object" }, + "outputSchema": { "type": "object" }, + "credentials": { "type": "array", "items": { "type": "string" } }, + "approvalRequired": { "type": "boolean" } + }, + "additionalProperties": true + }, + "agentConfig": { + "type": "object", + "required": ["name"], + "properties": { + "name": { "type": "string", "pattern": "^[a-zA-Z_][a-zA-Z0-9_-]*$" }, + "model": { "type": ["string", "null"] }, + "baseUrl": { "type": ["string", "null"] }, + "strategy": { "type": ["string", "null"] }, + "maxTurns": { "type": ["integer", "null"], "minimum": 0 }, + "timeoutSeconds": { "type": ["integer", "null"], "minimum": 0 }, + "external": { "type": ["boolean", "null"] }, + "instructions": { "anyOf": [{ "type": "string" }, { "type": "object" }, { "type": "null" }] }, + "tools": { "type": "array", "items": { "$ref": "#/$defs/tool" } }, + "agents": { "type": "array", "items": { "$ref": "#/$defs/agentConfig" } }, + "router": { "anyOf": [{ "$ref": "#/$defs/taskReference" }, { "$ref": "#/$defs/agentConfig" }] }, + "outputType": { "type": "object" }, + "guardrails": { "type": "array", "items": { "type": "object" } }, + "memory": { "type": "object" }, + "maxTokens": { "type": "integer", "minimum": 0 }, + "contextWindowBudget": { "type": "integer", "minimum": 0 }, + "temperature": { "type": "number" }, + "reasoningEffort": { "type": "string" }, + "stopWhen": { "$ref": "#/$defs/taskReference" }, + "termination": { "type": "object" }, + "handoffs": { "type": "array", "items": { "type": "object" } }, + "allowedTransitions": { "type": "array", "items": { "type": "string" } }, + "introduction": { "type": "string" }, + "metadata": { "type": "object" }, + "enablePlanning": { "type": "boolean" }, + "planner": { "$ref": "#/$defs/agentConfig" }, + "fallback": { "$ref": "#/$defs/agentConfig" }, + "callbacks": { "type": "array", "items": { "type": "object" } }, + "includeContents": { "type": "boolean" }, + "thinkingConfig": { "type": "object" }, + "requiredTools": { "type": "array", "items": { "type": "string" } }, + "prefillTools": { "type": "array", "items": { "type": "object" } }, + "fallbackMaxTurns": { "type": "integer", "minimum": 0 }, + "planSource": { "type": "string" }, + "plannerContext": { "type": "array", "items": { "type": "object" } }, + "synthesize": { "type": "boolean" }, + "maskedFields": { "type": "array", "items": { "type": "string" } }, + "gate": { "type": "object" }, + "codeExecution": { "type": "object" }, + "cliConfig": { "type": "object" }, + "credentials": { "type": "array", "items": { "type": "string" } }, + "_framework": { "type": "string" } + }, + "additionalProperties": false + } + } +} diff --git a/docs/agents/reference/agent-schema.md b/docs/agents/reference/agent-schema.md new file mode 100644 index 000000000..135cb019d --- /dev/null +++ b/docs/agents/reference/agent-schema.md @@ -0,0 +1,30 @@ +# Agent configuration contract + +**Audience:** integrators inspecting or validating the `agentConfig` payload sent +to the Conductor agent control plane. + +## Canonical schema + +The canonical Python wire contract is the dictionary emitted by +`conductor.ai.agents.config_serializer.AgentConfigSerializer` under the +`agentConfig` request field. Its serializer tests validate supported agent, +tool, guardrail, handoff, memory, termination, callbacks, planning, and framework +configuration shapes. + +Configuration keys are camelCase on the wire even when Python constructor fields +are snake_case. Nested `agents`, `router`, `planner`, and `fallback` serialize +recursively. The published [JSON Schema](agent-schema.json) rejects unknown root +agent fields while allowing intentionally open JSON payloads such as metadata, +tool input schemas, and framework passthrough configuration. + +## Compatibility + +The schema describes payloads emitted by the current Python serializer; it is not +a promise that arbitrary server-side fields can be supplied by callers. Extend the +serializer, schema, reference, and contract tests together when adding a public +agent field. For request envelopes and responses, see [AgentClient](client.md). + +## Next steps + +Read [agent definition fields](agent-definition.md) for Python authoring and +[runtime](runtime.md) for submission lifecycle. diff --git a/docs/agents/reference/api.md b/docs/agents/reference/api.md new file mode 100644 index 000000000..c6ba110c3 --- /dev/null +++ b/docs/agents/reference/api.md @@ -0,0 +1,14 @@ +# Agent API map + +| Need | Python API | Guide | +|---|---|---| +| Define an agent | `Agent`, `@agent`, `Agent.from_instance` | [Agents](../concepts/agents.md) | +| Define tools | `@tool`, `ToolDef`, built-in tool factories | [Tools](../concepts/tools.md) | +| Run lifecycle operations | `AgentRuntime`, module-level helpers | [Runtime](runtime.md) | +| Control an execution | `AgentClient`, `AgentHandle` | [Control plane](client.md) | +| Add safety controls | guardrails and termination classes | [Guardrails](../concepts/guardrails.md) | +| Compose agents | `Strategy`, handoffs, `plan_execute` | [Multi-agent](../concepts/multi-agent.md) | +| Bridge frameworks | framework extras | [Framework bridges](../README.md) | + +All public agent imports are available from `conductor.ai.agents`; detailed legacy +API lists remain available through [api-reference.md](../api-reference.md). diff --git a/docs/agents/reference/client.md b/docs/agents/reference/client.md new file mode 100644 index 000000000..eedbeab3b --- /dev/null +++ b/docs/agents/reference/client.md @@ -0,0 +1,29 @@ +# AgentClient control-plane reference + +**Audience:** applications that need direct control-plane access without managing +local Python tool workers. + +## Prerequisites + +Use a configured SDK client and ensure tools are server-native or already served +by a deployed worker process. + +`AgentClient` is the typed transport interface for `/agent/*` endpoints. Its +`OrkesAgentClient` implementation shares the SDK transport, token refresh, and +authentication behavior. + +| Operation | Method | +|---|---| +| Compile, deploy, start | `compile_agent`, `deploy_agent`, `start_agent` | +| Inspect | `get_status`, `get_execution`, `list_executions` | +| Human/control actions | `respond`, `stop`, `signal` | +| Stream events | `stream_sse` / `stream_sse_async` | + +Every operation has an async counterpart. `SSEUnavailableError` lets callers +choose a status-polling fallback. Use `AgentRuntime` unless an application needs +direct control-plane integration. + +## Expected result and next steps + +`stop`, `signal`, and `respond` change a durable execution; authorize callers and +make externally triggered requests idempotent. See [streaming and approval](../concepts/streaming-hitl.md) and [runtime](runtime.md). diff --git a/docs/agents/reference/runtime.md b/docs/agents/reference/runtime.md new file mode 100644 index 000000000..67eeb6ed1 --- /dev/null +++ b/docs/agents/reference/runtime.md @@ -0,0 +1,38 @@ +# AgentRuntime reference + +**Audience:** applications owning local tool workers and Conductor-agent lifecycle. + +## Prerequisites + +Create one runtime per application lifetime with a configured server connection. +Use a context manager for scripts and an application shutdown hook for services. + +`AgentRuntime` owns connection-facing clients, local tool workers, and execution +lifecycle. Use it as a context manager or call `shutdown()`. + +| Method | Returns | Purpose | +|---|---|---| +| `run` / `run_async` | `AgentResult` | Start and wait for completion. | +| `start` / `start_async` | `AgentHandle` | Start without waiting. | +| `stream` / `stream_async` | stream | Consume agent events. | +| `plan` | compiled definition | Compile without registration or execution. | +| `deploy` / `deploy_async` | deployment information | Register agent definitions. | +| `serve` | none | Deploy and poll tool workers. | +| `resume` / `resume_async` | `AgentHandle` | Reattach to an execution. | + +`AgentConfig.from_env()` reads canonical `CONDUCTOR_AGENT_*` settings. Connection, +auth, and SDK log level belong to `Configuration` and use `CONDUCTOR_*`. Share one +runtime for an application lifetime; do not create one per request. + +## Expected result and common failures + +`RunSettings` overrides model/runtime options for one run without mutating the +agent definition. Use `on_event` or `stream()` for progress, and `resume()` to +reattach local workers after a process restart. A `SCHEDULED` tool task means no +compatible worker process is polling; a model error normally means the server +provider setup is incomplete. + +## Cleanup and next steps + +Call `shutdown()` or exit the context manager to stop local workers. Continue with +[runtime modes](../concepts/deploy-serve-run.md) and [client control](client.md). diff --git a/docs/agents/writing-agents.md b/docs/agents/writing-agents.md index 057e9c6e0..2624753ef 100644 --- a/docs/agents/writing-agents.md +++ b/docs/agents/writing-agents.md @@ -1,479 +1,8 @@ -# Writing agents +# Writing Conductor agents -Everything is an `Agent`. A single agent wraps an LLM plus tools. An agent with -sub-agents is a multi-agent system. Compose, then run with an -[`AgentRuntime`](advanced.md). +This compatibility page has moved to the task-oriented guides: -- [Defining an agent](#defining-an-agent) -- [Instructions (static, dynamic, templated)](#instructions) -- [Tools](#tools) -- [Built-in tools](#built-in-tools) -- [Multi-agent strategies](#multi-agent-strategies) -- [Handoffs (swarm)](#handoffs-swarm) -- [Guardrails](#guardrails) -- [Termination and TextGate](#termination-and-textgate) -- [Callbacks](#callbacks) -- [Streaming and human-in-the-loop](#streaming-and-human-in-the-loop) -- [Schedules](#schedules) -- [Agents from a class (`Agent.from_instance`)](#agents-from-a-class) -- [Stateful agents](#stateful-agents) - -## Defining an agent - -Two equivalent ways: the `Agent` class, or the `@agent` decorator. - -### The `Agent` class - -```python -from conductor.ai.agents import Agent - -agent = Agent( - name="greeter", # required; [a-zA-Z_][a-zA-Z0-9_-]* - model="openai/gpt-4o", # "provider/model" - instructions="You are a friendly assistant.", - tools=[], # @tool functions or ToolDef - max_turns=25, # agent-loop iteration cap - temperature=None, - max_tokens=None, -) -``` - -Common constructor arguments: `name`, `model`, `instructions`, `tools`, `agents`, -`strategy`, `guardrails`, `output_type`, `termination`, `handoffs`, `callbacks`, -`max_turns`, `max_tokens`, `temperature`, `reasoning_effort`, -`thinking_budget_tokens`, `credentials`, `stateful`, `include_contents`, -`timeout_seconds`. See the [API reference](api-reference.md#agent) for the full list. - -### The `@agent` decorator - -The docstring becomes the instructions. The decorated function stays callable. - -```python -from conductor.ai.agents import agent, tool - -@tool -def get_weather(city: str) -> str: - """Get current weather for a city.""" - return f"72F and sunny in {city}" - -@agent(model="openai/gpt-4o", tools=[get_weather]) -def weatherbot(): - """You are a weather assistant.""" -``` - -A `@agent` function resolves to an `Agent` automatically when passed as a sub-agent -or to `runtime.run(...)`. When `model` is omitted it inherits the parent's model. - -## Instructions - -Instructions can be a string, a callable, or a server-side `PromptTemplate`. - -```python -# Static string -Agent(name="a", model="openai/gpt-4o", instructions="You are concise.") - -# Dynamic — a @agent function that RETURNS a string is used as instructions -@agent(model="openai/gpt-4o") -def planner(): - rules = load_rules() # evaluated at resolution/compile time - return f"You are a planner. Follow these rules:\n{rules}" - -# Named server-side template -from conductor.ai.agents import Agent, PromptTemplate -Agent(name="t", model="openai/gpt-4o", - instructions=PromptTemplate(name="support_prompt", - variables={"tier": "${workflow.input.user_tier}"})) -``` - -`PromptTemplate` references a template already stored on the server (managed via the -Conductor UI/API); the SDK does not create templates. - -## Tools - -Decorate a plain function with `@tool`. Type hints and the docstring generate the -tool's JSON schema. Tools run as durable Conductor worker tasks. - -```python -from conductor.ai.agents import tool - -@tool -def calculate(expression: str) -> dict: - """Evaluate a math expression.""" - return {"result": eval(expression, {"__builtins__": {}}, {})} - -@tool(approval_required=True, timeout_seconds=60, retry_count=2) -def send_email(to: str, subject: str, body: str) -> dict: - """Send an email.""" # pauses for human approval before running - return {"status": "sent", "to": to} - -agent = Agent(name="assistant", model="openai/gpt-4o", - tools=[calculate, send_email]) -``` - -`@tool` keyword arguments: `name`, `external`, `approval_required`, -`timeout_seconds`, `guardrails`, `credentials`, `stateful`, `max_calls`, -`retry_count=2`, `retry_delay_seconds=2`, `retry_policy="linear_backoff"`. - -### Tool context - -A tool can receive execution context by declaring a `ToolContext` parameter; tools -without it are unchanged. - -```python -from conductor.ai.agents import tool, ToolContext - -@tool -def remember(note: str, context: ToolContext) -> str: - context.state["last_note"] = note # session_id, execution_id, state, ... - return "noted" -``` - -### Inspecting tool defs — `ToolRegistry` / `get_tool_defs` - -Each `@tool` function carries a resolved `ToolDef` (accessible via `get_tool_def`). -`get_tool_defs(tools)` extracts them from a mixed list. The runtime's `ToolRegistry` -registers tool functions as Conductor workers; you normally never touch it directly — -the runtime does it for you when you `run`/`serve`/`deploy`. - -```python -from conductor.ai.agents.tool import get_tool_def, get_tool_defs -defs = get_tool_defs([calculate, send_email]) -print(defs[0].name, defs[0].input_schema) -``` - -## Built-in tools - -These constructors return `ToolDef`s that compile to native Conductor tasks — most -need no worker process. Add them to `tools=[...]`. - -| Constructor | Purpose | -|---|---| -| `http_tool(name, description, url, method="GET", headers=None, input_schema=None, credentials=None, ...)` | Call an HTTP endpoint (HttpTask) | -| `api_tool(url, name=None, headers=None, tool_names=None, max_tools=64, credentials=None)` | Expand an OpenAPI/Swagger/Postman spec into tools | -| `mcp_tool(server_url, name=None, headers=None, tool_names=None, max_tools=64, credentials=None)` | Expose tools from an MCP server | -| `human_tool(name, description, input_schema=None)` | Pause for human input (HUMAN task) | -| `image_tool(name, description, llm_provider, model, ...)` | Generate images | -| `audio_tool(name, description, llm_provider, model, ...)` | Generate audio / TTS | -| `video_tool(name, description, llm_provider, model, ...)` | Generate video | -| `pdf_tool(name="generate_pdf", description=..., ...)` | Generate a PDF from markdown | -| `index_tool(name, description, vector_db, index, embedding_model_provider, embedding_model, ...)` | Index documents into a vector DB (RAG ingest) | -| `search_tool(name, description, vector_db, index, embedding_model_provider, embedding_model, max_results=5, ...)` | Search a vector DB (RAG query) | -| `wait_for_message_tool(name, description, batch_size=1, blocking=True)` | Dequeue from the workflow message queue | -| `agent_tool(agent, name=None, description=None, retry_count=None, retry_delay_seconds=None, optional=None)` | Call another `Agent` as a tool (sub-workflow) | - -```python -from conductor.ai.agents import Agent, http_tool, mcp_tool, agent_tool - -weather = http_tool( - name="weather", description="Current weather", - url="https://api.example.com/weather", method="GET", - input_schema={"type": "object", "properties": {"city": {"type": "string"}}}, -) - -mcp = mcp_tool(server_url="https://mcp.example.com/sse") - -sub = Agent(name="researcher", model="openai/gpt-4o", instructions="Research a topic.") -main = Agent(name="lead", model="openai/gpt-4o", tools=[weather, mcp, agent_tool(sub)]) -``` - -### RAG (`index_tool` + `search_tool`) - -`index_tool` writes embeddings into a vector DB; `search_tool` queries it. Both -compile to native Conductor LLM index/search tasks — give the agent both to build a -retrieval loop. - -### OCG retrieval sub-agent - -`ocg_agent(...)` builds a prebuilt retrieval `Agent` over an Open Context Graph; its -tools compile to plain HTTP tasks. `ocg_tools(...)` returns the raw `ToolDef`s if you -want to assemble your own retriever. - -```python -from conductor.ai.agents import Agent, agent_tool -from conductor.ai.agents.ocg import ocg_agent - -retriever = ocg_agent(model="anthropic/claude-sonnet-4-6", - url="https://ocg.example.com", credential="OCG_KEY") -main = Agent(name="support", model="openai/gpt-4o", tools=[agent_tool(retriever)]) -``` - -`url` is required and binds the instance; `credential` names a server-side credential -(the secret never appears in code). Agents bound to different OCG instances must use -distinct `name`s. - -## Multi-agent strategies - -Pass sub-agents via `agents=[...]` and pick a `strategy`. Strategy values -(`Strategy` enum or plain strings): - -| Strategy | Behavior | -|---|---| -| `HANDOFF` (default) | Parent LLM delegates to the right specialist (sub-agents appear as callable tools) | -| `SEQUENTIAL` | Run sub-agents in order, piping output forward | -| `PARALLEL` | Run sub-agents concurrently, then aggregate | -| `ROUTER` | A `router` (Agent or callable) picks one sub-agent per turn | -| `ROUND_ROBIN` | Cycle through sub-agents | -| `RANDOM` | Pick a sub-agent at random | -| `SWARM` | Sub-agents transfer control via [handoffs](#handoffs-swarm) | -| `MANUAL` | Caller selects the next agent | -| `PLAN_EXECUTE` | A planner emits a JSON plan that is executed deterministically — see [Advanced](advanced.md#plans-and-plan_execute) | - -```python -from conductor.ai.agents import Agent, Strategy - -billing = Agent(name="billing", model="openai/gpt-4o", instructions="Billing.") -tech = Agent(name="technical", model="openai/gpt-4o", instructions="Tech support.") - -support = Agent( - name="support", model="openai/gpt-4o", - instructions="Route the request to the right specialist.", - agents=[billing, tech], - strategy=Strategy.HANDOFF, -) -``` - -Sequential pipelines also have a shorthand with `>>`: - -```python -pipeline = extract >> summarize >> translate # Strategy.SEQUENTIAL -``` - -`scatter_gather(name, worker, ...)` builds a coordinator that fans a problem out to N -parallel copies of `worker` (via `agent_tool`) and synthesizes the results. - -## Handoffs (swarm) - -With `strategy="swarm"`, declare `handoffs=[...]` rules that transfer control between -agents after a tool call or after the LLM speaks. - -```python -from conductor.ai.agents import Agent -from conductor.ai.agents.handoff import OnTextMention, OnToolResult, OnCondition - -refund = Agent(name="refund", model="openai/gpt-4o", instructions="Process refunds.") - -support = Agent( - name="support", model="openai/gpt-4o", instructions="Help the customer.", - agents=[refund], strategy="swarm", - handoffs=[ - OnToolResult(tool_name="check_order", target="refund"), # after a tool runs - OnToolResult(tool_name="check_order", target="refund", result_contains="late"), - OnTextMention(text="refund", target="refund"), # LLM output contains text (case-insensitive) - OnCondition(condition=lambda ctx: ctx.get("iteration", 0) > 5, # custom predicate - target="refund"), - ], -) -``` - -`allowed_transitions={"a": ["b", "c"]}` constrains which agent may follow which. - -## Guardrails - -Guardrails validate input or output. They compile to worker tasks before/after the -LLM call. Decorate a `(str) -> GuardrailResult` function, or use the prebuilt -`RegexGuardrail` / `LLMGuardrail`. - -```python -from conductor.ai.agents import Agent, guardrail, GuardrailResult, RegexGuardrail, LLMGuardrail, Guardrail - -@guardrail -def no_pii(content: str) -> GuardrailResult: - """Reject responses containing an SSN.""" - import re - if re.search(r"\d{3}-\d{2}-\d{4}", content): - return GuardrailResult(passed=False, message="Remove the SSN.") - return GuardrailResult(passed=True) - -no_emails = RegexGuardrail(patterns=[r"[\w.+-]+@[\w-]+\.[\w.-]+"], - name="no_emails", message="No email addresses.") - -safety = LLMGuardrail(model="anthropic/claude-sonnet-4-6", - policy="Reject harmful or discriminatory content.") - -agent = Agent(name="safe", model="openai/gpt-4o", - guardrails=[Guardrail(no_pii, position="output", on_fail="retry"), - no_emails, safety]) -``` - -`Guardrail(func, position="input"|"output", on_fail="retry"|"raise"|"fix"|"human", -name=None, max_retries=3)`. On `on_fail="retry"` the failure message is fed back to -the LLM and it tries again; `"human"` (output only) pauses for a human; -`"fix"` substitutes `GuardrailResult.fixed_output`. - -## Termination and TextGate - -`termination=` accepts a composable `TerminationCondition`. Combine with `&` (all) -and `|` (any). - -```python -from conductor.ai.agents import ( - Agent, TextMentionTermination, MaxMessageTermination, - TokenUsageTermination, StopMessageTermination, -) - -stop = TextMentionTermination("DONE") | MaxMessageTermination(50) -stop = StopMessageTermination("TERMINATE") & TokenUsageTermination(max_total_tokens=10_000) - -agent = Agent(name="loop", model="openai/gpt-4o", termination=stop) -``` - -- `TextMentionTermination(text, case_sensitive=False)` — substring match in output. -- `StopMessageTermination(stop_message="TERMINATE")` — exact (stripped) match. -- `MaxMessageTermination(max_messages)` — message/iteration cap. -- `TokenUsageTermination(max_total_tokens=, max_prompt_tokens=, max_completion_tokens=)`. - -`TextGate` stops a `>>` pipeline early when an agent's output contains a sentinel, -compiled server-side (no worker round-trip): - -```python -from conductor.ai.agents.gate import TextGate -stage = Agent(name="triage", model="openai/gpt-4o", gate=TextGate("ESCALATE")) -``` - -## Callbacks - -Subclass `CallbackHandler` to hook the lifecycle. Each method receives keyword -arguments from the server and returns `None` to continue or a non-empty `dict` to -short-circuit (e.g. override the LLM response). Multiple handlers chain in list order. - -```python -from conductor.ai.agents import Agent, CallbackHandler - -class Logger(CallbackHandler): - def on_model_start(self, **kwargs): - print("calling LLM with", len(kwargs.get("messages", [])), "messages") - return None # continue - def on_tool_end(self, **kwargs): - print("tool", kwargs.get("tool_name"), "done") - return None - -agent = Agent(name="watched", model="openai/gpt-4o", callbacks=[Logger()]) -``` - -Hook points: `on_agent_start`, `on_agent_end`, `on_model_start`, `on_model_end`, -`on_tool_start`, `on_tool_end`. (The old `before_model_callback`/`after_model_callback` -constructor args are deprecated — use `callbacks=[...]`.) - -## Streaming and human-in-the-loop - -`runtime.start(...)` returns an [`AgentHandle`](api-reference.md#agenthandle); iterate -`handle.stream()` for [`AgentEvent`](api-reference.md#agentevent)s. When a tool needs -human approval (`@tool(approval_required=True)`) or input (`human_tool`), the stream -emits a `WAITING` event and the workflow pauses. - -```python -from conductor.ai.agents import Agent, AgentRuntime, EventType, tool - -@tool(approval_required=True) -def transfer_funds(from_acct: str, to_acct: str, amount: float) -> dict: - """Transfer money; pauses for human approval first.""" - return {"status": "completed", "amount": amount} - -agent = Agent(name="banker", model="openai/gpt-4o", tools=[transfer_funds]) - -with AgentRuntime() as runtime: - handle = runtime.start(agent, "Transfer $500 from ACC-1 to ACC-2.") - for event in handle.stream(): - if event.type == EventType.TOOL_CALL: - print("tool_call", event.tool_name, event.args) - elif event.type == EventType.WAITING: - handle.approve() # or handle.reject("not authorized") - elif event.type == EventType.DONE: - print("done:", event.output) -``` - -HITL methods on the handle (and on a stream): - -- `approve(*, event=None)` — approve the pending tool call. -- `reject(reason="", *, event=None)` — reject it. -- `respond(output, *, event=None)` — answer a `human_tool` with arbitrary fields. -- `send(message, *, event=None)` — push a message to a waiting (multi-turn) agent. - -Pass `event=` to target a specific pending pause when more than one -is in flight (event-targeted approval): - -```python -for event in handle.stream(): - if event.type == EventType.WAITING: - handle.approve(event=event) # approve exactly this pending call -``` - -`runtime.run(agent, prompt, on_event=callback)` runs synchronously while streaming -events to `callback`. Async variants: `runtime.stream_async`, `await handle.approve_async(...)`, -`handle.stream_async()`. - -`EventType` values: `THINKING`, `TOOL_CALL`, `TOOL_RESULT`, `HANDOFF`, `WAITING`, -`MESSAGE`, `ERROR`, `DONE`, `GUARDRAIL_PASS`, `GUARDRAIL_FAIL`. - -## Schedules - -Run an agent on a cron schedule. Define `Schedule`s and attach them at deploy time, or -manage them through the schedule client. - -```python -from conductor.ai.agents import AgentRuntime, Schedule - -nightly = Schedule(name="nightly", cron="0 0 * * *", timezone="UTC", - input={"prompt": "Summarize today's tickets."}) - -with AgentRuntime() as runtime: - runtime.deploy(agent, schedules=[nightly]) # upsert these, prune the rest -``` - -`schedules=[]` purges all schedules for the agent; omitting `schedules` leaves them -untouched. The schedule lifecycle client (`runtime.schedules_client()` or -`runtime.client.schedules`) exposes `pause`, `resume`, `delete`, `run_now`, -`preview_next`, `reconcile`, plus the native `get_schedule`/`save_schedule`/ -`get_all_schedules` for reads, writes, and lists. See -[Advanced](advanced.md) and the [API reference](api-reference.md#schedule). - -## Agents from a class - -`Agent.from_instance(obj)` turns `@agent`-decorated **methods** on an object into -agents — handy for dependency injection and grouping related agents, tools, and -guardrails on one class. `@tool` and `@guardrail` methods on the same instance are -auto-attached (bound to `self`). - -```python -from conductor.ai.agents import Agent, agent, tool - -class Support: - def __init__(self, db): - self.db = db - - @tool - def lookup(self, order_id: str) -> dict: - """Look up an order.""" - return self.db.get(order_id) - - @agent(model="openai/gpt-4o") - def triage(self): - """Triage the request and answer using the lookup tool.""" - -support = Support(db=my_db) - -one = Agent.from_instance(support, "triage") # a single Agent by name -allg = Agent.from_instance(support) # list[Agent], one per @agent method -``` - -Sub-agents can be referenced by method name as strings in the `@agent`'s `agents=` -list; they resolve against sibling `@agent` methods (cycles raise). A method returning -a string provides dynamic instructions; returning an `Agent` makes it a factory. - -## Stateful agents - -Set `stateful=True` to scope the agent's (and its tools') worker tasks to a per-run -domain so state isn't shared across concurrent executions. Use it when a tool holds -per-execution state. - -```python -agent = Agent(name="session_agent", model="openai/gpt-4o", - tools=[remember], stateful=True) -``` - -For conversational continuity across `run` calls, pass a `session_id`: - -```python -runtime.run(agent, "My name is Ada.", session_id="user-42") -runtime.run(agent, "What's my name?", session_id="user-42") -``` +- [Agents](concepts/agents.md) and [tools](concepts/tools.md) +- [Multi-agent strategies](concepts/multi-agent.md), [guardrails](concepts/guardrails.md), and [termination](concepts/termination.md) +- [Streaming/HITL](concepts/streaming-hitl.md), [stateful agents](concepts/stateful.md), and [scheduling](concepts/scheduling.md) +- [Agent API map](reference/api.md) diff --git a/docs/api-map.md b/docs/api-map.md new file mode 100644 index 000000000..7a0a3cbb8 --- /dev/null +++ b/docs/api-map.md @@ -0,0 +1,15 @@ +# Core API map + +| Need | Python type | Reference | +|---|---|---| +| Configure transport and auth | `Configuration` / `OrkesClients` | [connection/authentication](connection-authentication.md) | +| Run workflow executions | `WorkflowExecutor` / `WorkflowClient` | [WORKFLOW.md](WORKFLOW.md) | +| Poll and update tasks | `TaskHandler` / `TaskClient` | [TASK_MANAGEMENT.md](TASK_MANAGEMENT.md) | +| Manage definitions | `MetadataClient` | [METADATA.md](METADATA.md) | +| Schedule workflows | `SchedulerClient` | [SCHEDULE.md](SCHEDULE.md) | +| Manage schemas | `SchemaClient` | [schema client](schema-client.md) | +| Manage secrets and integrations | `SecretClient` / `IntegrationClient` | [security](security.md) | +| Compile, deploy, run, signal agents | `AgentClient` / `AgentRuntime` | [agent control plane](agents/reference/client.md) | + +Python does not currently expose a public workflow-scoped `FileClient`; use the +server and task capabilities appropriate to your deployment instead. diff --git a/docs/compatibility.md b/docs/compatibility.md new file mode 100644 index 000000000..dbcfba33d --- /dev/null +++ b/docs/compatibility.md @@ -0,0 +1,24 @@ +# Compatibility matrix + +| Area | Supported baseline | Notes | +|---|---|---| +| Python SDK | Python 3.10+ | Defined by `pyproject.toml`. | +| OSS Conductor | Supported server deployment | Test the target server during upgrades. | +| Orkes | Supported tenant API | Enterprise features depend on tenant permissions. | +| Conductor agents | Server agent runtime and provider integration | Provider credentials live on the server. | + +The Python SDK does not currently provide Java's `FileClient` or Spring Boot +modules. This documentation does not present either as available Python support. + +## Workflow-scoped files + +The Python SDK does not currently expose a public workflow-scoped `FileClient`. +Use task-appropriate object storage or a server capability selected by your +deployment; do not copy Java `FileClient` examples into Python applications. + +## Spring framework integration + +Spring and Spring Boot modules are Java-specific. Host Python workers and +Conductor-agent services in the application framework your deployment uses; see +the [FastAPI worker example](../examples/fastapi_worker_service.py) and +[deployment/scaling](deployment-scaling.md). diff --git a/docs/connection-authentication.md b/docs/connection-authentication.md new file mode 100644 index 000000000..0fdd2df40 --- /dev/null +++ b/docs/connection-authentication.md @@ -0,0 +1,19 @@ +# Connection and authentication + +`Configuration()` reads `CONDUCTOR_SERVER_URL`, then defaults to +`http://localhost:8080/api`. It reads `CONDUCTOR_AUTH_KEY` and +`CONDUCTOR_AUTH_SECRET` together when the server requires key/secret auth. + +```python +from conductor.client.configuration.configuration import Configuration + +config = Configuration() +print(config.host) +``` + +**OSS:** a local development server may allow anonymous access. **Orkes:** use +the tenant API endpoint and an application access key. Never put credentials in +workflow inputs, agent prompts, or source control. + +If requests fail, verify that the URL ends in `/api`, the server is reachable, +and the credentials belong to that endpoint. Next: [core quickstart](core-quickstart.md). diff --git a/docs/core-quickstart.md b/docs/core-quickstart.md new file mode 100644 index 000000000..f86b2cca4 --- /dev/null +++ b/docs/core-quickstart.md @@ -0,0 +1,17 @@ +# Core workflow and worker quickstart + +**Prerequisites:** Python 3.10+, `pip install conductor-python`, and a reachable +Conductor server from [server setup](server-setup.md). + +Run the maintained example from its directory so its sibling workflow module is +importable: + +```shell +cd examples/helloworld +python helloworld.py +``` + +Expected result: the workflow completes and prints `workflow result: Hello World`. +The example registers its workflow, starts a local worker, executes the workflow, +and stops the worker. If the task stays scheduled, verify that the worker is +polling the exact task type. Continue with [workers](workers.md) or [workflows](workflows.md). diff --git a/docs/debugging.md b/docs/debugging.md new file mode 100644 index 000000000..effd5425a --- /dev/null +++ b/docs/debugging.md @@ -0,0 +1,15 @@ +# Debugging incidents + +Start with safe evidence: workflow ID, task reference, status, retry count, and +`reasonForIncompletion`. Confirm server reachability and authentication before +changing application code. + +| Symptom | First check | +|---|---| +| Connection error | `CONDUCTOR_SERVER_URL` includes `/api` and the server is healthy. | +| Task remains scheduled | A worker polls the exact task type and domain. | +| Authentication failure | `CONDUCTOR_AUTH_KEY` and `CONDUCTOR_AUTH_SECRET` target the active server. | +| Agent cannot call a model | The provider credential is configured on the server. | + +Use [WORKFLOW.md](WORKFLOW.md) and [agents/reference/client.md](agents/reference/client.md) +for the relevant inspection APIs. diff --git a/docs/deployment-scaling.md b/docs/deployment-scaling.md new file mode 100644 index 000000000..309c5d0f7 --- /dev/null +++ b/docs/deployment-scaling.md @@ -0,0 +1,9 @@ +# Deployment, scaling, and graceful shutdown + +Run workers and `AgentRuntime.serve()` processes as long-lived services. Scale by +adding worker instances and use task domains or queue limits to isolate workloads. +On shutdown, stop task handlers and runtimes so in-flight tasks can be redelivered +instead of being abandoned. + +Do not construct a new runtime per web request. Reuse clients and runtimes for the +application lifetime; use [reliability](reliability.md) for timeout and retry policy. diff --git a/docs/design/AGENT_SDK_PORTING_SPEC.md b/docs/design/AGENT_SDK_PORTING_SPEC.md index 467497924..3b98295ac 100644 --- a/docs/design/AGENT_SDK_PORTING_SPEC.md +++ b/docs/design/AGENT_SDK_PORTING_SPEC.md @@ -229,7 +229,7 @@ FUNCTION startInternal(agent, prompt, runSettings?, media?, sessionId?, payload = { agentConfig: configJson, prompt: prompt, sessionId: sessionId ?? "", media: media ?? [] } // optional keys, only when set: - // context, idempotencyKey, timeoutSeconds, credentials, runId + // context, idempotencyKey, timeoutSeconds, runId data = agentClient.startAgent(payload) // server: compile+register+start executionId = data["executionId"] requiredWorkers = data["requiredWorkers"] // optional set of task names @@ -620,12 +620,14 @@ open (auth-disabled) server — treat as anonymous. "context": { }, // optional "idempotencyKey": "…", // optional "timeoutSeconds": 120, // optional - "credentials": ["GH_TOKEN"], // optional — names to resolve for this run "runId": "…", // optional — stateful worker-domain routing "static_plan": { } // optional — PLAN_EXECUTE fixed plan } ``` +Credential names are local worker configuration. They populate task-definition runtime metadata +and are not part of the agent start request or workflow input. + ### 3.4 `agentConfig` LLM wire keys (subset relevant to RunSettings) Top level of the serialized agent config: diff --git a/docs/documentation-parity.md b/docs/documentation-parity.md new file mode 100644 index 000000000..df121203b --- /dev/null +++ b/docs/documentation-parity.md @@ -0,0 +1,20 @@ +# Java/Python documentation parity + +The Python SDK follows the same documentation information architecture as the +Java SDK while mapping instructions to the Python public API. This page makes the +intentional differences explicit so a missing page is not mistaken for support. + +| Java documentation capability | Python documentation counterpart | Status | +|---|---|---| +| Server, connection, quickstart, workflows, workers, lifecycle, testing | [Core guides](README.md#build) | Supported with Python APIs | +| Schema client, schedules/events, reliability, security, deployment, observability, debugging | [Operations guides](README.md#operate) | Supported where the target server exposes the capability | +| Agent concepts, runtime, API/client, definition contract | [Conductor agent guide](agents/README.md) | Supported with Python APIs | +| Google ADK and framework bridges | [Framework bridges](agents/README.md#framework-bridges) | Python includes Google ADK, LangChain, LangGraph, OpenAI Agents, and Claude Agent SDK bridges | +| Workflow-scoped FileClient | [Compatibility](compatibility.md#workflow-scoped-files) | Not currently exposed as a public Python client | +| Spring and Spring Boot integration | [Compatibility](compatibility.md#spring-framework-integration) | Java-specific; use Python application hosting patterns instead | + +## Maintenance rule + +When adding a Java-style guide or a Python capability, update this map and the +documentation hub in the same change. Do not claim a Java-only client or framework +feature is available in Python. diff --git a/docs/documentation-standard.md b/docs/documentation-standard.md new file mode 100644 index 000000000..2cd6ad756 --- /dev/null +++ b/docs/documentation-standard.md @@ -0,0 +1,11 @@ +# Documentation standard + +Every primary Python SDK guide must include its audience and prerequisites, an +OSS/Orkes capability label when behavior differs, and a security note when it +handles credentials, user data, tools, or external side effects. + +Commands must be runnable or marked **Fragment** and linked to a complete +repository example. State the expected result, common failure modes, cleanup, +and next steps. Use `conductor-python` and published PyPI versions rather than +stale pinned versions. CI validates internal Markdown links and curated example +paths. diff --git a/docs/examples.md b/docs/examples.md new file mode 100644 index 000000000..c2c14960d --- /dev/null +++ b/docs/examples.md @@ -0,0 +1,14 @@ +# Recommended examples + +The [examples catalog](../examples/README.md) is broad; start with these maintained +paths and review their side effects before using real credentials. + +| Path | Use | Expected result | +|---|---|---| +| [Hello World](../examples/helloworld/helloworld.py) | Core workflow and worker | Prints a completed workflow result. | +| [Basic agent](../examples/agents/01_basic_agent.py) | First Conductor agent | Prints an `AgentResult`. | +| [Plan and compile](../examples/agents/103_plan_and_compile.py) | Inspect an agent workflow | Prints the compiled plan/workflow shape. | +| [Agent quickstart](../examples/agents/quickstart/README.md) | Focused agent patterns | Runs the selected quickstart script. | + +Stop local servers and worker processes after experimenting. Provider credentials +must be configured on the Conductor server. diff --git a/docs/observability.md b/docs/observability.md new file mode 100644 index 000000000..12d28d4b1 --- /dev/null +++ b/docs/observability.md @@ -0,0 +1,8 @@ +# Metrics and logging + +Use SDK logging and the metrics collectors to observe polling, task execution, +workflow latency, retries, and failures. Configure log level with +`CONDUCTOR_LOG_LEVEL`; use `METRICS.md` for metric names and Prometheus setup. + +For agent executions, inspect the shared workflow record for inputs, outputs, tool +calls, retries, and status. Avoid logging credentials or unredacted sensitive data. diff --git a/docs/reliability.md b/docs/reliability.md new file mode 100644 index 000000000..91a608f4c --- /dev/null +++ b/docs/reliability.md @@ -0,0 +1,9 @@ +# Reliability: timeouts, retries, idempotency, and domains + +Set poll, response, and execution timeouts on every worker task definition. Retry +only idempotent or compensated work, use exponential backoff for transient remote +failures, and route resource-bound work to domains. + +Workers may receive a task more than once. Persist idempotency keys before external +side effects and use `TaskInProgress` or lease extension for long-running work. +Verify behavior with failure-path tests and inspect task reasons before retrying. diff --git a/docs/schedules-events.md b/docs/schedules-events.md new file mode 100644 index 000000000..7aa5d0cda --- /dev/null +++ b/docs/schedules-events.md @@ -0,0 +1,13 @@ +# Schedules and events + +Use `SchedulerClient` for workflow schedules and the event client for event-driven +integration. Give scheduled executions a stable correlation or idempotency key so +retries do not duplicate business effects. + +```python +scheduler = clients.get_scheduler_client() +# scheduler.save_schedule(...) +``` + +See [SCHEDULE.md](SCHEDULE.md) for the complete schedule request model and +[workflow lifecycle](workflow-lifecycle.md) for safe operational handling. diff --git a/docs/schema-client.md b/docs/schema-client.md new file mode 100644 index 000000000..6496f0ff6 --- /dev/null +++ b/docs/schema-client.md @@ -0,0 +1,9 @@ +# Schema client + +`SchemaClient` manages versioned schema definitions through the Conductor schema +API. Obtain it from `OrkesClients` when the connected server exposes the schema +service, then save, fetch, list, or delete schemas. + +**OSS/Orkes:** availability depends on the server deployment and permissions. +Validate schemas in a non-production environment before making them required by +workers. The generated request and model types are listed in [api-map](api-map.md). diff --git a/docs/security.md b/docs/security.md new file mode 100644 index 000000000..4c4e3743a --- /dev/null +++ b/docs/security.md @@ -0,0 +1,9 @@ +# Security and secrets + +Keep API keys, provider credentials, and signing secrets in the Conductor server +or its configured secret provider. Do not put them in workflow inputs, agent +prompts, task output, example source, or version control. + +Agent tools declare required credentials; a capable server delivers resolved values +only in task runtime metadata. Missing credentials fail before tool execution. +See [SECRET_MANAGEMENT.md](SECRET_MANAGEMENT.md) and [agent tools](agents/concepts/tools.md). diff --git a/docs/server-setup.md b/docs/server-setup.md new file mode 100644 index 000000000..e9480120c --- /dev/null +++ b/docs/server-setup.md @@ -0,0 +1,31 @@ +# Connect the Python SDK to a Conductor server + +**Audience:** developers running workflows or Conductor agents locally or against a hosted cluster. + +## Hosted or remote server + +Set the API URL, including `/api`, then add credentials only when the target +requires them: + +```shell +export CONDUCTOR_SERVER_URL=https://your-server.example/api +export CONDUCTOR_AUTH_KEY= +export CONDUCTOR_AUTH_SECRET= +``` + +## Local development + +The Conductor CLI is the preferred local path: + +```shell +conductor server start +conductor server status +export CONDUCTOR_SERVER_URL=http://localhost:8080/api +``` + +Docker is an alternative: `docker run --rm -p 8080:8080 conductoross/conductor:latest`. +Stop CLI-managed servers with `conductor server stop` and containers with +`docker stop `. + +For agent runs, configure the LLM provider credential on the server before +starting it. Continue with [connection/authentication](connection-authentication.md). diff --git a/docs/upgrading.md b/docs/upgrading.md new file mode 100644 index 000000000..ff6db7792 --- /dev/null +++ b/docs/upgrading.md @@ -0,0 +1,10 @@ +# Upgrade the Python SDK safely + +Before upgrading, read the release notes, test against the target Conductor +server, and pin the new package version in a staging environment. Run unit tests +and one representative workflow and agent execution before production rollout. + +The canonical agent configuration names use `CONDUCTOR_AGENT_*`. Legacy +configuration aliases continue to work for compatibility, but new deployments +should use the canonical names. Roll back by restoring the prior package lock and +keeping workflow definitions versioned rather than changing active behavior in place. diff --git a/docs/workers.md b/docs/workers.md new file mode 100644 index 000000000..dc77530d5 --- /dev/null +++ b/docs/workers.md @@ -0,0 +1,17 @@ +# Workers + +Workers poll a named task queue, execute idempotent business logic, and return a +result. Define a function worker with `@worker_task`, then run it with +`TaskHandler` or `AsyncTaskRunner`. + +```python +from conductor.client.worker.worker_task import worker_task + +@worker_task(task_definition_name="greet") +def greet(name: str) -> dict: + return {"result": f"Hello {name}"} +``` + +Use explicit retries, task timeouts, and domains for production isolation. Stop +task handlers during application shutdown. See the detailed [worker guide](WORKER.md), +[reliability](reliability.md), and [workflow testing](workflow-testing.md). diff --git a/docs/workflow-lifecycle.md b/docs/workflow-lifecycle.md new file mode 100644 index 000000000..3d4b75aac --- /dev/null +++ b/docs/workflow-lifecycle.md @@ -0,0 +1,11 @@ +# Workflow lifecycle and versioning + +Register a versioned workflow, start it through `WorkflowExecutor`, and inspect +its execution before changing behavior. Additive output changes are normally +safe; renamed inputs, removed outputs, and changed task references are breaking +and require a new workflow version. + +Use pause/resume for controlled maintenance, retry only transient failures, and +terminate executions with an explicit reason. Inspect failed tasks before retrying +to avoid replaying unsafe side effects. See [WORKFLOW.md](WORKFLOW.md) for the +complete lifecycle API. diff --git a/docs/workflow-testing.md b/docs/workflow-testing.md new file mode 100644 index 000000000..4f070b93a --- /dev/null +++ b/docs/workflow-testing.md @@ -0,0 +1,13 @@ +# Workflow testing + +Use the SDK workflow-test utilities to validate definitions with controlled task +outputs before deploying. Cover successful paths, retryable and terminal failures, +branching, timeouts, and workflow outputs. + +```shell +python -m pytest tests/unit +``` + +The maintained examples and detailed API are in [WORKFLOW_TESTING.md](WORKFLOW_TESTING.md). +Do not run examples with production credentials unless their external side effects +have been reviewed. diff --git a/docs/workflows.md b/docs/workflows.md new file mode 100644 index 000000000..5edebc2f8 --- /dev/null +++ b/docs/workflows.md @@ -0,0 +1,18 @@ +# Workflows + +Use `ConductorWorkflow` to build a workflow definition and `WorkflowExecutor` +to register, start, inspect, pause, resume, retry, or terminate executions. + +```python +from conductor.client.workflow.conductor_workflow import ConductorWorkflow + +workflow = ConductorWorkflow(name="greetings", version=1, executor=executor) +workflow >> greet(task_ref_name="greet", name=workflow.input("name")) +workflow.output_parameters({"result": "${greet.output.result}"}) +workflow.register(overwrite=True) +``` + +Use system task classes for HTTP, wait, switch, fork/join, sub-workflow, and +other orchestration primitives. See the detailed [workflow API guide](WORKFLOW.md) +and [workflow lifecycle](workflow-lifecycle.md). Keep versioned definitions +compatible with callers; never place secrets in workflow input. diff --git a/examples/agents/01_basic_agent.py b/examples/agents/01_basic_agent.py index dfbd8e65f..45b2ba108 100644 --- a/examples/agents/01_basic_agent.py +++ b/examples/agents/01_basic_agent.py @@ -7,9 +7,9 @@ ``runtime.run()``, and print the result. Requirements: - - Agentspan server with LLM support - - AGENTSPAN_SERVER_URL=http://localhost:8080/api in .env or environment - - AGENTSPAN_LLM_MODEL set in .env or environment (optional) + - Conductor server with LLM support + - CONDUCTOR_SERVER_URL=http://localhost:8080/api in .env or environment + - CONDUCTOR_AGENT_LLM_MODEL set in .env or environment (optional) """ from conductor.ai.agents import Agent, AgentRuntime @@ -33,7 +33,7 @@ # 1. Deploy once during CI/CD: # runtime.deploy(agent) # CLI alternative: - # agentspan deploy --package examples.01_basic_agent + # runtime.deploy(agent) from a release script # # 2. In a separate long-lived worker process: # runtime.serve(agent) diff --git a/examples/agents/02_tools.py b/examples/agents/02_tools.py index fd7c997b0..9dbbedc50 100644 --- a/examples/agents/02_tools.py +++ b/examples/agents/02_tools.py @@ -10,8 +10,8 @@ Requirements: - Conductor server with LLM support - - AGENTSPAN_SERVER_URL=http://localhost:8080/api as environment variable - - AGENTSPAN_LLM_MODEL=openai/gpt-4o-mini as environment variable + - CONDUCTOR_SERVER_URL=http://localhost:8080/api as environment variable + - CONDUCTOR_AGENT_LLM_MODEL=openai/gpt-4o-mini as environment variable """ from conductor.ai.agents import Agent, AgentRuntime, EventType, tool diff --git a/examples/agents/02a_simple_tools.py b/examples/agents/02a_simple_tools.py index 0f07cf46c..dc011eca3 100644 --- a/examples/agents/02a_simple_tools.py +++ b/examples/agents/02a_simple_tools.py @@ -11,8 +11,8 @@ Requirements: - Conductor server with LLM support - - AGENTSPAN_SERVER_URL=http://localhost:8080/api as environment variable - - AGENTSPAN_LLM_MODEL=openai/gpt-4o-mini as environment variable + - CONDUCTOR_SERVER_URL=http://localhost:8080/api as environment variable + - CONDUCTOR_AGENT_LLM_MODEL=openai/gpt-4o-mini as environment variable """ from conductor.ai.agents import Agent, AgentRuntime, tool @@ -49,7 +49,7 @@ def get_stock_price(symbol: str) -> dict: # 1. Deploy once during CI/CD: # runtime.deploy(agent) # CLI alternative: - # agentspan deploy --package examples.02a_simple_tools + # runtime.deploy(agent) from a release script # # 2. In a separate long-lived worker process: # runtime.serve(agent) diff --git a/examples/agents/02b_multi_step_tools.py b/examples/agents/02b_multi_step_tools.py index dff52ae97..c093e9677 100644 --- a/examples/agents/02b_multi_step_tools.py +++ b/examples/agents/02b_multi_step_tools.py @@ -18,8 +18,8 @@ Requirements: - Conductor server with LLM support - - AGENTSPAN_SERVER_URL=http://localhost:8080/api as environment variable - - AGENTSPAN_LLM_MODEL=openai/gpt-4o-mini as environment variable + - CONDUCTOR_SERVER_URL=http://localhost:8080/api as environment variable + - CONDUCTOR_AGENT_LLM_MODEL=openai/gpt-4o-mini as environment variable """ from typing import List @@ -90,7 +90,7 @@ def send_summary_email(to: str, subject: str, body: str) -> dict: # 1. Deploy once during CI/CD: # runtime.deploy(agent) # CLI alternative: - # agentspan deploy --package examples.02b_multi_step_tools + # runtime.deploy(agent) from a release script # # 2. In a separate long-lived worker process: # runtime.serve(agent) diff --git a/examples/agents/02c_tool_retry_config.py b/examples/agents/02c_tool_retry_config.py index 9fdf23fed..6eac0336c 100644 --- a/examples/agents/02c_tool_retry_config.py +++ b/examples/agents/02c_tool_retry_config.py @@ -11,8 +11,8 @@ Requirements: - Conductor server with LLM support - - AGENTSPAN_SERVER_URL=http://localhost:8080/api as environment variable - - AGENTSPAN_LLM_MODEL=openai/gpt-4o-mini as environment variable + - CONDUCTOR_SERVER_URL=http://localhost:8080/api as environment variable + - CONDUCTOR_AGENT_LLM_MODEL=openai/gpt-4o-mini as environment variable """ from conductor.ai.agents import Agent, AgentRuntime, tool diff --git a/examples/agents/03_structured_output.py b/examples/agents/03_structured_output.py index a4fe666ac..3834a25ea 100644 --- a/examples/agents/03_structured_output.py +++ b/examples/agents/03_structured_output.py @@ -9,8 +9,8 @@ Requirements: - Conductor server with LLM support - pydantic installed - - AGENTSPAN_SERVER_URL=http://localhost:8080/api as environment variable - - AGENTSPAN_LLM_MODEL=openai/gpt-4o-mini as environment variable + - CONDUCTOR_SERVER_URL=http://localhost:8080/api as environment variable + - CONDUCTOR_AGENT_LLM_MODEL=openai/gpt-4o-mini as environment variable """ from pydantic import BaseModel @@ -50,7 +50,7 @@ def get_weather(city: str) -> dict: # 1. Deploy once during CI/CD: # runtime.deploy(agent) # CLI alternative: - # agentspan deploy --package examples.03_structured_output + # runtime.deploy(agent) from a release script # # 2. In a separate long-lived worker process: # runtime.serve(agent) diff --git a/examples/agents/04_http_and_mcp_tools.py b/examples/agents/04_http_and_mcp_tools.py index 3c569ed41..70e9bc24e 100644 --- a/examples/agents/04_http_and_mcp_tools.py +++ b/examples/agents/04_http_and_mcp_tools.py @@ -19,15 +19,15 @@ # Or start with auth (requires storing the secret as a credential): mcp-testkit --transport http --auth - # Store credentials via CLI or Agentspan UI: - agentspan credentials set HTTP_TEST_API_KEY - agentspan credentials set MCP_TEST_API_KEY + # Store credentials via CLI or Conductor UI: + the Conductor server credential store + the Conductor server credential store Requirements: - Conductor server with LLM support - mcp-testkit running on http://localhost:3001 (see setup above) - - AGENTSPAN_SERVER_URL=http://localhost:8080/api as environment variable - - AGENTSPAN_LLM_MODEL=openai/gpt-4o-mini as environment variable + - CONDUCTOR_SERVER_URL=http://localhost:8080/api as environment variable + - CONDUCTOR_AGENT_LLM_MODEL=openai/gpt-4o-mini as environment variable """ from conductor.ai.agents import Agent, AgentRuntime, tool, http_tool, mcp_tool @@ -92,7 +92,7 @@ def format_report(title: str, body: str) -> dict: # 1. Deploy once during CI/CD: # runtime.deploy(agent) # CLI alternative: - # agentspan deploy --package examples.04_http_and_mcp_tools + # runtime.deploy(agent) from a release script # # 2. In a separate long-lived worker process: # runtime.serve(agent) diff --git a/examples/agents/04_mcp_weather.py b/examples/agents/04_mcp_weather.py index 2e9417cbc..b0646c0b5 100644 --- a/examples/agents/04_mcp_weather.py +++ b/examples/agents/04_mcp_weather.py @@ -20,17 +20,17 @@ # Or start with auth (requires storing the secret as a credential): mcp-testkit --transport http --auth - # Store credentials via CLI or Agentspan UI: - agentspan credentials set MCP_TEST_API_KEY + # Store credentials via CLI or Conductor UI: + the Conductor server credential store Requirements: - Conductor server with LLM support - mcp-testkit running on http://localhost:3001 (see setup above) - - AGENTSPAN_SERVER_URL=http://localhost:8080/api as environment variable - - AGENTSPAN_LLM_MODEL=openai/gpt-4o-mini as environment variable + - CONDUCTOR_SERVER_URL=http://localhost:8080/api as environment variable + - CONDUCTOR_AGENT_LLM_MODEL=openai/gpt-4o-mini as environment variable Docker gotcha: - When the AgentSpan server runs in Docker (e.g. `agentspan server start`), + When the Conductor server runs in Docker (e.g. `Conductor server start`), the *server* makes the MCP calls — not your local process. The server resolves `localhost` to its own container loopback, not your host machine. @@ -84,7 +84,7 @@ # 1. Deploy once during CI/CD: # runtime.deploy(agent) # CLI alternative: - # agentspan deploy --package examples.04_mcp_weather + # runtime.deploy(agent) from a release script # # 2. In a separate long-lived worker process: # runtime.serve(agent) diff --git a/examples/agents/05_handoffs.py b/examples/agents/05_handoffs.py index 9a2712d9e..aebc9fd20 100644 --- a/examples/agents/05_handoffs.py +++ b/examples/agents/05_handoffs.py @@ -8,8 +8,8 @@ Requirements: - Conductor server with LLM support - - AGENTSPAN_SERVER_URL=http://localhost:8080/api as environment variable - - AGENTSPAN_LLM_MODEL=openai/gpt-4o-mini as environment variable + - CONDUCTOR_SERVER_URL=http://localhost:8080/api as environment variable + - CONDUCTOR_AGENT_LLM_MODEL=openai/gpt-4o-mini as environment variable """ from conductor.ai.agents import Agent, AgentRuntime, Strategy, tool @@ -79,7 +79,7 @@ def get_pricing(product: str) -> dict: # 1. Deploy once during CI/CD: # runtime.deploy(support) # CLI alternative: - # agentspan deploy --package examples.05_handoffs + # runtime.deploy(agent) from a release script # # 2. In a separate long-lived worker process: # runtime.serve(support) diff --git a/examples/agents/06_sequential_pipeline.py b/examples/agents/06_sequential_pipeline.py index 90bd004cc..3375074b9 100644 --- a/examples/agents/06_sequential_pipeline.py +++ b/examples/agents/06_sequential_pipeline.py @@ -10,8 +10,8 @@ Requirements: - Conductor server with LLM support - - AGENTSPAN_SERVER_URL=http://localhost:8080/api as environment variable - - AGENTSPAN_LLM_MODEL=openai/gpt-4o-mini as environment variable + - CONDUCTOR_SERVER_URL=http://localhost:8080/api as environment variable + - CONDUCTOR_AGENT_LLM_MODEL=openai/gpt-4o-mini as environment variable """ from conductor.ai.agents import Agent, AgentRuntime, Strategy @@ -60,7 +60,7 @@ # 1. Deploy once during CI/CD: # runtime.deploy(pipeline) # CLI alternative: - # agentspan deploy --package examples.06_sequential_pipeline + # runtime.deploy(agent) from a release script # # 2. In a separate long-lived worker process: # runtime.serve(pipeline) diff --git a/examples/agents/07_parallel_agents.py b/examples/agents/07_parallel_agents.py index 98366c512..471767ec2 100644 --- a/examples/agents/07_parallel_agents.py +++ b/examples/agents/07_parallel_agents.py @@ -8,8 +8,8 @@ Requirements: - Conductor server with LLM support - - AGENTSPAN_SERVER_URL=http://localhost:8080/api as environment variable - - AGENTSPAN_LLM_MODEL=openai/gpt-4o-mini as environment variable + - CONDUCTOR_SERVER_URL=http://localhost:8080/api as environment variable + - CONDUCTOR_AGENT_LLM_MODEL=openai/gpt-4o-mini as environment variable """ from conductor.ai.agents import Agent, AgentRuntime, Strategy @@ -63,7 +63,7 @@ # 1. Deploy once during CI/CD: # runtime.deploy(analysis) # CLI alternative: - # agentspan deploy --package examples.07_parallel_agents + # runtime.deploy(agent) from a release script # # 2. In a separate long-lived worker process: # runtime.serve(analysis) diff --git a/examples/agents/08_router_agent.py b/examples/agents/08_router_agent.py index 0eb06c8a7..8c19fdcbf 100644 --- a/examples/agents/08_router_agent.py +++ b/examples/agents/08_router_agent.py @@ -17,8 +17,8 @@ Requirements: - Conductor server with LLM support - - AGENTSPAN_SERVER_URL=http://localhost:8080/api as environment variable - - AGENTSPAN_LLM_MODEL=openai/gpt-4o-mini as environment variable + - CONDUCTOR_SERVER_URL=http://localhost:8080/api as environment variable + - CONDUCTOR_AGENT_LLM_MODEL=openai/gpt-4o-mini as environment variable """ from conductor.ai.agents import Agent, AgentRuntime, Strategy @@ -77,7 +77,7 @@ # 1. Deploy once during CI/CD: # runtime.deploy(team) # CLI alternative: - # agentspan deploy --package examples.08_router_agent + # runtime.deploy(agent) from a release script # # 2. In a separate long-lived worker process: # runtime.serve(team) diff --git a/examples/agents/09_human_in_the_loop.py b/examples/agents/09_human_in_the_loop.py index efefc6875..44ca8df29 100644 --- a/examples/agents/09_human_in_the_loop.py +++ b/examples/agents/09_human_in_the_loop.py @@ -9,8 +9,8 @@ Requirements: - Conductor server with LLM support - - AGENTSPAN_SERVER_URL=http://localhost:8080/api as environment variable - - AGENTSPAN_LLM_MODEL=openai/gpt-4o-mini as environment variable + - CONDUCTOR_SERVER_URL=http://localhost:8080/api as environment variable + - CONDUCTOR_AGENT_LLM_MODEL=openai/gpt-4o-mini as environment variable """ from conductor.ai.agents import Agent, AgentRuntime, EventType, tool diff --git a/examples/agents/09b_hitl_with_feedback.py b/examples/agents/09b_hitl_with_feedback.py index 02b083624..988c3901d 100644 --- a/examples/agents/09b_hitl_with_feedback.py +++ b/examples/agents/09b_hitl_with_feedback.py @@ -14,8 +14,8 @@ Requirements: - Conductor server with LLM support - - AGENTSPAN_SERVER_URL=http://localhost:8080/api as environment variable - - AGENTSPAN_LLM_MODEL=openai/gpt-4o-mini as environment variable + - CONDUCTOR_SERVER_URL=http://localhost:8080/api as environment variable + - CONDUCTOR_AGENT_LLM_MODEL=openai/gpt-4o-mini as environment variable """ from conductor.ai.agents import Agent, AgentRuntime, EventType, tool diff --git a/examples/agents/09c_hitl_streaming.py b/examples/agents/09c_hitl_streaming.py index a3d41eef2..babb4c3f8 100644 --- a/examples/agents/09c_hitl_streaming.py +++ b/examples/agents/09c_hitl_streaming.py @@ -13,8 +13,8 @@ Requirements: - Conductor server with LLM support - - AGENTSPAN_SERVER_URL=http://localhost:8080/api as environment variable - - AGENTSPAN_LLM_MODEL=openai/gpt-4o-mini as environment variable + - CONDUCTOR_SERVER_URL=http://localhost:8080/api as environment variable + - CONDUCTOR_AGENT_LLM_MODEL=openai/gpt-4o-mini as environment variable """ from conductor.ai.agents import Agent, AgentRuntime, EventType, tool diff --git a/examples/agents/09d_human_tool.py b/examples/agents/09d_human_tool.py index d672f4377..a06ca8bfe 100644 --- a/examples/agents/09d_human_tool.py +++ b/examples/agents/09d_human_tool.py @@ -19,8 +19,8 @@ - The LLM using human input to make decisions Requirements: - - AGENTSPAN_SERVER_URL=http://localhost:8080/api - - AGENTSPAN_LLM_MODEL (default: openai/gpt-4o-mini) + - CONDUCTOR_SERVER_URL=http://localhost:8080/api + - CONDUCTOR_AGENT_LLM_MODEL (default: openai/gpt-4o-mini) """ from settings import settings diff --git a/examples/agents/100_issue_fixer_agent.py b/examples/agents/100_issue_fixer_agent.py index 467724f40..d09bacc55 100644 --- a/examples/agents/100_issue_fixer_agent.py +++ b/examples/agents/100_issue_fixer_agent.py @@ -23,8 +23,8 @@ python 100_issue_fixer_agent.py 42 Requirements: - - Agentspan server running - - GH_TOKEN: agentspan credentials set GH_TOKEN + - Conductor server running + - GH_TOKEN: the Conductor server credential store - gh CLI installed and authenticated - DG skill: git clone https://github.com/v1r3n/dinesh-gilfoyle ~/.claude/skills/dg - Full build toolchain (Go, Java 21, Python 3.10+, Node.js, pnpm, uv) @@ -52,7 +52,7 @@ ) # ── Project-Specific Configuration ──────────────────────────── -REPO = "agentspan-ai/agentspan" +REPO = "Conductor-ai/Conductor" REPO_URL = f"https://github.com/{REPO}" BRANCH_PREFIX = "fix/issue-" @@ -461,7 +461,7 @@ def main(): pr_number = args.pr # Create a temp working directory with a random suffix. - work_dir = os.path.join(tempfile.gettempdir(), f"agentspan-fix-{uuid.uuid4().hex[:12]}") + work_dir = os.path.join(tempfile.gettempdir(), f"Conductor-fix-{uuid.uuid4().hex[:12]}") set_working_dir(work_dir) print(f"Working directory: {work_dir}") diff --git a/examples/agents/103_plan_and_compile.py b/examples/agents/103_plan_and_compile.py index 883b29f5e..8cd58a221 100644 --- a/examples/agents/103_plan_and_compile.py +++ b/examples/agents/103_plan_and_compile.py @@ -17,13 +17,13 @@ - a ``validation`` block with a sandboxed success_condition Usage: - AGENTSPAN_SERVER_URL=http://localhost:8080/api \\ + CONDUCTOR_SERVER_URL=http://localhost:8080/api \\ OPENAI_API_KEY=... \\ python 103_plan_and_compile.py "Compute factorials of 1..5 and explain" Requirements: - - Agentspan server running with PLAN_AND_COMPILE registered - - OPENAI_API_KEY (or whichever provider matches AGENTSPAN_LLM_MODEL) + - Conductor server running with PLAN_AND_COMPILE registered + - OPENAI_API_KEY (or whichever provider matches CONDUCTOR_AGENT_LLM_MODEL) """ import math @@ -36,7 +36,7 @@ from settings import settings -SERVER_URL = os.environ.get("AGENTSPAN_SERVER_URL", "http://localhost:8080/api") +SERVER_URL = os.environ.get("CONDUCTOR_SERVER_URL", "http://localhost:8080/api") CONDUCTOR_BASE = SERVER_URL.rstrip("/").replace("/api", "") diff --git a/examples/agents/104_plan_execute_guardrails.py b/examples/agents/104_plan_execute_guardrails.py index b08df8734..d57af0c60 100644 --- a/examples/agents/104_plan_execute_guardrails.py +++ b/examples/agents/104_plan_execute_guardrails.py @@ -20,13 +20,13 @@ deterministic plan, and the harness's ``fallback`` agent recovers. Run: - AGENTSPAN_SERVER_URL=http://localhost:8080/api \\ + CONDUCTOR_SERVER_URL=http://localhost:8080/api \\ OPENAI_API_KEY=... \\ python 104_plan_execute_guardrails.py [topic] Requirements: - - Agentspan server running with PLAN_AND_COMPILE - - OPENAI_API_KEY (or matching provider for AGENTSPAN_LLM_MODEL) + - Conductor server running with PLAN_AND_COMPILE + - OPENAI_API_KEY (or matching provider for CONDUCTOR_AGENT_LLM_MODEL) """ import os @@ -46,7 +46,7 @@ from settings import settings -SERVER_URL = os.environ.get("AGENTSPAN_SERVER_URL", "http://localhost:8080/api") +SERVER_URL = os.environ.get("CONDUCTOR_SERVER_URL", "http://localhost:8080/api") CONDUCTOR_BASE = SERVER_URL.rstrip("/").replace("/api", "") diff --git a/examples/agents/106_plan_execute_agent_fanout.py b/examples/agents/106_plan_execute_agent_fanout.py index cba04be63..624e02f23 100644 --- a/examples/agents/106_plan_execute_agent_fanout.py +++ b/examples/agents/106_plan_execute_agent_fanout.py @@ -40,7 +40,7 @@ python 106_plan_execute_agent_fanout.py Requires: - - Agentspan server running (AGENTSPAN_SERVER_URL) + - Conductor server running (CONDUCTOR_SERVER_URL) - OPENAI_API_KEY (planner LLM gets called even when ``plan=`` is injected — its output is discarded but the call has to land somewhere) """ diff --git a/examples/agents/107_pac_mcp_proof.py b/examples/agents/107_pac_mcp_proof.py index 495ce47a2..c3afb63a0 100644 --- a/examples/agents/107_pac_mcp_proof.py +++ b/examples/agents/107_pac_mcp_proof.py @@ -25,8 +25,8 @@ # 1. Start mcp-testkit: uv run mcp-testkit --transport http --port 3001 - # 2. (Re)start agentspan server with the new PAC build: - kill + # 2. (Re)start Conductor server with the new PAC build: + kill cd server && ./gradlew bootRun # 3. Run this script: @@ -48,11 +48,11 @@ # ── Endpoints ───────────────────────────────────────────────────────── -AGENTSPAN_URL = os.environ.get("AGENTSPAN_SERVER_URL", "http://localhost:8080/api") -# Conductor REST runs alongside agentspan; we'll read the compiled +CONDUCTOR_URL = os.environ.get("CONDUCTOR_SERVER_URL", "http://localhost:8080/api") +# The agent runtime uses Conductor REST; read the compiled # WorkflowDef directly off Conductor to *prove* PAC emitted the right # task types (not just trust the SDK's view of execution.status). -CONDUCTOR_BASE = AGENTSPAN_URL.replace("/api", "") +CONDUCTOR_BASE = CONDUCTOR_URL.replace("/api", "") MCP_URL = "http://localhost:3001/mcp" @@ -215,7 +215,7 @@ def find_compiled_workflow_def(parent_id: str) -> tuple[str, dict]: if wd: # Read the WorkflowDef out of PAC's task output directly. # The /metadata/workflow/{name} endpoint returns only the - # placeholder agentspan registered up-front; PAC compiles + # placeholder Conductor registered up-front; PAC compiles # a fresh def per execution and emits it here. return out.get("workflowName", ""), wd sub = t.get("subWorkflowId") @@ -248,7 +248,7 @@ def main() -> int: print("=" * 70) print(" PAC end-to-end proof — PLAN_EXECUTE with toolType routing") print("=" * 70) - print(f" agentspan: {AGENTSPAN_URL}") + print(f" Conductor: {CONDUCTOR_URL}") print(f" conductor: {CONDUCTOR_BASE}") print(f" mcp: {MCP_URL}") print() diff --git a/examples/agents/108_plan_execute_refs.py b/examples/agents/108_plan_execute_refs.py index 717054e0d..e4b4a0af8 100644 --- a/examples/agents/108_plan_execute_refs.py +++ b/examples/agents/108_plan_execute_refs.py @@ -31,8 +31,8 @@ independently (two Refs in the same args map). Requirements: - - AGENTSPAN_SERVER_URL=http://localhost:8080/api (default) - - AGENTSPAN_LLM_MODEL=openai/gpt-4o-mini (default) + - CONDUCTOR_SERVER_URL=http://localhost:8080/api (default) + - CONDUCTOR_AGENT_LLM_MODEL=openai/gpt-4o-mini (default) """ from __future__ import annotations @@ -81,7 +81,7 @@ def main() -> None: name="ref_demo", tools=[produce, enrich, report], planner_instructions="(planner unused; static plan supplied)", - model=os.environ.get("AGENTSPAN_LLM_MODEL", "anthropic/claude-sonnet-4-6"), + model=os.environ.get("CONDUCTOR_AGENT_LLM_MODEL", "anthropic/claude-sonnet-4-6"), ) plan = Plan( @@ -124,7 +124,7 @@ def _show_pipeline_outputs(execution_id: str) -> None: import requests - base = os.environ.get("AGENTSPAN_SERVER_URL", "http://localhost:8080/api") + base = os.environ.get("CONDUCTOR_SERVER_URL", "http://localhost:8080/api") base_url = base.rstrip("/").replace("/api", "") parent = requests.get( diff --git a/examples/agents/109_plan_execute_replan.py b/examples/agents/109_plan_execute_replan.py index 545d7c0cb..a5b27e705 100644 --- a/examples/agents/109_plan_execute_replan.py +++ b/examples/agents/109_plan_execute_replan.py @@ -40,8 +40,8 @@ * The loop exits when the threshold is met OR ``max_iterations`` hits. Requirements: - - AGENTSPAN_SERVER_URL=http://localhost:8080/api (default) - - AGENTSPAN_LLM_MODEL=openai/gpt-4o-mini (default) + - CONDUCTOR_SERVER_URL=http://localhost:8080/api (default) + - CONDUCTOR_AGENT_LLM_MODEL=openai/gpt-4o-mini (default) - An LLM key for the chosen model (sections are generated, not static). """ @@ -346,7 +346,7 @@ def main(argv: list[str]) -> None: name="report_replan", tools=[create_directory, write_file, assemble_files, check_word_count], planner_instructions="(planner unused; plans supplied directly each iteration)", - model=os.environ.get("AGENTSPAN_LLM_MODEL", "anthropic/claude-sonnet-4-6"), + model=os.environ.get("CONDUCTOR_AGENT_LLM_MODEL", "anthropic/claude-sonnet-4-6"), ) with AgentRuntime() as runtime: diff --git a/examples/agents/10_guardrails.py b/examples/agents/10_guardrails.py index 548971ebf..609f5da26 100644 --- a/examples/agents/10_guardrails.py +++ b/examples/agents/10_guardrails.py @@ -22,8 +22,8 @@ Requirements: - Conductor server with LLM support - - AGENTSPAN_SERVER_URL=http://localhost:8080/api as environment variable - - AGENTSPAN_LLM_MODEL=openai/gpt-4o-mini as environment variable + - CONDUCTOR_SERVER_URL=http://localhost:8080/api as environment variable + - CONDUCTOR_AGENT_LLM_MODEL=openai/gpt-4o-mini as environment variable """ import re @@ -129,7 +129,7 @@ def no_pii(content: str) -> GuardrailResult: # 1. Deploy once during CI/CD: # runtime.deploy(agent) # CLI alternative: - # agentspan deploy --package examples.10_guardrails + # runtime.deploy(agent) from a release script # # 2. In a separate long-lived worker process: # runtime.serve(agent) diff --git a/examples/agents/110_plan_execute_replan_solve.py b/examples/agents/110_plan_execute_replan_solve.py index 53a3c9e59..e35e4b558 100644 --- a/examples/agents/110_plan_execute_replan_solve.py +++ b/examples/agents/110_plan_execute_replan_solve.py @@ -39,13 +39,13 @@ the LLM converges instead of repeating the same mistakes. Constraints for this demo: - 1. The sentence starts with the word "Agentspan". + 1. The sentence starts with the word "Conductor". 2. It contains all three keywords: "deterministic", "loop", "feedback". 3. It has exactly EXPECTED_WORD_COUNT words (default 20). Requirements: - - AGENTSPAN_SERVER_URL=http://localhost:8080/api (default) - - AGENTSPAN_LLM_MODEL=openai/gpt-4o-mini (default) + - CONDUCTOR_SERVER_URL=http://localhost:8080/api (default) + - CONDUCTOR_AGENT_LLM_MODEL=openai/gpt-4o-mini (default) - LLM key for the chosen model. """ @@ -63,7 +63,7 @@ CANDIDATES_PER_ITERATION = 2 MAX_ITERATIONS = 6 -EXPECTED_FIRST_WORD = "Agentspan" +EXPECTED_FIRST_WORD = "Conductor" EXPECTED_KEYWORDS = ( "deterministic", "loop", @@ -356,7 +356,7 @@ def main(argv: list[str]) -> None: name="sentence_solver", tools=[write_candidate, verify_candidates], planner_instructions="(planner unused; plans supplied directly each iteration)", - model=os.environ.get("AGENTSPAN_LLM_MODEL", "anthropic/claude-sonnet-4-6"), + model=os.environ.get("CONDUCTOR_AGENT_LLM_MODEL", "anthropic/claude-sonnet-4-6"), ) with AgentRuntime() as runtime: diff --git a/examples/agents/111_plan_execute_replan_binsearch.py b/examples/agents/111_plan_execute_replan_binsearch.py index 765744a90..c6716028b 100644 --- a/examples/agents/111_plan_execute_replan_binsearch.py +++ b/examples/agents/111_plan_execute_replan_binsearch.py @@ -35,10 +35,10 @@ see a loop running. Many times. As intended. Requirements: - - AGENTSPAN_SERVER_URL=http://localhost:8080/api (default) - - AGENTSPAN_LLM_MODEL=openai/gpt-4o-mini (default) + - CONDUCTOR_SERVER_URL=http://localhost:8080/api (default) + - CONDUCTOR_AGENT_LLM_MODEL=openai/gpt-4o-mini (default) - LLM key for the chosen model. - - AGENTSPAN_BINSEARCH_SECRET (optional override; default 642) + - CONDUCTOR_AGENT_BINSEARCH_SECRET (optional override; default 642) """ import json @@ -60,7 +60,7 @@ def _pick_secret() -> int: """Default secret is deliberately off the obvious binary-search midpoints so the LLM can't hit it on iter 0 by guessing 500. Override via env var to test convergence on other targets.""" - env = os.environ.get("AGENTSPAN_BINSEARCH_SECRET") + env = os.environ.get("CONDUCTOR_AGENT_BINSEARCH_SECRET") if env: return int(env) return 642 @@ -270,7 +270,7 @@ def main(argv: list[str]) -> None: name="binsearch", tools=[write_guess, check_guess], planner_instructions="(planner unused; plans supplied directly each iteration)", - model=os.environ.get("AGENTSPAN_LLM_MODEL", "anthropic/claude-sonnet-4-6"), + model=os.environ.get("CONDUCTOR_AGENT_LLM_MODEL", "anthropic/claude-sonnet-4-6"), ) with AgentRuntime() as runtime: diff --git a/examples/agents/112_dowhile_loop_inside_workflow.py b/examples/agents/112_dowhile_loop_inside_workflow.py index 1a2461e42..9a080083a 100644 --- a/examples/agents/112_dowhile_loop_inside_workflow.py +++ b/examples/agents/112_dowhile_loop_inside_workflow.py @@ -39,15 +39,15 @@ is under the budget. This is the shape of a *first-class* ``Strategy.PLAN_EXECUTE_REPLAN`` -that doesn't exist in Agentspan today (dg-review finding F1, +that doesn't exist in Conductor today (dg-review finding F1, recommendation #2). The example builds it by hand to show the full plan→compile→execute→replan structure end-to-end inside one workflow. Requirements: - - AGENTSPAN_SERVER_URL=http://localhost:8080/api (default) - - AGENTSPAN_LLM_MODEL=openai/gpt-4o-mini (default) + - CONDUCTOR_SERVER_URL=http://localhost:8080/api (default) + - CONDUCTOR_AGENT_LLM_MODEL=openai/gpt-4o-mini (default) - LLM key for the chosen model. - - AGENTSPAN_BINSEARCH_SECRET (optional override; default 642) + - CONDUCTOR_AGENT_BINSEARCH_SECRET (optional override; default 642) """ import json @@ -60,11 +60,11 @@ from conductor.ai.agents import AgentRuntime, plan_execute, tool -SERVER_URL = os.environ.get("AGENTSPAN_SERVER_URL", "http://localhost:8080/api") +SERVER_URL = os.environ.get("CONDUCTOR_SERVER_URL", "http://localhost:8080/api") BASE = SERVER_URL.rstrip("/").replace("/api", "") -MODEL = os.environ.get("AGENTSPAN_LLM_MODEL", "anthropic/claude-sonnet-4-6") -SECRET = int(os.environ.get("AGENTSPAN_BINSEARCH_SECRET", "642")) -MAX_ITER = int(os.environ.get("AGENTSPAN_DOWHILE_MAX_ITER", "12")) +MODEL = os.environ.get("CONDUCTOR_AGENT_LLM_MODEL", "anthropic/claude-sonnet-4-6") +SECRET = int(os.environ.get("CONDUCTOR_AGENT_BINSEARCH_SECRET", "642")) +MAX_ITER = int(os.environ.get("CONDUCTOR_AGENT_DOWHILE_MAX_ITER", "12")) WORKFLOW_NAME = "pae_replan_dowhile_demo" WORKFLOW_VERSION = 5 diff --git a/examples/agents/113_aml_sar_investigation_loop.py b/examples/agents/113_aml_sar_investigation_loop.py index 5697a5ef3..405d10f13 100644 --- a/examples/agents/113_aml_sar_investigation_loop.py +++ b/examples/agents/113_aml_sar_investigation_loop.py @@ -28,8 +28,8 @@ ``finalize_disposition``. Requirements: - - AGENTSPAN_SERVER_URL=http://localhost:8080/api (default) - - AGENTSPAN_LLM_MODEL=openai/gpt-4o-mini (default) + - CONDUCTOR_SERVER_URL=http://localhost:8080/api (default) + - CONDUCTOR_AGENT_LLM_MODEL=openai/gpt-4o-mini (default) - LLM key for the chosen model. """ @@ -43,10 +43,10 @@ from conductor.ai.agents import AgentRuntime, plan_execute, tool -SERVER_URL = os.environ.get("AGENTSPAN_SERVER_URL", "http://localhost:8080/api") +SERVER_URL = os.environ.get("CONDUCTOR_SERVER_URL", "http://localhost:8080/api") BASE = SERVER_URL.rstrip("/").replace("/api", "") -MODEL = os.environ.get("AGENTSPAN_LLM_MODEL", "anthropic/claude-sonnet-4-6") -MAX_ITER = int(os.environ.get("AGENTSPAN_AML_MAX_ITER", "10")) +MODEL = os.environ.get("CONDUCTOR_AGENT_LLM_MODEL", "anthropic/claude-sonnet-4-6") +MAX_ITER = int(os.environ.get("CONDUCTOR_AGENT_AML_MAX_ITER", "10")) WORKFLOW_NAME = "aml_sar_investigation_loop" WORKFLOW_VERSION = 5 @@ -316,7 +316,7 @@ def finalize_disposition( # Pull the JSON action out of the LLM's response. Two shapes possible: -# (a) Agentspan's LLM_CHAT_COMPLETE auto-parses a JSON-mode response, so +# (a) Conductor's LLM_CHAT_COMPLETE auto-parses a JSON-mode response, so # ``$.llm_out`` is already a Java Map. Walk it to a JS object. # (b) Plaintext path: ``$.llm_out`` is a string; regex out the JSON block. EXTRACT_ACTION_JS = TO_JS_OBJ_JS + ( @@ -727,7 +727,7 @@ def main(argv: list[str]) -> None: print(f" customer={ALERT['customer_id']}, ${ALERT['total_amount']:,} over 5 days") print(f"budget: {MAX_ITER} iterations\n") - # Register the workers via Agentspan runtime. + # Register the workers via Conductor runtime. print("setting up evidence-source workers via AgentRuntime...") harness = plan_execute( name="aml_tools_harness", diff --git a/examples/agents/114_portfolio_rebalance_loop.py b/examples/agents/114_portfolio_rebalance_loop.py index 65293283d..ef0935d37 100644 --- a/examples/agents/114_portfolio_rebalance_loop.py +++ b/examples/agents/114_portfolio_rebalance_loop.py @@ -30,8 +30,8 @@ * Termination on the iteration where submit_trades is called. Requirements: - - AGENTSPAN_SERVER_URL=http://localhost:8080/api (default) - - AGENTSPAN_LLM_MODEL=openai/gpt-4o-mini (default) + - CONDUCTOR_SERVER_URL=http://localhost:8080/api (default) + - CONDUCTOR_AGENT_LLM_MODEL=openai/gpt-4o-mini (default) - LLM key for the chosen model. """ @@ -45,10 +45,10 @@ from conductor.ai.agents import AgentRuntime, plan_execute, tool -SERVER_URL = os.environ.get("AGENTSPAN_SERVER_URL", "http://localhost:8080/api") +SERVER_URL = os.environ.get("CONDUCTOR_SERVER_URL", "http://localhost:8080/api") BASE = SERVER_URL.rstrip("/").replace("/api", "") -MODEL = os.environ.get("AGENTSPAN_LLM_MODEL", "anthropic/claude-sonnet-4-6") -MAX_ITER = int(os.environ.get("AGENTSPAN_REBAL_MAX_ITER", "8")) +MODEL = os.environ.get("CONDUCTOR_AGENT_LLM_MODEL", "anthropic/claude-sonnet-4-6") +MAX_ITER = int(os.environ.get("CONDUCTOR_AGENT_REBAL_MAX_ITER", "8")) WORKFLOW_NAME = "portfolio_rebalance_loop" WORKFLOW_VERSION = 6 diff --git a/examples/agents/115_plan_execute_planner_context.py b/examples/agents/115_plan_execute_planner_context.py index 11b7e2ff5..f0af92d30 100644 --- a/examples/agents/115_plan_execute_planner_context.py +++ b/examples/agents/115_plan_execute_planner_context.py @@ -57,8 +57,8 @@ the markdown that gets templated into the planner's user message. Requirements: - - AGENTSPAN_SERVER_URL=http://localhost:8080/api (default) - - AGENTSPAN_LLM_MODEL=openai/gpt-4o-mini (default) + - CONDUCTOR_SERVER_URL=http://localhost:8080/api (default) + - CONDUCTOR_AGENT_LLM_MODEL=openai/gpt-4o-mini (default) """ from __future__ import annotations @@ -114,7 +114,7 @@ def schedule_kickoff_call(customer_id: str, account_id: str) -> dict: def main() -> None: - model = os.environ.get("AGENTSPAN_LLM_MODEL", "anthropic/claude-sonnet-4-6") + model = os.environ.get("CONDUCTOR_AGENT_LLM_MODEL", "anthropic/claude-sonnet-4-6") planner = Agent( name="onboarding_planner", @@ -202,7 +202,7 @@ def _show_executed_steps(execution_id: str) -> None: """Walk into the plan_exec sub-workflow and print the tool tasks.""" import requests - base = os.environ.get("AGENTSPAN_SERVER_URL", "http://localhost:8080/api") + base = os.environ.get("CONDUCTOR_SERVER_URL", "http://localhost:8080/api") base_url = base.rstrip("/").replace("/api", "") parent = requests.get( diff --git a/examples/agents/116_ocg_subagent.py b/examples/agents/116_ocg_subagent.py index bd5cbb8c6..3be37b646 100644 --- a/examples/agents/116_ocg_subagent.py +++ b/examples/agents/116_ocg_subagent.py @@ -33,7 +33,7 @@ uv run python examples/116_ocg_subagent.py # against an embedded server (e.g. orkes on 8080), add: - # AGENTSPAN_SERVER_URL=http://localhost:8080/api + # CONDUCTOR_SERVER_URL=http://localhost:8080/api """ import os @@ -41,7 +41,7 @@ from conductor.ai.agents import Agent, AgentRuntime, agent_tool from conductor.ai.agents.ocg import ocg_agent -MODEL = os.environ.get("AGENTSPAN_LLM_MODEL", "anthropic/claude-sonnet-4-6") +MODEL = os.environ.get("CONDUCTOR_AGENT_LLM_MODEL", "anthropic/claude-sonnet-4-6") # Per-tool instance binding — required: every OCG tool binds the instance # it talks to; there is no server-side default. diff --git a/examples/agents/117_ocg_direct_tools.py b/examples/agents/117_ocg_direct_tools.py index dfb3a6c08..db0cde01b 100644 --- a/examples/agents/117_ocg_direct_tools.py +++ b/examples/agents/117_ocg_direct_tools.py @@ -33,7 +33,7 @@ uv run python examples/117_ocg_direct_tools.py # against an embedded server (e.g. orkes on 8080), add: - # AGENTSPAN_SERVER_URL=http://localhost:8080/api + # CONDUCTOR_SERVER_URL=http://localhost:8080/api """ import os @@ -41,7 +41,7 @@ from conductor.ai.agents import Agent, AgentRuntime from conductor.ai.agents.ocg import ocg_tools -MODEL = os.environ.get("AGENTSPAN_LLM_MODEL", "anthropic/claude-sonnet-4-6") +MODEL = os.environ.get("CONDUCTOR_AGENT_LLM_MODEL", "anthropic/claude-sonnet-4-6") OCG_INSTANCE_URL = os.environ.get("OCG_INSTANCE_URL") or "" OCG_CREDENTIAL = os.environ.get("OCG_CREDENTIAL") # credential-store name, never the key diff --git a/examples/agents/118_adaptive_loop_showcase.py b/examples/agents/118_adaptive_loop_showcase.py index 50d577ba4..94e929760 100644 --- a/examples/agents/118_adaptive_loop_showcase.py +++ b/examples/agents/118_adaptive_loop_showcase.py @@ -19,7 +19,7 @@ Every tool call (each attempt + verdict) is logged under one execution ID and visible in the UI at http://localhost:8080. -This is the correct Agentspan adaptive loop pattern — not Python +This is the correct Conductor adaptive loop pattern — not Python coordinating multiple runtime.run() calls, but the agent itself iterating within a single durable server-side execution. @@ -32,7 +32,7 @@ 6. Evening must be the most expensive slot each day. Usage: - agentspan server start + Conductor server start export OPENAI_API_KEY=sk-... uv run python3 118_adaptive_loop_showcase.py "Tokyo" uv run python3 118_adaptive_loop_showcase.py "Paris" --budget 60 @@ -55,7 +55,7 @@ MIN_PAID_COST: int = 15 # at least one activity per day must cost ≥ this MIN_DAILY_SPEND: int = 20 # each day must spend at least this much NUM_DAYS: int = 3 -MODEL: str = os.environ.get("AGENTSPAN_LLM_MODEL", "openai/gpt-4o-mini") +MODEL: str = os.environ.get("CONDUCTOR_AGENT_LLM_MODEL", "openai/gpt-4o-mini") # ── Validation tool (deterministic — no LLM) ───────────────────────────────── diff --git a/examples/agents/119_research_report_pae_replan.py b/examples/agents/119_research_report_pae_replan.py index 55fafc44a..6ddc0b329 100644 --- a/examples/agents/119_research_report_pae_replan.py +++ b/examples/agents/119_research_report_pae_replan.py @@ -33,7 +33,7 @@ → Quality improvements iteration by iteration. Requirements: - - agentspan server start + - Conductor server start - export OPENAI_API_KEY=sk-... (or ANTHROPIC_API_KEY) - uv run python3 119_research_report_pae_replan.py "AI agents in production" """ @@ -52,9 +52,9 @@ from conductor.ai.agents import AgentRuntime, plan_execute, tool -SERVER_URL = os.environ.get("AGENTSPAN_SERVER_URL", "http://localhost:8080/api") +SERVER_URL = os.environ.get("CONDUCTOR_SERVER_URL", "http://localhost:8080/api") BASE = SERVER_URL.rstrip("/").replace("/api", "") -MODEL = os.environ.get("AGENTSPAN_LLM_MODEL", "openai/gpt-4o-mini") +MODEL = os.environ.get("CONDUCTOR_AGENT_LLM_MODEL", "openai/gpt-4o-mini") MAX_ITER = int(os.environ.get("REPORT_MAX_ITER", "5")) WORKFLOW_NAME = "research_report_pae_replan" WORKFLOW_VERSION = 3 diff --git a/examples/agents/11_streaming.py b/examples/agents/11_streaming.py index 6429366fa..68144568e 100644 --- a/examples/agents/11_streaming.py +++ b/examples/agents/11_streaming.py @@ -8,8 +8,8 @@ Requirements: - Conductor server with LLM support - - AGENTSPAN_SERVER_URL=http://localhost:8080/api as environment variable - - AGENTSPAN_LLM_MODEL=openai/gpt-4o-mini as environment variable + - CONDUCTOR_SERVER_URL=http://localhost:8080/api as environment variable + - CONDUCTOR_AGENT_LLM_MODEL=openai/gpt-4o-mini as environment variable """ from conductor.ai.agents import Agent, AgentRuntime @@ -33,7 +33,7 @@ # 1. Deploy once during CI/CD: # runtime.deploy(agent) # CLI alternative: - # agentspan deploy --package examples.11_streaming + # runtime.deploy(agent) from a release script # # 2. In a separate long-lived worker process: # runtime.serve(agent) diff --git a/examples/agents/12_long_running.py b/examples/agents/12_long_running.py index b87c04587..e1edf10ed 100644 --- a/examples/agents/12_long_running.py +++ b/examples/agents/12_long_running.py @@ -9,8 +9,8 @@ Requirements: - Conductor server with LLM support - - AGENTSPAN_SERVER_URL=http://localhost:8080/api as environment variable - - AGENTSPAN_LLM_MODEL=openai/gpt-4o-mini as environment variable + - CONDUCTOR_SERVER_URL=http://localhost:8080/api as environment variable + - CONDUCTOR_AGENT_LLM_MODEL=openai/gpt-4o-mini as environment variable """ import time @@ -37,7 +37,7 @@ # 1. Deploy once during CI/CD: # runtime.deploy(agent) # CLI alternative: - # agentspan deploy --package examples.12_long_running + # runtime.deploy(agent) from a release script # # 2. In a separate long-lived worker process: # runtime.serve(agent) diff --git a/examples/agents/13_hierarchical_agents.py b/examples/agents/13_hierarchical_agents.py index ef10fcbc1..3ee8aa795 100644 --- a/examples/agents/13_hierarchical_agents.py +++ b/examples/agents/13_hierarchical_agents.py @@ -17,8 +17,8 @@ Requirements: - Conductor server with LLM support - - AGENTSPAN_SERVER_URL=http://localhost:8080/api as environment variable - - AGENTSPAN_LLM_MODEL=openai/gpt-4o-mini as environment variable + - CONDUCTOR_SERVER_URL=http://localhost:8080/api as environment variable + - CONDUCTOR_AGENT_LLM_MODEL=openai/gpt-4o-mini as environment variable """ from conductor.ai.agents import Agent, AgentRuntime, Strategy, OnTextMention @@ -110,16 +110,15 @@ if __name__ == "__main__": with AgentRuntime() as runtime: print("--- Technical question (CEO -> Engineering -> Backend) ---") - result = runtime.run(ceo, "Design a REST API for a user management system with authentication " - "and then ask marketing team to come up with a marketing campaign for the system with details on how to run these campaign") - result.print_result() + # result = runtime.run(ceo, "Design a REST API for a user management system with authentication " + # "and then ask marketing team to come up with a marketing campaign for the system with details on how to run these campaign") + # result.print_result() # Production pattern: # 1. Deploy once during CI/CD: # runtime.deploy(ceo) # CLI alternative: - # agentspan deploy --package examples.13_hierarchical_agents + # runtime.deploy(ceo) # # 2. In a separate long-lived worker process: - # runtime.serve(ceo) - + runtime.serve(ceo) diff --git a/examples/agents/14_existing_workers.py b/examples/agents/14_existing_workers.py index 1092476cb..e91d101b0 100644 --- a/examples/agents/14_existing_workers.py +++ b/examples/agents/14_existing_workers.py @@ -11,8 +11,8 @@ Requirements: - Conductor server with LLM support - conductor-python installed (provides @worker_task) - - AGENTSPAN_SERVER_URL=http://localhost:8080/api as environment variable - - AGENTSPAN_LLM_MODEL=openai/gpt-4o-mini as environment variable + - CONDUCTOR_SERVER_URL=http://localhost:8080/api as environment variable + - CONDUCTOR_AGENT_LLM_MODEL=openai/gpt-4o-mini as environment variable """ from conductor.client.worker.worker_task import worker_task @@ -79,7 +79,7 @@ def create_support_ticket(customer_id: str, issue: str, priority: str = "medium" # 1. Deploy once during CI/CD: # runtime.deploy(agent) # CLI alternative: - # agentspan deploy --package examples.14_existing_workers + # runtime.deploy(agent) from a release script # # 2. In a separate long-lived worker process: # runtime.serve(agent) diff --git a/examples/agents/15_agent_discussion.py b/examples/agents/15_agent_discussion.py index 0fb8e723d..d5c8478eb 100644 --- a/examples/agents/15_agent_discussion.py +++ b/examples/agents/15_agent_discussion.py @@ -20,8 +20,8 @@ Requirements: - Conductor server with LLM support - - AGENTSPAN_SERVER_URL=http://localhost:8080/api as environment variable - - AGENTSPAN_LLM_MODEL=openai/gpt-4o-mini as environment variable + - CONDUCTOR_SERVER_URL=http://localhost:8080/api as environment variable + - CONDUCTOR_AGENT_LLM_MODEL=openai/gpt-4o-mini as environment variable """ from conductor.ai.agents import Agent, AgentRuntime, Strategy @@ -88,7 +88,7 @@ # 1. Deploy once during CI/CD: # runtime.deploy(pipeline) # CLI alternative: - # agentspan deploy --package examples.15_agent_discussion + # runtime.deploy(agent) from a release script # # 2. In a separate long-lived worker process: # runtime.serve(pipeline) diff --git a/examples/agents/16_credentials_isolated_tool.py b/examples/agents/16_credentials_isolated_tool.py index 0b6721ec5..18e14a6e1 100644 --- a/examples/agents/16_credentials_isolated_tool.py +++ b/examples/agents/16_credentials_isolated_tool.py @@ -17,13 +17,13 @@ injected as env vars. The parent process's os.environ is unchanged. Setup (one-time, via CLI): - agentspan login # authenticate - agentspan credentials set GH_TOKEN # enter token when prompted + Conductor login # authenticate + the Conductor server credential store Requirements: - - Agentspan server running at AGENTSPAN_SERVER_URL - - AGENTSPAN_LLM_MODEL set (or defaults to openai/gpt-5.4) - - GH_TOKEN stored via `agentspan credentials set` OR set in os.environ + - Conductor server running at CONDUCTOR_SERVER_URL + - CONDUCTOR_AGENT_LLM_MODEL set (or defaults to openai/gpt-5.4) + - GH_TOKEN stored via `the Conductor server credential store` OR set in os.environ """ import os @@ -129,7 +129,7 @@ def create_github_issue(repo: str, title: str, body: str) -> dict: with AgentRuntime() as runtime: result = runtime.run( agent, - "List the 5 most recently updated repos for the 'agentspan-ai' GitHub org.", + "List the 5 most recently updated repos for the 'Conductor-ai' GitHub org.", ) result.print_result() @@ -137,7 +137,7 @@ def create_github_issue(repo: str, title: str, body: str) -> dict: # 1. Deploy once during CI/CD: # runtime.deploy(agent) # CLI alternative: - # agentspan deploy --package examples.16_credentials_isolated_tool + # runtime.deploy(agent) from a release script # # 2. In a separate long-lived worker process: # runtime.serve(agent) diff --git a/examples/agents/16_random_strategy.py b/examples/agents/16_random_strategy.py index de0dbec49..1f43a971d 100644 --- a/examples/agents/16_random_strategy.py +++ b/examples/agents/16_random_strategy.py @@ -9,8 +9,8 @@ Requirements: - Conductor server with LLM support - - AGENTSPAN_SERVER_URL=http://localhost:8080/api as environment variable - - AGENTSPAN_LLM_MODEL=openai/gpt-4o-mini as environment variable + - CONDUCTOR_SERVER_URL=http://localhost:8080/api as environment variable + - CONDUCTOR_AGENT_LLM_MODEL=openai/gpt-4o-mini as environment variable """ from conductor.ai.agents import Agent, AgentRuntime, Strategy @@ -65,7 +65,7 @@ # 1. Deploy once during CI/CD: # runtime.deploy(brainstorm) # CLI alternative: - # agentspan deploy --package examples.16_random_strategy + # runtime.deploy(agent) from a release script # # 2. In a separate long-lived worker process: # runtime.serve(brainstorm) diff --git a/examples/agents/16b_credentials_non_isolated.py b/examples/agents/16b_credentials_non_isolated.py index 3fe28f32f..648bd74c9 100644 --- a/examples/agents/16b_credentials_non_isolated.py +++ b/examples/agents/16b_credentials_non_isolated.py @@ -14,9 +14,9 @@ is read from process environment variables. Requirements: - - Agentspan server running at AGENTSPAN_SERVER_URL - - AGENTSPAN_LLM_MODEL set (or defaults via settings) - - STRIPE_SECRET_KEY stored: agentspan credentials set STRIPE_SECRET_KEY + - Conductor server running at CONDUCTOR_SERVER_URL + - CONDUCTOR_AGENT_LLM_MODEL set (or defaults via settings) + - STRIPE_SECRET_KEY stored: the Conductor server credential store """ from settings import settings @@ -40,7 +40,7 @@ def get_customer_balance(customer_id: str) -> dict: api_key = get_secret("STRIPE_SECRET_KEY") except CredentialNotFoundError: return { - "error": "STRIPE_SECRET_KEY not configured — run: agentspan credentials set STRIPE_SECRET_KEY " + "error": "STRIPE_SECRET_KEY is not configured in the Conductor server credential store." } import base64 @@ -122,5 +122,5 @@ def list_recent_charges(limit: int = 5) -> dict: # Production pattern: # 1. Deploy once during CI/CD: # runtime.deploy(agent) - # CLI alternative: agentspan deploy --package examples.16b_credentials_non_isolated + # CLI alternative: runtime.deploy(agent) from a release script # 2. In a separate long-lived worker process: runtime.serve(agent) diff --git a/examples/agents/16c_credentials_cli_tools.py b/examples/agents/16c_credentials_cli_tools.py index a2ace7211..016ef96bb 100644 --- a/examples/agents/16c_credentials_cli_tools.py +++ b/examples/agents/16c_credentials_cli_tools.py @@ -10,13 +10,13 @@ - Multi-credential tools (aws needs multiple env vars) Setup (one-time, via CLI): - agentspan login - agentspan credentials set GH_TOKEN - agentspan credentials set AWS_ACCESS_KEY_ID - agentspan credentials set AWS_SECRET_ACCESS_KEY + Conductor login + the Conductor server credential store + the Conductor server credential store + the Conductor server credential store Requirements: - - Agentspan server running at AGENTSPAN_SERVER_URL - - AGENTSPAN_LLM_MODEL set (or defaults to openai/gpt-5.4) + - Conductor server running at CONDUCTOR_SERVER_URL + - CONDUCTOR_AGENT_LLM_MODEL set (or defaults to openai/gpt-5.4) - gh and aws CLIs installed """ @@ -131,7 +131,7 @@ def aws_get_caller_identity() -> dict: # 1. Deploy once during CI/CD: # runtime.deploy(github_aws_agent) # CLI alternative: - # agentspan deploy --package examples.16c_credentials_cli_tools + # runtime.deploy(agent) from a release script # # 2. In a separate long-lived worker process: # runtime.serve(github_aws_agent) diff --git a/examples/agents/16d_credentials_gh_cli.py b/examples/agents/16d_credentials_gh_cli.py index 63588c534..ede8a9a10 100644 --- a/examples/agents/16d_credentials_gh_cli.py +++ b/examples/agents/16d_credentials_gh_cli.py @@ -9,13 +9,13 @@ - The agent calls `gh` commands directly — no subprocess boilerplate needed Setup (one-time, via CLI): - agentspan login - agentspan credentials set GH_TOKEN + Conductor login + the Conductor server credential store Requirements: - - Agentspan server running at AGENTSPAN_SERVER_URL - - AGENTSPAN_LLM_MODEL set (or defaults to openai/gpt-5.4) + - Conductor server running at CONDUCTOR_SERVER_URL + - CONDUCTOR_AGENT_LLM_MODEL set (or defaults to openai/gpt-5.4) - `gh` CLI installed (https://cli.github.com) - - GH_TOKEN stored via `agentspan credentials set` + - GH_TOKEN stored via `the Conductor server credential store` """ from conductor.ai.agents import Agent, AgentRuntime @@ -39,7 +39,7 @@ with AgentRuntime() as runtime: result = runtime.run( agent, - "List the 5 most recently updated repos for the 'agentspan'", + "List the 5 most recently updated repos for the 'Conductor'", ) result.print_result() @@ -47,7 +47,7 @@ # 1. Deploy once during CI/CD: # runtime.deploy(agent) # CLI alternative: - # agentspan deploy --package examples.16d_credentials_gh_cli + # runtime.deploy(agent) from a release script # # 2. In a separate long-lived worker process: # runtime.serve(agent) diff --git a/examples/agents/16e_credentials_http_tool.py b/examples/agents/16e_credentials_http_tool.py index 85fe628ff..7d89dbb62 100644 --- a/examples/agents/16e_credentials_http_tool.py +++ b/examples/agents/16e_credentials_http_tool.py @@ -13,11 +13,11 @@ in the workflow definition. Setup (one-time): - agentspan credentials set GITHUB_TOKEN + the Conductor server credential store Requirements: - - Agentspan server running at AGENTSPAN_SERVER_URL - - AGENTSPAN_LLM_MODEL set (or defaults to openai/gpt-5.4) - - GITHUB_TOKEN stored via `agentspan credentials set` + - Conductor server running at CONDUCTOR_SERVER_URL + - CONDUCTOR_AGENT_LLM_MODEL set (or defaults to openai/gpt-5.4) + - GITHUB_TOKEN stored via `the Conductor server credential store` """ from conductor.ai.agents import Agent, AgentRuntime @@ -30,7 +30,7 @@ list_repos = http_tool( name="list_github_repos", description="List public GitHub repositories for a user. Returns JSON array with name, url, and stars.", - url="https://api.github.com/users/agentspan/repos?per_page=5&sort=updated", + url="https://api.github.com/users/Conductor/repos?per_page=5&sort=updated", headers={ "Authorization": "Bearer ${GITHUB_TOKEN}", "Accept": "application/vnd.github.v3+json", @@ -48,14 +48,14 @@ if __name__ == "__main__": with AgentRuntime() as runtime: - result = runtime.run(agent, "List the repos for agentspan") + result = runtime.run(agent, "List the repos for Conductor") result.print_result() # Production pattern: # 1. Deploy once during CI/CD: # runtime.deploy(agent) # CLI alternative: - # agentspan deploy --package examples.16e_credentials_http_tool + # runtime.deploy(agent) from a release script # # 2. In a separate long-lived worker process: # runtime.serve(agent) diff --git a/examples/agents/16f_credentials_mcp_tool.py b/examples/agents/16f_credentials_mcp_tool.py index e20a8a983..eee00d125 100644 --- a/examples/agents/16f_credentials_mcp_tool.py +++ b/examples/agents/16f_credentials_mcp_tool.py @@ -14,14 +14,14 @@ # Start with auth (to demonstrate credential resolution): mcp-testkit --transport http --auth - # Store credentials via CLI or Agentspan UI: - agentspan credentials set MCP_API_KEY + # Store credentials via CLI or Conductor UI: + the Conductor server credential store Requirements: - - Agentspan server running at AGENTSPAN_SERVER_URL - - AGENTSPAN_LLM_MODEL set (or defaults to openai/gpt-4o-mini) + - Conductor server running at CONDUCTOR_SERVER_URL + - CONDUCTOR_AGENT_LLM_MODEL set (or defaults to openai/gpt-4o-mini) - mcp-testkit running on http://localhost:3001 (see setup above) - - MCP_API_KEY stored via CLI or Agentspan UI + - MCP_API_KEY stored via CLI or Conductor UI """ from conductor.ai.agents import Agent, AgentRuntime @@ -56,7 +56,7 @@ # 1. Deploy once during CI/CD: # runtime.deploy(agent) # CLI alternative: - # agentspan deploy --package examples.16f_credentials_mcp_tool + # runtime.deploy(agent) from a release script # # 2. In a separate long-lived worker process: # runtime.serve(agent) diff --git a/examples/agents/16g_credentials_framework_passthrough.py b/examples/agents/16g_credentials_framework_passthrough.py index 75ae15cab..7f2367346 100644 --- a/examples/agents/16g_credentials_framework_passthrough.py +++ b/examples/agents/16g_credentials_framework_passthrough.py @@ -10,15 +10,15 @@ - Works the same for LangChain, OpenAI Agent SDK, and Google ADK This pattern is used when you run a foreign framework agent (LangGraph, -LangChain, OpenAI, ADK) through Agentspan and need tools inside the +LangChain, OpenAI, ADK) through Conductor and need tools inside the graph to access credentials from the credential store. Setup (one-time): - agentspan credentials set GITHUB_TOKEN + the Conductor server credential store Requirements: - - Agentspan server running at AGENTSPAN_SERVER_URL - - AGENTSPAN_LLM_MODEL set (or defaults to openai/gpt-5.4) - - GITHUB_TOKEN stored via `agentspan credentials set` + - Conductor server running at CONDUCTOR_SERVER_URL + - CONDUCTOR_AGENT_LLM_MODEL set (or defaults to openai/gpt-5.4) + - GITHUB_TOKEN stored via `the Conductor server credential store` - langgraph installed: pip install langgraph langchain-openai """ @@ -74,7 +74,7 @@ def create_langgraph_agent(): # 1. Deploy once during CI/CD: # runtime.deploy(graph) # CLI alternative: - # agentspan deploy --package examples.16g_credentials_framework_passthrough + # runtime.deploy(agent) from a release script # # 2. In a separate long-lived worker process: # runtime.serve(graph) diff --git a/examples/agents/16h_credentials_external_worker.py b/examples/agents/16h_credentials_external_worker.py index 92f79be40..af43af83c 100644 --- a/examples/agents/16h_credentials_external_worker.py +++ b/examples/agents/16h_credentials_external_worker.py @@ -19,11 +19,11 @@ simulate both in one file for demonstration. Setup (one-time): - agentspan credentials set GITHUB_TOKEN + the Conductor server credential store Requirements: - - Agentspan server running at AGENTSPAN_SERVER_URL - - AGENTSPAN_LLM_MODEL set (or defaults to openai/gpt-5.4) - - GITHUB_TOKEN stored via `agentspan credentials set` + - Conductor server running at CONDUCTOR_SERVER_URL + - CONDUCTOR_AGENT_LLM_MODEL set (or defaults to openai/gpt-5.4) + - GITHUB_TOKEN stored via `the Conductor server credential store` """ from conductor.ai.agents import Agent, AgentRuntime, tool, resolve_credentials diff --git a/examples/agents/16i_credentials_langchain.py b/examples/agents/16i_credentials_langchain.py index da92b982d..ec55beed8 100644 --- a/examples/agents/16i_credentials_langchain.py +++ b/examples/agents/16i_credentials_langchain.py @@ -9,11 +9,11 @@ and injected into os.environ before the agent runs Setup (one-time): - agentspan credentials set GITHUB_TOKEN + the Conductor server credential store Requirements: - - Agentspan server running at AGENTSPAN_SERVER_URL - - AGENTSPAN_LLM_MODEL set (or defaults to openai/gpt-5.4) - - GITHUB_TOKEN stored via `agentspan credentials set` + - Conductor server running at CONDUCTOR_SERVER_URL + - CONDUCTOR_AGENT_LLM_MODEL set (or defaults to openai/gpt-5.4) + - GITHUB_TOKEN stored via `the Conductor server credential store` - langchain installed: pip install langchain langchain-openai """ @@ -70,7 +70,7 @@ def create_langchain_agent(): # 1. Deploy once during CI/CD: # runtime.deploy(agent) # CLI alternative: - # agentspan deploy --package examples.16i_credentials_langchain + # runtime.deploy(agent) from a release script # # 2. In a separate long-lived worker process: # runtime.serve(agent) diff --git a/examples/agents/16j_credentials_openai_sdk.py b/examples/agents/16j_credentials_openai_sdk.py index e1b15e05a..61c1e3cc4 100644 --- a/examples/agents/16j_credentials_openai_sdk.py +++ b/examples/agents/16j_credentials_openai_sdk.py @@ -9,11 +9,11 @@ - OpenAI agent tools can read credentials from os.environ Setup (one-time): - agentspan credentials set GITHUB_TOKEN + the Conductor server credential store Requirements: - - Agentspan server running at AGENTSPAN_SERVER_URL - - AGENTSPAN_LLM_MODEL set (or defaults to openai/gpt-5.4) - - GITHUB_TOKEN stored via `agentspan credentials set` + - Conductor server running at CONDUCTOR_SERVER_URL + - CONDUCTOR_AGENT_LLM_MODEL set (or defaults to openai/gpt-5.4) + - GITHUB_TOKEN stored via `the Conductor server credential store` - openai-agents installed: pip install openai-agents """ @@ -64,7 +64,7 @@ def create_openai_agent(): # 1. Deploy once during CI/CD: # runtime.deploy(agent) # CLI alternative: - # agentspan deploy --package examples.16j_credentials_openai_sdk + # runtime.deploy(agent) from a release script # # 2. In a separate long-lived worker process: # runtime.serve(agent) diff --git a/examples/agents/16k_credentials_google_adk.py b/examples/agents/16k_credentials_google_adk.py index b0a2634a1..d2b878caf 100644 --- a/examples/agents/16k_credentials_google_adk.py +++ b/examples/agents/16k_credentials_google_adk.py @@ -9,11 +9,11 @@ and injected into os.environ before agent execution Setup (one-time): - agentspan credentials set GITHUB_TOKEN + the Conductor server credential store Requirements: - - Agentspan server running at AGENTSPAN_SERVER_URL - - AGENTSPAN_LLM_MODEL set (or defaults to openai/gpt-5.4) - - GITHUB_TOKEN stored via `agentspan credentials set` + - Conductor server running at CONDUCTOR_SERVER_URL + - CONDUCTOR_AGENT_LLM_MODEL set (or defaults to openai/gpt-5.4) + - GITHUB_TOKEN stored via `the Conductor server credential store` - google-adk installed: pip install google-adk """ @@ -58,7 +58,7 @@ def check_github_auth() -> str: # 1. Deploy once during CI/CD: # runtime.deploy(agent) # CLI alternative: - # agentspan deploy --package examples.16k_credentials_google_adk + # runtime.deploy(agent) from a release script # # 2. In a separate long-lived worker process: # runtime.serve(agent) diff --git a/examples/agents/17_swarm_orchestration.py b/examples/agents/17_swarm_orchestration.py index 48a45c279..794c7b4bc 100644 --- a/examples/agents/17_swarm_orchestration.py +++ b/examples/agents/17_swarm_orchestration.py @@ -20,8 +20,8 @@ Requirements: - Conductor server with LLM support - - AGENTSPAN_SERVER_URL=http://localhost:8080/api as environment variable - - AGENTSPAN_LLM_MODEL=openai/gpt-4o-mini as environment variable + - CONDUCTOR_SERVER_URL=http://localhost:8080/api as environment variable + - CONDUCTOR_AGENT_LLM_MODEL=openai/gpt-4o-mini as environment variable """ from conductor.ai.agents import Agent, AgentRuntime, Strategy @@ -85,7 +85,7 @@ # 1. Deploy once during CI/CD: # runtime.deploy(support) # CLI alternative: - # agentspan deploy --package examples.17_swarm_orchestration + # runtime.deploy(agent) from a release script # # 2. In a separate long-lived worker process: # runtime.serve(support) diff --git a/examples/agents/18_manual_selection.py b/examples/agents/18_manual_selection.py index 7f4aed981..bf7fbf9a1 100644 --- a/examples/agents/18_manual_selection.py +++ b/examples/agents/18_manual_selection.py @@ -15,8 +15,8 @@ Requirements: - Conductor server with LLM support - - AGENTSPAN_SERVER_URL=http://localhost:8080/api as environment variable - - AGENTSPAN_LLM_MODEL=openai/gpt-4o-mini as environment variable + - CONDUCTOR_SERVER_URL=http://localhost:8080/api as environment variable + - CONDUCTOR_AGENT_LLM_MODEL=openai/gpt-4o-mini as environment variable """ from conductor.ai.agents import Agent, AgentRuntime, EventType, Strategy diff --git a/examples/agents/19_composable_termination.py b/examples/agents/19_composable_termination.py index 34ceab431..2e120fff8 100644 --- a/examples/agents/19_composable_termination.py +++ b/examples/agents/19_composable_termination.py @@ -13,8 +13,8 @@ Requirements: - Conductor server with LLM support - - AGENTSPAN_SERVER_URL=http://localhost:8080/api as environment variable - - AGENTSPAN_LLM_MODEL=openai/gpt-4o-mini as environment variable + - CONDUCTOR_SERVER_URL=http://localhost:8080/api as environment variable + - CONDUCTOR_AGENT_LLM_MODEL=openai/gpt-4o-mini as environment variable """ from conductor.ai.agents import ( @@ -103,7 +103,7 @@ def search(query: str) -> str: # 1. Deploy once during CI/CD: # runtime.deploy(agent1) # CLI alternative: - # agentspan deploy --package examples.19_composable_termination + # runtime.deploy(agent) from a release script # # 2. In a separate long-lived worker process: # runtime.serve(agent1) diff --git a/examples/agents/20_constrained_transitions.py b/examples/agents/20_constrained_transitions.py index e9e367a9b..02fd2cb49 100644 --- a/examples/agents/20_constrained_transitions.py +++ b/examples/agents/20_constrained_transitions.py @@ -13,8 +13,8 @@ Requirements: - Conductor server with LLM support - - AGENTSPAN_SERVER_URL=http://localhost:8080/api as environment variable - - AGENTSPAN_LLM_MODEL=openai/gpt-4o-mini as environment variable + - CONDUCTOR_SERVER_URL=http://localhost:8080/api as environment variable + - CONDUCTOR_AGENT_LLM_MODEL=openai/gpt-4o-mini as environment variable """ from conductor.ai.agents import Agent, AgentRuntime, Strategy @@ -77,7 +77,7 @@ # 1. Deploy once during CI/CD: # runtime.deploy(code_review) # CLI alternative: - # agentspan deploy --package examples.20_constrained_transitions + # runtime.deploy(agent) from a release script # # 2. In a separate long-lived worker process: # runtime.serve(code_review) diff --git a/examples/agents/21_regex_guardrails.py b/examples/agents/21_regex_guardrails.py index dd498edaf..ea8ab2574 100644 --- a/examples/agents/21_regex_guardrails.py +++ b/examples/agents/21_regex_guardrails.py @@ -16,8 +16,8 @@ Requirements: - Conductor server with LLM support - - AGENTSPAN_SERVER_URL=http://localhost:8080/api as environment variable - - AGENTSPAN_LLM_MODEL=openai/gpt-4o-mini as environment variable + - CONDUCTOR_SERVER_URL=http://localhost:8080/api as environment variable + - CONDUCTOR_AGENT_LLM_MODEL=openai/gpt-4o-mini as environment variable """ from conductor.ai.agents import Agent, AgentRuntime, OnFail, Position, RegexGuardrail, tool @@ -116,7 +116,7 @@ def get_user_profile(user_id: str) -> dict: # 1. Deploy once during CI/CD: # runtime.deploy(agent) # CLI alternative: - # agentspan deploy --package examples.21_regex_guardrails + # runtime.deploy(agent) from a release script # # 2. In a separate long-lived worker process: # runtime.serve(agent) diff --git a/examples/agents/22_llm_guardrails.py b/examples/agents/22_llm_guardrails.py index ab6ec1cd5..e8dc8ef26 100644 --- a/examples/agents/22_llm_guardrails.py +++ b/examples/agents/22_llm_guardrails.py @@ -15,8 +15,8 @@ Requirements: - Conductor server with LLM support - pip install litellm (for the guardrail LLM call) - - AGENTSPAN_SERVER_URL=http://localhost:8080/api as environment variable - - AGENTSPAN_LLM_MODEL=openai/gpt-4o-mini as environment variable + - CONDUCTOR_SERVER_URL=http://localhost:8080/api as environment variable + - CONDUCTOR_AGENT_LLM_MODEL=openai/gpt-4o-mini as environment variable - OPENAI_API_KEY=sk-... as environment variable """ @@ -67,7 +67,7 @@ # 1. Deploy once during CI/CD: # runtime.deploy(agent) # CLI alternative: - # agentspan deploy --package examples.22_llm_guardrails + # runtime.deploy(agent) from a release script # # 2. In a separate long-lived worker process: # runtime.serve(agent) diff --git a/examples/agents/23_token_tracking.py b/examples/agents/23_token_tracking.py index 63831d664..f361f6d4e 100644 --- a/examples/agents/23_token_tracking.py +++ b/examples/agents/23_token_tracking.py @@ -8,8 +8,8 @@ Requirements: - Conductor server with LLM support - - AGENTSPAN_SERVER_URL=http://localhost:8080/api as environment variable - - AGENTSPAN_LLM_MODEL=openai/gpt-4o-mini as environment variable + - CONDUCTOR_SERVER_URL=http://localhost:8080/api as environment variable + - CONDUCTOR_AGENT_LLM_MODEL=openai/gpt-4o-mini as environment variable """ from conductor.ai.agents import Agent, AgentRuntime, tool @@ -61,7 +61,7 @@ def calculate(expression: str) -> str: # 1. Deploy once during CI/CD: # runtime.deploy(agent) # CLI alternative: - # agentspan deploy --package examples.23_token_tracking + # runtime.deploy(agent) from a release script # # 2. In a separate long-lived worker process: # runtime.serve(agent) diff --git a/examples/agents/24_code_execution.py b/examples/agents/24_code_execution.py index c181faef5..3ade522b4 100644 --- a/examples/agents/24_code_execution.py +++ b/examples/agents/24_code_execution.py @@ -16,8 +16,8 @@ - Conductor server with LLM support - Docker (for DockerCodeExecutor example) - pip install jupyter_client ipykernel (for JupyterCodeExecutor) - - AGENTSPAN_SERVER_URL=http://localhost:8080/api as environment variable - - AGENTSPAN_LLM_MODEL=openai/gpt-4o-mini as environment variable + - CONDUCTOR_SERVER_URL=http://localhost:8080/api as environment variable + - CONDUCTOR_AGENT_LLM_MODEL=openai/gpt-4o-mini as environment variable """ from conductor.ai.agents import Agent, AgentRuntime @@ -90,7 +90,7 @@ # 1. Deploy once during CI/CD: # runtime.deploy(coder) # CLI alternative: - # agentspan deploy --package examples.24_code_execution + # runtime.deploy(agent) from a release script # # 2. In a separate long-lived worker process: # runtime.serve(coder) diff --git a/examples/agents/25_semantic_memory.py b/examples/agents/25_semantic_memory.py index 569951a47..8e6dfec4a 100644 --- a/examples/agents/25_semantic_memory.py +++ b/examples/agents/25_semantic_memory.py @@ -11,8 +11,8 @@ Requirements: - Conductor server with LLM support - - AGENTSPAN_SERVER_URL=http://localhost:8080/api as environment variable - - AGENTSPAN_LLM_MODEL=openai/gpt-4o-mini as environment variable + - CONDUCTOR_SERVER_URL=http://localhost:8080/api as environment variable + - CONDUCTOR_AGENT_LLM_MODEL=openai/gpt-4o-mini as environment variable """ from conductor.ai.agents import Agent, AgentRuntime, tool @@ -80,7 +80,7 @@ def get_customer_context(query: str) -> str: # 1. Deploy once during CI/CD: # runtime.deploy(agent) # CLI alternative: - # agentspan deploy --package examples.25_semantic_memory + # runtime.deploy(agent) from a release script # # 2. In a separate long-lived worker process: # runtime.serve(agent) diff --git a/examples/agents/26_opentelemetry_tracing.py b/examples/agents/26_opentelemetry_tracing.py index 95f1c107b..163139e93 100644 --- a/examples/agents/26_opentelemetry_tracing.py +++ b/examples/agents/26_opentelemetry_tracing.py @@ -16,8 +16,8 @@ Requirements: - pip install opentelemetry-api opentelemetry-sdk - Conductor server with LLM support - - AGENTSPAN_SERVER_URL=http://localhost:8080/api as environment variable - - AGENTSPAN_LLM_MODEL=openai/gpt-4o-mini as environment variable + - CONDUCTOR_SERVER_URL=http://localhost:8080/api as environment variable + - CONDUCTOR_AGENT_LLM_MODEL=openai/gpt-4o-mini as environment variable """ from conductor.ai.agents import Agent, AgentRuntime, is_tracing_enabled, tool @@ -75,7 +75,7 @@ def lookup(query: str) -> str: # 1. Deploy once during CI/CD: # runtime.deploy(agent) # CLI alternative: - # agentspan deploy --package examples.26_opentelemetry_tracing + # runtime.deploy(agent) from a release script # # 2. In a separate long-lived worker process: # runtime.serve(agent) diff --git a/examples/agents/28_gpt_assistant_agent.py b/examples/agents/28_gpt_assistant_agent.py index f55fa16eb..29da4914f 100644 --- a/examples/agents/28_gpt_assistant_agent.py +++ b/examples/agents/28_gpt_assistant_agent.py @@ -15,8 +15,8 @@ - pip install openai - Conductor server with LLM support - OPENAI_API_KEY=sk-... as environment variable - - AGENTSPAN_SERVER_URL=http://localhost:8080/api as environment variable - - AGENTSPAN_LLM_MODEL=openai/gpt-4o-mini as environment variable + - CONDUCTOR_SERVER_URL=http://localhost:8080/api as environment variable + - CONDUCTOR_AGENT_LLM_MODEL=openai/gpt-4o-mini as environment variable """ from conductor.ai.agents import AgentRuntime @@ -57,7 +57,7 @@ # 1. Deploy once during CI/CD: # runtime.deploy(data_analyst) # CLI alternative: - # agentspan deploy --package examples.28_gpt_assistant_agent + # runtime.deploy(agent) from a release script # # 2. In a separate long-lived worker process: # runtime.serve(data_analyst) diff --git a/examples/agents/29_agent_introductions.py b/examples/agents/29_agent_introductions.py index e0920579b..e2bc3035e 100644 --- a/examples/agents/29_agent_introductions.py +++ b/examples/agents/29_agent_introductions.py @@ -12,8 +12,8 @@ Requirements: - Conductor server with LLM support - - AGENTSPAN_SERVER_URL=http://localhost:8080/api as environment variable - - AGENTSPAN_LLM_MODEL=openai/gpt-4o-mini as environment variable + - CONDUCTOR_SERVER_URL=http://localhost:8080/api as environment variable + - CONDUCTOR_AGENT_LLM_MODEL=openai/gpt-4o-mini as environment variable """ from conductor.ai.agents import Agent, AgentRuntime, Strategy @@ -88,7 +88,7 @@ # 1. Deploy once during CI/CD: # runtime.deploy(design_review) # CLI alternative: - # agentspan deploy --package examples.29_agent_introductions + # runtime.deploy(agent) from a release script # # 2. In a separate long-lived worker process: # runtime.serve(design_review) diff --git a/examples/agents/30_multimodal_agent.py b/examples/agents/30_multimodal_agent.py index 6ff077dcb..f20c85904 100644 --- a/examples/agents/30_multimodal_agent.py +++ b/examples/agents/30_multimodal_agent.py @@ -15,8 +15,8 @@ Requirements: - Conductor server with LLM support (OpenAI key configured) - - AGENTSPAN_SERVER_URL=http://localhost:8080/api as environment variable - - AGENTSPAN_LLM_MODEL=openai/gpt-4o-mini as environment variable + - CONDUCTOR_SERVER_URL=http://localhost:8080/api as environment variable + - CONDUCTOR_AGENT_LLM_MODEL=openai/gpt-4o-mini as environment variable """ from conductor.ai.agents import Agent, AgentRuntime, tool @@ -136,7 +136,7 @@ def save_analysis(title: str, analysis: str) -> str: # 1. Deploy once during CI/CD: # runtime.deploy(vision_agent) # CLI alternative: - # agentspan deploy --package examples.30_multimodal_agent + # runtime.deploy(agent) from a release script # # 2. In a separate long-lived worker process: # runtime.serve(vision_agent) diff --git a/examples/agents/30_skills_dg_review.py b/examples/agents/30_skills_dg_review.py index 74dbee04a..428b74066 100644 --- a/examples/agents/30_skills_dg_review.py +++ b/examples/agents/30_skills_dg_review.py @@ -12,7 +12,7 @@ Requirements: - Conductor server with LLM support - - AGENTSPAN_SERVER_URL=http://localhost:8080/api as environment variable + - CONDUCTOR_SERVER_URL=http://localhost:8080/api as environment variable - /dg skill installed (https://github.com/v1r3n/dinesh-gilfoyle) Install /dg: diff --git a/examples/agents/31_skills_conductor.py b/examples/agents/31_skills_conductor.py index 258bbc0a2..e9970609f 100644 --- a/examples/agents/31_skills_conductor.py +++ b/examples/agents/31_skills_conductor.py @@ -11,7 +11,7 @@ Requirements: - Conductor server with LLM support - - AGENTSPAN_SERVER_URL=http://localhost:8080/api as environment variable + - CONDUCTOR_SERVER_URL=http://localhost:8080/api as environment variable - conductor-skills installed (https://github.com/conductor-oss/conductor-skills) Install conductor-skills: diff --git a/examples/agents/31_tool_guardrails.py b/examples/agents/31_tool_guardrails.py index 44c4d2599..ff5a8d000 100644 --- a/examples/agents/31_tool_guardrails.py +++ b/examples/agents/31_tool_guardrails.py @@ -12,8 +12,8 @@ Requirements: - Conductor server with LLM support - - AGENTSPAN_SERVER_URL=http://localhost:8080/api as environment variable - - AGENTSPAN_LLM_MODEL=openai/gpt-4o-mini as environment variable + - CONDUCTOR_SERVER_URL=http://localhost:8080/api as environment variable + - CONDUCTOR_AGENT_LLM_MODEL=openai/gpt-4o-mini as environment variable """ import re @@ -95,7 +95,7 @@ def run_query(query: str) -> str: # 1. Deploy once during CI/CD: # runtime.deploy(agent) # CLI alternative: - # agentspan deploy --package examples.31_tool_guardrails + # runtime.deploy(agent) from a release script # # 2. In a separate long-lived worker process: # runtime.serve(agent) diff --git a/examples/agents/32_human_guardrail.py b/examples/agents/32_human_guardrail.py index 46c93aa60..0cade717c 100644 --- a/examples/agents/32_human_guardrail.py +++ b/examples/agents/32_human_guardrail.py @@ -9,8 +9,8 @@ Requirements: - Conductor server with LLM support - - AGENTSPAN_SERVER_URL=http://localhost:8080/api as environment variable - - AGENTSPAN_LLM_MODEL=openai/gpt-4o-mini as environment variable + - CONDUCTOR_SERVER_URL=http://localhost:8080/api as environment variable + - CONDUCTOR_AGENT_LLM_MODEL=openai/gpt-4o-mini as environment variable """ from conductor.ai.agents import ( diff --git a/examples/agents/32_skills_multi_agent.py b/examples/agents/32_skills_multi_agent.py index 6ab32781d..b0159c9de 100644 --- a/examples/agents/32_skills_multi_agent.py +++ b/examples/agents/32_skills_multi_agent.py @@ -12,7 +12,7 @@ Requirements: - Conductor server with LLM support - - AGENTSPAN_SERVER_URL=http://localhost:8080/api as environment variable + - CONDUCTOR_SERVER_URL=http://localhost:8080/api as environment variable - /dg skill installed (https://github.com/v1r3n/dinesh-gilfoyle) - conductor skill installed (https://github.com/conductor-oss/conductor-skills) """ diff --git a/examples/agents/33_external_workers.py b/examples/agents/33_external_workers.py index 5b910124f..9064337a1 100644 --- a/examples/agents/33_external_workers.py +++ b/examples/agents/33_external_workers.py @@ -17,8 +17,8 @@ Requirements: - Conductor server with LLM support - The referenced workers must be running somewhere - - AGENTSPAN_SERVER_URL=http://localhost:8080/api as environment variable - - AGENTSPAN_LLM_MODEL=openai/gpt-4o-mini as environment variable + - CONDUCTOR_SERVER_URL=http://localhost:8080/api as environment variable + - CONDUCTOR_AGENT_LLM_MODEL=openai/gpt-4o-mini as environment variable """ from conductor.ai.agents import Agent, AgentRuntime, tool @@ -101,7 +101,7 @@ def check_inventory(product_id: str, warehouse: str = "default") -> dict: # 1. Deploy once during CI/CD: # runtime.deploy(support_agent) # CLI alternative: - # agentspan deploy --package examples.33_external_workers + # runtime.deploy(agent) from a release script # # 2. In a separate long-lived worker process: # runtime.serve(support_agent) diff --git a/examples/agents/33_single_turn_tool.py b/examples/agents/33_single_turn_tool.py index 5ac84feae..e45e204e1 100644 --- a/examples/agents/33_single_turn_tool.py +++ b/examples/agents/33_single_turn_tool.py @@ -13,8 +13,8 @@ Requirements: - Conductor server with LLM support - - AGENTSPAN_SERVER_URL=http://localhost:8080/api as environment variable - - AGENTSPAN_LLM_MODEL=openai/gpt-4o-mini as environment variable + - CONDUCTOR_SERVER_URL=http://localhost:8080/api as environment variable + - CONDUCTOR_AGENT_LLM_MODEL=openai/gpt-4o-mini as environment variable """ from conductor.ai.agents import Agent, AgentRuntime, tool @@ -45,7 +45,7 @@ def get_weather(city: str) -> dict: # 1. Deploy once during CI/CD: # runtime.deploy(agent) # CLI alternative: - # agentspan deploy --package examples.33_single_turn_tool + # runtime.deploy(agent) from a release script # # 2. In a separate long-lived worker process: # runtime.serve(agent) diff --git a/examples/agents/35_standalone_guardrails.py b/examples/agents/35_standalone_guardrails.py index 4193883f0..72f57cb66 100644 --- a/examples/agents/35_standalone_guardrails.py +++ b/examples/agents/35_standalone_guardrails.py @@ -15,8 +15,8 @@ Requirements: Part 1 (standalone): none — no server, no LLM, no workers. Part 2 (as workers): Conductor server - - AGENTSPAN_SERVER_URL=http://localhost:8080/api as environment variable - - AGENTSPAN_LLM_MODEL=openai/gpt-4o-mini as environment variable + - CONDUCTOR_SERVER_URL=http://localhost:8080/api as environment variable + - CONDUCTOR_AGENT_LLM_MODEL=openai/gpt-4o-mini as environment variable """ import re diff --git a/examples/agents/36_simple_agent_guardrails.py b/examples/agents/36_simple_agent_guardrails.py index 03ecdace4..331877a9e 100644 --- a/examples/agents/36_simple_agent_guardrails.py +++ b/examples/agents/36_simple_agent_guardrails.py @@ -20,8 +20,8 @@ Requirements: - Conductor server with LLM support - - AGENTSPAN_SERVER_URL=http://localhost:8080/api as environment variable - - AGENTSPAN_LLM_MODEL=openai/gpt-4o-mini as environment variable + - CONDUCTOR_SERVER_URL=http://localhost:8080/api as environment variable + - CONDUCTOR_AGENT_LLM_MODEL=openai/gpt-4o-mini as environment variable """ from conductor.ai.agents import ( @@ -114,7 +114,7 @@ def min_length(content: str) -> GuardrailResult: # 1. Deploy once during CI/CD: # runtime.deploy(agent) # CLI alternative: - # agentspan deploy --package examples.36_simple_agent_guardrails + # runtime.deploy(agent) from a release script # # 2. In a separate long-lived worker process: # runtime.serve(agent) diff --git a/examples/agents/37_fix_guardrail.py b/examples/agents/37_fix_guardrail.py index 4b54aca91..b0fd54a09 100644 --- a/examples/agents/37_fix_guardrail.py +++ b/examples/agents/37_fix_guardrail.py @@ -19,8 +19,8 @@ Requirements: - Conductor server with LLM support - - AGENTSPAN_SERVER_URL=http://localhost:8080/api as environment variable - - AGENTSPAN_LLM_MODEL=openai/gpt-4o-mini as environment variable + - CONDUCTOR_SERVER_URL=http://localhost:8080/api as environment variable + - CONDUCTOR_AGENT_LLM_MODEL=openai/gpt-4o-mini as environment variable """ import re @@ -141,7 +141,7 @@ def get_contact_info(name: str) -> dict: # 1. Deploy once during CI/CD: # runtime.deploy(agent) # CLI alternative: - # agentspan deploy --package examples.37_fix_guardrail + # runtime.deploy(agent) from a release script # # 2. In a separate long-lived worker process: # runtime.serve(agent) diff --git a/examples/agents/38_tech_trends.py b/examples/agents/38_tech_trends.py index 75427ca01..16ff0e9a8 100644 --- a/examples/agents/38_tech_trends.py +++ b/examples/agents/38_tech_trends.py @@ -28,9 +28,9 @@ Run: Export as environment variables: - AGENTSPAN_SERVER_URL=https://developer.orkescloud.com/api - AGENTSPAN_AUTH_KEY= - AGENTSPAN_AUTH_SECRET= + CONDUCTOR_SERVER_URL=https://developer.orkescloud.com/api + CONDUCTOR_AUTH_KEY= + CONDUCTOR_AUTH_SECRET= python 38_tech_trends.py """ @@ -323,7 +323,7 @@ def compare_numbers( # 1. Deploy once during CI/CD: # runtime.deploy(pipeline) # CLI alternative: - # agentspan deploy --package examples.38_tech_trends + # runtime.deploy(agent) from a release script # # 2. In a separate long-lived worker process: # runtime.serve(pipeline) diff --git a/examples/agents/39_local_code_execution.py b/examples/agents/39_local_code_execution.py index 947bbd5d5..0a9a6f94c 100644 --- a/examples/agents/39_local_code_execution.py +++ b/examples/agents/39_local_code_execution.py @@ -15,8 +15,8 @@ Requirements: - Conductor server with LLM support - - AGENTSPAN_SERVER_URL=http://localhost:8080/api as environment variable - - AGENTSPAN_LLM_MODEL=openai/gpt-4o-mini as environment variable + - CONDUCTOR_SERVER_URL=http://localhost:8080/api as environment variable + - CONDUCTOR_AGENT_LLM_MODEL=openai/gpt-4o-mini as environment variable """ from conductor.ai.agents import Agent, AgentRuntime, CodeExecutionConfig @@ -102,7 +102,7 @@ # 1. Deploy once during CI/CD: # runtime.deploy(simple_coder) # CLI alternative: - # agentspan deploy --package examples.39_local_code_execution + # runtime.deploy(agent) from a release script # # 2. In a separate long-lived worker process: # runtime.serve(simple_coder) diff --git a/examples/agents/39a_docker_code_execution.py b/examples/agents/39a_docker_code_execution.py index 3f1786767..72615094f 100644 --- a/examples/agents/39a_docker_code_execution.py +++ b/examples/agents/39a_docker_code_execution.py @@ -10,7 +10,7 @@ Requirements: - Conductor server with LLM support - Docker installed and daemon running - - export AGENTSPAN_SERVER_URL=http://localhost:8080/api + - export CONDUCTOR_SERVER_URL=http://localhost:8080/api """ from conductor.ai.agents import Agent, AgentRuntime, CodeExecutionConfig @@ -48,7 +48,7 @@ # 1. Deploy once during CI/CD: # runtime.deploy(docker_coder) # CLI alternative: - # agentspan deploy --package examples.39a_docker_code_execution + # runtime.deploy(agent) from a release script # # 2. In a separate long-lived worker process: # runtime.serve(docker_coder) diff --git a/examples/agents/39b_jupyter_code_execution.py b/examples/agents/39b_jupyter_code_execution.py index 46f93aa3d..441c5c24b 100644 --- a/examples/agents/39b_jupyter_code_execution.py +++ b/examples/agents/39b_jupyter_code_execution.py @@ -11,7 +11,7 @@ Requirements: - Conductor server with LLM support - pip install jupyter_client ipykernel - - export AGENTSPAN_SERVER_URL=http://localhost:8080/api + - export CONDUCTOR_SERVER_URL=http://localhost:8080/api """ from conductor.ai.agents import Agent, AgentRuntime, CodeExecutionConfig @@ -52,7 +52,7 @@ # 1. Deploy once during CI/CD: # runtime.deploy(jupyter_coder) # CLI alternative: - # agentspan deploy --package examples.39b_jupyter_code_execution + # runtime.deploy(agent) from a release script # # 2. In a separate long-lived worker process: # runtime.serve(jupyter_coder) diff --git a/examples/agents/39c_serverless_code_execution.py b/examples/agents/39c_serverless_code_execution.py index 82f385795..f6badfa7a 100644 --- a/examples/agents/39c_serverless_code_execution.py +++ b/examples/agents/39c_serverless_code_execution.py @@ -18,7 +18,7 @@ Requirements: - Conductor server with LLM support - - export AGENTSPAN_SERVER_URL=http://localhost:8080/api + - export CONDUCTOR_SERVER_URL=http://localhost:8080/api """ import json @@ -99,7 +99,7 @@ def _start_mock_server(port: int = 9753) -> HTTPServer: # 1. Deploy once during CI/CD: # runtime.deploy(serverless_coder) # CLI alternative: - # agentspan deploy --package examples.39c_serverless_code_execution + # runtime.deploy(agent) from a release script # # 2. In a separate long-lived worker process: # runtime.serve(serverless_coder) diff --git a/examples/agents/40_media_generation_agent.py b/examples/agents/40_media_generation_agent.py index caa20038d..b35257b9b 100644 --- a/examples/agents/40_media_generation_agent.py +++ b/examples/agents/40_media_generation_agent.py @@ -18,8 +18,8 @@ Requirements: - Conductor server with OpenAI integration configured - - AGENTSPAN_SERVER_URL=http://localhost:8080/api as environment variable - - AGENTSPAN_LLM_MODEL=openai/gpt-4o-mini as environment variable + - CONDUCTOR_SERVER_URL=http://localhost:8080/api as environment variable + - CONDUCTOR_AGENT_LLM_MODEL=openai/gpt-4o-mini as environment variable """ from conductor.ai.agents import Agent, AgentRuntime, audio_tool, image_tool, video_tool @@ -85,7 +85,7 @@ # 1. Deploy once during CI/CD: # runtime.deploy(media_agent) # CLI alternative: - # agentspan deploy --package examples.40_media_generation_agent + # runtime.deploy(agent) from a release script # # 2. In a separate long-lived worker process: # runtime.serve(media_agent) diff --git a/examples/agents/41_sequential_pipeline_tools.py b/examples/agents/41_sequential_pipeline_tools.py index da1518d6a..c53639b58 100644 --- a/examples/agents/41_sequential_pipeline_tools.py +++ b/examples/agents/41_sequential_pipeline_tools.py @@ -14,8 +14,8 @@ Requirements: - Conductor server with LLM support - - AGENTSPAN_SERVER_URL=http://localhost:8080/api as environment variable - - AGENTSPAN_LLM_MODEL=openai/gpt-4o-mini as environment variable + - CONDUCTOR_SERVER_URL=http://localhost:8080/api as environment variable + - CONDUCTOR_AGENT_LLM_MODEL=openai/gpt-4o-mini as environment variable """ from conductor.ai.agents import Agent, AgentRuntime, tool @@ -211,7 +211,7 @@ def assemble_production(title: str, total_scenes: int, # 1. Deploy once during CI/CD: # runtime.deploy(pipeline) # CLI alternative: - # agentspan deploy --package examples.41_sequential_pipeline_tools + # runtime.deploy(agent) from a release script # # 2. In a separate long-lived worker process: # runtime.serve(pipeline) diff --git a/examples/agents/42_security_testing.py b/examples/agents/42_security_testing.py index f4c227f41..6380ce4ba 100644 --- a/examples/agents/42_security_testing.py +++ b/examples/agents/42_security_testing.py @@ -18,8 +18,8 @@ Requirements: - Conductor server with LLM support - - AGENTSPAN_SERVER_URL=http://localhost:8080/api as environment variable - - AGENTSPAN_LLM_MODEL=openai/gpt-4o-mini as environment variable + - CONDUCTOR_SERVER_URL=http://localhost:8080/api as environment variable + - CONDUCTOR_AGENT_LLM_MODEL=openai/gpt-4o-mini as environment variable """ from conductor.ai.agents import Agent, AgentRuntime, tool @@ -144,7 +144,7 @@ def score_safety(response_text: str, attack_category: str) -> dict: # 1. Deploy once during CI/CD: # runtime.deploy(pipeline) # CLI alternative: - # agentspan deploy --package examples.42_security_testing + # runtime.deploy(agent) from a release script # # 2. In a separate long-lived worker process: # runtime.serve(pipeline) diff --git a/examples/agents/43_data_security_pipeline.py b/examples/agents/43_data_security_pipeline.py index 21c7304d3..a875614a6 100644 --- a/examples/agents/43_data_security_pipeline.py +++ b/examples/agents/43_data_security_pipeline.py @@ -17,8 +17,8 @@ Requirements: - Conductor server with LLM support - - AGENTSPAN_SERVER_URL=http://localhost:8080/api as environment variable - - AGENTSPAN_LLM_MODEL=openai/gpt-4o-mini as environment variable + - CONDUCTOR_SERVER_URL=http://localhost:8080/api as environment variable + - CONDUCTOR_AGENT_LLM_MODEL=openai/gpt-4o-mini as environment variable """ import json @@ -141,7 +141,7 @@ def redact_sensitive_fields(data: str) -> dict: # 1. Deploy once during CI/CD: # runtime.deploy(pipeline) # CLI alternative: - # agentspan deploy --package examples.43_data_security_pipeline + # runtime.deploy(agent) from a release script # # 2. In a separate long-lived worker process: # runtime.serve(pipeline) diff --git a/examples/agents/44_safety_guardrails.py b/examples/agents/44_safety_guardrails.py index 4dac603c0..4697c4c66 100644 --- a/examples/agents/44_safety_guardrails.py +++ b/examples/agents/44_safety_guardrails.py @@ -18,8 +18,8 @@ Requirements: - Conductor server with LLM support - - AGENTSPAN_SERVER_URL=http://localhost:8080/api as environment variable - - AGENTSPAN_LLM_MODEL=openai/gpt-4o-mini as environment variable + - CONDUCTOR_SERVER_URL=http://localhost:8080/api as environment variable + - CONDUCTOR_AGENT_LLM_MODEL=openai/gpt-4o-mini as environment variable """ import re @@ -132,7 +132,7 @@ def sanitize_response(text: str, pii_types: str = "") -> dict: # 1. Deploy once during CI/CD: # runtime.deploy(pipeline) # CLI alternative: - # agentspan deploy --package examples.44_safety_guardrails + # runtime.deploy(agent) from a release script # # 2. In a separate long-lived worker process: # runtime.serve(pipeline) diff --git a/examples/agents/45_agent_tool.py b/examples/agents/45_agent_tool.py index f2092e111..59ccb9a03 100644 --- a/examples/agents/45_agent_tool.py +++ b/examples/agents/45_agent_tool.py @@ -14,8 +14,8 @@ Requirements: - Conductor server with AgentTool support - - AGENTSPAN_SERVER_URL=http://localhost:8080/api as environment variable - - AGENTSPAN_LLM_MODEL=openai/gpt-4o-mini as environment variable + - CONDUCTOR_SERVER_URL=http://localhost:8080/api as environment variable + - CONDUCTOR_AGENT_LLM_MODEL=openai/gpt-4o-mini as environment variable """ from conductor.ai.agents import Agent, AgentRuntime, agent_tool, tool @@ -105,7 +105,7 @@ def calculate(expression: str) -> dict: # 1. Deploy once during CI/CD: # runtime.deploy(manager) # CLI alternative: - # agentspan deploy --package examples.45_agent_tool + # runtime.deploy(agent) from a release script # # 2. In a separate long-lived worker process: # runtime.serve(manager) diff --git a/examples/agents/46_transfer_control.py b/examples/agents/46_transfer_control.py index c30c6da7a..f8d6a77ab 100644 --- a/examples/agents/46_transfer_control.py +++ b/examples/agents/46_transfer_control.py @@ -9,8 +9,8 @@ Requirements: - Conductor server - - AGENTSPAN_SERVER_URL=http://localhost:8080/api as environment variable - - AGENTSPAN_LLM_MODEL=openai/gpt-4o-mini as environment variable + - CONDUCTOR_SERVER_URL=http://localhost:8080/api as environment variable + - CONDUCTOR_AGENT_LLM_MODEL=openai/gpt-4o-mini as environment variable """ from conductor.ai.agents import Agent, AgentRuntime, tool @@ -110,7 +110,7 @@ def write_summary(findings: str) -> dict: # 1. Deploy once during CI/CD: # runtime.deploy(coordinator) # CLI alternative: - # agentspan deploy --package examples.46_transfer_control + # runtime.deploy(agent) from a release script # # 2. In a separate long-lived worker process: # runtime.serve(coordinator) diff --git a/examples/agents/47_callbacks.py b/examples/agents/47_callbacks.py index 1ec39919d..4a7d45738 100644 --- a/examples/agents/47_callbacks.py +++ b/examples/agents/47_callbacks.py @@ -9,8 +9,8 @@ Requirements: - Conductor server with callback support - - AGENTSPAN_SERVER_URL=http://localhost:8080/api as environment variable - - AGENTSPAN_LLM_MODEL=openai/gpt-4o-mini as environment variable + - CONDUCTOR_SERVER_URL=http://localhost:8080/api as environment variable + - CONDUCTOR_AGENT_LLM_MODEL=openai/gpt-4o-mini as environment variable """ from conductor.ai.agents import Agent, AgentRuntime, tool @@ -92,7 +92,7 @@ def get_facts(topic: str) -> dict: # 1. Deploy once during CI/CD: # runtime.deploy(agent) # CLI alternative: - # agentspan deploy --package examples.47_callbacks + # runtime.deploy(agent) from a release script # # 2. In a separate long-lived worker process: # runtime.serve(agent) diff --git a/examples/agents/48_planner.py b/examples/agents/48_planner.py index da964fec0..28dc044e9 100644 --- a/examples/agents/48_planner.py +++ b/examples/agents/48_planner.py @@ -9,8 +9,8 @@ Requirements: - Conductor server with planner support - - AGENTSPAN_SERVER_URL=http://localhost:8080/api as environment variable - - AGENTSPAN_LLM_MODEL=openai/gpt-4o-mini as environment variable + - CONDUCTOR_SERVER_URL=http://localhost:8080/api as environment variable + - CONDUCTOR_AGENT_LLM_MODEL=openai/gpt-4o-mini as environment variable """ from settings import settings @@ -82,7 +82,7 @@ def write_section(title: str, content: str) -> dict: # 1. Deploy once during CI/CD: # runtime.deploy(agent) # CLI alternative: - # agentspan deploy --package examples.48_planner + # runtime.deploy(agent) from a release script # # 2. In a separate long-lived worker process: # runtime.serve(agent) diff --git a/examples/agents/49_include_contents.py b/examples/agents/49_include_contents.py index bc3f430ba..33c5f7ac0 100644 --- a/examples/agents/49_include_contents.py +++ b/examples/agents/49_include_contents.py @@ -10,8 +10,8 @@ Requirements: - Conductor server with include_contents support - - AGENTSPAN_SERVER_URL=http://localhost:8080/api as environment variable - - AGENTSPAN_LLM_MODEL=openai/gpt-4o-mini as environment variable + - CONDUCTOR_SERVER_URL=http://localhost:8080/api as environment variable + - CONDUCTOR_AGENT_LLM_MODEL=openai/gpt-4o-mini as environment variable """ from conductor.ai.agents import Agent, AgentRuntime, tool @@ -74,7 +74,7 @@ def summarize_text(text: str) -> dict: # 1. Deploy once during CI/CD: # runtime.deploy(coordinator) # CLI alternative: - # agentspan deploy --package examples.49_include_contents + # runtime.deploy(agent) from a release script # # 2. In a separate long-lived worker process: # runtime.serve(coordinator) diff --git a/examples/agents/50_thinking_config.py b/examples/agents/50_thinking_config.py index 559c95c12..7f3731643 100644 --- a/examples/agents/50_thinking_config.py +++ b/examples/agents/50_thinking_config.py @@ -11,8 +11,8 @@ Requirements: - Conductor server with thinking config support - A model that supports extended thinking (e.g., Claude with thinking) - - AGENTSPAN_SERVER_URL=http://localhost:8080/api as environment variable - - AGENTSPAN_LLM_MODEL=openai/gpt-4o-mini as environment variable + - CONDUCTOR_SERVER_URL=http://localhost:8080/api as environment variable + - CONDUCTOR_AGENT_LLM_MODEL=openai/gpt-4o-mini as environment variable """ from conductor.ai.agents import Agent, AgentRuntime, tool @@ -61,7 +61,7 @@ def calculate(expression: str) -> dict: # 1. Deploy once during CI/CD: # runtime.deploy(agent) # CLI alternative: - # agentspan deploy --package examples.50_thinking_config + # runtime.deploy(agent) from a release script # # 2. In a separate long-lived worker process: # runtime.serve(agent) diff --git a/examples/agents/51_shared_state.py b/examples/agents/51_shared_state.py index 51a8bd99e..ea51f5c9f 100644 --- a/examples/agents/51_shared_state.py +++ b/examples/agents/51_shared_state.py @@ -10,8 +10,8 @@ Requirements: - Conductor server with state support - - AGENTSPAN_SERVER_URL=http://localhost:8080/api as environment variable - - AGENTSPAN_LLM_MODEL=openai/gpt-4o-mini as environment variable + - CONDUCTOR_SERVER_URL=http://localhost:8080/api as environment variable + - CONDUCTOR_AGENT_LLM_MODEL=openai/gpt-4o-mini as environment variable """ from conductor.ai.agents import Agent, AgentRuntime, tool @@ -90,7 +90,7 @@ def clear_list(context: ToolContext = None) -> dict: # 1. Deploy once during CI/CD: # runtime.deploy(agent) # CLI alternative: - # agentspan deploy --package examples.51_shared_state + # runtime.deploy(agent) from a release script # # 2. In a separate long-lived worker process: # runtime.serve(agent) diff --git a/examples/agents/52_nested_strategies.py b/examples/agents/52_nested_strategies.py index b1f2a5d2d..a7d9506dc 100644 --- a/examples/agents/52_nested_strategies.py +++ b/examples/agents/52_nested_strategies.py @@ -10,8 +10,8 @@ Requirements: - Conductor server - - AGENTSPAN_SERVER_URL=http://localhost:8080/api as environment variable - - AGENTSPAN_LLM_MODEL=openai/gpt-4o-mini as environment variable + - CONDUCTOR_SERVER_URL=http://localhost:8080/api as environment variable + - CONDUCTOR_AGENT_LLM_MODEL=openai/gpt-4o-mini as environment variable """ from conductor.ai.agents import Agent, AgentRuntime @@ -69,7 +69,7 @@ # 1. Deploy once during CI/CD: # runtime.deploy(pipeline) # CLI alternative: - # agentspan deploy --package examples.52_nested_strategies + # runtime.deploy(agent) from a release script # # 2. In a separate long-lived worker process: # runtime.serve(pipeline) diff --git a/examples/agents/53_agent_lifecycle_callbacks.py b/examples/agents/53_agent_lifecycle_callbacks.py index 170dba6a5..8cdf16968 100644 --- a/examples/agents/53_agent_lifecycle_callbacks.py +++ b/examples/agents/53_agent_lifecycle_callbacks.py @@ -9,8 +9,8 @@ Requirements: - Conductor server with callback support - - AGENTSPAN_SERVER_URL=http://localhost:8080/api in .env or environment - - AGENTSPAN_LLM_MODEL=openai/gpt-4o-mini in .env or environment + - CONDUCTOR_SERVER_URL=http://localhost:8080/api in .env or environment + - CONDUCTOR_AGENT_LLM_MODEL=openai/gpt-4o-mini in .env or environment """ import time @@ -87,7 +87,7 @@ def lookup_weather(city: str) -> dict: # 1. Deploy once during CI/CD: # runtime.deploy(agent) # CLI alternative: - # agentspan deploy --package examples.53_agent_lifecycle_callbacks + # runtime.deploy(agent) from a release script # # 2. In a separate long-lived worker process: # runtime.serve(agent) diff --git a/examples/agents/54_software_bug_assistant.py b/examples/agents/54_software_bug_assistant.py index 94a56106a..960edfa32 100644 --- a/examples/agents/54_software_bug_assistant.py +++ b/examples/agents/54_software_bug_assistant.py @@ -10,8 +10,8 @@ Requirements: - Conductor server with AgentTool + MCP support - - AGENTSPAN_SERVER_URL=http://localhost:8080/api in .env or environment - - AGENTSPAN_LLM_MODEL=openai/gpt-4o-mini in .env or environment + - CONDUCTOR_SERVER_URL=http://localhost:8080/api in .env or environment + - CONDUCTOR_AGENT_LLM_MODEL=openai/gpt-4o-mini in .env or environment - GH_TOKEN in .env or environment """ @@ -269,7 +269,7 @@ def search_web(query: str) -> dict: # 1. Deploy once during CI/CD: # runtime.deploy(software_assistant) # CLI alternative: - # agentspan deploy --package examples.54_software_bug_assistant + # runtime.deploy(agent) from a release script # # 2. In a separate long-lived worker process: # runtime.serve(software_assistant) diff --git a/examples/agents/55_ml_engineering.py b/examples/agents/55_ml_engineering.py index fc6719d62..d39542d2b 100644 --- a/examples/agents/55_ml_engineering.py +++ b/examples/agents/55_ml_engineering.py @@ -15,14 +15,14 @@ python 55_ml_engineering.py Requirements: - - Agentspan server running - - OPENAI_API_KEY stored: agentspan credentials set OPENAI_API_KEY + - Conductor server running + - OPENAI_API_KEY stored: the Conductor server credential store """ import os from conductor.ai.agents import Agent, AgentRuntime -MODEL = os.environ.get("AGENTSPAN_LLM_MODEL", "anthropic/claude-sonnet-4-6") +MODEL = os.environ.get("CONDUCTOR_AGENT_LLM_MODEL", "anthropic/claude-sonnet-4-6") # ── Phase 1: Data Analysis ──────────────────────────────────────── @@ -99,7 +99,7 @@ # 1. Deploy once during CI/CD: # rt.deploy(ml_pipeline) # CLI alternative: - # agentspan deploy --package examples.55_ml_engineering + # runtime.deploy(agent) from a release script # # 2. In a separate long-lived worker process: # rt.serve(ml_pipeline) diff --git a/examples/agents/56_rag_agent.py b/examples/agents/56_rag_agent.py index 36ee4cb4a..4d07d2cc6 100644 --- a/examples/agents/56_rag_agent.py +++ b/examples/agents/56_rag_agent.py @@ -16,8 +16,8 @@ Requirements: - Conductor server with RAG system tasks enabled (--spring.profiles.active=rag) - A configured vector database (e.g., pgvector) - - AGENTSPAN_SERVER_URL=http://localhost:8080/api in .env or environment - - AGENTSPAN_LLM_MODEL=openai/gpt-4o-mini in .env or environment + - CONDUCTOR_SERVER_URL=http://localhost:8080/api in .env or environment + - CONDUCTOR_AGENT_LLM_MODEL=openai/gpt-4o-mini in .env or environment """ from conductor.ai.agents import Agent, AgentRuntime, search_tool, index_tool @@ -214,7 +214,7 @@ # 1. Deploy once during CI/CD: # runtime.deploy(rag_agent) # CLI alternative: - # agentspan deploy --package examples.56_rag_agent + # runtime.deploy(agent) from a release script # # 2. In a separate long-lived worker process: # runtime.serve(rag_agent) diff --git a/examples/agents/57_plan_dry_run.py b/examples/agents/57_plan_dry_run.py index 29ec25d11..894eb042f 100644 --- a/examples/agents/57_plan_dry_run.py +++ b/examples/agents/57_plan_dry_run.py @@ -14,8 +14,8 @@ Requirements: - Conductor server running - - AGENTSPAN_SERVER_URL=http://localhost:8080/api in .env or environment - - AGENTSPAN_LLM_MODEL=openai/gpt-4o-mini in .env or environment + - CONDUCTOR_SERVER_URL=http://localhost:8080/api in .env or environment + - CONDUCTOR_AGENT_LLM_MODEL=openai/gpt-4o-mini in .env or environment """ import json diff --git a/examples/agents/58_scatter_gather.py b/examples/agents/58_scatter_gather.py index bc84e670a..3b2a5c7b9 100644 --- a/examples/agents/58_scatter_gather.py +++ b/examples/agents/58_scatter_gather.py @@ -14,7 +14,7 @@ Requirements: - Conductor server running - - AGENTSPAN_SERVER_URL=http://localhost:8080/api in .env or environment + - CONDUCTOR_SERVER_URL=http://localhost:8080/api in .env or environment - AGENT_SECONDARY_LLM_MODEL=openai/gpt-4o in .env or environment """ @@ -134,7 +134,7 @@ def search_knowledge_base(query: str) -> dict: # 1. Deploy once during CI/CD: # runtime.deploy(coordinator) # CLI alternative: - # agentspan deploy --package examples.58_scatter_gather + # runtime.deploy(agent) from a release script # # 2. In a separate long-lived worker process: # runtime.serve(coordinator) diff --git a/examples/agents/59_coding_agent.py b/examples/agents/59_coding_agent.py index 44c917c48..30e15683b 100644 --- a/examples/agents/59_coding_agent.py +++ b/examples/agents/59_coding_agent.py @@ -20,7 +20,7 @@ Requirements: - Conductor server running - - AGENTSPAN_SERVER_URL=http://localhost:8080/api in .env or environment + - CONDUCTOR_SERVER_URL=http://localhost:8080/api in .env or environment """ from conductor.ai.agents import Agent, AgentRuntime, Strategy @@ -102,7 +102,7 @@ # 1. Deploy once during CI/CD: # runtime.deploy(coder) # CLI alternative: - # agentspan deploy --package examples.59_coding_agent + # runtime.deploy(agent) from a release script # # 2. In a separate long-lived worker process: # runtime.serve(coder) diff --git a/examples/agents/60_github_coding_agent.py b/examples/agents/60_github_coding_agent.py index ec7fef3ee..d41b3812c 100644 --- a/examples/agents/60_github_coding_agent.py +++ b/examples/agents/60_github_coding_agent.py @@ -26,7 +26,7 @@ Requirements: - Conductor server running - - AGENTSPAN_SERVER_URL=http://localhost:8080/api in .env or environment + - CONDUCTOR_SERVER_URL=http://localhost:8080/api in .env or environment - gh CLI authenticated (gh auth status) - Git configured with push access to the repo """ @@ -39,7 +39,7 @@ from conductor.ai.agents.handoff import OnTextMention from conductor.ai.agents.tool import tool -REPO = "agentspan/codingexamples" +REPO = "Conductor/codingexamples" WORK_DIR = f"/tmp/codingexamples-{uuid.uuid4().hex[:8]}" @@ -407,7 +407,7 @@ def create_pull_request(title: str, body: str, issue_number: int = 0) -> str: # 1. Deploy once during CI/CD: # runtime.deploy(coding_team) # CLI alternative: - # agentspan deploy --package examples.60_github_coding_agent + # runtime.deploy(agent) from a release script # # 2. In a separate long-lived worker process: # runtime.serve(coding_team) diff --git a/examples/agents/60a_github_coding_agent_simple.py b/examples/agents/60a_github_coding_agent_simple.py index 86e5a3843..674f9f402 100644 --- a/examples/agents/60a_github_coding_agent_simple.py +++ b/examples/agents/60a_github_coding_agent_simple.py @@ -27,7 +27,7 @@ Requirements: - Conductor server running - - AGENTSPAN_SERVER_URL=http://localhost:8080/api in .env or environment + - CONDUCTOR_SERVER_URL=http://localhost:8080/api in .env or environment - gh CLI authenticated (gh auth status) - Git configured with push access to the repo """ @@ -37,7 +37,7 @@ from conductor.ai.agents import Agent, AgentRuntime, Strategy from conductor.ai.agents.handoff import OnTextMention -REPO = "agentspan/codingexamples" +REPO = "Conductor/codingexamples" WORK_DIR = f"/tmp/codingexamples-{uuid.uuid4().hex[:8]}" # ── GitHub Agent: handles all git/gh operations ────────────────────── @@ -202,7 +202,7 @@ # 1. Deploy once during CI/CD: # runtime.deploy(coding_team) # CLI alternative: - # agentspan deploy --package examples.60a_github_coding_agent_simple + # runtime.deploy(agent) from a release script # # 2. In a separate long-lived worker process: # runtime.serve(coding_team) diff --git a/examples/agents/61_github_coding_agent_chained.py b/examples/agents/61_github_coding_agent_chained.py index fe43a86f6..f47f6f6d9 100644 --- a/examples/agents/61_github_coding_agent_chained.py +++ b/examples/agents/61_github_coding_agent_chained.py @@ -11,11 +11,11 @@ Run: python github_coding_agent.py # Deploy + serve - agentspan run github_pipeline "..." # Trigger (from another terminal) + Conductor run github_pipeline "..." # Trigger (from another terminal) Requirements: - - Agentspan server running - - GITHUB_TOKEN stored: agentspan credentials set GITHUB_TOKEN + - Conductor server running + - GITHUB_TOKEN stored: the Conductor server credential store - gh CLI installed """ @@ -24,7 +24,7 @@ from conductor.ai.agents.gate import TextGate from conductor.ai.agents.handoff import OnTextMention -REPO = "agentspan-ai/codingexamples" +REPO = "Conductor-ai/codingexamples" MODEL = "anthropic/claude-sonnet-4-6" @@ -184,7 +184,7 @@ def _pr_done(context: dict, **kwargs) -> bool: # 1. Deploy once during CI/CD: # rt.deploy(pipeline) # CLI alternative: - # agentspan deploy --package examples.61_github_coding_agent_chained + # runtime.deploy(agent) from a release script # # 2. In a separate long-lived worker process: # rt.serve(pipeline) diff --git a/examples/agents/61a_github_coding_agent_claude_code.py b/examples/agents/61a_github_coding_agent_claude_code.py index 952916d25..307d3c808 100644 --- a/examples/agents/61a_github_coding_agent_claude_code.py +++ b/examples/agents/61a_github_coding_agent_claude_code.py @@ -24,8 +24,8 @@ python 61a_github_coding_agent_claude_code.py Requirements: - - Agentspan server running - - GITHUB_TOKEN stored: agentspan credentials set GITHUB_TOKEN + - Conductor server running + - GITHUB_TOKEN stored: the Conductor server credential store - gh CLI installed - Claude Code SDK installed (pip install claude-code-sdk) """ @@ -34,7 +34,7 @@ from conductor.ai.agents.cli_config import CliConfig from conductor.ai.agents.gate import TextGate -REPO = "agentspan-ai/codingexamples" +REPO = "Conductor-ai/codingexamples" MODEL = "anthropic/claude-sonnet-4-6" @@ -185,7 +185,7 @@ def _pr_done(context: dict, **kwargs) -> bool: # 1. Deploy once during CI/CD: # rt.deploy(pipeline) # CLI alternative: - # agentspan deploy --package examples.61a_github_coding_agent_claude_code + # runtime.deploy(agent) from a release script # # 2. In a separate long-lived worker process: # rt.serve(pipeline) diff --git a/examples/agents/62_cli_tool_guardrails.py b/examples/agents/62_cli_tool_guardrails.py index 67399d3d5..06df409aa 100644 --- a/examples/agents/62_cli_tool_guardrails.py +++ b/examples/agents/62_cli_tool_guardrails.py @@ -25,7 +25,7 @@ Requirements: - Conductor server with LLM support - - AGENTSPAN_SERVER_URL=http://localhost:8080/api in .env or environment + - CONDUCTOR_SERVER_URL=http://localhost:8080/api in .env or environment """ from settings import settings @@ -96,7 +96,7 @@ # 1. Deploy once during CI/CD: # runtime.deploy(ops_agent) # CLI alternative: - # agentspan deploy --package examples.62_cli_tool_guardrails + # runtime.deploy(agent) from a release script # # 2. In a separate long-lived worker process: # runtime.serve(ops_agent) diff --git a/examples/agents/62_coding_agent_openai.py b/examples/agents/62_coding_agent_openai.py index 166222f52..19d193edb 100644 --- a/examples/agents/62_coding_agent_openai.py +++ b/examples/agents/62_coding_agent_openai.py @@ -1,12 +1,12 @@ # Copyright (c) 2025 Agentspan # Licensed under the MIT License. See LICENSE file in the project root for details. -"""Coding Agent (OpenAI fallback) — a Claude Code alternative via Agentspan. +"""Coding Agent (OpenAI fallback) — a Claude Code alternative via Conductor. Use this when Claude Code is unavailable (outages, rate limits, etc.). It provides the same core workflow — read/edit files, run shell commands, execute code, review changes — but runs on OpenAI GPT-4o (or any provider you set via -AGENTSPAN_LLM_MODEL). +CONDUCTOR_AGENT_LLM_MODEL). Architecture: coder ↔ qa_reviewer (SWARM — LLM-driven handoffs) @@ -31,15 +31,15 @@ python 62_coding_agent_openai.py Environment variables: - AGENTSPAN_SERVER_URL — Agentspan server (default: http://localhost:8080/api) - AGENTSPAN_LLM_MODEL — override model (default: openai/gpt-4o) + CONDUCTOR_SERVER_URL — Conductor server (default: http://localhost:8080/api) + CONDUCTOR_AGENT_LLM_MODEL — override model (default: openai/gpt-4o) OPENAI_API_KEY — required for default OpenAI model CODING_AGENT_CWD — working directory for file ops (default: current dir) Requirements: - - Agentspan server running (agentspan server start) - - AGENTSPAN_SERVER_URL set - - OPENAI_API_KEY set (or AGENTSPAN_LLM_MODEL pointing to another provider) + - Conductor server running (Conductor server start) + - CONDUCTOR_SERVER_URL set + - OPENAI_API_KEY set (or CONDUCTOR_AGENT_LLM_MODEL pointing to another provider) """ from __future__ import annotations @@ -56,7 +56,7 @@ # ── Configuration ───────────────────────────────────────────────────────────── -MODEL = os.environ.get("AGENTSPAN_LLM_MODEL", "openai/gpt-4o") +MODEL = os.environ.get("CONDUCTOR_AGENT_LLM_MODEL", "openai/gpt-4o") # Root directory that file tools operate within; agents see paths relative to it. WORKDIR = os.environ.get("CODING_AGENT_CWD", os.getcwd()) @@ -340,7 +340,7 @@ def search_code( def _banner() -> None: provider = MODEL.split("/")[0] if "/" in MODEL else MODEL print("=" * 60) - print(" Coding Agent (Agentspan fallback for Claude Code outages)") + print(" Coding Agent (Conductor fallback for Claude Code outages)") print(f" Model : {MODEL}") print(f" Workdir: {WORKDIR}") print("=" * 60) @@ -375,4 +375,4 @@ def _banner() -> None: # Production deployment pattern: # 1. Deploy once: runtime.deploy(coder) # 2. Serve workers: runtime.serve(coder) - # CLI: agentspan deploy --package examples.62_coding_agent_openai + # CLI: runtime.deploy(agent) from a release script diff --git a/examples/agents/63_deploy.py b/examples/agents/63_deploy.py index a77eee2e6..676760dd0 100644 --- a/examples/agents/63_deploy.py +++ b/examples/agents/63_deploy.py @@ -18,8 +18,8 @@ Requirements: - Conductor server running - - AGENTSPAN_SERVER_URL=http://localhost:8080/api in .env or environment - - AGENTSPAN_LLM_MODEL=openai/gpt-4o-mini in .env or environment + - CONDUCTOR_SERVER_URL=http://localhost:8080/api in .env or environment + - CONDUCTOR_AGENT_LLM_MODEL=openai/gpt-4o-mini in .env or environment """ from conductor.ai.agents import Agent, AgentRuntime, tool @@ -79,7 +79,7 @@ def check_status(service: str) -> str: # for info in results: # print(f"Deployed: {info.agent_name} -> {info.registered_name}") # CLI alternative: - # agentspan deploy --package examples.63_deploy + # runtime.deploy(agent) from a release script # # 2. In a separate long-lived worker process: # runtime.serve(doc_assistant, ops_bot) diff --git a/examples/agents/63b_serve.py b/examples/agents/63b_serve.py index c95f8d90d..7aa782271 100644 --- a/examples/agents/63b_serve.py +++ b/examples/agents/63b_serve.py @@ -21,8 +21,8 @@ Requirements: - Conductor server running - Agents already deployed (run 63_deploy.py first) - - AGENTSPAN_SERVER_URL=http://localhost:8080/api in .env or environment - - AGENTSPAN_LLM_MODEL=openai/gpt-4o-mini in .env or environment + - CONDUCTOR_SERVER_URL=http://localhost:8080/api in .env or environment + - CONDUCTOR_AGENT_LLM_MODEL=openai/gpt-4o-mini in .env or environment """ from conductor.ai.agents import Agent, AgentRuntime, tool @@ -80,7 +80,7 @@ def check_status(service: str) -> str: # 1. Deploy once during CI/CD: # runtime.deploy(doc_assistant, ops_bot) # CLI alternative: - # agentspan deploy --package examples.63b_serve + # runtime.deploy(agent) from a release script # # 2. In a separate long-lived worker process: # runtime.serve(doc_assistant, ops_bot) diff --git a/examples/agents/63c_run_by_name.py b/examples/agents/63c_run_by_name.py index 8341f9234..de8caf822 100644 --- a/examples/agents/63c_run_by_name.py +++ b/examples/agents/63c_run_by_name.py @@ -12,7 +12,7 @@ - Conductor server running - Agent deployed (run 63_deploy.py first) - Workers running (run 63b_serve.py in another terminal) - - AGENTSPAN_SERVER_URL=http://localhost:8080/api in .env or environment + - CONDUCTOR_SERVER_URL=http://localhost:8080/api in .env or environment """ from conductor.ai.agents import AgentRuntime @@ -27,7 +27,7 @@ # 1. Deploy once during CI/CD: # runtime.deploy(...) # CLI alternative: - # agentspan deploy --package examples.63c_run_by_name + # runtime.deploy(agent) from a release script # # 2. In a separate long-lived worker process: # runtime.serve(...) diff --git a/examples/agents/63d_serve_from_package.py b/examples/agents/63d_serve_from_package.py index e17014921..b2a966951 100644 --- a/examples/agents/63d_serve_from_package.py +++ b/examples/agents/63d_serve_from_package.py @@ -16,7 +16,7 @@ Requirements: - Conductor server running - - AGENTSPAN_SERVER_URL=http://localhost:8080/api in .env or environment + - CONDUCTOR_SERVER_URL=http://localhost:8080/api in .env or environment - A Python package with Agent instances at module level """ @@ -61,7 +61,7 @@ def health_check() -> str: # 1. Deploy once during CI/CD: # runtime.deploy(monitoring_agent, *discover_agents(["myapp.agents"])) # CLI alternative: - # agentspan deploy --package examples.63d_serve_from_package + # runtime.deploy(agent) from a release script # # 2. In a separate long-lived worker process: # runtime.serve(monitoring_agent, packages=["myapp.agents"]) diff --git a/examples/agents/63e_run_monitoring.py b/examples/agents/63e_run_monitoring.py index 9af81022c..6e78b2b61 100644 --- a/examples/agents/63e_run_monitoring.py +++ b/examples/agents/63e_run_monitoring.py @@ -10,7 +10,7 @@ Requirements: - Conductor server running - 63d_serve_from_package.py running in another terminal - - AGENTSPAN_SERVER_URL=http://localhost:8080/api in .env or environment + - CONDUCTOR_SERVER_URL=http://localhost:8080/api in .env or environment """ from conductor.ai.agents import AgentRuntime @@ -25,7 +25,7 @@ # 1. Deploy once during CI/CD: # runtime.deploy(...) # CLI alternative: - # agentspan deploy --package examples.63e_run_monitoring + # runtime.deploy(agent) from a release script # # 2. In a separate long-lived worker process: # runtime.serve(...) diff --git a/examples/agents/64_swarm_with_tools.py b/examples/agents/64_swarm_with_tools.py index 38f8c906f..8eb00f5c2 100644 --- a/examples/agents/64_swarm_with_tools.py +++ b/examples/agents/64_swarm_with_tools.py @@ -15,8 +15,8 @@ Requirements: - Conductor server with LLM support - - AGENTSPAN_SERVER_URL=http://localhost:8080/api in .env or environment - - AGENTSPAN_LLM_MODEL=openai/gpt-4o-mini in .env or environment + - CONDUCTOR_SERVER_URL=http://localhost:8080/api in .env or environment + - CONDUCTOR_AGENT_LLM_MODEL=openai/gpt-4o-mini in .env or environment """ from conductor.ai.agents import Agent, AgentRuntime, Strategy, tool @@ -112,7 +112,7 @@ def lookup_order(order_id: str) -> dict: # 1. Deploy once during CI/CD: # runtime.deploy(support) # CLI alternative: - # agentspan deploy --package examples.64_swarm_with_tools + # runtime.deploy(agent) from a release script # # 2. In a separate long-lived worker process: # runtime.serve(support) diff --git a/examples/agents/65_parallel_with_tools.py b/examples/agents/65_parallel_with_tools.py index 30dc2d26a..dd9fe51cd 100644 --- a/examples/agents/65_parallel_with_tools.py +++ b/examples/agents/65_parallel_with_tools.py @@ -17,8 +17,8 @@ Requirements: - Conductor server with LLM support - - AGENTSPAN_SERVER_URL=http://localhost:8080/api in .env or environment - - AGENTSPAN_LLM_MODEL=openai/gpt-4o-mini in .env or environment + - CONDUCTOR_SERVER_URL=http://localhost:8080/api in .env or environment + - CONDUCTOR_AGENT_LLM_MODEL=openai/gpt-4o-mini in .env or environment """ from conductor.ai.agents import Agent, AgentRuntime, Strategy, tool @@ -95,7 +95,7 @@ def lookup_order(order_id: str) -> dict: # 1. Deploy once during CI/CD: # runtime.deploy(analysis) # CLI alternative: - # agentspan deploy --package examples.65_parallel_with_tools + # runtime.deploy(agent) from a release script # # 2. In a separate long-lived worker process: # runtime.serve(analysis) diff --git a/examples/agents/66_handoff_to_parallel.py b/examples/agents/66_handoff_to_parallel.py index f205036cb..afa8c09c8 100644 --- a/examples/agents/66_handoff_to_parallel.py +++ b/examples/agents/66_handoff_to_parallel.py @@ -16,8 +16,8 @@ Requirements: - Conductor server with LLM support - - AGENTSPAN_SERVER_URL=http://localhost:8080/api in .env or environment - - AGENTSPAN_LLM_MODEL=openai/gpt-4o-mini in .env or environment + - CONDUCTOR_SERVER_URL=http://localhost:8080/api in .env or environment + - CONDUCTOR_AGENT_LLM_MODEL=openai/gpt-4o-mini in .env or environment """ from conductor.ai.agents import Agent, AgentRuntime, Strategy @@ -112,7 +112,7 @@ # 1. Deploy once during CI/CD: # runtime.deploy(coordinator) # CLI alternative: - # agentspan deploy --package examples.66_handoff_to_parallel + # runtime.deploy(agent) from a release script # # 2. In a separate long-lived worker process: # runtime.serve(coordinator) diff --git a/examples/agents/67_router_to_sequential.py b/examples/agents/67_router_to_sequential.py index be807ffa3..ed8d05049 100644 --- a/examples/agents/67_router_to_sequential.py +++ b/examples/agents/67_router_to_sequential.py @@ -20,8 +20,8 @@ Requirements: - Conductor server with LLM support - - AGENTSPAN_SERVER_URL=http://localhost:8080/api in .env or environment - - AGENTSPAN_LLM_MODEL=openai/gpt-4o-mini in .env or environment + - CONDUCTOR_SERVER_URL=http://localhost:8080/api in .env or environment + - CONDUCTOR_AGENT_LLM_MODEL=openai/gpt-4o-mini in .env or environment """ from conductor.ai.agents import Agent, AgentRuntime, Strategy @@ -124,7 +124,7 @@ # 1. Deploy once during CI/CD: # runtime.deploy(team) # CLI alternative: - # agentspan deploy --package examples.67_router_to_sequential + # runtime.deploy(agent) from a release script # # 2. In a separate long-lived worker process: # runtime.serve(team) diff --git a/examples/agents/68_context_condensation.py b/examples/agents/68_context_condensation.py index ce7b20f3a..54baf0265 100644 --- a/examples/agents/68_context_condensation.py +++ b/examples/agents/68_context_condensation.py @@ -26,7 +26,7 @@ --------------------------------------------- Add to ``server/src/main/resources/application.properties`` and restart:: - agentspan.default-context-window=10000 + Conductor.default-context-window=10000 Why: gpt-4o-mini has a 128 K context window; 25 × 800-token responses (~20 K tokens) would not overflow it naturally. Setting the window to 10 K forces @@ -35,9 +35,9 @@ agents that accumulate very large tool outputs. Requirements: - - Conductor server with LLM support + ``agentspan.default-context-window=10000`` - - AGENTSPAN_SERVER_URL=http://localhost:8080/api as environment variable - - AGENTSPAN_LLM_MODEL=openai/gpt-4o-mini as environment variable + - Conductor server with LLM support + ``Conductor.default-context-window=10000`` + - CONDUCTOR_SERVER_URL=http://localhost:8080/api as environment variable + - CONDUCTOR_AGENT_LLM_MODEL=openai/gpt-4o-mini as environment variable """ from conductor.ai.agents import Agent, AgentRuntime, agent_tool, tool @@ -382,7 +382,7 @@ def fetch_domain_data(domain: str) -> dict: # 1. Deploy once during CI/CD: # runtime.deploy(orchestrator) # CLI alternative: - # agentspan deploy --package examples.68_context_condensation + # runtime.deploy(agent) from a release script # # 2. In a separate long-lived worker process: # runtime.serve(orchestrator) diff --git a/examples/agents/70_ce_support_agent.py b/examples/agents/70_ce_support_agent.py index a52317af5..9da22f409 100644 --- a/examples/agents/70_ce_support_agent.py +++ b/examples/agents/70_ce_support_agent.py @@ -3,7 +3,7 @@ Takes a Zendesk ticket number and investigates across Zendesk, JIRA, HubSpot, Notion (runbooks), and GitHub to produce a solution with a priority rating. -Required credentials (set via `agentspan credentials set `): `):> +Required credentials (set via `the Conductor server credential store`): `):> ZENDESK_SUBDOMAIN – e.g. "mycompany" ZENDESK_EMAIL – admin email for API auth ZENDESK_API_TOKEN – Zendesk API token @@ -18,7 +18,7 @@ NOTION_RUNBOOK_DB_ID – Database ID of the runbooks database in Notion GITHUB_TOKEN – GitHub personal access token - GITHUB_ORG – GitHub organization name (e.g. "agentspan-dev") + GITHUB_ORG – GitHub organization name (e.g. "Conductor-dev") Usage: diff --git a/examples/agents/71_api_tool.py b/examples/agents/71_api_tool.py index 3df18ef55..9b3c599f5 100644 --- a/examples/agents/71_api_tool.py +++ b/examples/agents/71_api_tool.py @@ -23,15 +23,15 @@ # Or start with auth (requires storing the secret as a credential): mcp-testkit --transport http --auth - # Store credentials via CLI or Agentspan UI: - agentspan credentials set HTTP_TEST_API_KEY + # Store credentials via CLI or Conductor UI: + the Conductor server credential store Requirements: - Conductor server with LLM support - - AGENTSPAN_SERVER_URL=http://localhost:8080/api as environment variable - - AGENTSPAN_LLM_MODEL=openai/gpt-4o-mini as environment variable + - CONDUCTOR_SERVER_URL=http://localhost:8080/api as environment variable + - CONDUCTOR_AGENT_LLM_MODEL=openai/gpt-4o-mini as environment variable - mcp-testkit running on http://localhost:3001 (for examples 1-3, see setup above) - - For GitHub example: agentspan credentials set GITHUB_TOKEN ghp_xxx + - For GitHub example: the Conductor server credential store """ from conductor.ai.agents import Agent, AgentRuntime, api_tool, tool @@ -129,7 +129,7 @@ def calculate(expression: str) -> dict: # it needs. # # Before running: -# agentspan credentials set GITHUB_TOKEN ghp_xxxxxxxxxxxx +# the Conductor server credential store github = api_tool( url="https://api.github.com", @@ -171,7 +171,7 @@ def calculate(expression: str) -> dict: # 1. Deploy once during CI/CD: # runtime.deploy(math_agent) # CLI alternative: - # agentspan deploy --package examples.71_api_tool + # runtime.deploy(agent) from a release script # # 2. In a separate long-lived worker process: # runtime.serve(math_agent) diff --git a/examples/agents/72_client_reconnect.md b/examples/agents/72_client_reconnect.md index b8ebfa08f..077179daf 100644 --- a/examples/agents/72_client_reconnect.md +++ b/examples/agents/72_client_reconnect.md @@ -4,7 +4,7 @@ This demo proves that an agent execution survives a hard kill of the local SDK p ## Prerequisites -Start the Agentspan server with Docker Compose from the deployment branch or worktree: +Start a Conductor server with Docker Compose or the Conductor CLI: ```bash cd deployment/docker-compose @@ -26,8 +26,8 @@ pip install conductor-agent-sdk Set the server URL and model: ```bash -export AGENTSPAN_SERVER_URL=http://localhost:8080/api -export AGENTSPAN_LLM_MODEL=openai/gpt-4o-mini +export CONDUCTOR_SERVER_URL=http://localhost:8080/api +export CONDUCTOR_AGENT_LLM_MODEL=openai/gpt-4o-mini ``` ## Terminal 1: Start the agent @@ -41,7 +41,7 @@ Wait for: ```text Agent is durably paused on the server. Now hard-kill this client from another terminal with: - python 72_client_reconnect.py kill-client --client-info-file /tmp/agentspan_client_reconnect.client.json + python 72_client_reconnect.py kill-client --client-info-file /tmp/conductor_agent_client_reconnect.client.json ``` ## Terminal 2: Hard-kill the client diff --git a/examples/agents/72_client_reconnect.py b/examples/agents/72_client_reconnect.py index 5bb530aa5..d313ed78b 100644 --- a/examples/agents/72_client_reconnect.py +++ b/examples/agents/72_client_reconnect.py @@ -11,12 +11,12 @@ - Reconnecting later by execution_id and continuing the same workflow This proves client-process durability. The local Python process can die, but -the workflow state remains stored on the Agentspan/Conductor server. +the workflow state remains stored on the Conductor/Conductor server. Requirements: - - Agentspan server running - - AGENTSPAN_SERVER_URL=http://localhost:8080/api in environment - - AGENTSPAN_LLM_MODEL set (default: openai/gpt-4o-mini via settings.py) + - Conductor server running + - CONDUCTOR_SERVER_URL=http://localhost:8080/api in environment + - CONDUCTOR_AGENT_LLM_MODEL set (default: openai/gpt-4o-mini via settings.py) - Provider API key configured on the server (for example OPENAI_API_KEY) """ @@ -33,8 +33,8 @@ from conductor.ai.agents import Agent, AgentRuntime, tool from settings import settings -DEFAULT_WORKFLOW_FILE = Path("/tmp/agentspan_client_reconnect.execution_id") -DEFAULT_CLIENT_INFO_FILE = Path("/tmp/agentspan_client_reconnect.client.json") +DEFAULT_WORKFLOW_FILE = Path("/tmp/Conductor_client_reconnect.execution_id") +DEFAULT_CLIENT_INFO_FILE = Path("/tmp/Conductor_client_reconnect.client.json") @tool(approval_required=True) @@ -89,7 +89,7 @@ def run_once(prompt: str) -> None: # 1. Deploy once during CI/CD: # runtime.deploy(agent) # CLI alternative: - # agentspan deploy --package examples.72_client_reconnect + # runtime.deploy(agent) from a release script # # 2. In a separate long-lived worker process: # runtime.serve(agent) diff --git a/examples/agents/73_worker_restart_recovery.md b/examples/agents/73_worker_restart_recovery.md index df207eb85..77785b41d 100644 --- a/examples/agents/73_worker_restart_recovery.md +++ b/examples/agents/73_worker_restart_recovery.md @@ -4,7 +4,7 @@ This demo proves that an agent execution survives worker-service outage and cont ## Prerequisites -Start the Agentspan server with Docker Compose from the deployment branch or worktree: +Start a Conductor server with Docker Compose or the Conductor CLI: ```bash cd deployment/docker-compose @@ -26,8 +26,8 @@ pip install conductor-agent-sdk Set the server URL and model: ```bash -export AGENTSPAN_SERVER_URL=http://localhost:8080/api -export AGENTSPAN_LLM_MODEL=openai/gpt-4o-mini +export CONDUCTOR_SERVER_URL=http://localhost:8080/api +export CONDUCTOR_AGENT_LLM_MODEL=openai/gpt-4o-mini ``` ## Terminal 1: Deploy the agent definition @@ -42,7 +42,7 @@ python 73_worker_restart_recovery.py deploy python 73_worker_restart_recovery.py serve ``` -This writes the worker PID and process group to `/tmp/agentspan_worker_restart.worker.json`. +This writes the worker PID and process group to `/tmp/conductor_agent_worker_restart.worker.json`. ## Terminal 3: Kill the worker service @@ -72,7 +72,7 @@ python 73_worker_restart_recovery.py serve python 73_worker_restart_recovery.py status ``` -The attempt history file at `/tmp/agentspan_worker_restart.attempts.json` should eventually show: +The attempt history file at `/tmp/conductor_agent_worker_restart.attempts.json` should eventually show: - no attempts while the worker service is down - attempt 1 starts and completes after the worker service comes back diff --git a/examples/agents/73_worker_restart_recovery.py b/examples/agents/73_worker_restart_recovery.py index 816c5871d..a416d02ee 100644 --- a/examples/agents/73_worker_restart_recovery.py +++ b/examples/agents/73_worker_restart_recovery.py @@ -10,13 +10,13 @@ - Watching the same workflow complete after the worker service returns This proves worker-service durability. The workflow remains stored on the -Agentspan/Conductor server while Python tool workers are unavailable, and it +Conductor/Conductor server while Python tool workers are unavailable, and it continues when a worker service comes back online. Requirements: - - Agentspan server running - - AGENTSPAN_SERVER_URL=http://localhost:8080/api in environment - - AGENTSPAN_LLM_MODEL set (default: openai/gpt-4o-mini via settings.py) + - Conductor server running + - CONDUCTOR_SERVER_URL=http://localhost:8080/api in environment + - CONDUCTOR_AGENT_LLM_MODEL set (default: openai/gpt-4o-mini via settings.py) - Provider API key configured on the server (for example OPENAI_API_KEY) """ @@ -32,9 +32,9 @@ from conductor.ai.agents import Agent, AgentRuntime, tool from settings import settings -DEFAULT_WORKFLOW_FILE = Path("/tmp/agentspan_worker_restart.execution_id") -DEFAULT_WORKER_INFO_FILE = Path("/tmp/agentspan_worker_restart.worker.json") -DEFAULT_ATTEMPT_FILE = Path("/tmp/agentspan_worker_restart.attempts.json") +DEFAULT_WORKFLOW_FILE = Path("/tmp/Conductor_worker_restart.execution_id") +DEFAULT_WORKER_INFO_FILE = Path("/tmp/Conductor_worker_restart.worker.json") +DEFAULT_ATTEMPT_FILE = Path("/tmp/Conductor_worker_restart.attempts.json") def now_iso() -> str: @@ -129,7 +129,7 @@ def run_once() -> None: # 1. Deploy once during CI/CD: # runtime.deploy(agent) # CLI alternative: - # agentspan deploy --package examples.73_worker_restart_recovery + # runtime.deploy(agent) from a release script # # 2. In a separate long-lived worker process: # runtime.serve(agent) diff --git a/examples/agents/74_cli_error_output.py b/examples/agents/74_cli_error_output.py index cec00b9c8..4f0a586db 100644 --- a/examples/agents/74_cli_error_output.py +++ b/examples/agents/74_cli_error_output.py @@ -7,8 +7,8 @@ Requirements: - Conductor server with LLM support - - AGENTSPAN_SERVER_URL (e.g. http://localhost:8080/api) - - AGENTSPAN_LLM_MODEL (e.g. openai/gpt-4o-mini) + - CONDUCTOR_SERVER_URL (e.g. http://localhost:8080/api) + - CONDUCTOR_AGENT_LLM_MODEL (e.g. openai/gpt-4o-mini) """ from conductor.ai.agents import Agent, AgentRuntime @@ -48,7 +48,7 @@ # 1. Deploy once during CI/CD: # rt.deploy(agent) # CLI alternative: - # agentspan deploy --package examples.74_cli_error_output + # runtime.deploy(agent) from a release script # # 2. In a separate long-lived worker process: # rt.serve(agent) diff --git a/examples/agents/75_wait_for_message.py b/examples/agents/75_wait_for_message.py index 78ae57693..b3ad0f4f4 100644 --- a/examples/agents/75_wait_for_message.py +++ b/examples/agents/75_wait_for_message.py @@ -14,14 +14,14 @@ Requirements: - Conductor server with WMQ support (conductor.workflow-message-queue.enabled=true) - - AGENTSPAN_SERVER_URL=http://localhost:8080/api as environment variable - - AGENTSPAN_LLM_MODEL=openai/gpt-4o-mini as environment variable + - CONDUCTOR_SERVER_URL=http://localhost:8080/api as environment variable + - CONDUCTOR_AGENT_LLM_MODEL=openai/gpt-4o-mini as environment variable """ import os import time -os.environ.setdefault("AGENTSPAN_LOG_LEVEL", "WARNING") +os.environ.setdefault("CONDUCTOR_AGENT_LOG_LEVEL", "WARNING") from conductor.ai.agents import Agent, AgentRuntime, wait_for_message_tool, tool from settings import settings diff --git a/examples/agents/76_wait_for_message_streaming.py b/examples/agents/76_wait_for_message_streaming.py index 4d5cf9632..f443cac75 100644 --- a/examples/agents/76_wait_for_message_streaming.py +++ b/examples/agents/76_wait_for_message_streaming.py @@ -13,16 +13,16 @@ drives the conversation by sending messages and reading streamed events. Requirements: - - AgentSpan server running at http://localhost:8080 - - AGENTSPAN_SERVER_URL=http://localhost:8080/api as environment variable - - AGENTSPAN_LLM_MODEL=openai/gpt-4o-mini as environment variable + - Conductor server running at http://localhost:8080 + - CONDUCTOR_SERVER_URL=http://localhost:8080/api as environment variable + - CONDUCTOR_AGENT_LLM_MODEL=openai/gpt-4o-mini as environment variable """ import os import threading import time -os.environ.setdefault("AGENTSPAN_LOG_LEVEL", "WARNING") +os.environ.setdefault("CONDUCTOR_AGENT_LOG_LEVEL", "WARNING") from conductor.ai.agents import Agent, AgentRuntime, EventType, wait_for_message_tool, tool from settings import settings diff --git a/examples/agents/77_kafka_consumer_agent.py b/examples/agents/77_kafka_consumer_agent.py index 30f27b963..fc4271942 100644 --- a/examples/agents/77_kafka_consumer_agent.py +++ b/examples/agents/77_kafka_consumer_agent.py @@ -16,9 +16,9 @@ Requirements: - Kafka broker on localhost:9092 with topic le_random_topic - - AgentSpan server running at http://localhost:8080 - - AGENTSPAN_SERVER_URL=http://localhost:8080/api as environment variable - - AGENTSPAN_LLM_MODEL=openai/gpt-4o-mini as environment variable + - Conductor server running at http://localhost:8080 + - CONDUCTOR_SERVER_URL=http://localhost:8080/api as environment variable + - CONDUCTOR_AGENT_LLM_MODEL=openai/gpt-4o-mini as environment variable - confluent-kafka (uv pip install confluent-kafka) """ @@ -29,7 +29,7 @@ KAFKA_BOOTSTRAP = "localhost:9092" KAFKA_TOPIC = "le_random_topic" -KAFKA_GROUP = "agentspan-echo-group" +KAFKA_GROUP = "Conductor-echo-group" @tool diff --git a/examples/agents/78_approval_workflow.py b/examples/agents/78_approval_workflow.py index 2275f22ce..014ea3d4d 100644 --- a/examples/agents/78_approval_workflow.py +++ b/examples/agents/78_approval_workflow.py @@ -34,9 +34,9 @@ blocks on flag_for_approval until the operator responds. Requirements: - - AgentSpan server running at http://localhost:8080 - - AGENTSPAN_SERVER_URL=http://localhost:8080/api as environment variable - - AGENTSPAN_LLM_MODEL=openai/gpt-4o-mini as environment variable + - Conductor server running at http://localhost:8080 + - CONDUCTOR_SERVER_URL=http://localhost:8080/api as environment variable + - CONDUCTOR_AGENT_LLM_MODEL=openai/gpt-4o-mini as environment variable """ import json @@ -46,7 +46,7 @@ import time from pathlib import Path -os.environ.setdefault("AGENTSPAN_LOG_LEVEL", "WARNING") +os.environ.setdefault("CONDUCTOR_AGENT_LOG_LEVEL", "WARNING") from conductor.ai.agents import Agent, AgentRuntime, tool, wait_for_message_tool from settings import settings diff --git a/examples/agents/79_agent_message_bus.py b/examples/agents/79_agent_message_bus.py index e31dcea69..8449e8afd 100644 --- a/examples/agents/79_agent_message_bus.py +++ b/examples/agents/79_agent_message_bus.py @@ -29,9 +29,9 @@ Researcher autonomously drives the Writer. Requirements: - - AgentSpan server running at http://localhost:8080 - - AGENTSPAN_SERVER_URL=http://localhost:8080/api as environment variable - - AGENTSPAN_LLM_MODEL=openai/gpt-4o-mini as environment variable + - Conductor server running at http://localhost:8080 + - CONDUCTOR_SERVER_URL=http://localhost:8080/api as environment variable + - CONDUCTOR_AGENT_LLM_MODEL=openai/gpt-4o-mini as environment variable """ import os @@ -40,7 +40,7 @@ import time from pathlib import Path -os.environ.setdefault("AGENTSPAN_LOG_LEVEL", "WARNING") +os.environ.setdefault("CONDUCTOR_AGENT_LOG_LEVEL", "WARNING") from conductor.ai.agents import Agent, AgentRuntime, tool, wait_for_message_tool from settings import settings diff --git a/examples/agents/80_live_dashboard.py b/examples/agents/80_live_dashboard.py index a0166899d..4d63a8936 100644 --- a/examples/agents/80_live_dashboard.py +++ b/examples/agents/80_live_dashboard.py @@ -38,9 +38,9 @@ content pipeline. Requirements: - - AgentSpan server running at http://localhost:8080 - - AGENTSPAN_SERVER_URL=http://localhost:8080/api as environment variable - - AGENTSPAN_LLM_MODEL=anthropic/claude-sonnet-4-20250514 as environment variable + - Conductor server running at http://localhost:8080 + - CONDUCTOR_SERVER_URL=http://localhost:8080/api as environment variable + - CONDUCTOR_AGENT_LLM_MODEL=anthropic/claude-sonnet-4-20250514 as environment variable """ import json @@ -52,7 +52,7 @@ import time from pathlib import Path -os.environ.setdefault("AGENTSPAN_LOG_LEVEL", "WARNING") +os.environ.setdefault("CONDUCTOR_AGENT_LOG_LEVEL", "WARNING") from settings import settings diff --git a/examples/agents/81_chat_repl.py b/examples/agents/81_chat_repl.py index 36333613b..d0eec4e55 100644 --- a/examples/agents/81_chat_repl.py +++ b/examples/agents/81_chat_repl.py @@ -44,9 +44,9 @@ bullet_split — split input into one bullet point per sentence Requirements: - - AgentSpan server running at http://localhost:8080 - - AGENTSPAN_SERVER_URL=http://localhost:8080/api as environment variable - - AGENTSPAN_LLM_MODEL=anthropic/claude-sonnet-4-20250514 as environment variable + - Conductor server running at http://localhost:8080 + - CONDUCTOR_SERVER_URL=http://localhost:8080/api as environment variable + - CONDUCTOR_AGENT_LLM_MODEL=anthropic/claude-sonnet-4-20250514 as environment variable """ import argparse @@ -57,8 +57,8 @@ import time from pathlib import Path -# Keep conductor worker startup logs silent by default; set AGENTSPAN_LOG_LEVEL=INFO to see them. -os.environ.setdefault("AGENTSPAN_LOG_LEVEL", "WARNING") +# Keep conductor worker startup logs silent by default; set CONDUCTOR_AGENT_LOG_LEVEL=INFO to see them. +os.environ.setdefault("CONDUCTOR_AGENT_LOG_LEVEL", "WARNING") from settings import settings @@ -94,7 +94,7 @@ )), } -SESSION_FILE = Path("/tmp/agentspan_chat_repl.session") +SESSION_FILE = Path("/tmp/Conductor_chat_repl.session") # --------------------------------------------------------------------------- # Filesystem IPC setup diff --git a/examples/agents/82_coding_agent.py b/examples/agents/82_coding_agent.py index 830fb283a..19636e402 100644 --- a/examples/agents/82_coding_agent.py +++ b/examples/agents/82_coding_agent.py @@ -1,7 +1,7 @@ # Copyright (c) 2025 Agentspan # Licensed under the MIT License. See LICENSE file in the project root for details. -"""Coding Agent REPL — a filesystem-aware coding assistant backed by AgentSpan runtime. +"""Coding Agent REPL — a filesystem-aware coding assistant backed by Conductor runtime. This example is a Claude Code-style assistant you can actually use in a working session. It runs as a durable Conductor workflow, giving you things a local agent cannot: @@ -18,9 +18,9 @@ python 82_coding_agent.py --resume # resume last session Requirements: - - AgentSpan server running at http://localhost:8080 - - AGENTSPAN_SERVER_URL=http://localhost:8080/api - - AGENTSPAN_LLM_MODEL=anthropic/claude-sonnet-4-20250514 + - Conductor server running at http://localhost:8080 + - CONDUCTOR_SERVER_URL=http://localhost:8080/api + - CONDUCTOR_AGENT_LLM_MODEL=anthropic/claude-sonnet-4-20250514 """ import argparse @@ -31,7 +31,7 @@ import threading from pathlib import Path -os.environ.setdefault("AGENTSPAN_LOG_LEVEL", "WARNING") +os.environ.setdefault("CONDUCTOR_AGENT_LOG_LEVEL", "WARNING") from conductor.ai.agents import Agent, AgentRuntime, EventType, tool, wait_for_message_tool from settings import settings @@ -40,7 +40,7 @@ # Constants # --------------------------------------------------------------------------- -SESSION_FILE = Path("/tmp/agentspan_coding_agent.session") +SESSION_FILE = Path("/tmp/Conductor_coding_agent.session") _DEFAULT_SHELL_TIMEOUT = 30 # seconds per shell command _MAX_FILE_BYTES = 200_000 # 200 KB — refuse larger files in read_file _MAX_SHELL_OUTPUT = 8_000 # truncate shell output shown to the LLM diff --git a/examples/agents/82_fan_out_fan_in.py b/examples/agents/82_fan_out_fan_in.py index 04ae5d582..71b9f6f3f 100644 --- a/examples/agents/82_fan_out_fan_in.py +++ b/examples/agents/82_fan_out_fan_in.py @@ -32,8 +32,8 @@ aggregates the three answers into a side-by-side comparison report. Requirements: - - AgentSpan server running (CONDUCTOR_SERVER_URL / AGENTSPAN_SERVER_URL) - - AGENTSPAN_LLM_MODEL set to a working model + - Conductor server running (CONDUCTOR_SERVER_URL / CONDUCTOR_SERVER_URL) + - CONDUCTOR_AGENT_LLM_MODEL set to a working model """ import json diff --git a/examples/agents/82b_coding_agent_tui.py b/examples/agents/82b_coding_agent_tui.py index 9aa1a1d70..e0b2341bb 100644 --- a/examples/agents/82b_coding_agent_tui.py +++ b/examples/agents/82b_coding_agent_tui.py @@ -19,9 +19,9 @@ python 82b_coding_agent_tui.py --cwd /path/to/repo Requirements: - - AgentSpan server running at http://localhost:8080 - - AGENTSPAN_SERVER_URL=http://localhost:8080/api - - AGENTSPAN_LLM_MODEL=openai/gpt-4o + - Conductor server running at http://localhost:8080 + - CONDUCTOR_SERVER_URL=http://localhost:8080/api + - CONDUCTOR_AGENT_LLM_MODEL=openai/gpt-4o """ import argparse @@ -34,7 +34,7 @@ from dataclasses import dataclass, field from pathlib import Path -os.environ.setdefault("AGENTSPAN_LOG_LEVEL", "WARNING") +os.environ.setdefault("CONDUCTOR_AGENT_LOG_LEVEL", "WARNING") from prompt_toolkit import Application from prompt_toolkit.buffer import Buffer @@ -49,7 +49,7 @@ # Constants # --------------------------------------------------------------------------- -SESSION_FILE = Path("/tmp/agentspan_coding_agent_tui.session") +SESSION_FILE = Path("/tmp/Conductor_coding_agent_tui.session") _DEFAULT_SHELL_TIMEOUT = 30 _MAX_FILE_BYTES = 200_000 _MAX_SHELL_OUTPUT = 8_000 diff --git a/examples/agents/83_stateful_resume.py b/examples/agents/83_stateful_resume.py index 6bc0b708b..ecf46c320 100644 --- a/examples/agents/83_stateful_resume.py +++ b/examples/agents/83_stateful_resume.py @@ -32,8 +32,8 @@ Requirements: - Conductor server with WMQ support (conductor.workflow-message-queue.enabled=true) - - AGENTSPAN_SERVER_URL=http://localhost:8080/api as environment variable - - AGENTSPAN_LLM_MODEL=openai/gpt-4o-mini as environment variable + - CONDUCTOR_SERVER_URL=http://localhost:8080/api as environment variable + - CONDUCTOR_AGENT_LLM_MODEL=openai/gpt-4o-mini as environment variable """ import time @@ -41,7 +41,7 @@ from conductor.ai.agents import Agent, AgentRuntime, tool, wait_for_message_tool from settings import settings -SESSION_FILE = "/tmp/agentspan_stateful_resume.session" +SESSION_FILE = "/tmp/Conductor_stateful_resume.session" @tool diff --git a/examples/agents/84_deterministic_stop.py b/examples/agents/84_deterministic_stop.py index 79bf35a8e..a32243925 100644 --- a/examples/agents/84_deterministic_stop.py +++ b/examples/agents/84_deterministic_stop.py @@ -31,15 +31,15 @@ The LLM could ignore this. handle.stop() makes this unnecessary. Requirements: - - Agentspan server (with _stop_requested support in compiler) - - AGENTSPAN_SERVER_URL=http://localhost:8080/api as environment variable - - AGENTSPAN_LLM_MODEL=openai/gpt-4o-mini as environment variable + - Conductor server (with _stop_requested support in compiler) + - CONDUCTOR_SERVER_URL=http://localhost:8080/api as environment variable + - CONDUCTOR_AGENT_LLM_MODEL=openai/gpt-4o-mini as environment variable """ import os import time -os.environ.setdefault("AGENTSPAN_LOG_LEVEL", "WARNING") +os.environ.setdefault("CONDUCTOR_AGENT_LOG_LEVEL", "WARNING") from conductor.ai.agents import Agent, AgentRuntime, tool, wait_for_message_tool from settings import settings diff --git a/examples/agents/85_plan_execute_harness.py b/examples/agents/85_plan_execute_harness.py index 6a5ef3adf..2041e22ec 100644 --- a/examples/agents/85_plan_execute_harness.py +++ b/examples/agents/85_plan_execute_harness.py @@ -40,9 +40,9 @@ python 85_plan_execute_harness.py "Climate change mitigation strategies for 2030" Requirements: - - Agentspan server with PLAN_EXECUTE strategy support - - AGENTSPAN_SERVER_URL=http://localhost:8080/api as environment variable - - AGENTSPAN_LLM_MODEL set (or defaults to openai/gpt-4o-mini) + - Conductor server with PLAN_EXECUTE strategy support + - CONDUCTOR_SERVER_URL=http://localhost:8080/api as environment variable + - CONDUCTOR_AGENT_LLM_MODEL set (or defaults to openai/gpt-4o-mini) """ import json diff --git a/examples/agents/86_coding_agent.py b/examples/agents/86_coding_agent.py index 75bc287f2..3e8d091ad 100644 --- a/examples/agents/86_coding_agent.py +++ b/examples/agents/86_coding_agent.py @@ -74,9 +74,9 @@ python 86_coding_agent.py "Fix the failing test in tests/test_math.py" Requirements: - - Agentspan server with PLAN_EXECUTE strategy support - - AGENTSPAN_SERVER_URL=http://localhost:8080/api - - AGENTSPAN_LLM_MODEL set (or defaults to openai/gpt-4o-mini) + - Conductor server with PLAN_EXECUTE strategy support + - CONDUCTOR_SERVER_URL=http://localhost:8080/api + - CONDUCTOR_AGENT_LLM_MODEL set (or defaults to openai/gpt-4o-mini) """ import os @@ -234,7 +234,7 @@ def write_coder_plan(content: str) -> str: # ── Executor tools — declared on the harness, called by the compiled plan ──── # The planner does NOT have these. They are declared on the ``coder`` harness -# via ``tools=`` so Agentspan registers their Conductor task definitions. +# via ``tools=`` so Conductor registers their Conductor task definitions. # The compiled plan calls them by name as SIMPLE tasks. @@ -381,7 +381,7 @@ def write_file(path: str, content: str) -> str: ) # The harness: PLAN_EXECUTE with planner only (no fallback). -# tools= declares the executor tools so Agentspan registers their task +# tools= declares the executor tools so Conductor registers their task # definitions; the compiled plan calls them as SIMPLE Conductor tasks. coder = Agent( name="coder", diff --git a/examples/agents/90_guardrail_e2e_tests.py b/examples/agents/90_guardrail_e2e_tests.py index 5c740e01f..0cf45b7e2 100644 --- a/examples/agents/90_guardrail_e2e_tests.py +++ b/examples/agents/90_guardrail_e2e_tests.py @@ -51,8 +51,8 @@ Requirements: - Conductor server running - - AGENTSPAN_SERVER_URL=http://localhost:8080/api in .env or environment - - AGENTSPAN_LLM_MODEL in .env or environment + - CONDUCTOR_SERVER_URL=http://localhost:8080/api in .env or environment + - CONDUCTOR_AGENT_LLM_MODEL in .env or environment """ import sys diff --git a/examples/agents/91_slack_autofix_agent.py b/examples/agents/91_slack_autofix_agent.py index e73d96ff8..c0f33fbe3 100644 --- a/examples/agents/91_slack_autofix_agent.py +++ b/examples/agents/91_slack_autofix_agent.py @@ -18,13 +18,13 @@ └── pr_creator — creates branch + commit + PR Requirements: - - AGENTSPAN_SERVER_URL=http://localhost:8080/api - - AGENTSPAN_LLM_MODEL=anthropic/claude-opus-4-6 (or gpt-4o) + - CONDUCTOR_SERVER_URL=http://localhost:8080/api + - CONDUCTOR_AGENT_LLM_MODEL=anthropic/claude-opus-4-6 (or gpt-4o) - SLACK_BOT_TOKEN=xoxb-... (Bot token with channels:read, channels:history) - SLACK_CHANNEL_ID=C... (Channel to monitor) - GITHUB_TOKEN=ghp_... (Token with repo write access) - REPO_PATH=/path/to/repo (Local path to the codebase) - - GITHUB_REPO=owner/repo (e.g. agentspan-ai/agentspan) + - GITHUB_REPO=owner/repo (e.g. Conductor-ai/Conductor) Usage: # Run once — picks up latest unprocessed bug report @@ -56,7 +56,7 @@ # ── State file — tracks last processed Slack message ─────────────────────── -STATE_FILE = Path("/tmp/agentspan_autofix_state.json") +STATE_FILE = Path("/tmp/Conductor_autofix_state.json") def _load_state() -> dict: @@ -335,7 +335,7 @@ def post_slack_reply(channel: str, thread_ts: str, message: str) -> str: model=settings.llm_model, tools=[search_codebase, read_file, run_git_command], instructions=""" -You are a senior engineer investigating a bug in the Agentspan codebase. +You are a senior engineer investigating a bug in the Conductor codebase. Given a bug description, you: 1. Search the codebase to find the relevant files diff --git a/examples/agents/92_openai_agents_compat.py b/examples/agents/92_openai_agents_compat.py index b68537706..e2d0dde01 100644 --- a/examples/agents/92_openai_agents_compat.py +++ b/examples/agents/92_openai_agents_compat.py @@ -3,13 +3,13 @@ """OpenAI Agents SDK compatibility — drop-in Runner replacement. -Shows how to migrate an openai-agents script to Agentspan by changing +Shows how to migrate an openai-agents script to Conductor by changing one import line. Everything else stays identical. Before (runs directly against OpenAI): from agents import Runner -After (runs on Agentspan — durable, observable, scalable): +After (runs on Conductor — durable, observable, scalable): from conductor.ai import Runner The rest of the code — Agent definition, @function_tool decorators, @@ -22,20 +22,20 @@ from conductor.ai import Runner # ← change this one line from agents import Agent, function_tool # ← unchanged -Pattern B — use Agentspan for everything (no openai-agents dependency):: +Pattern B — use Conductor for everything (no openai-agents dependency):: from conductor.ai import Runner, function_tool from conductor.ai.agents import Agent Requirements: - - AGENTSPAN_SERVER_URL=http://localhost:8080/api - - AGENTSPAN_LLM_MODEL=openai/gpt-4o (or anthropic/claude-opus-4-6) + - CONDUCTOR_SERVER_URL=http://localhost:8080/api + - CONDUCTOR_AGENT_LLM_MODEL=openai/gpt-4o (or anthropic/claude-opus-4-6) Usage: # Pattern A (requires openai-agents installed: uv add openai-agents) python 92_openai_agents_compat.py --pattern a - # Pattern B (Agentspan only, no openai-agents needed) + # Pattern B (Conductor only, no openai-agents needed) python 92_openai_agents_compat.py --pattern b python 92_openai_agents_compat.py # default: pattern b """ @@ -75,10 +75,10 @@ def get_time(timezone: str) -> str: return f"Unknown timezone: {timezone}" -# ── Pattern B — pure Agentspan, no openai-agents dependency ──────────────── +# ── Pattern B — pure Conductor, no openai-agents dependency ──────────────── def run_pattern_b() -> None: - """Run using Agentspan's own Agent and function_tool (same result).""" + """Run using Conductor's own Agent and function_tool (same result).""" from conductor.ai import Runner from conductor.ai.agents import Agent from settings import settings @@ -100,7 +100,7 @@ def run_pattern_b() -> None: # ── Pattern A — keep openai-agents Agent/function_tool, swap only Runner ─── def run_pattern_a() -> None: - """Run with openai-agents Agent but Agentspan's Runner. + """Run with openai-agents Agent but Conductor's Runner. Requires: uv add openai-agents """ @@ -114,7 +114,7 @@ def run_pattern_a() -> None: # ── The ONE line you change ──────────────────────────────────────────── # from agents import Runner # ← original openai-agents import - from conductor.ai import Runner # ← drop-in Agentspan replacement + from conductor.ai import Runner # ← drop-in Conductor replacement @function_tool def get_weather(city: str) -> str: @@ -161,7 +161,7 @@ def get_time(timezone: str) -> str: "--pattern", choices=["a", "b"], default="b", - help="a = openai-agents Agent + Agentspan Runner; b = pure Agentspan (default)", + help="a = openai-agents Agent + Conductor Runner; b = pure Conductor (default)", ) args = parser.parse_args() diff --git a/examples/agents/93_openai_runner_hello_world.py b/examples/agents/93_openai_runner_hello_world.py index ce00e5091..517690ed0 100644 --- a/examples/agents/93_openai_runner_hello_world.py +++ b/examples/agents/93_openai_runner_hello_world.py @@ -9,7 +9,7 @@ Before (runs directly against OpenAI): from agents import Runner -After (runs on Agentspan — durable, observable, scalable): +After (runs on Conductor — durable, observable, scalable): from conductor.ai import Runner The diff: @@ -20,8 +20,8 @@ Requirements: - uv add openai-agents - - AGENTSPAN_SERVER_URL=http://localhost:8080/api - - AGENTSPAN_LLM_MODEL=openai/gpt-4o (or any supported model) + - CONDUCTOR_SERVER_URL=http://localhost:8080/api + - CONDUCTOR_AGENT_LLM_MODEL=openai/gpt-4o (or any supported model) Usage: python 93_openai_runner_hello_world.py @@ -33,7 +33,7 @@ # ── Only this line changes ────────────────────────────────────────────────── # from agents import Runner # ← original (runs directly on OpenAI) -from conductor.ai import Runner # ← agentspan (runs on Agentspan) +from conductor.ai import Runner # ← Conductor (runs on Conductor) # ─────────────────────────────────────────────────────────────────────────── diff --git a/examples/agents/94_openai_runner_tools.py b/examples/agents/94_openai_runner_tools.py index 977aaf9e0..36d4019c6 100644 --- a/examples/agents/94_openai_runner_tools.py +++ b/examples/agents/94_openai_runner_tools.py @@ -9,7 +9,7 @@ Before (runs directly against OpenAI): from agents import Runner -After (runs on Agentspan — durable, observable, scalable): +After (runs on Conductor — durable, observable, scalable): from conductor.ai import Runner The diff: @@ -17,14 +17,14 @@ +from conductor.ai import Runner @function_tool decorators, Agent definition, and result.final_output -are completely unchanged. Agentspan executes each tool call as a durable +are completely unchanged. Conductor executes each tool call as a durable worker task — if the process crashes mid-run, execution resumes from the last successful tool call. Requirements: - uv add openai-agents - - AGENTSPAN_SERVER_URL=http://localhost:8080/api - - AGENTSPAN_LLM_MODEL=openai/gpt-4o + - CONDUCTOR_SERVER_URL=http://localhost:8080/api + - CONDUCTOR_AGENT_LLM_MODEL=openai/gpt-4o Usage: python 94_openai_runner_tools.py @@ -39,7 +39,7 @@ # ── Only this line changes ────────────────────────────────────────────────── # from agents import Runner # ← original (runs directly on OpenAI) -from conductor.ai import Runner # ← agentspan (runs on Agentspan) +from conductor.ai import Runner # ← Conductor (runs on Conductor) # ─────────────────────────────────────────────────────────────────────────── diff --git a/examples/agents/95_openai_runner_handoffs.py b/examples/agents/95_openai_runner_handoffs.py index 5d8624f73..3f9559668 100644 --- a/examples/agents/95_openai_runner_handoffs.py +++ b/examples/agents/95_openai_runner_handoffs.py @@ -9,7 +9,7 @@ Before (runs directly against OpenAI): from agents import Runner -After (runs on Agentspan — durable, observable, scalable): +After (runs on Conductor — durable, observable, scalable): from conductor.ai import Runner The diff: @@ -17,13 +17,13 @@ +from conductor.ai import Runner Agent definitions, handoffs list, and the Runner.run() call are unchanged. -Agentspan records every handoff decision in the execution history — you can -replay the full agent-to-agent routing in the Agentspan UI. +Conductor records every handoff decision in the execution history — you can +replay the full agent-to-agent routing in the Conductor UI. Requirements: - uv add openai-agents - - AGENTSPAN_SERVER_URL=http://localhost:8080/api - - AGENTSPAN_LLM_MODEL=openai/gpt-4o + - CONDUCTOR_SERVER_URL=http://localhost:8080/api + - CONDUCTOR_AGENT_LLM_MODEL=openai/gpt-4o Usage: python 95_openai_runner_handoffs.py @@ -35,7 +35,7 @@ # ── Only this line changes ────────────────────────────────────────────────── # from agents import Runner # ← original (runs directly on OpenAI) -from conductor.ai import Runner # ← agentspan (runs on Agentspan) +from conductor.ai import Runner # ← Conductor (runs on Conductor) # ─────────────────────────────────────────────────────────────────────────── french_agent = Agent( diff --git a/examples/agents/96_openai_runner_streaming.py b/examples/agents/96_openai_runner_streaming.py index 0c850ec19..2b814e1eb 100644 --- a/examples/agents/96_openai_runner_streaming.py +++ b/examples/agents/96_openai_runner_streaming.py @@ -9,14 +9,14 @@ Before (runs directly against OpenAI): from agents import Runner -After (runs on Agentspan — durable, observable, scalable): +After (runs on Conductor — durable, observable, scalable): from conductor.ai import Runner The diff: -from agents import Runner +from conductor.ai import Runner -Agentspan's streaming model differs from openai-agents in that it streams +Conductor's streaming model differs from openai-agents in that it streams *execution events* (LLM calls, tool calls, results) rather than tokens. The final response arrives in the "done" event's output field. @@ -30,8 +30,8 @@ Requirements: - uv add openai-agents - - AGENTSPAN_SERVER_URL=http://localhost:8080/api - - AGENTSPAN_LLM_MODEL=openai/gpt-4o + - CONDUCTOR_SERVER_URL=http://localhost:8080/api + - CONDUCTOR_AGENT_LLM_MODEL=openai/gpt-4o Usage: python 96_openai_runner_streaming.py @@ -43,7 +43,7 @@ # ── Only this line changes ────────────────────────────────────────────────── # from agents import Runner # ← original (runs directly on OpenAI) -from conductor.ai import Runner # ← agentspan (runs on Agentspan) +from conductor.ai import Runner # ← Conductor (runs on Conductor) # ─────────────────────────────────────────────────────────────────────────── @@ -55,8 +55,8 @@ async def main(): stream = await Runner.run_streamed(agent, input="Please tell me 5 jokes.") - # Iterate Agentspan AgentEvent objects as they arrive from the server. - # Agentspan streams execution events — the final answer is in the "done" event. + # Iterate Conductor AgentEvent objects as they arrive from the server. + # Conductor streams execution events — the final answer is in the "done" event. async for event in stream: if event.type == "thinking" and event.content: # Show which task is running (LLM or tool name) diff --git a/examples/agents/97_openai_runner_sandbox.py b/examples/agents/97_openai_runner_sandbox.py index c95194023..9c5aca057 100644 --- a/examples/agents/97_openai_runner_sandbox.py +++ b/examples/agents/97_openai_runner_sandbox.py @@ -9,7 +9,7 @@ Before (runs directly against OpenAI): from agents import Runner -After (runs on Agentspan — durable, observable, scalable): +After (runs on Conductor — durable, observable, scalable): from conductor.ai import Runner The diff: @@ -17,21 +17,21 @@ +from conductor.ai import Runner Sandbox agents run code in an isolated Docker environment. The model can -inspect a workspace (files, directories) using a shell tool. With AgentspanRunner: - - Every shell command the model executes is recorded in Agentspan - - The full sandbox session is visible in the Agentspan UI - - If the process crashes, the Agentspan execution history is preserved +inspect a workspace (files, directories) using a shell tool. With ConductorRunner: + - Every shell command the model executes is recorded in Conductor + - The full sandbox session is visible in the Conductor UI + - If the process crashes, the Conductor execution history is preserved Architecture: SandboxAgent — openai-agents sandbox agent (file inspection via shell) Docker — isolated container with the workspace files - AgentspanRunner — routes execution through Agentspan instead of OpenAI directly + ConductorRunner — routes execution through Conductor instead of OpenAI directly Requirements: - uv add openai-agents - Docker running locally (docker ps should work) - - AGENTSPAN_SERVER_URL=http://localhost:8080/api - - AGENTSPAN_LLM_MODEL=openai/gpt-4o + - CONDUCTOR_SERVER_URL=http://localhost:8080/api + - CONDUCTOR_AGENT_LLM_MODEL=openai/gpt-4o Usage: python 97_openai_runner_sandbox.py @@ -68,7 +68,7 @@ # ── Only this line changes ────────────────────────────────────────────────── # from agents import Runner # ← original (runs directly on OpenAI) -from conductor.ai import Runner # ← agentspan (runs on Agentspan) +from conductor.ai import Runner # ← Conductor (runs on Conductor) # ─────────────────────────────────────────────────────────────────────────── DEFAULT_QUESTION = "Summarize this project in 2 sentences." @@ -82,7 +82,7 @@ def _build_manifest() -> Manifest: "README.md": File( content=( b"# Demo Project\n\n" - b"A tiny demo project for the Agentspan sandbox runner example.\n" + b"A tiny demo project for the Conductor sandbox runner example.\n" b"The model can inspect files through the shell tool.\n" ) ), @@ -146,13 +146,13 @@ async def main(model: str, question: str) -> None: try: async with sandbox: - # Run the agent — AgentspanRunner.run_streamed() is a drop-in for Runner.run_streamed() + # Run the agent — ConductorRunner.run_streamed() is a drop-in for Runner.run_streamed() stream = await Runner.run_streamed( agent, question, run_config=RunConfig( sandbox=SandboxRunConfig(session=sandbox), - workflow_name="Agentspan Docker sandbox example", + workflow_name="Conductor Docker sandbox example", ), ) @@ -171,13 +171,13 @@ async def main(model: str, question: str) -> None: result = await stream.get_result() print(f"\n\nExecution ID: {result.execution_id}") - print("(View full run in the Agentspan UI)") + print("(View full run in the Conductor UI)") finally: await docker_client.delete(sandbox) if __name__ == "__main__": - parser = argparse.ArgumentParser(description="Agentspan sandbox agent (Docker)") + parser = argparse.ArgumentParser(description="Conductor sandbox agent (Docker)") parser.add_argument("--model", default=DEFAULT_MODEL, help="LLM model to use") parser.add_argument("--question", default=DEFAULT_QUESTION, help="Question to ask") args = parser.parse_args() diff --git a/examples/agents/README.md b/examples/agents/README.md index 5a68f0bc1..41bae16ef 100644 --- a/examples/agents/README.md +++ b/examples/agents/README.md @@ -1,348 +1,28 @@ -# Examples +# Conductor-agent examples -Runnable examples demonstrating every feature of the Agentspan SDK. +Runnable Python examples for durable Conductor agents. Start a server, configure +the provider integration on that server, then use the canonical environment names: ---- - -## Examples vs. Production - -> **Every example uses `runtime.run()` for convenience. In production, you should not.** - -Examples call `runtime.run()` so you can try them in a single command — no setup, no -separate processes. But `run()` blocks the caller until the agent finishes, which is fine -for demos but not how you deploy real agents. - -### Production: Deploy → Serve → Run - -In production, the three concerns are separated: - -``` -┌──────────────────────────────────────────────────────────────┐ -│ 1. DEPLOY (once, during CI/CD) │ -│ Registers the agent definition with the Agentspan server │ -│ │ -│ runtime.deploy(agent) │ -│ # or CLI: agentspan deploy --package my_agents │ -├──────────────────────────────────────────────────────────────┤ -│ 2. SERVE (long-running worker process) │ -│ Listens for tool-call tasks and executes them │ -│ │ -│ runtime.serve(agent) │ -│ # typically run as a daemon, container, or systemd unit │ -├──────────────────────────────────────────────────────────────┤ -│ 3. RUN (on-demand, from anywhere) │ -│ Triggers an agent execution │ -│ │ -│ agentspan run "prompt" │ -│ # or SDK: runtime.run("agent_name", "prompt") │ -│ # or REST API │ -└──────────────────────────────────────────────────────────────┘ -``` - -Every example includes the deploy/serve pattern as commented code at the bottom of its -`__main__` block — look for the `# Production pattern:` comment. - -See [63_deploy.py](63_deploy.py), [63b_serve.py](63b_serve.py), and -[63c_run_by_name.py](63c_run_by_name.py) for a complete working example of this pattern. - ---- - -## Getting Started - -### 1. Install dependencies - -The core examples (numbered files in this directory) only need the `conductor-agent-sdk` package: - -```bash -uv pip install conductor-agent-sdk +```shell +export CONDUCTOR_SERVER_URL=http://localhost:8080/api +export CONDUCTOR_AGENT_LLM_MODEL=openai/gpt-4o-mini ``` -Framework-specific examples require additional packages. Install only what you need: - -#### LangChain examples (`langchain/`) - -```bash -uv pip install langchain langchain-core langchain-openai -``` - -| Package | Required | Notes | -|---------|----------|-------| -| `langchain` | Yes | Core framework, includes `create_agent` | -| `langchain-core` | Yes | Tools, prompts, output parsers, messages | -| `langchain-openai` | Yes | `ChatOpenAI` LLM provider | -| `pydantic` | Some examples | Used for structured output (03, 04, 24, 25) | - -#### LangGraph examples (`langgraph/`) - -```bash -uv pip install langgraph langchain-core langchain-openai -``` - -| Package | Required | Notes | -|---------|----------|-------| -| `langgraph` | Yes | `StateGraph`, `create_react_agent`, prebuilt nodes | -| `langchain-core` | Yes | Messages, tools, documents | -| `langchain-openai` | Yes | `ChatOpenAI` LLM provider | -| `langchain-anthropic` | Optional | Only for `43_react_agent_multi_model.py` (requires `ANTHROPIC_API_KEY`) | -| `pydantic` | Some examples | Used for structured output (08) | - -#### OpenAI Agents SDK examples (`openai/`) - -```bash -uv pip install openai-agents -``` - -| Package | Required | Notes | -|---------|----------|-------| -| `openai-agents` | Yes | `Agent`, `function_tool`, `ModelSettings`, guardrails | -| `pydantic` | Some examples | Used for structured output (03) | - -Requires `OPENAI_API_KEY` environment variable. - -#### Google ADK examples (`adk/`) - -```bash -uv pip install google-adk -``` - -| Package | Required | Notes | -|---------|----------|-------| -| `google-adk` | Yes | `Agent`, `SequentialAgent`, `ParallelAgent`, `LoopAgent`, planners | -| `pydantic` | Some examples | Used for structured output (03) | - -Requires `GOOGLE_GEMINI_API_KEY` environment variable. - -#### Install everything - -To install all framework dependencies at once: - -```bash -uv pip install langchain langchain-core langchain-openai langgraph openai-agents google-adk -``` - -### 2. Configure your environment - -Export environment variables: - -```bash -export AGENTSPAN_LLM_MODEL=openai/gpt-4o-mini -export AGENTSPAN_SERVER_URL=http://localhost:8080/api -# export AGENTSPAN_AUTH_KEY= # if authentication is enabled -# export AGENTSPAN_AUTH_SECRET= -``` - -#### 2.1. Choose a model - -The `AGENTSPAN_LLM_MODEL` variable uses the `provider/model-name` format. Examples: - -| Provider | Model string | API key env var | -|----------|-------------|-----------------| -| OpenAI | `anthropic/claude-sonnet-4-6` (default) | `OPENAI_API_KEY` | -| Anthropic | `anthropic/claude-sonnet-4-20250514` | `ANTHROPIC_API_KEY` | -| Google Gemini | `google_gemini/gemini-2.0-flash` | `GOOGLE_GEMINI_API_KEY` | -| AWS Bedrock | `aws_bedrock/...` | AWS credentials | -| Azure OpenAI | `azure_openai/...` | Azure credentials | - -All supported providers: `openai`, `anthropic`, `google_gemini`, `google_vertex_ai`, -`azure_openai`, `aws_bedrock`, `cohere`, `mistral`, `groq`, `perplexity`, -`hugging_face`, `deepseek`. - -### 3. Run an example - -```bash -# Core SDK examples -python examples/01_basic_agent.py -python examples/15_agent_discussion.py - -# Framework-specific examples -python examples/langchain/01_hello_world.py -python examples/langgraph/01_hello_world.py -python examples/openai/01_basic_agent.py -python examples/adk/01_basic_agent.py -``` - ---- - -## Basic Examples - -| # | Example | What it demonstrates | -|---|---------|---------------------| -| 01 | [Basic Agent](01_basic_agent.py) | Simplest possible agent — single LLM, no tools, 5 lines of code | -| 02 | [Tools](02_tools.py) | Multiple `@tool` functions, approval-required tools | - -## Tool Calling - -| # | Example | What it demonstrates | -|---|---------|---------------------| -| 02a | [Simple Tools](02a_simple_tools.py) | Two tools (weather, stocks) — LLM picks the right one | -| 02b | [Multi-Step Tools](02b_multi_step_tools.py) | Chained tool calls: lookup → fetch → calculate → answer | -| 03 | [Structured Output](03_structured_output.py) | Pydantic `output_type` for typed, validated responses | -| 04 | [HTTP & MCP Tools](04_http_and_mcp_tools.py) | Server-side tools via `http_tool()` and `mcp_tool()` — no workers needed | -| 04b | [MCP Weather](04_mcp_weather.py) | Real-time weather via an MCP server | -| 14 | [Existing Workers](14_existing_workers.py) | Use existing `@worker_task` functions directly as agent tools | -| 33 | [Single Turn Tool](33_single_turn_tool.py) | Single-turn tool invocation with immediate response | -| 33 | [External Workers](33_external_workers.py) | Reference workers in other services via `@tool(external=True)` — no local code needed | - -## Multi-Agent Orchestration - -| # | Example | Pattern | Key API | -|---|---------|---------|---------| -| 05 | [Handoffs](05_handoffs.py) | LLM-driven delegation to sub-agents | `strategy="handoff"` | -| 06 | [Sequential Pipeline](06_sequential_pipeline.py) | Agents run in order, output chains forward | `strategy="sequential"`, `>>` operator | -| 07 | [Parallel Agents](07_parallel_agents.py) | All agents run concurrently, results aggregated | `strategy="parallel"` | -| 08 | [Router Agent](08_router_agent.py) | Router (Agent or callable) selects which sub-agent runs | `strategy="router"` | -| 13 | [Hierarchical Agents](13_hierarchical_agents.py) | 3-level nested hierarchy: CEO → leads → specialists | Nested `strategy="handoff"` | -| 15 | [Agent Discussion](15_agent_discussion.py) | Round-robin debate between agents, piped to a summarizer | `strategy="round_robin"`, `>>` | -| 16 | [Random Strategy](16_random_strategy.py) | Random agent selected each turn (brainstorming) | `strategy="random"` | -| 17 | [Swarm Orchestration](17_swarm_orchestration.py) | Automatic transitions via handoff conditions | `strategy="swarm"`, `OnTextMention` | -| 18 | [Manual Selection](18_manual_selection.py) | Human picks which agent speaks each turn | `strategy="manual"` | -| 20 | [Constrained Transitions](20_constrained_transitions.py) | Restrict which agents can follow which | `allowed_transitions` | -| 29 | [Agent Introductions](29_agent_introductions.py) | Agents introduce themselves before a group discussion | `introduction` parameter | -| 38 | [Tech Trends](38_tech_trends.py) | Multi-agent research pipeline with live HTTP API tools | `>>` operator, `from __future__ import annotations` | - -## Human-in-the-Loop - -| # | Example | What it demonstrates | -|---|---------|---------------------| -| 09 | [Human-in-the-Loop](09_human_in_the_loop.py) | Tool approval gate — approve or reject before execution | `approval_required=True` | -| 09b | [HITL with Feedback](09b_hitl_with_feedback.py) | Custom feedback via `respond()` — editorial review with revision notes | `handle.respond()` | -| 09c | [HITL with Streaming](09c_hitl_streaming.py) | Real-time event stream with approval pauses | `stream()` + `approve()` | - -## Guardrails & Safety - -| # | Example | What it demonstrates | -|---|---------|---------------------| -| 10 | [Guardrails](10_guardrails.py) | Output validation with `@guardrail` decorator, `OnFail`/`Position` enums | `@guardrail`, `OnFail`, `Position` | -| 21 | [Regex Guardrails](21_regex_guardrails.py) | Pattern-based blocking (emails, SSNs) and allow-listing (JSON) | `RegexGuardrail` | -| 22 | [LLM Guardrails](22_llm_guardrails.py) | AI-powered content safety evaluation via a judge LLM | `LLMGuardrail` | -| 31 | [Tool Guardrails](31_tool_guardrails.py) | Pre-execution validation on tool inputs (SQL injection blocking) | `@tool(guardrails=[...])` | -| 32 | [Human Guardrail](32_human_guardrail.py) | Pause agent for human review when output fails validation | `on_fail="human"` | -| 35 | [Standalone Guardrails](35_standalone_guardrails.py) | Use `@guardrail` as plain callables — no agent, no server needed | `@guardrail`, `GuardrailResult` | -| 36 | [Simple Agent Guardrails](36_simple_agent_guardrails.py) | Guardrails on agents without tools — mixed regex (InlineTask) + custom (worker) | `RegexGuardrail`, `@guardrail` | -| 37 | [Fix Guardrail](37_fix_guardrail.py) | Auto-correct output instead of retrying — deterministic fixes | `on_fail="fix"`, `fixed_output` | - -## Termination Conditions - -| # | Example | What it demonstrates | -|---|---------|---------------------| -| 19 | [Composable Termination](19_composable_termination.py) | Text mention, stop message, max messages, token budget, AND/OR composition | `TextMentionTermination`, `&`, `\|` | - -## Code Execution - -| # | Example | What it demonstrates | -|---|---------|---------------------| -| 24 | [Code Execution](24_code_execution.py) | Local, Docker, Jupyter, and serverless code execution sandboxes | `LocalCodeExecutor`, `DockerCodeExecutor` | - -## Memory - -| # | Example | What it demonstrates | -|---|---------|---------------------| -| 25 | [Semantic Memory](25_semantic_memory.py) | Long-term memory with similarity-based retrieval across sessions | `SemanticMemory` | - -## Observability - -| # | Example | What it demonstrates | -|---|---------|---------------------| -| 23 | [Token Tracking](23_token_tracking.py) | Per-run token usage and cost estimation | `result.token_usage` | -| 26 | [OpenTelemetry Tracing](26_opentelemetry_tracing.py) | Industry-standard OTel spans for runs, tools, and handoffs | `tracing` module | - -## Execution Modes - -| # | Example | What it demonstrates | -|---|---------|---------------------| -| 11 | [Streaming](11_streaming.py) | Default `runtime.run()` flow with a commented `runtime.stream()` alternative for real-time events | `runtime.run()`, `AgentEvent`, `EventType` | -| 12 | [Long-Running](12_long_running.py) | Default `runtime.run()` flow with a commented `runtime.start()` alternative for async polling | `runtime.run()`, `runtime.start()`, `handle.get_status()` | -| 72 | [Client Reconnect](72_client_reconnect.py) | Default `runtime.run()` flow plus an advanced reconnect demo that resumes the same execution after client death | `runtime.run()`, `runtime.start()`, `runtime.get_status()`, `runtime.respond()` | -| 73 | [Worker Restart Recovery](73_worker_restart_recovery.py) | Default `runtime.run()` flow plus an advanced deploy/serve/start recovery demo | `runtime.run()`, `runtime.deploy()`, `runtime.serve()`, `runtime.start()` | - -## Multimodal - -| # | Example | What it demonstrates | -|---|---------|---------------------| -| 30 | [Multimodal Agent](30_multimodal_agent.py) | Image/video analysis with vision models via the `media` parameter | `media=["url"]` | - -## Integrations - -| # | Example | What it demonstrates | -|---|---------|---------------------| -| 28 | [GPT Assistant Agent](28_gpt_assistant_agent.py) | Wrap OpenAI Assistants API (with code interpreter) as a Conductor agent | `GPTAssistantAgent` | - ---- - -## Troubleshooting - -### SSL Certificate Errors on macOS - -Examples that make outbound HTTPS calls (e.g., `38_tech_trends.py`) may fail with: -``` -[SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: unable to get local issuer certificate -``` - -This happens because macOS Python framework installs do not link to system certificates. -Fix by running (once per Python installation): - -```bash -# Replace 3.12 with your Python version -/Applications/Python\ 3.12/Install\ Certificates.command -``` - -### PEP 563 Compatibility - -Tool functions defined in modules that use `from __future__ import annotations` work -correctly. The SDK resolves string annotations to real types at registration time. +## Start here -## Feature Index +| Example | Demonstrates | +|---|---| +| `01_basic_agent.py` | A minimal agent and `runtime.run()`. | +| `02a_simple_tools.py` | Python worker tools. | +| `05_handoffs.py` | Agent handoffs. | +| `09_human_in_the_loop.py` | Approval and resume. | +| `57_plan_dry_run.py` | Compile without running. | +| `63b_serve.py` | Deploy and serve workers. | -Quick lookup — find the right example for any SDK feature: +Run an example from this directory: `python 01_basic_agent.py`. For production, +deploy with `AgentRuntime.deploy()` and run workers with `AgentRuntime.serve()`; +the Conductor CLI manages the server with `conductor server start`. -| Feature | Example(s) | -|---------|-----------| -| `Agent` | 01 | -| `@tool` decorator | 02, 02a, 02b | -| `http_tool()` | 04 | -| `mcp_tool()` | 04, 04b | -| `output_type` (Pydantic) | 03 | -| `strategy="handoff"` | 05, 13 | -| `strategy="sequential"`, `>>` | 06, 15 | -| `strategy="parallel"` | 07 | -| `strategy="router"` | 08 | -| `strategy="round_robin"` | 15, 20, 29 | -| `strategy="random"` | 16 | -| `strategy="swarm"` | 17 | -| `strategy="manual"` | 18 | -| `allowed_transitions` | 20 | -| `introduction` | 29 | -| `approval_required=True` | 02, 09 | -| `handle.approve()` / `reject()` | 09 | -| `handle.respond()` / `send()` | 09b, 27 | -| `runtime.run()` | 01, 02, 11, 12, 72, 73 | -| `runtime.stream()` | 09c, 11 | -| `runtime.start()` | 12, 18, 27, 72, 73 | -| `@guardrail` decorator | 10, 35 | -| `Guardrail` | 10, 32 | -| `OnFail` / `Position` enums | 10 | -| `RegexGuardrail` | 21 | -| `LLMGuardrail` | 22 | -| `on_fail="fix"` | 37 | -| `on_fail="human"` | 32 | -| `fixed_output` | 37 | -| `@tool(guardrails=[...])` | 31 | -| `TextMentionTermination` | 19 | -| `StopMessageTermination` | 19 | -| `MaxMessageTermination` | 19 | -| `TokenUsageTermination` | 19 | -| `&` / `\|` (composable) | 19 | -| `LocalCodeExecutor` | 24 | -| `DockerCodeExecutor` | 24 | -| `JupyterCodeExecutor` | 24 | -| `ServerlessCodeExecutor` | 24 | -| `SemanticMemory` | 25 | -| `TokenUsage` | 23 | -| OpenTelemetry tracing | 26 | -| `GPTAssistantAgent` | 28 | -| `@worker_task` as tools | 14 | -| `@tool(external=True)` | 33 | -| `OnTextMention` / `OnToolResult` | 17 | -| `media` (multimodal input) | 30 | -| `PromptTemplate` | kitchen_sink | -| `from __future__ import annotations` | 38 | +Framework-specific examples are in [ADK](adk/README.md), +[LangGraph](langgraph/README.md), and [OpenAI Agents SDK](openai/README.md). +Review tool side effects before using real credentials. diff --git a/examples/agents/_issue_fixer_instructions.py b/examples/agents/_issue_fixer_instructions.py index cbd154af2..387a93ba4 100644 --- a/examples/agents/_issue_fixer_instructions.py +++ b/examples/agents/_issue_fixer_instructions.py @@ -147,7 +147,7 @@ "issue_title": "", "change_type": "bug_fix" or "feature", "date": "<YYYY-MM-DD>", - "author": "agentspan-bot", + "author": "Conductor-bot", "root_cause": "<what was broken and why>", "what_changed": [ {{"file": "<path>", "change": "<what was modified and why>"}} diff --git a/examples/agents/_issue_fixer_tools.py b/examples/agents/_issue_fixer_tools.py index 61b3ce6fa..3f32c0707 100644 --- a/examples/agents/_issue_fixer_tools.py +++ b/examples/agents/_issue_fixer_tools.py @@ -766,7 +766,7 @@ def get_text(self): return "".join(self._texts) try: - req = urllib.request.Request(url, headers={"User-Agent": "AgentSpan-IssueFixer/1.0"}) + req = urllib.request.Request(url, headers={"User-Agent": "Conductor-IssueFixer/1.0"}) with urllib.request.urlopen(req, timeout=30) as resp: content_type = resp.headers.get("Content-Type", "") raw = resp.read(500_000).decode("utf-8", errors="replace") diff --git a/examples/agents/adk/00_hello_world.py b/examples/agents/adk/00_hello_world.py index cec1c1041..b1cb2a20e 100644 --- a/examples/agents/adk/00_hello_world.py +++ b/examples/agents/adk/00_hello_world.py @@ -10,8 +10,8 @@ Requirements: - pip install google-adk - GOOGLE_API_KEY or GEMINI_API_KEY environment variable - - AGENTSPAN_LLM_MODEL=google_gemini/gemini-2.0-flash (for AgentSpan runs) - - AGENTSPAN_SERVER_URL=http://localhost:8080/api (for AgentSpan runs) + - CONDUCTOR_AGENT_LLM_MODEL=google_gemini/gemini-2.0-flash (for Conductor runs) + - CONDUCTOR_SERVER_URL=http://localhost:8080/api (for Conductor runs) """ from google.adk.agents import Agent @@ -36,7 +36,7 @@ # 1. Deploy once during CI/CD: # runtime.deploy(agent) # CLI alternative: - # agentspan deploy --package examples.adk.00_hello_world + # runtime.deploy(agent) from a release script # # 2. In a separate long-lived worker process: # runtime.serve(agent) diff --git a/examples/agents/adk/01_basic_agent.py b/examples/agents/adk/01_basic_agent.py index c6f34c697..5fc3bfa89 100644 --- a/examples/agents/adk/01_basic_agent.py +++ b/examples/agents/adk/01_basic_agent.py @@ -12,8 +12,8 @@ Requirements: - pip install google-adk - Conductor server with Google Gemini LLM integration configured - - AGENTSPAN_SERVER_URL=http://localhost:8080/api as environment variable - - AGENTSPAN_LLM_MODEL=google_gemini/gemini-2.0-flash as environment variable + - CONDUCTOR_SERVER_URL=http://localhost:8080/api as environment variable + - CONDUCTOR_AGENT_LLM_MODEL=google_gemini/gemini-2.0-flash as environment variable """ from google.adk.agents import Agent @@ -39,7 +39,7 @@ # 1. Deploy once during CI/CD: # runtime.deploy(agent) # CLI alternative: - # agentspan deploy --package examples.adk.01_basic_agent + # runtime.deploy(agent) from a release script # # 2. In a separate long-lived worker process: # runtime.serve(agent) diff --git a/examples/agents/adk/02_function_tools.py b/examples/agents/adk/02_function_tools.py index 25ced00a2..e442eb214 100644 --- a/examples/agents/adk/02_function_tools.py +++ b/examples/agents/adk/02_function_tools.py @@ -12,8 +12,8 @@ Requirements: - pip install google-adk - Conductor server with Google Gemini LLM integration configured - - AGENTSPAN_SERVER_URL=http://localhost:8080/api as environment variable - - AGENTSPAN_LLM_MODEL=google_gemini/gemini-2.0-flash as environment variable + - CONDUCTOR_SERVER_URL=http://localhost:8080/api as environment variable + - CONDUCTOR_AGENT_LLM_MODEL=google_gemini/gemini-2.0-flash as environment variable """ from google.adk.agents import Agent @@ -103,7 +103,7 @@ def get_time_zone(city: str) -> dict: # 1. Deploy once during CI/CD: # runtime.deploy(agent) # CLI alternative: - # agentspan deploy --package examples.adk.02_function_tools + # runtime.deploy(agent) from a release script # # 2. In a separate long-lived worker process: # runtime.serve(agent) diff --git a/examples/agents/adk/03_structured_output.py b/examples/agents/adk/03_structured_output.py index 2e7b51b78..1d4e12471 100644 --- a/examples/agents/adk/03_structured_output.py +++ b/examples/agents/adk/03_structured_output.py @@ -11,8 +11,8 @@ Requirements: - pip install google-adk pydantic - Conductor server with Google Gemini LLM integration configured - - AGENTSPAN_SERVER_URL=http://localhost:8080/api as environment variable - - AGENTSPAN_LLM_MODEL=google_gemini/gemini-2.0-flash as environment variable + - CONDUCTOR_SERVER_URL=http://localhost:8080/api as environment variable + - CONDUCTOR_AGENT_LLM_MODEL=google_gemini/gemini-2.0-flash as environment variable """ from typing import List @@ -74,7 +74,7 @@ class Recipe(BaseModel): # 1. Deploy once during CI/CD: # runtime.deploy(agent) # CLI alternative: - # agentspan deploy --package examples.adk.03_structured_output + # runtime.deploy(agent) from a release script # # 2. In a separate long-lived worker process: # runtime.serve(agent) diff --git a/examples/agents/adk/04_sub_agents.py b/examples/agents/adk/04_sub_agents.py index 3d20635d4..d120e9b77 100644 --- a/examples/agents/adk/04_sub_agents.py +++ b/examples/agents/adk/04_sub_agents.py @@ -11,8 +11,8 @@ Requirements: - pip install google-adk - Conductor server with Google Gemini LLM integration configured - - AGENTSPAN_SERVER_URL=http://localhost:8080/api as environment variable - - AGENTSPAN_LLM_MODEL=google_gemini/gemini-2.0-flash as environment variable + - CONDUCTOR_SERVER_URL=http://localhost:8080/api as environment variable + - CONDUCTOR_AGENT_LLM_MODEL=google_gemini/gemini-2.0-flash as environment variable """ from google.adk.agents import Agent @@ -148,7 +148,7 @@ def get_travel_advisory(country: str) -> dict: # 1. Deploy once during CI/CD: # runtime.deploy(coordinator) # CLI alternative: - # agentspan deploy --package examples.adk.04_sub_agents + # runtime.deploy(agent) from a release script # # 2. In a separate long-lived worker process: # runtime.serve(coordinator) diff --git a/examples/agents/adk/05_generation_config.py b/examples/agents/adk/05_generation_config.py index 838c3a632..c549e9658 100644 --- a/examples/agents/adk/05_generation_config.py +++ b/examples/agents/adk/05_generation_config.py @@ -12,8 +12,8 @@ Requirements: - pip install google-adk - Conductor server with Google Gemini LLM integration configured - - AGENTSPAN_SERVER_URL=http://localhost:8080/api as environment variable - - AGENTSPAN_LLM_MODEL=google_gemini/gemini-2.0-flash as environment variable + - CONDUCTOR_SERVER_URL=http://localhost:8080/api as environment variable + - CONDUCTOR_AGENT_LLM_MODEL=google_gemini/gemini-2.0-flash as environment variable """ from google.adk.agents import Agent @@ -62,7 +62,7 @@ # 1. Deploy once during CI/CD: # runtime.deploy(factual_agent) # CLI alternative: - # agentspan deploy --package examples.adk.05_generation_config + # runtime.deploy(agent) from a release script # # 2. In a separate long-lived worker process: # runtime.serve(factual_agent) diff --git a/examples/agents/adk/06_streaming.py b/examples/agents/adk/06_streaming.py index b5e65af97..698bb9bfb 100644 --- a/examples/agents/adk/06_streaming.py +++ b/examples/agents/adk/06_streaming.py @@ -11,8 +11,8 @@ Requirements: - pip install google-adk - Conductor server with Google Gemini LLM integration configured - - AGENTSPAN_SERVER_URL=http://localhost:8080/api as environment variable - - AGENTSPAN_LLM_MODEL=google_gemini/gemini-2.0-flash as environment variable + - CONDUCTOR_SERVER_URL=http://localhost:8080/api as environment variable + - CONDUCTOR_AGENT_LLM_MODEL=google_gemini/gemini-2.0-flash as environment variable """ from google.adk.agents import Agent @@ -71,7 +71,7 @@ def search_documentation(query: str) -> dict: # 1. Deploy once during CI/CD: # runtime.deploy(agent) # CLI alternative: - # agentspan deploy --package examples.adk.06_streaming + # runtime.deploy(agent) from a release script # # 2. In a separate long-lived worker process: # runtime.serve(agent) diff --git a/examples/agents/adk/07_output_key_state.py b/examples/agents/adk/07_output_key_state.py index f50a21ef7..dce6f8d05 100644 --- a/examples/agents/adk/07_output_key_state.py +++ b/examples/agents/adk/07_output_key_state.py @@ -11,8 +11,8 @@ Requirements: - pip install google-adk - Conductor server with Google Gemini LLM integration configured - - AGENTSPAN_SERVER_URL=http://localhost:8080/api as environment variable - - AGENTSPAN_LLM_MODEL=google_gemini/gemini-2.0-flash as environment variable + - CONDUCTOR_SERVER_URL=http://localhost:8080/api as environment variable + - CONDUCTOR_AGENT_LLM_MODEL=google_gemini/gemini-2.0-flash as environment variable """ from google.adk.agents import Agent @@ -115,7 +115,7 @@ def generate_chart_description(metric: str, value: str) -> dict: # 1. Deploy once during CI/CD: # runtime.deploy(coordinator) # CLI alternative: - # agentspan deploy --package examples.adk.07_output_key_state + # runtime.deploy(agent) from a release script # # 2. In a separate long-lived worker process: # runtime.serve(coordinator) diff --git a/examples/agents/adk/08_instruction_templating.py b/examples/agents/adk/08_instruction_templating.py index ea4b0e61e..5368c8b30 100644 --- a/examples/agents/adk/08_instruction_templating.py +++ b/examples/agents/adk/08_instruction_templating.py @@ -11,8 +11,8 @@ Requirements: - pip install google-adk - Conductor server with Google Gemini LLM integration configured - - AGENTSPAN_SERVER_URL=http://localhost:8080/api as environment variable - - AGENTSPAN_LLM_MODEL=google_gemini/gemini-2.0-flash as environment variable + - CONDUCTOR_SERVER_URL=http://localhost:8080/api as environment variable + - CONDUCTOR_AGENT_LLM_MODEL=google_gemini/gemini-2.0-flash as environment variable """ from google.adk.agents import Agent @@ -101,7 +101,7 @@ def search_tutorials(topic: str, level: str = "intermediate") -> dict: # 1. Deploy once during CI/CD: # runtime.deploy(agent) # CLI alternative: - # agentspan deploy --package examples.adk.08_instruction_templating + # runtime.deploy(agent) from a release script # # 2. In a separate long-lived worker process: # runtime.serve(agent) diff --git a/examples/agents/adk/09_multi_tool_agent.py b/examples/agents/adk/09_multi_tool_agent.py index 9bcafc443..a8175e3c6 100644 --- a/examples/agents/adk/09_multi_tool_agent.py +++ b/examples/agents/adk/09_multi_tool_agent.py @@ -12,8 +12,8 @@ Requirements: - pip install google-adk - Conductor server with Google Gemini LLM integration configured - - AGENTSPAN_SERVER_URL=http://localhost:8080/api as environment variable - - AGENTSPAN_LLM_MODEL=google_gemini/gemini-2.0-flash as environment variable + - CONDUCTOR_SERVER_URL=http://localhost:8080/api as environment variable + - CONDUCTOR_AGENT_LLM_MODEL=google_gemini/gemini-2.0-flash as environment variable """ from typing import List @@ -157,7 +157,7 @@ def apply_coupon(subtotal: float, coupon_code: str) -> dict: # 1. Deploy once during CI/CD: # runtime.deploy(agent) # CLI alternative: - # agentspan deploy --package examples.adk.09_multi_tool_agent + # runtime.deploy(agent) from a release script # # 2. In a separate long-lived worker process: # runtime.serve(agent) diff --git a/examples/agents/adk/10_hierarchical_agents.py b/examples/agents/adk/10_hierarchical_agents.py index ace6c0f2b..feb3dd4bb 100644 --- a/examples/agents/adk/10_hierarchical_agents.py +++ b/examples/agents/adk/10_hierarchical_agents.py @@ -12,8 +12,8 @@ Requirements: - pip install google-adk - Conductor server with Google Gemini LLM integration configured - - AGENTSPAN_SERVER_URL=http://localhost:8080/api as environment variable - - AGENTSPAN_LLM_MODEL=google_gemini/gemini-2.0-flash as environment variable + - CONDUCTOR_SERVER_URL=http://localhost:8080/api as environment variable + - CONDUCTOR_AGENT_LLM_MODEL=google_gemini/gemini-2.0-flash as environment variable """ from google.adk.agents import Agent @@ -180,7 +180,7 @@ def check_performance_metrics(service: str) -> dict: # 1. Deploy once during CI/CD: # runtime.deploy(coordinator) # CLI alternative: - # agentspan deploy --package examples.adk.10_hierarchical_agents + # runtime.deploy(agent) from a release script # # 2. In a separate long-lived worker process: # runtime.serve(coordinator) diff --git a/examples/agents/adk/11_sequential_agent.py b/examples/agents/adk/11_sequential_agent.py index 8e738312c..0270b4989 100644 --- a/examples/agents/adk/11_sequential_agent.py +++ b/examples/agents/adk/11_sequential_agent.py @@ -63,7 +63,7 @@ def main(): # 1. Deploy once during CI/CD: # runtime.deploy(pipeline) # CLI alternative: - # agentspan deploy --package examples.adk.11_sequential_agent + # runtime.deploy(agent) from a release script # # 2. In a separate long-lived worker process: # runtime.serve(pipeline) diff --git a/examples/agents/adk/12_parallel_agent.py b/examples/agents/adk/12_parallel_agent.py index 75d14c227..2fb78d055 100644 --- a/examples/agents/adk/12_parallel_agent.py +++ b/examples/agents/adk/12_parallel_agent.py @@ -63,7 +63,7 @@ def main(): # 1. Deploy once during CI/CD: # runtime.deploy(parallel_analysis) # CLI alternative: - # agentspan deploy --package examples.adk.12_parallel_agent + # runtime.deploy(agent) from a release script # # 2. In a separate long-lived worker process: # runtime.serve(parallel_analysis) diff --git a/examples/agents/adk/13_loop_agent.py b/examples/agents/adk/13_loop_agent.py index bc4fa2307..6bf823dd6 100644 --- a/examples/agents/adk/13_loop_agent.py +++ b/examples/agents/adk/13_loop_agent.py @@ -62,7 +62,7 @@ def main(): # 1. Deploy once during CI/CD: # runtime.deploy(refinement_loop) # CLI alternative: - # agentspan deploy --package examples.adk.13_loop_agent + # runtime.deploy(agent) from a release script # # 2. In a separate long-lived worker process: # runtime.serve(refinement_loop) diff --git a/examples/agents/adk/14_callbacks.py b/examples/agents/adk/14_callbacks.py index 24b71e86e..d8a9c1e15 100644 --- a/examples/agents/adk/14_callbacks.py +++ b/examples/agents/adk/14_callbacks.py @@ -79,7 +79,7 @@ def check_order_status(order_id: str) -> dict: # 1. Deploy once during CI/CD: # runtime.deploy(agent) # CLI alternative: - # agentspan deploy --package examples.adk.14_callbacks + # runtime.deploy(agent) from a release script # # 2. In a separate long-lived worker process: # runtime.serve(agent) diff --git a/examples/agents/adk/15_global_instruction.py b/examples/agents/adk/15_global_instruction.py index 59e4a204e..14f31f867 100644 --- a/examples/agents/adk/15_global_instruction.py +++ b/examples/agents/adk/15_global_instruction.py @@ -81,7 +81,7 @@ def get_store_hours(location: str) -> dict: # 1. Deploy once during CI/CD: # runtime.deploy(agent) # CLI alternative: - # agentspan deploy --package examples.adk.15_global_instruction + # runtime.deploy(agent) from a release script # # 2. In a separate long-lived worker process: # runtime.serve(agent) diff --git a/examples/agents/adk/16_customer_service.py b/examples/agents/adk/16_customer_service.py index 261783021..f13c49d84 100644 --- a/examples/agents/adk/16_customer_service.py +++ b/examples/agents/adk/16_customer_service.py @@ -103,7 +103,7 @@ def update_account_plan(account_id: str, new_plan: str) -> dict: # 1. Deploy once during CI/CD: # runtime.deploy(agent) # CLI alternative: - # agentspan deploy --package examples.adk.16_customer_service + # runtime.deploy(agent) from a release script # # 2. In a separate long-lived worker process: # runtime.serve(agent) diff --git a/examples/agents/adk/17_financial_advisor.py b/examples/agents/adk/17_financial_advisor.py index 4ad792d96..ee7bdc0ba 100644 --- a/examples/agents/adk/17_financial_advisor.py +++ b/examples/agents/adk/17_financial_advisor.py @@ -141,7 +141,7 @@ def estimate_tax_impact(gains: float, holding_period_months: int) -> dict: # 1. Deploy once during CI/CD: # runtime.deploy(coordinator) # CLI alternative: - # agentspan deploy --package examples.adk.17_financial_advisor + # runtime.deploy(agent) from a release script # # 2. In a separate long-lived worker process: # runtime.serve(coordinator) diff --git a/examples/agents/adk/18_order_processing.py b/examples/agents/adk/18_order_processing.py index 0e03f1242..08b537c2f 100644 --- a/examples/agents/adk/18_order_processing.py +++ b/examples/agents/adk/18_order_processing.py @@ -102,7 +102,7 @@ def place_order(item_skus: str, shipping_method: str = "standard", payment_metho # 1. Deploy once during CI/CD: # runtime.deploy(agent) # CLI alternative: - # agentspan deploy --package examples.adk.18_order_processing + # runtime.deploy(agent) from a release script # # 2. In a separate long-lived worker process: # runtime.serve(agent) diff --git a/examples/agents/adk/19_supply_chain.py b/examples/agents/adk/19_supply_chain.py index 151398a65..f496c91c5 100644 --- a/examples/agents/adk/19_supply_chain.py +++ b/examples/agents/adk/19_supply_chain.py @@ -136,7 +136,7 @@ def get_demand_forecast(sku: str, weeks_ahead: int = 4) -> dict: # 1. Deploy once during CI/CD: # runtime.deploy(coordinator) # CLI alternative: - # agentspan deploy --package examples.adk.19_supply_chain + # runtime.deploy(agent) from a release script # # 2. In a separate long-lived worker process: # runtime.serve(coordinator) diff --git a/examples/agents/adk/20_blog_writer.py b/examples/agents/adk/20_blog_writer.py index 477f876c7..cf63d09fa 100644 --- a/examples/agents/adk/20_blog_writer.py +++ b/examples/agents/adk/20_blog_writer.py @@ -117,7 +117,7 @@ def check_seo_keywords(topic: str) -> dict: # 1. Deploy once during CI/CD: # runtime.deploy(coordinator) # CLI alternative: - # agentspan deploy --package examples.adk.20_blog_writer + # runtime.deploy(agent) from a release script # # 2. In a separate long-lived worker process: # runtime.serve(coordinator) diff --git a/examples/agents/adk/21_agent_tool.py b/examples/agents/adk/21_agent_tool.py index 6207c9247..24dabdd30 100644 --- a/examples/agents/adk/21_agent_tool.py +++ b/examples/agents/adk/21_agent_tool.py @@ -18,8 +18,8 @@ Requirements: - pip install google-adk - Conductor server with AgentTool support - - AGENTSPAN_SERVER_URL=http://localhost:8080/api as environment variable - - AGENTSPAN_LLM_MODEL=google_gemini/gemini-2.0-flash as environment variable + - CONDUCTOR_SERVER_URL=http://localhost:8080/api as environment variable + - CONDUCTOR_AGENT_LLM_MODEL=google_gemini/gemini-2.0-flash as environment variable """ from google.adk.agents import Agent @@ -130,7 +130,7 @@ def compute(expression: str) -> dict: # 1. Deploy once during CI/CD: # runtime.deploy(manager) # CLI alternative: - # agentspan deploy --package examples.adk.21_agent_tool + # runtime.deploy(agent) from a release script # # 2. In a separate long-lived worker process: # runtime.serve(manager) diff --git a/examples/agents/adk/22_transfer_control.py b/examples/agents/adk/22_transfer_control.py index f0461c9df..9fe9ebc91 100644 --- a/examples/agents/adk/22_transfer_control.py +++ b/examples/agents/adk/22_transfer_control.py @@ -18,8 +18,8 @@ Requirements: - pip install google-adk - Conductor server with transfer control support - - AGENTSPAN_SERVER_URL=http://localhost:8080/api as environment variable - - AGENTSPAN_LLM_MODEL=google_gemini/gemini-2.0-flash as environment variable + - CONDUCTOR_SERVER_URL=http://localhost:8080/api as environment variable + - CONDUCTOR_AGENT_LLM_MODEL=google_gemini/gemini-2.0-flash as environment variable """ from google.adk.agents import LlmAgent @@ -85,7 +85,7 @@ # 1. Deploy once during CI/CD: # runtime.deploy(coordinator) # CLI alternative: - # agentspan deploy --package examples.adk.22_transfer_control + # runtime.deploy(agent) from a release script # # 2. In a separate long-lived worker process: # runtime.serve(coordinator) diff --git a/examples/agents/adk/23_callbacks.py b/examples/agents/adk/23_callbacks.py index 54be7784d..eea2b4a06 100644 --- a/examples/agents/adk/23_callbacks.py +++ b/examples/agents/adk/23_callbacks.py @@ -16,8 +16,8 @@ Requirements: - pip install google-adk - Conductor server with callback support - - AGENTSPAN_SERVER_URL=http://localhost:8080/api as environment variable - - AGENTSPAN_LLM_MODEL=google_gemini/gemini-2.0-flash as environment variable + - CONDUCTOR_SERVER_URL=http://localhost:8080/api as environment variable + - CONDUCTOR_AGENT_LLM_MODEL=google_gemini/gemini-2.0-flash as environment variable """ import json @@ -97,7 +97,7 @@ def inspect_after_model(callback_position: str, agent_name: str, # 1. Deploy once during CI/CD: # runtime.deploy(agent) # CLI alternative: - # agentspan deploy --package examples.adk.23_callbacks + # runtime.deploy(agent) from a release script # # 2. In a separate long-lived worker process: # runtime.serve(agent) diff --git a/examples/agents/adk/24_planner.py b/examples/agents/adk/24_planner.py index 9776fe633..96006c958 100644 --- a/examples/agents/adk/24_planner.py +++ b/examples/agents/adk/24_planner.py @@ -11,8 +11,8 @@ Requirements: - pip install google-adk - Conductor server - - AGENTSPAN_SERVER_URL=http://localhost:8080/api as environment variable - - AGENTSPAN_LLM_MODEL=google_gemini/gemini-2.0-flash as environment variable + - CONDUCTOR_SERVER_URL=http://localhost:8080/api as environment variable + - CONDUCTOR_AGENT_LLM_MODEL=google_gemini/gemini-2.0-flash as environment variable """ from google.adk.agents import LlmAgent @@ -97,7 +97,7 @@ def write_section(title: str, content: str) -> dict: # 1. Deploy once during CI/CD: # runtime.deploy(agent) # CLI alternative: - # agentspan deploy --package examples.adk.24_planner + # runtime.deploy(agent) from a release script # # 2. In a separate long-lived worker process: # runtime.serve(agent) diff --git a/examples/agents/adk/25_camel_security.py b/examples/agents/adk/25_camel_security.py index 166c01372..870460694 100644 --- a/examples/agents/adk/25_camel_security.py +++ b/examples/agents/adk/25_camel_security.py @@ -14,8 +14,8 @@ Requirements: - pip install google-adk - Conductor server - - AGENTSPAN_SERVER_URL=http://localhost:8080/api as environment variable - - AGENTSPAN_LLM_MODEL=google_gemini/gemini-2.0-flash as environment variable + - CONDUCTOR_SERVER_URL=http://localhost:8080/api as environment variable + - CONDUCTOR_AGENT_LLM_MODEL=google_gemini/gemini-2.0-flash as environment variable """ from google.adk.agents import Agent, SequentialAgent @@ -135,7 +135,7 @@ def redact_sensitive_fields(data: str) -> dict: # 1. Deploy once during CI/CD: # runtime.deploy(pipeline) # CLI alternative: - # agentspan deploy --package examples.adk.25_camel_security + # runtime.deploy(agent) from a release script # # 2. In a separate long-lived worker process: # runtime.serve(pipeline) diff --git a/examples/agents/adk/26_safety_guardrails.py b/examples/agents/adk/26_safety_guardrails.py index 64b0eff23..393b722a2 100644 --- a/examples/agents/adk/26_safety_guardrails.py +++ b/examples/agents/adk/26_safety_guardrails.py @@ -14,8 +14,8 @@ Requirements: - pip install google-adk - Conductor server - - AGENTSPAN_SERVER_URL=http://localhost:8080/api as environment variable - - AGENTSPAN_LLM_MODEL=google_gemini/gemini-2.0-flash as environment variable + - CONDUCTOR_SERVER_URL=http://localhost:8080/api as environment variable + - CONDUCTOR_AGENT_LLM_MODEL=google_gemini/gemini-2.0-flash as environment variable """ import re @@ -124,7 +124,7 @@ def sanitize_response(text: str, pii_types: str = "") -> dict: # 1. Deploy once during CI/CD: # runtime.deploy(safe_pipeline) # CLI alternative: - # agentspan deploy --package examples.adk.26_safety_guardrails + # runtime.deploy(agent) from a release script # # 2. In a separate long-lived worker process: # runtime.serve(safe_pipeline) diff --git a/examples/agents/adk/27_security_agent.py b/examples/agents/adk/27_security_agent.py index a8467da04..a72c56b09 100644 --- a/examples/agents/adk/27_security_agent.py +++ b/examples/agents/adk/27_security_agent.py @@ -16,8 +16,8 @@ Requirements: - pip install google-adk - Conductor server - - AGENTSPAN_SERVER_URL=http://localhost:8080/api as environment variable - - AGENTSPAN_LLM_MODEL=google_gemini/gemini-2.0-flash as environment variable + - CONDUCTOR_SERVER_URL=http://localhost:8080/api as environment variable + - CONDUCTOR_AGENT_LLM_MODEL=google_gemini/gemini-2.0-flash as environment variable """ from google.adk.agents import Agent, SequentialAgent @@ -140,7 +140,7 @@ def score_safety(response_text: str, attack_category: str) -> dict: # 1. Deploy once during CI/CD: # runtime.deploy(security_test) # CLI alternative: - # agentspan deploy --package examples.adk.27_security_agent + # runtime.deploy(agent) from a release script # # 2. In a separate long-lived worker process: # runtime.serve(security_test) diff --git a/examples/agents/adk/28_movie_pipeline.py b/examples/agents/adk/28_movie_pipeline.py index d3e51b68a..6053ea825 100644 --- a/examples/agents/adk/28_movie_pipeline.py +++ b/examples/agents/adk/28_movie_pipeline.py @@ -14,8 +14,8 @@ Requirements: - pip install google-adk - Conductor server - - AGENTSPAN_SERVER_URL=http://localhost:8080/api as environment variable - - AGENTSPAN_LLM_MODEL=google_gemini/gemini-2.0-flash as environment variable + - CONDUCTOR_SERVER_URL=http://localhost:8080/api as environment variable + - CONDUCTOR_AGENT_LLM_MODEL=google_gemini/gemini-2.0-flash as environment variable """ from google.adk.agents import Agent, SequentialAgent @@ -213,7 +213,7 @@ def assemble_production(title: str, total_scenes: int, # 1. Deploy once during CI/CD: # runtime.deploy(movie_pipeline) # CLI alternative: - # agentspan deploy --package examples.adk.28_movie_pipeline + # runtime.deploy(agent) from a release script # # 2. In a separate long-lived worker process: # runtime.serve(movie_pipeline) diff --git a/examples/agents/adk/29_include_contents.py b/examples/agents/adk/29_include_contents.py index e9341560e..d692feedf 100644 --- a/examples/agents/adk/29_include_contents.py +++ b/examples/agents/adk/29_include_contents.py @@ -9,8 +9,8 @@ Requirements: - pip install google-adk - Conductor server with include_contents support - - AGENTSPAN_SERVER_URL=http://localhost:8080/api as environment variable - - AGENTSPAN_LLM_MODEL=google_gemini/gemini-2.0-flash as environment variable + - CONDUCTOR_SERVER_URL=http://localhost:8080/api as environment variable + - CONDUCTOR_AGENT_LLM_MODEL=google_gemini/gemini-2.0-flash as environment variable """ from google.adk.agents import Agent @@ -63,7 +63,7 @@ # 1. Deploy once during CI/CD: # runtime.deploy(coordinator) # CLI alternative: - # agentspan deploy --package examples.adk.29_include_contents + # runtime.deploy(agent) from a release script # # 2. In a separate long-lived worker process: # runtime.serve(coordinator) diff --git a/examples/agents/adk/30_thinking_config.py b/examples/agents/adk/30_thinking_config.py index c6ed84a1c..90fb665c2 100644 --- a/examples/agents/adk/30_thinking_config.py +++ b/examples/agents/adk/30_thinking_config.py @@ -9,8 +9,8 @@ Requirements: - pip install google-adk - Conductor server with thinking config support - - AGENTSPAN_SERVER_URL=http://localhost:8080/api as environment variable - - AGENTSPAN_LLM_MODEL=google_gemini/gemini-2.0-flash as environment variable + - CONDUCTOR_SERVER_URL=http://localhost:8080/api as environment variable + - CONDUCTOR_AGENT_LLM_MODEL=google_gemini/gemini-2.0-flash as environment variable """ from google.adk.agents import Agent @@ -65,7 +65,7 @@ def calculate(expression: str) -> dict: # 1. Deploy once during CI/CD: # runtime.deploy(agent) # CLI alternative: - # agentspan deploy --package examples.adk.30_thinking_config + # runtime.deploy(agent) from a release script # # 2. In a separate long-lived worker process: # runtime.serve(agent) diff --git a/examples/agents/adk/31_shared_state.py b/examples/agents/adk/31_shared_state.py index 647cceeff..b0e29f030 100644 --- a/examples/agents/adk/31_shared_state.py +++ b/examples/agents/adk/31_shared_state.py @@ -9,8 +9,8 @@ Requirements: - pip install google-adk - Conductor server with state support - - AGENTSPAN_SERVER_URL=http://localhost:8080/api as environment variable - - AGENTSPAN_LLM_MODEL=google_gemini/gemini-2.0-flash as environment variable + - CONDUCTOR_SERVER_URL=http://localhost:8080/api as environment variable + - CONDUCTOR_AGENT_LLM_MODEL=google_gemini/gemini-2.0-flash as environment variable """ from google.adk.agents import Agent @@ -90,7 +90,7 @@ def clear_list(tool_context: ToolContext) -> dict: # 1. Deploy once during CI/CD: # runtime.deploy(agent) # CLI alternative: - # agentspan deploy --package examples.adk.31_shared_state + # runtime.deploy(agent) from a release script # # 2. In a separate long-lived worker process: # runtime.serve(agent) diff --git a/examples/agents/adk/32_nested_strategies.py b/examples/agents/adk/32_nested_strategies.py index 481335352..2a112fc59 100644 --- a/examples/agents/adk/32_nested_strategies.py +++ b/examples/agents/adk/32_nested_strategies.py @@ -9,8 +9,8 @@ Requirements: - pip install google-adk - Conductor server - - AGENTSPAN_SERVER_URL=http://localhost:8080/api as environment variable - - AGENTSPAN_LLM_MODEL=google_gemini/gemini-2.0-flash as environment variable + - CONDUCTOR_SERVER_URL=http://localhost:8080/api as environment variable + - CONDUCTOR_AGENT_LLM_MODEL=google_gemini/gemini-2.0-flash as environment variable """ from google.adk.agents import Agent, ParallelAgent, SequentialAgent @@ -76,7 +76,7 @@ # 1. Deploy once during CI/CD: # runtime.deploy(pipeline) # CLI alternative: - # agentspan deploy --package examples.adk.32_nested_strategies + # runtime.deploy(agent) from a release script # # 2. In a separate long-lived worker process: # runtime.serve(pipeline) diff --git a/examples/agents/adk/33_software_bug_assistant.py b/examples/agents/adk/33_software_bug_assistant.py index 0b8bd07f1..4232d5c87 100644 --- a/examples/agents/adk/33_software_bug_assistant.py +++ b/examples/agents/adk/33_software_bug_assistant.py @@ -21,8 +21,8 @@ Requirements: - Conductor server with AgentTool + MCP support - - AGENTSPAN_SERVER_URL=http://localhost:8080/api in .env or environment - - AGENTSPAN_LLM_MODEL=google_gemini/gemini-2.0-flash in .env or environment + - CONDUCTOR_SERVER_URL=http://localhost:8080/api in .env or environment + - CONDUCTOR_AGENT_LLM_MODEL=google_gemini/gemini-2.0-flash in .env or environment - GH_TOKEN in .env or environment """ @@ -281,7 +281,7 @@ def search_web(query: str) -> dict: # 1. Deploy once during CI/CD: # runtime.deploy(software_assistant) # CLI alternative: - # agentspan deploy --package examples.adk.33_software_bug_assistant + # runtime.deploy(agent) from a release script # # 2. In a separate long-lived worker process: # runtime.serve(software_assistant) diff --git a/examples/agents/adk/34_ml_engineering.py b/examples/agents/adk/34_ml_engineering.py index 6ac29765d..71f004db3 100644 --- a/examples/agents/adk/34_ml_engineering.py +++ b/examples/agents/adk/34_ml_engineering.py @@ -28,8 +28,8 @@ Requirements: - pip install google-adk - Conductor server - - AGENTSPAN_SERVER_URL=http://localhost:8080/api in .env or environment - - AGENTSPAN_LLM_MODEL=google_gemini/gemini-2.0-flash in .env or environment + - CONDUCTOR_SERVER_URL=http://localhost:8080/api in .env or environment + - CONDUCTOR_AGENT_LLM_MODEL=google_gemini/gemini-2.0-flash in .env or environment """ from google.adk.agents import Agent, LoopAgent, ParallelAgent, SequentialAgent @@ -214,7 +214,7 @@ # 1. Deploy once during CI/CD: # runtime.deploy(ml_pipeline) # CLI alternative: - # agentspan deploy --package examples.adk.34_ml_engineering + # runtime.deploy(agent) from a release script # # 2. In a separate long-lived worker process: # runtime.serve(ml_pipeline) diff --git a/examples/agents/adk/35_rag_agent.py b/examples/agents/adk/35_rag_agent.py index 5735849cc..f317a2fba 100644 --- a/examples/agents/adk/35_rag_agent.py +++ b/examples/agents/adk/35_rag_agent.py @@ -27,8 +27,8 @@ - pip install google-adk - Conductor server with RAG system tasks enabled (--spring.profiles.active=rag) - A configured vector database (e.g., pgvector) - - AGENTSPAN_SERVER_URL=http://localhost:8080/api in .env or environment - - AGENTSPAN_LLM_MODEL=google_gemini/gemini-2.0-flash in .env or environment + - CONDUCTOR_SERVER_URL=http://localhost:8080/api in .env or environment + - CONDUCTOR_AGENT_LLM_MODEL=google_gemini/gemini-2.0-flash in .env or environment """ from conductor.ai.agents import Agent, AgentRuntime, search_tool, index_tool @@ -208,7 +208,7 @@ # 1. Deploy once during CI/CD: # runtime.deploy(rag_agent) # CLI alternative: - # agentspan deploy --package examples.adk.35_rag_agent + # runtime.deploy(agent) from a release script # # 2. In a separate long-lived worker process: # runtime.serve(rag_agent) diff --git a/examples/agents/adk/README.md b/examples/agents/adk/README.md index d600e9577..674d4fc4c 100644 --- a/examples/agents/adk/README.md +++ b/examples/agents/adk/README.md @@ -1,81 +1,9 @@ -# Google ADK Examples +# Google ADK Conductor-agent examples -These examples demonstrate running agents written with [Google's Agent Development Kit (ADK)](https://github.com/google/adk-python) (`google-adk`) on the Agentspan runtime. +These examples run standard Google ADK agents through the durable Conductor agent +runtime. Install `conductor-python[adk]`, configure a server-side Gemini/provider +integration, and set `CONDUCTOR_SERVER_URL` plus `CONDUCTOR_AGENT_LLM_MODEL`. -The agents are defined using standard ADK classes — Agentspan auto-detects the framework, serializes the agent generically, and the server normalizes the config into an agent execution. **Zero translation code in the SDK.** - -## Prerequisites - -```bash -uv pip install google-adk conductor-agent-sdk -``` - -| Package | Required | Notes | -|---------|----------|-------| -| `google-adk` | Yes | `Agent`, `SequentialAgent`, `ParallelAgent`, `LoopAgent`, planners | -| `pydantic` | Some examples | Used for structured output (03) | - -Export environment variables: - -```bash -export AGENTSPAN_LLM_MODEL=openai/gpt-4o-mini -export AGENTSPAN_SERVER_URL=http://localhost:8080/api -export GOOGLE_GEMINI_API_KEY=your-key -``` - -## Examples - -| # | File | Feature | Description | -|---|------|---------|-------------| -| 01 | [01_basic_agent.py](01_basic_agent.py) | **Basic Agent** | Simplest agent — single LLM, no tools. Shows auto-detection and server normalization. | -| 02 | [02_function_tools.py](02_function_tools.py) | **Function Tools** | Multiple Python functions as tools with typed params and docstrings. ADK auto-converts them. | -| 03 | [03_structured_output.py](03_structured_output.py) | **Structured Output** | Pydantic `output_schema` for enforced JSON responses. Combined with `generate_content_config`. | -| 04 | [04_sub_agents.py](04_sub_agents.py) | **Sub-Agents** | Multi-agent orchestration with coordinator → specialist routing via `sub_agents`. | -| 05 | [05_generation_config.py](05_generation_config.py) | **Generation Config** | `generate_content_config` for temperature and output token control. Creative vs. factual agents. | -| 06 | [06_streaming.py](06_streaming.py) | **Streaming** | Default `runtime.run()` flow with a commented `runtime.stream()` alternative for SSE events. | -| 07 | [07_output_key_state.py](07_output_key_state.py) | **Output Key & State** | `output_key` for storing agent results in session state. Multi-agent data passing. | -| 08 | [08_instruction_templating.py](08_instruction_templating.py) | **Instruction Templating** | ADK's `{variable}` syntax in instructions for dynamic context injection from state. | -| 09 | [09_multi_tool_agent.py](09_multi_tool_agent.py) | **Multi-Tool Agent** | Complex tool orchestration with 4 tools (search, inventory, shipping, coupons). Best-practice dict returns. | -| 10 | [10_hierarchical_agents.py](10_hierarchical_agents.py) | **Hierarchical Agents** | Multi-level delegation: coordinator → team leads → specialists. Deep sub_agents nesting. | - -## Feature Coverage - -| Google ADK Feature | Example(s) | -|---|---| -| `Agent` class | All | -| Function tools (auto-converted) | 02, 04, 06, 07, 08, 09, 10 | -| `sub_agents` (multi-agent) | 04, 07, 10 | -| `output_schema` (structured output) | 03 | -| `generate_content_config` (temperature, tokens) | 03, 05 | -| `output_key` (state management) | 07 | -| `instruction` templating (`{var}`) | 08 | -| `description` (for agent routing) | 04, 10 | -| Streaming (`runtime.stream()`, commented alternative) | 06 | -| Multi-tool orchestration | 09 | -| Hierarchical sub-agents (3 levels) | 10 | - -## How It Works - -``` -Google ADK Agent object - │ - ▼ (auto-detected by type(agent).__module__.startswith("google.adk")) -Generic serializer → JSON dict + callable extraction - │ - ▼ POST /api/agent/start { framework: "google_adk", rawConfig: {...} } -Server GoogleADKNormalizer → AgentConfig → Conductor WorkflowDef - │ - ▼ -Agentspan runtime executes the agent -``` - -## Key ADK Differences from OpenAI - -| Concept | Google ADK | OpenAI Agents SDK | -|---|---|---| -| Instructions | `instruction` (singular) | `instructions` (plural) | -| Multi-agent | `sub_agents` | `handoffs` | -| Model config | `generate_content_config` dict | `ModelSettings` class | -| Structured output | `output_schema` | `output_type` | -| Tool definition | Plain Python functions | `@function_tool` decorator | -| State management | `output_key` + `{var}` templating | Context/Sessions | +Run examples from `examples/agents` so shared settings resolve correctly. See the +[Google ADK guide](../../../docs/agents/frameworks/google-adk.md) for the supported +bridge contract. diff --git a/examples/agents/blog-and-video-examples/handoff/03_issue_triage_github_discord.py b/examples/agents/blog-and-video-examples/handoff/03_issue_triage_github_discord.py index 5eab5309e..f5391ef0c 100644 --- a/examples/agents/blog-and-video-examples/handoff/03_issue_triage_github_discord.py +++ b/examples/agents/blog-and-video-examples/handoff/03_issue_triage_github_discord.py @@ -20,10 +20,10 @@ 8. Specialist calls post_to_discord() — notifies the right channel Setup: - pip install agentspan requests - agentspan server start + pip install Conductor requests + Conductor server start - # Store credentials in the AgentSpan UI (localhost:8080 → Credentials): + # Store credentials in the Conductor UI (localhost:8080 → Credentials): # GITHUB_TOKEN = GitHub personal access token (needs repo scope) # DISCORD_TOKEN = Discord bot token diff --git a/examples/agents/blog-and-video-examples/handoff/03_issue_triage_handoff.py b/examples/agents/blog-and-video-examples/handoff/03_issue_triage_handoff.py index a3b576c98..a2ae234cd 100644 --- a/examples/agents/blog-and-video-examples/handoff/03_issue_triage_handoff.py +++ b/examples/agents/blog-and-video-examples/handoff/03_issue_triage_handoff.py @@ -11,8 +11,8 @@ the routing at runtime — not a fixed pipeline, not keyword matching. Setup: - pip install agentspan - agentspan server start + pip install Conductor + Conductor server start python 03_issue_triage_handoff.py """ diff --git a/examples/agents/blog-and-video-examples/manual/07_editorial_manual.py b/examples/agents/blog-and-video-examples/manual/07_editorial_manual.py index 7e0c9ea3e..fcc72e80e 100644 --- a/examples/agents/blog-and-video-examples/manual/07_editorial_manual.py +++ b/examples/agents/blog-and-video-examples/manual/07_editorial_manual.py @@ -5,8 +5,8 @@ based on what the draft needs at each stage. Setup: - pip install agentspan - agentspan server start + pip install Conductor + Conductor server start python 08_editorial_manual.py """ diff --git a/examples/agents/blog-and-video-examples/parallel_execution/02_code_review_parallel.py b/examples/agents/blog-and-video-examples/parallel_execution/02_code_review_parallel.py index 2b4bf49bf..c1d3774e4 100644 --- a/examples/agents/blog-and-video-examples/parallel_execution/02_code_review_parallel.py +++ b/examples/agents/blog-and-video-examples/parallel_execution/02_code_review_parallel.py @@ -15,8 +15,8 @@ - sub_results for per-agent outputs Setup: - pip install agentspan - agentspan server start + pip install Conductor + Conductor server start python 02_code_review_parallel.py """ diff --git a/examples/agents/blog-and-video-examples/parallel_execution/02_code_review_parallel_github.py b/examples/agents/blog-and-video-examples/parallel_execution/02_code_review_parallel_github.py index 88df8c109..53528011c 100644 --- a/examples/agents/blog-and-video-examples/parallel_execution/02_code_review_parallel_github.py +++ b/examples/agents/blog-and-video-examples/parallel_execution/02_code_review_parallel_github.py @@ -10,10 +10,10 @@ PR diff from GitHub and posts the review as a PR comment. Setup: - pip install agentspan requests - agentspan server start + pip install Conductor requests + Conductor server start - # Store credentials in the AgentSpan UI (localhost:8080 → Credentials): + # Store credentials in the Conductor UI (localhost:8080 → Credentials): # GITHUB_TOKEN = your GitHub personal access token (needs repo scope) python 02_code_review_parallel_github.py @@ -157,7 +157,7 @@ def post_pr_review(repo: str, pr_number: int, body: str) -> dict: # ── Run ─────────────────────────────────────────────────────────── if __name__ == "__main__": - REPO = "deeptireddy-lab/agentspan-metrics" + REPO = "deeptireddy-lab/Conductor-metrics" PR_NUMBER = 1 with AgentRuntime() as runtime: diff --git a/examples/agents/blog-and-video-examples/random/06_brainstorm_random.py b/examples/agents/blog-and-video-examples/random/06_brainstorm_random.py index 25b720b3f..6b48a974e 100644 --- a/examples/agents/blog-and-video-examples/random/06_brainstorm_random.py +++ b/examples/agents/blog-and-video-examples/random/06_brainstorm_random.py @@ -5,8 +5,8 @@ which perspective comes next. Setup: - pip install agentspan - agentspan server start + pip install Conductor + Conductor server start python 07_brainstorm_random.py """ diff --git a/examples/agents/blog-and-video-examples/random/07_brainstorm_random.py b/examples/agents/blog-and-video-examples/random/07_brainstorm_random.py index 25b720b3f..6b48a974e 100644 --- a/examples/agents/blog-and-video-examples/random/07_brainstorm_random.py +++ b/examples/agents/blog-and-video-examples/random/07_brainstorm_random.py @@ -5,8 +5,8 @@ which perspective comes next. Setup: - pip install agentspan - agentspan server start + pip install Conductor + Conductor server start python 07_brainstorm_random.py """ diff --git a/examples/agents/blog-and-video-examples/random/07_brainstorm_random_blog.md b/examples/agents/blog-and-video-examples/random/07_brainstorm_random_blog.md index 468e27839..ab7c02991 100644 --- a/examples/agents/blog-and-video-examples/random/07_brainstorm_random_blog.md +++ b/examples/agents/blog-and-video-examples/random/07_brainstorm_random_blog.md @@ -2,7 +2,7 @@ *By Deepti Reddy | May 2026* -*This is Part 6 of an 8-part series covering every multi-agent strategy in Agentspan. Today: the random strategy — a random agent is selected each turn. No rotation, no decision — pure randomness.* +*This is Part 6 of an 8-part series covering Conductor-agent multi-agent strategies. Today: the random strategy — a random agent is selected each turn. No rotation, no decision — pure randomness.* --- @@ -12,12 +12,12 @@ But what if predictability is the problem? In brainstorming, you do not want a f That is the random strategy. Each turn, a random agent is selected. No pattern. No schedule. The same agent might go twice in a row, or not at all for three turns. The randomness creates variety in perspective that a fixed rotation cannot. -## What is Agentspan +## What are Conductor agents -Agentspan is an orchestration layer for building, bringing, and observing AI agents as durable workflows. +Conductor agents are an orchestration layer for building, bringing, and observing AI agents as durable workflows. -- **Build**: define agents with the Agentspan SDK using Agent, @tool, and 8 multi-agent strategies. Compiles to server-side workflows that survive crashes. -- **Bring**: already using an agent framework such as LangGraph, OpenAI Agents SDK, or Google ADK? Pass your agents directly to run(). Agentspan adds durability and orchestration on top. +- **Build**: define agents with the Conductor Python SDK using Agent, @tool, and multi-agent strategies. Compiles to server-side workflows that survive crashes. +- **Bring**: already using an agent framework such as LangGraph, OpenAI Agents SDK, or Google ADK? Pass your agents directly to run(). Conductor adds durability and orchestration on top. - **Observe**: every execution is inspectable in the dashboard. See agent flows, inputs/outputs, tool calls, and token usage. Debug failures, replay runs. ## Setup @@ -26,10 +26,10 @@ Two commands: ```bash pip install conductor-agent-sdk -agentspan server start +conductor server start ``` -This gives you a local Agentspan server with a visual dashboard at localhost:8080. +This gives you a local Conductor server with a visual dashboard at localhost:8080. ## What we are building @@ -221,13 +221,13 @@ Random generates ideas. Round robin reviews them. The summarizer produces the fi ```bash pip install conductor-agent-sdk -agentspan server start +conductor server start python 07_brainstorm_random.py ``` -- **GitHub**: [github.com/agentspan-ai/agentspan](https://github.com/agentspan-ai/agentspan) -- **Blog examples**: [github.com/agentspan-ai/agentspan/tree/main/sdk/python/examples/blog_and_videos/random](https://github.com/agentspan-ai/agentspan/tree/main/sdk/python/examples/blog_and_videos/random) -- **Docs**: [agentspan.ai/docs](https://agentspan.ai/docs) +- **GitHub**: [conductor-oss/conductor](https://github.com/conductor-oss/conductor) +- **Blog examples**: [Python SDK examples](https://github.com/conductoross/sdk/tree/main/python-sdk/examples/agents) +- **Docs**: [Python SDK documentation](../../../README.md) - **Discord**: [https://discord.com/invite/ajcA66JcKq](https://discord.com/invite/ajcA66JcKq) ## What's next diff --git a/examples/agents/blog-and-video-examples/round_robin/06_code_review_debate.py b/examples/agents/blog-and-video-examples/round_robin/06_code_review_debate.py index 369408d7f..ada606ea1 100644 --- a/examples/agents/blog-and-video-examples/round_robin/06_code_review_debate.py +++ b/examples/agents/blog-and-video-examples/round_robin/06_code_review_debate.py @@ -5,8 +5,8 @@ After the debate, a summarizer produces the final verdict. Setup: - pip install agentspan - agentspan server start + pip install Conductor + Conductor server start python 06_code_review_debate.py """ diff --git a/examples/agents/blog-and-video-examples/sequential_pipeline/02_support_ticket_pipeline.py b/examples/agents/blog-and-video-examples/sequential_pipeline/02_support_ticket_pipeline.py index af5624f2c..8c80cf634 100644 --- a/examples/agents/blog-and-video-examples/sequential_pipeline/02_support_ticket_pipeline.py +++ b/examples/agents/blog-and-video-examples/sequential_pipeline/02_support_ticket_pipeline.py @@ -15,8 +15,8 @@ - Three specialist agents chained together Setup: - pip install agentspan - agentspan server start + pip install Conductor + Conductor server start python 02_support_ticket_pipeline.py """ diff --git a/examples/agents/blog-and-video-examples/sequential_pipeline/02_support_ticket_zendesk.py b/examples/agents/blog-and-video-examples/sequential_pipeline/02_support_ticket_zendesk.py index 5e80a98a7..afda08f85 100644 --- a/examples/agents/blog-and-video-examples/sequential_pipeline/02_support_ticket_zendesk.py +++ b/examples/agents/blog-and-video-examples/sequential_pipeline/02_support_ticket_zendesk.py @@ -10,10 +10,10 @@ from Zendesk instead of using hardcoded input. Setup: - pip install agentspan requests - agentspan server start + pip install Conductor requests + Conductor server start - # Store credentials in the AgentSpan UI (localhost:8080 → Credentials): + # Store credentials in the Conductor UI (localhost:8080 → Credentials): # ZENDESK_API = your Zendesk API token # ZENDESK_EMAIL = your Zendesk email (e.g. you@company.com) @@ -26,7 +26,7 @@ # ── Zendesk Tools ──────────────────────────────────────────────── -# Credentials are injected into os.environ by the AgentSpan server +# Credentials are injected into os.environ by the Conductor server # at execution time. No secrets in code. ZENDESK_SUBDOMAIN = "orkeshelp" diff --git a/examples/agents/blog-and-video-examples/swarm/04_support_swarm.py b/examples/agents/blog-and-video-examples/swarm/04_support_swarm.py index 586ae9b64..79db5b436 100644 --- a/examples/agents/blog-and-video-examples/swarm/04_support_swarm.py +++ b/examples/agents/blog-and-video-examples/swarm/04_support_swarm.py @@ -5,8 +5,8 @@ back to the front-line. Peer-to-peer, not top-down. Setup: - pip install agentspan - agentspan server start + pip install Conductor + Conductor server start python 05_support_swarm.py """ diff --git a/examples/agents/blog_and_videos/email-subscription-agent/README.md b/examples/agents/blog_and_videos/email-subscription-agent/README.md index b197c35fa..c35ebe71a 100644 --- a/examples/agents/blog_and_videos/email-subscription-agent/README.md +++ b/examples/agents/blog_and_videos/email-subscription-agent/README.md @@ -2,7 +2,7 @@ An AI agent that scans your email inbox for recurring charges, flags unused or duplicate subscriptions, and tells you exactly what to cancel and where — with a running total of how much you'd save. -Built with [Agentspan](https://agentspan.ai/). +Built with [Conductor](https://github.com/conductor-oss/conductor). --- @@ -28,7 +28,7 @@ By default it runs on sample inbox data so you can try it immediately with no se ## Setup -**1. Install Agentspan** +**1. Install the Conductor Python SDK** ```bash pip install conductor-agent-sdk @@ -40,12 +40,12 @@ pip install conductor-agent-sdk export ANTHROPIC_API_KEY=your_key_here ``` -**3. Start the Agentspan server** +**3. Start the Conductor server** -Agentspan runs on top of Conductor, which needs a local server process running in the background. +Conductor needs a local server process running in the background. ```bash -agentspan server start +conductor server start ``` **4. Run the agent** diff --git a/examples/agents/blog_and_videos/router/04_router_triage.py b/examples/agents/blog_and_videos/router/04_router_triage.py index ae6b3b71b..cf17fbc8b 100644 --- a/examples/agents/blog_and_videos/router/04_router_triage.py +++ b/examples/agents/blog_and_videos/router/04_router_triage.py @@ -5,8 +5,8 @@ brains, each doing what it is good at. Setup: - pip install agentspan - agentspan server start + pip install Conductor + Conductor server start python split-the-brain.py """ diff --git a/examples/agents/claude_agent_sdk/01_basic_agent.py b/examples/agents/claude_agent_sdk/01_basic_agent.py index b7516a193..ac017093a 100644 --- a/examples/agents/claude_agent_sdk/01_basic_agent.py +++ b/examples/agents/claude_agent_sdk/01_basic_agent.py @@ -6,7 +6,7 @@ export ANTHROPIC_API_KEY=sk-... Usage: - # Start the agentspan server first, then: + # Start the Conductor server first, then: uv run python examples/claude_agent_sdk/01_basic_agent.py """ @@ -38,7 +38,7 @@ # 1. Deploy once during CI/CD: # runtime.deploy(reviewer) # CLI alternative: - # agentspan deploy --package examples.claude_agent_sdk.01_basic_agent + # runtime.deploy(agent) from a release script # # 2. In a separate long-lived worker process: # runtime.serve(reviewer) diff --git a/examples/agents/claude_agent_sdk/02_claude_code_config.py b/examples/agents/claude_agent_sdk/02_claude_code_config.py index c52f1ab21..d5fcd3475 100644 --- a/examples/agents/claude_agent_sdk/02_claude_code_config.py +++ b/examples/agents/claude_agent_sdk/02_claude_code_config.py @@ -36,7 +36,7 @@ def main(): # 1. Deploy once during CI/CD: # runtime.deploy(reviewer) # CLI alternative: - # agentspan deploy --package examples.claude_agent_sdk.02_claude_code_config + # runtime.deploy(agent) from a release script # # 2. In a separate long-lived worker process: # runtime.serve(reviewer) diff --git a/examples/agents/claude_agent_sdk/05_build_and_review.py b/examples/agents/claude_agent_sdk/05_build_and_review.py index c493932d5..5cf58b2be 100644 --- a/examples/agents/claude_agent_sdk/05_build_and_review.py +++ b/examples/agents/claude_agent_sdk/05_build_and_review.py @@ -99,7 +99,7 @@ # 1. Deploy once during CI/CD: # runtime.deploy(build_review_swarm) # CLI alternative: - # agentspan deploy --package examples.claude_agent_sdk.05_build_and_review + # runtime.deploy(agent) from a release script # # 2. In a separate long-lived worker process: # runtime.serve(build_review_swarm) diff --git a/examples/agents/claude_agent_sdk/06_github_issue_swarm.py b/examples/agents/claude_agent_sdk/06_github_issue_swarm.py index 6e42c7ee8..0c0600f3e 100644 --- a/examples/agents/claude_agent_sdk/06_github_issue_swarm.py +++ b/examples/agents/claude_agent_sdk/06_github_issue_swarm.py @@ -577,7 +577,7 @@ def shell(command: str) -> str: # 1. Deploy once during CI/CD: # runtime.deploy(github_coding_swarm) # CLI alternative: - # agentspan deploy --package examples.claude_agent_sdk.06_github_issue_swarm + # runtime.deploy(agent) from a release script # # 2. In a separate long-lived worker process: # runtime.serve(github_coding_swarm) diff --git a/examples/agents/dump_agent_configs.py b/examples/agents/dump_agent_configs.py index df1eaddb1..4a5684f7d 100644 --- a/examples/agents/dump_agent_configs.py +++ b/examples/agents/dump_agent_configs.py @@ -16,8 +16,8 @@ # Ensure the examples directory is on the path for settings import sys.path.insert(0, os.path.dirname(__file__)) # Force a consistent model name so both SDKs produce identical values -os.environ["AGENTSPAN_LLM_MODEL"] = "openai/gpt-4o-mini" -os.environ["AGENTSPAN_SECONDARY_LLM_MODEL"] = "openai/gpt-4o" +os.environ["CONDUCTOR_AGENT_LLM_MODEL"] = "openai/gpt-4o-mini" +os.environ["CONDUCTOR_AGENT_SECONDARY_LLM_MODEL"] = "openai/gpt-4o" from conductor.ai.agents.config_serializer import AgentConfigSerializer diff --git a/examples/agents/hello_world_agent_schedule.py b/examples/agents/hello_world_agent_schedule.py index acc82ae83..3eeacb393 100644 --- a/examples/agents/hello_world_agent_schedule.py +++ b/examples/agents/hello_world_agent_schedule.py @@ -1,11 +1,11 @@ """Real-agent scheduling demo. -A genuine ``Agent(...)`` — LLM-backed — deployed via the agentspan SDK and -attached to a cron schedule in one call. Watches the agentspan-runtime fire +A genuine ``Agent(...)`` — LLM-backed — deployed via the Conductor SDK and +attached to a cron schedule in one call. Watches the Conductor-runtime fire the agent on a cadence and shows execution history with the LLM output. Requires: - - agentspan-runtime running on port 8080 with the scheduler module + - Conductor-runtime running on port 8080 with the scheduler module (see docs/design/plans/2026-05-27-agent-scheduling.md task 6) - An OPENAI_API_KEY (or change the model) @@ -28,10 +28,10 @@ SERVER = ( os.environ.get("CONDUCTOR_SERVER_URL") - or os.environ.get("AGENTSPAN_SERVER_URL") + or os.environ.get("CONDUCTOR_SERVER_URL") or "http://localhost:8080/api" ) -MODEL = os.environ.get("AGENTSPAN_MODEL", "anthropic/claude-sonnet-4-6") +MODEL = os.environ.get("CONDUCTOR_AGENT_MODEL", "anthropic/claude-sonnet-4-6") def fetch_executions(agent_name: str, limit: int = 20) -> list[dict]: diff --git a/examples/agents/hello_world_every_second.py b/examples/agents/hello_world_every_second.py index 6f0726369..8fe2fb743 100644 --- a/examples/agents/hello_world_every_second.py +++ b/examples/agents/hello_world_every_second.py @@ -22,7 +22,7 @@ from conductor.ai.agents.schedule import Schedule SERVER = "http://localhost:8080/api" -MODEL = os.environ.get("AGENTSPAN_MODEL", "anthropic/claude-sonnet-4-6") +MODEL = os.environ.get("CONDUCTOR_AGENT_MODEL", "anthropic/claude-sonnet-4-6") def fetch_executions(agent_name: str, limit: int = 30) -> list[dict]: diff --git a/examples/agents/hello_world_schedule.py b/examples/agents/hello_world_schedule.py index b99472942..4fdcbbd01 100644 --- a/examples/agents/hello_world_schedule.py +++ b/examples/agents/hello_world_schedule.py @@ -1,6 +1,6 @@ """Hello-world scheduling demo. -Schedules a "Hello, world!" workflow every 2 seconds via the agentspan SDK, +Schedules a "Hello, world!" workflow every 2 seconds via the Conductor SDK, waits, then prints the execution history. The "agent" here is a Conductor workflow with a single INLINE task that @@ -8,7 +8,7 @@ instead of a real LLM Agent keeps the demo deterministic and free — the scheduling pipeline is identical either way. -NOTE: Targets OSS Conductor at port 8089. The agentspan-runtime on port +NOTE: Targets OSS Conductor at port 8089. The Conductor-runtime on port 8080 doesn't (yet) include the scheduler module — see ``docs/design/plans/2026-05-27-agent-scheduling.md`` open dependency #1. @@ -31,7 +31,7 @@ CONDUCTOR_API = ( os.environ.get("CONDUCTOR_SERVER_URL") - or os.environ.get("AGENTSPAN_SERVER_URL") + or os.environ.get("CONDUCTOR_SERVER_URL") or "http://localhost:8080/api" ) @@ -42,7 +42,7 @@ def register_hello_world_workflow(name: str) -> None: "name": name, "version": 1, "description": "Hello-world demo for agent scheduling", - "ownerEmail": "demo@agentspan.test", + "ownerEmail": "demo@Conductor.test", "schemaVersion": 2, "timeoutSeconds": 30, "timeoutPolicy": "TIME_OUT_WF", diff --git a/examples/agents/kitchen_sink.py b/examples/agents/kitchen_sink.py index 56248be65..c0b944ddd 100644 --- a/examples/agents/kitchen_sink.py +++ b/examples/agents/kitchen_sink.py @@ -3,7 +3,7 @@ """Kitchen Sink — Content Publishing Platform. -A single mega-workflow that exercises every Agentspan SDK feature (89 features). +A single mega-workflow that exercises every Conductor SDK feature (89 features). See design/sdk-design/kitchen-sink.md for the full scenario specification. Demonstrates: @@ -28,13 +28,13 @@ # Or start with auth (requires storing the secret as a credential): mcp-testkit --transport http --auth <secret> - # Store credentials via CLI or Agentspan UI: - agentspan credentials set MCP_AUTH_TOKEN <secret> - agentspan credentials set SEARCH_API_KEY <key> + # Store credentials via CLI or Conductor UI: + the Conductor server credential store + the Conductor server credential store Requirements: - Conductor server with LLM support - - AGENTSPAN_SERVER_URL, AGENTSPAN_LLM_MODEL env vars + - CONDUCTOR_SERVER_URL, CONDUCTOR_AGENT_LLM_MODEL env vars - mcp-testkit running on http://localhost:3001 (for MCP/HTTP tools) - For full execution: Docker, credential store configured """ @@ -776,7 +776,7 @@ def should_handoff_to_publisher(messages: list, **kwargs) -> bool: # 1. Deploy once during CI/CD: # runtime.deploy(full_pipeline) # CLI alternative: - # agentspan deploy --package examples.kitchen_sink + # runtime.deploy(agent) from a release script # # 2. In a separate long-lived worker process: # runtime.serve(full_pipeline) diff --git a/examples/agents/langgraph/01_hello_world.py b/examples/agents/langgraph/01_hello_world.py index 9602cf5a2..ce76d6e8f 100644 --- a/examples/agents/langgraph/01_hello_world.py +++ b/examples/agents/langgraph/01_hello_world.py @@ -9,7 +9,7 @@ - Printing the result Requirements: - - AGENTSPAN_SERVER_URL=http://localhost:8080/api + - CONDUCTOR_SERVER_URL=http://localhost:8080/api - OPENAI_API_KEY for ChatOpenAI """ @@ -19,7 +19,7 @@ llm = ChatOpenAI(model="gpt-4o-mini", temperature=0) -# create_agent with no tools — pure LLM chat, detected as langgraph by Agentspan +# create_agent with no tools — pure LLM chat, detected as langgraph by Conductor graph = create_agent(llm, tools=[], name="hello_world_agent") if __name__ == "__main__": @@ -32,7 +32,7 @@ # 1. Deploy once during CI/CD: # runtime.deploy(graph) # CLI alternative: - # agentspan deploy --package examples.langgraph.01_hello_world + # runtime.deploy(agent) from a release script # # 2. In a separate long-lived worker process: # runtime.serve(graph) diff --git a/examples/agents/langgraph/02_react_with_tools.py b/examples/agents/langgraph/02_react_with_tools.py index 91b696fb9..0e406771c 100644 --- a/examples/agents/langgraph/02_react_with_tools.py +++ b/examples/agents/langgraph/02_react_with_tools.py @@ -9,7 +9,7 @@ - Calculator, string operations, and date utilities Requirements: - - AGENTSPAN_SERVER_URL=http://localhost:8080/api + - CONDUCTOR_SERVER_URL=http://localhost:8080/api - OPENAI_API_KEY for ChatOpenAI """ @@ -71,7 +71,7 @@ def get_today() -> str: # 1. Deploy once during CI/CD: # runtime.deploy(graph) # CLI alternative: - # agentspan deploy --package examples.langgraph.02_react_with_tools + # runtime.deploy(agent) from a release script # # 2. In a separate long-lived worker process: # runtime.serve(graph) diff --git a/examples/agents/langgraph/03_memory.py b/examples/agents/langgraph/03_memory.py index 5e4514035..f0528eedc 100644 --- a/examples/agents/langgraph/03_memory.py +++ b/examples/agents/langgraph/03_memory.py @@ -9,7 +9,7 @@ - How the agent remembers context from earlier messages Requirements: - - AGENTSPAN_SERVER_URL=http://localhost:8080/api + - CONDUCTOR_SERVER_URL=http://localhost:8080/api - OPENAI_API_KEY for ChatOpenAI """ @@ -61,7 +61,7 @@ # 1. Deploy once during CI/CD: # runtime.deploy(graph) # CLI alternative: - # agentspan deploy --package examples.langgraph.03_memory + # runtime.deploy(agent) from a release script # # 2. In a separate long-lived worker process: # runtime.serve(graph) diff --git a/examples/agents/langgraph/04_simple_stategraph.py b/examples/agents/langgraph/04_simple_stategraph.py index 19dc00d53..a288c6c60 100644 --- a/examples/agents/langgraph/04_simple_stategraph.py +++ b/examples/agents/langgraph/04_simple_stategraph.py @@ -10,7 +10,7 @@ - Compiling and naming the graph Requirements: - - AGENTSPAN_SERVER_URL=http://localhost:8080/api + - CONDUCTOR_SERVER_URL=http://localhost:8080/api - OPENAI_API_KEY for ChatOpenAI """ @@ -79,7 +79,7 @@ def generate_answer(state: State) -> State: # 1. Deploy once during CI/CD: # runtime.deploy(graph) # CLI alternative: - # agentspan deploy --package examples.langgraph.04_simple_stategraph + # runtime.deploy(agent) from a release script # # 2. In a separate long-lived worker process: # runtime.serve(graph) diff --git a/examples/agents/langgraph/05_tool_node.py b/examples/agents/langgraph/05_tool_node.py index e323f1a64..010574796 100644 --- a/examples/agents/langgraph/05_tool_node.py +++ b/examples/agents/langgraph/05_tool_node.py @@ -10,7 +10,7 @@ - Annotated list reducer for message accumulation Requirements: - - AGENTSPAN_SERVER_URL=http://localhost:8080/api + - CONDUCTOR_SERVER_URL=http://localhost:8080/api - OPENAI_API_KEY for ChatOpenAI """ @@ -98,7 +98,7 @@ def call_model(state: State) -> State: # 1. Deploy once during CI/CD: # runtime.deploy(graph) # CLI alternative: - # agentspan deploy --package examples.langgraph.05_tool_node + # runtime.deploy(agent) from a release script # # 2. In a separate long-lived worker process: # runtime.serve(graph) diff --git a/examples/agents/langgraph/06_conditional_routing.py b/examples/agents/langgraph/06_conditional_routing.py index b092e9cbc..6273e7027 100644 --- a/examples/agents/langgraph/06_conditional_routing.py +++ b/examples/agents/langgraph/06_conditional_routing.py @@ -9,7 +9,7 @@ - Multiple terminal nodes converging to END Requirements: - - AGENTSPAN_SERVER_URL=http://localhost:8080/api + - CONDUCTOR_SERVER_URL=http://localhost:8080/api - OPENAI_API_KEY for ChatOpenAI """ @@ -104,7 +104,7 @@ def handle_neutral(state: State) -> State: # 1. Deploy once during CI/CD: # runtime.deploy(graph) # CLI alternative: - # agentspan deploy --package examples.langgraph.06_conditional_routing + # runtime.deploy(agent) from a release script # # 2. In a separate long-lived worker process: # runtime.serve(graph) diff --git a/examples/agents/langgraph/07_system_prompt.py b/examples/agents/langgraph/07_system_prompt.py index fee5f5e0d..129a608f8 100644 --- a/examples/agents/langgraph/07_system_prompt.py +++ b/examples/agents/langgraph/07_system_prompt.py @@ -9,7 +9,7 @@ - How the system prompt shapes all LLM responses Requirements: - - AGENTSPAN_SERVER_URL=http://localhost:8080/api + - CONDUCTOR_SERVER_URL=http://localhost:8080/api - OPENAI_API_KEY for ChatOpenAI """ @@ -54,7 +54,7 @@ # 1. Deploy once during CI/CD: # runtime.deploy(graph) # CLI alternative: - # agentspan deploy --package examples.langgraph.07_system_prompt + # runtime.deploy(agent) from a release script # # 2. In a separate long-lived worker process: # runtime.serve(graph) diff --git a/examples/agents/langgraph/08_structured_output.py b/examples/agents/langgraph/08_structured_output.py index 8644e72d7..24b498dbe 100644 --- a/examples/agents/langgraph/08_structured_output.py +++ b/examples/agents/langgraph/08_structured_output.py @@ -9,7 +9,7 @@ - Accessing fields of the structured response Requirements: - - AGENTSPAN_SERVER_URL=http://localhost:8080/api + - CONDUCTOR_SERVER_URL=http://localhost:8080/api - OPENAI_API_KEY for ChatOpenAI """ @@ -55,7 +55,7 @@ class MovieReview(BaseModel): # 1. Deploy once during CI/CD: # runtime.deploy(graph) # CLI alternative: - # agentspan deploy --package examples.langgraph.08_structured_output + # runtime.deploy(agent) from a release script # # 2. In a separate long-lived worker process: # runtime.serve(graph) diff --git a/examples/agents/langgraph/09_math_agent.py b/examples/agents/langgraph/09_math_agent.py index 4e61c82b5..da68469b9 100644 --- a/examples/agents/langgraph/09_math_agent.py +++ b/examples/agents/langgraph/09_math_agent.py @@ -9,7 +9,7 @@ - Chaining multiple tool calls to solve multi-step problems Requirements: - - AGENTSPAN_SERVER_URL=http://localhost:8080/api + - CONDUCTOR_SERVER_URL=http://localhost:8080/api - OPENAI_API_KEY for ChatOpenAI """ @@ -92,7 +92,7 @@ def factorial(n: int) -> str: # 1. Deploy once during CI/CD: # runtime.deploy(graph) # CLI alternative: - # agentspan deploy --package examples.langgraph.09_math_agent + # runtime.deploy(agent) from a release script # # 2. In a separate long-lived worker process: # runtime.serve(graph) diff --git a/examples/agents/langgraph/10_research_agent.py b/examples/agents/langgraph/10_research_agent.py index f54c7c908..d5468b7b9 100644 --- a/examples/agents/langgraph/10_research_agent.py +++ b/examples/agents/langgraph/10_research_agent.py @@ -9,7 +9,7 @@ - Building a multi-step research workflow via tool chaining Requirements: - - AGENTSPAN_SERVER_URL=http://localhost:8080/api + - CONDUCTOR_SERVER_URL=http://localhost:8080/api - OPENAI_API_KEY for ChatOpenAI """ @@ -109,7 +109,7 @@ def cite_source(claim: str, source_type: str = "academic") -> str: # 1. Deploy once during CI/CD: # runtime.deploy(graph) # CLI alternative: - # agentspan deploy --package examples.langgraph.10_research_agent + # runtime.deploy(agent) from a release script # # 2. In a separate long-lived worker process: # runtime.serve(graph) diff --git a/examples/agents/langgraph/11_customer_support.py b/examples/agents/langgraph/11_customer_support.py index 227e5509a..72a56973a 100644 --- a/examples/agents/langgraph/11_customer_support.py +++ b/examples/agents/langgraph/11_customer_support.py @@ -9,7 +9,7 @@ - Billing, technical, and general support branches Requirements: - - AGENTSPAN_SERVER_URL=http://localhost:8080/api + - CONDUCTOR_SERVER_URL=http://localhost:8080/api - OPENAI_API_KEY for ChatOpenAI """ @@ -138,7 +138,7 @@ def handle_general(state: State) -> State: # 1. Deploy once during CI/CD: # runtime.deploy(graph) # CLI alternative: - # agentspan deploy --package examples.langgraph.11_customer_support + # runtime.deploy(agent) from a release script # # 2. In a separate long-lived worker process: # runtime.serve(graph) diff --git a/examples/agents/langgraph/12_code_agent.py b/examples/agents/langgraph/12_code_agent.py index 598762830..ceb0f3759 100644 --- a/examples/agents/langgraph/12_code_agent.py +++ b/examples/agents/langgraph/12_code_agent.py @@ -9,7 +9,7 @@ - Multi-step tool usage: write then explain, or analyze then fix Requirements: - - AGENTSPAN_SERVER_URL=http://localhost:8080/api + - CONDUCTOR_SERVER_URL=http://localhost:8080/api - OPENAI_API_KEY for ChatOpenAI """ @@ -152,7 +152,7 @@ def fix_bug(code: str, error_message: str) -> str: # 1. Deploy once during CI/CD: # runtime.deploy(graph) # CLI alternative: - # agentspan deploy --package examples.langgraph.12_code_agent + # runtime.deploy(agent) from a release script # # 2. In a separate long-lived worker process: # runtime.serve(graph) diff --git a/examples/agents/langgraph/13_multi_turn.py b/examples/agents/langgraph/13_multi_turn.py index 9f1aee752..a7e7f7a63 100644 --- a/examples/agents/langgraph/13_multi_turn.py +++ b/examples/agents/langgraph/13_multi_turn.py @@ -10,7 +10,7 @@ - A practical use case: interview preparation assistant Requirements: - - AGENTSPAN_SERVER_URL=http://localhost:8080/api + - CONDUCTOR_SERVER_URL=http://localhost:8080/api - OPENAI_API_KEY for ChatOpenAI """ @@ -75,7 +75,7 @@ # 1. Deploy once during CI/CD: # runtime.deploy(graph) # CLI alternative: - # agentspan deploy --package examples.langgraph.13_multi_turn + # runtime.deploy(agent) from a release script # # 2. In a separate long-lived worker process: # runtime.serve(graph) diff --git a/examples/agents/langgraph/14_qa_agent.py b/examples/agents/langgraph/14_qa_agent.py index cbd281332..7f6d11631 100644 --- a/examples/agents/langgraph/14_qa_agent.py +++ b/examples/agents/langgraph/14_qa_agent.py @@ -9,7 +9,7 @@ - Grounded answer generation using retrieved context Requirements: - - AGENTSPAN_SERVER_URL=http://localhost:8080/api + - CONDUCTOR_SERVER_URL=http://localhost:8080/api - OPENAI_API_KEY for ChatOpenAI """ @@ -102,7 +102,7 @@ def generate_answer(state: State) -> State: # 1. Deploy once during CI/CD: # runtime.deploy(graph) # CLI alternative: - # agentspan deploy --package examples.langgraph.14_qa_agent + # runtime.deploy(agent) from a release script # # 2. In a separate long-lived worker process: # runtime.serve(graph) diff --git a/examples/agents/langgraph/15_data_pipeline.py b/examples/agents/langgraph/15_data_pipeline.py index 812829682..b4709924b 100644 --- a/examples/agents/langgraph/15_data_pipeline.py +++ b/examples/agents/langgraph/15_data_pipeline.py @@ -9,7 +9,7 @@ - Using an LLM at the analysis and reporting stages Requirements: - - AGENTSPAN_SERVER_URL=http://localhost:8080/api + - CONDUCTOR_SERVER_URL=http://localhost:8080/api - OPENAI_API_KEY for ChatOpenAI """ @@ -121,7 +121,7 @@ def generate_report(state: State) -> State: # 1. Deploy once during CI/CD: # runtime.deploy(graph) # CLI alternative: - # agentspan deploy --package examples.langgraph.15_data_pipeline + # runtime.deploy(agent) from a release script # # 2. In a separate long-lived worker process: # runtime.serve(graph) diff --git a/examples/agents/langgraph/16_parallel_branches.py b/examples/agents/langgraph/16_parallel_branches.py index 490913f02..4b5e11e59 100644 --- a/examples/agents/langgraph/16_parallel_branches.py +++ b/examples/agents/langgraph/16_parallel_branches.py @@ -10,7 +10,7 @@ - Practical use case: parallel pros/cons analysis Requirements: - - AGENTSPAN_SERVER_URL=http://localhost:8080/api + - CONDUCTOR_SERVER_URL=http://localhost:8080/api - OPENAI_API_KEY for ChatOpenAI """ @@ -99,7 +99,7 @@ def merge_and_summarize(state: State) -> State: # 1. Deploy once during CI/CD: # runtime.deploy(graph) # CLI alternative: - # agentspan deploy --package examples.langgraph.16_parallel_branches + # runtime.deploy(agent) from a release script # # 2. In a separate long-lived worker process: # runtime.serve(graph) diff --git a/examples/agents/langgraph/17_error_recovery.py b/examples/agents/langgraph/17_error_recovery.py index bdeeb5d5f..c3105283c 100644 --- a/examples/agents/langgraph/17_error_recovery.py +++ b/examples/agents/langgraph/17_error_recovery.py @@ -10,7 +10,7 @@ - Conditional routing based on whether an error occurred Requirements: - - AGENTSPAN_SERVER_URL=http://localhost:8080/api + - CONDUCTOR_SERVER_URL=http://localhost:8080/api - OPENAI_API_KEY for ChatOpenAI """ @@ -106,7 +106,7 @@ def recover_from_error(state: State) -> State: # 1. Deploy once during CI/CD: # runtime.deploy(graph) # CLI alternative: - # agentspan deploy --package examples.langgraph.17_error_recovery + # runtime.deploy(agent) from a release script # # 2. In a separate long-lived worker process: # runtime.serve(graph) diff --git a/examples/agents/langgraph/18_tools_condition.py b/examples/agents/langgraph/18_tools_condition.py index fd80469ab..8df5d3524 100644 --- a/examples/agents/langgraph/18_tools_condition.py +++ b/examples/agents/langgraph/18_tools_condition.py @@ -9,7 +9,7 @@ - Practical use: a weather and timezone information agent Requirements: - - AGENTSPAN_SERVER_URL=http://localhost:8080/api + - CONDUCTOR_SERVER_URL=http://localhost:8080/api - OPENAI_API_KEY for ChatOpenAI """ @@ -97,7 +97,7 @@ def agent(state: State) -> State: # 1. Deploy once during CI/CD: # runtime.deploy(graph) # CLI alternative: - # agentspan deploy --package examples.langgraph.18_tools_condition + # runtime.deploy(agent) from a release script # # 2. In a separate long-lived worker process: # runtime.serve(graph) diff --git a/examples/agents/langgraph/19_document_analysis.py b/examples/agents/langgraph/19_document_analysis.py index 97b7d9704..182f429a6 100644 --- a/examples/agents/langgraph/19_document_analysis.py +++ b/examples/agents/langgraph/19_document_analysis.py @@ -9,7 +9,7 @@ - Chaining multiple tools to produce a comprehensive document report Requirements: - - AGENTSPAN_SERVER_URL=http://localhost:8080/api + - CONDUCTOR_SERVER_URL=http://localhost:8080/api - OPENAI_API_KEY for ChatOpenAI """ @@ -174,7 +174,7 @@ def classify_sentiment(text: str) -> str: # 1. Deploy once during CI/CD: # runtime.deploy(graph) # CLI alternative: - # agentspan deploy --package examples.langgraph.19_document_analysis + # runtime.deploy(agent) from a release script # # 2. In a separate long-lived worker process: # runtime.serve(graph) diff --git a/examples/agents/langgraph/20_planner_agent.py b/examples/agents/langgraph/20_planner_agent.py index f3c0d8440..d6c794523 100644 --- a/examples/agents/langgraph/20_planner_agent.py +++ b/examples/agents/langgraph/20_planner_agent.py @@ -10,7 +10,7 @@ - Practical use case: project breakdown and task execution Requirements: - - AGENTSPAN_SERVER_URL=http://localhost:8080/api + - CONDUCTOR_SERVER_URL=http://localhost:8080/api - OPENAI_API_KEY for ChatOpenAI """ @@ -130,7 +130,7 @@ def review(state: State) -> State: # 1. Deploy once during CI/CD: # runtime.deploy(graph) # CLI alternative: - # agentspan deploy --package examples.langgraph.20_planner_agent + # runtime.deploy(agent) from a release script # # 2. In a separate long-lived worker process: # runtime.serve(graph) diff --git a/examples/agents/langgraph/21_subgraph.py b/examples/agents/langgraph/21_subgraph.py index a9bcccd28..ad7a380d8 100644 --- a/examples/agents/langgraph/21_subgraph.py +++ b/examples/agents/langgraph/21_subgraph.py @@ -10,7 +10,7 @@ - Practical use case: document processing pipeline with a nested analysis subgraph Requirements: - - AGENTSPAN_SERVER_URL=http://localhost:8080/api + - CONDUCTOR_SERVER_URL=http://localhost:8080/api - OPENAI_API_KEY for ChatOpenAI """ @@ -134,7 +134,7 @@ def build_report(state: DocumentState) -> DocumentState: # 1. Deploy once during CI/CD: # runtime.deploy(graph) # CLI alternative: - # agentspan deploy --package examples.langgraph.21_subgraph + # runtime.deploy(agent) from a release script # # 2. In a separate long-lived worker process: # runtime.serve(graph) diff --git a/examples/agents/langgraph/22_human_in_the_loop.py b/examples/agents/langgraph/22_human_in_the_loop.py index b67b2c4fb..182a85a78 100644 --- a/examples/agents/langgraph/22_human_in_the_loop.py +++ b/examples/agents/langgraph/22_human_in_the_loop.py @@ -12,11 +12,11 @@ - Interactive streaming with schema-driven console prompts The workflow pauses at the review step and waits for a human to approve or -reject the draft via the AgentSpan UI or API. This is true human-in-the-loop, +reject the draft via the Conductor UI or API. This is true human-in-the-loop, not an LLM simulating a reviewer. Requirements: - - AGENTSPAN_SERVER_URL=http://localhost:8080/api + - CONDUCTOR_SERVER_URL=http://localhost:8080/api - OPENAI_API_KEY for ChatOpenAI """ diff --git a/examples/agents/langgraph/23_retry_on_error.py b/examples/agents/langgraph/23_retry_on_error.py index cb487ad37..0da18253e 100644 --- a/examples/agents/langgraph/23_retry_on_error.py +++ b/examples/agents/langgraph/23_retry_on_error.py @@ -10,7 +10,7 @@ - Practical use case: calling an unreliable external API with retries Requirements: - - AGENTSPAN_SERVER_URL=http://localhost:8080/api + - CONDUCTOR_SERVER_URL=http://localhost:8080/api - OPENAI_API_KEY for ChatOpenAI """ @@ -80,7 +80,7 @@ def format_output(state: State) -> State: # 1. Deploy once during CI/CD: # runtime.deploy(graph) # CLI alternative: - # agentspan deploy --package examples.langgraph.23_retry_on_error + # runtime.deploy(agent) from a release script # # 2. In a separate long-lived worker process: # runtime.serve(graph) diff --git a/examples/agents/langgraph/24_map_reduce.py b/examples/agents/langgraph/24_map_reduce.py index b1d56661f..e6e151968 100644 --- a/examples/agents/langgraph/24_map_reduce.py +++ b/examples/agents/langgraph/24_map_reduce.py @@ -10,7 +10,7 @@ - Practical use case: analyzing multiple documents simultaneously Requirements: - - AGENTSPAN_SERVER_URL=http://localhost:8080/api + - CONDUCTOR_SERVER_URL=http://localhost:8080/api - OPENAI_API_KEY for ChatOpenAI """ @@ -117,7 +117,7 @@ def reduce_summaries(state: OverallState) -> OverallState: # 1. Deploy once during CI/CD: # runtime.deploy(graph) # CLI alternative: - # agentspan deploy --package examples.langgraph.24_map_reduce + # runtime.deploy(agent) from a release script # # 2. In a separate long-lived worker process: # runtime.serve(graph) diff --git a/examples/agents/langgraph/25_supervisor.py b/examples/agents/langgraph/25_supervisor.py index 39d4a7d8f..caecb4339 100644 --- a/examples/agents/langgraph/25_supervisor.py +++ b/examples/agents/langgraph/25_supervisor.py @@ -10,7 +10,7 @@ - Practical use case: research → writing → editing pipeline with supervisor control Requirements: - - AGENTSPAN_SERVER_URL=http://localhost:8080/api + - CONDUCTOR_SERVER_URL=http://localhost:8080/api - OPENAI_API_KEY for ChatOpenAI """ @@ -109,7 +109,7 @@ def route(state: State) -> str: # 1. Deploy once during CI/CD: # runtime.deploy(graph) # CLI alternative: - # agentspan deploy --package examples.langgraph.25_supervisor + # runtime.deploy(agent) from a release script # # 2. In a separate long-lived worker process: # runtime.serve(graph) diff --git a/examples/agents/langgraph/26_agent_handoff.py b/examples/agents/langgraph/26_agent_handoff.py index 1d63307af..e84902702 100644 --- a/examples/agents/langgraph/26_agent_handoff.py +++ b/examples/agents/langgraph/26_agent_handoff.py @@ -10,7 +10,7 @@ - Practical use case: customer service triage → billing / technical / general routing Requirements: - - AGENTSPAN_SERVER_URL=http://localhost:8080/api + - CONDUCTOR_SERVER_URL=http://localhost:8080/api - OPENAI_API_KEY for ChatOpenAI """ @@ -128,7 +128,7 @@ def route_to_specialist(state: State) -> str: # 1. Deploy once during CI/CD: # runtime.deploy(graph) # CLI alternative: - # agentspan deploy --package examples.langgraph.26_agent_handoff + # runtime.deploy(agent) from a release script # # 2. In a separate long-lived worker process: # runtime.serve(graph) diff --git a/examples/agents/langgraph/27_persistent_memory.py b/examples/agents/langgraph/27_persistent_memory.py index c755fabc2..b06cd65c0 100644 --- a/examples/agents/langgraph/27_persistent_memory.py +++ b/examples/agents/langgraph/27_persistent_memory.py @@ -10,7 +10,7 @@ - Practical use case: multi-turn chatbot that remembers earlier exchanges Requirements: - - AGENTSPAN_SERVER_URL=http://localhost:8080/api + - CONDUCTOR_SERVER_URL=http://localhost:8080/api - OPENAI_API_KEY for ChatOpenAI """ @@ -63,7 +63,7 @@ def chat(state: State) -> State: # 1. Deploy once during CI/CD: # runtime.deploy(graph) # CLI alternative: - # agentspan deploy --package examples.langgraph.27_persistent_memory + # runtime.deploy(agent) from a release script # # 2. In a separate long-lived worker process: # runtime.serve(graph) diff --git a/examples/agents/langgraph/28_streaming_tokens.py b/examples/agents/langgraph/28_streaming_tokens.py index 7f0871797..7c03468ec 100644 --- a/examples/agents/langgraph/28_streaming_tokens.py +++ b/examples/agents/langgraph/28_streaming_tokens.py @@ -10,7 +10,7 @@ - Practical use case: streaming a long-form answer to the terminal Requirements: - - AGENTSPAN_SERVER_URL=http://localhost:8080/api + - CONDUCTOR_SERVER_URL=http://localhost:8080/api - OPENAI_API_KEY for ChatOpenAI """ @@ -62,7 +62,7 @@ def stream_to_console(prompt: str): # 1. Deploy once during CI/CD: # runtime.deploy(graph) # CLI alternative: - # agentspan deploy --package examples.langgraph.28_streaming_tokens + # runtime.deploy(agent) from a release script # # 2. In a separate long-lived worker process: # runtime.serve(graph) diff --git a/examples/agents/langgraph/29_tool_categories.py b/examples/agents/langgraph/29_tool_categories.py index 17c96cee9..ffe01d752 100644 --- a/examples/agents/langgraph/29_tool_categories.py +++ b/examples/agents/langgraph/29_tool_categories.py @@ -10,7 +10,7 @@ - The LLM correctly selects the right tool for each query Requirements: - - AGENTSPAN_SERVER_URL=http://localhost:8080/api + - CONDUCTOR_SERVER_URL=http://localhost:8080/api - OPENAI_API_KEY for ChatOpenAI """ @@ -135,7 +135,7 @@ def day_of_week(date_str: str) -> str: # 1. Deploy once during CI/CD: # runtime.deploy(graph) # CLI alternative: - # agentspan deploy --package examples.langgraph.29_tool_categories + # runtime.deploy(agent) from a release script # # 2. In a separate long-lived worker process: # runtime.serve(graph) diff --git a/examples/agents/langgraph/30_code_interpreter.py b/examples/agents/langgraph/30_code_interpreter.py index e4c1a29ce..8857aea9f 100644 --- a/examples/agents/langgraph/30_code_interpreter.py +++ b/examples/agents/langgraph/30_code_interpreter.py @@ -10,7 +10,7 @@ - Practical use case: interactive Python tutor / coding assistant Requirements: - - AGENTSPAN_SERVER_URL=http://localhost:8080/api + - CONDUCTOR_SERVER_URL=http://localhost:8080/api - OPENAI_API_KEY for ChatOpenAI """ @@ -137,7 +137,7 @@ def check_syntax(code: str) -> str: # 1. Deploy once during CI/CD: # runtime.deploy(graph) # CLI alternative: - # agentspan deploy --package examples.langgraph.30_code_interpreter + # runtime.deploy(agent) from a release script # # 2. In a separate long-lived worker process: # runtime.serve(graph) diff --git a/examples/agents/langgraph/31_classify_and_route.py b/examples/agents/langgraph/31_classify_and_route.py index b0204a22c..1caa77cfa 100644 --- a/examples/agents/langgraph/31_classify_and_route.py +++ b/examples/agents/langgraph/31_classify_and_route.py @@ -10,7 +10,7 @@ - Practical use case: smart help desk that routes to the right department Requirements: - - AGENTSPAN_SERVER_URL=http://localhost:8080/api + - CONDUCTOR_SERVER_URL=http://localhost:8080/api - OPENAI_API_KEY for ChatOpenAI """ @@ -137,7 +137,7 @@ def route(state: State) -> str: # 1. Deploy once during CI/CD: # runtime.deploy(graph) # CLI alternative: - # agentspan deploy --package examples.langgraph.31_classify_and_route + # runtime.deploy(agent) from a release script # # 2. In a separate long-lived worker process: # runtime.serve(graph) diff --git a/examples/agents/langgraph/32_reflection_agent.py b/examples/agents/langgraph/32_reflection_agent.py index cd82b0d6b..350be0869 100644 --- a/examples/agents/langgraph/32_reflection_agent.py +++ b/examples/agents/langgraph/32_reflection_agent.py @@ -10,7 +10,7 @@ - Practical use case: essay generation with quality self-improvement Requirements: - - AGENTSPAN_SERVER_URL=http://localhost:8080/api + - CONDUCTOR_SERVER_URL=http://localhost:8080/api - OPENAI_API_KEY for ChatOpenAI """ @@ -111,7 +111,7 @@ def finalize(state: State) -> State: # 1. Deploy once during CI/CD: # runtime.deploy(graph) # CLI alternative: - # agentspan deploy --package examples.langgraph.32_reflection_agent + # runtime.deploy(agent) from a release script # # 2. In a separate long-lived worker process: # runtime.serve(graph) diff --git a/examples/agents/langgraph/33_output_validator.py b/examples/agents/langgraph/33_output_validator.py index 6435a94c9..276e42192 100644 --- a/examples/agents/langgraph/33_output_validator.py +++ b/examples/agents/langgraph/33_output_validator.py @@ -10,7 +10,7 @@ - Practical use case: ensuring the LLM always returns valid JSON Requirements: - - AGENTSPAN_SERVER_URL=http://localhost:8080/api + - CONDUCTOR_SERVER_URL=http://localhost:8080/api - OPENAI_API_KEY for ChatOpenAI """ @@ -122,7 +122,7 @@ def finalize(state: State) -> State: # 1. Deploy once during CI/CD: # runtime.deploy(graph) # CLI alternative: - # agentspan deploy --package examples.langgraph.33_output_validator + # runtime.deploy(agent) from a release script # # 2. In a separate long-lived worker process: # runtime.serve(graph) diff --git a/examples/agents/langgraph/34_rag_pipeline.py b/examples/agents/langgraph/34_rag_pipeline.py index b2158ad9a..61c97efa6 100644 --- a/examples/agents/langgraph/34_rag_pipeline.py +++ b/examples/agents/langgraph/34_rag_pipeline.py @@ -11,7 +11,7 @@ - Practical use case: Q&A over a private knowledge base Requirements: - - AGENTSPAN_SERVER_URL=http://localhost:8080/api + - CONDUCTOR_SERVER_URL=http://localhost:8080/api - OPENAI_API_KEY for ChatOpenAI """ @@ -46,11 +46,11 @@ ), Document( page_content=( - "Agentspan provides a runtime for deploying LangGraph and LangChain agents at scale. " + "Conductor provides a runtime for deploying LangGraph and LangChain agents at scale. " "It uses Conductor as an orchestration engine and exposes agents as Conductor tasks. " "The AgentRuntime class handles worker registration and lifecycle management." ), - metadata={"source": "agentspan_docs", "topic": "agentspan"}, + metadata={"source": "Conductor_docs", "topic": "Conductor"}, ), Document( page_content=( @@ -174,7 +174,7 @@ def decide_to_generate(state: State) -> str: # 1. Deploy once during CI/CD: # runtime.deploy(graph) # CLI alternative: - # agentspan deploy --package examples.langgraph.34_rag_pipeline + # runtime.deploy(agent) from a release script # # 2. In a separate long-lived worker process: # runtime.serve(graph) diff --git a/examples/agents/langgraph/35_conversation_manager.py b/examples/agents/langgraph/35_conversation_manager.py index 854df9f20..1a9e0a688 100644 --- a/examples/agents/langgraph/35_conversation_manager.py +++ b/examples/agents/langgraph/35_conversation_manager.py @@ -10,7 +10,7 @@ - Practical use case: long-running chatbot that handles context limits gracefully Requirements: - - AGENTSPAN_SERVER_URL=http://localhost:8080/api + - CONDUCTOR_SERVER_URL=http://localhost:8080/api - OPENAI_API_KEY for ChatOpenAI """ @@ -121,7 +121,7 @@ def respond(state: State) -> State: # 1. Deploy once during CI/CD: # runtime.deploy(graph) # CLI alternative: - # agentspan deploy --package examples.langgraph.35_conversation_manager + # runtime.deploy(agent) from a release script # # 2. In a separate long-lived worker process: # runtime.serve(graph) diff --git a/examples/agents/langgraph/36_debate_agents.py b/examples/agents/langgraph/36_debate_agents.py index d73c094fd..4347d0cf0 100644 --- a/examples/agents/langgraph/36_debate_agents.py +++ b/examples/agents/langgraph/36_debate_agents.py @@ -10,7 +10,7 @@ - Practical use case: pros/cons analysis, brainstorming, red-teaming Requirements: - - AGENTSPAN_SERVER_URL=http://localhost:8080/api + - CONDUCTOR_SERVER_URL=http://localhost:8080/api - OPENAI_API_KEY for ChatOpenAI """ @@ -124,7 +124,7 @@ def continue_or_judge(state: State) -> str: # 1. Deploy once during CI/CD: # runtime.deploy(graph) # CLI alternative: - # agentspan deploy --package examples.langgraph.36_debate_agents + # runtime.deploy(agent) from a release script # # 2. In a separate long-lived worker process: # runtime.serve(graph) diff --git a/examples/agents/langgraph/37_document_grader.py b/examples/agents/langgraph/37_document_grader.py index a9a3267f1..fbbb9da4b 100644 --- a/examples/agents/langgraph/37_document_grader.py +++ b/examples/agents/langgraph/37_document_grader.py @@ -10,7 +10,7 @@ - Practical use case: search result re-ranking and citation-based Q&A Requirements: - - AGENTSPAN_SERVER_URL=http://localhost:8080/api + - CONDUCTOR_SERVER_URL=http://localhost:8080/api - OPENAI_API_KEY for ChatOpenAI """ @@ -120,7 +120,7 @@ def generate_answer(state: State) -> State: # 1. Deploy once during CI/CD: # runtime.deploy(graph) # CLI alternative: - # agentspan deploy --package examples.langgraph.37_document_grader + # runtime.deploy(agent) from a release script # # 2. In a separate long-lived worker process: # runtime.serve(graph) diff --git a/examples/agents/langgraph/38_state_machine.py b/examples/agents/langgraph/38_state_machine.py index 00daac2de..23a3dab9e 100644 --- a/examples/agents/langgraph/38_state_machine.py +++ b/examples/agents/langgraph/38_state_machine.py @@ -10,7 +10,7 @@ - Practical use case: e-commerce order processing pipeline Requirements: - - AGENTSPAN_SERVER_URL=http://localhost:8080/api + - CONDUCTOR_SERVER_URL=http://localhost:8080/api - OPENAI_API_KEY for ChatOpenAI """ @@ -147,7 +147,7 @@ def route_after_payment(state: OrderState) -> str: # 1. Deploy once during CI/CD: # runtime.deploy(graph) # CLI alternative: - # agentspan deploy --package examples.langgraph.38_state_machine + # runtime.deploy(agent) from a release script # # 2. In a separate long-lived worker process: # runtime.serve(graph) diff --git a/examples/agents/langgraph/39_tool_call_chain.py b/examples/agents/langgraph/39_tool_call_chain.py index 4caa4c699..0a9035b5f 100644 --- a/examples/agents/langgraph/39_tool_call_chain.py +++ b/examples/agents/langgraph/39_tool_call_chain.py @@ -10,7 +10,7 @@ - Practical use case: data enrichment pipeline (fetch → transform → validate → summarize) Requirements: - - AGENTSPAN_SERVER_URL=http://localhost:8080/api + - CONDUCTOR_SERVER_URL=http://localhost:8080/api - OPENAI_API_KEY for ChatOpenAI """ @@ -126,7 +126,7 @@ def agent(state: State) -> State: # 1. Deploy once during CI/CD: # runtime.deploy(graph) # CLI alternative: - # agentspan deploy --package examples.langgraph.39_tool_call_chain + # runtime.deploy(agent) from a release script # # 2. In a separate long-lived worker process: # runtime.serve(graph) diff --git a/examples/agents/langgraph/40_agent_as_tool.py b/examples/agents/langgraph/40_agent_as_tool.py index 3d961178f..e0e14fd51 100644 --- a/examples/agents/langgraph/40_agent_as_tool.py +++ b/examples/agents/langgraph/40_agent_as_tool.py @@ -10,7 +10,7 @@ - Practical use case: orchestrator dispatching to a math agent and a writing agent Requirements: - - AGENTSPAN_SERVER_URL=http://localhost:8080/api + - CONDUCTOR_SERVER_URL=http://localhost:8080/api - OPENAI_API_KEY for ChatOpenAI """ @@ -133,7 +133,7 @@ def orchestrator(state: SimpleState) -> SimpleState: # 1. Deploy once during CI/CD: # runtime.deploy(graph) # CLI alternative: - # agentspan deploy --package examples.langgraph.40_agent_as_tool + # runtime.deploy(agent) from a release script # # 2. In a separate long-lived worker process: # runtime.serve(graph) diff --git a/examples/agents/langgraph/41_react_agent_basic.py b/examples/agents/langgraph/41_react_agent_basic.py index 75f8d7537..44172b85b 100644 --- a/examples/agents/langgraph/41_react_agent_basic.py +++ b/examples/agents/langgraph/41_react_agent_basic.py @@ -5,12 +5,12 @@ Demonstrates: - Using langgraph.prebuilt.create_react_agent directly with AgentRuntime - - No Agentspan wrapper needed — pass the graph straight to runtime.run() - - Agentspan detects the ReAct structure and runs LLM + tools on Conductor + - No Conductor wrapper needed — pass the graph straight to runtime.run() + - Conductor detects the ReAct structure and runs LLM + tools on Conductor (AI_MODEL task for the LLM, SIMPLE tasks per tool) Requirements: - - AGENTSPAN_SERVER_URL=http://localhost:8080/api + - CONDUCTOR_SERVER_URL=http://localhost:8080/api - OPENAI_API_KEY for ChatOpenAI """ @@ -51,7 +51,7 @@ def reverse_string(text: str) -> str: llm = ChatOpenAI(model="gpt-4o-mini", temperature=0) -# create_react_agent from langgraph.prebuilt — no Agentspan wrapper needed. +# create_react_agent from langgraph.prebuilt — no Conductor wrapper needed. # AgentRuntime automatically detects the "agent" + "tools" node structure, # extracts the LLM and tools, and runs them on Conductor as separate tasks. graph = create_react_agent(llm, tools=[calculate, count_words, reverse_string], name="math_and_text_agent") @@ -62,7 +62,7 @@ def reverse_string(text: str) -> str: graph, "What is sqrt(256) + 2**10? " "Also count the words in 'the quick brown fox jumps over the lazy dog'. " - "And what is 'Agentspan' reversed?", + "And what is 'Conductor' reversed?", ) print(f"Status: {result.status}") result.print_result() @@ -71,7 +71,7 @@ def reverse_string(text: str) -> str: # 1. Deploy once during CI/CD: # runtime.deploy(graph) # CLI alternative: - # agentspan deploy --package examples.langgraph.41_react_agent_basic + # runtime.deploy(agent) from a release script # # 2. In a separate long-lived worker process: # runtime.serve(graph) diff --git a/examples/agents/langgraph/42_react_agent_system_prompt.py b/examples/agents/langgraph/42_react_agent_system_prompt.py index bad2ee777..d07101d30 100644 --- a/examples/agents/langgraph/42_react_agent_system_prompt.py +++ b/examples/agents/langgraph/42_react_agent_system_prompt.py @@ -5,12 +5,12 @@ Demonstrates: - Passing a system prompt via the prompt parameter (LangGraph 1.x API) - - Agentspan extracts the system prompt and forwards it to the server + - Conductor extracts the system prompt and forwards it to the server as the agent's instructions — no information is lost - Custom persona carried through the full Conductor execution Requirements: - - AGENTSPAN_SERVER_URL=http://localhost:8080/api + - CONDUCTOR_SERVER_URL=http://localhost:8080/api - OPENAI_API_KEY for ChatOpenAI """ @@ -65,7 +65,7 @@ def convert_units(value: float, from_unit: str, to_unit: str) -> str: llm = ChatOpenAI(model="gpt-4o-mini", temperature=0) # prompt sets the system prompt (LangGraph 1.x API, replaces state_modifier). -# Agentspan extracts the prompt from the graph's closure and forwards it +# Conductor extracts the prompt from the graph's closure and forwards it # to the server as the agent's instructions. graph = create_react_agent( llm, @@ -88,7 +88,7 @@ def convert_units(value: float, from_unit: str, to_unit: str) -> str: # 1. Deploy once during CI/CD: # runtime.deploy(graph) # CLI alternative: - # agentspan deploy --package examples.langgraph.42_react_agent_system_prompt + # runtime.deploy(agent) from a release script # # 2. In a separate long-lived worker process: # runtime.serve(graph) diff --git a/examples/agents/langgraph/43_react_agent_multi_model.py b/examples/agents/langgraph/43_react_agent_multi_model.py index dc866408f..7f02c7b63 100644 --- a/examples/agents/langgraph/43_react_agent_multi_model.py +++ b/examples/agents/langgraph/43_react_agent_multi_model.py @@ -6,10 +6,10 @@ Demonstrates: - create_react_agent with ChatAnthropic (Claude) instead of OpenAI - Model is auto-detected from the LLM instance and forwarded to Conductor - - Same code, different model — no Agentspan-specific changes needed + - Same code, different model — no Conductor-specific changes needed Requirements: - - AGENTSPAN_SERVER_URL=http://localhost:8080/api + - CONDUCTOR_SERVER_URL=http://localhost:8080/api - ANTHROPIC_API_KEY for ChatAnthropic """ @@ -56,7 +56,7 @@ def day_of_week(date_str: str) -> str: llm = ChatAnthropic(model="claude-haiku-4-5-20251001", temperature=0) -# Agentspan auto-detects ChatAnthropic and routes LLM calls through +# Conductor auto-detects ChatAnthropic and routes LLM calls through # the anthropic/ provider on the Conductor server. graph = create_react_agent(llm, tools=[get_today, days_between, day_of_week], name="date_calculator_agent") @@ -75,7 +75,7 @@ def day_of_week(date_str: str) -> str: # 1. Deploy once during CI/CD: # runtime.deploy(graph) # CLI alternative: - # agentspan deploy --package examples.langgraph.43_react_agent_multi_model + # runtime.deploy(agent) from a release script # # 2. In a separate long-lived worker process: # runtime.serve(graph) diff --git a/examples/agents/langgraph/44_context_condensation.py b/examples/agents/langgraph/44_context_condensation.py index dcbc6225f..addc1d524 100644 --- a/examples/agents/langgraph/44_context_condensation.py +++ b/examples/agents/langgraph/44_context_condensation.py @@ -27,10 +27,10 @@ --------------------------------------------- Add to ``server/src/main/resources/application.properties`` and restart:: - agentspan.default-context-window=10000 + Conductor.default-context-window=10000 Requirements: - - AGENTSPAN_SERVER_URL=http://localhost:8080/api + - CONDUCTOR_SERVER_URL=http://localhost:8080/api - OPENAI_API_KEY """ @@ -358,7 +358,7 @@ def deep_analyst(domain: str) -> str: # 1. Deploy once during CI/CD: # runtime.deploy(graph) # CLI alternative: - # agentspan deploy --package examples.langgraph.44_context_condensation + # runtime.deploy(agent) from a release script # # 2. In a separate long-lived worker process: # runtime.serve(graph) diff --git a/examples/agents/langgraph/45_advanced_orchestration.py b/examples/agents/langgraph/45_advanced_orchestration.py index de0bd6e63..196cbf608 100644 --- a/examples/agents/langgraph/45_advanced_orchestration.py +++ b/examples/agents/langgraph/45_advanced_orchestration.py @@ -10,7 +10,7 @@ - Practical use case: automated business report generation from raw data inputs Requirements: - - AGENTSPAN_SERVER_URL=http://localhost:8080/api + - CONDUCTOR_SERVER_URL=http://localhost:8080/api - OPENAI_API_KEY for ChatOpenAI """ @@ -184,7 +184,7 @@ def compile_report( # 1. Deploy once during CI/CD: # runtime.deploy(graph) # CLI alternative: - # agentspan deploy --package examples.langgraph.45_advanced_orchestration + # runtime.deploy(agent) from a release script # # 2. In a separate long-lived worker process: # runtime.serve(graph) diff --git a/examples/agents/langgraph/46_crash_and_resume.py b/examples/agents/langgraph/46_crash_and_resume.py index d65ba7e32..a715b0e7b 100644 --- a/examples/agents/langgraph/46_crash_and_resume.py +++ b/examples/agents/langgraph/46_crash_and_resume.py @@ -22,7 +22,7 @@ resume logic, no execution_id needed. Why this matters: - LangGraph graphs running through Agentspan are compiled into durable + LangGraph graphs running through Conductor are compiled into durable Conductor workflows. If your process crashes (OOM, deploy, exception), no work is lost — the server holds the workflow state. You just need to restart serve() and the workers pick up from where they left off. @@ -38,7 +38,7 @@ runtime.start("sales_analyst", "prompt") # or via server API / UI Requirements: - - AGENTSPAN_SERVER_URL=http://localhost:8080/api + - CONDUCTOR_SERVER_URL=http://localhost:8080/api - OPENAI_API_KEY for ChatOpenAI """ @@ -51,8 +51,8 @@ from conductor.ai.agents import AgentRuntime -SESSION_FILE = "/tmp/agentspan_langgraph_resume.session" -SERVER_URL = os.environ.get("AGENTSPAN_SERVER_URL", "http://localhost:8080/api") +SESSION_FILE = "/tmp/Conductor_langgraph_resume.session" +SERVER_URL = os.environ.get("CONDUCTOR_SERVER_URL", "http://localhost:8080/api") UI_BASE = SERVER_URL.replace("/api", "") @@ -147,7 +147,7 @@ def generate_report(analysis: str) -> str: ui_link = f"{UI_BASE}/execution/{saved_execution_id}" print("-" * 60) -print("Open the Agentspan UI to see the execution in RUNNING state:") +print("Open the Conductor UI to see the execution in RUNNING state:") print(f" {ui_link}") print() print("The workflow is alive on the server but stalled — no workers are") diff --git a/examples/agents/langgraph/README.md b/examples/agents/langgraph/README.md index 58de06c4e..114bddf13 100644 --- a/examples/agents/langgraph/README.md +++ b/examples/agents/langgraph/README.md @@ -1,189 +1,8 @@ -# LangGraph Examples +# LangGraph Conductor-agent examples -46 examples demonstrating LangGraph integration with Agentspan, from hello-world to advanced multi-agent systems. +These examples run LangGraph graphs through the durable Conductor-agent runtime. +Install `conductor-python[langgraph]`, configure the server-side provider, and set +`CONDUCTOR_SERVER_URL` plus `CONDUCTOR_AGENT_LLM_MODEL` before running them. -## Prerequisites - -```bash -uv pip install langgraph langchain-core langchain-openai conductor-agent-sdk -``` - -| Package | Required | Notes | -|---------|----------|-------| -| `langgraph` | Yes | `StateGraph`, `create_react_agent`, `ToolNode`, `tools_condition` | -| `langchain-core` | Yes | Messages, tools, documents | -| `langchain-openai` | Yes | `ChatOpenAI` LLM provider | -| `langchain-anthropic` | Optional | Only for `43_react_agent_multi_model.py` (requires `ANTHROPIC_API_KEY`) | -| `pydantic` | Some examples | Used for structured output (08) | - -## Quick Start - -```bash -export AGENTSPAN_SERVER_URL=http://localhost:8080/api -export OPENAI_API_KEY=sk-... - -cd sdk/python -uv run python examples/langgraph/01_hello_world.py -``` - -## Examples - -### Basics (01–05) - -| # | File | Topic | -|---|------|-------| -| 01 | `01_hello_world.py` | Simplest agent with `create_agent`, no tools | -| 02 | `02_react_with_tools.py` | ReAct agent with `@tool` functions | -| 03 | `03_memory.py` | Multi-turn memory with `MemorySaver` + `session_id` | -| 04 | `04_simple_stategraph.py` | Custom `StateGraph` with typed state | -| 05 | `05_tool_node.py` | `ToolNode` + `tools_condition` standard loop | - -### Graph Patterns (06–10) - -| # | File | Topic | -|---|------|-------| -| 06 | `06_conditional_routing.py` | Conditional edges with routing functions | -| 07 | `07_system_prompt.py` | Custom system prompt via `prompt` parameter | -| 08 | `08_structured_output.py` | Structured/JSON output with `with_structured_output` | -| 09 | `09_math_agent.py` | Math tools (calculator, statistics) | -| 10 | `10_research_agent.py` | Multi-step research pipeline | - -### Domain Agents (11–15) - -| # | File | Topic | -|---|------|-------| -| 11 | `11_customer_support.py` | Customer service triage and response | -| 12 | `12_code_agent.py` | Code generation and explanation | -| 13 | `13_multi_turn.py` | Multi-turn conversation with history | -| 14 | `14_qa_agent.py` | Question answering with context | -| 15 | `15_data_pipeline.py` | Sequential data processing pipeline | - -### Advanced Patterns (16–20) - -| # | File | Topic | -|---|------|-------| -| 16 | `16_parallel_branches.py` | Parallel execution with `Send` API | -| 17 | `17_error_recovery.py` | Error handling and fallback nodes | -| 18 | `18_tools_condition.py` | Complex tool routing with multiple conditions | -| 19 | `19_document_analysis.py` | Multi-step document analysis | -| 20 | `20_planner_agent.py` | Plan → Execute → Review pipeline | - -### Composition & Reliability (21–25) - -| # | File | Topic | -|---|------|-------| -| 21 | `21_subgraph.py` | Nested subgraphs for modular composition | -| 22 | `22_human_in_the_loop.py` | Interrupt/resume with human approval | -| 23 | `23_retry_on_error.py` | Automatic retry with `RetryPolicy` | -| 24 | `24_map_reduce.py` | Fan-out / fan-in with `Send` API | -| 25 | `25_supervisor.py` | Supervisor orchestrating specialist agents | - -### Multi-Agent & Memory (26–30) - -| # | File | Topic | -|---|------|-------| -| 26 | `26_agent_handoff.py` | Explicit agent handoff (triage → specialist) | -| 27 | `27_persistent_memory.py` | Cross-session state with `MemorySaver` | -| 28 | `28_streaming_tokens.py` | Token-by-token streaming with `stream_mode="messages"` | -| 29 | `29_tool_categories.py` | Organized tool categories (math, string, date) | -| 30 | `30_code_interpreter.py` | Safe expression evaluation and code analysis | - -### Intelligence Patterns (31–35) - -| # | File | Topic | -|---|------|-------| -| 31 | `31_classify_and_route.py` | LLM-based classification + domain routing | -| 32 | `32_reflection_agent.py` | Generate → critique → improve loop | -| 33 | `33_output_validator.py` | Generate → validate → retry until schema passes | -| 34 | `34_rag_pipeline.py` | RAG with retrieve → grade → rewrite → generate | -| 35 | `35_conversation_manager.py` | Sliding window + auto-summarization | - -### Advanced Multi-Agent (36–40) - -| # | File | Topic | -|---|------|-------| -| 36 | `36_debate_agents.py` | Two agents arguing opposing positions | -| 37 | `37_document_grader.py` | Score + filter documents for relevance | -| 38 | `38_state_machine.py` | Order processing as an explicit state machine | -| 39 | `39_tool_call_chain.py` | Chaining sequential tool calls (ToolNode loop) | -| 40 | `40_agent_as_tool.py` | Compiled graph wrapped as `@tool` for orchestrators | - -### ReAct Variants & Production (41–46) - -| # | File | Topic | -|---|------|-------| -| 41 | `41_react_agent_basic.py` | Basic ReAct pattern | -| 42 | `42_react_agent_system_prompt.py` | ReAct with system prompt | -| 43 | `43_react_agent_multi_model.py` | Multi-model ReAct (OpenAI + Anthropic) | -| 44 | `44_context_condensation.py` | Orchestrator + sub-agent stress test | -| 45 | `45_advanced_orchestration.py` | Complex orchestration patterns | -| 46 | `46_crash_and_resume.py` | Crash recovery: resume execution after process restart | - -## Common Patterns - -### Basic `create_agent` (detected as `langgraph`) -```python -from langchain.agents import create_agent -from langchain_openai import ChatOpenAI -from langchain_core.tools import tool -from conductor.ai.agents import AgentRuntime - -llm = ChatOpenAI(model="gpt-4o-mini", temperature=0) - -@tool -def my_tool(input: str) -> str: - """Tool description.""" - return f"Result: {input}" - -graph = create_agent(llm, tools=[my_tool], name="my_agent") - -with AgentRuntime() as runtime: - result = runtime.run(graph, "your prompt") - result.print_result() -``` - -### Custom `StateGraph` -```python -from langgraph.graph import StateGraph, START, END -from typing import TypedDict - -class State(TypedDict): - messages: list - result: str - -def my_node(state: State) -> State: - return {"result": "done"} - -builder = StateGraph(State) -builder.add_node("process", my_node) -builder.add_edge(START, "process") -builder.add_edge("process", END) -graph = builder.compile(name="my_graph") -``` - -### Session-based memory -```python -with AgentRuntime() as runtime: - result = runtime.run(graph, "prompt", session_id="user-123") -``` - -### Crash & Resume -```python -# Deploy once (CI/CD): -with AgentRuntime() as runtime: - runtime.deploy(graph) - -# Long-running worker process (restart on crash): -with AgentRuntime() as runtime: - runtime.serve(graph) # blocks, polls for tasks - -# After a crash, just restart serve() — workers reconnect, -# stalled tasks resume automatically. No special logic needed. -``` - -## Requirements - -- Python 3.11+ -- `uv` package manager -- `AGENTSPAN_SERVER_URL` — Agentspan server endpoint -- `OPENAI_API_KEY` — OpenAI API key for `ChatOpenAI` +Use the examples to explore graph execution, tools, state, streaming, and recovery. +See the [LangGraph guide](../../../docs/agents/frameworks/langgraph.md). diff --git a/examples/agents/openai/01_basic_agent.py b/examples/agents/openai/01_basic_agent.py index a157d3a08..563367787 100644 --- a/examples/agents/openai/01_basic_agent.py +++ b/examples/agents/openai/01_basic_agent.py @@ -12,8 +12,8 @@ Requirements: - pip install openai-agents - Conductor server with OpenAI LLM integration configured - - AGENTSPAN_SERVER_URL=http://localhost:8080/api as environment variable - - AGENTSPAN_LLM_MODEL=openai/gpt-4o-mini as environment variable + - CONDUCTOR_SERVER_URL=http://localhost:8080/api as environment variable + - CONDUCTOR_AGENT_LLM_MODEL=openai/gpt-4o-mini as environment variable """ from agents import Agent @@ -38,7 +38,7 @@ # 1. Deploy once during CI/CD: # runtime.deploy(agent) # CLI alternative: - # agentspan deploy --package examples.openai.01_basic_agent + # runtime.deploy(agent) from a release script # # 2. In a separate long-lived worker process: # runtime.serve(agent) diff --git a/examples/agents/openai/02_function_tools.py b/examples/agents/openai/02_function_tools.py index 1a864fe00..b65b40277 100644 --- a/examples/agents/openai/02_function_tools.py +++ b/examples/agents/openai/02_function_tools.py @@ -12,8 +12,8 @@ Requirements: - pip install openai-agents - Conductor server with OpenAI LLM integration configured - - AGENTSPAN_SERVER_URL=http://localhost:8080/api as environment variable - - AGENTSPAN_LLM_MODEL=openai/gpt-4o-mini as environment variable + - CONDUCTOR_SERVER_URL=http://localhost:8080/api as environment variable + - CONDUCTOR_AGENT_LLM_MODEL=openai/gpt-4o-mini as environment variable """ from agents import Agent, function_tool @@ -86,7 +86,7 @@ def lookup_population(city: str) -> str: # 1. Deploy once during CI/CD: # runtime.deploy(agent) # CLI alternative: - # agentspan deploy --package examples.openai.02_function_tools + # runtime.deploy(agent) from a release script # # 2. In a separate long-lived worker process: # runtime.serve(agent) diff --git a/examples/agents/openai/03_structured_output.py b/examples/agents/openai/03_structured_output.py index 2d99c1212..9af366393 100644 --- a/examples/agents/openai/03_structured_output.py +++ b/examples/agents/openai/03_structured_output.py @@ -11,8 +11,8 @@ Requirements: - pip install openai-agents pydantic - Conductor server with OpenAI LLM integration configured - - AGENTSPAN_SERVER_URL=http://localhost:8080/api as environment variable - - AGENTSPAN_LLM_MODEL=openai/gpt-4o-mini as environment variable + - CONDUCTOR_SERVER_URL=http://localhost:8080/api as environment variable + - CONDUCTOR_AGENT_LLM_MODEL=openai/gpt-4o-mini as environment variable """ from typing import List @@ -65,7 +65,7 @@ class MovieList(BaseModel): # 1. Deploy once during CI/CD: # runtime.deploy(agent) # CLI alternative: - # agentspan deploy --package examples.openai.03_structured_output + # runtime.deploy(agent) from a release script # # 2. In a separate long-lived worker process: # runtime.serve(agent) diff --git a/examples/agents/openai/04_handoffs.py b/examples/agents/openai/04_handoffs.py index 1feecaa68..790c6e668 100644 --- a/examples/agents/openai/04_handoffs.py +++ b/examples/agents/openai/04_handoffs.py @@ -12,8 +12,8 @@ Requirements: - pip install openai-agents - Conductor server with OpenAI LLM integration configured - - AGENTSPAN_SERVER_URL=http://localhost:8080/api as environment variable - - AGENTSPAN_LLM_MODEL=openai/gpt-4o-mini as environment variable + - CONDUCTOR_SERVER_URL=http://localhost:8080/api as environment variable + - CONDUCTOR_AGENT_LLM_MODEL=openai/gpt-4o-mini as environment variable """ from agents import Agent, function_tool @@ -114,7 +114,7 @@ def get_product_info(product_name: str) -> str: # 1. Deploy once during CI/CD: # runtime.deploy(triage_agent) # CLI alternative: - # agentspan deploy --package examples.openai.04_handoffs + # runtime.deploy(agent) from a release script # # 2. In a separate long-lived worker process: # runtime.serve(triage_agent) diff --git a/examples/agents/openai/05_guardrails.py b/examples/agents/openai/05_guardrails.py index 6c5bb3ff9..d3a37c9b9 100644 --- a/examples/agents/openai/05_guardrails.py +++ b/examples/agents/openai/05_guardrails.py @@ -12,8 +12,8 @@ Requirements: - pip install openai-agents - Conductor server with OpenAI LLM integration configured - - AGENTSPAN_SERVER_URL=http://localhost:8080/api as environment variable - - AGENTSPAN_LLM_MODEL=openai/gpt-4o-mini as environment variable + - CONDUCTOR_SERVER_URL=http://localhost:8080/api as environment variable + - CONDUCTOR_AGENT_LLM_MODEL=openai/gpt-4o-mini as environment variable """ from agents import ( @@ -130,7 +130,7 @@ def check_output_safety(ctx, agent, output) -> GuardrailFunctionOutput: # 1. Deploy once during CI/CD: # runtime.deploy(agent) # CLI alternative: - # agentspan deploy --package examples.openai.05_guardrails + # runtime.deploy(agent) from a release script # # 2. In a separate long-lived worker process: # runtime.serve(agent) diff --git a/examples/agents/openai/06_model_settings.py b/examples/agents/openai/06_model_settings.py index cba2dfcf3..2bd4c174e 100644 --- a/examples/agents/openai/06_model_settings.py +++ b/examples/agents/openai/06_model_settings.py @@ -12,8 +12,8 @@ Requirements: - pip install openai-agents - Conductor server with OpenAI LLM integration configured - - AGENTSPAN_SERVER_URL=http://localhost:8080/api as environment variable - - AGENTSPAN_LLM_MODEL=openai/gpt-4o-mini as environment variable + - CONDUCTOR_SERVER_URL=http://localhost:8080/api as environment variable + - CONDUCTOR_AGENT_LLM_MODEL=openai/gpt-4o-mini as environment variable """ from agents import Agent, ModelSettings @@ -64,7 +64,7 @@ # 1. Deploy once during CI/CD: # runtime.deploy(creative_agent) # CLI alternative: - # agentspan deploy --package examples.openai.06_model_settings + # runtime.deploy(agent) from a release script # # 2. In a separate long-lived worker process: # runtime.serve(creative_agent) diff --git a/examples/agents/openai/07_streaming.py b/examples/agents/openai/07_streaming.py index eefa468fb..35023be82 100644 --- a/examples/agents/openai/07_streaming.py +++ b/examples/agents/openai/07_streaming.py @@ -11,8 +11,8 @@ Requirements: - pip install openai-agents - Conductor server with OpenAI LLM integration configured - - AGENTSPAN_SERVER_URL=http://localhost:8080/api as environment variable - - AGENTSPAN_LLM_MODEL=openai/gpt-4o-mini as environment variable + - CONDUCTOR_SERVER_URL=http://localhost:8080/api as environment variable + - CONDUCTOR_AGENT_LLM_MODEL=openai/gpt-4o-mini as environment variable """ from agents import Agent, function_tool @@ -61,7 +61,7 @@ def search_knowledge_base(query: str) -> str: # 1. Deploy once during CI/CD: # runtime.deploy(agent) # CLI alternative: - # agentspan deploy --package examples.openai.07_streaming + # runtime.deploy(agent) from a release script # # 2. In a separate long-lived worker process: # runtime.serve(agent) diff --git a/examples/agents/openai/08_agent_as_tool.py b/examples/agents/openai/08_agent_as_tool.py index fc07c369c..ecf51122c 100644 --- a/examples/agents/openai/08_agent_as_tool.py +++ b/examples/agents/openai/08_agent_as_tool.py @@ -11,8 +11,8 @@ Requirements: - pip install openai-agents - Conductor server with OpenAI LLM integration configured - - AGENTSPAN_SERVER_URL=http://localhost:8080/api as environment variable - - AGENTSPAN_LLM_MODEL=openai/gpt-4o-mini as environment variable + - CONDUCTOR_SERVER_URL=http://localhost:8080/api as environment variable + - CONDUCTOR_AGENT_LLM_MODEL=openai/gpt-4o-mini as environment variable """ from agents import Agent, function_tool @@ -107,7 +107,7 @@ def extract_keywords(text: str) -> str: # 1. Deploy once during CI/CD: # runtime.deploy(manager) # CLI alternative: - # agentspan deploy --package examples.openai.08_agent_as_tool + # runtime.deploy(agent) from a release script # # 2. In a separate long-lived worker process: # runtime.serve(manager) diff --git a/examples/agents/openai/09_dynamic_instructions.py b/examples/agents/openai/09_dynamic_instructions.py index dbf330a83..5fbad6119 100644 --- a/examples/agents/openai/09_dynamic_instructions.py +++ b/examples/agents/openai/09_dynamic_instructions.py @@ -11,8 +11,8 @@ Requirements: - pip install openai-agents - Conductor server with OpenAI LLM integration configured - - AGENTSPAN_SERVER_URL=http://localhost:8080/api as environment variable - - AGENTSPAN_LLM_MODEL=openai/gpt-4o-mini as environment variable + - CONDUCTOR_SERVER_URL=http://localhost:8080/api as environment variable + - CONDUCTOR_AGENT_LLM_MODEL=openai/gpt-4o-mini as environment variable """ from datetime import datetime @@ -80,7 +80,7 @@ def add_todo(task: str, priority: str = "medium") -> str: # 1. Deploy once during CI/CD: # runtime.deploy(agent) # CLI alternative: - # agentspan deploy --package examples.openai.09_dynamic_instructions + # runtime.deploy(agent) from a release script # # 2. In a separate long-lived worker process: # runtime.serve(agent) diff --git a/examples/agents/openai/10_multi_model.py b/examples/agents/openai/10_multi_model.py index 344f6d8fe..b3febf541 100644 --- a/examples/agents/openai/10_multi_model.py +++ b/examples/agents/openai/10_multi_model.py @@ -11,9 +11,9 @@ Requirements: - pip install openai-agents - Conductor server with OpenAI LLM integrations configured - - AGENTSPAN_SERVER_URL=http://localhost:8080/api as environment variable - - AGENTSPAN_LLM_MODEL=openai/gpt-4o-mini as environment variable - - AGENTSPAN_SECONDARY_LLM_MODEL=openai/gpt-4o as environment variable + - CONDUCTOR_SERVER_URL=http://localhost:8080/api as environment variable + - CONDUCTOR_AGENT_LLM_MODEL=openai/gpt-4o-mini as environment variable + - CONDUCTOR_AGENT_SECONDARY_LLM_MODEL=openai/gpt-4o as environment variable """ from agents import Agent, ModelSettings, function_tool @@ -116,7 +116,7 @@ def generate_code_sample(language: str, topic: str) -> str: # 1. Deploy once during CI/CD: # runtime.deploy(triage) # CLI alternative: - # agentspan deploy --package examples.openai.10_multi_model + # runtime.deploy(agent) from a release script # # 2. In a separate long-lived worker process: # runtime.serve(triage) diff --git a/examples/agents/openai/README.md b/examples/agents/openai/README.md index 723b7f9c4..ed1ecd328 100644 --- a/examples/agents/openai/README.md +++ b/examples/agents/openai/README.md @@ -1,70 +1,10 @@ -# OpenAI Agent SDK Examples +# OpenAI Agents SDK-style Conductor-agent examples -These examples demonstrate running agents written with the [OpenAI Agents SDK](https://github.com/openai/openai-agents-python) (`openai-agents`) on the Agentspan runtime. +These examples show OpenAI Agents SDK-style agents running on the durable +Conductor runtime. Install `conductor-python[openai-agents]`, configure the +provider on the Conductor server, and set `CONDUCTOR_SERVER_URL` and +`CONDUCTOR_AGENT_LLM_MODEL`. -The agents are defined using standard OpenAI SDK classes and decorators — Agentspan auto-detects the framework, serializes the agent generically, and the server normalizes the config into an agent execution. **Zero translation code in the SDK.** - -## Prerequisites - -```bash -uv pip install openai-agents conductor-agent-sdk -``` - -| Package | Required | Notes | -|---------|----------|-------| -| `openai-agents` | Yes | `Agent`, `function_tool`, `ModelSettings`, guardrails | -| `pydantic` | Some examples | Used for structured output (03) | - -Export environment variables: - -```bash -export AGENTSPAN_LLM_MODEL=openai/gpt-4o-mini -export AGENTSPAN_SERVER_URL=http://localhost:8080/api -export OPENAI_API_KEY=your-key -``` - -## Examples - -| # | File | Feature | Description | -|---|------|---------|-------------| -| 01 | [01_basic_agent.py](01_basic_agent.py) | **Basic Agent** | Simplest agent — single LLM, no tools. Shows auto-detection and server normalization. | -| 02 | [02_function_tools.py](02_function_tools.py) | **Function Tools** | Multiple `@function_tool` decorated functions with typed parameters. Tools are auto-extracted as Conductor workers. | -| 03 | [03_structured_output.py](03_structured_output.py) | **Structured Output** | Pydantic `output_type` for enforced JSON schema responses. Combined with `ModelSettings`. | -| 04 | [04_handoffs.py](04_handoffs.py) | **Handoffs** | Multi-agent orchestration with triage → specialist handoffs. Maps to Conductor's `strategy="handoff"`. | -| 05 | [05_guardrails.py](05_guardrails.py) | **Guardrails** | Input guardrails (PII detection) and output guardrails (safety filtering). Guardrail functions become Conductor workers. | -| 06 | [06_model_settings.py](06_model_settings.py) | **Model Settings** | `ModelSettings` for temperature and max_tokens tuning. Creative vs. precise agents. | -| 07 | [07_streaming.py](07_streaming.py) | **Streaming** | Default `runtime.run()` flow with a commented `runtime.stream()` alternative for SSE events. | -| 08 | [08_agent_as_tool.py](08_agent_as_tool.py) | **Agent-as-Tool** | Manager pattern with `Agent.as_tool()`. Manager retains control and synthesizes specialist results. | -| 09 | [09_dynamic_instructions.py](09_dynamic_instructions.py) | **Dynamic Instructions** | Callable instruction function that generates context-aware prompts (time-of-day, user preferences). | -| 10 | [10_multi_model.py](10_multi_model.py) | **Multi-Model** | Multiple agents with shared `settings.llm_model`. Override via `AGENTSPAN_LLM_MODEL` env var. | - -## Feature Coverage - -| OpenAI SDK Feature | Example(s) | -|---|---| -| `Agent` class | All | -| `@function_tool` decorator | 02, 04, 05, 07, 08, 09, 10 | -| `handoffs` | 04, 10 | -| `output_type` (structured output) | 03 | -| `ModelSettings` (temperature, max_tokens) | 03, 06, 10 | -| `InputGuardrail` / `OutputGuardrail` | 05 | -| `Agent.as_tool()` (manager pattern) | 08 | -| Dynamic instructions (callable) | 09 | -| Multiple models | 10 | -| Streaming (`runtime.stream()`, commented alternative) | 07 | -| Multi-agent patterns | 04, 08, 10 | - -## How It Works - -``` -OpenAI Agent object - │ - ▼ (auto-detected by type(agent).__module__ == "agents") -Generic serializer → JSON dict + callable extraction - │ - ▼ POST /api/agent/start { framework: "openai", rawConfig: {...} } -Server OpenAINormalizer → AgentConfig → Conductor WorkflowDef - │ - ▼ -Agentspan runtime executes the agent -``` +The bridge preserves familiar agent, tool, handoff, and streaming shapes while +Conductor records workflow state and tool execution. See the +[framework guide](../../../docs/agents/frameworks/openai.md). diff --git a/examples/agents/quickstart/01_basic_agent.py b/examples/agents/quickstart/01_basic_agent.py index b39d8c0b3..b155e4561 100644 --- a/examples/agents/quickstart/01_basic_agent.py +++ b/examples/agents/quickstart/01_basic_agent.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -"""Basic agent — the simplest possible agentspan example.""" +"""Basic agent — the simplest possible Conductor example.""" from conductor.ai.agents import Agent, AgentRuntime @@ -20,7 +20,7 @@ # 1. Deploy once during CI/CD: # rt.deploy(agent) # CLI alternative: - # agentspan deploy --package examples.quickstart.01_basic_agent + # runtime.deploy(agent) from a release script # # 2. In a separate long-lived worker process: # rt.serve(agent) diff --git a/examples/agents/quickstart/02_tools.py b/examples/agents/quickstart/02_tools.py index e1283a7f5..934514ae5 100644 --- a/examples/agents/quickstart/02_tools.py +++ b/examples/agents/quickstart/02_tools.py @@ -28,7 +28,7 @@ def get_weather(city: str) -> str: # 1. Deploy once during CI/CD: # rt.deploy(agent) # CLI alternative: - # agentspan deploy --package examples.quickstart.02_tools + # runtime.deploy(agent) from a release script # # 2. In a separate long-lived worker process: # rt.serve(agent) diff --git a/examples/agents/quickstart/03_multi_agent.py b/examples/agents/quickstart/03_multi_agent.py index 2040746da..9490217fd 100644 --- a/examples/agents/quickstart/03_multi_agent.py +++ b/examples/agents/quickstart/03_multi_agent.py @@ -30,7 +30,7 @@ # 1. Deploy once during CI/CD: # rt.deploy(pipeline) # CLI alternative: - # agentspan deploy --package examples.quickstart.03_multi_agent + # runtime.deploy(agent) from a release script # # 2. In a separate long-lived worker process: # rt.serve(pipeline) diff --git a/examples/agents/quickstart/04_guardrails.py b/examples/agents/quickstart/04_guardrails.py index 29e9ae48f..9191831cd 100644 --- a/examples/agents/quickstart/04_guardrails.py +++ b/examples/agents/quickstart/04_guardrails.py @@ -28,7 +28,7 @@ # 1. Deploy once during CI/CD: # rt.deploy(agent) # CLI alternative: - # agentspan deploy --package examples.quickstart.04_guardrails + # runtime.deploy(agent) from a release script # # 2. In a separate long-lived worker process: # rt.serve(agent) diff --git a/examples/agents/quickstart/05_claude_code.py b/examples/agents/quickstart/05_claude_code.py index 75ca49dc2..a5f70c156 100644 --- a/examples/agents/quickstart/05_claude_code.py +++ b/examples/agents/quickstart/05_claude_code.py @@ -20,7 +20,7 @@ # 1. Deploy once during CI/CD: # rt.deploy(agent) # CLI alternative: - # agentspan deploy --package examples.quickstart.05_claude_code + # runtime.deploy(agent) from a release script # # 2. In a separate long-lived worker process: # rt.serve(agent) diff --git a/examples/agents/quickstart/run_all.py b/examples/agents/quickstart/run_all.py index ecf15ec52..208125b17 100644 --- a/examples/agents/quickstart/run_all.py +++ b/examples/agents/quickstart/run_all.py @@ -47,7 +47,7 @@ def _load_module(filename: str): # ── Config ────────────────────────────────────────────── TIMEOUT_SECONDS = 30 -SERVER_URL = os.environ.get("AGENTSPAN_SERVER_URL", "http://localhost:8080/api") +SERVER_URL = os.environ.get("CONDUCTOR_SERVER_URL", "http://localhost:8080/api") @dataclass @@ -85,7 +85,7 @@ def fetch_execution(execution_id: str) -> dict: return resp.json() -# System task types managed by Conductor/AgentSpan — everything else is a tool worker task +# System task types managed by Conductor/Conductor — everything else is a tool worker task SYSTEM_TASK_TYPES = { "LLM_CHAT_COMPLETE", "SET_VARIABLE", "DO_WHILE", "SWITCH", "FORK", "JOIN", "INLINE", "SUB_WORKFLOW", "HUMAN", "TERMINATE", "WAIT", "EVENT", diff --git a/examples/agents/run_all_examples.py b/examples/agents/run_all_examples.py index 74bc84b0b..d7edd07f1 100644 --- a/examples/agents/run_all_examples.py +++ b/examples/agents/run_all_examples.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 # Copyright (c) 2025 Agentspan # Licensed under the MIT License. See LICENSE file in the project root for details. -"""Run every agent example against a live Agentspan server and report status. +"""Run every agent example against a live Conductor server and report status. "Works fine" means: * PASS — process exits 0 within the timeout (no compile/import/runtime error, @@ -24,8 +24,8 @@ [--only PATTERN] [--out report.html] Env (with sensible defaults for the local dev server on :6767): - CONDUCTOR_SERVER_URL / AGENTSPAN_SERVER_URL server API base - AGENTSPAN_LLM_MODEL model steered to a working provider + CONDUCTOR_SERVER_URL / CONDUCTOR_SERVER_URL server API base + CONDUCTOR_AGENT_LLM_MODEL model steered to a working provider """ from __future__ import annotations @@ -255,13 +255,13 @@ def main() -> int: ap.add_argument("--out", default=str(HERE / "run_report.html")) args = ap.parse_args() - server = os.environ.get("CONDUCTOR_SERVER_URL") or os.environ.get("AGENTSPAN_SERVER_URL") or DEFAULT_SERVER - model = os.environ.get("AGENTSPAN_LLM_MODEL", DEFAULT_MODEL) + server = os.environ.get("CONDUCTOR_SERVER_URL") or os.environ.get("CONDUCTOR_SERVER_URL") or DEFAULT_SERVER + model = os.environ.get("CONDUCTOR_AGENT_LLM_MODEL", DEFAULT_MODEL) env = dict(os.environ) env["CONDUCTOR_SERVER_URL"] = server - env["AGENTSPAN_SERVER_URL"] = server - env["AGENTSPAN_LLM_MODEL"] = model + env["CONDUCTOR_SERVER_URL"] = server + env["CONDUCTOR_AGENT_LLM_MODEL"] = model env["PYTHONPATH"] = os.pathsep.join([str(REPO / "src"), str(HERE), env.get("PYTHONPATH", "")]) env["PYTHONUNBUFFERED"] = "1" diff --git a/examples/agents/settings.py b/examples/agents/settings.py index 2f03abe0b..7e4142741 100644 --- a/examples/agents/settings.py +++ b/examples/agents/settings.py @@ -3,15 +3,15 @@ """Shared settings for all examples. -Set ``AGENTSPAN_LLM_MODEL`` as an environment variable to override the +Set ``CONDUCTOR_AGENT_LLM_MODEL`` as an environment variable to override the default model used by all examples:: - export AGENTSPAN_LLM_MODEL=anthropic/claude-sonnet-4-20250514 - export AGENTSPAN_LLM_MODEL=google_gemini/gemini-2.0-flash + export CONDUCTOR_AGENT_LLM_MODEL=anthropic/claude-sonnet-4-20250514 + export CONDUCTOR_AGENT_LLM_MODEL=google_gemini/gemini-2.0-flash If unset, defaults to ``anthropic/claude-sonnet-4-6``. -``AGENTSPAN_SECONDARY_LLM_MODEL`` provides a second model for multi-model examples +``CONDUCTOR_AGENT_SECONDARY_LLM_MODEL`` provides a second model for multi-model examples (e.g., cheap triage vs capable specialist). Defaults to ``openai/gpt-4o``. """ @@ -27,8 +27,14 @@ class Settings: @classmethod def from_env(cls) -> "Settings": return cls( - llm_model=os.environ.get("AGENTSPAN_LLM_MODEL", "anthropic/claude-sonnet-4-6"), - secondary_llm_model=os.environ.get("AGENTSPAN_SECONDARY_LLM_MODEL", "openai/gpt-4o"), + llm_model=( + os.environ.get("CONDUCTOR_AGENT_LLM_MODEL") + or "openai/gpt-4o" + ), + secondary_llm_model=( + os.environ.get("CONDUCTOR_AGENT_SECONDARY_LLM_MODEL") + or "openai/gpt-4o" + ), ) diff --git a/examples/agents/testing_multi_agent_correctness.py b/examples/agents/testing_multi_agent_correctness.py index 75d8f2ba3..578923523 100644 --- a/examples/agents/testing_multi_agent_correctness.py +++ b/examples/agents/testing_multi_agent_correctness.py @@ -7,7 +7,7 @@ ================================ This file demonstrates how to write correctness tests for every multi-agent -strategy in Agentspan. Each section shows: +strategy in Conductor. Each section shows: 1. How the agent is defined 2. What "correct behavior" means for that strategy @@ -17,7 +17,7 @@ Run the mock tests with: pytest examples/testing_multi_agent_correctness.py -v -The live tests require a running Agentspan server and are marked with +The live tests require a running Conductor server and are marked with @pytest.mark.integration. """ @@ -1032,7 +1032,7 @@ def test_output_guardrail_catches_bad_response(self): # 10. LIVE TESTS (require running server) # ═══════════════════════════════════════════════════════════════════════ # -# These tests run against a real Agentspan server with real LLM calls. +# These tests run against a real Conductor server with real LLM calls. # The assertions are more tolerant — checking behavior patterns rather # than exact strings. # ═══════════════════════════════════════════════════════════════════════ @@ -1408,7 +1408,7 @@ class TestEvalRunnerLive: @pytest.fixture def runtime(self): """Skip if no server available.""" - pytest.skip("Requires running Agentspan server") + pytest.skip("Requires running Conductor server") def test_handoff_eval(self, runtime): """Run eval suite for handoff correctness.""" @@ -1475,7 +1475,7 @@ class TestLiveMultiAgent: @pytest.fixture def runtime(self): """Skip if no server available.""" - pytest.skip("Requires running Agentspan server") + pytest.skip("Requires running Conductor server") # from conductor.ai.agents import AgentRuntime # rt = AgentRuntime() # yield rt diff --git a/pyproject.toml b/pyproject.toml index 3c4dca238..625393618 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -78,9 +78,10 @@ pytest-asyncio = ">=0.21" pytest-xdist = ">=3.0" pytest-rerunfailures = ">=14.0" mypy = ">=1.10" +jsonschema = ">=4.23" [tool.poetry.plugins."pytest11"] -agentspan-testing = "conductor.ai.agents.testing.pytest_plugin" +conductor-agents-testing = "conductor.ai.agents.testing.pytest_plugin" [tool.ruff] target-version = "py310" diff --git a/src/conductor/ai/agents/__init__.py b/src/conductor/ai/agents/__init__.py index 6bdeda041..f9aef7487 100644 --- a/src/conductor/ai/agents/__init__.py +++ b/src/conductor/ai/agents/__init__.py @@ -1,7 +1,7 @@ # Copyright (c) 2025 Agentspan # Licensed under the MIT License. See LICENSE file in the project root for details. -"""Agentspan Agents SDK — durable, scalable, observable AI agents. +"""Conductor Agent Python SDK — durable, scalable, observable AI agents. This is the public API surface. Import everything you need from here:: @@ -55,7 +55,12 @@ def get_weather(city: str) -> str: ) # Exceptions -from conductor.ai.agents.exceptions import AgentAPIError, AgentNotFoundError, AgentspanError +from conductor.ai.agents.exceptions import ( + AgentAPIError, + AgentNotFoundError, + ConductorAgentError, + AgentspanError, +) # Extended agent types from conductor.ai.agents.ext import GPTAssistantAgent @@ -344,6 +349,7 @@ def resolve_credentials(task: object, names: list) -> dict: "OnTextMention", "OnCondition", # Exceptions + "ConductorAgentError", "AgentspanError", "AgentAPIError", "AgentNotFoundError", diff --git a/src/conductor/ai/agents/_internal/agent_http.py b/src/conductor/ai/agents/_internal/agent_http.py index 2c0e06c7e..0b67ea6af 100644 --- a/src/conductor/ai/agents/_internal/agent_http.py +++ b/src/conductor/ai/agents/_internal/agent_http.py @@ -1,7 +1,7 @@ # Copyright (c) 2025 Agentspan # Licensed under the MIT License. See LICENSE file in the project root for details. -"""Authenticated HTTP to Agentspan ``/agent/*`` endpoints via the SDK ``ApiClient``. +"""Authenticated HTTP to Conductor-agent ``/agent/*`` endpoints via the SDK ``ApiClient``. Framework workers (claude_agent_sdk, langchain, langgraph) run in spawned worker processes and receive ``(server_url, auth_key, auth_secret)`` as plain strings — a @@ -72,7 +72,7 @@ def agent_post( *, read_response: bool = False, ) -> Optional[Any]: - """POST to an Agentspan ``/agent/*`` endpoint through the SDK ``ApiClient``. + """POST to a Conductor-agent ``/agent/*`` endpoint through the SDK ``ApiClient``. ``path`` is relative to the server host (which already ends in ``/api``), so it must start with ``/`` and omit ``/api`` — e.g. ``/agent/events/{id}``. diff --git a/src/conductor/ai/agents/exceptions.py b/src/conductor/ai/agents/exceptions.py index 05a55f502..a27cf64de 100644 --- a/src/conductor/ai/agents/exceptions.py +++ b/src/conductor/ai/agents/exceptions.py @@ -13,6 +13,7 @@ from conductor.client.ai.agent_errors import ( # noqa: F401 AgentAPIError, AgentNotFoundError, + ConductorAgentError, AgentspanError, _raise_api_error, ) diff --git a/src/conductor/ai/agents/frameworks/claude_agent_sdk.py b/src/conductor/ai/agents/frameworks/claude_agent_sdk.py index 6ae969186..75fc9b6f9 100644 --- a/src/conductor/ai/agents/frameworks/claude_agent_sdk.py +++ b/src/conductor/ai/agents/frameworks/claude_agent_sdk.py @@ -1,4 +1,4 @@ -# sdk/python/src/agentspan/agents/frameworks/claude_agent_sdk.py +# sdk/python/src/conductor/ai/agents/frameworks/claude_agent_sdk.py # Copyright (c) 2025 Agentspan # Licensed under the MIT License. See LICENSE file in the project root for details. @@ -376,7 +376,7 @@ async def _run_query(prompt: str, options: Any) -> Tuple[str, Any]: # --------------------------------------------------------------------------- -# Agentspan hooks +# Conductor-agent hooks # --------------------------------------------------------------------------- @@ -388,7 +388,7 @@ def _build_agentspan_hooks( auth_secret: str, metadata: Dict[str, Any], ) -> Dict[str, list]: - """Build agentspan instrumentation hooks for the Claude Agent SDK. + """Build Conductor-agent instrumentation hooks for the Claude Agent SDK. Returns a dict mapping event names to lists of HookMatcher dataclasses. All hook callbacks are defensive (try/except, return {}). @@ -851,7 +851,7 @@ async def _stop(input_data: dict, tool_use_id: str | None, context: Any) -> dict def _merge_hooks(options: Any, agentspan_hooks: Dict[str, list]) -> Any: - """Merge user hooks and agentspan hooks, preserving user hooks first. + """Merge user hooks and Conductor-agent hooks, preserving user hooks first. Returns a new options object with the merged hooks dict. """ @@ -968,7 +968,7 @@ def _create_tracking_workflow( """Create a bare tracking workflow for a subagent. Returns the execution ID of the new workflow, or None on failure. - Uses POST /api/agent/execution (Agentspan custom endpoint). + Uses POST /api/agent/execution (Conductor agent endpoint). """ body: Dict[str, Any] = {"workflowName": workflow_name, "input": input_data} if parent_workflow_id: @@ -995,7 +995,7 @@ def _inject_tool_task( """Inject a display-only task into the running workflow execution. Called synchronously from PreToolUse so the task exists before the tool runs. - Uses POST /api/agent/{executionId}/tasks (Agentspan custom endpoint). + Uses POST /api/agent/{executionId}/tasks (Conductor agent endpoint). For SUB_WORKFLOW tasks, pass sub_workflow_param with keys: name, version, executionId (the tracking sub-workflow). diff --git a/src/conductor/ai/agents/frameworks/langchain.py b/src/conductor/ai/agents/frameworks/langchain.py index d4fba09ff..72cb3af68 100644 --- a/src/conductor/ai/agents/frameworks/langchain.py +++ b/src/conductor/ai/agents/frameworks/langchain.py @@ -1,4 +1,4 @@ -# sdk/python/src/agentspan/agents/frameworks/langchain.py +# sdk/python/src/conductor/ai/agents/frameworks/langchain.py # Copyright (c) 2025 Agentspan # Licensed under the MIT License. See LICENSE file in the project root for details. @@ -171,8 +171,8 @@ def _get_callback_handler_class() -> type: from langchain_core.callbacks import BaseCallbackHandler - class AgentspanCallbackHandler(BaseCallbackHandler): - """LangChain callback handler that pushes events to Agentspan SSE via HTTP. + class ConductorAgentCallbackHandler(BaseCallbackHandler): + """LangChain callback handler that pushes events to Conductor-agent SSE via HTTP. Must inherit from BaseCallbackHandler so LangChain's AgentExecutor recognises it as a valid callback. Plain classes are rejected at runtime. @@ -230,7 +230,7 @@ def on_tool_error(self, error, **kwargs): self._auth_secret, ) - _callback_handler_class = AgentspanCallbackHandler + _callback_handler_class = ConductorAgentCallbackHandler return _callback_handler_class diff --git a/src/conductor/ai/agents/frameworks/langgraph.py b/src/conductor/ai/agents/frameworks/langgraph.py index 872152c5f..985d4fc8f 100644 --- a/src/conductor/ai/agents/frameworks/langgraph.py +++ b/src/conductor/ai/agents/frameworks/langgraph.py @@ -1,4 +1,4 @@ -# sdk/python/src/agentspan/agents/frameworks/langgraph.py +# sdk/python/src/conductor/ai/agents/frameworks/langgraph.py # Copyright (c) 2025 Agentspan # Licensed under the MIT License. See LICENSE file in the project root for details. @@ -1666,7 +1666,7 @@ def _process_updates_chunk( auth_key: str, auth_secret: str, ) -> None: - """Map a LangGraph 'updates' chunk to Agentspan SSE events and push non-blocking.""" + """Map a LangGraph 'updates' chunk to Conductor-agent SSE events non-blockingly.""" for node_name, state_updates in chunk.items(): # Always emit a thinking event for each node execution _push_event_nonblocking( diff --git a/src/conductor/ai/agents/langchain.py b/src/conductor/ai/agents/langchain.py index 82dd7c1dc..0da145a3d 100644 --- a/src/conductor/ai/agents/langchain.py +++ b/src/conductor/ai/agents/langchain.py @@ -3,7 +3,7 @@ """User-facing create_agent wrapper for LangChain/LangGraph agents. -Import from here instead of langchain.agents so that Agentspan can +Import from here instead of langchain.agents so that Conductor can extract the LLM model, tools, and instructions for proper server-side orchestration (AI_MODEL + SIMPLE tasks — same as OpenAI/ADK). @@ -39,10 +39,10 @@ def create_agent( system_prompt: Optional[Union[str, Any]] = None, **kwargs: Any, ) -> Any: - """Agentspan wrapper around ``langchain.agents.create_agent``. + """Conductor wrapper around ``langchain.agents.create_agent``. Captures the LLM, tools, and instructions *before* compilation so that - Agentspan can translate the agent into a proper server-side workflow + Conductor can translate the agent into a proper server-side workflow (AI_MODEL task for the LLM + SIMPLE tasks for each tool), matching the OpenAI/ADK integration pattern. @@ -56,7 +56,7 @@ def create_agent( Returns: A ``CompiledStateGraph`` with ``._agentspan_meta`` attached for - proper Agentspan serialization. + proper Conductor serialization. """ from langchain.agents import create_agent as _lc_create_agent # type: ignore[import] diff --git a/src/conductor/ai/agents/openai_compat.py b/src/conductor/ai/agents/openai_compat.py index 2ef19129d..2ab504f5a 100644 --- a/src/conductor/ai/agents/openai_compat.py +++ b/src/conductor/ai/agents/openai_compat.py @@ -11,8 +11,8 @@ # After from conductor.ai import Runner -Your agents now run on Agentspan instead of directly against OpenAI. -Agentspan adds durability, observability, human-in-the-loop, and horizontal +Your agents now run on Conductor instead of directly against OpenAI. +Conductor adds durability, observability, human-in-the-loop, and horizontal scaling — no other code changes needed. Compatible with openai-agents-python ``Agent``, ``@function_tool``, and @@ -76,7 +76,7 @@ def final_output(self) -> Any: @property def execution_id(self) -> str: - """The Agentspan execution ID for debugging.""" + """The Conductor execution ID for debugging.""" return self._agent_result.execution_id def __repr__(self) -> str: @@ -92,7 +92,7 @@ def _model_to_agentspan(model: Any) -> str: ``"gpt-4o"`` → ``"openai/gpt-4o"`` ``"claude-opus-4-6"``→ ``"anthropic/claude-opus-4-6"`` ``"openai/gpt-4o"`` → ``"openai/gpt-4o"`` (already qualified) - ``None`` → ``""`` (Agentspan uses AGENTSPAN_LLM_MODEL env var) + ``None`` → ``""`` (the default model comes from configuration) """ if not model: return "" @@ -136,7 +136,7 @@ class _CtxStub: openai-agents' FunctionTool.on_invoke_tool(ctx, json_str) accesses ctx attributes for tracing/span creation. When executing inside an - Agentspan worker (outside of an openai-agents Runner loop) there is + Conductor worker (outside of an openai-agents Runner loop) there is no real RunContextWrapper, so we supply a stub that silently returns None for any attribute access rather than raising AttributeError. """ @@ -149,14 +149,14 @@ def __getattr__(self, name: str) -> Any: def _convert_function_tool(ft: Any) -> Any: - """Convert an openai-agents ``FunctionTool`` to an Agentspan ``ToolDef``. + """Convert an openai-agents ``FunctionTool`` to a Conductor ``ToolDef``. Args: ft: Object with ``.name``, ``.description``, ``.params_json_schema``, and ``.on_invoke_tool(ctx, input_json_str)`` attributes. Returns: - An Agentspan :class:`~conductor.ai.agents.tool.ToolDef`. + A Conductor :class:`~conductor.ai.agents.tool.ToolDef`. """ from conductor.ai.agents.tool import ToolDef @@ -185,9 +185,9 @@ def _sync_wrapper(**kwargs: Any) -> Any: def _to_agentspan_agent(agent: Any) -> Any: - """Convert an openai-agents ``Agent`` to an Agentspan ``Agent``. + """Convert an openai-agents ``Agent`` to a Conductor ``Agent``. - If *agent* is already an Agentspan ``Agent`` it is returned unchanged. + If *agent* is already a Conductor ``Agent`` it is returned unchanged. Duck-typed: any object with ``name``, ``instructions``, ``model``, and ``tools`` attributes is accepted. """ @@ -212,7 +212,11 @@ def _to_agentspan_agent(agent: Any) -> Any: model: str = ( _model_to_agentspan(_raw_model) if _raw_model - else (os.environ.get("AGENTSPAN_LLM_MODEL", "openai/gpt-4o")) + else ( + os.environ.get("CONDUCTOR_AGENT_LLM_MODEL") + or os.environ.get("AGENTSPAN_LLM_MODEL") + or "openai/gpt-4o" + ) ) raw_tools: list = getattr(agent, "tools", []) or [] @@ -225,7 +229,7 @@ def _to_agentspan_agent(agent: Any) -> Any: else: logger.warning( "Skipping unrecognised tool type '%s' — " - "wrap it with Agentspan's @tool decorator to include it.", + "wrap it with Conductor's @tool decorator to include it.", type(t).__name__, ) @@ -241,18 +245,18 @@ def _to_agentspan_agent(agent: Any) -> Any: def _run_agent(starting_agent: Any, max_turns: int) -> Any: - """Resolve the agent to pass to the Agentspan runtime. + """Resolve the agent to pass to the Conductor runtime. Foreign framework agents (openai-agents, google-adk, …) are passed through unchanged — the runtime's :func:`detect_framework` handles - serialisation and tool registration. Native Agentspan Agents are also + serialization and tool registration. Native Conductor Agents are also passed through unchanged (with optional ``max_turns`` override). Only truly unknown objects fall back to :func:`_to_agentspan_agent`. """ - from conductor.ai.agents.agent import Agent as AgentspanAgent + from conductor.ai.agents.agent import Agent as ConductorAgent from conductor.ai.agents.frameworks.serializer import detect_framework - if isinstance(starting_agent, AgentspanAgent): + if isinstance(starting_agent, ConductorAgent): if max_turns != 10: starting_agent.max_turns = max_turns return starting_agent @@ -290,7 +294,7 @@ class Runner: ``await Runner.run(agent, input)`` Async execution. Returns a :class:`RunResult`. ``Runner.run_streamed(agent, input)`` - Streaming execution. Returns an Agentspan :class:`AsyncAgentStream`. + Streaming execution. Returns a Conductor :class:`AsyncAgentStream`. """ @classmethod @@ -308,7 +312,7 @@ def run_sync( Drop-in for ``Runner.run_sync(agent, input)``. Args: - starting_agent: An openai-agents ``Agent`` or Agentspan ``Agent``. + starting_agent: An openai-agents ``Agent`` or Conductor ``Agent``. input: The user's input message. context: Ignored — present only for openai-agents API compatibility. max_turns: Maximum agent loop iterations. @@ -338,7 +342,7 @@ async def run( Drop-in for ``await Runner.run(agent, input)``. Args: - starting_agent: An openai-agents ``Agent`` or Agentspan ``Agent``. + starting_agent: An openai-agents ``Agent`` or Conductor ``Agent``. input: The user's input message. context: Ignored — present only for openai-agents API compatibility. max_turns: Maximum agent loop iterations. @@ -367,15 +371,15 @@ async def run_streamed( Drop-in for ``Runner.run_streamed(agent, input)``. - Returns an Agentspan :class:`~conductor.ai.agents.result.AsyncAgentStream` + Returns a Conductor :class:`~conductor.ai.agents.result.AsyncAgentStream` which supports ``async for event in stream`` iteration. - Note: Agentspan event types (``"tool_call"``, ``"done"``, etc.) differ + Note: Conductor event types (``"tool_call"``, ``"done"``, etc.) differ from openai-agents ``StreamEvent`` types. For full streaming event compatibility, iterate and map events as needed. Args: - starting_agent: An openai-agents ``Agent`` or Agentspan ``Agent``. + starting_agent: An openai-agents ``Agent`` or Conductor ``Agent``. input: The user's input message. context: Ignored — present only for openai-agents API compatibility. max_turns: Maximum agent loop iterations. diff --git a/src/conductor/ai/agents/runtime/_dispatch.py b/src/conductor/ai/agents/runtime/_dispatch.py index 988c77e64..72068bb02 100644 --- a/src/conductor/ai/agents/runtime/_dispatch.py +++ b/src/conductor/ai/agents/runtime/_dispatch.py @@ -159,7 +159,7 @@ def _coerce_value(value, annotation): def _resolve_secrets_from_task(task, names: list) -> dict: """Resolve declared secret *names* from the values the host delivered on the task. - Secured hosts (e.g. orkes/agentspan) resolve a worker's declared + Secured Conductor hosts resolve a worker's declared ``TaskDef.runtimeMetadata`` secret names at poll time and deliver the values on the wire-only ``Task.runtimeMetadata`` map (conductor-oss PR #1255) — never persisted to task input. The worker just reads them here; there is no separate @@ -179,7 +179,7 @@ def _resolve_secrets_from_task(task, names: list) -> dict: raise CredentialNotFoundError( missing, "Not delivered by the server on this task. Ensure each secret is stored " - "(agentspan credentials set --name <NAME>) and declared on the tool/agent " + "in the Conductor credential store and declared on the tool/agent " "so the server resolves it at poll time.", ) return resolved diff --git a/src/conductor/ai/agents/runtime/_liveness.py b/src/conductor/ai/agents/runtime/_liveness.py index c36271fce..1a8f4d53d 100644 --- a/src/conductor/ai/agents/runtime/_liveness.py +++ b/src/conductor/ai/agents/runtime/_liveness.py @@ -155,7 +155,7 @@ def verify( "The worker subprocess(es) are not running. This usually means " "fork() failed or an exception was swallowed during " "WorkerManager.start(). Check process logs and retry start(). " - "Set AGENTSPAN_LIVENESS_ENABLED=false to disable this check." + "Set CONDUCTOR_AGENT_LIVENESS_ENABLED=false to disable this check." ), ) @@ -267,7 +267,7 @@ def _tick(self) -> bool: "No worker is polling for these tasks. If the original " "process died, re-run with the same idempotency_key (or " "call runtime.resume(execution_id, agent)) to re-attach " - "workers. Set AGENTSPAN_LIVENESS_ENABLED=false to disable." + "workers. Set CONDUCTOR_AGENT_LIVENESS_ENABLED=false to disable." ), ) try: diff --git a/src/conductor/ai/agents/runtime/_worker_entries.py b/src/conductor/ai/agents/runtime/_worker_entries.py index 9373f13f2..f37f43b69 100644 --- a/src/conductor/ai/agents/runtime/_worker_entries.py +++ b/src/conductor/ai/agents/runtime/_worker_entries.py @@ -23,7 +23,7 @@ - :func:`probe_spawn_safety` — a registration-time pickle probe, enabled per worker group as each group's workers become spawn-safe. -Design: idea-5 ``design.md`` in the combine-agentspan analysis workspace. +Design: idea-5 ``design.md`` in the agent-runtime analysis workspace. NOTE: deliberately NO ``from __future__ import annotations`` here — the task runners read parameter types from ``inspect.signature(execute_function)`` diff --git a/src/conductor/ai/agents/runtime/config.py b/src/conductor/ai/agents/runtime/config.py index 9a52b00b7..e1f6b5a8a 100644 --- a/src/conductor/ai/agents/runtime/config.py +++ b/src/conductor/ai/agents/runtime/config.py @@ -20,36 +20,59 @@ from typing import Optional -def _env(var: str, default=None): - """Read an environment variable, returning *default* if unset.""" - return os.environ.get(var, default) +def _env(var: str, default=None, *, legacy_var: Optional[str] = None): + """Read a canonical environment variable with an optional legacy fallback. + + Blank values are treated as unset. The SDK documents only the canonical + ``CONDUCTOR_AGENT_*`` names; ``AGENTSPAN_*`` remains a compatibility path + for applications upgrading in place. + """ + value = os.environ.get(var) + if value is not None and value.strip() != "": + return value + if legacy_var: + legacy_value = os.environ.get(legacy_var) + if legacy_value is not None and legacy_value.strip() != "": + return legacy_value + return default logger = logging.getLogger("conductor.ai.agents.config") -def _env_bool(var: str, default: bool = False) -> bool: - """Read a boolean environment variable (true/1/yes → True).""" - val = os.environ.get(var) - if val is None or val.strip() == "": +def _env_bool(var: str, default: bool = False, *, legacy_var: Optional[str] = None) -> bool: + """Read a boolean environment variable; malformed values use *default*.""" + val = _env(var, legacy_var=legacy_var) + if val is None: return default - return val.lower() in ("true", "1", "yes") - - -def _env_int(var: str, default: int = 0) -> int: - """Read an integer environment variable.""" - val = os.environ.get(var) - if val is None or val.strip() == "": + normalized = val.lower() + if normalized in ("true", "1", "yes", "on"): + return True + if normalized in ("false", "0", "no", "off"): + return False + return default + + +def _env_int(var: str, default: int = 0, *, legacy_var: Optional[str] = None) -> int: + """Read an integer environment variable; malformed values use *default*.""" + val = _env(var, legacy_var=legacy_var) + if val is None: + return default + try: + return int(val) + except ValueError: return default - return int(val) -def _env_float(var: str, default: float = 0.0) -> float: - """Read a float environment variable.""" - val = os.environ.get(var) - if val is None or val.strip() == "": +def _env_float(var: str, default: float = 0.0, *, legacy_var: Optional[str] = None) -> float: + """Read a float environment variable; malformed values use *default*.""" + val = _env(var, legacy_var=legacy_var) + if val is None: + return default + try: + return float(val) + except ValueError: return default - return float(val) @dataclass @@ -86,17 +109,46 @@ class AgentConfig: @classmethod def from_env(cls) -> AgentConfig: - """Create an ``AgentConfig`` by reading ``AGENTSPAN_*`` env vars.""" + """Create an ``AgentConfig`` from canonical environment variables. + + ``CONDUCTOR_AGENT_*`` wins over the corresponding legacy setting when + both are supplied. + """ return cls( - worker_poll_interval_ms=_env_int("AGENTSPAN_WORKER_POLL_INTERVAL", 100), - worker_thread_count=_env_int("AGENTSPAN_WORKER_THREADS", 1), - auto_start_workers=_env_bool("AGENTSPAN_AUTO_START_WORKERS", True), - daemon_workers=_env_bool("AGENTSPAN_DAEMON_WORKERS", True), - auto_register_integrations=_env_bool("AGENTSPAN_INTEGRATIONS_AUTO_REGISTER", False), - streaming_enabled=_env_bool("AGENTSPAN_STREAMING_ENABLED", True), - liveness_enabled=_env_bool("AGENTSPAN_LIVENESS_ENABLED", True), - liveness_stall_seconds=_env_float("AGENTSPAN_LIVENESS_STALL_SECONDS", 30.0), + worker_poll_interval_ms=_env_int( + "CONDUCTOR_AGENT_WORKER_POLL_INTERVAL", 100, + legacy_var="AGENTSPAN_WORKER_POLL_INTERVAL", + ), + worker_thread_count=_env_int( + "CONDUCTOR_AGENT_WORKER_THREADS", 1, + legacy_var="AGENTSPAN_WORKER_THREADS", + ), + auto_start_workers=_env_bool( + "CONDUCTOR_AGENT_AUTO_START_WORKERS", True, + legacy_var="AGENTSPAN_AUTO_START_WORKERS", + ), + daemon_workers=_env_bool( + "CONDUCTOR_AGENT_DAEMON_WORKERS", True, + legacy_var="AGENTSPAN_DAEMON_WORKERS", + ), + auto_register_integrations=_env_bool( + "CONDUCTOR_AGENT_INTEGRATIONS_AUTO_REGISTER", False, + legacy_var="AGENTSPAN_INTEGRATIONS_AUTO_REGISTER", + ), + streaming_enabled=_env_bool( + "CONDUCTOR_AGENT_STREAMING_ENABLED", True, + legacy_var="AGENTSPAN_STREAMING_ENABLED", + ), + liveness_enabled=_env_bool( + "CONDUCTOR_AGENT_LIVENESS_ENABLED", True, + legacy_var="AGENTSPAN_LIVENESS_ENABLED", + ), + liveness_stall_seconds=_env_float( + "CONDUCTOR_AGENT_LIVENESS_STALL_SECONDS", 30.0, + legacy_var="AGENTSPAN_LIVENESS_STALL_SECONDS", + ), liveness_check_interval_seconds=_env_float( - "AGENTSPAN_LIVENESS_CHECK_INTERVAL_SECONDS", 10.0 + "CONDUCTOR_AGENT_LIVENESS_CHECK_INTERVAL_SECONDS", 10.0, + legacy_var="AGENTSPAN_LIVENESS_CHECK_INTERVAL_SECONDS", ), ) diff --git a/src/conductor/ai/agents/runtime/credentials/__init__.py b/src/conductor/ai/agents/runtime/credentials/__init__.py index ab69580a1..53aa49a42 100644 --- a/src/conductor/ai/agents/runtime/credentials/__init__.py +++ b/src/conductor/ai/agents/runtime/credentials/__init__.py @@ -1,7 +1,7 @@ # Copyright (c) 2025 Agentspan # Licensed under the MIT License. See LICENSE file in the project root for details. -"""Credential management subpackage for the Agentspan Python SDK.""" +"""Credential management for the Conductor Python SDK.""" from __future__ import annotations diff --git a/src/conductor/ai/agents/runtime/credentials/types.py b/src/conductor/ai/agents/runtime/credentials/types.py index 79d536e0f..10d3dc8f5 100644 --- a/src/conductor/ai/agents/runtime/credentials/types.py +++ b/src/conductor/ai/agents/runtime/credentials/types.py @@ -15,7 +15,7 @@ class CredentialNotFoundError(AgentspanError): Raised when a declared credential is not found in the credential store. There is no env var fallback for declared credentials — store them with - ``agentspan credentials set --name <NAME>``. + Configure the credential in the Conductor server's secret provider. """ def __init__(self, missing_names: List[str], detail: str = "") -> None: diff --git a/src/conductor/ai/agents/runtime/runtime.py b/src/conductor/ai/agents/runtime/runtime.py index 0c54363e2..8c0345267 100644 --- a/src/conductor/ai/agents/runtime/runtime.py +++ b/src/conductor/ai/agents/runtime/runtime.py @@ -183,7 +183,7 @@ def _resolve_loop_iteration(iteration: object) -> int: The compiler wires a worker's ``iteration`` input from the loop counter reference ``${<agent>_loop.iteration}``. Conductor cores disagree on whether that loop-output reference resolves for tasks executing *inside* the loop - body: the OSS core agentspan compiles against resolves it to the live integer, + body: the OSS core agent compiler resolves it to the live integer, but some embedding hosts' cores (e.g. orkes-conductor) leave it unresolved and deliver ``None`` — which then crashes ``iteration >= max_retries`` comparisons. @@ -575,8 +575,6 @@ def _start_via_server( payload["idempotencyKey"] = idempotency_key if timeout is not None: payload["timeoutSeconds"] = timeout - if credentials: - payload["credentials"] = credentials if run_id: payload["runId"] = run_id if static_plan is not None: @@ -639,8 +637,6 @@ async def _start_via_server_async( payload["idempotencyKey"] = idempotency_key if timeout is not None: payload["timeoutSeconds"] = timeout - if credentials: - payload["credentials"] = credentials if run_id: payload["runId"] = run_id @@ -682,8 +678,6 @@ async def _start_framework_via_server_async( } if idempotency_key: payload["idempotencyKey"] = idempotency_key - if credentials: - payload["credentials"] = credentials data = await self._agent_client.start_agent_async(payload) execution_id = data.get("executionId", "") @@ -2988,8 +2982,6 @@ def _start_framework_via_server( } if idempotency_key: payload["idempotencyKey"] = idempotency_key - if credentials: - payload["credentials"] = credentials data = self._agent_client.start_agent(payload) execution_id = data.get("executionId", "") diff --git a/src/conductor/ai/agents/skill.py b/src/conductor/ai/agents/skill.py index 74426f537..029ba2369 100644 --- a/src/conductor/ai/agents/skill.py +++ b/src/conductor/ai/agents/skill.py @@ -145,7 +145,7 @@ def skill( search_path: Optional[List[str]] = None, params: Optional[Dict[str, Any]] = None, ) -> Agent: - """Load an Agent Skills directory as an Agentspan Agent. + """Load an Agent Skills directory as a Conductor agent. Args: path: Path to skill directory containing SKILL.md. diff --git a/src/conductor/ai/agents/testing/eval_runner.py b/src/conductor/ai/agents/testing/eval_runner.py index c30a4d6b6..222b753df 100644 --- a/src/conductor/ai/agents/testing/eval_runner.py +++ b/src/conductor/ai/agents/testing/eval_runner.py @@ -187,7 +187,7 @@ def failed_cases(self) -> List[EvalCaseResult]: class CorrectnessEval: - """Runs eval cases against a live Agentspan runtime. + """Runs eval cases against a live Conductor-agent runtime. Args: runtime: An :class:`AgentRuntime` instance (or any object with a diff --git a/src/conductor/ai/agents/testing/pytest_plugin.py b/src/conductor/ai/agents/testing/pytest_plugin.py index 01ab5a35f..159849dbd 100644 --- a/src/conductor/ai/agents/testing/pytest_plugin.py +++ b/src/conductor/ai/agents/testing/pytest_plugin.py @@ -4,7 +4,7 @@ """Pytest plugin — fixtures and markers for agent correctness testing. Registered automatically via the ``pytest11`` entry point when -``agentspan`` is installed. +the optional Conductor-agent dependencies are installed. """ from __future__ import annotations diff --git a/src/conductor/ai/agents/testing/semantic.py b/src/conductor/ai/agents/testing/semantic.py index f2dfe6a69..dd9c1f324 100644 --- a/src/conductor/ai/agents/testing/semantic.py +++ b/src/conductor/ai/agents/testing/semantic.py @@ -5,7 +5,7 @@ Requires ``litellm`` (optional dependency). Install with:: - pip install agentspan[testing] + pip install conductor-python[agents] Usage:: @@ -52,7 +52,7 @@ def assert_output_satisfies( except ImportError: raise ImportError( "litellm is required for semantic assertions.\n" - "Install it with: pip install agentspan[testing]" + "Install it with: pip install conductor-python[agents]" ) output = str(result.output) if result.output is not None else "" diff --git a/src/conductor/client/ai/__init__.py b/src/conductor/client/ai/__init__.py index b917b4b8b..c6d124576 100644 --- a/src/conductor/client/ai/__init__.py +++ b/src/conductor/client/ai/__init__.py @@ -19,6 +19,7 @@ from conductor.client.ai.agent_errors import ( AgentAPIError, AgentNotFoundError, + ConductorAgentError, AgentspanError, ) from conductor.client.ai.schedule import Schedule, ScheduleInfo @@ -33,6 +34,7 @@ "AgentClient", "AgentAPIError", "AgentNotFoundError", + "ConductorAgentError", "AgentspanError", "InvalidCronExpression", "Schedule", diff --git a/src/conductor/client/ai/agent_errors.py b/src/conductor/client/ai/agent_errors.py index 9a2d43512..8284cc4c6 100644 --- a/src/conductor/client/ai/agent_errors.py +++ b/src/conductor/client/ai/agent_errors.py @@ -7,18 +7,23 @@ library-specific HTTP errors from ``requests`` or ``httpx``. This module is the canonical home of the hierarchy formerly defined in -``conductor.ai.agents.exceptions`` (which now re-exports these same objects, -so ``except AgentspanError`` catches errors raised by either layer). +``conductor.ai.agents.exceptions``. ``AgentspanError`` remains an alias for +backward compatibility; new code should catch ``ConductorAgentError``. """ from __future__ import annotations -class AgentspanError(Exception): +class ConductorAgentError(Exception): """Base exception for all agent SDK errors.""" -class AgentAPIError(AgentspanError): +# Kept as the identical class object so existing ``except`` clauses and +# ``isinstance`` checks continue to work during the naming migration. +AgentspanError = ConductorAgentError + + +class AgentAPIError(ConductorAgentError): """An HTTP error from the agent runtime API.""" def __init__(self, status_code: int, message: str, url: str = ""): diff --git a/tests/unit/ai/test_agent_error_alias.py b/tests/unit/ai/test_agent_error_alias.py new file mode 100644 index 000000000..e4249a8a9 --- /dev/null +++ b/tests/unit/ai/test_agent_error_alias.py @@ -0,0 +1,11 @@ +"""Public compatibility tests for the Conductor-agent error rename.""" + +from conductor.ai.agents import AgentspanError, ConductorAgentError +from conductor.client.ai import AgentspanError as ClientLegacyError +from conductor.client.ai import ConductorAgentError as ClientError + + +def test_legacy_error_is_the_canonical_error_class(): + assert ConductorAgentError is AgentspanError + assert ClientError is ClientLegacyError + assert ClientError is ConductorAgentError diff --git a/tests/unit/ai/test_agent_schema_contract.py b/tests/unit/ai/test_agent_schema_contract.py new file mode 100644 index 000000000..eecd1db33 --- /dev/null +++ b/tests/unit/ai/test_agent_schema_contract.py @@ -0,0 +1,51 @@ +"""Keep the documented agent wire contract aligned with the serializer.""" + +from __future__ import annotations + +import json +from pathlib import Path + +import jsonschema +import pytest + +from conductor.ai.agents.agent import Agent +from conductor.ai.agents.config_serializer import AgentConfigSerializer +from conductor.ai.agents.guardrail import RegexGuardrail +from conductor.ai.agents.termination import TextMentionTermination +from conductor.ai.agents.tool import tool + + +ROOT = Path(__file__).resolve().parents[3] +SCHEMA_PATH = ROOT / "docs" / "agents" / "reference" / "agent-schema.json" + + +@tool +def _schema_tool(query: str) -> dict: + """Return a deterministic result for schema validation.""" + return {"query": query} + + +def test_agent_schema_is_valid_and_accepts_representative_serializer_output(): + schema = json.loads(SCHEMA_PATH.read_text()) + jsonschema.Draft202012Validator.check_schema(schema) + agent = Agent( + name="schema_root", + model="openai/gpt-4o-mini", + instructions="Return a concise answer.", + tools=[_schema_tool], + agents=[Agent(name="schema_child", model="openai/gpt-4o-mini")], + strategy="handoff", + guardrails=[RegexGuardrail(name="safe", patterns=[".*"])], + termination=TextMentionTermination(text="DONE"), + metadata={"owner": "docs"}, + max_tokens=100, + temperature=0.1, + ) + payload = AgentConfigSerializer().serialize(agent) + jsonschema.validate(payload, schema) + + +def test_agent_schema_rejects_unknown_root_fields(): + schema = json.loads(SCHEMA_PATH.read_text()) + with pytest.raises(jsonschema.ValidationError): + jsonschema.validate({"name": "valid_name", "unknownField": True}, schema) diff --git a/tests/unit/ai/test_config_env.py b/tests/unit/ai/test_config_env.py index 864b3affd..2390f240b 100644 --- a/tests/unit/ai/test_config_env.py +++ b/tests/unit/ai/test_config_env.py @@ -80,6 +80,10 @@ def test_empty_string_uses_default(self): with mock.patch.dict(os.environ, {"NUM": ""}, clear=True): assert _env_int("NUM", 7) == 7 + def test_invalid_value_uses_default(self): + with mock.patch.dict(os.environ, {"NUM": "not-a-number"}, clear=True): + assert _env_int("NUM", 7) == 7 + class TestEnvFloat: """Tests for _env_float() helper.""" @@ -96,6 +100,10 @@ def test_empty_string_uses_default(self): with mock.patch.dict(os.environ, {"SECS": ""}, clear=True): assert _env_float("SECS", 30.0) == 30.0 + def test_invalid_value_uses_default(self): + with mock.patch.dict(os.environ, {"SECS": "not-a-number"}, clear=True): + assert _env_float("SECS", 30.0) == 30.0 + class TestAgentConfigFromEnv: """Tests for AgentConfig.from_env() — agent-runtime settings only.""" @@ -109,9 +117,9 @@ def test_defaults(self): def test_boolean_env_vars(self): env = { - "AGENTSPAN_DAEMON_WORKERS": "false", - "AGENTSPAN_INTEGRATIONS_AUTO_REGISTER": "true", - "AGENTSPAN_STREAMING_ENABLED": "no", + "CONDUCTOR_AGENT_DAEMON_WORKERS": "false", + "CONDUCTOR_AGENT_INTEGRATIONS_AUTO_REGISTER": "true", + "CONDUCTOR_AGENT_STREAMING_ENABLED": "no", } with mock.patch.dict(os.environ, env, clear=True): config = AgentConfig.from_env() @@ -121,8 +129,8 @@ def test_boolean_env_vars(self): def test_numeric_env_vars(self): env = { - "AGENTSPAN_WORKER_POLL_INTERVAL": "250", - "AGENTSPAN_WORKER_THREADS": "4", + "CONDUCTOR_AGENT_WORKER_POLL_INTERVAL": "250", + "CONDUCTOR_AGENT_WORKER_THREADS": "4", } with mock.patch.dict(os.environ, env, clear=True): config = AgentConfig.from_env() @@ -133,6 +141,23 @@ def test_direct_construction(self): config = AgentConfig(worker_thread_count=8) assert config.worker_thread_count == 8 + def test_legacy_values_remain_supported(self): + with mock.patch.dict( + os.environ, {"AGENTSPAN_WORKER_THREADS": "4"}, clear=True + ): + assert AgentConfig.from_env().worker_thread_count == 4 + + def test_canonical_value_wins_over_legacy(self): + with mock.patch.dict( + os.environ, + { + "CONDUCTOR_AGENT_WORKER_THREADS": "8", + "AGENTSPAN_WORKER_THREADS": "4", + }, + clear=True, + ): + assert AgentConfig.from_env().worker_thread_count == 8 + class TestLivenessConfig: """Liveness monitor settings (used by stateful runs).""" @@ -146,9 +171,9 @@ def test_defaults(self): def test_from_env(self): env = { - "AGENTSPAN_LIVENESS_ENABLED": "false", - "AGENTSPAN_LIVENESS_STALL_SECONDS": "45", - "AGENTSPAN_LIVENESS_CHECK_INTERVAL_SECONDS": "5", + "CONDUCTOR_AGENT_LIVENESS_ENABLED": "false", + "CONDUCTOR_AGENT_LIVENESS_STALL_SECONDS": "45", + "CONDUCTOR_AGENT_LIVENESS_CHECK_INTERVAL_SECONDS": "5", } with mock.patch.dict(os.environ, env, clear=True): config = AgentConfig.from_env() diff --git a/tests/unit/ai/test_runtime.py b/tests/unit/ai/test_runtime.py index b9f2d0385..1e0c792ff 100644 --- a/tests/unit/ai/test_runtime.py +++ b/tests/unit/ai/test_runtime.py @@ -1867,6 +1867,16 @@ def test_start_via_server_omits_idempotency_key_when_none(self, runtime): payload = runtime._agent_client.start_agent.call_args[0][0] assert "idempotencyKey" not in payload + def test_start_via_server_keeps_credentials_out_of_payload(self, runtime): + """Credential names configure workers locally and are not workflow input.""" + agent = Agent(name="test", model="openai/gpt-4o") + runtime._agent_client.start_agent = MagicMock(return_value={"executionId": "wf-1"}) + + runtime._start_via_server(agent, "hi", credentials=["OPENAI_API_KEY"]) + + payload = runtime._agent_client.start_agent.call_args[0][0] + assert "credentials" not in payload + class TestStartFrameworkViaServer: """Test _start_framework_via_server() sends correct framework payloads.""" @@ -1881,8 +1891,8 @@ def runtime(self): config = AgentConfig() return AgentRuntime(settings=config) - def test_start_framework_via_server_passes_credentials(self, runtime): - """Framework start payload includes request-level credentials.""" + def test_start_framework_via_server_keeps_credentials_out_of_payload(self, runtime): + """Credential names configure workers locally and are not workflow input.""" runtime._agent_client.start_agent = MagicMock(return_value={"executionId": "wf-fw-1"}) runtime._start_framework_via_server( framework="openai", @@ -1892,7 +1902,7 @@ def test_start_framework_via_server_passes_credentials(self, runtime): ) payload = runtime._agent_client.start_agent.call_args[0][0] - assert payload["credentials"] == ["OPENAI_API_KEY"] + assert "credentials" not in payload def test_start_framework_via_server_passes_context(self, runtime): """Framework start payload includes context.""" diff --git a/tests/unit/test_documentation_links.py b/tests/unit/test_documentation_links.py new file mode 100644 index 000000000..a41483ef8 --- /dev/null +++ b/tests/unit/test_documentation_links.py @@ -0,0 +1,71 @@ +"""Validate maintained documentation links and GitHub-style anchors.""" + +from __future__ import annotations + +import re +from pathlib import Path + + +ROOT = Path(__file__).resolve().parents[2] +LINK_RE = re.compile(r"(?<!!)\[[^]]*\]\(([^)]+)\)") +HEADING_RE = re.compile(r"^#{1,6}\s+(.+?)\s*#*\s*$", re.MULTILINE) + +CORE_GUIDES = ( + "api-map", "compatibility", "connection-authentication", "core-quickstart", + "debugging", "deployment-scaling", "documentation-parity", "documentation-standard", + "examples", "observability", "reliability", "schedules-events", "schema-client", + "security", "server-setup", "upgrading", "workers", "workflow-lifecycle", + "workflow-testing", "workflows", +) +CANONICAL_DOCS = [ + ROOT / "README.md", + ROOT / "docs" / "README.md", + *[ROOT / "docs" / f"{name}.md" for name in CORE_GUIDES], + *sorted((ROOT / "docs" / "agents").rglob("*.md")), + ROOT / "examples" / "agents" / "README.md", + ROOT / "examples" / "agents" / "adk" / "README.md", + ROOT / "examples" / "agents" / "langgraph" / "README.md", + ROOT / "examples" / "agents" / "openai" / "README.md", +] + + +def _slug(heading: str) -> str: + heading = re.sub(r"`", "", heading).lower() + heading = re.sub(r"[^\w\s-]", "", heading) + return re.sub(r"[-\s]+", "-", heading).strip("-") + + +def _anchors(document: Path) -> set[str]: + counts: dict[str, int] = {} + anchors: set[str] = set() + for heading in HEADING_RE.findall(document.read_text()): + slug = _slug(heading) + count = counts.get(slug, 0) + counts[slug] = count + 1 + anchors.add(slug if count == 0 else f"{slug}-{count}") + return anchors + + +def _links(document: Path): + for target in LINK_RE.findall(document.read_text()): + target = target.strip().strip("<>") + if not target or "://" in target or target.startswith(("mailto:", "#")): + yield target + continue + yield target + + +def test_curated_documentation_links_and_anchors_exist(): + failures = [] + for document in CANONICAL_DOCS: + assert document.exists(), document + for target in _links(document): + if not target or "://" in target or target.startswith("mailto:"): + continue + path_text, _, anchor = target.partition("#") + target_document = document if not path_text else (document.parent / path_text).resolve() + if not target_document.exists(): + failures.append(f"{document.relative_to(ROOT)} -> missing {path_text}") + elif anchor and target_document.suffix == ".md" and anchor not in _anchors(target_document): + failures.append(f"{document.relative_to(ROOT)} -> missing anchor #{anchor} in {path_text or document.name}") + assert not failures, "Broken documentation links:\n" + "\n".join(failures) diff --git a/tests/unit/test_documentation_quality.py b/tests/unit/test_documentation_quality.py new file mode 100644 index 000000000..10f5c0b04 --- /dev/null +++ b/tests/unit/test_documentation_quality.py @@ -0,0 +1,64 @@ +"""Guard the README shape and public Conductor-agent documentation terminology.""" + +from __future__ import annotations + +import re +from pathlib import Path + + +ROOT = Path(__file__).resolve().parents[2] +README_SECTIONS = [ + "Choose your path", + "Choose your Conductor server", + "Why Conductor?", + "Requirements and compatibility", + "Install the SDK", + "AI agent quickstart", + "Workflow and worker quickstart", + "Common tasks", + "Troubleshooting", + "Support and project policies", + "License", +] +PRIMARY_GUIDES = [ + "docs/agents/concepts/agents.md", + "docs/agents/concepts/tools.md", + "docs/agents/concepts/multi-agent.md", + "docs/agents/concepts/guardrails.md", + "docs/agents/concepts/deploy-serve-run.md", + "docs/agents/concepts/streaming-hitl.md", + "docs/agents/reference/runtime.md", + "docs/agents/reference/client.md", +] + + +def test_readme_mirrors_the_java_sdk_information_architecture(): + headings = re.findall(r"^## (.+)$", (ROOT / "README.md").read_text(), re.MULTILINE) + positions = [headings.index(section) for section in README_SECTIONS] + assert positions == sorted(positions) + readme = (ROOT / "README.md").read_text() + assert "conductor-oss/python-sdk" in readme + assert "CONDUCTOR_AGENT_LLM_MODEL" in readme + assert "AGENTSPAN" not in readme + + +def test_primary_agent_guides_include_navigation_and_outcome_sections(): + for relative_path in PRIMARY_GUIDES: + content = (ROOT / relative_path).read_text() + assert "## Prerequisites" in content, relative_path + assert "## Expected result" in content or "## Expected result and" in content, relative_path + assert "## Next steps" in content or "## Cleanup and next steps" in content or "## Expected result and next steps" in content, relative_path + + +def test_public_documentation_and_examples_do_not_use_legacy_branding(): + paths = [ROOT / "README.md", *[p for p in (ROOT / "docs").rglob("*.md") if "docs/design" not in str(p)]] + paths.extend((ROOT / "examples" / "agents").rglob("*.md")) + paths.extend((ROOT / "examples" / "agents").rglob("*.py")) + violations = [] + for path in paths: + for number, line in enumerate(path.read_text().splitlines(), start=1): + if "copyright" in line.lower(): + continue + if re.search(r"agentspan|agent span", line, re.IGNORECASE): + violations.append(f"{path.relative_to(ROOT)}:{number}: {line.strip()}") + assert not violations, "Legacy branding remains:\n" + "\n".join(violations) diff --git a/tests/unit/test_pytest_plugin_metadata.py b/tests/unit/test_pytest_plugin_metadata.py new file mode 100644 index 000000000..b4c1f71ad --- /dev/null +++ b/tests/unit/test_pytest_plugin_metadata.py @@ -0,0 +1,17 @@ +"""Ensure pytest auto-loads the Conductor-agent plugin exactly once.""" + +from __future__ import annotations + +import tomllib +from pathlib import Path + + +ROOT = Path(__file__).resolve().parents[2] + + +def test_agent_pytest_plugin_has_one_canonical_entry_point(): + pyproject = tomllib.loads((ROOT / "pyproject.toml").read_text()) + plugins = pyproject["tool"]["poetry"]["plugins"]["pytest11"] + assert plugins == { + "conductor-agents-testing": "conductor.ai.agents.testing.pytest_plugin" + }