feat: Microsoft Agent Framework compatibility and bypass mode#46
feat: Microsoft Agent Framework compatibility and bypass mode#46
Conversation
Add MSAF-compatible mock response handling and bypass mode infrastructure: - Add OpenAI Responses format support to mock handler with proper SSE events - Add prompt text extraction for context-aware mock responses - Add format-aware mock adapter with target format detection - Add bypass mode to plugin factory with MockAdapter fallback - Add inject_detection_payload config toggle for generic API usage - Add openai_thinking_xml streaming configuration - Add format adapter streaming configuration support - Add MSAF integration and real library tests - Add bypass mode factory tests
There was a problem hiding this comment.
Pull request overview
Adds Microsoft Agent Framework–compatible behavior to the Codex proxy by making mock/bypass mode format-aware (OpenAI Chat vs OpenAI Responses vs Anthropic), adding a bypass-mode adapter fallback in provider factories, and propagating the openai_thinking_xml streaming setting through format adapters.
Changes:
- Implement OpenAI Responses-format mock streaming (SSE event sequence) and endpoint/format-chain based target format detection in the mock adapter.
- Add provider-factory bypass mode that returns
MockAdapter(with clear warning/error handling) plus coverage for bypass behavior. - Add
inject_detection_payloadCodex config toggle and propagateopenai_thinking_xmlvia format-adapter ContextVar, with unit/integration tests for MSAF-style workflows.
Reviewed changes
Copilot reviewed 19 out of 20 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| uv.lock | Updates locked dependencies (notably mcp bump) to support new functionality. |
| tests/unit/services/test_mock_adapter.py | Extends mock-adapter tests for target format resolution and prompt extraction call path. |
| tests/unit/services/mocking/test_mock_handler.py | Adds prompt extraction tests and validates OpenAI Responses SSE event sequences. |
| tests/unit/llms/test_llms_streaming_settings.py | Verifies openai_thinking_xml propagation and ContextVar task isolation. |
| tests/unit/core/test_provider_factory_bypass.py | Adds unit coverage for provider factory bypass mode behavior. |
| tests/plugins/codex/unit/test_adapter.py | Adds coverage for disabling detection payload injection and preserving user reasoning. |
| tests/plugins/codex/integration/test_msaf_real_library.py | Adds multi-step MSAF-style workflow tests through the proxy (httpx). |
| tests/plugins/codex/integration/test_msaf_compat.py | Adds MSAF compatibility integration tests (no CLI injection, no thinking XML). |
| tests/plugins/codex/integration/test_codex_basic.py | Adds integration validation for bypass-mode Responses streaming events. |
| ccproxy/services/mocking/mock_handler.py | Implements format-aware mock generation + OpenAI Responses streaming SSE events + prompt extraction. |
| ccproxy/services/factories.py | Wires OpenAI Responses mock adapter and propagates openai_thinking_xml into adapters/registry. |
| ccproxy/services/adapters/mock_adapter.py | Adds format inference (format_chain / endpoint) and passes prompt/format into mock handler. |
| ccproxy/services/adapters/format_adapter.py | Adds configure_streaming() and sets openai_thinking_xml via ContextVar during conversions. |
| ccproxy/plugins/codex/config.py | Introduces inject_detection_payload configuration option. |
| ccproxy/plugins/codex/adapter.py | Skips detection/template injection when configured; normalizes input and strips unsupported params. |
| ccproxy/llms/formatters/openai_to_openai/streams.py | Respects ContextVar-controlled openai_thinking_xml when emitting thinking segments. |
| ccproxy/llms/formatters/openai_to_openai/responses.py | Respects ContextVar-controlled openai_thinking_xml when merging/serializing thinking. |
| ccproxy/llms/formatters/context.py | Adds ContextVar helpers for openai_thinking_xml. |
| ccproxy/core/plugins/factories.py | Adds bypass-mode factory path returning MockAdapter and logging a lifecycle warning. |
| .ccproxy.codex.msaf.toml.example | Adds example config for MSAF usage (incl. disabling detection injection and thinking XML). |
Comments suppressed due to low confidence (1)
ccproxy/services/adapters/mock_adapter.py:116
handle_request()always creates a newRequestContextwith a hard-codedrequest_id("mock-request"), even whenrequest.state.contextalready exists. This makes request/stream IDs collide across requests in bypass mode and breaks log correlation (the upstream middleware/decorators generate per-request UUIDs). Consider reusingrequest.state.contextwhen present, otherwise generate a unique request_id (e.g., fromX-Request-IDheader oruuid4()).
# Get endpoint from context or request URL
endpoint = request.url.path
if hasattr(request.state, "context"):
ctx = request.state.context
endpoint = ctx.metadata.get("endpoint", request.url.path)
target_format = self._resolve_target_format(request, endpoint)
model = "unknown"
try:
body_json = json.loads(body) if body else {}
model = body_json.get("model", "unknown")
except json.JSONDecodeError:
pass
except UnicodeDecodeError:
pass
except Exception as e:
logger.debug("stream_flag_extraction_error", error=str(e))
# Create request context
ctx = RequestContext(
request_id="mock-request",
start_time=time.perf_counter(),
logger=structlog.get_logger(__name__),
)
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| @@ -64,7 +107,6 @@ async def handle_request( | |||
| pass | |||
| except Exception as e: | |||
| logger.debug("stream_flag_extraction_error", error=str(e)) | |||
There was a problem hiding this comment.
In the exception handler when parsing the request body for model, the log event name is stream_flag_extraction_error, which is misleading (this block isn't extracting the stream flag). Renaming the event (e.g., model_extraction_error or body_parse_error) would make logs easier to interpret.
| logger.debug("stream_flag_extraction_error", error=str(e)) | |
| logger.debug("model_extraction_error", error=str(e)) |
Summary
inject_detection_payloadconfig toggle for generic API usageReplaces #44 (auto-closed when #43 merged). PR 2 of 3 from original #41.
Test plan