codex-backend-sdk 0.5.8__tar.gz → 0.5.9__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 (80) hide show
  1. {codex_backend_sdk-0.5.8 → codex_backend_sdk-0.5.9}/CHANGELOG.md +11 -0
  2. {codex_backend_sdk-0.5.8 → codex_backend_sdk-0.5.9}/PKG-INFO +1 -1
  3. {codex_backend_sdk-0.5.8 → codex_backend_sdk-0.5.9}/codex_backend_sdk/__init__.py +1 -1
  4. {codex_backend_sdk-0.5.8 → codex_backend_sdk-0.5.9}/codex_backend_sdk/resources/realtime.py +43 -6
  5. {codex_backend_sdk-0.5.8 → codex_backend_sdk-0.5.9}/docs/api-reference.md +29 -5
  6. {codex_backend_sdk-0.5.8 → codex_backend_sdk-0.5.9}/docs/backend-api.md +20 -4
  7. {codex_backend_sdk-0.5.8 → codex_backend_sdk-0.5.9}/docs/openai-compatibility.md +2 -1
  8. {codex_backend_sdk-0.5.8 → codex_backend_sdk-0.5.9}/pyproject.toml +1 -1
  9. {codex_backend_sdk-0.5.8 → codex_backend_sdk-0.5.9}/tests/test_realtime_resource.py +34 -0
  10. {codex_backend_sdk-0.5.8 → codex_backend_sdk-0.5.9}/uv.lock +1 -1
  11. {codex_backend_sdk-0.5.8 → codex_backend_sdk-0.5.9}/.gitignore +0 -0
  12. {codex_backend_sdk-0.5.8 → codex_backend_sdk-0.5.9}/LICENSE +0 -0
  13. {codex_backend_sdk-0.5.8 → codex_backend_sdk-0.5.9}/README.md +0 -0
  14. {codex_backend_sdk-0.5.8 → codex_backend_sdk-0.5.9}/codex_backend_sdk/_client.py +0 -0
  15. {codex_backend_sdk-0.5.8 → codex_backend_sdk-0.5.9}/codex_backend_sdk/_codex_version.py +0 -0
  16. {codex_backend_sdk-0.5.8 → codex_backend_sdk-0.5.9}/codex_backend_sdk/_models.py +0 -0
  17. {codex_backend_sdk-0.5.8 → codex_backend_sdk-0.5.9}/codex_backend_sdk/_streaming.py +0 -0
  18. {codex_backend_sdk-0.5.8 → codex_backend_sdk-0.5.9}/codex_backend_sdk/_transport.py +0 -0
  19. {codex_backend_sdk-0.5.8 → codex_backend_sdk-0.5.9}/codex_backend_sdk/_utils.py +0 -0
  20. {codex_backend_sdk-0.5.8 → codex_backend_sdk-0.5.9}/codex_backend_sdk/codex_client.py +0 -0
  21. {codex_backend_sdk-0.5.8 → codex_backend_sdk-0.5.9}/codex_backend_sdk/oauth.py +0 -0
  22. {codex_backend_sdk-0.5.8 → codex_backend_sdk-0.5.9}/codex_backend_sdk/pkce.py +0 -0
  23. {codex_backend_sdk-0.5.8 → codex_backend_sdk-0.5.9}/codex_backend_sdk/resources/__init__.py +0 -0
  24. {codex_backend_sdk-0.5.8 → codex_backend_sdk-0.5.9}/codex_backend_sdk/resources/_responses_payloads.py +0 -0
  25. {codex_backend_sdk-0.5.8 → codex_backend_sdk-0.5.9}/codex_backend_sdk/resources/_responses_routing.py +0 -0
  26. {codex_backend_sdk-0.5.8 → codex_backend_sdk-0.5.9}/codex_backend_sdk/resources/chatgpt.py +0 -0
  27. {codex_backend_sdk-0.5.8 → codex_backend_sdk-0.5.9}/codex_backend_sdk/resources/chatgpt_apps.py +0 -0
  28. {codex_backend_sdk-0.5.8 → codex_backend_sdk-0.5.9}/codex_backend_sdk/resources/chatgpt_connectors.py +0 -0
  29. {codex_backend_sdk-0.5.8 → codex_backend_sdk-0.5.9}/codex_backend_sdk/resources/chatgpt_plugin_bundles.py +0 -0
  30. {codex_backend_sdk-0.5.8 → codex_backend_sdk-0.5.9}/codex_backend_sdk/resources/chatgpt_plugin_shares.py +0 -0
  31. {codex_backend_sdk-0.5.8 → codex_backend_sdk-0.5.9}/codex_backend_sdk/resources/chatgpt_plugins.py +0 -0
  32. {codex_backend_sdk-0.5.8 → codex_backend_sdk-0.5.9}/codex_backend_sdk/resources/chatgpt_writing_blocks.py +0 -0
  33. {codex_backend_sdk-0.5.8 → codex_backend_sdk-0.5.9}/codex_backend_sdk/resources/codex.py +0 -0
  34. {codex_backend_sdk-0.5.8 → codex_backend_sdk-0.5.9}/codex_backend_sdk/resources/codex_web_search.py +0 -0
  35. {codex_backend_sdk-0.5.8 → codex_backend_sdk-0.5.9}/codex_backend_sdk/resources/files.py +0 -0
  36. {codex_backend_sdk-0.5.8 → codex_backend_sdk-0.5.9}/codex_backend_sdk/resources/images.py +0 -0
  37. {codex_backend_sdk-0.5.8 → codex_backend_sdk-0.5.9}/codex_backend_sdk/resources/models.py +0 -0
  38. {codex_backend_sdk-0.5.8 → codex_backend_sdk-0.5.9}/codex_backend_sdk/resources/openai_oauth.py +0 -0
  39. {codex_backend_sdk-0.5.8 → codex_backend_sdk-0.5.9}/codex_backend_sdk/resources/remote_control.py +0 -0
  40. {codex_backend_sdk-0.5.8 → codex_backend_sdk-0.5.9}/codex_backend_sdk/resources/responses.py +0 -0
  41. {codex_backend_sdk-0.5.8 → codex_backend_sdk-0.5.9}/codex_backend_sdk/resources/responses_websocket.py +0 -0
  42. {codex_backend_sdk-0.5.8 → codex_backend_sdk-0.5.9}/codex_backend_sdk/resources/worktree_snapshots.py +0 -0
  43. {codex_backend_sdk-0.5.8 → codex_backend_sdk-0.5.9}/codex_backend_sdk/storage.py +0 -0
  44. {codex_backend_sdk-0.5.8 → codex_backend_sdk-0.5.9}/docs/codex-source-audit.md +0 -0
  45. {codex_backend_sdk-0.5.8 → codex_backend_sdk-0.5.9}/docs/desktop-endpoint-inventory.md +0 -0
  46. {codex_backend_sdk-0.5.8 → codex_backend_sdk-0.5.9}/docs/endpoint-coverage.md +0 -0
  47. {codex_backend_sdk-0.5.8 → codex_backend_sdk-0.5.9}/examples/agent.py +0 -0
  48. {codex_backend_sdk-0.5.8 → codex_backend_sdk-0.5.9}/tests/conftest.py +0 -0
  49. {codex_backend_sdk-0.5.8 → codex_backend_sdk-0.5.9}/tests/general/test_codex_source_contracts.py +0 -0
  50. {codex_backend_sdk-0.5.8 → codex_backend_sdk-0.5.9}/tests/test_account_info.py +0 -0
  51. {codex_backend_sdk-0.5.8 → codex_backend_sdk-0.5.9}/tests/test_basic.py +0 -0
  52. {codex_backend_sdk-0.5.8 → codex_backend_sdk-0.5.9}/tests/test_chatgpt_apps.py +0 -0
  53. {codex_backend_sdk-0.5.8 → codex_backend_sdk-0.5.9}/tests/test_chatgpt_connectors.py +0 -0
  54. {codex_backend_sdk-0.5.8 → codex_backend_sdk-0.5.9}/tests/test_chatgpt_plugin_bundles.py +0 -0
  55. {codex_backend_sdk-0.5.8 → codex_backend_sdk-0.5.9}/tests/test_chatgpt_plugin_shares.py +0 -0
  56. {codex_backend_sdk-0.5.8 → codex_backend_sdk-0.5.9}/tests/test_chatgpt_plugins.py +0 -0
  57. {codex_backend_sdk-0.5.8 → codex_backend_sdk-0.5.9}/tests/test_chatgpt_projects_gizmos.py +0 -0
  58. {codex_backend_sdk-0.5.8 → codex_backend_sdk-0.5.9}/tests/test_chatgpt_resources.py +0 -0
  59. {codex_backend_sdk-0.5.8 → codex_backend_sdk-0.5.9}/tests/test_chatgpt_search_files.py +0 -0
  60. {codex_backend_sdk-0.5.8 → codex_backend_sdk-0.5.9}/tests/test_chatgpt_writing_blocks.py +0 -0
  61. {codex_backend_sdk-0.5.8 → codex_backend_sdk-0.5.9}/tests/test_client_retry.py +0 -0
  62. {codex_backend_sdk-0.5.8 → codex_backend_sdk-0.5.9}/tests/test_codex_resources.py +0 -0
  63. {codex_backend_sdk-0.5.8 → codex_backend_sdk-0.5.9}/tests/test_codex_version.py +0 -0
  64. {codex_backend_sdk-0.5.8 → codex_backend_sdk-0.5.9}/tests/test_codex_web_search.py +0 -0
  65. {codex_backend_sdk-0.5.8 → codex_backend_sdk-0.5.9}/tests/test_conversation.py +0 -0
  66. {codex_backend_sdk-0.5.8 → codex_backend_sdk-0.5.9}/tests/test_files_resource.py +0 -0
  67. {codex_backend_sdk-0.5.8 → codex_backend_sdk-0.5.9}/tests/test_hosted_mcp.py +0 -0
  68. {codex_backend_sdk-0.5.8 → codex_backend_sdk-0.5.9}/tests/test_images.py +0 -0
  69. {codex_backend_sdk-0.5.8 → codex_backend_sdk-0.5.9}/tests/test_oauth.py +0 -0
  70. {codex_backend_sdk-0.5.8 → codex_backend_sdk-0.5.9}/tests/test_openai_compatibility.py +0 -0
  71. {codex_backend_sdk-0.5.8 → codex_backend_sdk-0.5.9}/tests/test_openai_oauth_resources.py +0 -0
  72. {codex_backend_sdk-0.5.8 → codex_backend_sdk-0.5.9}/tests/test_prompt_cache_usage.py +0 -0
  73. {codex_backend_sdk-0.5.8 → codex_backend_sdk-0.5.9}/tests/test_reasoning.py +0 -0
  74. {codex_backend_sdk-0.5.8 → codex_backend_sdk-0.5.9}/tests/test_remote_control_resource.py +0 -0
  75. {codex_backend_sdk-0.5.8 → codex_backend_sdk-0.5.9}/tests/test_responses_resource.py +0 -0
  76. {codex_backend_sdk-0.5.8 → codex_backend_sdk-0.5.9}/tests/test_responses_routing.py +0 -0
  77. {codex_backend_sdk-0.5.8 → codex_backend_sdk-0.5.9}/tests/test_responses_websocket.py +0 -0
  78. {codex_backend_sdk-0.5.8 → codex_backend_sdk-0.5.9}/tests/test_structured_output.py +0 -0
  79. {codex_backend_sdk-0.5.8 → codex_backend_sdk-0.5.9}/tests/test_tools.py +0 -0
  80. {codex_backend_sdk-0.5.8 → codex_backend_sdk-0.5.9}/tests/test_worktree_snapshots.py +0 -0
