codex-api-proxy 0.1.2__py3-none-any.whl → 0.1.4__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,3 +1,3 @@
1
1
  """OpenAI-compatible HTTP proxy backed by Codex/OpenAI credentials."""
2
2
 
3
- __version__ = "0.1.2"
3
+ __version__ = "0.1.4"
@@ -46,7 +46,7 @@ def chat_to_responses_request(
46
46
  {
47
47
  "type": "message",
48
48
  "role": "user",
49
- "content": [{"type": "input_text", "text": text}],
49
+ "content": message_content_to_response_content(message.get("content", "")),
50
50
  }
51
51
  )
52
52
  continue
@@ -147,6 +147,69 @@ def message_content_to_text(content: object) -> str:
147
147
  return "".join(parts)
148
148
 
149
149
 
150
+ def message_content_to_response_content(content: object) -> list[dict[str, str]]:
151
+ if isinstance(content, str):
152
+ return [{"type": "input_text", "text": content}]
153
+ if not isinstance(content, list):
154
+ return [{"type": "input_text", "text": ""}]
155
+
156
+ parts: list[dict[str, str]] = []
157
+ for item in content:
158
+ if isinstance(item, str):
159
+ parts.append({"type": "input_text", "text": item})
160
+ continue
161
+ if not isinstance(item, dict):
162
+ continue
163
+
164
+ part_type = item.get("type")
165
+ text = item.get("text")
166
+ if part_type in {"text", "input_text"} and isinstance(text, str):
167
+ parts.append({"type": "input_text", "text": text})
168
+ continue
169
+
170
+ image_part = _image_content_part(item)
171
+ if image_part:
172
+ parts.append(image_part)
173
+
174
+ return parts or [{"type": "input_text", "text": ""}]
175
+
176
+
177
+ def _image_content_part(item: dict) -> dict[str, str] | None:
178
+ part_type = item.get("type")
179
+ if part_type == "image_url":
180
+ image_url = item.get("image_url")
181
+ url: object
182
+ detail: object = item.get("detail")
183
+ if isinstance(image_url, dict):
184
+ url = image_url.get("url")
185
+ detail = image_url.get("detail", detail)
186
+ else:
187
+ url = image_url
188
+ if not isinstance(url, str):
189
+ return None
190
+ part = {"type": "input_image", "image_url": url}
191
+ if isinstance(detail, str):
192
+ part["detail"] = detail
193
+ return part
194
+
195
+ if part_type == "input_image":
196
+ part = {"type": "input_image"}
197
+ image_url = item.get("image_url")
198
+ file_id = item.get("file_id")
199
+ detail = item.get("detail")
200
+ if isinstance(image_url, str):
201
+ part["image_url"] = image_url
202
+ if isinstance(file_id, str):
203
+ part["file_id"] = file_id
204
+ if isinstance(detail, str):
205
+ part["detail"] = detail
206
+ if "image_url" not in part and "file_id" not in part:
207
+ return None
208
+ return part
209
+
210
+ return None
211
+
212
+
150
213
  def extract_output_text(response: dict) -> str:
151
214
  chunks: list[str] = []
152
215
  for item in response.get("output", []):
codex_api_proxy/config.py CHANGED
@@ -76,6 +76,14 @@ def upstream_https_proxy(explicit_proxy: str | None = None) -> str | None:
76
76
  return DEFAULT_HTTPS_PROXY
77
77
 
78
78
 
79
+ def upstream_ssl_verify() -> bool | str:
80
+ for key in ("CODEX_API_PROXY_CA_BUNDLE", "REQUESTS_CA_BUNDLE", "SSL_CERT_FILE"):
81
+ value = os.environ.get(key, "").strip()
82
+ if value:
83
+ return value
84
+ return True
85
+
86
+
79
87
  def upstream_originator() -> str:
80
88
  override = os.environ.get("CODEX_INTERNAL_ORIGINATOR_OVERRIDE", "").strip()
81
89
  return override or DEFAULT_ORIGINATOR
@@ -95,6 +103,7 @@ def upstream_client(explicit_proxy: str | None = None) -> httpx.AsyncClient:
95
103
  return httpx.AsyncClient(
96
104
  timeout=DEFAULT_TIMEOUT,
97
105
  proxy=upstream_https_proxy(explicit_proxy),
106
+ verify=upstream_ssl_verify(),
98
107
  trust_env=False,
99
108
  )
100
109
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: codex-api-proxy
3
- Version: 0.1.2
3
+ Version: 0.1.4
4
4
  Summary: Local OpenAI-compatible HTTP proxy backed by Codex/OpenAI credentials
5
5
  Author: codex-api-proxy contributors
6
6
  License-Expression: MIT
@@ -102,6 +102,20 @@ You can also set the upstream proxy explicitly at startup:
102
102
  codex-api-proxy start --proxy=http://127.0.0.1:8118
103
103
  ```
104
104
 
105
+ If the upstream connection passes through a corporate TLS proxy or another endpoint
106
+ using a private/self-signed certificate chain, point the proxy at a CA bundle that
107
+ trusts that chain:
108
+
109
+ - `CODEX_API_PROXY_CA_BUNDLE`
110
+ - `REQUESTS_CA_BUNDLE`
111
+ - `SSL_CERT_FILE`
112
+
113
+ For example:
114
+
115
+ ```bash
116
+ CODEX_API_PROXY_CA_BUNDLE=/path/to/internal-ca-bundle.pem codex-api-proxy start
117
+ ```
118
+
105
119
  ## Run
106
120
 
107
121
  Start in the background:
@@ -164,6 +178,7 @@ Environment variables for the local server:
164
178
  - `CODEX_PROXY_PORT`
165
179
  - `CODEX_PROXY_API_KEY`
166
180
  - `CODEX_API_PROXY_HTTPS_PROXY`
181
+ - `CODEX_API_PROXY_CA_BUNDLE`
167
182
  - `CODEX_PROXY_LOG_LEVEL`
168
183
 
169
184
  Token refresh compatibility variables:
@@ -209,6 +224,32 @@ curl -sS http://127.0.0.1:8765/v1/responses \
209
224
  -d '{"model":"gpt-5.5","input":"Reply with exactly: pong"}'
210
225
  ```
