oauth-codex 2.0.1__tar.gz → 2.0.2__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 (63) hide show
  1. {oauth_codex-2.0.1 → oauth_codex-2.0.2}/PKG-INFO +1 -1
  2. {oauth_codex-2.0.1 → oauth_codex-2.0.2}/pyproject.toml +1 -1
  3. {oauth_codex-2.0.1 → oauth_codex-2.0.2}/src/oauth_codex/_client.py +5 -12
  4. {oauth_codex-2.0.1 → oauth_codex-2.0.2}/src/oauth_codex/_engine.py +14 -3
  5. {oauth_codex-2.0.1 → oauth_codex-2.0.2}/src/oauth_codex/_version.py +1 -1
  6. {oauth_codex-2.0.1 → oauth_codex-2.0.2}/src/oauth_codex/core_types.py +1 -1
  7. {oauth_codex-2.0.1 → oauth_codex-2.0.2}/src/oauth_codex/tooling.py +13 -3
  8. {oauth_codex-2.0.1 → oauth_codex-2.0.2}/src/oauth_codex.egg-info/PKG-INFO +1 -1
  9. {oauth_codex-2.0.1 → oauth_codex-2.0.2}/tests/test_engine_stream_and_continuity.py +1 -3
  10. {oauth_codex-2.0.1 → oauth_codex-2.0.2}/tests/test_generate_sync.py +27 -0
  11. {oauth_codex-2.0.1 → oauth_codex-2.0.2}/README.md +0 -0
  12. {oauth_codex-2.0.1 → oauth_codex-2.0.2}/setup.cfg +0 -0
  13. {oauth_codex-2.0.1 → oauth_codex-2.0.2}/src/oauth_codex/__init__.py +0 -0
  14. {oauth_codex-2.0.1 → oauth_codex-2.0.2}/src/oauth_codex/_base_client.py +0 -0
  15. {oauth_codex-2.0.1 → oauth_codex-2.0.2}/src/oauth_codex/_exceptions.py +0 -0
  16. {oauth_codex-2.0.1 → oauth_codex-2.0.2}/src/oauth_codex/_models.py +0 -0
  17. {oauth_codex-2.0.1 → oauth_codex-2.0.2}/src/oauth_codex/_module_client.py +0 -0
  18. {oauth_codex-2.0.1 → oauth_codex-2.0.2}/src/oauth_codex/_resource.py +0 -0
  19. {oauth_codex-2.0.1 → oauth_codex-2.0.2}/src/oauth_codex/_types.py +0 -0
  20. {oauth_codex-2.0.1 → oauth_codex-2.0.2}/src/oauth_codex/auth/__init__.py +0 -0
  21. {oauth_codex-2.0.1 → oauth_codex-2.0.2}/src/oauth_codex/auth/config.py +0 -0
  22. {oauth_codex-2.0.1 → oauth_codex-2.0.2}/src/oauth_codex/auth/pkce.py +0 -0
  23. {oauth_codex-2.0.1 → oauth_codex-2.0.2}/src/oauth_codex/auth/store.py +0 -0
  24. {oauth_codex-2.0.1 → oauth_codex-2.0.2}/src/oauth_codex/auth/token_manager.py +0 -0
  25. {oauth_codex-2.0.1 → oauth_codex-2.0.2}/src/oauth_codex/compat_store.py +0 -0
  26. {oauth_codex-2.0.1 → oauth_codex-2.0.2}/src/oauth_codex/errors.py +0 -0
  27. {oauth_codex-2.0.1 → oauth_codex-2.0.2}/src/oauth_codex/py.typed +0 -0
  28. {oauth_codex-2.0.1 → oauth_codex-2.0.2}/src/oauth_codex/resources/__init__.py +0 -0
  29. {oauth_codex-2.0.1 → oauth_codex-2.0.2}/src/oauth_codex/resources/_wrappers.py +0 -0
  30. {oauth_codex-2.0.1 → oauth_codex-2.0.2}/src/oauth_codex/resources/files.py +0 -0
  31. {oauth_codex-2.0.1 → oauth_codex-2.0.2}/src/oauth_codex/resources/models.py +0 -0
  32. {oauth_codex-2.0.1 → oauth_codex-2.0.2}/src/oauth_codex/resources/responses/__init__.py +0 -0
  33. {oauth_codex-2.0.1 → oauth_codex-2.0.2}/src/oauth_codex/resources/responses/_helpers.py +0 -0
  34. {oauth_codex-2.0.1 → oauth_codex-2.0.2}/src/oauth_codex/resources/responses/input_tokens.py +0 -0
  35. {oauth_codex-2.0.1 → oauth_codex-2.0.2}/src/oauth_codex/resources/responses/responses.py +0 -0
  36. {oauth_codex-2.0.1 → oauth_codex-2.0.2}/src/oauth_codex/resources/vector_stores/__init__.py +0 -0
  37. {oauth_codex-2.0.1 → oauth_codex-2.0.2}/src/oauth_codex/resources/vector_stores/file_batches.py +0 -0
  38. {oauth_codex-2.0.1 → oauth_codex-2.0.2}/src/oauth_codex/resources/vector_stores/files.py +0 -0
  39. {oauth_codex-2.0.1 → oauth_codex-2.0.2}/src/oauth_codex/resources/vector_stores/vector_stores.py +0 -0
  40. {oauth_codex-2.0.1 → oauth_codex-2.0.2}/src/oauth_codex/store.py +0 -0
  41. {oauth_codex-2.0.1 → oauth_codex-2.0.2}/src/oauth_codex/types/__init__.py +0 -0
  42. {oauth_codex-2.0.1 → oauth_codex-2.0.2}/src/oauth_codex/types/file_deleted.py +0 -0
  43. {oauth_codex-2.0.1 → oauth_codex-2.0.2}/src/oauth_codex/types/file_object.py +0 -0
  44. {oauth_codex-2.0.1 → oauth_codex-2.0.2}/src/oauth_codex/types/responses/__init__.py +0 -0
  45. {oauth_codex-2.0.1 → oauth_codex-2.0.2}/src/oauth_codex/types/responses/input_token_count_response.py +0 -0
  46. {oauth_codex-2.0.1 → oauth_codex-2.0.2}/src/oauth_codex/types/responses/response.py +0 -0
  47. {oauth_codex-2.0.1 → oauth_codex-2.0.2}/src/oauth_codex/types/responses/response_stream_event.py +0 -0
  48. {oauth_codex-2.0.1 → oauth_codex-2.0.2}/src/oauth_codex/types/shared/__init__.py +0 -0
  49. {oauth_codex-2.0.1 → oauth_codex-2.0.2}/src/oauth_codex/types/shared/model_capabilities.py +0 -0
  50. {oauth_codex-2.0.1 → oauth_codex-2.0.2}/src/oauth_codex/types/shared/usage.py +0 -0
  51. {oauth_codex-2.0.1 → oauth_codex-2.0.2}/src/oauth_codex/types/vector_stores/__init__.py +0 -0
  52. {oauth_codex-2.0.1 → oauth_codex-2.0.2}/src/oauth_codex/types/vector_stores/vector_store.py +0 -0
  53. {oauth_codex-2.0.1 → oauth_codex-2.0.2}/src/oauth_codex/types/vector_stores/vector_store_deleted.py +0 -0
  54. {oauth_codex-2.0.1 → oauth_codex-2.0.2}/src/oauth_codex/types/vector_stores/vector_store_file.py +0 -0
  55. {oauth_codex-2.0.1 → oauth_codex-2.0.2}/src/oauth_codex/types/vector_stores/vector_store_file_batch.py +0 -0
  56. {oauth_codex-2.0.1 → oauth_codex-2.0.2}/src/oauth_codex/types/vector_stores/vector_store_search_response.py +0 -0
  57. {oauth_codex-2.0.1 → oauth_codex-2.0.2}/src/oauth_codex/version.py +0 -0
  58. {oauth_codex-2.0.1 → oauth_codex-2.0.2}/src/oauth_codex.egg-info/SOURCES.txt +0 -0
  59. {oauth_codex-2.0.1 → oauth_codex-2.0.2}/src/oauth_codex.egg-info/dependency_links.txt +0 -0
  60. {oauth_codex-2.0.1 → oauth_codex-2.0.2}/src/oauth_codex.egg-info/requires.txt +0 -0
  61. {oauth_codex-2.0.1 → oauth_codex-2.0.2}/src/oauth_codex.egg-info/top_level.txt +0 -0
  62. {oauth_codex-2.0.1 → oauth_codex-2.0.2}/tests/test_generate_async.py +0 -0
  63. {oauth_codex-2.0.1 → oauth_codex-2.0.2}/tests/test_public_surface.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: oauth-codex
