1- """OpenAI Responses and Chat Completions clients for the gatekeeper."""
1+ """OpenAI Responses API client for the gatekeeper."""
22
33import os
44
55from typing import Any
6- from urllib .parse import urlparse
76
87from linux_mcp_server .config import CONFIG
98from linux_mcp_server .config import ReasoningEffort
109from linux_mcp_server .gatekeeper .http_utils import DEFAULT_TIMEOUT_SECONDS
11- from linux_mcp_server .gatekeeper .http_utils import GatekeeperHTTPError
1210from linux_mcp_server .gatekeeper .http_utils import normalize_model_id
1311from linux_mcp_server .gatekeeper .http_utils import post_json
14- from linux_mcp_server .gatekeeper .schema import openai_response_format
1512from linux_mcp_server .gatekeeper .schema import openai_text_format
16- from linux_mcp_server .gatekeeper .usage import extract_openai_chat_completions_usage
1713from linux_mcp_server .gatekeeper .usage import extract_openai_responses_usage
1814from linux_mcp_server .models import GatekeeperCompletion
1915
@@ -43,26 +39,6 @@ def _get_openai_base_url() -> str:
4339 return (configured or os .environ .get ("OPENAI_API_BASE" ) or OPENAI_DEFAULT_BASE_URL ).rstrip ("/" )
4440
4541
46- def _prefers_openai_chat_completions (base_url : str ) -> bool :
47- """Hosts known to expose only Chat Completions, not the Responses API."""
48- path = urlparse (base_url ).path or ""
49- return "/endpoints/openapi" in path
50-
51-
52- def _openai_template_kwargs () -> dict [str , Any ]:
53- if CONFIG .gatekeeper .openai is None :
54- return {}
55- return CONFIG .gatekeeper .openai .template_kwargs
56-
57-
58- def _apply_chat_completions_extras (body : dict [str , Any ]) -> dict [str , Any ]:
59- """Merge template_kwargs into Chat Completions bodies (llama.cpp, etc.)."""
60- template_kwargs = _openai_template_kwargs ()
61- if template_kwargs :
62- body ["chat_template_kwargs" ] = template_kwargs
63- return body
64-
65-
6642def _build_responses_body (prompt : str , * , max_tokens : int ) -> dict [str , Any ]:
6743 body : dict [str , Any ] = {
6844 "model" : normalize_model_id (CONFIG .gatekeeper .model or "" ),
@@ -79,21 +55,6 @@ def _build_responses_body(prompt: str, *, max_tokens: int) -> dict[str, Any]:
7955 return body
8056
8157
82- def build_chat_completions_body (prompt : str , * , max_tokens : int ) -> dict [str , Any ]:
83- body : dict [str , Any ] = {
84- "model" : normalize_model_id (CONFIG .gatekeeper .model or "" ),
85- "messages" : [{"role" : "user" , "content" : prompt }],
86- "max_completion_tokens" : max_tokens ,
87- "temperature" : CONFIG .gatekeeper .temperature ,
88- }
89- if CONFIG .gatekeeper .structured_output :
90- body ["response_format" ] = openai_response_format ()
91- reasoning_effort = CONFIG .gatekeeper .reasoning_effort
92- if reasoning_effort is not None :
93- body ["reasoning_effort" ] = reasoning_effort .value
94- return _apply_chat_completions_extras (body )
95-
96-
9758def _extract_responses_text (response : dict [str , Any ]) -> str :
9859 output_text = response .get ("output_text" )
9960 if isinstance (output_text , str ) and output_text .strip ():
@@ -114,15 +75,6 @@ def _extract_responses_text(response: dict[str, Any]) -> str:
11475 return "" .join (chunks ).strip ()
11576
11677
117- def extract_chat_completions_text (response : dict [str , Any ]) -> str :
118- choices = response .get ("choices" , [])
119- if not choices :
120- return ""
121- message = choices [0 ].get ("message" , {})
122- content = message .get ("content" )
123- return (content or "" ).strip () if isinstance (content , str ) else ""
124-
125-
12678async def complete_openai (
12779 prompt : str , * , max_tokens : int , timeout : int = DEFAULT_TIMEOUT_SECONDS
12880) -> GatekeeperCompletion :
@@ -131,37 +83,16 @@ async def complete_openai(
13183 ** _openai_auth_headers (),
13284 "Content-Type" : "application/json" ,
13385 }
134-
135- # Try the Responses API first, falling back to Chat Completions if it's not available.
136- if not _prefers_openai_chat_completions (base_url ):
137- try :
138- response = await post_json (
139- provider = "openai" ,
140- url = f"{ base_url } /responses" ,
141- headers = headers ,
142- body = _build_responses_body (prompt , max_tokens = max_tokens ),
143- timeout = timeout ,
144- )
145- usage = extract_openai_responses_usage (response )
146- return GatekeeperCompletion (
147- text = _extract_responses_text (response ),
148- prompt_tokens = usage .input_tokens ,
149- completion_tokens = usage .output_tokens ,
150- )
151- except GatekeeperHTTPError as exc :
152- if exc .status_code not in {404 , 405 }:
153- raise
154-
15586 response = await post_json (
15687 provider = "openai" ,
157- url = f"{ base_url } /chat/completions " ,
88+ url = f"{ base_url } /responses " ,
15889 headers = headers ,
159- body = build_chat_completions_body (prompt , max_tokens = max_tokens ),
90+ body = _build_responses_body (prompt , max_tokens = max_tokens ),
16091 timeout = timeout ,
16192 )
162- usage = extract_openai_chat_completions_usage (response )
93+ usage = extract_openai_responses_usage (response )
16394 return GatekeeperCompletion (
164- text = extract_chat_completions_text (response ),
95+ text = _extract_responses_text (response ),
16596 prompt_tokens = usage .input_tokens ,
16697 completion_tokens = usage .output_tokens ,
16798 )
0 commit comments