codex-backend-sdk 0.2.0__tar.gz → 0.3.1__tar.gz

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.
Files changed (38) hide show
  1. {codex_backend_sdk-0.2.0 → codex_backend_sdk-0.3.1}/.gitignore +1 -0
  2. codex_backend_sdk-0.3.1/PKG-INFO +451 -0
  3. codex_backend_sdk-0.3.1/README.md +425 -0
  4. {codex_backend_sdk-0.2.0 → codex_backend_sdk-0.3.1}/codex_backend_sdk/__init__.py +13 -1
  5. codex_backend_sdk-0.3.1/codex_backend_sdk/_client.py +264 -0
  6. codex_backend_sdk-0.3.1/codex_backend_sdk/_models.py +235 -0
  7. codex_backend_sdk-0.3.1/codex_backend_sdk/_streaming.py +50 -0
  8. codex_backend_sdk-0.3.1/codex_backend_sdk/_utils.py +75 -0
  9. codex_backend_sdk-0.3.1/codex_backend_sdk/codex_client.py +56 -0
  10. codex_backend_sdk-0.3.1/codex_backend_sdk/resources/__init__.py +1 -0
  11. codex_backend_sdk-0.3.1/codex_backend_sdk/resources/codex.py +129 -0
  12. codex_backend_sdk-0.3.1/codex_backend_sdk/resources/files.py +116 -0
  13. codex_backend_sdk-0.3.1/codex_backend_sdk/resources/models.py +85 -0
  14. codex_backend_sdk-0.3.1/codex_backend_sdk/resources/openai_oauth.py +126 -0
  15. codex_backend_sdk-0.3.1/codex_backend_sdk/resources/realtime.py +63 -0
  16. codex_backend_sdk-0.3.1/codex_backend_sdk/resources/responses.py +378 -0
  17. {codex_backend_sdk-0.2.0 → codex_backend_sdk-0.3.1}/docs/backend-api.md +214 -9
  18. {codex_backend_sdk-0.2.0 → codex_backend_sdk-0.3.1}/pyproject.toml +1 -1
  19. codex_backend_sdk-0.3.1/tests/test_codex_resources.py +173 -0
  20. codex_backend_sdk-0.3.1/tests/test_files_resource.py +114 -0
  21. codex_backend_sdk-0.3.1/tests/test_openai_oauth_resources.py +88 -0
  22. codex_backend_sdk-0.3.1/tests/test_realtime_resource.py +80 -0
  23. {codex_backend_sdk-0.2.0 → codex_backend_sdk-0.3.1}/tests/test_responses_resource.py +73 -5
  24. codex_backend_sdk-0.2.0/.vscode/settings.json +0 -3
  25. codex_backend_sdk-0.2.0/PKG-INFO +0 -190
  26. codex_backend_sdk-0.2.0/README.md +0 -164
  27. codex_backend_sdk-0.2.0/codex_backend_sdk/codex_client.py +0 -786
  28. {codex_backend_sdk-0.2.0 → codex_backend_sdk-0.3.1}/LICENSE +0 -0
  29. {codex_backend_sdk-0.2.0 → codex_backend_sdk-0.3.1}/codex_backend_sdk/oauth.py +0 -0
  30. {codex_backend_sdk-0.2.0 → codex_backend_sdk-0.3.1}/codex_backend_sdk/pkce.py +0 -0
  31. {codex_backend_sdk-0.2.0 → codex_backend_sdk-0.3.1}/codex_backend_sdk/storage.py +0 -0
  32. {codex_backend_sdk-0.2.0 → codex_backend_sdk-0.3.1}/examples/agent.py +0 -0
  33. {codex_backend_sdk-0.2.0 → codex_backend_sdk-0.3.1}/tests/conftest.py +0 -0
  34. {codex_backend_sdk-0.2.0 → codex_backend_sdk-0.3.1}/tests/test_basic.py +0 -0
  35. {codex_backend_sdk-0.2.0 → codex_backend_sdk-0.3.1}/tests/test_conversation.py +0 -0
  36. {codex_backend_sdk-0.2.0 → codex_backend_sdk-0.3.1}/tests/test_reasoning.py +0 -0
  37. {codex_backend_sdk-0.2.0 → codex_backend_sdk-0.3.1}/tests/test_structured_output.py +0 -0
  38. {codex_backend_sdk-0.2.0 → codex_backend_sdk-0.3.1}/tests/test_tools.py +0 -0
