refactor(tutorials): migrate to the unified harness surface + renumber#428
Merged
Conversation
1 task
f35bef7 to
b3efb55
Compare
2 tasks
b3efb55 to
1e6b84c
Compare
Contributor
Author
|
@greptile review |
1 similar comment
Contributor
Author
|
@greptile review |
5953df6 to
a84f83b
Compare
5f23b89 to
f06e819
Compare
Retire the duplicate pre-unified `harness_*` tutorials and migrate every tutorial onto the canonical unified harness surface (UnifiedEmitter / Turn / convert_* helpers). Renumber onto the `NNN_<name>` paradigm, fixing the 060/130/140 collision; codex takes fresh 070/140/150 slots. Non-breaking: example sources only; no shipped SDK API changes. The unified surface already exists; the deprecated tracing handlers are still present and are removed in a follow-up. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
f06e819 to
9b36537
Compare
… at130-langgraph Greptile review on #428: the 130_langgraph temporal tutorial's graph.py builds ChatOpenAI(model=MODEL_NAME) but the manifest only mapped REDIS_URL, so a deployed worker would fail on its first model call. Add the OPENAI_API_KEY credential and the deployment.global.agent name/description block to match the sibling migrated tutorials (e.g. at110-pydantic-ai). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Greptile review on #428: the sync/async/temporal codex tutorials spawn `codex exec --json` but their Dockerfiles installed only OS+Python deps, so a live request/activity would fail with codex not on PATH. Add nodejs/npm and `npm install -g @openai/codex` to all three images. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…rkflow Greptile review on #428: thread workflow.now() through RunHarnessAgentParams to auto_send_turn so a retried activity re-emits the turn's messages with stable timestamps instead of new server-side ones (which could reorder/duplicate them). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Second slice of #425. Migrates every tutorial onto the canonical unified harness surface and renumbers them onto the
NNN_<name>paradigm.harness_*tutorials.acp.py(and temporalagent.py/workflow.py) to the unified surface (UnifiedEmitter/ Turn /convert_*helpers).070/140/150slots.Why non-breaking
The unified surface already exists on
next, and the deprecated tracing handlers are still present here. Consumers are migrated first; the handler removal lands in the follow-up harness-consolidation PRs.Notes
Part of a stacked split of #425. Targets
next; merge after #427.🤖 Generated with Claude Code
Greptile Summary
This PR migrates all tutorials onto the canonical
UnifiedEmitter/Turn/convert_*harness surface, retires the oldharness_*duplicate directories, and renumbers them onto theNNN_<name>paradigm (codex takes the freshly freed070/140/150slots).030_langgraph,040_pydantic_ai,050_openai_agents,100–120counterparts): replace bespoke tracing callbacks and per-provider stream helpers withUnifiedEmitter.yield_turn/auto_send_turnwrapping the appropriateTurnobject.110_pydantic_ai,120_openai_agents,130_langgraph): class renames and docstring updates only; new150_codexadds a full Temporal-durable codex workflow with a subprocess activity (run_codex_turn) that correctly passescreated_at=workflow.now()for deterministic replay and uses incremental UTF-8 decoding for stdout.nodejs+npm+@openai/codex, placing thecodexbinary onPATHat runtime.Confidence Score: 5/5
Safe to merge; the migration is mechanical and all runtime-critical paths (deterministic timestamps, Codex CLI installation, retry policies on the openai-agents activity) look correct.
The changes are a consistent mechanical swap of per-provider streaming helpers for the unified emitter surface across ~200 tutorial files. The new Temporal Codex tutorial correctly handles subprocess I/O in an activity, passes workflow.now() for replay determinism, and the Dockerfiles now install the Codex CLI. No logic paths were altered in the core SDK itself.
examples/tutorials/10_async/10_temporal/120_openai_agents/project/workflow.py carries a stale class name from the deleted 140_harness_openai slot.
Important Files Changed
Flowchart
%%{init: {'theme': 'neutral'}}%% flowchart TD subgraph Before["Before (per-provider helpers)"] A1["create_langgraph_tracing_handler\n+ convert_langgraph_to_agentex_events"] --> S1["stream / Redis / Temporal channel"] A2["create_pydantic_ai_tracing_handler\n+ convert_pydantic_ai_to_agentex_events"] --> S1 A3["OpenAIAgentsPlugin\n(bespoke tracing wiring)"] --> S1 end subgraph After["After (unified harness surface)"] B1["LangGraphTurn(stream)"] --> UE["UnifiedEmitter\n.yield_turn() — sync\n.auto_send_turn() — async/Temporal"] B2["PydanticAITurn(stream)"] --> UE B3["OpenAITurn(result)"] --> UE B4["CodexTurn(events)"] --> UE UE --> CH["Single canonical\nchannel (HTTP yield / Redis / Temporal activity)"] end Before -.->|"migration"| After%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%% flowchart TD subgraph Before["Before (per-provider helpers)"] A1["create_langgraph_tracing_handler\n+ convert_langgraph_to_agentex_events"] --> S1["stream / Redis / Temporal channel"] A2["create_pydantic_ai_tracing_handler\n+ convert_pydantic_ai_to_agentex_events"] --> S1 A3["OpenAIAgentsPlugin\n(bespoke tracing wiring)"] --> S1 end subgraph After["After (unified harness surface)"] B1["LangGraphTurn(stream)"] --> UE["UnifiedEmitter\n.yield_turn() — sync\n.auto_send_turn() — async/Temporal"] B2["PydanticAITurn(stream)"] --> UE B3["OpenAITurn(result)"] --> UE B4["CodexTurn(events)"] --> UE UE --> CH["Single canonical\nchannel (HTTP yield / Redis / Temporal activity)"] end Before -.->|"migration"| AfterComments Outside Diff (5)
examples/tutorials/10_async/10_temporal/150_codex/project/workflow.py, line 96-108 (link)retry_policyon the codex activity — default retries could duplicate streamed eventsWithout an explicit
retry_policy, Temporal's default (up to 10 attempts) applies. Therun_codex_turnactivity callsUnifiedEmitter.auto_send_turn, which streams events to Redis as they arrive. If the activity fails mid-stream and Temporal retries it, the retry spawns a fresh codex process that pushes its events again — producing duplicate messages in the task stream. AddingRetryPolicy(maximum_attempts=1)prevents this, matching the expectation that each turn produces exactly one stream of output.Prompt To Fix With AI
examples/tutorials/00_sync/070_codex/Dockerfile, line 5-18 (link)The sync, async, and Temporal Codex tutorials all shell out to
codex exec, but their Dockerfiles only install OS and Python dependencies. In packaged images, requests or activities will fail at subprocess startup becausecodexis missing fromPATH. This affectsexamples/tutorials/00_sync/070_codex/Dockerfile,examples/tutorials/10_async/00_base/140_codex/Dockerfile, andexamples/tutorials/10_async/10_temporal/150_codex/Dockerfile. Please install the Codex CLI and its runtime in each image, or avoid depending on an external executable.Artifacts
Repro: focused subprocess and Dockerfile omission check
Repro: command output showing codex absent and all three subprocess paths fail
Prompt To Fix With AI
examples/tutorials/10_async/00_base/140_codex/Dockerfile, line 6-18 (link)The sync, async, and Temporal Codex tutorials all spawn
codex exec --jsonfrom their agent or worker code, but the migrated Dockerfiles only install OS and Python dependencies. Containers built fromexamples/tutorials/00_sync/070_codex/Dockerfile,examples/tutorials/10_async/00_base/140_codex/Dockerfile, andexamples/tutorials/10_async/10_temporal/150_codex/Dockerfilewill fail on the first live request or activity becausecodexis not onPATH. Please package the Codex CLI in each image, such as by installing Node/npm and@openai/codex, before running the agent or worker.Artifacts
Repro: Docker build attempt showing docker is unavailable in this environment
Repro: fallback harness that inspects the anchored Dockerfile and runs Docker build/run if available
Repro: fallback harness output showing no Codex CLI install step in the anchored Dockerfile
Prompt To Fix With AI
examples/tutorials/10_async/10_temporal/120_openai_agents/project/workflow.py, line 81-87 (link)This Temporal workflow streams AgentEx messages from the activity through
UnifiedEmitter.auto_send_turn, but the activity params do not carrycreated_at=workflow.now(). When the activity is retried after partially streaming a turn, the same logical agent messages can be persisted again with new server-side timestamps, which can reorder or duplicate them relative to the echoed user message and other turns. Please pass a deterministiccreated_atthroughRunHarnessAgentParamsand forward it toauto_send_turn.Artifacts
Repro: focused runtime harness with mocks for workflow and activity timestamp capture
Repro: harness output showing missing created_at in workflow params and auto_send_turn call
Prompt To Fix With AI
General comment
Reviews (8): Last reviewed commit: "fix(tutorials): pass deterministic creat..." | Re-trigger Greptile