@@ -2,6 +2,17 @@
2
2
 
3
3
  All notable changes to this project will be documented in this file.
4
4
 
5
+ ## [0.5.9] - 2026-10-24
6
+
7
+ ### Added
8
+ - OAuth transcription-only primary Realtime WebSocket with JSON events,
9
+ context-managed cleanup, bounded socket timeout, and no automatic audio replay.
10
+ Verified with synthetic PCM16LE mono 24 kHz speech, no developer API key.
11
+
12
+ ### Documentation
13
+ - Distinguish verified Realtime transcription from the legacy batch route's
14
+ current HTTP 403 responses; OAuth success does not establish quota coverage.
15
+
5
16
  ## [0.5.8] - 2026-10-22
6
17
 
7
18
  ### Added
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: codex-backend-sdk
3
- Version: 0.5.8
3
+ Version: 0.5.9
4
4
  Summary: Unofficial Python SDK for the ChatGPT Codex backend API
5
5
  License: MIT
6
6
  License-File: LICENSE
@@ -17,7 +17,7 @@ Quickstart:
17
17
  print(response.output_text)
18
18
  """
19
19
 
20
- __version__ = "0.5.8"
20
+ __version__ = "0.5.9"
21
21
 
22
22
  from .oauth import (
23
23
  DeviceCode,
@@ -3,6 +3,7 @@
3
3
  from __future__ import annotations
4
4
 
5
5
  import json
6
+ from contextlib import contextmanager
6
7
  import threading
7
8
  import urllib.parse
8
9
  import uuid
@@ -33,6 +34,38 @@ class Realtime:
33
34
  self.sideband = RealtimeSideband(client)
34
35
 
35
36
 
37
+ @contextmanager
38
+ def connect(self, *, intent: str, timeout: Optional[float] = None):
39
+ """Open a transcription-only primary WebSocket with ChatGPT OAuth.
40
+
41
+ Events are JSON dictionaries sent/received with ``send``/``recv``.
42
+ There is no automatic retry or audio replay. Availability and usage
43
+ attribution depend on the backend; subscription coverage is not implied.
44
+ """
45
+ if intent != "transcription":
46
+ raise CodexBackendUnsupportedParameterError(
47
+ "Only intent='transcription' is supported by the OAuth primary socket."
48
+ )
49
+ store = self._client._store
50
+ if store is None or not store.access_token:
51
+ raise RuntimeError("Realtime transcription requires ChatGPT OAuth authentication.")
52
+ headers = {
53
+ "Authorization": f"Bearer {store.access_token}",
54
+ "originator": "codex_cli_rs",
55
+ "x-session-id": str(uuid.uuid4()),
56
+ }
57
+ if store.account_id:
58
+ headers["ChatGPT-Account-ID"] = store.account_id
59
+ socket = websocket.create_connection(
60
+ "wss://api.openai.com/v1/realtime?intent=transcription",
61
+ header=headers,
62
+ timeout=self._client._timeout if timeout is None else timeout,
63
+ redirect_limit=0,
64
+ )
65
+ with RealtimeConnection(socket) as connection:
66
+ yield connection
67
+
68
+
36
69
  class Live:
37
70
  """OpenAI-compatible Live resource backed by ChatGPT OAuth.