211
226
 
227
+ Image input through Chat Completions:
228
+
229
+ ```bash
230
+ BASE64_IMAGE=$(base64 < image.jpg)
231
+ curl -sS http://127.0.0.1:8765/v1/chat/completions \
232
+ -H 'Content-Type: application/json' \
233
+ -d '{
234
+ "model": "gpt-5.5",
235
+ "messages": [{
236
+ "role": "user",
237
+ "content": [
238
+ {"type": "text", "text": "What is in this image?"},
239
+ {
240
+ "type": "image_url",
241
+ "image_url": {
242
+ "url": "data:image/jpeg;base64,'"$BASE64_IMAGE"'",
243
+ "detail": "high"
244
+ }
245
+ }
246
+ ]
247
+ }]
248
+ }'
249
+ ```
250
+
251
+ When using ChatGPT Codex credentials, Chat Completions image parts are converted to Responses API `input_image` parts. `/v1/responses` requests are passed through unchanged.
252
+
212
253
  When `--api-key` is configured:
213
254
 
214
255
  ```bash
@@ -0,0 +1,13 @@
1
+ codex_api_proxy/__init__.py,sha256=kST1dtci895ogqNSin9iq_TpqSfvECZ13FXqGJJOdZY,94
2
+ codex_api_proxy/auth.py,sha256=TG3dyhMFwRHgUyPyWMe-zbuuhIRAz_mva0oH2J_OfQg,10894
3
+ codex_api_proxy/chat_completions.py,sha256=NoS2bRwCXUmydWF-KobT5OXk-nMJSjHknje4XMk9q18,11057
4
+ codex_api_proxy/cli.py,sha256=ljaXci-vjfhNNxJi9ej7rB-iijZViHriA-xW19PW-D4,15806
5
+ codex_api_proxy/config.py,sha256=Uy_XIzk22ChBLwHPY8mf3frf6H1p2XKkozjqniqSkJc,3775
6
+ codex_api_proxy/main.py,sha256=9xBRqFV9TsLhAALhNBvBU1ci5ulCCs29qvEMmJwudts,23224
7
+ codex_api_proxy/models.py,sha256=x8JH44SBC3nC4F_OeZMLnNM3q2qWqBFxHQJOmZ-7adk,3411
8
+ codex_api_proxy/sse_utils.py,sha256=Tkn63Edv9l8rHjZPtx65U9PDkVV4cgq7hQS_FYwdIoc,1064
9
+ codex_api_proxy-0.1.4.dist-info/METADATA,sha256=1s13zqbAqw_bohRn3-G4ak51HSneemqgZrpiU7zaOyk,7650
10
+ codex_api_proxy-0.1.4.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
11
+ codex_api_proxy-0.1.4.dist-info/entry_points.txt,sha256=a28sZIZgr1ImRSTMZ2ba8uDSUTJcgLtr1CgVLDdTgjE,61
12
+ codex_api_proxy-0.1.4.dist-info/top_level.txt,sha256=hksltGPYaEc8y1_UkFJjXOdNQRLhHIR8-8NLTpLMqrw,16
13
+ codex_api_proxy-0.1.4.dist-info/RECORD,,
@@ -1,13 +0,0 @@
1
- codex_api_proxy/__init__.py,sha256=PqpkJejAPDKHipW2iDcxHullWyc9padUv2u52f3AC3A,94
2
- codex_api_proxy/auth.py,sha256=TG3dyhMFwRHgUyPyWMe-zbuuhIRAz_mva0oH2J_OfQg,10894
3
- codex_api_proxy/chat_completions.py,sha256=AykPAzkyVOzBcT1prepjzvACO4-Njk-LJF3G_oWvQ5U,8971
4
- codex_api_proxy/cli.py,sha256=ljaXci-vjfhNNxJi9ej7rB-iijZViHriA-xW19PW-D4,15806
5
- codex_api_proxy/config.py,sha256=4fVHv8zsg8brpioOA9w_YZXCpuiFGizYeatlya4VLc4,3502
6
- codex_api_proxy/main.py,sha256=9xBRqFV9TsLhAALhNBvBU1ci5ulCCs29qvEMmJwudts,23224
7
- codex_api_proxy/models.py,sha256=x8JH44SBC3nC4F_OeZMLnNM3q2qWqBFxHQJOmZ-7adk,3411
8
- codex_api_proxy/sse_utils.py,sha256=Tkn63Edv9l8rHjZPtx65U9PDkVV4cgq7hQS_FYwdIoc,1064
9
- codex_api_proxy-0.1.2.dist-info/METADATA,sha256=y29LnfG7dDck0seQ_si0wXOZIBqgHtj0knzntOgG0lM,6547
10
- codex_api_proxy-0.1.2.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
11
- codex_api_proxy-0.1.2.dist-info/entry_points.txt,sha256=a28sZIZgr1ImRSTMZ2ba8uDSUTJcgLtr1CgVLDdTgjE,61
12
- codex_api_proxy-0.1.2.dist-info/top_level.txt,sha256=hksltGPYaEc8y1_UkFJjXOdNQRLhHIR8-8NLTpLMqrw,16
13
- codex_api_proxy-0.1.2.dist-info/RECORD,,