Skip to content

Adding OpenAI Agents SDK adapter#149

Open
amitkojha05 wants to merge 6 commits into
BetterDB-inc:masterfrom
amitkojha05:feat/openai-agents-adapter
Open

Adding OpenAI Agents SDK adapter#149
amitkojha05 wants to merge 6 commits into
BetterDB-inc:masterfrom
amitkojha05:feat/openai-agents-adapter

Conversation

@amitkojha05
Copy link
Copy Markdown
Contributor

@amitkojha05 amitkojha05 commented May 4, 2026

Summary

Adds betterdb_agent_cache.adapters.openai_agents — cache adapter for the OpenAI Agents SDK (openai-agents).

This is distinct from the existing openai.py (Chat Completions) and openai_responses.py (Responses API) adapters. The Agents SDK wraps those behind its own Model abstract class with get_response() / stream_response(), and Runner orchestrates multi-turn agent loops with handoffs and guardrails on top. This adapter intercepts at the Model.get_response() level — so agent workloads that replay the same tool-call sequences (evaluation, testing, multi-agent
orchestration) skip the API entirely.

Changes

  • New: betterdb_agent_cache/adapters/openai_agents.py

    • prepare_params() — normalizes Agents SDK get_response() inputs (system_instructions + Responses API input items) to LlmCacheParams
    • CachedModel(model, cache) — wraps any Agents SDK Model, intercepts get_response() for cache-before-call. stream_response() delegated directly (streaming not cached — BetterDB convention)
    • CachedModelProvider(provider, cache) — wraps any ModelProvider so every Model it returns is cache-enabled. Recommended integration via RunConfig(model_provider=...)
    • _normalize_input_item(item, normalizer) extracted as private helper matching the _normalize_user_content / _normalize_block pattern from peer adapters
    • _rebuild_output() gracefully handles OpenAI SDK Pydantic models when available, falls back to SimpleNamespace for older stacks
  • New: tests/adapters/test_openai_agents.py — 9 tests covering prepare_params normalization (string input, list input, settings), CachedModel (miss+store text, miss+store tool calls, hit, different prompts, error propagation, streaming delegation), CachedModelProvider

  • New: examples/openai_agents/ — runnable example (text agent + tools agent) with README

  • Modified: pyproject.toml — added openai_agents optional extra, openai-agents>=0.0.14 to dev and all, openai-agents to keywords

No changes to proprietary/. No changes to packages/agent-cache/ (TypeScript).

Design decisions

  • prepare_params takes (system_instructions, input, model_name, model_settings) as separate args because the SDK separates them in get_response() — same rationale as the Pydantic AI adapter
  • tools, handoffs, output_schema excluded from cache key — safe when one CachedModel wraps a single Agent (documented inline)
  • Binary content limitation documented in module docstring and _normalize_input_item docstring; follow-up can add normalizer dispatch

Checklist

  • Unit / integration tests added
  • Docs added / updated
  • Roborev review passed (internal)
  • Competitive analysis done / discussed (internal)
  • Blog post about it discussed (internal)

Test note: 9 tests cover all paths including tool call round-trips and SDK version compatibility. ruff check clean, pytest green.


Note

Medium Risk
Introduces a new caching integration at the Agents SDK Model.get_response() layer and changes the LLM cache hit return shape to include stored token counts, which could affect any callers relying on prior LlmCacheResult fields or token/cost accounting.

Overview
Adds an OpenAI Agents SDK adapter (adapters/openai_agents.py) that wraps an Agents SDK Model/ModelProvider to cache get_response() results (including tool-call outputs) while explicitly not caching stream_response().

Extends LLM cache hit results to return stored input_tokens/output_tokens (persisted in cache entries) so adapters can reconstruct usage on cache hits, and adds tests plus a runnable examples/openai_agents demo; pyproject.toml gains an openai_agents extra and dev/all deps for openai-agents.

Reviewed by Cursor Bugbot for commit 842c541. Bugbot is set up for automated code reviews on this repo. Configure here.

@amitkojha05
Copy link
Copy Markdown
Contributor Author