38
71
 
@@ -114,8 +147,8 @@ class Live:
114
147
  )
115
148
 
116
149
 
117
- class RealtimeSidebandConnection:
118
- """JSON transport joined to an existing Realtime v3 WebRTC call."""
150
+ class RealtimeConnection:
151
+ """Synchronous JSON event transport with deterministic socket cleanup."""
119
152
 
120
153
  def __init__(self, socket: Any, *, graceful_close: bool = False) -> None:
121
154
  self._socket = socket
@@ -125,20 +158,20 @@ class RealtimeSidebandConnection:
125
158
 
126
159
  def send(self, event: Any) -> None:
127
160
  if self.closed:
128
- raise RuntimeError("Realtime sideband connection is closed.")
161
+ raise RuntimeError("Realtime connection is closed.")
129
162
  payload = json.dumps(_jsonable(event), ensure_ascii=False)
130
163
  with self._send_lock:
131
164
  self._socket.send(payload)
132
165
 
133
166
  def recv(self) -> dict[str, Any]:
134
167
  if self.closed:
135
- raise RuntimeError("Realtime sideband connection is closed.")
168
+ raise RuntimeError("Realtime connection is closed.")
136
169
  payload = self._socket.recv()
137
170
  if isinstance(payload, bytes):
138
171
  payload = payload.decode("utf-8")
