Skip to content

Conversation

@OriNachum
Copy link
Collaborator

Chat completions validated

Getting for Responses:
2025-06-14 00:19:07,537 - api_adapter - ERROR - Error in proxy endpoint: cannot access local variable 'response' where it is not associated with a value
INFO: 127.0.0.1:33502 - "POST /chat/completions HTTP/1.1" 500 Internal Server Error
2025-06-14 00:19:08,392 - api_adapter - ERROR - Error in proxy endpoint: cannot access local variable 'response' where it is not associated with a value
INFO: 127.0.0.1:33502 - "POST /chat/completions HTTP/1.1" 500 Internal Server Error

Looks like it broke - I'll fix before merge

logger.error(f"Error during chat completions stream proxy: {e}")
yield f"data: {json.dumps({'error': str(e)})}\n\n".encode()

return StreamingResponse(stream_proxy(), media_type="text/event-stream")

Check warning

Code scanning / CodeQL

Information exposure through an exception Medium

Stack trace information
flows to this location and may be exposed to an external user.

Copilot Autofix

AI 7 months ago

To fix the issue, the exception message (str(e)) should be replaced with a generic error message when sending responses to external users. The detailed exception information should continue to be logged server-side for debugging purposes. This ensures that sensitive information is not exposed while maintaining the ability to diagnose issues internally.

Specifically:

  1. Replace str(e) in the yield statement on line 146 with a generic error message, such as "An internal error occurred during streaming.".
  2. Ensure that the detailed exception information (e) is logged using logger.error() for internal debugging.
Suggested changeset 1
src/open_responses_server/chat_completions_service.py

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/src/open_responses_server/chat_completions_service.py b/src/open_responses_server/chat_completions_service.py
--- a/src/open_responses_server/chat_completions_service.py
+++ b/src/open_responses_server/chat_completions_service.py
@@ -145,3 +145,3 @@
                         logger.error(f"Error during chat completions stream proxy: {e}")
-                        yield f"data: {json.dumps({'error': str(e)})}\n\n".encode()
+                        yield f"data: {json.dumps({'error': 'An internal error occurred during streaming.'})}\n\n".encode()
 
@@ -152,3 +152,3 @@
             async def error_stream():
-                yield f"data: {json.dumps({'error': str(e)})}\n\n".encode()
+                yield f"data: {json.dumps({'error': 'An internal error occurred during streaming.'})}\n\n".encode()
             return StreamingResponse(error_stream(), media_type="text/event-stream", status_code=500)
EOF
@@ -145,3 +145,3 @@
logger.error(f"Error during chat completions stream proxy: {e}")
yield f"data: {json.dumps({'error': str(e)})}\n\n".encode()
yield f"data: {json.dumps({'error': 'An internal error occurred during streaming.'})}\n\n".encode()

@@ -152,3 +152,3 @@
async def error_stream():
yield f"data: {json.dumps({'error': str(e)})}\n\n".encode()
yield f"data: {json.dumps({'error': 'An internal error occurred during streaming.'})}\n\n".encode()
return StreamingResponse(error_stream(), media_type="text/event-stream", status_code=500)
Copilot is powered by AI and may make mistakes. Always verify output.
yield f"data: {json.dumps({'type': 'error', 'error': {'message': str(e)}})}\n\n"

return StreamingResponse(
stream_response(),

Check warning

Code scanning / CodeQL

Information exposure through an exception Medium

Stack trace information
flows to this location and may be exposed to an external user.

Copilot Autofix

AI 7 months ago

To fix the issue, the exception message (str(e)) should not be included in the response sent to the user. Instead, a generic error message should be returned, while the detailed stack trace is logged on the server for debugging purposes. This approach ensures that sensitive information is not exposed to external users while still allowing developers to diagnose issues.

Steps to implement the fix:

  1. Replace the str(e) in the response payload on line 255 with a generic error message, such as "An internal error has occurred.".
  2. Ensure that the detailed exception message is logged using logger.error for debugging purposes.
  3. Verify that the fix does not alter the application's functionality beyond securing the error handling.
Suggested changeset 1
src/open_responses_server/api_controller.py

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/src/open_responses_server/api_controller.py b/src/open_responses_server/api_controller.py
--- a/src/open_responses_server/api_controller.py
+++ b/src/open_responses_server/api_controller.py
@@ -254,3 +254,3 @@
                     logger.error(f"Error in stream_response: {str(e)}")
-                    yield f"data: {json.dumps({'type': 'error', 'error': {'message': str(e)}})}\n\n"
+                    yield f"data: {json.dumps({'type': 'error', 'error': {'message': 'An internal error has occurred.'}})}\n\n"
             
@@ -268,3 +268,3 @@
             status_code=500,
-            detail=f"Error processing request: {str(e)}"
+            detail="An internal error has occurred."
         )
EOF
@@ -254,3 +254,3 @@
logger.error(f"Error in stream_response: {str(e)}")
yield f"data: {json.dumps({'type': 'error', 'error': {'message': str(e)}})}\n\n"
yield f"data: {json.dumps({'type': 'error', 'error': {'message': 'An internal error has occurred.'}})}\n\n"

@@ -268,3 +268,3 @@
status_code=500,
detail=f"Error processing request: {str(e)}"
detail="An internal error has occurred."
)
Copilot is powered by AI and may make mistakes. Always verify output.
@sonarqubecloud
Copy link

Quality Gate Failed Quality Gate failed

Failed conditions
0.0% Coverage on New Code (required ≥ 80%)
3.5% Duplication on New Code (required ≤ 3%)
C Reliability Rating on New Code (required ≥ A)
C Security Rating on New Code (required ≥ A)

See analysis details on SonarQube Cloud

Catch issues before they fail your Quality Gate with our IDE extension SonarQube for IDE

@OriNachum OriNachum marked this pull request as ready for review June 18, 2025 10:14
@OriNachum OriNachum requested a review from Copilot June 18, 2025 10:14
@OriNachum OriNachum merged commit c4c91ba into main Jun 18, 2025
7 of 11 checks passed
@OriNachum OriNachum deleted the task/refactor-2 branch June 18, 2025 10:17
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull Request Overview

Refactors the monolithic server into a modular structure, reorganizes startup scripts and Docker entrypoint, and updates test tooling to use the new /v1/chat/completions path.

  • Modularized core logic into common/ (config, llm_client, mcp_manager), models/, and dedicated service modules.
  • Updated CLI, shell scripts, start.py, and Dockerfile to launch the new server_entrypoint.
  • Consolidated Pydantic models in responses_models.py and streamlined tool injection and execution.

Reviewed Changes

Copilot reviewed 25 out of 25 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
test_tools/call_chat_completions_proxy_mcp.sh Updated proxy script to call /v1/chat/completions and capture output
test_tools/call_chat_completions_proxy.sh Changed endpoint path to /v1/chat/completions
start.py Added a bash startup script (invokes CLI)
src/open_responses_server/server_entrypoint.py New entrypoint module for running the FastAPI app via uvicorn
src/open_responses_server/models/responses_models.py Consolidated and added all response-related Pydantic models
src/open_responses_server/common/mcp_manager.py Introduced MCPManager and MCPServer for tool caching/execution
src/open_responses_server/common/llm_client.py Added LLMClient for managing the async HTTP client
src/open_responses_server/common/config.py Centralized environment variables and logging setup
src/open_responses_server/cli.py Updated imports and uvicorn invocation to use the new entrypoint
src/open_responses_server/chat_completions_service.py New service to handle streaming and non‐streaming chat completions with tool support
docs/testing-guide.md Updated test guidance to mock server_entrypoint
Dockerfile Updated container entrypoint to launch server_entrypoint:app
Comments suppressed due to low confidence (2)

@@ -0,0 +1,3 @@
#!/bin/bash

uv run ./src/open_responses_server/cli.py start No newline at end of file
Copy link

Copilot AI Jun 18, 2025

Choose a reason for hiding this comment

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

The command uv run is not a standard invocation and will likely fail. Consider replacing it with uvicorn open_responses_server.cli:app --reload or invoking the CLI via python -m open_responses_server.cli start.