3
- Version: 2.0.1
3
+ Version: 2.0.2
4
4
  Summary: Codex OAuth-based Python SDK with a single Client and generate-first API
5
5
  Author: Codex
6
6
  Requires-Python: >=3.11
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "oauth-codex"
7
- version = "2.0.1"
7
+ version = "2.0.2"
8
8
  description = "Codex OAuth-based Python SDK with a single Client and generate-first API"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.11"
@@ -20,7 +20,7 @@ from .core_types import (
20
20
  ToolResult,
21
21
  )
22
22
  from .store import FallbackTokenStore
23
- from .tooling import callable_to_tool_schema
23
+ from .tooling import callable_to_tool_schema, normalize_tool_output
24
24
 
25
25
  DEFAULT_MODEL = "gpt-5.3-codex"
26
26
  DEFAULT_MAX_TOOL_ROUNDS = 8
@@ -364,7 +364,7 @@ class OAuthCodexClient(SyncAPIClient):
364
364
  ) -> list[ToolResult]:
365
365
  results: list[ToolResult] = []
366
366
  for call in tool_calls:
367
- output: str | dict[str, Any]
367
+ output: dict[str, Any]
368
368
  tool = tools_by_name.get(call.name)
369
369
  if tool is None:
370
370
  output = {"error": f"tool not found: {call.name}"}
@@ -387,7 +387,7 @@ class OAuthCodexClient(SyncAPIClient):
387
387
  ) -> list[ToolResult]:
388
388
  results: list[ToolResult] = []
389
389
  for call in tool_calls:
390
- output: str | dict[str, Any]
390
+ output: dict[str, Any]
391
391
  tool = tools_by_name.get(call.name)
392
392
  if tool is None:
393
393
  output = {"error": f"tool not found: {call.name}"}
@@ -411,15 +411,8 @@ class OAuthCodexClient(SyncAPIClient):
411
411
  raise TypeError("tool arguments must be a JSON object")
412
412
  return parsed
413
413
 
414
- def _normalize_tool_output(self, output: Any) -> str | dict[str, Any]:
415
- if isinstance(output, dict):
416
- return output
417
- if isinstance(output, str):
418
- return output
419
- try:
420
- return json.dumps(output, ensure_ascii=True)
421
- except TypeError:
422
- return str(output)
414
+ def _normalize_tool_output(self, output: Any) -> dict[str, Any]:
415
+ return normalize_tool_output(output)
423
416
 
424
417
 
425
418
  Client = OAuthCodexClient
@@ -40,7 +40,12 @@ from .errors import (
40
40
  TokenRefreshError,
41
41
  )
42
42
  from .store import FallbackTokenStore
43
- from .tooling import normalize_tool_inputs, to_responses_tools, tool_results_to_response_items
43
+ from .tooling import (
44
+ normalize_tool_inputs,
45
+ normalize_tool_output,
46
+ to_responses_tools,
47
+ tool_results_to_response_items,
48
+ )
44
49
  from .core_types import (
45
50
  GenerateResult,
46
51
  InputTokensCountResult,
@@ -1408,14 +1413,20 @@ class OAuthCodexClient:
1408
1413
  normalized: list[ToolResult] = []
1409
1414
  for item in tool_results:
1410
1415
  if isinstance(item, ToolResult):
1411
- normalized.append(item)
1416
+ normalized.append(
1417
+ ToolResult(
1418
+ tool_call_id=item.tool_call_id,
1419
+ name=item.name,
1420
+ output=normalize_tool_output(item.output),
1421
+ )
1422
+ )
1412
1423
  continue
1413
1424
  if isinstance(item, dict):
1414
1425
  normalized.append(
1415
1426
  ToolResult(
1416
1427
  tool_call_id=str(item["tool_call_id"]),
1417
1428
  name=str(item["name"]),
1418
- output=item["output"],
1429
+ output=normalize_tool_output(item.get("output")),
1419
1430
  )
1420
1431
  )
1421
1432
  continue
@@ -1,2 +1,2 @@
1
1
  __title__ = "oauth-codex"
2
- __version__ = "2.0.1"
2
+ __version__ = "2.0.2"
@@ -78,7 +78,7 @@ class ToolCall:
78
78
  class ToolResult:
79
79
  tool_call_id: str
80
80
  name: str
81
- output: str | dict[str, Any]
81
+ output: dict[str, Any]
82
82
 
83
83
 
84
84
  @dataclass
@@ -157,10 +157,20 @@ def to_responses_tools(
157
157
  return normalized
158
158
 
159
159
 
160
- def serialize_tool_output(output: str | dict[str, Any]) -> str:
161
- if isinstance(output, str):
160
+ def normalize_tool_output(output: Any) -> dict[str, Any]:
161
+ if isinstance(output, dict):
162
162
  return output
163
- return json.dumps(output, ensure_ascii=True)
163
+ if isinstance(output, str):
164
+ return {"output": output}
165
+ try:
166
+ json.dumps(output, ensure_ascii=True)
167
+ return {"output": output}
168
+ except TypeError:
169
+ return {"output": str(output)}
170
+
171
+
172
+ def serialize_tool_output(output: Any) -> str:
173
+ return json.dumps(normalize_tool_output(output), ensure_ascii=True)
164
174
 
165
175
 
166
176
  def tool_results_to_response_items(tool_results: list[ToolResult] | None) -> list[dict[str, Any]]:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: oauth-codex
3
- Version: 2.0.1
3
+ Version: 2.0.2
4
4
  Summary: Codex OAuth-based Python SDK with a single Client and generate-first API
5
5
  Author: Codex
6
6
  Requires-Python: >=3.11
@@ -77,9 +77,7 @@ def test_map_stream_raises_when_call_id_cannot_be_restored() -> None:
77
77
 
78
78
  def test_tool_results_to_response_items_rejects_empty_tool_call_id() -> None:
79
79
  with pytest.raises(SDKRequestError) as exc_info:
80
- tool_results_to_response_items(
81
- [ToolResult(tool_call_id=" ", name="tool", output="This is a tool response.")]
82
- )
80
+ tool_results_to_response_items([ToolResult(tool_call_id=" ", name="tool", output={"ok": True})])
83
81
  assert exc_info.value.provider_code == "invalid_tool_call_id"
84
82
 
85
83
 
@@ -120,6 +120,33 @@ def test_generate_tool_failure_is_forwarded_to_model(monkeypatch: pytest.MonkeyP
120
120
  assert tool_results[0].output == {"error": "boom"}
121
121
 
122
122
 
123
+ def test_generate_wraps_string_tool_output_as_dict(monkeypatch: pytest.MonkeyPatch) -> None:
124
+ client = _client()
125
+ calls: list[dict[str, object]] = []
126
+
127
+ def fake_generate(**kwargs):
128
+ calls.append(kwargs)
129
+ if len(calls) == 1:
130
+ return GenerateResult(
131
+ text="",
132
+ tool_calls=[ToolCall(id="call_1", name="echo", arguments_json='{"query":"hello"}')],
133
+ finish_reason="tool_calls",
134
+ response_id="resp_1",
135
+ )
136
+ return GenerateResult(text="done", tool_calls=[], finish_reason="stop", response_id="resp_2")
137
+
138
+ monkeypatch.setattr(client._engine, "generate", fake_generate)
139
+
140
+ def echo(query: str) -> str:
141
+ return query
142
+
143
+ out = client.generate("run once", tools=[echo])
144
+
145
+ assert out == "done"
146
+ tool_results = calls[1]["tool_results"]
147
+ assert tool_results[0].output == {"output": "hello"}
148
+
149
+
123
150
  def test_generate_raises_when_tool_round_limit_exceeded(monkeypatch: pytest.MonkeyPatch) -> None:
124
151
  client = _client()
125
152
  client.max_tool_rounds = 2
File without changes
File without changes