139
172
  event = json.loads(payload)
140
173
  if not isinstance(event, dict):
141
- raise TypeError("Expected a Realtime sideband event to be a JSON object.")
174
+ raise TypeError("Expected a Realtime event to be a JSON object.")
142
175
  return event
143
176
 
144
177
  def close(self) -> None:
@@ -159,13 +192,17 @@ class RealtimeSidebandConnection:
159
192
  except (IndexError, StopIteration):
160
193
  return
161
194
 
162
- def __enter__(self) -> "RealtimeSidebandConnection":
195
+ def __enter__(self) -> "RealtimeConnection":
163
196
  return self
164
197
 
165
198
  def __exit__(self, *_: Any) -> None:
166
199
  self.close()
167
200
 
168
201
 
202
+ class RealtimeSidebandConnection(RealtimeConnection):
203
+ """JSON transport joined to an existing Realtime v3 WebRTC call."""
204
+
205
+
169
206
  class LiveSidebandConnection(RealtimeSidebandConnection):
170
207
  """Live-shaped event stream over the Codex OAuth sideband dialect."""
171
208
 
@@ -250,7 +250,8 @@ content and a MIME type.
250
250
  Required: `file`, `model`. Supported optional backend parameters are `language`,
251
251
  `prompt`, `response_format` (`json` or `text`), `temperature`, `extra_headers`,
