Skip to content

Add avp-openai-agent package for the OpenAI Agents SDK - #7

Open
Patrick Carney (pcarney8) wants to merge 6 commits into
mainfrom
avp-openai-agent
Open

Add avp-openai-agent package for the OpenAI Agents SDK#7
Patrick Carney (pcarney8) wants to merge 6 commits into
mainfrom
avp-openai-agent

Conversation

@pcarney8

Copy link
Copy Markdown

Summary

  • New python/agents/avp-openai-agent/: subclasses agents.RunHooks and translates the OpenAI Agents SDK lifecycle into AVP v0.1 events. Mirrors avp-claude-agent for the OpenAI side. Includes TracedOpenAIRunner for drop-in observability over an existing Runner.run call, plus CLI (avp-openai-agent describe + stdin/NDJSON).
  • Shared pricing: OpenAI rows added to avp/data/prices.json (gpt-5 family, gpt-4.1 family, o4-mini), plus AVP_PRICES_PATH env override so ops can hot-swap a price table without a code release (next-best-thing to a dynamic pricing API — neither OpenAI nor Anthropic publishes one).
  • Makefile: test-real-llm and examples gates loosened to accept either ANTHROPIC_API_KEY or OPENAI_API_KEY; each test/example self-skips on missing preflight. New worked example 04_openai_agents_audited.py.
  • Handoff → subagent mapping: OpenAI Agents SDK handoffs (control transfers) surface as AVP subagent_invoked/subagent_returned pairs to keep the v0.1 wire frozen. Semantic stretch documented in the translator and README.

Test plan

  • make test — 324 tests pass across all 5 packages (19 new in avp-openai-agent, 2 new in avp pricing)
  • make conformance — 20/20 cases pass, 22/22 event types covered
  • make format-check && make lint — clean
  • Real-LLM smoke — 3/3 pass against gpt-5-nano (~9.6s, well under $0.01)
  • Example 04 end-to-end against live gpt-5-nano — converged, $0.0002, full lifecycle wire shape correct
  • make smoke (full paid matrix) — defer to reviewer / CI

Known follow-ups (separate PRs)

  • avp-openai SDK adapter for the raw Responses API (parallels avp-anthropic)
  • avp-codex-agent for the Codex CLI (no upstream Python SDK; subprocess + stream parsing)

🤖 Generated with Claude Code

New python/agents/avp-openai-agent/: subclasses agents.RunHooks and
translates the SDK's lifecycle into AVP v0.1 events, mirroring
avp-claude-agent for Anthropic. Maps on_llm_start/end → model_turn_*,
on_tool_start/end → tool_*, on_handoff + on_agent_end → subagent_*,
and reasoning items → reasoning_emitted. Ships a TracedOpenAIRunner
for drop-in observability over an existing Runner.run call.

Shared pricing gains OpenAI rows (gpt-5 family, gpt-4.1 family,
o4-mini) plus an AVP_PRICES_PATH env override so ops can hot-swap a
price table without a code release. Makefile real-LLM and examples
gates loosened to accept either ANTHROPIC_API_KEY or OPENAI_API_KEY;
each test/example self-skips on missing preflight.

Verified: make test (324 tests), make conformance (20/20 cases,
22/22 events), real-LLM smoke (3/3 against gpt-5-nano), example 04
end-to-end against live gpt-5-nano.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@pcarney8 Patrick Carney (pcarney8) added the enhancement New feature or request label May 11, 2026
Patrick Carney (pcarney8) and others added 5 commits May 11, 2026 16:16
node_modules/ and package-lock.json are both gitignored for
typescript/avp/, so a fresh checkout has neither and `make smoke`
fails on `tsc: command not found`. Install on demand if the
directory is missing.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Tests: five new files (+26 unit tests, +1 real-LLM) matching the
shape avp-claude-agent already has:
  - test_local_tools_bridge: function_tool registry conversion
  - test_provider_from_env: gen_ai.provider.name resolution rules
  - test_agent_started_surface: tools/subagents/OTel attrs on
    agent_started, plus the once-per-root invariant
  - test_cost_source: avp.cost.source provenance (computed/unknown)
    and wire spelling under the dotted alias
  - test_subagent: handoff edge cases (sequential, frame parenting,
    defensive no-ops, structured outputs)
  - real-LLM: function-tool round-trip (retries once if the model
    skips the tool, then fails)

Examples: rename 04 -> 08 so OpenAI examples group at the end
(01-07 Anthropic / Claude Code, 08-09 OpenAI). New 09 mirrors 07's
TracedOpenAIRunner / drop-in observability pattern.

Makefile: positional provider scoping. `make smoke anthropic`,
`make test-real-llm openai`, `make examples anthropic` all work;
provider word is parsed from MAKECMDGOALS, `anthropic` / `openai`
declared as no-op .PHONY targets so Make doesn't error on the bare
word, smoke uses $(MAKE) recursion to propagate PROVIDER through
sub-targets. Help text updated. Bare `make smoke` / etc. unchanged.

Verified: make test (350 tests pass, up from 324), make conformance
(20/20, 22/22), provider-scoped dry-runs route to the correct
package + example lists.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Each provider now owns a decade: 01-09 Anthropic / Claude Code,
10-19 OpenAI. Leaves room for additional examples in either lane
without renumbering.

  08_openai_agents_audited       -> 10_openai_agents_audited
  09_openai_agents_traced_client -> 11_openai_agents_traced_client

Updated docstring labels, the in-file `Run:` hint, and the Makefile
EXAMPLES_OPENAI list.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Before: ReasoningEmittedEvent fell through to the catchall, printing
just the class name. In live trajectory output this produced a
confusing layout — a bare "ReasoningEmittedEvent" line followed by
the next event's content on the line below looked like one event
with two unrelated parts.

After: ReasoningEmittedEvent renders as either the truncated summary
text or a "<redacted>" marker, matching the level of detail every
other event already gets from format_event.

Caught in example 11 output where gpt-5-nano produced a redacted
reasoning item alongside a real text_emitted; the bare class-name
fall-through made it look like the reasoning carried the answer.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Without `reasoning.summary` set in ModelSettings, the OpenAI Responses
API returns an empty `summary` on each ResponseReasoningItem — GPT-5
still reasons server-side (you still pay reasoning tokens) but the
text isn't surfaced. The translator honestly emits
`avp.reasoning.redacted=true`, which is correct but not what an audit
demo should show.

Set `Reasoning(summary="auto")` on each example's Agent so
ReasoningEmittedEvent carries plaintext.

Passthrough by design: SDK-level knobs stay at the SDK layer (your
own Agent construction); Commission stays provider-neutral. Example
10 demonstrates this by passing a custom `agent_factory` to the
translator rather than relying on the default-built Agent.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant