Skip to main content

Agent Workflows (Python)

Python Temporal worker for agent execution using pure OpenAI Agents SDK with KivotRunner.

Architecture

Client → Go HTTP API → Python AgentWorkflow (Temporal) → Go Activities (Model/Tool) → Events (Kafka)
  • Python AgentWorkflow: Workflow using pure OpenAI Agents SDK
  • KivotRunner: Drop-in replacement for official Runner, routes to Go activities
  • Go Activities: All I/O operations (model calls, tool calls, event emission)
  • Cross-language: Python workflow calls Go activities via Temporal

Project Structure

openai-agents/
├── kivot-agents/              # KivotRunner + Plugin implementation
│   ├── __init__.py
│   ├── runner.py                 # Main runner logic
│   ├── plugin.py                 # OpenAI Agents Plugin (NEW!)
│   ├── overrides.py              # Runtime configuration context (NEW!)
│   ├── types.py                  # AgentResult type
│   ├── runner_test.py            # Unit tests (14 passing)
│   └── plugin_test.py            # Plugin tests (7 passing, NEW!)
├── examples/
│   ├── README.md                 # Examples guide
│   ├── plugin_example.py         # Example: Streaming Path
│   ├── result_only_example.py    # Example: Result Path
│   └── both_paths_example.py     # Example: Using both paths
├── docs/
│   └── streaming-vs-result.md    # Guide: Two data paths (NEW!)
├── worker.py                     # Temporal worker with plugin
├── requirements.txt
├── pyproject.toml                # Package config with pytest, mypy
├── TODO.md                       # Known issues and future improvements
└── README.md

Quick Start

1. Write Agent Workflow (Pure OpenAI SDK!)

from agents import Agent
from kivot-agents import KivotRunner
from temporalio import workflow

@workflow.defn
class MyAgent:
    @workflow.run
    async def run(self, prompt: str) -> str:
        # Pure OpenAI Agents SDK code!
        agent = Agent(
            name="Assistant",
            instructions="You are helpful.",
            model="gpt-4o-mini",
        )

        result = await KivotRunner.run(agent, input=prompt)
        return result.final_output

That's it! No Kivot-specific wrappers.

2. Development Setup

Prerequisites

  • Python 3.11+
  • Temporal server running (via docker-compose)
  • Go worker running (for activities)

Install Dependencies

cd openai-agents
python -m venv venv
source venv/bin/activate  # or `venv\Scripts\activate` on Windows
pip install -r requirements.txt

Run Example Worker

Each example includes its own worker mode:

# Option 1: Streaming example (interactive UI)
python examples/plugin_example.py worker

# Option 2: Result example (tests/API)
python examples/result_only_example.py worker

# Option 3: Both paths example (production)
python examples/both_paths_example.py worker

The worker will:

  1. Connect to Temporal at localhost:7233 (configurable via TEMPORAL_HOST)
  2. Listen on task queue agent-workflows
  3. Execute agent workflows started by Go HTTP API

3. Test Your Agent

Start a run:

curl -X POST http://localhost:8080/v1/runs/start \
  -H 'Content-Type: application/json' \
  -d '{
    "agent_id": "demo-agent",
    "input": "Hello!"
  }'

Stream events:

wscat -c "ws://localhost:8080/v1/runs/stream?run_id=<run_id>"

Check Temporal UI:

open http://localhost:8088

How KivotRunner Works

Developer Code (Pure OpenAI SDK)
    Agent(...) + KivotRunner.run(agent, input)
        ↓
KivotRunner (Kivot Integration)
    Agent loop: model call → tool calls → model call → completion
        ↓ ModelStepActivity (Go)
        ↓ ToolStepActivity (Go)
            ↓
        ┌───────────────────────────────┐
        │   TWO INDEPENDENT PATHS:      │
        ├───────────────────────────────┤
        │ PATH 1: Events → Kafka →      │
        │         WebSocket (Streaming) │
        │                               │
        │ PATH 2: Result → Workflow     │
        │         Return (Final Answer) │
        └───────────────────────────────┘

Inside workflow: Routes to Go activities for durable execution

Outside workflow: Falls back to official Runner (perfect for local testing)

Two Independent Data Paths

Kivot provides two ways to get data from agent workflows:

Path 1: Streaming (Kafka → WebSocket)

Purpose: Real-time events for interactive UX

# Stream events as they happen
async with session.ws_connect(ws_url) as ws:
    async for event in ws:
        if event["type"] == "model.delta":
            print(event["delta"]["content"], end="")  # Typing effect!

Use cases:

  • Web UI with typing effect
  • CLI with progress indicators
  • Real-time monitoring

Path 2: Result (Temporal Workflow)

Purpose: Final answer for programmatic access

# Get final result
result = await client.execute_workflow(
    SimpleAgent.run,
    "What is 2+2?",
    id="calc-123",
    task_queue="agent-workflows"
)
print(result)  # "The answer is 4"

Use cases:

  • Unit tests
  • API integrations
  • Batch processing

Both Paths Work Simultaneously!

# Start workflow (PATH 2)
handle = await client.start_workflow(Agent.run, question)

# Stream events (PATH 1) in background
asyncio.create_task(stream_events(handle.id))

# Await final result (PATH 2)
result = await handle.result()

📖 Full guide: docs/streaming-vs-result.md

Agent Loop Example

Simple Agent (No Tools)

result = await KivotRunner.run(agent, input="Hello")

# KivotRunner internally:
# 1. Call Go ModelStepActivity
# 2. Return final output

Agent with Tools

result = await KivotRunner.run(agent, input="Search logs for errors")

# KivotRunner agent loop:
# Iteration 1:
#   1. Call Go ModelStepActivity (analyze request)
#   2. Detect tool_calls in response
#   3. For each tool call:
#      - Call Go ToolStepActivity
#      - Add tool result to message history
#
# Iteration 2:
#   4. Call Go ModelStepActivity (with tool results)
#   5. Return final output (no more tool_calls)

Testing

Unit Tests

Run all tests:

cd openai-agents
pytest kivot-agents/ -v

Test results: 21 passed, 1 skipped

Runner Tests (runner_test.py)

pytest kivot-agents/runner_test.py -v

Coverage:

  • Message preparation (string, dict, messages format)
  • Tool formatting (dict, callable, to_dict method)
  • Agent loop (simple completion, tool calls, max iterations)

Results: 14 passed, 1 skipped

Plugin Tests (plugin_test.py)

pytest kivot-agents/plugin_test.py -v

Coverage:

  • Plugin initialization with defaults
  • Custom parameters configuration
  • Context manager (set_kivot_agent_overrides)
  • Nested contexts
  • Task queue and policy refs

Results: 7 passed

Run All Tests

# All tests
pytest kivot-agents/ -v

# Or just use pytest (configured in pyproject.toml)
pytest

Examples

We provide 3 comprehensive examples demonstrating different data access patterns:

Example Focus Use Case
plugin_example.py 🌊 Streaming Path Interactive UI with real-time events
result_only_example.py 📦 Result Path Tests, API, batch processing
both_paths_example.py 🔀 Both Paths Production APIs with streaming + storage

Quick Start: Plugin Example (Streaming)

File: examples/plugin_example.py

# Worker setup with plugin
plugin = OpenAIAgentsKivotPlugin(
    model_params=ActivityParameters(
        start_to_close_timeout=timedelta(seconds=90)
    )
)
client = await Client.connect("localhost:7233", plugins=[plugin])
worker = Worker(client, task_queue="agent-workflows", workflows=[...])

# Run and stream events
python examples/plugin_example.py worker   # Terminal 1
python examples/plugin_example.py client   # Terminal 2 - see streaming!

Quick Start: Result Only (Programmatic)

File: examples/result_only_example.py

# Simple await - no streaming complexity
result = await client.execute_workflow(
    Agent.run,
    "What is 2+2?",
    id="calc-123",
    task_queue="agent-workflows"
)
print(result)  # "The answer is 4"

# Run examples
python examples/result_only_example.py worker     # Terminal 1
python examples/result_only_example.py examples   # Terminal 2

Quick Start: Both Paths (Production)

File: examples/both_paths_example.py

# Start workflow
handle = await client.start_workflow(Agent.run, question)

# Stream events in background
asyncio.create_task(stream_events(handle.id))

# Await final result
result = await handle.result()

# Run demo
python examples/both_paths_example.py worker  # Terminal 1
python examples/both_paths_example.py demo    # Terminal 2

📖 Full examples guide: examples/README.md

Environment Variables

  • TEMPORAL_HOST: Temporal server address (default: localhost:7233)
  • TASK_QUEUE: Task queue name (default: openai-agents)

Implementation Status

✅ Phase 1-2: KivotRunner Implementation (Complete)

  • Package structure created (kivot-agents/)
  • KivotRunner class with agent loop
  • Helper functions for messages and tools
  • Type hints and error handling
  • Comprehensive unit tests (14 passing)
  • Falls back to official Runner outside workflow

✅ Phase 3: Examples & Documentation (Complete)

  • Simple agent example (examples/simple_agent.py)
  • Ops agent with tools (examples/ops_agent.py)
  • Documentation updated (docs/AGENT_WORKFLOWS.md)
  • Migration guide from old approach
  • Worker updated to use new examples
  • Legacy workflows removed

✅ Phase 4: Plugin Integration (Complete)

  • OpenAIAgentsKivotPlugin class (plugin.py)
  • Runtime configuration context (overrides.py)
  • Plugin unit tests (7 passing)
  • Worker integration with plugin
  • Example: Streaming Path (plugin_example.py)
  • Example: Result Path (result_only_example.py)
  • Example: Both Paths (both_paths_example.py)
  • Documentation: Two data paths (docs/streaming-vs-result.md)
  • Examples guide (examples/README.md)

🔧 Phase 5: Integration & Testing (Next)

  • Integration tests with actual Temporal workflows
  • E2E tests with Go activities
  • Performance testing
  • Error scenario testing

Why This Approach?

✅ CORRECT Developer Experience

from agents import Agent
from kivot-agents import KivotRunner

agent = Agent(name="Assistant", instructions="You are helpful.")
result = await KivotRunner.run(agent, input=prompt)

Benefits:

❌ WRONG (Old Approach)

# Kivot-specific code
result = await self._call_model(agent_config, user_question)

Problems:

  • Not compatible with OpenAI SDK
  • Can't use official examples
  • Higher learning curve
  • Kivot lock-in

References

Metadata

Release files for kivot-agents 0.1.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for kivot-agents 0.1.0
File Size Uploaded Provenance
kivot_agents-0.1.0.tar.gz 47.6 kB Without provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for kivot-agents 0.1.0
File Interpreter ABI Platform
kivot_agents-0.1.0-py3-none-any.whl Python 3 none any Details

Total release size: 98.4 kB

Release files / kivot_agents-0.1.0.tar.gz

Download URL kivot_agents-0.1.0.tar.gz
Size 47.6 kB
Tags Source
SHA-256 checksum
How to use checksums
82b6d51d895aeb9979a94673b3ab6e8cf2351a1b7e02d561b871cb78ffe37591
BLAKE2b-256 checksum
How to use checksums
7af9fdfac3484216d39f9920fe1a41d324af00f250f245a78882511250fbdc9b
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.8.18

Release files / kivot_agents-0.1.0-py3-none-any.whl

Download URL kivot_agents-0.1.0-py3-none-any.whl
Size 50.8 kB
Tags Python 3
SHA-256 checksum
How to use checksums
6615ab986817fd016a6adfd7bb6a6d4afa74e6aba29d3b9479063531d19bf0af
BLAKE2b-256 checksum
How to use checksums
f3d40e3449d581b08088616542039c9b640303d2bfb5ded3dd5918e59faac7cd
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.8.18

Security for kivot-agents 0.1.0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Release history Release notifications | RSS feed

This release

0.1.0 This release

2 release files No provenance

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page