252
252
  `extra_query`, `extra_body`, and `timeout`. Returns typed `Transcription` for
253
- JSON or `str` for text.
253
+ JSON or `str` for text. The legacy batch route returned HTTP 403 in live tests
254
+ on October 24, 2026; prefer the verified transcription WebSocket below.
254
255
 
255
256
  The OpenAI-compatible signature rejects unsupported `chunking_strategy`,
256
257
  `include`, `known_speaker_names`, `known_speaker_references`, `stream`, and
@@ -271,6 +272,7 @@ Platform quota rather than the ChatGPT subscription.
271
272
 
272
273
  | Method | Parameters | Returns |
273
274
  | --- | --- | --- |
275
+ | `client.realtime.connect(...)` | `intent="transcription"`, optional `timeout` | context manager yielding a JSON `RealtimeConnection` |
274
276
  | `client.live.create(...)` | official `session`, `transport`; standard request extensions | `LiveCreateResponse` with `session.id` and `transport.sdp` |
275
277
  | `client.live.sideband.connect(...)` | official sideband keywords including `session_id`, `graceful_close`, request/socket/retry options | lazy `LiveSidebandConnectionManager` context manager |
276
278
  | `client.realtime.calls.create_v3(...)` | `sdp`, `session`, optional `session_id`, `thread_id`, transport extensions | typed `RealtimeCallResponse` |
@@ -284,11 +286,11 @@ apply `response.transport.sdp`, then attach with
284
286
  adapter presents the backend call ID as the Live session ID. The connection is
285
287
  iterable and yields forward-compatible `LiveEvent` objects with attribute
286
288
  access. The public primary `client.live.connect()` transport raises an explicit
287
- unsupported error because ChatGPT OAuth currently exposes only WebRTC plus an
288
- attached sideband.
289
+ unsupported error. This is distinct from the transcription-only primary
290
+ `client.realtime.connect(intent="transcription")` socket, verified with OAuth
291
+ on October 24, 2026. Its quota/billing attribution is not established.
289
292
 
290
- `create_v3()` requires the effective model to be `gpt-live-1-codex` or
291
- `gpt-live-1-boulder-alpha`, removes any caller-supplied session ID, generates
293
+ `create_v3()` forwards the requested model and explicit correlation IDs, generates
292
294
  required backend correlation IDs when omitted, and applies the current Codex v3
293
295
  intent/architecture. The result exposes `answer_sdp`, `call_id`, raw `session`,
294
296
  and original response text. The sideband connection joins that call over
@@ -768,3 +770,25 @@ validation errors according to the boundary.
768
770
  mutations.
769
771
  - Commercial/admin, attestation, reporting, telemetry, analytics, beacon, and
770
772
  personal-access-token surfaces are outside the public SDK API.
