Skip to content

Add OpenAI Responses API adapter and developer role support - #17

Merged
callingmedic911 merged 14 commits into
mainfrom
apandey/openai-responses-adapter
Mar 4, 2026
Merged

Add OpenAI Responses API adapter and developer role support#17
callingmedic911 merged 14 commits into
mainfrom
apandey/openai-responses-adapter

Conversation

@callingmedic911

@callingmedic911 callingmedic911 commented Feb 25, 2026

Copy link
Copy Markdown
Member

Summary

  • Adds OpenAIResponsesAdapter (openai_responses flavor) so prompts are formatted for the Responses API ({"type": "message", ...} items, system stripped to system_content)
  • Tool schema in flat Responses API style; output schema as {"format": {"type": "json_schema", ...}}
  • all_messages() now uses adapter-formatted llm_prompt consistently, falling back to raw messages for string adapters (Llama)
  • Adds developer role support: passes through natively for openai_responses, coerced to system with a warning for all other flavors
  • BoundPrompt.format() uses dataclasses.replace to reflect effective flavor in prompt_info when overridden

Test plan

  • Adapter tests: OpenAI Responses strips system, keeps developer, handles media
  • Format tests: developer coercion for anthropic_chat, openai_chat; passthrough for openai_responses
  • Existing all_messages tests updated for new llm_prompt-based behavior
  • Full suite: 174 passed, 0 failures
  • make type-check: no new type issues

Note

Add OpenAI Responses API adapter and preserve developer role for the 'openai_responses' flavor in OpenAIResponsesAdapter.to_llm_syntax and prompt formatting

Introduce the openai_responses flavor with a new adapter that strips system messages and wraps items as Responses API messages; update prompt formatting to map tools/output to Responses API shapes and preserve the developer role for this flavor; update the example to call the Responses API; add tests for adapter behavior and role coercion; set REQUESTS_CA_BUNDLE for make run-%.

📍Where to Start

Start with the OpenAIResponsesAdapter and flavor switch in adapters.py, then review the formatting logic in prompts.py.

Changes since #17 opened

  • Introduced RoleSupport dataclass and prepare_messages function in freeplay.resources.adapters module to centralize role validation and coercion [1892d6f]
  • Integrated prepare_messages into BoundPrompt.format method in freeplay.resources.prompts module to replace inline role coercion logic [1892d6f]
  • Declared role support configurations for all adapter classes in freeplay.resources.adapters module [1892d6f]
  • Modified BoundPrompt.__format_output_schema method in freeplay.resources.prompts module to return normalized schema directly for 'openai_responses' flavor [1892d6f]
  • Updated examples.openai_responses_api.py script to pass structured format specification for OpenAI Responses API [1892d6f]
  • Updated line numbers in scripts/type-baseline/pyright-baseline.json [1892d6f]
  • Updated line number references in pyright-baseline.json type checking baseline file [0960253]
  • Added openai_responses flavor adapter for the OpenAI Responses API [74bee68]
  • Added support for developer role with coercion and mapping [74bee68]
  • Removed changelog entry for image support in prompt templates [74bee68]
  • Bumped version from 0.5.9 to 0.5.10 [74bee68]

Macroscope summarized 164a993.

OpenAI's Responses API uses a different message/tool/output schema shape
than Chat Completions. This adds a new flavor so get_formatted(flavor_name=
"openai_responses") returns correctly shaped data:

- OpenAIResponsesAdapter: subclass of OpenAIAdapter that strips system
  messages (they go to the instructions param) and wraps each message as
  a native Responses API item with {"type": "message", ...}
- Tool schema: flat format (no nested function wrapper)
- Output schema: transforms to {"format": {"type": "json_schema", ...}}
- Metadata fix: dataclasses.replace on prompt_info when flavor is overridden
- Flavor-to-provider mapping: openai_responses -> openai
- Makefile: run-% now sets REQUESTS_CA_BUNDLE via mkcert for local HTTPS
- Updated example to use the new flavor with proper recording format
@coderabbitai

coderabbitai Bot commented Feb 25, 2026