@@ -10,3 +10,4 @@ build/
10
10
  venv/
11
11
  .pytest_cache/
12
12
  .claude/
13
+ .vscode/
@@ -0,0 +1,451 @@
1
+ Metadata-Version: 2.4
2
+ Name: codex-backend-sdk
3
+ Version: 0.3.1
4
+ Summary: Unofficial Python SDK for the ChatGPT Codex backend API
5
+ License: MIT
6
+ License-File: LICENSE
7
+ Keywords: chatgpt,codex,llm,openai,sdk
8
+ Classifier: Development Status :: 3 - Alpha
9
+ Classifier: Intended Audience :: Developers
10
+ Classifier: License :: OSI Approved :: MIT License
11
+ Classifier: Programming Language :: Python :: 3
12
+ Classifier: Programming Language :: Python :: 3.9
13
+ Classifier: Programming Language :: Python :: 3.10
14
+ Classifier: Programming Language :: Python :: 3.11
15
+ Classifier: Programming Language :: Python :: 3.12
16
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
17
+ Requires-Python: >=3.9
18
+ Requires-Dist: pydantic>=2.0
19
+ Requires-Dist: requests>=2.28
20
+ Provides-Extra: dev
21
+ Requires-Dist: openai>=1.0; extra == 'dev'
22
+ Requires-Dist: pytest>=7.0; extra == 'dev'
23
+ Provides-Extra: openai
24
+ Requires-Dist: openai>=1.0; extra == 'openai'
25
+ Description-Content-Type: text/markdown
26
+
27
+ # codex-backend-sdk
28
+
29
+ Unofficial Python SDK for the ChatGPT Codex backend API
30
+ (`chatgpt.com/backend-api/codex`).
31
+
32
+ This package mirrors the official OpenAI Python SDK shape for the API surface
33
+ that the Codex backend exposes. Use `OpenAI`, `client.responses.create(...)`,
34
+ and `client.models.list()` just as you would with `openai-python`, with
35
+ Codex-specific authentication and backend limitations under the hood.
36
+
37
+ > **Requirements:** a ChatGPT Plus, Pro, or Enterprise subscription.
38
+ > Authentication goes through ChatGPT OAuth and stores tokens in
39
+ > `~/.codex/auth.json`.
40
+
41
+ > **Disclaimer:** This is an independent, community-maintained library that
42
+ > reverse-engineers undocumented endpoints of `chatgpt.com`. It is not
43
+ > affiliated with, endorsed by, or supported by OpenAI.
44
+
45
+ ## Installation
46
+
47
+ ```bash
48
+ git clone https://github.com/B4PT0R/codex-backend-sdk.git
49
+ cd codex-backend-sdk
50
+ pip install -e .
51
+ ```
52
+
53
+ ## Basic Usage
54
+
55
+ ```python
56
+ from codex_backend_sdk import OpenAI
57
+
58
+ client = OpenAI().authenticate()
59
+
60
+ response = client.responses.create(
61
+ model="gpt-5.4",
62
+ input="Explain quicksort in one paragraph.",
63
+ )
64
+
65
+ print(response.output_text)
66
+ ```
67
+
68
+ ## Streaming
69
+
70
+ ```python
71
+ stream = client.responses.create(
72
+ model="gpt-5.4",
73
+ input="Say 'hi' five times.",
74
+ stream=True,
75
+ )
76
+
77
+ for event in stream:
78
+ if event.type in {"response.output_text.delta", "response.content_part.delta"}:
79
+ delta = event.delta
80
+ print(delta if isinstance(delta, str) else delta.get("text", ""), end="")
81
+ ```
82
+
83
+ ## Models
84
+
85
+ ```python
86
+ models = client.models.list()
87
+ for model in models:
88
+ print(model.id, model.display_name, model.context_window)
89
+
90
+ info = client.models.retrieve("gpt-5.4")
91
+ ```
92
+
93
+ ## Multi-Turn Input
94
+
95
+ The Codex backend does not expose `previous_response_id`, so pass prior
96
+ input/output items explicitly.
97
+
98
+ ```python
99
+ history = [
100
+ {"role": "user", "content": "My name is Alice. Say OK."},
101
+ ]
102
+
103
+ reply1 = client.responses.create(input=history).output_text
104
+ history.append({"role": "assistant", "content": reply1})
105
+ history.append({"role": "user", "content": "What is my name?"})
106
+
107
+ reply2 = client.responses.create(input=history).output_text
108
+ print(reply2)
109
+ ```
110
+
111
+ ## Function Calling
112
+
113
+ ```python
114
+ import json
115
+
116
+ tools = [{
117
+ "type": "function",
118
+ "name": "get_weather",
119
+ "description": "Get the current weather for a city.",
120
+ "parameters": {
121
+ "type": "object",
122
+ "properties": {"city": {"type": "string"}},
123
+ "required": ["city"],
124
+ "additionalProperties": False,
125
+ },
126
+ }]
127
+
128
+ first = client.responses.create(
129
+ input="What's the weather in Paris?",
130
+ tools=tools,
131
+ )
132
+
133
+ call = next(item for item in first.output if item["type"] == "function_call")
134
+ result = {"temperature": 18, "unit": "celsius", "condition": "cloudy"}
135
+
136
+ second = client.responses.create(
137
+ input=[
138
+ call,
139
+ {
140
+ "type": "function_call_output",
141
+ "call_id": call["call_id"],
142
+ "output": json.dumps(result),
143
+ },
144
+ ],
145
+ tools=tools,
146
+ )
147
+
148
+ print(second.output_text)
149
+ ```
150
+
151
+ ## Structured Output
152
+
153
+ ```python
154
+ schema = {
155
+ "title": "person",
156
+ "type": "object",
157
+ "properties": {
158
+ "name": {"type": "string"},
159
+ "age": {"type": "integer"},
160
+ },
161
+ "required": ["name", "age"],
162
+ "additionalProperties": False,
163
+ }
164
+
165
+ response = client.responses.create(
166
+ input="Extract: Bob is 42 years old.",
167
+ text={
168
+ "format": {
169
+ "type": "json_schema",
170
+ "name": "person",
171
+ "schema": schema,
172
+ "strict": True,
173
+ }
174
+ },
175
+ )
176
+ ```
177
+
178
+ ## Supported Backend Endpoints
179
+
180
+ The SDK exposes the supported backend endpoints through either OpenAI-shaped
181
+ resources (`responses`, `models`, `realtime`) or Codex-only resources (`codex`).
182
+
183
+ | Backend endpoint | SDK method | Notes |
184
+ |---|---|---|
185
+ | `POST /backend-api/codex/responses` | `client.responses.create(...)` | Stream-only backend; non-streaming SDK calls are collected from SSE events. |
186
+ | `POST /backend-api/codex/responses/compact` | `client.responses.compact(...)` | Codex-specific helper for encrypted context compaction. |
187
+ | `POST /backend-api/codex/memories/trace_summarize` | `client.codex.memories.trace_summarize(...)` | Raw Codex memory trace summarization helper. |
188
+ | `GET /backend-api/codex/models` | `client.models.list()` / `client.models.retrieve(...)` | OpenAI-shaped model objects with Codex metadata preserved as extra fields. |
189
+ | `POST /backend-api/codex/realtime/calls` | `client.realtime.calls.create(...)` | OpenAI-shaped SDP call creation for realtime sessions. |
190
+ | `wss://api.openai.com/v1/realtime?model=...` | `client.realtime_websocket_url(...)` / `client.realtime_websocket_headers(...)` | Helper surface used by codex-agent's realtime plugin. |
191
+ | `POST /v1/embeddings` | `client.embeddings.create(...)` | Uses the Codex OAuth access token against `api.openai.com`; verified with `text-embedding-3-small`. |
192
+ | `POST /v1/audio/transcriptions` | `client.audio.transcriptions.create(...)` | Uses the Codex OAuth access token against `api.openai.com`; verified with `gpt-4o-mini-transcribe`. |
193
+ | `GET /backend-api/wham/usage` | `client.codex.usage()` | Codex/ChatGPT quota and rate-limit status. |
194
+ | `GET /backend-api/wham/config/requirements` | `client.codex.config.requirements()` | Raw managed requirements/config payload for the authenticated account. |
195
+ | `GET /backend-api/wham/tasks/list` | `client.codex.tasks.list(...)` | Raw Codex cloud task listing. |
196
+ | `GET /backend-api/wham/tasks/{task_id}` | `client.codex.tasks.retrieve(task_id)` | Raw Codex cloud task detail. |
197
+ | `GET /backend-api/wham/tasks/{task_id}/turns` | `client.codex.tasks.turns.list(task_id)` | Raw task turn mapping. |
198
+ | `GET /backend-api/wham/tasks/{task_id}/turns/{turn_id}/sibling_turns` | `client.codex.tasks.turns.sibling_turns(task_id, turn_id)` | Raw sibling turn list. |
199
+ | `GET /backend-api/wham/environments` | `client.codex.environments.list()` | Raw Codex cloud environment list. |
200
+ | `POST /backend-api/files` + signed upload | `client.files.upload(...)` | Uploads local files for Codex Apps/MCP file parameters and returns `sediment://...` metadata. |
201
+ | `GET /backend-api/memories` | `client.codex.memories.list()` | Raw ChatGPT memory payload for the authenticated account. |
202
+ | `GET /backend-api/user_system_messages` | `client.codex.user_system_messages.retrieve()` | Raw ChatGPT customization/system-message payload. |
203
+
204
+ ### Responses
205
+
206
+ `client.responses.create(...)` follows the official OpenAI Responses API where
207
+ the Codex backend overlaps with it.
208
+
209
+ Supported request fields:
210
+
211
+ - `model`
212
+ - `input`
213
+ - `instructions`
214
+ - `include`
215
+ - `parallel_tool_calls`
216
+ - `prompt_cache_key`
217
+ - `reasoning`
218
+ - `service_tier`
219
+ - `store=False`
220
+ - `stream`
221
+ - `text`
222
+ - `tool_choice`
223
+ - `tools`
224
+
225
+ The backend itself requires streaming. When `stream=True`, the SDK yields
226
+ `ResponseStreamEvent` objects directly. When `stream` is omitted or false, the
227
+ SDK consumes the SSE stream and returns a collected `Response`.
228
+
229
+ ```python
230
+ response = client.responses.create(
231
+ model="gpt-5.4",
232
+ instructions="Be concise.",
233
+ input=[
234
+ {"role": "user", "content": "Summarize this API shape."},
235
+ ],
236
+ reasoning={"effort": "medium", "summary": "auto"},
237
+ include=["reasoning.encrypted_content"],
238
+ text={"verbosity": "medium"},
239
+ prompt_cache_key="session-123",
240
+ )
241
+ ```
242
+
243
+ Unsupported official Responses parameters are rejected explicitly with
244
+ `CodexBackendUnsupportedParameterError`, including `temperature`, `top_p`,
245
+ `max_output_tokens`, `metadata`, `user`, `safety_identifier`, `truncation`,
246
+ `previous_response_id`, `conversation`, `background`, `prompt`,
247
+ `prompt_cache_retention`, and `stream_options`.
248
+
249
+ ### Context Compaction
250
+
251
+ `client.responses.compact(...)` is specific to the Codex backend. It compresses
252
+ a long Responses-style input list into an opaque encrypted compaction summary
253
+ that can be replayed in later `input` arrays.
254
+
255
+ ```python
256
+ compacted = client.responses.compact(
257
+ model="gpt-5.4",
258
+ instructions="Keep task-critical context.",
259
+ input=history,
260
+ )
261
+
262
+ history = compacted.output
263
+ ```
264
+
265
+ The returned `CompactedResponse.output` contains regular response items plus
266
+ one or more `{"type": "compaction_summary", ...}` items. Treat those summaries
267
+ as opaque backend state.
268
+
269
+ ### Models
270
+
271
+ `client.models.list()` and `client.models.retrieve(model)` mirror the official
272
+ OpenAI models resource, while preserving Codex-specific metadata as extra
273
+ Pydantic fields.
274
+
275
+ ```python
276
+ models = client.models.list()
277
+ for model in models:
278
+ print(
279
+ model.id,
280
+ model.context_window,
281
+ model.supported_in_api,
282
+ model.supports_reasoning_summaries,
283
+ )
284
+ ```
285
+
286
+ Common extra fields include:
287
+
288
+ - `display_name`
289
+ - `description`
290
+ - `context_window`
291
+ - `supported_in_api`
292
+ - `supports_reasoning_summaries`
293
+ - `support_verbosity`
294
+ - `default_verbosity`
295
+ - `default_reasoning_level`
296
+ - `supported_reasoning_levels`
297
+ - `auto_compact_token_limit`
298
+ - `prefer_websockets`
299
+ - `input_modalities`
300
+ - `available_in_plans`
301
+ - `base_instructions`
302
+ - `priority`
303
+ - `raw`
304
+
305
+ ### Realtime
306
+
307
+ The SDK keeps the realtime surface available for integrations that bridge Codex
308
+ auth with voice sessions.
309
+
310
+ `client.realtime.calls.create(...)` mirrors the official OpenAI SDK call shape:
311
+
312
+ ```python
313
+ answer = client.realtime.calls.create(
314
+ sdp=offer_sdp,
315
+ session={"type": "realtime", "model": "gpt-realtime-1.5"},
316
+ )
317
+
318
+ print(answer.text)
319
+ ```
320
+
321
+ For WebSocket-based plugins such as `codex-agent`, the client also exposes small
322
+ helpers that reuse the OpenAI API key stored by the Codex OAuth flow:
323
+
324
+ ```python
325
+ url = client.realtime_websocket_url(model="gpt-realtime-1.5")
326
+ headers = client.realtime_websocket_headers(session_id="voice-session")
327
+ ```
328
+
329
+ `realtime_websocket_headers(...)` requires `~/.codex/auth.json` to contain
330
+ `openai_api_key`. The default `authenticate(request_api_key=True)` flow stores
331
+ that key when available.
332
+
333
+ ### Embeddings
334
+
335
+ `client.embeddings.create(...)` mirrors the official OpenAI embeddings resource
336
+ and sends the Codex OAuth access token directly to `api.openai.com/v1`.
337
+
338
+ ```python
339
+ embedding = client.embeddings.create(
340
+ model="text-embedding-3-small",
341
+ input="Embed this sentence.",
342
+ dimensions=256,
343
+ )
344
+
345
+ print(embedding.data[0].embedding)
346
+ ```
347
+
348
+ ### Audio Transcriptions
349
+
350
+ `client.audio.transcriptions.create(...)` mirrors the official OpenAI
351
+ transcriptions resource for non-streaming calls.
352
+
353
+ ```python
354
+ with open("meeting.wav", "rb") as audio:
355
+ transcription = client.audio.transcriptions.create(
356
+ model="gpt-4o-mini-transcribe",
357
+ file=("meeting.wav", audio, "audio/wav"),
358
+ response_format="json",
359
+ )
360
+
361
+ print(transcription.text)
362
+ ```
363
+
364
+ ### Quota And Usage
365
+
366
+ `client.codex.usage()` calls the ChatGPT WHAM usage endpoint. It returns the raw
367
+ quota payload from the backend because the shape contains plan-specific fields.
368
+
369
+ ```python
370
+ quota = client.codex.usage()
371
+ primary = quota.get("rate_limit", {}).get("primary_window", {})
372
+ print(primary.get("used_percent"))
373
+ ```
374
+
375
+ Typical fields include:
376
+
377
+ - `plan_type`
378
+ - `rate_limit.allowed`
379
+ - `rate_limit.limit_reached`
380
+ - `rate_limit.primary_window`
381
+ - `rate_limit.secondary_window`
382
+ - `additional_rate_limits`
383
+ - `credits`
384
+ - `rate_limit_reached_type`
385
+
386
+ ### Codex Cloud Tasks
387
+
388
+ The `client.codex.tasks` and `client.codex.environments` namespaces expose
389
+ read-only WHAM cloud-task payloads as raw backend dictionaries.
390
+
391
+ ```python
392
+ tasks = client.codex.tasks.list(limit=10)
393
+ task = client.codex.tasks.retrieve(tasks["items"][0]["id"])
394
+ turns = client.codex.tasks.turns.list(task["task"]["id"])
395
+ environments = client.codex.environments.list()
396
+ ```
397
+
398
+ Supported task-list filters are `limit`, `cursor`, `task_filter`, and
399
+ `environment_id`.
400
+
401
+ ### ChatGPT Account Data
402
+
403
+ The `client.codex` namespace also exposes read-only ChatGPT account data that is
404
+ not part of the official OpenAI SDK.
405
+
406
+ ```python
407
+ memories = client.codex.memories.list()
408
+ customization = client.codex.user_system_messages.retrieve()
409
+ requirements = client.codex.config.requirements()
410
+ ```
411
+
412
+ These methods return raw backend dictionaries because these payloads can contain
413
+ personal account-specific fields and may change without notice.
414
+
415
+ `client.codex.memories.trace_summarize(...)` exposes the Codex memory
416
+ summarization endpoint used by the official client. It returns the raw backend
417
+ dictionary:
418
+
419
+ ```python
420
+ summary = client.codex.memories.trace_summarize(
421
+ model="gpt-5.4",
422
+ traces=[
423
+ {
424
+ "id": "trace_1",
425
+ "metadata": {"source_path": "memory.jsonl"},
426
+ "items": [{"type": "message", "content": "Remember this"}],
427
+ }
428
+ ],
429
+ reasoning={"effort": "low"},
430
+ )
431
+ ```
432
+
433
+ ### File Uploads
434
+
435
+ `client.files.upload(...)` follows the official Codex file flow for Apps/MCP
436
+ file parameters: create file metadata under ChatGPT, upload bytes to the signed
437
+ URL, then finalize the upload.
438
+
439
+ ```python
440
+ uploaded = client.files.upload("report.csv")
441
+ print(uploaded.uri) # sediment://file_...
442
+ ```
443
+
444
+ ### Observed But Not Exposed
445
+
446
+ The reverse-engineering notes in `docs/backend-api.md` include additional
447
+ observed endpoints. They are not exposed as SDK resources yet because they are
448
+ plan-gated, unavailable on `chatgpt.com`, or not stable enough:
449
+
450
+ - `POST /v1/audio/speech` (auth reaches the endpoint, but Pro OAuth lacks
451
+ `api.model.audio.request` in current tests)