773
+
774
+ ### Transcription-only OAuth WebSocket
775
+
776
+ ```python
777
+ client.authenticate(interactive=False)
778
+ with client.realtime.connect(intent="transcription", timeout=10) as connection:
779
+ connection.send({"type": "session.update", "session": {
780
+ "type": "transcription", "audio": {"input": {
781
+ "format": {"type": "audio/pcm", "rate": 24000},
782
+ "transcription": {"model": "gpt-4o-mini-transcribe", "language": "fr"},
783
+ "turn_detection": None,
784
+ }},
785
+ }})
786
+ # Wait for session.updated (handle error events), then send base64 PCM16LE
787
+ # mono chunks as input_audio_buffer.append and input_audio_buffer.commit.
788
+ # Receive conversation.item.input_audio_transcription.completed.
789
+ ```
790
+
791
+ This synchronous raw-event surface has no automatic retry/replay, generated
792
+ assistant response, tools, or output audio. Set a bounded socket timeout and an
793
+ application-level total deadline. Context exit closes the socket even on failure.
794
+ Only `intent="transcription"` is supported; model/session events remain caller-owned.
@@ -402,7 +402,8 @@ low-level `client.realtime.sideband.connect(...)`) joins its
402
402
  JSON control/delegation channel at `/v1/live/{call_id}` with the current ChatGPT
403
403
  OAuth account. This transport is used for client-managed delegation events and
404
404
  context appends. The SDK intentionally does not expose the unrelated API-key
405
- Voice v2 WebSocket surface.
405
+ Voice v2 conversation WebSocket surface. It separately exposes the verified
406
+ transcription-only primary socket described below.
406
407
 
407
408
  ---
408
409
 
@@ -410,8 +411,10 @@ Voice v2 WebSocket surface.
410
411
 
411
412
  These OpenAI-shaped resources deliberately use different upstreams. Embeddings
412
413
  remain an OpenAI Platform call and consume the associated developer-account
413
- quota. Batch transcription uses the authenticated ChatGPT backend and does not
414
- require a developer API key.
414
+ quota. Legacy batch transcription uses the authenticated ChatGPT backend but
415
+ returned HTTP 403 in current live tests. The transcription-only Realtime socket
416
+ accepts ChatGPT OAuth without a developer API key; its quota/billing attribution
417
+ is not established.
415
418
 
416
419
  ### `POST /v1/embeddings`
417
420
 
@@ -437,13 +440,26 @@ subscription.
437
440
 
438
441
  **SDK method**: `client.audio.transcriptions.create(...)`
439
442
 
440
- **Status**: Supported for non-streaming ChatGPT batch transcription. The SDK
443
+ **Status**: Legacy adapter present, but live tests with WebM and WAV returned
444
+ HTTP 403 HTML on October 24, 2026. This is not proof of permanent removal. The SDK
441
445
  uploads multipart audio with the OAuth bearer and `ChatGPT-Account-ID`, and
442
446
  supports the `model`, `language`, `prompt`, `temperature`, and `json`/`text`
443
447
  response options used by Codex Agent. Streaming, timestamps, speaker references,
444
448
  chunking, SRT, and VTT are rejected locally rather than falling back to a
445
449
  billable Platform endpoint.
446
450
 
451
+ ### `WSS /v1/realtime?intent=transcription` (api.openai.com)
452
+
453
+ **SDK method**: `client.realtime.connect(intent="transcription", timeout=...)`
454
+
455
+ **Status**: Verified October 24, 2026 with a ChatGPT OAuth bearer, no API key.
456
+ `session.update` with type `transcription`, PCM16LE mono 24 kHz,
457
+ `gpt-4o-mini-transcribe`, and `turn_detection: null` accepted synthetic speech and
458
+ returned the correct `conversation.item.input_audio_transcription.completed` text.
459
+ The ChatGPT backend URL `/backend-api/codex/realtime?intent=transcription` instead
460
+ returned HTTP 403. Subscription coverage and quota/billing attribution remain
461
+ unverified; successful OAuth authentication alone does not establish either.
462
+
447
463
  ### `POST /backend-api/codex/images/generations`
448
464
 
449
465
  **SDK method**: `client.images.generate(...)`
@@ -20,7 +20,8 @@ parameter cannot disappear silently from the backend client surface.
20
20
  | `responses.compact` | Official parameters preserved; Codex tool/reasoning controls are additive | Supported request options and `extra_body` are forwarded | Stored-response continuation and explicit Platform prompt-cache controls are unavailable |
21
21
  | `models.list` / `retrieve` | Official parameters preserved; `force_refresh` is additive | Returns iterable OpenAI-shaped model pages | Catalog retrieval is a Codex endpoint and `retrieve` resolves from that catalog |