Copy link
Copy Markdown

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds an "openai_responses" flavor: new OpenAIResponsesAdapter and tests, prompt formatting and provider-mapping updates to support OpenAI Responses tool/output schemas and message lists, an updated example using the Responses API, and a Makefile change setting REQUESTS_CA_BUNDLE for run targets.

Changes

Cohort / File(s) Summary
Adapter & tests
src/freeplay/resources/adapters.py, tests/test_adapters.py
Add OpenAIResponsesAdapter that converts provider messages to the Responses shape, filters system messages, and register adapter for openai_responses. Tests added to validate behavior and exports.
Prompt formatting & provider mapping
src/freeplay/resources/prompts.py
Support openai_responses flavor: map to openai provider, emit tools as list of dicts with type/name/description/parameters, wrap output schema as {"format":{"type":"json_schema",...}}, and adjust FormattedPrompt.all_messages to accept list-form provider messages; propagate effective PromptInfo when flavor changes.
Example usage
examples/openai_responses_api.py
Refactor to build response_params from formatted_prompt, call Responses API with formatted_prompt.all_messages() as messages, add diagnostic prints, and simplify session recording output.
Build / Run target
Makefile
In run-% target, set REQUESTS_CA_BUNDLE to mkcert root CA path before invoking Python to ensure requests use that CA.

Sequence Diagram(s)

sequenceDiagram
    participant Client as Client / Example
    participant Prompt as PromptFormatter
    participant Adapter as OpenAIResponsesAdapter
    participant API as OpenAI Responses API
    participant Recorder as Session Recorder

    Client->>Prompt: build FormattedPrompt (template, inputs)
    Prompt->>Adapter: formatted_prompt.all_messages()
    Adapter->>API: send messages + response_params
    API-->>Adapter: completion/result
    Adapter->>Recorder: record session (all_messages, session_info, call_info)
    Recorder-->>Client: completion_id
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Suggested reviewers

  • asutermo

Poem

🐰 I hopped through code with curious eyes,
Messages shaped neat in new replies,
System roles swept soft away,
Responses romp and find their way,
A carrot cheer — the rabbit sighs.

🚥 Pre-merge checks | ✅ 1 | ❌ 1

❌ Failed checks (1 inconclusive)

Check name Status Explanation Resolution
Title check ❓ Inconclusive The title mentions OpenAI Responses API adapter but omits the developer role support change that is claimed in the title. Clarify whether 'developer role support' is part of this PR; if not, revise title to 'Add OpenAI Responses API adapter'. If included, describe it in the changeset context.
✅ Passed checks (1 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch apandey/openai-responses-adapter

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (3)
examples/openai_responses_api.py (1)

33-35: dict[str, Any] requires Python 3.9+; rest of codebase uses Dict[str, Any].

The library code consistently uses Dict from typing. If the project's minimum Python version is < 3.9, this will fail at runtime. Since this is an example file it's lower risk, but worth aligning for consistency.

♻️ Proposed fix
-response_params: dict[str, Any] = {
+response_params: Dict[str, Any] = {

And update the import on line 3:

-from typing import Any
+from typing import Any, Dict
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@examples/openai_responses_api.py` around lines 33 - 35, Change the type
annotation for response_params from built-in generic syntax to the typing module
alias used across the codebase: replace dict[str, Any] with Dict[str, Any], and
ensure Dict and Any are imported from typing (update the import at top of
examples/openai_responses_api.py to include Dict). Locate the response_params
declaration and formatted_prompt.prompt_info.model_parameters usage to confirm
the annotation change is applied consistently.
src/freeplay/resources/prompts.py (1)

174-184: List-based branching is implicit — could misfire for non-Responses providers.

The isinstance(converted, list) check is used as a proxy for "this is a Responses API output." If any future provider's model_dump() or to_dict() returns a list (or if a user passes a list as new_message), this branch will incorrectly wrap self._messages with {"type": "message", ...}.

Consider keying on the flavor (available via self.prompt_info.flavor_name) instead of the shape of the converted output.

♻️ Sketch
     def all_messages(self, new_message: ProviderMessage) -> List[Dict[str, Any]]:
         converted = convert_provider_message_to_dict(new_message)
-        if not isinstance(converted, list):
-            return self._messages + [converted]
-        # Responses API: output is a list of typed items. Wrap input
-        # messages as OpenAI Responses message items so the full list
-        # uses a consistent format for the recording API.
-        wrapped: List[Dict[str, Any]] = [
-            {"type": "message", **m} for m in self._messages
-        ]
-        return wrapped + converted
+        if self.prompt_info.flavor_name == "openai_responses":
+            wrapped: List[Dict[str, Any]] = [
+                {"type": "message", **m} for m in self._messages
+            ]
+            if isinstance(converted, list):
+                return wrapped + converted
+            return wrapped + [converted]
+        if not isinstance(converted, list):
+            return self._messages + [converted]
+        return self._messages + converted
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/freeplay/resources/prompts.py` around lines 174 - 184, The branching in
all_messages currently treats any list-shaped converted value as a Responses API
output; change it to test the provider flavor via self.prompt_info.flavor_name
(e.g., compare to the Responses flavor string used in your app) instead of using
isinstance(converted, list). Specifically, in the all_messages method (and
around convert_provider_message_to_dict usage), detect if
self.prompt_info.flavor_name indicates the Responses API and only then wrap
existing self._messages into {"type":"message", **m} items before concatenating
with converted; otherwise, treat converted as a regular item (or list) and
append/extend without wrapping. Ensure you still handle both single-dict and
list converted values, but use flavor_name as the authoritative switch so
non-Responses providers that happen to return lists are not mis-classified.
Makefile (1)

29-30: mkcert is now a hard dependency for run-%.

If mkcert is not installed, this target will fail for all examples, not just the ones that need HTTPS to a local server. Consider guarding this or documenting the requirement.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@Makefile` around lines 29 - 30, The Makefile target run-% currently
unconditionally calls mkcert to set REQUESTS_CA_BUNDLE which makes mkcert a hard
dependency; modify the run-% target so it first checks for mkcert's presence (or
document the requirement) and only sets REQUESTS_CA_BUNDLE="$$(mkcert
-CAROOT)/rootCA.pem" when mkcert is available, otherwise fall back to running
the example without setting REQUESTS_CA_BUNDLE or print a clear error
instructing the user to install mkcert; update the run-% target and any
associated docs to reflect this conditional behavior.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/freeplay/resources/adapters.py`:
- Around line 264-273: The to_llm_syntax override in OpenAIResponsesAdapter
calls super().to_llm_syntax() which is typed as Union[str, List[Dict[str,
Any]]]; narrow that union before treating elements as dicts: assign formatted =
super().to_llm_syntax(messages), then add an explicit runtime/type check (e.g.,
if isinstance(formatted, str): raise TypeError(...) or return an appropriate
conversion) and only iterate when formatted is a List[Dict[str, Any]] so the
unpack {"type": "message", **m} and m["role"] are valid; update the method
OpenAIResponsesAdapter.to_llm_syntax to perform this check and raise a clear
error for the impossible str branch.

---

Nitpick comments:
In `@examples/openai_responses_api.py`:
- Around line 33-35: Change the type annotation for response_params from
built-in generic syntax to the typing module alias used across the codebase:
replace dict[str, Any] with Dict[str, Any], and ensure Dict and Any are imported
from typing (update the import at top of examples/openai_responses_api.py to
include Dict). Locate the response_params declaration and
formatted_prompt.prompt_info.model_parameters usage to confirm the annotation
change is applied consistently.

In `@Makefile`:
- Around line 29-30: The Makefile target run-% currently unconditionally calls
mkcert to set REQUESTS_CA_BUNDLE which makes mkcert a hard dependency; modify
the run-% target so it first checks for mkcert's presence (or document the
requirement) and only sets REQUESTS_CA_BUNDLE="$$(mkcert -CAROOT)/rootCA.pem"
when mkcert is available, otherwise fall back to running the example without
setting REQUESTS_CA_BUNDLE or print a clear error instructing the user to
install mkcert; update the run-% target and any associated docs to reflect this
conditional behavior.

In `@src/freeplay/resources/prompts.py`:
- Around line 174-184: The branching in all_messages currently treats any
list-shaped converted value as a Responses API output; change it to test the
provider flavor via self.prompt_info.flavor_name (e.g., compare to the Responses
flavor string used in your app) instead of using isinstance(converted, list).
Specifically, in the all_messages method (and around
convert_provider_message_to_dict usage), detect if self.prompt_info.flavor_name
indicates the Responses API and only then wrap existing self._messages into
{"type":"message", **m} items before concatenating with converted; otherwise,
treat converted as a regular item (or list) and append/extend without wrapping.
Ensure you still handle both single-dict and list converted values, but use
flavor_name as the authoritative switch so non-Responses providers that happen
to return lists are not mis-classified.

ℹ️ Review info

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between da24790 and adc81c0.

📒 Files selected for processing (5)
  • Makefile
  • examples/openai_responses_api.py
  • src/freeplay/resources/adapters.py
  • src/freeplay/resources/prompts.py
  • tests/test_adapters.py

Comment thread src/freeplay/resources/adapters.py Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (2)
examples/openai_responses_api.py (2)

27-31: Gate verbose payload logging behind a debug flag.

These prints can expose prompt/system/user content in logs. Prefer conditional debug logging instead of always printing.

Suggested diff
-print(f"Instructions (system): {formatted_prompt.system_content}")
-print(f"Input messages: {formatted_prompt.llm_prompt}")
-print(f"Tool schema: {formatted_prompt.tool_schema}")
-print(f"Output schema: {formatted_prompt.formatted_output_schema}")
+if os.environ.get("DEBUG_OPENAI_RESPONSES_EXAMPLE") == "1":
+    print(f"Instructions (system): {formatted_prompt.system_content}")
+    print(f"Input messages: {formatted_prompt.llm_prompt}")
+    print(f"Tool schema: {formatted_prompt.tool_schema}")
+    print(f"Output schema: {formatted_prompt.formatted_output_schema}")
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@examples/openai_responses_api.py` around lines 27 - 31, Replace the
unconditional print statements that expose prompt content
(formatted_prompt.system_content, formatted_prompt.llm_prompt,
formatted_prompt.tool_schema, formatted_prompt.formatted_output_schema) with
conditional debug-level logging: check a debug flag or
logger.isEnabledFor(logging.DEBUG) and emit these values via logger.debug rather
than print, so sensitive prompt/user content is only logged when debug is
explicitly enabled.

20-25: Make the Responses flavor explicit in this example.

Line 20 currently relies on prompt/template defaults. Since this script is specifically for Responses API, pass flavor_name="openai_responses" so the payload shape is deterministic.

Suggested diff
 formatted_prompt = fp_client.prompts.get_formatted(
     project_id=project_id,
     template_name="my-openai-prompt",
+    flavor_name="openai_responses",
     environment="latest",
     variables=input_variables,
 )
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@examples/openai_responses_api.py` around lines 20 - 25, The example call to
fp_client.prompts.get_formatted (assigning formatted_prompt) should explicitly
set the Responses flavor so the returned payload shape is deterministic; update
the get_formatted invocation to include flavor_name="openai_responses" alongside
project_id, template_name, environment, and variables so the code always
retrieves the OpenAI Responses-shaped prompt.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@examples/openai_responses_api.py`:
- Around line 33-47: The splatted response_params may contain reserved keys
(e.g., "model") from formatted_prompt.prompt_info.model_parameters and will
conflict with explicit arguments to openai_client.responses.create; before
constructing response_params remove reserved keys such as "model" (and "input"
if present) from formatted_prompt.prompt_info.model_parameters, then merge the
cleaned dict into response_params so openai_client.responses.create(input=...,
model=..., **response_params) cannot receive duplicate keys.

---

Nitpick comments:
In `@examples/openai_responses_api.py`:
- Around line 27-31: Replace the unconditional print statements that expose
prompt content (formatted_prompt.system_content, formatted_prompt.llm_prompt,
formatted_prompt.tool_schema, formatted_prompt.formatted_output_schema) with
conditional debug-level logging: check a debug flag or
logger.isEnabledFor(logging.DEBUG) and emit these values via logger.debug rather
than print, so sensitive prompt/user content is only logged when debug is
explicitly enabled.
- Around line 20-25: The example call to fp_client.prompts.get_formatted
(assigning formatted_prompt) should explicitly set the Responses flavor so the
returned payload shape is deterministic; update the get_formatted invocation to
include flavor_name="openai_responses" alongside project_id, template_name,
environment, and variables so the code always retrieves the OpenAI
Responses-shaped prompt.

ℹ️ Review info

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between adc81c0 and a5eb46c.

📒 Files selected for processing (2)
  • examples/openai_responses_api.py
  • src/freeplay/resources/adapters.py
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/freeplay/resources/adapters.py

Comment thread examples/openai_responses_api.py Outdated
Instead of re-wrapping raw messages with {"type": "message", ...} in
all_messages, use self._llm_prompt which the adapter already formatted
correctly. This avoids a dict spread ordering bug and keeps the adapter
as the single source of truth for message formatting.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/freeplay/resources/prompts.py`:
- Around line 275-277: Guard the "openai_responses" branch against a missing
json_schema: before building inner = output_schema.get("json_schema", {}), check
that "json_schema" exists and is non-empty on output_schema (referencing the
flavor_name == "openai_responses" branch, the output_schema variable, and
inner), and if it's missing produce a clear failure (e.g., raise a ValueError
with a descriptive message including flavor_name/output_schema) or return an
explicit fallback instead of returning an incomplete {"format": {"type":
"json_schema", **inner}} structure.
- Around line 175-183: The method mixes raw dict messages (self._messages) with
adapter-formatted messages (self._llm_prompt + converted list), causing mixed
payload schemas; fix by normalizing to a single schema: when converted is a
list, map/convert self._messages into the same adapter-formatted shape used by
self._llm_prompt before concatenation (i.e., produce an adapter-formatted base
from self._messages and then return that + converted); use the same conversion
logic or helper that produced _llm_prompt so the concatenation of
list(self._llm_prompt or []) and converted is consistent with items derived from
self._messages, referencing convert_provider_message_to_dict, self._messages,
self._llm_prompt, new_message and converted to locate the code.

ℹ️ Review info

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a5eb46c and 0ba7684.

📒 Files selected for processing (1)
  • src/freeplay/resources/prompts.py

Comment thread src/freeplay/resources/prompts.py
Comment thread src/freeplay/resources/prompts.py Outdated
Comment on lines +275 to +277
elif flavor_name == "openai_responses":
inner = output_schema.get("json_schema", {})
return {"format": {"type": "json_schema", **inner}}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Add a guard for missing json_schema before formatting.

When json_schema is absent, this returns an incomplete structure and fails later in a less actionable place.

💡 Proposed fix
         elif flavor_name == "openai_responses":
-            inner = output_schema.get("json_schema", {})
+            inner = output_schema.get("json_schema")
+            if not inner:
+                raise FreeplayConfigurationError(
+                    "Missing json_schema in output_schema for openai_responses flavor."
+                )
             return {"format": {"type": "json_schema", **inner}}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/freeplay/resources/prompts.py` around lines 275 - 277, Guard the
"openai_responses" branch against a missing json_schema: before building inner =
output_schema.get("json_schema", {}), check that "json_schema" exists and is
non-empty on output_schema (referencing the flavor_name == "openai_responses"
branch, the output_schema variable, and inner), and if it's missing produce a
clear failure (e.g., raise a ValueError with a descriptive message including
flavor_name/output_schema) or return an explicit fallback instead of returning
an incomplete {"format": {"type": "json_schema", **inner}} structure.

Always prefer adapter-formatted messages (_llm_prompt) over raw
messages for recording. Falls back to _messages for string-format
adapters (Llama) where _llm_prompt is None.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

♻️ Duplicate comments (1)
src/freeplay/resources/prompts.py (1)

276-278: ⚠️ Potential issue | 🟡 Minor

Add explicit validation for missing json_schema.

At Line 277, defaulting to {} can silently build {"format": {"type": "json_schema"}}, which fails later with a less actionable error. This should fail fast with a clear configuration error.

💡 Proposed fix
-            inner = output_schema.get("json_schema", {})
+            inner = output_schema.get("json_schema")
+            if not inner:
+                raise FreeplayConfigurationError(
+                    "Missing json_schema in output_schema for openai_responses flavor."
+                )
             return {"format": {"type": "json_schema", **inner}}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/freeplay/resources/prompts.py` around lines 276 - 278, When handling
flavor_name == "openai_responses" in the code that reads output_schema, stop
defaulting json_schema to {} and instead validate that output_schema contains a
non-empty "json_schema" key; in the branch where inner =
output_schema.get("json_schema", {}), replace that with an explicit check (e.g.,
if "json_schema" not in output_schema or not output_schema["json_schema"]: raise
ValueError("openai_responses requires a non-empty 'json_schema' in
output_schema")) so the function fails fast with a clear error rather than
returning {"format": {"type": "json_schema"}} and causing downstream failures.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/freeplay/resources/prompts.py`:
- Around line 175-183: The code uses "self._llm_prompt or self._messages" which
treats an empty list as falsy and incorrectly falls back to raw messages; change
this to explicitly check for None (e.g., use "self._llm_prompt if
self._llm_prompt is not None else self._messages") when computing input_messages
so an intentionally empty _llm_prompt is respected; update the assignment that
builds input_messages (referencing _llm_prompt, _messages and
convert_provider_message_to_dict) to use that None check and preserve the
List[Dict[str, Any]] typing.

---

Duplicate comments:
In `@src/freeplay/resources/prompts.py`:
- Around line 276-278: When handling flavor_name == "openai_responses" in the
code that reads output_schema, stop defaulting json_schema to {} and instead
validate that output_schema contains a non-empty "json_schema" key; in the
branch where inner = output_schema.get("json_schema", {}), replace that with an
explicit check (e.g., if "json_schema" not in output_schema or not
output_schema["json_schema"]: raise ValueError("openai_responses requires a
non-empty 'json_schema' in output_schema")) so the function fails fast with a
clear error rather than returning {"format": {"type": "json_schema"}} and
causing downstream failures.

ℹ️ Review info

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 0ba7684 and 5533112.

📒 Files selected for processing (1)
  • src/freeplay/resources/prompts.py

Comment thread src/freeplay/resources/prompts.py
The developer role (higher-precedence system instructions from OpenAI) is
now accepted in prompt templates. For openai_responses the role passes
through natively; for all other flavors it is coerced to system in
BoundPrompt.format() with a warning, so individual adapters need no
changes.
Comment thread src/freeplay/resources/prompts.py Outdated
The all_messages method now uses llm_prompt (adapter-formatted) which
for Anthropic strips system messages. Tests were still expecting the
old count that included system. Adjust to expect 3 instead of 4.
@callingmedic911 callingmedic911 changed the title Add OpenAI Responses API adapter Add OpenAI Responses API adapter and developer role support Feb 25, 2026
Comment thread examples/openai_responses_api.py Outdated
if formatted_prompt.tool_schema:
response_params["tools"] = formatted_prompt.tool_schema
if formatted_prompt.formatted_output_schema:
response_params["text"] = formatted_prompt.formatted_output_schema

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

The output schema param is really called text?

Comment thread src/freeplay/resources/prompts.py Outdated
return output_schema
elif flavor_name == "openai_responses":
inner = output_schema.get("json_schema", {})
return {"format": {"type": "json_schema", **inner}}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

i'm not sure this handling is consistent with the other flavors -- they don't provide the top level json_schema annotation.. It is a bit different than how we handle tools.

Comment thread src/freeplay/resources/prompts.py Outdated
# Coerce developer → system for flavors that don't support the developer role.
# openai_responses supports developer natively; all others treat it as system.
messages = self.messages
if final_flavor != "openai_responses" and any(

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Can this logic live in the adapter instead of in the general format?

@callingmedic911
callingmedic911 merged commit 9088e94 into main Mar 4, 2026
5 checks passed
@callingmedic911
callingmedic911 deleted the apandey/openai-responses-adapter branch March 4, 2026 22:24
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