Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
f4303d1
chore(deps): drop unused runtime deps and exclude tests from wheel (#…
max-parke-scale May 26, 2026
38ed338
feat(api): add cleaned_at field to task response types
stainless-app[bot] May 26, 2026
6f1c14f
refactor(types): promote protocol types to agentex.protocol.* (#371)
max-parke-scale May 27, 2026
feec842
perf(tracing): span queue linger + per-loop httpx keepalive (#362)
smoreinis May 29, 2026
e1b31d9
feat(deps): bump openai-agents to >=0.14.3 for scale-sandbox oai_agen…
danielmillerp May 29, 2026
0a2418c
fix(tutorials): restore tutorial CI deps after agentex-sdk 0.11.5 (py…
smoreinis May 29, 2026
d04624e
feat(lib): expose data_converter kwarg on AgentexWorker and Temporal …
matteolibrizzi-scale May 29, 2026
ab5a7d9
chore: back-merge release 0.11.5 into next (#381)
declan-scale May 29, 2026
a66d239
feat(examples): OpenAI Agents SDK local-sandbox tutorials (sync + asy…
danielmillerp May 30, 2026
7b32a0d
perf(tracing): bounded-concurrency span export (#374)
smoreinis Jun 1, 2026
13d3eab
chore: back-merge release 0.11.6 into next (#384)
smoreinis Jun 1, 2026
ad01337
Merge remote-tracking branch 'origin/main' into stas/back-merge-main-…
smoreinis Jun 1, 2026
2e1a2da
Merge pull request #385 from scaleapi/stas/back-merge-main-into-next-2
smoreinis Jun 1, 2026
6669012
feat(tracing): OTel span queue and export telemetry (SGPINF-1863) (#373)
james-cardenas Jun 1, 2026
bbc9e02
feat(cli): add Temporal + LangGraph agent template and example (#383)
danielmillerp Jun 1, 2026
6677892
Merge remote-tracking branch 'origin/main' into chore/back-merge-0.11…
smoreinis Jun 1, 2026
8b3b1bf
Merge pull request #387 from scaleapi/chore/back-merge-0.11.7-into-next
smoreinis Jun 1, 2026
5a4bd70
release: 0.11.8
stainless-app[bot] Jun 1, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .release-please-manifest.json
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
{
".": "0.11.7"
".": "0.11.8"
}
15 changes: 15 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,20 @@
# Changelog

## Unreleased

### Features

* **tracing:** emit OTel metrics for async span queue depth, batch drain, and SGP export success/failure (HTTP status labels). Disable SDK-side recording with ``AGENTEX_TRACING_METRICS=0``.

## 0.11.8 (2026-06-01)

Full Changelog: [v0.11.7...v0.11.8](https://github.com/scaleapi/scale-agentex-python/compare/v0.11.7...v0.11.8)

### Features

* **cli:** add Temporal + LangGraph agent template and example ([#383](https://github.com/scaleapi/scale-agentex-python/issues/383)) ([bbc9e02](https://github.com/scaleapi/scale-agentex-python/commit/bbc9e02d2a2b063a3e509a07ffca8ca4bf459e57))
* **tracing:** OTel span queue and export telemetry (SGPINF-1863) ([#373](https://github.com/scaleapi/scale-agentex-python/issues/373)) ([6669012](https://github.com/scaleapi/scale-agentex-python/commit/6669012638481a63bdd7629582818796ca31bdf3))

## 0.11.7 (2026-06-01)

Full Changelog: [v0.11.6...v0.11.7](https://github.com/scaleapi/scale-agentex-python/compare/v0.11.6...v0.11.7)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# Python
__pycache__/
*.py[cod]
*$py.class
*.so
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
*.egg-info/
.installed.cfg
*.egg

# Environments
.env**
.venv
env/
venv/
ENV/
env.bak/
venv.bak/

# IDE
.idea/
.vscode/
*.swp
*.swo

# Git
.git
.gitignore

# Misc
.DS_Store
13 changes: 13 additions & 0 deletions examples/tutorials/10_async/10_temporal/130_langgraph/.env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# at130-langgraph - Environment Variables
# Copy this file to .env and fill in the values

# API key for your LLM provider
LITELLM_API_KEY=

# LLM base URL (optional - override to use a different provider)
# OPENAI_BASE_URL=

# SGP Configuration (optional - for tracing)
# SGP_API_KEY=
# SGP_ACCOUNT_ID=
# SGP_CLIENT_BASE_URL=
43 changes: 43 additions & 0 deletions examples/tutorials/10_async/10_temporal/130_langgraph/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# syntax=docker/dockerfile:1.3
FROM python:3.12-slim
COPY --from=ghcr.io/astral-sh/uv:0.6.4 /uv /uvx /bin/

# Install system dependencies
RUN apt-get update && apt-get install -y \
htop \
vim \
curl \
tar \
python3-dev \
postgresql-client \
build-essential \
libpq-dev \
gcc \
cmake \
netcat-openbsd \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/*

RUN uv pip install --system --upgrade pip setuptools wheel

ENV UV_HTTP_TIMEOUT=1000

COPY 10_async/10_temporal/130_langgraph/pyproject.toml /app/130_langgraph/pyproject.toml
COPY 10_async/10_temporal/130_langgraph/README.md /app/130_langgraph/README.md

WORKDIR /app/130_langgraph

COPY 10_async/10_temporal/130_langgraph/project /app/130_langgraph/project
COPY 10_async/10_temporal/130_langgraph/tests /app/130_langgraph/tests
COPY test_utils /app/test_utils

RUN uv pip install --system .[dev]

ENV PYTHONPATH=/app

ENV AGENT_NAME=at130-langgraph

CMD ["uvicorn", "project.acp:acp", "--host", "0.0.0.0", "--port", "8000"]

# When we deploy the worker, we will replace the CMD with the following
# CMD ["python", "-m", "run_worker"]
58 changes: 58 additions & 0 deletions examples/tutorials/10_async/10_temporal/130_langgraph/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
# at130-langgraph — AgentEx Temporal + LangGraph

A minimal Temporal-backed [LangGraph](https://langchain-ai.github.io/langgraph/)
agent. It uses the official [`temporalio.contrib.langgraph`](https://docs.temporal.io/develop/python/integrations/langgraph)
plugin so each LangGraph node runs as a durable **Temporal activity** (the LLM
`agent` node) or inline in the **workflow** (the `tools` node) — set per node
with `execute_in`. *Temporal is the runtime; LangGraph is the agent framework.*

> The Temporal LangGraph plugin is currently **experimental**.

## The graph

```
START → agent → (tool calls?) → tools → agent
→ (no tool calls?) → END
```

- `agent` (`execute_in="activity"`): the LLM call — a retried, observable Temporal activity.
- `tools` (`execute_in="workflow"`): runs the tool calls inline in the workflow.

The router and tools are `async` so LangGraph awaits them directly (a sync
callable is offloaded via `run_in_executor`, which Temporal workflows forbid).

## Project structure

```
130_langgraph/
├── project/
│ ├── acp.py # Thin async ACP server; registers the LangGraphPlugin
│ ├── workflow.py # Runs the graph each turn; keeps multi-turn memory
│ ├── graph.py # LangGraph graph; nodes tagged execute_in activity/workflow
│ └── tools.py # Async tool(s)
└── run_worker.py is project/run_worker.py
```

## Running

```bash
agentex agents run --manifest manifest.yaml
```

Open the Temporal UI at http://localhost:8080 to watch the workflow and the
`agent` activity execute. Use `dev.ipynb` to create a task and send messages.

## Adding tools

Define an **async** `@tool` in `project/tools.py` and add it to `TOOLS`. The
model is bound with `TOOLS` and the tool node runs them by name.

For a fuller version with human-in-the-loop approval and graph-introspection
queries, scaffold the `temporal-langgraph` template via `agentex init`.

## Tests

- `tests/test_graph_temporal.py` — hermetic ReAct-loop test with a stub model,
plus a live end-to-end run through the real Temporal plugin (skipped unless
`LITELLM_API_KEY` is set).
- `tests/test_agent.py` — live integration against a running agent.
126 changes: 126 additions & 0 deletions examples/tutorials/10_async/10_temporal/130_langgraph/dev.ipynb
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
{
"cells": [
{
"cell_type": "code",
"execution_count": null,
"id": "36834357",
"metadata": {},
"outputs": [],
"source": [
"from agentex import Agentex\n",
"\n",
"client = Agentex(base_url=\"http://localhost:5003\")"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "d1c309d6",
"metadata": {},
"outputs": [],
"source": [
"AGENT_NAME = \"at130-langgraph\""
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "9f6e6ef0",
"metadata": {},
"outputs": [],
"source": [
"# (REQUIRED) Create a new task. For Async agents, you must create a task for messages to be associated with.\n",
"import uuid\n",
"\n",
"rpc_response = client.agents.create_task(\n",
" agent_name=AGENT_NAME,\n",
" params={\n",
" \"name\": f\"{str(uuid.uuid4())[:8]}-task\",\n",
" \"params\": {}\n",
" }\n",
")\n",
"\n",
"task = rpc_response.result\n",
"print(task)"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "b03b0d37",
"metadata": {},
"outputs": [],
"source": [
"# Send an event to the agent\n",
"\n",
"# The response is expected to be a list of TaskMessage objects, which is a union of the following types:\n",
"# - TextContent: A message with just text content \n",
"# - DataContent: A message with JSON-serializable data content\n",
"# - ToolRequestContent: A message with a tool request, which contains a JSON-serializable request to call a tool\n",
"# - ToolResponseContent: A message with a tool response, which contains response object from a tool call in its content\n",
"\n",
"# When processing the message/send response, if you are expecting more than TextContent, such as DataContent, ToolRequestContent, or ToolResponseContent, you can process them as well\n",
"\n",
"rpc_response = client.agents.send_event(\n",
" agent_name=AGENT_NAME,\n",
" params={\n",
" \"content\": {\"type\": \"text\", \"author\": \"user\", \"content\": \"Hello what can you do?\"},\n",
" \"task_id\": task.id,\n",
" }\n",
")\n",
"\n",
"event = rpc_response.result\n",
"print(event)"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "a6927cc0",
"metadata": {},
"outputs": [],
"source": [
"# Subscribe to the async task messages produced by the agent\n",
"from agentex.lib.utils.dev_tools import subscribe_to_async_task_messages\n",
"\n",
"task_messages = subscribe_to_async_task_messages(\n",
" client=client,\n",
" task=task, \n",
" only_after_timestamp=event.created_at, \n",
" print_messages=True,\n",
" rich_print=True,\n",
" timeout=5,\n",
")"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "4864e354",
"metadata": {},
"outputs": [],
"source": []
}
],
"metadata": {
"kernelspec": {
"display_name": ".venv",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.12.9"
}
},
"nbformat": 4,
"nbformat_minor": 5
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
# Agent Environment Configuration
# ------------------------------
# This file defines environment-specific settings for your agent.
# This DIFFERS from the manifest.yaml file in that it is used to program things that are ONLY per environment.

# ********** EXAMPLE **********
# schema_version: "v1" # This is used to validate the file structure and is not used by the agentex CLI
# environments:
# dev:
# auth:
# principal:
# user_id: "1234567890"
# user_name: "John Doe"
# user_email: "john.doe@example.com"
# user_role: "admin"
# user_permissions: "read, write, delete"
# helm_overrides: # This is used to override the global helm values.yaml file in the agentex-agent helm charts
# replicas: 3
# resources:
# requests:
# cpu: "1000m"
# memory: "2Gi"
# limits:
# cpu: "2000m"
# memory: "4Gi"
# env:
# - name: LOG_LEVEL
# value: "DEBUG"
# - name: ENVIRONMENT
# value: "staging"
#
# kubernetes:
# # OPTIONAL - Otherwise it will be derived from separately. However, this can be used to override the derived
# # namespace and deploy it with in the same namespace that already exists for a separate agent.
# namespace: "team-at130-langgraph"
# ********** END EXAMPLE **********

schema_version: "v1" # This is used to validate the file structure and is not used by the agentex CLI
environments:
dev:
auth:
principal:
user_id: # TODO: Fill in
account_id: # TODO: Fill in
helm_overrides:
# This is used to override the global helm values.yaml file in the agentex-agent helm charts
replicaCount: 2
resources:
requests:
cpu: "500m"
memory: "1Gi"
limits:
cpu: "1000m"
memory: "2Gi"
temporal-worker:
enabled: true
replicaCount: 2
resources:
requests:
cpu: "500m"
memory: "1Gi"
limits:
cpu: "1000m"
memory: "2Gi"
Loading
Loading