22
22
  | `embeddings.create` | Official signature preserved | Uses the OpenAI endpoint with the Codex OAuth bearer | Availability depends on OAuth scopes and account rollout |
23
- | `audio.transcriptions.create` | Official signature preserved | File inputs, JSON/text output, language, prompt, and temperature are supported | Streaming, diarization, timestamps, `keywords`, and multi-language hints raise explicitly |
23
+ | `realtime.connect` | Transcription-only OAuth subset | Context manager, raw JSON send/recv, deterministic close | Only `intent="transcription"`; no automatic replay; quota attribution unverified |
24
+ | `audio.transcriptions.create` | Official signature preserved | Legacy batch adapter retains file inputs and options; live route returned HTTP 403 on October 24, 2026 | Streaming, diarization, timestamps, `keywords`, and multi-language hints raise explicitly |
24
25
  | `images.generate` | Official signature preserved | GPT Image generation returns OpenAI-shaped base64 results | Codex always returns non-streamed PNG/base64 output; format/compression and Platform user attribution are unavailable |
25
26
  | `images.edit` | Official signature preserved | Single/multiple file inputs, URLs, data URLs, reference objects, and masks are normalized to Codex JSON | A mask targets the first image; `file_id` is contract-derived but not live-verified; Codex returns non-streamed PNG/base64 output |
26
27
  | `files.create` | Official signature is present | Not callable with Codex OAuth | Live probe returned missing `api.files.write`; the method raises a scope-specific unsupported error |
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "codex-backend-sdk"
7
- version = "0.5.8"
7
+ version = "0.5.9"
8
8
  description = "Unofficial Python SDK for the ChatGPT Codex backend API"
9
9
  readme = "README.md"
10
10
  license = { text = "MIT" }
@@ -279,3 +279,37 @@ def test_realtime_call_response_requires_valid_location():
279
279
  assert "does not contain a valid call id" in str(exc)
280
280
  else:
281
281
  raise AssertionError("Expected invalid Location to fail")
282
+
283
+
284
+ def test_primary_transcription_socket_uses_oauth_and_closes_on_failure(monkeypatch):
285
+ client = FakeRealtimeClient()
286
+ socket = FakeWebSocket([json.dumps({"type": "session.created"})])
287
+ captured = {}
288
+ def connect(url, **kwargs):
289
+ captured.update(url=url, **kwargs)
290
+ return socket
291
+ monkeypatch.setattr("codex_backend_sdk.resources.realtime.websocket.create_connection", connect)
292
+ with pytest.raises(ValueError, match="consumer"):
293
+ with client.realtime.connect(intent="transcription", timeout=12) as connection:
294
+ assert connection.recv() == {"type": "session.created"}
295
+ connection.send({"type": "input_audio_buffer.commit"})
296
+ raise ValueError("consumer")
297
+ assert captured["url"] == "wss://api.openai.com/v1/realtime?intent=transcription"
298
+ assert captured["header"]["Authorization"] == "Bearer chatgpt-token"
299
+ assert captured["header"]["ChatGPT-Account-ID"] == "account-id"
300
+ assert captured["header"]["x-session-id"]
301
+ assert captured["timeout"] == 12
302
+ assert captured["redirect_limit"] == 0
303
+ assert json.loads(socket.sent[0]) == {"type": "input_audio_buffer.commit"}
304
+ assert socket.closed
305
+
306
+
307
+ def test_primary_socket_rejects_other_intents_and_missing_oauth():
308
+ client = FakeRealtimeClient()
309
+ with pytest.raises(NotImplementedError):
310
+ with client.realtime.connect(intent="realtime"):
311
+ pass
312
+ client._store = None
313
+ with pytest.raises(RuntimeError, match="ChatGPT OAuth"):
314
+ with client.realtime.connect(intent="transcription"):
315
+ pass
@@ -162,7 +162,7 @@ wheels = [
162
162
 
163
163
  [[package]]
164
164
  name = "codex-backend-sdk"
165
- version = "0.5.8"
165
+ version = "0.5.9"
166
166
  source = { editable = "." }
167
167
  dependencies = [
168
168
  { name = "pydantic" },