I have read the CLA Document and I hereby sign the CLA

@amitkojha05
Copy link
Copy Markdown
Contributor Author

@KIvanow @jamby77 Please review this PR ,would love to hear your feedback.

@jamby77 jamby77 requested a review from KIvanow May 5, 2026 15:28
@jamby77
Copy link
Copy Markdown
Collaborator

jamby77 commented May 5, 2026

@amitkojha05 you need to resolve conflicts


async def _normalize_input_item(
item: Any,
normalizer: BinaryNormalizer,
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If not used in thie iteration, drop the parameter

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done — dropped the parameter from _normalize_input_item

cached = await self._cache.llm.check(params)
if cached.hit:
output = _rebuild_output(cached.content_blocks, cached.response)
return _cache_hit_model_response(output, _make_usage(0, 0))
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

pass real cached tokens into _make_usage instead of (0, 0).

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed — added input_tokens and output_tokens to LlmCacheResult, populated from the stored entry in llm_cache.py. Cache hits now call _make_usage(cached.input_tokens, cached.output_tokens) instead of (0, 0)

Comment thread packages/agent-cache-py/betterdb_agent_cache/adapters/openai_agents.py Outdated
# This is safe when one CachedModel wraps a single Agent whose tools
# don't change between calls — the typical usage pattern.
params = await prepare_params(
system_instructions, input, model_name, model_settings, self._opts,
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tool context omitted from cache key

Medium Severity

tools, handoffs, and output_schema affect get_response() behavior but are excluded from params. Shared CachedModelProvider usage can therefore replay tool calls or structured outputs from another agent configuration.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit be73f0f. Configure here.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Intentional design — documented in the inline comment above. Safe when one CachedModel wraps a single Agent with fixed tools, which is the typical usage pattern. Including tools in the cache key is deferred to a follow-up PR.

Comment thread packages/agent-cache-py/betterdb_agent_cache/adapters/openai_agents.py Outdated
) -> LlmCacheParams:
"""Convert OpenAI Agents SDK get_response() args to canonical ``LlmCacheParams``."""
_ = opts

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Prepare options do nothing

Low Severity

prepare_params discards opts, leaving OpenAIAgentsPrepareOptions.normalizer and wrapper opts arguments with no effect. This creates a public option that cannot influence binary or multimodal normalization despite mirroring the peer adapter API.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit ceedb6b. Configure here.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

opts is retained for API consistency with peer adapters. The normalizer field will be wired into _normalize_input_item when binary/multimodal dispatch is added — documented in the inline comment.

@amitkojha05
Copy link
Copy Markdown
Contributor Author

@jamby77 @KIvanow all review points addressed in the latest commits. make_persisting_valkey_client added to conftest.py . Happy to iterate if anything needs adjustment.

Copy link
Copy Markdown

@cursor cursor Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 2 potential issues.

There are 4 total unresolved issues (including 2 from previous reviews).

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 842c541. Configure here.

role="assistant",
content=text_parts,
),
)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing nested try/except in message rebuild fallback

Medium Severity

The except TypeError handler for ResponseOutputMessage.model_construct at line 307–315 calls model_construct again without a nested try/except, unlike the text_part helper (lines 248–251) which correctly wraps the retry in its own try/except Exception. In Python, if the retry inside except TypeError raises a different exception, the sibling except Exception on line 316 does not catch it — it only catches exceptions from the original try block. This means a failed retry would propagate as an unhandled exception, crashing the cache-hit path instead of falling back to SimpleNamespace.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 842c541. Configure here.

params,
store_blocks,
LlmStoreOptions(tokens={"input": inp, "output": out_tok}),
)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cache store failure discards valid LLM response

High Severity

After a successful LLM call, _extract_blocks and store_multipart run without any error handling. store_multipart raises ValkeyCommandError if Valkey is unreachable, and the valid response is never returned to the caller. A transient cache infrastructure failure (Valkey down, network blip) would crash the entire agent workflow even though the LLM already produced a valid result. A caching layer's store path needs to fail gracefully so it doesn't block the primary operation.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 842c541. Configure here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants