fix(responses): support OpenAI Responses tool loops - #65

Open
saxyguy81 wants to merge 2 commits into
CaddyGlow:mainfrom
saxyguy81:codex/responses-function-call-ids
Open

fix(responses): support OpenAI Responses tool loops#65
saxyguy81 wants to merge 2 commits into
CaddyGlow:mainfrom
saxyguy81:codex/responses-function-call-ids

Conversation

@saxyguy81

@saxyguy81saxyguy81 commented May 10, 2026

Copy link
Copy Markdown

Summary

  • Normalize OpenAI Responses function_call item IDs to fc_* while preserving call_id as call_*.
  • Add true OpenAI-compatible Responses route coverage for /responses, /v1/responses, and /codex/v1/responses.
  • Add local previous_response_id continuation state for Codex Responses tool loops, scoped by client auth context with TTL/max-entry cleanup.
  • Accept Droid/Factory Responses streaming fields such as stream_options.include_usage, plus Droid-sent prompt_cache_retention and safety_identifier, while stripping Codex-backend-unsupported fields before upstream dispatch.
  • Preserve valid Responses SSE for streaming tool calls, including response.function_call_arguments.*.item_id == fc_*, completed function_call.id == fc_*, and function_call.call_id == call_*.
  • Normalize provider/FastAPI 4xx failures into OpenAI-style JSON error envelopes so clients see rejected parameters instead of empty bodies.
  • Rebuild sparse Codex response.completed payloads from streamed output items when the final upstream completed event omits output.

This remains parallel to #64; it is the Responses/OpenAI-provider compatibility PR. The latest commit folds in the Droid/Factory streaming and continuation compatibility work on top of the original fc_* fix.

Validation

  • git diff --check
  • uv run ruff check ccproxy/plugins/codex/plugin.py ccproxy/plugins/codex/config.py ccproxy/plugins/codex/adapter.py ccproxy/plugins/codex/routes.py ccproxy/plugins/codex/responses_state.py ccproxy/llms/models/openai.py ccproxy/llms/streaming/accumulators.py ccproxy/llms/formatters/common/identifiers.py ccproxy/streaming/deferred.py ccproxy/streaming/buffer.py ccproxy/streaming/errors.py tests/plugins/codex/integration/test_codex_basic.py tests/plugins/codex/unit/test_adapter.py tests/plugins/codex/unit/test_responses_state.py tests/unit/llms/streaming/test_accumulators.py tests/unit/llms/test_openai_responses_request_models.py tests/unit/streaming/test_buffer_parse_responses.py
  • uv run ruff format --check ccproxy/plugins/codex/plugin.py ccproxy/plugins/codex/config.py ccproxy/plugins/codex/adapter.py ccproxy/plugins/codex/routes.py ccproxy/plugins/codex/responses_state.py ccproxy/llms/models/openai.py ccproxy/llms/streaming/accumulators.py ccproxy/llms/formatters/common/identifiers.py ccproxy/streaming/deferred.py ccproxy/streaming/buffer.py ccproxy/streaming/errors.py tests/plugins/codex/integration/test_codex_basic.py tests/plugins/codex/unit/test_adapter.py tests/plugins/codex/unit/test_responses_state.py tests/unit/llms/streaming/test_accumulators.py tests/unit/llms/test_openai_responses_request_models.py tests/unit/streaming/test_buffer_parse_responses.py
  • uv run pytest tests/plugins/codex/unit/test_adapter.py tests/plugins/codex/unit/test_responses_state.py tests/unit/llms/test_openai_responses_request_models.py tests/unit/streaming/test_buffer_parse_responses.py tests/unit/llms/streaming/test_accumulators.py tests/plugins/codex/integration/test_codex_basic.py -q - 58 passed
  • uv run pytest tests/plugins/codex/unit/test_adapter.py tests/plugins/codex/unit/test_routes.py tests/plugins/codex/unit/test_responses_state.py tests/plugins/codex/integration/test_codex_basic.py tests/plugins/codex/integration/test_codex_websocket.py tests/unit/streaming/test_buffer_parse_responses.py tests/unit/llms/streaming/test_accumulators.py -q - 67 passed
  • uv run pytest tests/plugins/codex tests/unit/streaming/test_buffer_parse_responses.py tests/unit/llms/streaming/test_accumulators.py tests/integration/test_streaming_converters.py -q - 91 passed, 1 existing Pydantic deprecation warning
  • uv run mypy ccproxy/plugins/codex/adapter.py ccproxy/plugins/codex/routes.py ccproxy/plugins/codex/responses_state.py ccproxy/plugins/codex/plugin.py ccproxy/plugins/codex/config.py ccproxy/llms/models/openai.py ccproxy/llms/streaming/accumulators.py ccproxy/llms/formatters/common/identifiers.py ccproxy/streaming/deferred.py ccproxy/streaming/buffer.py ccproxy/streaming/errors.py tests/plugins/codex/integration/test_codex_basic.py tests/plugins/codex/unit/test_adapter.py tests/plugins/codex/unit/test_responses_state.py tests/unit/llms/streaming/test_accumulators.py tests/unit/llms/test_openai_responses_request_models.py tests/unit/streaming/test_buffer_parse_responses.py - success, 17 source files
  • ./Taskfile check - all checks passed; mypy success on 592 source files; 616 files already formatted

Live local smoke

  • Local ccproxy /health returned pass after restart.
  • Live curl verified streaming /responses, /v1/responses, and /codex/v1/responses with stream_options.include_usage.
  • Live curl verified streamed function tool call + previous_response_id continuation, with fc_* item IDs and call_* call IDs.
  • Live curl verified unknown previous_response_id returns OpenAI-style JSON error body.
  • Droid/Factory target passed:
droid exec \
--cwd /Users/smhanan/Projects/inline \
-m custom:ccproxy-chatgpt-5.5-0 \
--auto medium \
'Use the Execute tool to run exactly: pwd && git branch --show-current. Then reply beginning GPT_DROID_EXEC_OK.'

Output began with GPT_DROID_EXEC_OK and returned /Users/smhanan/Projects/inline plus branch codex/inline-typora-repair.

@saxyguy81
saxyguy81 marked this pull request as ready for review May 10, 2026 07:10
@saxyguy81

Copy link
Copy Markdown
Author

Coordination note for reviewers:

This PR is intentionally parallel to #64 and #66.

There is no intended dependency between these PRs. They can be reviewed and merged independently; if one lands first, I will rebase the others only if GitHub reports a conflict.

@saxyguy81saxyguy81 changed the title fix(responses): normalize function call item idsfix(responses): support OpenAI Responses tool loopsMay 11, 2026
Sign up for freeto 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.

1 participant

@saxyguy81
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

fix(responses): support OpenAI Responses tool loops - #65

Open
saxyguy81 wants to merge 2 commits into
CaddyGlow:mainfrom
saxyguy81:codex/responses-function-call-ids
Open

fix(responses): support OpenAI Responses tool loops#65
saxyguy81 wants to merge 2 commits into
CaddyGlow:mainfrom
saxyguy81:codex/responses-function-call-ids

Conversation

@saxyguy81

@saxyguy81saxyguy81 commented May 10, 2026

Copy link
Copy Markdown

Summary

  • Normalize OpenAI Responses function_call item IDs to fc_* while preserving call_id as call_*.
  • Add true OpenAI-compatible Responses route coverage for /responses, /v1/responses, and /codex/v1/responses.
  • Add local previous_response_id continuation state for Codex Responses tool loops, scoped by client auth context with TTL/max-entry cleanup.
  • Accept Droid/Factory Responses streaming fields such as stream_options.include_usage, plus Droid-sent prompt_cache_retention and safety_identifier, while stripping Codex-backend-unsupported fields before upstream dispatch.
  • Preserve valid Responses SSE for streaming tool calls, including response.function_call_arguments.*.item_id == fc_*, completed function_call.id == fc_*, and function_call.call_id == call_*.
  • Normalize provider/FastAPI 4xx failures into OpenAI-style JSON error envelopes so clients see rejected parameters instead of empty bodies.
  • Rebuild sparse Codex response.completed payloads from streamed output items when the final upstream completed event omits output.

This remains parallel to #64; it is the Responses/OpenAI-provider compatibility PR. The latest commit folds in the Droid/Factory streaming and continuation compatibility work on top of the original fc_* fix.

Validation

  • git diff --check
  • uv run ruff check ccproxy/plugins/codex/plugin.py ccproxy/plugins/codex/config.py ccproxy/plugins/codex/adapter.py ccproxy/plugins/codex/routes.py ccproxy/plugins/codex/responses_state.py ccproxy/llms/models/openai.py ccproxy/llms/streaming/accumulators.py ccproxy/llms/formatters/common/identifiers.py ccproxy/streaming/deferred.py ccproxy/streaming/buffer.py ccproxy/streaming/errors.py tests/plugins/codex/integration/test_codex_basic.py tests/plugins/codex/unit/test_adapter.py tests/plugins/codex/unit/test_responses_state.py tests/unit/llms/streaming/test_accumulators.py tests/unit/llms/test_openai_responses_request_models.py tests/unit/streaming/test_buffer_parse_responses.py
  • uv run ruff format --check ccproxy/plugins/codex/plugin.py ccproxy/plugins/codex/config.py ccproxy/plugins/codex/adapter.py ccproxy/plugins/codex/routes.py ccproxy/plugins/codex/responses_state.py ccproxy/llms/models/openai.py ccproxy/llms/streaming/accumulators.py ccproxy/llms/formatters/common/identifiers.py ccproxy/streaming/deferred.py ccproxy/streaming/buffer.py ccproxy/streaming/errors.py tests/plugins/codex/integration/test_codex_basic.py tests/plugins/codex/unit/test_adapter.py tests/plugins/codex/unit/test_responses_state.py tests/unit/llms/streaming/test_accumulators.py tests/unit/llms/test_openai_responses_request_models.py tests/unit/streaming/test_buffer_parse_responses.py
  • uv run pytest tests/plugins/codex/unit/test_adapter.py tests/plugins/codex/unit/test_responses_state.py tests/unit/llms/test_openai_responses_request_models.py tests/unit/streaming/test_buffer_parse_responses.py tests/unit/llms/streaming/test_accumulators.py tests/plugins/codex/integration/test_codex_basic.py -q - 58 passed
  • uv run pytest tests/plugins/codex/unit/test_adapter.py tests/plugins/codex/unit/test_routes.py tests/plugins/codex/unit/test_responses_state.py tests/plugins/codex/integration/test_codex_basic.py tests/plugins/codex/integration/test_codex_websocket.py tests/unit/streaming/test_buffer_parse_responses.py tests/unit/llms/streaming/test_accumulators.py -q - 67 passed
  • uv run pytest tests/plugins/codex tests/unit/streaming/test_buffer_parse_responses.py tests/unit/llms/streaming/test_accumulators.py tests/integration/test_streaming_converters.py -q - 91 passed, 1 existing Pydantic deprecation warning
  • uv run mypy ccproxy/plugins/codex/adapter.py ccproxy/plugins/codex/routes.py ccproxy/plugins/codex/responses_state.py ccproxy/plugins/codex/plugin.py ccproxy/plugins/codex/config.py ccproxy/llms/models/openai.py ccproxy/llms/streaming/accumulators.py ccproxy/llms/formatters/common/identifiers.py ccproxy/streaming/deferred.py ccproxy/streaming/buffer.py ccproxy/streaming/errors.py tests/plugins/codex/integration/test_codex_basic.py tests/plugins/codex/unit/test_adapter.py tests/plugins/codex/unit/test_responses_state.py tests/unit/llms/streaming/test_accumulators.py tests/unit/llms/test_openai_responses_request_models.py tests/unit/streaming/test_buffer_parse_responses.py - success, 17 source files
  • ./Taskfile check - all checks passed; mypy success on 592 source files; 616 files already formatted

Live local smoke

  • Local ccproxy /health returned pass after restart.
  • Live curl verified streaming /responses, /v1/responses, and /codex/v1/responses with stream_options.include_usage.
  • Live curl verified streamed function tool call + previous_response_id continuation, with fc_* item IDs and call_* call IDs.
  • Live curl verified unknown previous_response_id returns OpenAI-style JSON error body.
  • Droid/Factory target passed:
droid exec \
--cwd /Users/smhanan/Projects/inline \
-m custom:ccproxy-chatgpt-5.5-0 \
--auto medium \
'Use the Execute tool to run exactly: pwd && git branch --show-current. Then reply beginning GPT_DROID_EXEC_OK.'

Output began with GPT_DROID_EXEC_OK and returned /Users/smhanan/Projects/inline plus branch codex/inline-typora-repair.

@saxyguy81
saxyguy81 marked this pull request as ready for review May 10, 2026 07:10
@saxyguy81

Copy link
Copy Markdown
Author

Coordination note for reviewers:

This PR is intentionally parallel to #64 and #66.

There is no intended dependency between these PRs. They can be reviewed and merged independently; if one lands first, I will rebase the others only if GitHub reports a conflict.

@saxyguy81saxyguy81 changed the title fix(responses): normalize function call item idsfix(responses): support OpenAI Responses tool loopsMay 11, 2026
Sign up for freeto 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.

1 participant

@saxyguy81
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

fix(responses): support OpenAI Responses tool loops - #65

Open
saxyguy81 wants to merge 2 commits into
CaddyGlow:mainfrom
saxyguy81:codex/responses-function-call-ids
Open

fix(responses): support OpenAI Responses tool loops#65
saxyguy81 wants to merge 2 commits into
CaddyGlow:mainfrom
saxyguy81:codex/responses-function-call-ids

Conversation

@saxyguy81

@saxyguy81saxyguy81 commented May 10, 2026

Copy link
Copy Markdown

Summary

  • Normalize OpenAI Responses function_call item IDs to fc_* while preserving call_id as call_*.
  • Add true OpenAI-compatible Responses route coverage for /responses, /v1/responses, and /codex/v1/responses.
  • Add local previous_response_id continuation state for Codex Responses tool loops, scoped by client auth context with TTL/max-entry cleanup.
  • Accept Droid/Factory Responses streaming fields such as stream_options.include_usage, plus Droid-sent prompt_cache_retention and safety_identifier, while stripping Codex-backend-unsupported fields before upstream dispatch.
  • Preserve valid Responses SSE for streaming tool calls, including response.function_call_arguments.*.item_id == fc_*, completed function_call.id == fc_*, and function_call.call_id == call_*.
  • Normalize provider/FastAPI 4xx failures into OpenAI-style JSON error envelopes so clients see rejected parameters instead of empty bodies.
  • Rebuild sparse Codex response.completed payloads from streamed output items when the final upstream completed event omits output.

This remains parallel to #64; it is the Responses/OpenAI-provider compatibility PR. The latest commit folds in the Droid/Factory streaming and continuation compatibility work on top of the original fc_* fix.

Validation

  • git diff --check
  • uv run ruff check ccproxy/plugins/codex/plugin.py ccproxy/plugins/codex/config.py ccproxy/plugins/codex/adapter.py ccproxy/plugins/codex/routes.py ccproxy/plugins/codex/responses_state.py ccproxy/llms/models/openai.py ccproxy/llms/streaming/accumulators.py ccproxy/llms/formatters/common/identifiers.py ccproxy/streaming/deferred.py ccproxy/streaming/buffer.py ccproxy/streaming/errors.py tests/plugins/codex/integration/test_codex_basic.py tests/plugins/codex/unit/test_adapter.py tests/plugins/codex/unit/test_responses_state.py tests/unit/llms/streaming/test_accumulators.py tests/unit/llms/test_openai_responses_request_models.py tests/unit/streaming/test_buffer_parse_responses.py
  • uv run ruff format --check ccproxy/plugins/codex/plugin.py ccproxy/plugins/codex/config.py ccproxy/plugins/codex/adapter.py ccproxy/plugins/codex/routes.py ccproxy/plugins/codex/responses_state.py ccproxy/llms/models/openai.py ccproxy/llms/streaming/accumulators.py ccproxy/llms/formatters/common/identifiers.py ccproxy/streaming/deferred.py ccproxy/streaming/buffer.py ccproxy/streaming/errors.py tests/plugins/codex/integration/test_codex_basic.py tests/plugins/codex/unit/test_adapter.py tests/plugins/codex/unit/test_responses_state.py tests/unit/llms/streaming/test_accumulators.py tests/unit/llms/test_openai_responses_request_models.py tests/unit/streaming/test_buffer_parse_responses.py
  • uv run pytest tests/plugins/codex/unit/test_adapter.py tests/plugins/codex/unit/test_responses_state.py tests/unit/llms/test_openai_responses_request_models.py tests/unit/streaming/test_buffer_parse_responses.py tests/unit/llms/streaming/test_accumulators.py tests/plugins/codex/integration/test_codex_basic.py -q - 58 passed
  • uv run pytest tests/plugins/codex/unit/test_adapter.py tests/plugins/codex/unit/test_routes.py tests/plugins/codex/unit/test_responses_state.py tests/plugins/codex/integration/test_codex_basic.py tests/plugins/codex/integration/test_codex_websocket.py tests/unit/streaming/test_buffer_parse_responses.py tests/unit/llms/streaming/test_accumulators.py -q - 67 passed
  • uv run pytest tests/plugins/codex tests/unit/streaming/test_buffer_parse_responses.py tests/unit/llms/streaming/test_accumulators.py tests/integration/test_streaming_converters.py -q - 91 passed, 1 existing Pydantic deprecation warning
  • uv run mypy ccproxy/plugins/codex/adapter.py ccproxy/plugins/codex/routes.py ccproxy/plugins/codex/responses_state.py ccproxy/plugins/codex/plugin.py ccproxy/plugins/codex/config.py ccproxy/llms/models/openai.py ccproxy/llms/streaming/accumulators.py ccproxy/llms/formatters/common/identifiers.py ccproxy/streaming/deferred.py ccproxy/streaming/buffer.py ccproxy/streaming/errors.py tests/plugins/codex/integration/test_codex_basic.py tests/plugins/codex/unit/test_adapter.py tests/plugins/codex/unit/test_responses_state.py tests/unit/llms/streaming/test_accumulators.py tests/unit/llms/test_openai_responses_request_models.py tests/unit/streaming/test_buffer_parse_responses.py - success, 17 source files
  • ./Taskfile check - all checks passed; mypy success on 592 source files; 616 files already formatted

Live local smoke

  • Local ccproxy /health returned pass after restart.
  • Live curl verified streaming /responses, /v1/responses, and /codex/v1/responses with stream_options.include_usage.
  • Live curl verified streamed function tool call + previous_response_id continuation, with fc_* item IDs and call_* call IDs.
  • Live curl verified unknown previous_response_id returns OpenAI-style JSON error body.
  • Droid/Factory target passed:
droid exec \
--cwd /Users/smhanan/Projects/inline \
-m custom:ccproxy-chatgpt-5.5-0 \
--auto medium \
'Use the Execute tool to run exactly: pwd && git branch --show-current. Then reply beginning GPT_DROID_EXEC_OK.'

Output began with GPT_DROID_EXEC_OK and returned /Users/smhanan/Projects/inline plus branch codex/inline-typora-repair.

@saxyguy81
saxyguy81 marked this pull request as ready for review May 10, 2026 07:10
@saxyguy81

Copy link
Copy Markdown
Author

Coordination note for reviewers:

This PR is intentionally parallel to #64 and #66.

There is no intended dependency between these PRs. They can be reviewed and merged independently; if one lands first, I will rebase the others only if GitHub reports a conflict.

@saxyguy81saxyguy81 changed the title fix(responses): normalize function call item idsfix(responses): support OpenAI Responses tool loopsMay 11, 2026
Sign up for freeto 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.

1 participant

@saxyguy81
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

fix(responses): support OpenAI Responses tool loops - #65

Open
saxyguy81 wants to merge 2 commits into
CaddyGlow:mainfrom
saxyguy81:codex/responses-function-call-ids
Open

fix(responses): support OpenAI Responses tool loops#65
saxyguy81 wants to merge 2 commits into
CaddyGlow:mainfrom
saxyguy81:codex/responses-function-call-ids

Conversation

@saxyguy81

@saxyguy81saxyguy81 commented May 10, 2026

Copy link
Copy Markdown

Summary

  • Normalize OpenAI Responses function_call item IDs to fc_* while preserving call_id as call_*.
  • Add true OpenAI-compatible Responses route coverage for /responses, /v1/responses, and /codex/v1/responses.
  • Add local previous_response_id continuation state for Codex Responses tool loops, scoped by client auth context with TTL/max-entry cleanup.
  • Accept Droid/Factory Responses streaming fields such as stream_options.include_usage, plus Droid-sent prompt_cache_retention and safety_identifier, while stripping Codex-backend-unsupported fields before upstream dispatch.
  • Preserve valid Responses SSE for streaming tool calls, including response.function_call_arguments.*.item_id == fc_*, completed function_call.id == fc_*, and function_call.call_id == call_*.
  • Normalize provider/FastAPI 4xx failures into OpenAI-style JSON error envelopes so clients see rejected parameters instead of empty bodies.
  • Rebuild sparse Codex response.completed payloads from streamed output items when the final upstream completed event omits output.

This remains parallel to #64; it is the Responses/OpenAI-provider compatibility PR. The latest commit folds in the Droid/Factory streaming and continuation compatibility work on top of the original fc_* fix.

Validation

  • git diff --check
  • uv run ruff check ccproxy/plugins/codex/plugin.py ccproxy/plugins/codex/config.py ccproxy/plugins/codex/adapter.py ccproxy/plugins/codex/routes.py ccproxy/plugins/codex/responses_state.py ccproxy/llms/models/openai.py ccproxy/llms/streaming/accumulators.py ccproxy/llms/formatters/common/identifiers.py ccproxy/streaming/deferred.py ccproxy/streaming/buffer.py ccproxy/streaming/errors.py tests/plugins/codex/integration/test_codex_basic.py tests/plugins/codex/unit/test_adapter.py tests/plugins/codex/unit/test_responses_state.py tests/unit/llms/streaming/test_accumulators.py tests/unit/llms/test_openai_responses_request_models.py tests/unit/streaming/test_buffer_parse_responses.py
  • uv run ruff format --check ccproxy/plugins/codex/plugin.py ccproxy/plugins/codex/config.py ccproxy/plugins/codex/adapter.py ccproxy/plugins/codex/routes.py ccproxy/plugins/codex/responses_state.py ccproxy/llms/models/openai.py ccproxy/llms/streaming/accumulators.py ccproxy/llms/formatters/common/identifiers.py ccproxy/streaming/deferred.py ccproxy/streaming/buffer.py ccproxy/streaming/errors.py tests/plugins/codex/integration/test_codex_basic.py tests/plugins/codex/unit/test_adapter.py tests/plugins/codex/unit/test_responses_state.py tests/unit/llms/streaming/test_accumulators.py tests/unit/llms/test_openai_responses_request_models.py tests/unit/streaming/test_buffer_parse_responses.py
  • uv run pytest tests/plugins/codex/unit/test_adapter.py tests/plugins/codex/unit/test_responses_state.py tests/unit/llms/test_openai_responses_request_models.py tests/unit/streaming/test_buffer_parse_responses.py tests/unit/llms/streaming/test_accumulators.py tests/plugins/codex/integration/test_codex_basic.py -q - 58 passed
  • uv run pytest tests/plugins/codex/unit/test_adapter.py tests/plugins/codex/unit/test_routes.py tests/plugins/codex/unit/test_responses_state.py tests/plugins/codex/integration/test_codex_basic.py tests/plugins/codex/integration/test_codex_websocket.py tests/unit/streaming/test_buffer_parse_responses.py tests/unit/llms/streaming/test_accumulators.py -q - 67 passed
  • uv run pytest tests/plugins/codex tests/unit/streaming/test_buffer_parse_responses.py tests/unit/llms/streaming/test_accumulators.py tests/integration/test_streaming_converters.py -q - 91 passed, 1 existing Pydantic deprecation warning
  • uv run mypy ccproxy/plugins/codex/adapter.py ccproxy/plugins/codex/routes.py ccproxy/plugins/codex/responses_state.py ccproxy/plugins/codex/plugin.py ccproxy/plugins/codex/config.py ccproxy/llms/models/openai.py ccproxy/llms/streaming/accumulators.py ccproxy/llms/formatters/common/identifiers.py ccproxy/streaming/deferred.py ccproxy/streaming/buffer.py ccproxy/streaming/errors.py tests/plugins/codex/integration/test_codex_basic.py tests/plugins/codex/unit/test_adapter.py tests/plugins/codex/unit/test_responses_state.py tests/unit/llms/streaming/test_accumulators.py tests/unit/llms/test_openai_responses_request_models.py tests/unit/streaming/test_buffer_parse_responses.py - success, 17 source files
  • ./Taskfile check - all checks passed; mypy success on 592 source files; 616 files already formatted

Live local smoke

  • Local ccproxy /health returned pass after restart.
  • Live curl verified streaming /responses, /v1/responses, and /codex/v1/responses with stream_options.include_usage.
  • Live curl verified streamed function tool call + previous_response_id continuation, with fc_* item IDs and call_* call IDs.
  • Live curl verified unknown previous_response_id returns OpenAI-style JSON error body.
  • Droid/Factory target passed:
droid exec \
--cwd /Users/smhanan/Projects/inline \
-m custom:ccproxy-chatgpt-5.5-0 \
--auto medium \
'Use the Execute tool to run exactly: pwd && git branch --show-current. Then reply beginning GPT_DROID_EXEC_OK.'

Output began with GPT_DROID_EXEC_OK and returned /Users/smhanan/Projects/inline plus branch codex/inline-typora-repair.

@saxyguy81
saxyguy81 marked this pull request as ready for review May 10, 2026 07:10
@saxyguy81

Copy link
Copy Markdown
Author

Coordination note for reviewers:

This PR is intentionally parallel to #64 and #66.

There is no intended dependency between these PRs. They can be reviewed and merged independently; if one lands first, I will rebase the others only if GitHub reports a conflict.

@saxyguy81saxyguy81 changed the title fix(responses): normalize function call item idsfix(responses): support OpenAI Responses tool loopsMay 11, 2026
Sign up for freeto 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.

1 participant

@saxyguy81
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

fix(responses): support OpenAI Responses tool loops - #65

Open
saxyguy81 wants to merge 2 commits into
CaddyGlow:mainfrom
saxyguy81:codex/responses-function-call-ids
Open

fix(responses): support OpenAI Responses tool loops#65
saxyguy81 wants to merge 2 commits into
CaddyGlow:mainfrom
saxyguy81:codex/responses-function-call-ids

Conversation

@saxyguy81

@saxyguy81saxyguy81 commented May 10, 2026

Copy link
Copy Markdown

Summary

  • Normalize OpenAI Responses function_call item IDs to fc_* while preserving call_id as call_*.
  • Add true OpenAI-compatible Responses route coverage for /responses, /v1/responses, and /codex/v1/responses.
  • Add local previous_response_id continuation state for Codex Responses tool loops, scoped by client auth context with TTL/max-entry cleanup.
  • Accept Droid/Factory Responses streaming fields such as stream_options.include_usage, plus Droid-sent prompt_cache_retention and safety_identifier, while stripping Codex-backend-unsupported fields before upstream dispatch.
  • Preserve valid Responses SSE for streaming tool calls, including response.function_call_arguments.*.item_id == fc_*, completed function_call.id == fc_*, and function_call.call_id == call_*.
  • Normalize provider/FastAPI 4xx failures into OpenAI-style JSON error envelopes so clients see rejected parameters instead of empty bodies.
  • Rebuild sparse Codex response.completed payloads from streamed output items when the final upstream completed event omits output.

This remains parallel to #64; it is the Responses/OpenAI-provider compatibility PR. The latest commit folds in the Droid/Factory streaming and continuation compatibility work on top of the original fc_* fix.

Validation

  • git diff --check
  • uv run ruff check ccproxy/plugins/codex/plugin.py ccproxy/plugins/codex/config.py ccproxy/plugins/codex/adapter.py ccproxy/plugins/codex/routes.py ccproxy/plugins/codex/responses_state.py ccproxy/llms/models/openai.py ccproxy/llms/streaming/accumulators.py ccproxy/llms/formatters/common/identifiers.py ccproxy/streaming/deferred.py ccproxy/streaming/buffer.py ccproxy/streaming/errors.py tests/plugins/codex/integration/test_codex_basic.py tests/plugins/codex/unit/test_adapter.py tests/plugins/codex/unit/test_responses_state.py tests/unit/llms/streaming/test_accumulators.py tests/unit/llms/test_openai_responses_request_models.py tests/unit/streaming/test_buffer_parse_responses.py
  • uv run ruff format --check ccproxy/plugins/codex/plugin.py ccproxy/plugins/codex/config.py ccproxy/plugins/codex/adapter.py ccproxy/plugins/codex/routes.py ccproxy/plugins/codex/responses_state.py ccproxy/llms/models/openai.py ccproxy/llms/streaming/accumulators.py ccproxy/llms/formatters/common/identifiers.py ccproxy/streaming/deferred.py ccproxy/streaming/buffer.py ccproxy/streaming/errors.py tests/plugins/codex/integration/test_codex_basic.py tests/plugins/codex/unit/test_adapter.py tests/plugins/codex/unit/test_responses_state.py tests/unit/llms/streaming/test_accumulators.py tests/unit/llms/test_openai_responses_request_models.py tests/unit/streaming/test_buffer_parse_responses.py
  • uv run pytest tests/plugins/codex/unit/test_adapter.py tests/plugins/codex/unit/test_responses_state.py tests/unit/llms/test_openai_responses_request_models.py tests/unit/streaming/test_buffer_parse_responses.py tests/unit/llms/streaming/test_accumulators.py tests/plugins/codex/integration/test_codex_basic.py -q - 58 passed
  • uv run pytest tests/plugins/codex/unit/test_adapter.py tests/plugins/codex/unit/test_routes.py tests/plugins/codex/unit/test_responses_state.py tests/plugins/codex/integration/test_codex_basic.py tests/plugins/codex/integration/test_codex_websocket.py tests/unit/streaming/test_buffer_parse_responses.py tests/unit/llms/streaming/test_accumulators.py -q - 67 passed
  • uv run pytest tests/plugins/codex tests/unit/streaming/test_buffer_parse_responses.py tests/unit/llms/streaming/test_accumulators.py tests/integration/test_streaming_converters.py -q - 91 passed, 1 existing Pydantic deprecation warning
  • uv run mypy ccproxy/plugins/codex/adapter.py ccproxy/plugins/codex/routes.py ccproxy/plugins/codex/responses_state.py ccproxy/plugins/codex/plugin.py ccproxy/plugins/codex/config.py ccproxy/llms/models/openai.py ccproxy/llms/streaming/accumulators.py ccproxy/llms/formatters/common/identifiers.py ccproxy/streaming/deferred.py ccproxy/streaming/buffer.py ccproxy/streaming/errors.py tests/plugins/codex/integration/test_codex_basic.py tests/plugins/codex/unit/test_adapter.py tests/plugins/codex/unit/test_responses_state.py tests/unit/llms/streaming/test_accumulators.py tests/unit/llms/test_openai_responses_request_models.py tests/unit/streaming/test_buffer_parse_responses.py - success, 17 source files
  • ./Taskfile check - all checks passed; mypy success on 592 source files; 616 files already formatted

Live local smoke

  • Local ccproxy /health returned pass after restart.
  • Live curl verified streaming /responses, /v1/responses, and /codex/v1/responses with stream_options.include_usage.
  • Live curl verified streamed function tool call + previous_response_id continuation, with fc_* item IDs and call_* call IDs.
  • Live curl verified unknown previous_response_id returns OpenAI-style JSON error body.
  • Droid/Factory target passed:
droid exec \
--cwd /Users/smhanan/Projects/inline \
-m custom:ccproxy-chatgpt-5.5-0 \
--auto medium \
'Use the Execute tool to run exactly: pwd && git branch --show-current. Then reply beginning GPT_DROID_EXEC_OK.'

Output began with GPT_DROID_EXEC_OK and returned /Users/smhanan/Projects/inline plus branch codex/inline-typora-repair.

@saxyguy81
saxyguy81 marked this pull request as ready for review May 10, 2026 07:10
@saxyguy81

Copy link
Copy Markdown
Author

Coordination note for reviewers:

This PR is intentionally parallel to #64 and #66.

There is no intended dependency between these PRs. They can be reviewed and merged independently; if one lands first, I will rebase the others only if GitHub reports a conflict.

@saxyguy81saxyguy81 changed the title fix(responses): normalize function call item idsfix(responses): support OpenAI Responses tool loopsMay 11, 2026
Sign up for freeto 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.

1 participant

@saxyguy81
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

fix(responses): support OpenAI Responses tool loops - #65

Open
saxyguy81 wants to merge 2 commits into
CaddyGlow:mainfrom
saxyguy81:codex/responses-function-call-ids
Open

fix(responses): support OpenAI Responses tool loops#65
saxyguy81 wants to merge 2 commits into
CaddyGlow:mainfrom
saxyguy81:codex/responses-function-call-ids

Conversation

@saxyguy81

@saxyguy81saxyguy81 commented May 10, 2026

Copy link
Copy Markdown

Summary

  • Normalize OpenAI Responses function_call item IDs to fc_* while preserving call_id as call_*.
  • Add true OpenAI-compatible Responses route coverage for /responses, /v1/responses, and /codex/v1/responses.
  • Add local previous_response_id continuation state for Codex Responses tool loops, scoped by client auth context with TTL/max-entry cleanup.
  • Accept Droid/Factory Responses streaming fields such as stream_options.include_usage, plus Droid-sent prompt_cache_retention and safety_identifier, while stripping Codex-backend-unsupported fields before upstream dispatch.
  • Preserve valid Responses SSE for streaming tool calls, including response.function_call_arguments.*.item_id == fc_*, completed function_call.id == fc_*, and function_call.call_id == call_*.
  • Normalize provider/FastAPI 4xx failures into OpenAI-style JSON error envelopes so clients see rejected parameters instead of empty bodies.
  • Rebuild sparse Codex response.completed payloads from streamed output items when the final upstream completed event omits output.

This remains parallel to #64; it is the Responses/OpenAI-provider compatibility PR. The latest commit folds in the Droid/Factory streaming and continuation compatibility work on top of the original fc_* fix.

Validation

  • git diff --check
  • uv run ruff check ccproxy/plugins/codex/plugin.py ccproxy/plugins/codex/config.py ccproxy/plugins/codex/adapter.py ccproxy/plugins/codex/routes.py ccproxy/plugins/codex/responses_state.py ccproxy/llms/models/openai.py ccproxy/llms/streaming/accumulators.py ccproxy/llms/formatters/common/identifiers.py ccproxy/streaming/deferred.py ccproxy/streaming/buffer.py ccproxy/streaming/errors.py tests/plugins/codex/integration/test_codex_basic.py tests/plugins/codex/unit/test_adapter.py tests/plugins/codex/unit/test_responses_state.py tests/unit/llms/streaming/test_accumulators.py tests/unit/llms/test_openai_responses_request_models.py tests/unit/streaming/test_buffer_parse_responses.py
  • uv run ruff format --check ccproxy/plugins/codex/plugin.py ccproxy/plugins/codex/config.py ccproxy/plugins/codex/adapter.py ccproxy/plugins/codex/routes.py ccproxy/plugins/codex/responses_state.py ccproxy/llms/models/openai.py ccproxy/llms/streaming/accumulators.py ccproxy/llms/formatters/common/identifiers.py ccproxy/streaming/deferred.py ccproxy/streaming/buffer.py ccproxy/streaming/errors.py tests/plugins/codex/integration/test_codex_basic.py tests/plugins/codex/unit/test_adapter.py tests/plugins/codex/unit/test_responses_state.py tests/unit/llms/streaming/test_accumulators.py tests/unit/llms/test_openai_responses_request_models.py tests/unit/streaming/test_buffer_parse_responses.py
  • uv run pytest tests/plugins/codex/unit/test_adapter.py tests/plugins/codex/unit/test_responses_state.py tests/unit/llms/test_openai_responses_request_models.py tests/unit/streaming/test_buffer_parse_responses.py tests/unit/llms/streaming/test_accumulators.py tests/plugins/codex/integration/test_codex_basic.py -q - 58 passed
  • uv run pytest tests/plugins/codex/unit/test_adapter.py tests/plugins/codex/unit/test_routes.py tests/plugins/codex/unit/test_responses_state.py tests/plugins/codex/integration/test_codex_basic.py tests/plugins/codex/integration/test_codex_websocket.py tests/unit/streaming/test_buffer_parse_responses.py tests/unit/llms/streaming/test_accumulators.py -q - 67 passed
  • uv run pytest tests/plugins/codex tests/unit/streaming/test_buffer_parse_responses.py tests/unit/llms/streaming/test_accumulators.py tests/integration/test_streaming_converters.py -q - 91 passed, 1 existing Pydantic deprecation warning
  • uv run mypy ccproxy/plugins/codex/adapter.py ccproxy/plugins/codex/routes.py ccproxy/plugins/codex/responses_state.py ccproxy/plugins/codex/plugin.py ccproxy/plugins/codex/config.py ccproxy/llms/models/openai.py ccproxy/llms/streaming/accumulators.py ccproxy/llms/formatters/common/identifiers.py ccproxy/streaming/deferred.py ccproxy/streaming/buffer.py ccproxy/streaming/errors.py tests/plugins/codex/integration/test_codex_basic.py tests/plugins/codex/unit/test_adapter.py tests/plugins/codex/unit/test_responses_state.py tests/unit/llms/streaming/test_accumulators.py tests/unit/llms/test_openai_responses_request_models.py tests/unit/streaming/test_buffer_parse_responses.py - success, 17 source files
  • ./Taskfile check - all checks passed; mypy success on 592 source files; 616 files already formatted

Live local smoke

  • Local ccproxy /health returned pass after restart.
  • Live curl verified streaming /responses, /v1/responses, and /codex/v1/responses with stream_options.include_usage.
  • Live curl verified streamed function tool call + previous_response_id continuation, with fc_* item IDs and call_* call IDs.
  • Live curl verified unknown previous_response_id returns OpenAI-style JSON error body.
  • Droid/Factory target passed:
droid exec \
--cwd /Users/smhanan/Projects/inline \
-m custom:ccproxy-chatgpt-5.5-0 \
--auto medium \
'Use the Execute tool to run exactly: pwd && git branch --show-current. Then reply beginning GPT_DROID_EXEC_OK.'

Output began with GPT_DROID_EXEC_OK and returned /Users/smhanan/Projects/inline plus branch codex/inline-typora-repair.

@saxyguy81
saxyguy81 marked this pull request as ready for review May 10, 2026 07:10
@saxyguy81

Copy link
Copy Markdown
Author

Coordination note for reviewers:

This PR is intentionally parallel to #64 and #66.

There is no intended dependency between these PRs. They can be reviewed and merged independently; if one lands first, I will rebase the others only if GitHub reports a conflict.

@saxyguy81saxyguy81 changed the title fix(responses): normalize function call item idsfix(responses): support OpenAI Responses tool loopsMay 11, 2026
Sign up for freeto 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.

1 participant

@saxyguy81
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

fix(responses): support OpenAI Responses tool loops - #65

Open
saxyguy81 wants to merge 2 commits into
CaddyGlow:mainfrom
saxyguy81:codex/responses-function-call-ids
Open

fix(responses): support OpenAI Responses tool loops#65
saxyguy81 wants to merge 2 commits into
CaddyGlow:mainfrom
saxyguy81:codex/responses-function-call-ids

Conversation

@saxyguy81

@saxyguy81saxyguy81 commented May 10, 2026

Copy link
Copy Markdown

Summary

  • Normalize OpenAI Responses function_call item IDs to fc_* while preserving call_id as call_*.
  • Add true OpenAI-compatible Responses route coverage for /responses, /v1/responses, and /codex/v1/responses.
  • Add local previous_response_id continuation state for Codex Responses tool loops, scoped by client auth context with TTL/max-entry cleanup.
  • Accept Droid/Factory Responses streaming fields such as stream_options.include_usage, plus Droid-sent prompt_cache_retention and safety_identifier, while stripping Codex-backend-unsupported fields before upstream dispatch.
  • Preserve valid Responses SSE for streaming tool calls, including response.function_call_arguments.*.item_id == fc_*, completed function_call.id == fc_*, and function_call.call_id == call_*.
  • Normalize provider/FastAPI 4xx failures into OpenAI-style JSON error envelopes so clients see rejected parameters instead of empty bodies.
  • Rebuild sparse Codex response.completed payloads from streamed output items when the final upstream completed event omits output.

This remains parallel to #64; it is the Responses/OpenAI-provider compatibility PR. The latest commit folds in the Droid/Factory streaming and continuation compatibility work on top of the original fc_* fix.

Validation

  • git diff --check
  • uv run ruff check ccproxy/plugins/codex/plugin.py ccproxy/plugins/codex/config.py ccproxy/plugins/codex/adapter.py ccproxy/plugins/codex/routes.py ccproxy/plugins/codex/responses_state.py ccproxy/llms/models/openai.py ccproxy/llms/streaming/accumulators.py ccproxy/llms/formatters/common/identifiers.py ccproxy/streaming/deferred.py ccproxy/streaming/buffer.py ccproxy/streaming/errors.py tests/plugins/codex/integration/test_codex_basic.py tests/plugins/codex/unit/test_adapter.py tests/plugins/codex/unit/test_responses_state.py tests/unit/llms/streaming/test_accumulators.py tests/unit/llms/test_openai_responses_request_models.py tests/unit/streaming/test_buffer_parse_responses.py
  • uv run ruff format --check ccproxy/plugins/codex/plugin.py ccproxy/plugins/codex/config.py ccproxy/plugins/codex/adapter.py ccproxy/plugins/codex/routes.py ccproxy/plugins/codex/responses_state.py ccproxy/llms/models/openai.py ccproxy/llms/streaming/accumulators.py ccproxy/llms/formatters/common/identifiers.py ccproxy/streaming/deferred.py ccproxy/streaming/buffer.py ccproxy/streaming/errors.py tests/plugins/codex/integration/test_codex_basic.py tests/plugins/codex/unit/test_adapter.py tests/plugins/codex/unit/test_responses_state.py tests/unit/llms/streaming/test_accumulators.py tests/unit/llms/test_openai_responses_request_models.py tests/unit/streaming/test_buffer_parse_responses.py
  • uv run pytest tests/plugins/codex/unit/test_adapter.py tests/plugins/codex/unit/test_responses_state.py tests/unit/llms/test_openai_responses_request_models.py tests/unit/streaming/test_buffer_parse_responses.py tests/unit/llms/streaming/test_accumulators.py tests/plugins/codex/integration/test_codex_basic.py -q - 58 passed
  • uv run pytest tests/plugins/codex/unit/test_adapter.py tests/plugins/codex/unit/test_routes.py tests/plugins/codex/unit/test_responses_state.py tests/plugins/codex/integration/test_codex_basic.py tests/plugins/codex/integration/test_codex_websocket.py tests/unit/streaming/test_buffer_parse_responses.py tests/unit/llms/streaming/test_accumulators.py -q - 67 passed
  • uv run pytest tests/plugins/codex tests/unit/streaming/test_buffer_parse_responses.py tests/unit/llms/streaming/test_accumulators.py tests/integration/test_streaming_converters.py -q - 91 passed, 1 existing Pydantic deprecation warning
  • uv run mypy ccproxy/plugins/codex/adapter.py ccproxy/plugins/codex/routes.py ccproxy/plugins/codex/responses_state.py ccproxy/plugins/codex/plugin.py ccproxy/plugins/codex/config.py ccproxy/llms/models/openai.py ccproxy/llms/streaming/accumulators.py ccproxy/llms/formatters/common/identifiers.py ccproxy/streaming/deferred.py ccproxy/streaming/buffer.py ccproxy/streaming/errors.py tests/plugins/codex/integration/test_codex_basic.py tests/plugins/codex/unit/test_adapter.py tests/plugins/codex/unit/test_responses_state.py tests/unit/llms/streaming/test_accumulators.py tests/unit/llms/test_openai_responses_request_models.py tests/unit/streaming/test_buffer_parse_responses.py - success, 17 source files
  • ./Taskfile check - all checks passed; mypy success on 592 source files; 616 files already formatted

Live local smoke

  • Local ccproxy /health returned pass after restart.
  • Live curl verified streaming /responses, /v1/responses, and /codex/v1/responses with stream_options.include_usage.
  • Live curl verified streamed function tool call + previous_response_id continuation, with fc_* item IDs and call_* call IDs.
  • Live curl verified unknown previous_response_id returns OpenAI-style JSON error body.
  • Droid/Factory target passed:
droid exec \
--cwd /Users/smhanan/Projects/inline \
-m custom:ccproxy-chatgpt-5.5-0 \
--auto medium \
'Use the Execute tool to run exactly: pwd && git branch --show-current. Then reply beginning GPT_DROID_EXEC_OK.'

Output began with GPT_DROID_EXEC_OK and returned /Users/smhanan/Projects/inline plus branch codex/inline-typora-repair.

@saxyguy81
saxyguy81 marked this pull request as ready for review May 10, 2026 07:10
@saxyguy81

Copy link
Copy Markdown
Author

Coordination note for reviewers:

This PR is intentionally parallel to #64 and #66.

There is no intended dependency between these PRs. They can be reviewed and merged independently; if one lands first, I will rebase the others only if GitHub reports a conflict.

@saxyguy81saxyguy81 changed the title fix(responses): normalize function call item idsfix(responses): support OpenAI Responses tool loopsMay 11, 2026
Sign up for freeto 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.

1 participant

@saxyguy81
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

fix(responses): support OpenAI Responses tool loops - #65

Open
saxyguy81 wants to merge 2 commits into
CaddyGlow:mainfrom
saxyguy81:codex/responses-function-call-ids
Open

fix(responses): support OpenAI Responses tool loops#65
saxyguy81 wants to merge 2 commits into
CaddyGlow:mainfrom
saxyguy81:codex/responses-function-call-ids

Conversation

@saxyguy81

@saxyguy81saxyguy81 commented May 10, 2026

Copy link
Copy Markdown

Summary

  • Normalize OpenAI Responses function_call item IDs to fc_* while preserving call_id as call_*.
  • Add true OpenAI-compatible Responses route coverage for /responses, /v1/responses, and /codex/v1/responses.
  • Add local previous_response_id continuation state for Codex Responses tool loops, scoped by client auth context with TTL/max-entry cleanup.
  • Accept Droid/Factory Responses streaming fields such as stream_options.include_usage, plus Droid-sent prompt_cache_retention and safety_identifier, while stripping Codex-backend-unsupported fields before upstream dispatch.
  • Preserve valid Responses SSE for streaming tool calls, including response.function_call_arguments.*.item_id == fc_*, completed function_call.id == fc_*, and function_call.call_id == call_*.
  • Normalize provider/FastAPI 4xx failures into OpenAI-style JSON error envelopes so clients see rejected parameters instead of empty bodies.
  • Rebuild sparse Codex response.completed payloads from streamed output items when the final upstream completed event omits output.

This remains parallel to #64; it is the Responses/OpenAI-provider compatibility PR. The latest commit folds in the Droid/Factory streaming and continuation compatibility work on top of the original fc_* fix.

Validation

  • git diff --check
  • uv run ruff check ccproxy/plugins/codex/plugin.py ccproxy/plugins/codex/config.py ccproxy/plugins/codex/adapter.py ccproxy/plugins/codex/routes.py ccproxy/plugins/codex/responses_state.py ccproxy/llms/models/openai.py ccproxy/llms/streaming/accumulators.py ccproxy/llms/formatters/common/identifiers.py ccproxy/streaming/deferred.py ccproxy/streaming/buffer.py ccproxy/streaming/errors.py tests/plugins/codex/integration/test_codex_basic.py tests/plugins/codex/unit/test_adapter.py tests/plugins/codex/unit/test_responses_state.py tests/unit/llms/streaming/test_accumulators.py tests/unit/llms/test_openai_responses_request_models.py tests/unit/streaming/test_buffer_parse_responses.py
  • uv run ruff format --check ccproxy/plugins/codex/plugin.py ccproxy/plugins/codex/config.py ccproxy/plugins/codex/adapter.py ccproxy/plugins/codex/routes.py ccproxy/plugins/codex/responses_state.py ccproxy/llms/models/openai.py ccproxy/llms/streaming/accumulators.py ccproxy/llms/formatters/common/identifiers.py ccproxy/streaming/deferred.py ccproxy/streaming/buffer.py ccproxy/streaming/errors.py tests/plugins/codex/integration/test_codex_basic.py tests/plugins/codex/unit/test_adapter.py tests/plugins/codex/unit/test_responses_state.py tests/unit/llms/streaming/test_accumulators.py tests/unit/llms/test_openai_responses_request_models.py tests/unit/streaming/test_buffer_parse_responses.py
  • uv run pytest tests/plugins/codex/unit/test_adapter.py tests/plugins/codex/unit/test_responses_state.py tests/unit/llms/test_openai_responses_request_models.py tests/unit/streaming/test_buffer_parse_responses.py tests/unit/llms/streaming/test_accumulators.py tests/plugins/codex/integration/test_codex_basic.py -q - 58 passed
  • uv run pytest tests/plugins/codex/unit/test_adapter.py tests/plugins/codex/unit/test_routes.py tests/plugins/codex/unit/test_responses_state.py tests/plugins/codex/integration/test_codex_basic.py tests/plugins/codex/integration/test_codex_websocket.py tests/unit/streaming/test_buffer_parse_responses.py tests/unit/llms/streaming/test_accumulators.py -q - 67 passed
  • uv run pytest tests/plugins/codex tests/unit/streaming/test_buffer_parse_responses.py tests/unit/llms/streaming/test_accumulators.py tests/integration/test_streaming_converters.py -q - 91 passed, 1 existing Pydantic deprecation warning
  • uv run mypy ccproxy/plugins/codex/adapter.py ccproxy/plugins/codex/routes.py ccproxy/plugins/codex/responses_state.py ccproxy/plugins/codex/plugin.py ccproxy/plugins/codex/config.py ccproxy/llms/models/openai.py ccproxy/llms/streaming/accumulators.py ccproxy/llms/formatters/common/identifiers.py ccproxy/streaming/deferred.py ccproxy/streaming/buffer.py ccproxy/streaming/errors.py tests/plugins/codex/integration/test_codex_basic.py tests/plugins/codex/unit/test_adapter.py tests/plugins/codex/unit/test_responses_state.py tests/unit/llms/streaming/test_accumulators.py tests/unit/llms/test_openai_responses_request_models.py tests/unit/streaming/test_buffer_parse_responses.py - success, 17 source files
  • ./Taskfile check - all checks passed; mypy success on 592 source files; 616 files already formatted

Live local smoke

  • Local ccproxy /health returned pass after restart.
  • Live curl verified streaming /responses, /v1/responses, and /codex/v1/responses with stream_options.include_usage.
  • Live curl verified streamed function tool call + previous_response_id continuation, with fc_* item IDs and call_* call IDs.
  • Live curl verified unknown previous_response_id returns OpenAI-style JSON error body.
  • Droid/Factory target passed:
droid exec \
--cwd /Users/smhanan/Projects/inline \
-m custom:ccproxy-chatgpt-5.5-0 \
--auto medium \
'Use the Execute tool to run exactly: pwd && git branch --show-current. Then reply beginning GPT_DROID_EXEC_OK.'

Output began with GPT_DROID_EXEC_OK and returned /Users/smhanan/Projects/inline plus branch codex/inline-typora-repair.

@saxyguy81
saxyguy81 marked this pull request as ready for review May 10, 2026 07:10
@saxyguy81

Copy link
Copy Markdown
Author

Coordination note for reviewers:

This PR is intentionally parallel to #64 and #66.

There is no intended dependency between these PRs. They can be reviewed and merged independently; if one lands first, I will rebase the others only if GitHub reports a conflict.

@saxyguy81saxyguy81 changed the title fix(responses): normalize function call item idsfix(responses): support OpenAI Responses tool loopsMay 11, 2026
Sign up for freeto 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.

1 participant

@saxyguy81