Suggested change
uv run ./src/open_responses_server/cli.py start
python -m open_responses_server.cli start

Copilot uses AI. Check for mistakes.
@@ -0,0 +1,3 @@
#!/bin/bash
Copy link

Copilot AI Jun 18, 2025

Choose a reason for hiding this comment

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

[nitpick] This file uses a bash shebang but has a .py extension. Either rename it to .sh or switch the shebang to a Python interpreter (e.g., #!/usr/bin/env python3).

Copilot uses AI. Check for mistakes.
arguments = json.loads(function_call.get("arguments", "{}"))
result = await mcp_manager.execute_mcp_tool(tool_name, arguments)
logger.info(f"Executed tool {tool_name} with result: {result}")
#result is serlized as: "meta=None content=[TextContent(type='text', text="[{'name': 'listings'}]", annotations=None)] isError=False"
Copy link

Copilot AI Jun 18, 2025

Choose a reason for hiding this comment

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

Typo in comment: serlized should be serialized.

Suggested change
#result is serlized as: "meta=None content=[TextContent(type='text', text="[{'name': 'listings'}]", annotations=None)] isError=False"
#result is serialized as: "meta=None content=[TextContent(type='text', text="[{'name': 'listings'}]", annotations=None)] isError=False"

Copilot uses AI. Check for mistakes.
Comment on lines +151 to +163
for server in self.mcp_servers:
try:
# No need to list tools again, just try to execute
# A better approach would be to cache which server has which tool
# For now, we'll keep the logic of checking before calling
tools = await server.list_tools()
if any(tool.get("name") == tool_name for tool in tools):
logger.info(f"Executing tool {tool_name} on server {server.name}")
return await server.execute_tool(tool_name, arguments)
except Exception:
logger.warning(f"Error checking/executing tool on server {server.name}: {traceback.format_exc()}")
continue
raise RuntimeError(f"Tool '{tool_name}' not found on any active MCP server")
Copy link

Copilot AI Jun 18, 2025

Choose a reason for hiding this comment

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

Calling list_tools() on every execute_mcp_tool invocation can be expensive. Consider caching server-to-tool mappings to avoid repeated RPC calls.

Suggested change
for server in self.mcp_servers:
try:
# No need to list tools again, just try to execute
# A better approach would be to cache which server has which tool
# For now, we'll keep the logic of checking before calling
tools = await server.list_tools()
if any(tool.get("name") == tool_name for tool in tools):
logger.info(f"Executing tool {tool_name} on server {server.name}")
return await server.execute_tool(tool_name, arguments)
except Exception:
logger.warning(f"Error checking/executing tool on server {server.name}: {traceback.format_exc()}")
continue
raise RuntimeError(f"Tool '{tool_name}' not found on any active MCP server")
server = self.tool_to_server_map.get(tool_name)
if server:
try:
logger.info(f"Executing tool {tool_name} on server {server.name}")
return await server.execute_tool(tool_name, arguments)
except Exception:
logger.warning(f"Error executing tool {tool_name} on server {server.name}: {traceback.format_exc()}")
raise RuntimeError(f"Error executing tool '{tool_name}' on server {server.name}")
raise RuntimeError(f"Tool '{tool_name}' not found on any active MCP server")
raise RuntimeError(f"Tool '{tool_name}' not found on any active MCP server")

Copilot uses AI. Check for mistakes.
async def shutdown_mcp_servers(self):
"""Clean up all MCP servers on shutdown."""
if self._refresh_task:
self._refresh_task.cancel()
Copy link

Copilot AI Jun 18, 2025

Choose a reason for hiding this comment

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

Cancelling the _refresh_task without awaiting it may leave the background loop running. Consider awaiting task termination or handling asyncio.CancelledError for a clean shutdown.

Suggested change
self._refresh_task.cancel()
self._refresh_task.cancel()
try:
await self._refresh_task
except asyncio.CancelledError:
logger.info("Refresh task was cancelled cleanly.")

Copilot uses AI. Check for mistakes.
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