oauth-codex 2.0.2__tar.gz → 2.1.0__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.
- {oauth_codex-2.0.2 → oauth_codex-2.1.0}/PKG-INFO +19 -1
- {oauth_codex-2.0.2 → oauth_codex-2.1.0}/README.md +18 -0
- {oauth_codex-2.0.2 → oauth_codex-2.1.0}/pyproject.toml +1 -1
- {oauth_codex-2.0.2 → oauth_codex-2.1.0}/src/oauth_codex/_client.py +118 -8
- {oauth_codex-2.0.2 → oauth_codex-2.1.0}/src/oauth_codex/_engine.py +48 -5
- {oauth_codex-2.0.2 → oauth_codex-2.1.0}/src/oauth_codex/_version.py +1 -1
- {oauth_codex-2.0.2 → oauth_codex-2.1.0}/src/oauth_codex/tooling.py +52 -5
- {oauth_codex-2.0.2 → oauth_codex-2.1.0}/src/oauth_codex.egg-info/PKG-INFO +19 -1
- {oauth_codex-2.0.2 → oauth_codex-2.1.0}/tests/test_engine_stream_and_continuity.py +45 -1
- {oauth_codex-2.0.2 → oauth_codex-2.1.0}/tests/test_generate_async.py +35 -0
- {oauth_codex-2.0.2 → oauth_codex-2.1.0}/tests/test_generate_sync.py +109 -0
- {oauth_codex-2.0.2 → oauth_codex-2.1.0}/setup.cfg +0 -0
- {oauth_codex-2.0.2 → oauth_codex-2.1.0}/src/oauth_codex/__init__.py +0 -0
- {oauth_codex-2.0.2 → oauth_codex-2.1.0}/src/oauth_codex/_base_client.py +0 -0
- {oauth_codex-2.0.2 → oauth_codex-2.1.0}/src/oauth_codex/_exceptions.py +0 -0
- {oauth_codex-2.0.2 → oauth_codex-2.1.0}/src/oauth_codex/_models.py +0 -0
- {oauth_codex-2.0.2 → oauth_codex-2.1.0}/src/oauth_codex/_module_client.py +0 -0
- {oauth_codex-2.0.2 → oauth_codex-2.1.0}/src/oauth_codex/_resource.py +0 -0
- {oauth_codex-2.0.2 → oauth_codex-2.1.0}/src/oauth_codex/_types.py +0 -0
- {oauth_codex-2.0.2 → oauth_codex-2.1.0}/src/oauth_codex/auth/__init__.py +0 -0
- {oauth_codex-2.0.2 → oauth_codex-2.1.0}/src/oauth_codex/auth/config.py +0 -0
- {oauth_codex-2.0.2 → oauth_codex-2.1.0}/src/oauth_codex/auth/pkce.py +0 -0
- {oauth_codex-2.0.2 → oauth_codex-2.1.0}/src/oauth_codex/auth/store.py +0 -0
- {oauth_codex-2.0.2 → oauth_codex-2.1.0}/src/oauth_codex/auth/token_manager.py +0 -0
- {oauth_codex-2.0.2 → oauth_codex-2.1.0}/src/oauth_codex/compat_store.py +0 -0
- {oauth_codex-2.0.2 → oauth_codex-2.1.0}/src/oauth_codex/core_types.py +0 -0
- {oauth_codex-2.0.2 → oauth_codex-2.1.0}/src/oauth_codex/errors.py +0 -0
- {oauth_codex-2.0.2 → oauth_codex-2.1.0}/src/oauth_codex/py.typed +0 -0
- {oauth_codex-2.0.2 → oauth_codex-2.1.0}/src/oauth_codex/resources/__init__.py +0 -0
- {oauth_codex-2.0.2 → oauth_codex-2.1.0}/src/oauth_codex/resources/_wrappers.py +0 -0
- {oauth_codex-2.0.2 → oauth_codex-2.1.0}/src/oauth_codex/resources/files.py +0 -0
- {oauth_codex-2.0.2 → oauth_codex-2.1.0}/src/oauth_codex/resources/models.py +0 -0
- {oauth_codex-2.0.2 → oauth_codex-2.1.0}/src/oauth_codex/resources/responses/__init__.py +0 -0
- {oauth_codex-2.0.2 → oauth_codex-2.1.0}/src/oauth_codex/resources/responses/_helpers.py +0 -0
- {oauth_codex-2.0.2 → oauth_codex-2.1.0}/src/oauth_codex/resources/responses/input_tokens.py +0 -0
- {oauth_codex-2.0.2 → oauth_codex-2.1.0}/src/oauth_codex/resources/responses/responses.py +0 -0
- {oauth_codex-2.0.2 → oauth_codex-2.1.0}/src/oauth_codex/resources/vector_stores/__init__.py +0 -0
- {oauth_codex-2.0.2 → oauth_codex-2.1.0}/src/oauth_codex/resources/vector_stores/file_batches.py +0 -0
- {oauth_codex-2.0.2 → oauth_codex-2.1.0}/src/oauth_codex/resources/vector_stores/files.py +0 -0
- {oauth_codex-2.0.2 → oauth_codex-2.1.0}/src/oauth_codex/resources/vector_stores/vector_stores.py +0 -0
- {oauth_codex-2.0.2 → oauth_codex-2.1.0}/src/oauth_codex/store.py +0 -0
- {oauth_codex-2.0.2 → oauth_codex-2.1.0}/src/oauth_codex/types/__init__.py +0 -0
- {oauth_codex-2.0.2 → oauth_codex-2.1.0}/src/oauth_codex/types/file_deleted.py +0 -0
- {oauth_codex-2.0.2 → oauth_codex-2.1.0}/src/oauth_codex/types/file_object.py +0 -0
- {oauth_codex-2.0.2 → oauth_codex-2.1.0}/src/oauth_codex/types/responses/__init__.py +0 -0
- {oauth_codex-2.0.2 → oauth_codex-2.1.0}/src/oauth_codex/types/responses/input_token_count_response.py +0 -0
- {oauth_codex-2.0.2 → oauth_codex-2.1.0}/src/oauth_codex/types/responses/response.py +0 -0
- {oauth_codex-2.0.2 → oauth_codex-2.1.0}/src/oauth_codex/types/responses/response_stream_event.py +0 -0
- {oauth_codex-2.0.2 → oauth_codex-2.1.0}/src/oauth_codex/types/shared/__init__.py +0 -0
- {oauth_codex-2.0.2 → oauth_codex-2.1.0}/src/oauth_codex/types/shared/model_capabilities.py +0 -0
- {oauth_codex-2.0.2 → oauth_codex-2.1.0}/src/oauth_codex/types/shared/usage.py +0 -0
- {oauth_codex-2.0.2 → oauth_codex-2.1.0}/src/oauth_codex/types/vector_stores/__init__.py +0 -0
- {oauth_codex-2.0.2 → oauth_codex-2.1.0}/src/oauth_codex/types/vector_stores/vector_store.py +0 -0
- {oauth_codex-2.0.2 → oauth_codex-2.1.0}/src/oauth_codex/types/vector_stores/vector_store_deleted.py +0 -0
- {oauth_codex-2.0.2 → oauth_codex-2.1.0}/src/oauth_codex/types/vector_stores/vector_store_file.py +0 -0
- {oauth_codex-2.0.2 → oauth_codex-2.1.0}/src/oauth_codex/types/vector_stores/vector_store_file_batch.py +0 -0
- {oauth_codex-2.0.2 → oauth_codex-2.1.0}/src/oauth_codex/types/vector_stores/vector_store_search_response.py +0 -0
- {oauth_codex-2.0.2 → oauth_codex-2.1.0}/src/oauth_codex/version.py +0 -0
- {oauth_codex-2.0.2 → oauth_codex-2.1.0}/src/oauth_codex.egg-info/SOURCES.txt +0 -0
- {oauth_codex-2.0.2 → oauth_codex-2.1.0}/src/oauth_codex.egg-info/dependency_links.txt +0 -0
- {oauth_codex-2.0.2 → oauth_codex-2.1.0}/src/oauth_codex.egg-info/requires.txt +0 -0
- {oauth_codex-2.0.2 → oauth_codex-2.1.0}/src/oauth_codex.egg-info/top_level.txt +0 -0
- {oauth_codex-2.0.2 → oauth_codex-2.1.0}/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
|
|
3
|
+
Version: 2.1.0
|
|
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
|
|
@@ -64,6 +64,24 @@ text = client.generate(
|
|
|
64
64
|
print(text)
|
|
65
65
|
```
|
|
66
66
|
|
|
67
|
+
Single-parameter Pydantic tool inputs are also supported.
|
|
68
|
+
|
|
69
|
+
```python
|
|
70
|
+
from pydantic import BaseModel
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
class ToolInput(BaseModel):
|
|
74
|
+
query: str
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def tool(input: ToolInput) -> str:
|
|
78
|
+
return f"Tool received query: {input.query}"
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
text = client.generate("Use the tool", tools=[tool])
|
|
82
|
+
print(text)
|
|
83
|
+
```
|
|
84
|
+
|
|
67
85
|
If a tool raises an exception, the SDK forwards it to the model as `{\"error\": ...}` and continues the loop.
|
|
68
86
|
|
|
69
87
|
## Async
|
|
@@ -50,6 +50,24 @@ text = client.generate(
|
|
|
50
50
|
print(text)
|
|
51
51
|
```
|
|
52
52
|
|
|
53
|
+
Single-parameter Pydantic tool inputs are also supported.
|
|
54
|
+
|
|
55
|
+
```python
|
|
56
|
+
from pydantic import BaseModel
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
class ToolInput(BaseModel):
|
|
60
|
+
query: str
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def tool(input: ToolInput) -> str:
|
|
64
|
+
return f"Tool received query: {input.query}"
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
text = client.generate("Use the tool", tools=[tool])
|
|
68
|
+
print(text)
|
|
69
|
+
```
|
|
70
|
+
|
|
53
71
|
If a tool raises an exception, the SDK forwards it to the model as `{\"error\": ...}` and continues the loop.
|
|
54
72
|
|
|
55
73
|
## Async
|
|
@@ -6,7 +6,9 @@ import inspect
|
|
|
6
6
|
import json
|
|
7
7
|
import mimetypes
|
|
8
8
|
from pathlib import Path
|
|
9
|
-
from typing import Any, AsyncIterator, Callable, Iterator
|
|
9
|
+
from typing import Any, AsyncIterator, Callable, Iterator, get_type_hints
|
|
10
|
+
|
|
11
|
+
from pydantic import BaseModel
|
|
10
12
|
|
|
11
13
|
from ._base_client import SyncAPIClient
|
|
12
14
|
from ._engine import OAuthCodexClient as _EngineClient
|
|
@@ -23,7 +25,7 @@ from .store import FallbackTokenStore
|
|
|
23
25
|
from .tooling import callable_to_tool_schema, normalize_tool_output
|
|
24
26
|
|
|
25
27
|
DEFAULT_MODEL = "gpt-5.3-codex"
|
|
26
|
-
DEFAULT_MAX_TOOL_ROUNDS =
|
|
28
|
+
DEFAULT_MAX_TOOL_ROUNDS = 16
|
|
27
29
|
|
|
28
30
|
|
|
29
31
|
class OAuthCodexClient(SyncAPIClient):
|
|
@@ -105,7 +107,11 @@ class OAuthCodexClient(SyncAPIClient):
|
|
|
105
107
|
for _ in range(self.max_tool_rounds):
|
|
106
108
|
result = self._engine.generate(
|
|
107
109
|
model=self._resolve_model(model),
|
|
108
|
-
messages=
|
|
110
|
+
messages=self._messages_for_round(
|
|
111
|
+
messages=messages,
|
|
112
|
+
previous_response_id=previous_response_id,
|
|
113
|
+
tool_results=tool_results,
|
|
114
|
+
),
|
|
109
115
|
tools=normalized_tools,
|
|
110
116
|
tool_results=tool_results,
|
|
111
117
|
reasoning={"effort": reasoning_effort},
|
|
@@ -151,7 +157,11 @@ class OAuthCodexClient(SyncAPIClient):
|
|
|
151
157
|
for _ in range(self.max_tool_rounds):
|
|
152
158
|
result = await self._engine.agenerate(
|
|
153
159
|
model=self._resolve_model(model),
|
|
154
|
-
messages=
|
|
160
|
+
messages=self._messages_for_round(
|
|
161
|
+
messages=messages,
|
|
162
|
+
previous_response_id=previous_response_id,
|
|
163
|
+
tool_results=tool_results,
|
|
164
|
+
),
|
|
155
165
|
tools=normalized_tools,
|
|
156
166
|
tool_results=tool_results,
|
|
157
167
|
reasoning={"effort": reasoning_effort},
|
|
@@ -198,7 +208,11 @@ class OAuthCodexClient(SyncAPIClient):
|
|
|
198
208
|
round_response_id: str | None = previous_response_id
|
|
199
209
|
events = self._engine.generate_stream(
|
|
200
210
|
model=self._resolve_model(model),
|
|
201
|
-
messages=
|
|
211
|
+
messages=self._messages_for_round(
|
|
212
|
+
messages=messages,
|
|
213
|
+
previous_response_id=previous_response_id,
|
|
214
|
+
tool_results=tool_results,
|
|
215
|
+
),
|
|
202
216
|
tools=normalized_tools,
|
|
203
217
|
tool_results=tool_results,
|
|
204
218
|
reasoning={"effort": reasoning_effort},
|
|
@@ -247,7 +261,11 @@ class OAuthCodexClient(SyncAPIClient):
|
|
|
247
261
|
round_response_id: str | None = previous_response_id
|
|
248
262
|
events = await self._engine.agenerate_stream(
|
|
249
263
|
model=self._resolve_model(model),
|
|
250
|
-
messages=
|
|
264
|
+
messages=self._messages_for_round(
|
|
265
|
+
messages=messages,
|
|
266
|
+
previous_response_id=previous_response_id,
|
|
267
|
+
tool_results=tool_results,
|
|
268
|
+
),
|
|
251
269
|
tools=normalized_tools,
|
|
252
270
|
tool_results=tool_results,
|
|
253
271
|
reasoning={"effort": reasoning_effort},
|
|
@@ -276,6 +294,28 @@ class OAuthCodexClient(SyncAPIClient):
|
|
|
276
294
|
def _resolve_model(self, model: str | None) -> str:
|
|
277
295
|
return model or self.default_model
|
|
278
296
|
|
|
297
|
+
def _is_tool_continuation_round(
|
|
298
|
+
self,
|
|
299
|
+
*,
|
|
300
|
+
previous_response_id: str | None,
|
|
301
|
+
tool_results: list[ToolResult] | None,
|
|
302
|
+
) -> bool:
|
|
303
|
+
return bool(previous_response_id) and bool(tool_results)
|
|
304
|
+
|
|
305
|
+
def _messages_for_round(
|
|
306
|
+
self,
|
|
307
|
+
*,
|
|
308
|
+
messages: list[Message],
|
|
309
|
+
previous_response_id: str | None,
|
|
310
|
+
tool_results: list[ToolResult] | None,
|
|
311
|
+
) -> list[Message]:
|
|
312
|
+
if self._is_tool_continuation_round(
|
|
313
|
+
previous_response_id=previous_response_id,
|
|
314
|
+
tool_results=tool_results,
|
|
315
|
+
):
|
|
316
|
+
return []
|
|
317
|
+
return messages
|
|
318
|
+
|
|
279
319
|
def _build_messages(
|
|
280
320
|
self,
|
|
281
321
|
*,
|
|
@@ -371,7 +411,8 @@ class OAuthCodexClient(SyncAPIClient):
|
|
|
371
411
|
else:
|
|
372
412
|
try:
|
|
373
413
|
kwargs = self._parse_tool_kwargs(call.arguments_json)
|
|
374
|
-
|
|
414
|
+
normalized_kwargs = self._normalize_tool_kwargs(tool, kwargs)
|
|
415
|
+
value = tool(**normalized_kwargs)
|
|
375
416
|
if inspect.isawaitable(value):
|
|
376
417
|
raise TypeError("async tool is not supported in generate(); use agenerate()")
|
|
377
418
|
output = self._normalize_tool_output(value)
|
|
@@ -394,7 +435,8 @@ class OAuthCodexClient(SyncAPIClient):
|
|
|
394
435
|
else:
|
|
395
436
|
try:
|
|
396
437
|
kwargs = self._parse_tool_kwargs(call.arguments_json)
|
|
397
|
-
|
|
438
|
+
normalized_kwargs = self._normalize_tool_kwargs(tool, kwargs)
|
|
439
|
+
value = tool(**normalized_kwargs)
|
|
398
440
|
if inspect.isawaitable(value):
|
|
399
441
|
value = await value
|
|
400
442
|
output = self._normalize_tool_output(value)
|
|
@@ -411,6 +453,74 @@ class OAuthCodexClient(SyncAPIClient):
|
|
|
411
453
|
raise TypeError("tool arguments must be a JSON object")
|
|
412
454
|
return parsed
|
|
413
455
|
|
|
456
|
+
def _normalize_tool_kwargs(
|
|
457
|
+
self,
|
|
458
|
+
tool: Callable[..., Any],
|
|
459
|
+
kwargs: dict[str, Any],
|
|
460
|
+
) -> dict[str, Any]:
|
|
461
|
+
signature = inspect.signature(tool)
|
|
462
|
+
resolved_hints = self._resolve_tool_type_hints(tool)
|
|
463
|
+
params = [
|
|
464
|
+
param
|
|
465
|
+
for param in signature.parameters.values()
|
|
466
|
+
if param.kind not in (param.VAR_POSITIONAL, param.VAR_KEYWORD)
|
|
467
|
+
]
|
|
468
|
+
if not params:
|
|
469
|
+
return kwargs
|
|
470
|
+
|
|
471
|
+
if len(params) == 1:
|
|
472
|
+
param = params[0]
|
|
473
|
+
model_type = self._resolve_pydantic_model_type(
|
|
474
|
+
resolved_hints.get(param.name, param.annotation)
|
|
475
|
+
)
|
|
476
|
+
if model_type is not None:
|
|
477
|
+
if param.name in kwargs:
|
|
478
|
+
payload = kwargs[param.name]
|
|
479
|
+
if isinstance(payload, model_type):
|
|
480
|
+
return kwargs
|
|
481
|
+
if not isinstance(payload, dict):
|
|
482
|
+
raise TypeError(f"tool argument `{param.name}` must be a JSON object")
|
|
483
|
+
normalized = dict(kwargs)
|
|
484
|
+
normalized[param.name] = model_type.model_validate(payload)
|
|
485
|
+
return normalized
|
|
486
|
+
|
|
487
|
+
if not kwargs and param.default is not inspect._empty:
|
|
488
|
+
return kwargs
|
|
489
|
+
|
|
490
|
+
payload = kwargs
|
|
491
|
+
normalized_payload = (
|
|
492
|
+
payload
|
|
493
|
+
if isinstance(payload, model_type)
|
|
494
|
+
else model_type.model_validate(payload)
|
|
495
|
+
)
|
|
496
|
+
return {param.name: normalized_payload}
|
|
497
|
+
|
|
498
|
+
normalized = dict(kwargs)
|
|
499
|
+
for param in params:
|
|
500
|
+
model_type = self._resolve_pydantic_model_type(
|
|
501
|
+
resolved_hints.get(param.name, param.annotation)
|
|
502
|
+
)
|
|
503
|
+
if model_type is None or param.name not in normalized:
|
|
504
|
+
continue
|
|
505
|
+
payload = normalized[param.name]
|
|
506
|
+
if isinstance(payload, model_type):
|
|
507
|
+
continue
|
|
508
|
+
if not isinstance(payload, dict):
|
|
509
|
+
raise TypeError(f"tool argument `{param.name}` must be a JSON object")
|
|
510
|
+
normalized[param.name] = model_type.model_validate(payload)
|
|
511
|
+
return normalized
|
|
512
|
+
|
|
513
|
+
def _resolve_pydantic_model_type(self, annotation: Any) -> type[BaseModel] | None:
|
|
514
|
+
if isinstance(annotation, type) and issubclass(annotation, BaseModel):
|
|
515
|
+
return annotation
|
|
516
|
+
return None
|
|
517
|
+
|
|
518
|
+
def _resolve_tool_type_hints(self, tool: Callable[..., Any]) -> dict[str, Any]:
|
|
519
|
+
try:
|
|
520
|
+
return get_type_hints(tool)
|
|
521
|
+
except Exception:
|
|
522
|
+
return {}
|
|
523
|
+
|
|
414
524
|
def _normalize_tool_output(self, output: Any) -> dict[str, Any]:
|
|
415
525
|
return normalize_tool_output(output)
|
|
416
526
|
|
|
@@ -199,7 +199,15 @@ class OAuthCodexClient:
|
|
|
199
199
|
) -> str | GenerateResult:
|
|
200
200
|
self._require_responses_mode(api_mode)
|
|
201
201
|
|
|
202
|
-
|
|
202
|
+
allows_empty_messages = self._is_tool_continuation_request(
|
|
203
|
+
previous_response_id=previous_response_id,
|
|
204
|
+
tool_results=tool_results,
|
|
205
|
+
)
|
|
206
|
+
normalized_messages = self._normalize_messages(
|
|
207
|
+
prompt=prompt,
|
|
208
|
+
messages=messages,
|
|
209
|
+
allow_empty_messages=allows_empty_messages,
|
|
210
|
+
)
|
|
203
211
|
normalized_tools = normalize_tool_inputs(tools)
|
|
204
212
|
normalized_tool_results = self._normalize_tool_results(tool_results)
|
|
205
213
|
|
|
@@ -276,7 +284,15 @@ class OAuthCodexClient:
|
|
|
276
284
|
) -> str | GenerateResult:
|
|
277
285
|
self._require_responses_mode(api_mode)
|
|
278
286
|
|
|
279
|
-
|
|
287
|
+
allows_empty_messages = self._is_tool_continuation_request(
|
|
288
|
+
previous_response_id=previous_response_id,
|
|
289
|
+
tool_results=tool_results,
|
|
290
|
+
)
|
|
291
|
+
normalized_messages = self._normalize_messages(
|
|
292
|
+
prompt=prompt,
|
|
293
|
+
messages=messages,
|
|
294
|
+
allow_empty_messages=allows_empty_messages,
|
|
295
|
+
)
|
|
280
296
|
normalized_tools = normalize_tool_inputs(tools)
|
|
281
297
|
normalized_tool_results = self._normalize_tool_results(tool_results)
|
|
282
298
|
|
|
@@ -353,7 +369,15 @@ class OAuthCodexClient:
|
|
|
353
369
|
) -> Iterator[str] | Iterator[StreamEvent]:
|
|
354
370
|
self._require_responses_mode(api_mode)
|
|
355
371
|
|
|
356
|
-
|
|
372
|
+
allows_empty_messages = self._is_tool_continuation_request(
|
|
373
|
+
previous_response_id=previous_response_id,
|
|
374
|
+
tool_results=tool_results,
|
|
375
|
+
)
|
|
376
|
+
normalized_messages = self._normalize_messages(
|
|
377
|
+
prompt=prompt,
|
|
378
|
+
messages=messages,
|
|
379
|
+
allow_empty_messages=allows_empty_messages,
|
|
380
|
+
)
|
|
357
381
|
normalized_tools = normalize_tool_inputs(tools)
|
|
358
382
|
normalized_tool_results = self._normalize_tool_results(tool_results)
|
|
359
383
|
|
|
@@ -426,7 +450,15 @@ class OAuthCodexClient:
|
|
|
426
450
|
) -> AsyncIterator[str] | AsyncIterator[StreamEvent]:
|
|
427
451
|
self._require_responses_mode(api_mode)
|
|
428
452
|
|
|
429
|
-
|
|
453
|
+
allows_empty_messages = self._is_tool_continuation_request(
|
|
454
|
+
previous_response_id=previous_response_id,
|
|
455
|
+
tool_results=tool_results,
|
|
456
|
+
)
|
|
457
|
+
normalized_messages = self._normalize_messages(
|
|
458
|
+
prompt=prompt,
|
|
459
|
+
messages=messages,
|
|
460
|
+
allow_empty_messages=allows_empty_messages,
|
|
461
|
+
)
|
|
430
462
|
normalized_tools = normalize_tool_inputs(tools)
|
|
431
463
|
normalized_tool_results = self._normalize_tool_results(tool_results)
|
|
432
464
|
|
|
@@ -1392,6 +1424,7 @@ class OAuthCodexClient:
|
|
|
1392
1424
|
*,
|
|
1393
1425
|
prompt: str | None,
|
|
1394
1426
|
messages: list[Message] | None,
|
|
1427
|
+
allow_empty_messages: bool = False,
|
|
1395
1428
|
) -> list[Message]:
|
|
1396
1429
|
if (prompt is None and messages is None) or (prompt is not None and messages is not None):
|
|
1397
1430
|
raise ValueError("Provide exactly one of `prompt` or `messages`")
|
|
@@ -1399,10 +1432,20 @@ class OAuthCodexClient:
|
|
|
1399
1432
|
if prompt is not None:
|
|
1400
1433
|
return [{"role": "user", "content": prompt}]
|
|
1401
1434
|
|
|
1402
|
-
if not isinstance(messages, list)
|
|
1435
|
+
if not isinstance(messages, list):
|
|
1436
|
+
raise ValueError("`messages` must be a non-empty list")
|
|
1437
|
+
if not messages and not allow_empty_messages:
|
|
1403
1438
|
raise ValueError("`messages` must be a non-empty list")
|
|
1404
1439
|
return [dict(item) for item in messages]
|
|
1405
1440
|
|
|
1441
|
+
def _is_tool_continuation_request(
|
|
1442
|
+
self,
|
|
1443
|
+
*,
|
|
1444
|
+
previous_response_id: str | None,
|
|
1445
|
+
tool_results: list[ToolResult] | None,
|
|
1446
|
+
) -> bool:
|
|
1447
|
+
return bool(previous_response_id) and bool(tool_results)
|
|
1448
|
+
|
|
1406
1449
|
def _normalize_tool_results(
|
|
1407
1450
|
self,
|
|
1408
1451
|
tool_results: list[ToolResult] | None,
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
__title__ = "oauth-codex"
|
|
2
|
-
__version__ = "2.0
|
|
2
|
+
__version__ = "2.1.0"
|
|
@@ -3,11 +3,32 @@ from __future__ import annotations
|
|
|
3
3
|
import inspect
|
|
4
4
|
import json
|
|
5
5
|
from types import UnionType
|
|
6
|
-
from typing import Any, get_args, get_origin
|
|
6
|
+
from typing import Any, get_args, get_origin, get_type_hints
|
|
7
7
|
|
|
8
8
|
from .core_types import ToolInput, ToolResult, ToolSchema
|
|
9
9
|
from .errors import SDKRequestError
|
|
10
10
|
|
|
11
|
+
try:
|
|
12
|
+
from pydantic import BaseModel
|
|
13
|
+
except Exception: # pragma: no cover - pydantic is a runtime dependency
|
|
14
|
+
BaseModel = None # type: ignore[assignment]
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def _is_pydantic_model_type(annotation: Any) -> bool:
|
|
18
|
+
return bool(
|
|
19
|
+
BaseModel is not None
|
|
20
|
+
and isinstance(annotation, type)
|
|
21
|
+
and issubclass(annotation, BaseModel)
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def _pydantic_model_to_schema(model_type: type[Any]) -> dict[str, Any]:
|
|
26
|
+
if hasattr(model_type, "model_json_schema"):
|
|
27
|
+
schema = model_type.model_json_schema()
|
|
28
|
+
if isinstance(schema, dict):
|
|
29
|
+
return schema
|
|
30
|
+
return {"type": "object"}
|
|
31
|
+
|
|
11
32
|
|
|
12
33
|
def _python_type_to_schema(annotation: Any) -> dict[str, Any]:
|
|
13
34
|
if annotation is inspect._empty:
|
|
@@ -33,6 +54,8 @@ def _python_type_to_schema(annotation: Any) -> dict[str, Any]:
|
|
|
33
54
|
args = get_args(annotation)
|
|
34
55
|
|
|
35
56
|
if origin is None:
|
|
57
|
+
if _is_pydantic_model_type(annotation):
|
|
58
|
+
return _pydantic_model_to_schema(annotation)
|
|
36
59
|
if annotation is str:
|
|
37
60
|
return {"type": "string"}
|
|
38
61
|
if annotation is int:
|
|
@@ -67,16 +90,39 @@ def _python_type_to_schema(annotation: Any) -> dict[str, Any]:
|
|
|
67
90
|
|
|
68
91
|
def callable_to_tool_schema(func: Any) -> ToolSchema:
|
|
69
92
|
signature = inspect.signature(func)
|
|
93
|
+
try:
|
|
94
|
+
resolved_hints = get_type_hints(func)
|
|
95
|
+
except Exception:
|
|
96
|
+
resolved_hints = {}
|
|
97
|
+
|
|
70
98
|
doc = inspect.getdoc(func) or ""
|
|
71
99
|
description = doc.splitlines()[0] if doc else f"Tool `{getattr(func, '__name__', 'tool')}`"
|
|
72
100
|
|
|
101
|
+
params = [
|
|
102
|
+
param
|
|
103
|
+
for param in signature.parameters.values()
|
|
104
|
+
if param.kind not in (param.VAR_POSITIONAL, param.VAR_KEYWORD)
|
|
105
|
+
]
|
|
106
|
+
if len(params) == 1:
|
|
107
|
+
single = params[0]
|
|
108
|
+
single_annotation = resolved_hints.get(single.name, single.annotation)
|
|
109
|
+
if _is_pydantic_model_type(single_annotation):
|
|
110
|
+
model_schema = _python_type_to_schema(single_annotation)
|
|
111
|
+
if model_schema.get("type") == "object":
|
|
112
|
+
return {
|
|
113
|
+
"type": "function",
|
|
114
|
+
"name": getattr(func, "__name__", "tool"),
|
|
115
|
+
"description": description,
|
|
116
|
+
"parameters": model_schema,
|
|
117
|
+
}
|
|
118
|
+
|
|
73
119
|
properties: dict[str, Any] = {}
|
|
74
120
|
required: list[str] = []
|
|
75
121
|
|
|
76
|
-
for
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
properties[name] = _python_type_to_schema(
|
|
122
|
+
for param in params:
|
|
123
|
+
name = param.name
|
|
124
|
+
annotation = resolved_hints.get(name, param.annotation)
|
|
125
|
+
properties[name] = _python_type_to_schema(annotation)
|
|
80
126
|
if param.default is inspect._empty:
|
|
81
127
|
required.append(name)
|
|
82
128
|
|
|
@@ -93,6 +139,7 @@ def callable_to_tool_schema(func: Any) -> ToolSchema:
|
|
|
93
139
|
}
|
|
94
140
|
|
|
95
141
|
|
|
142
|
+
|
|
96
143
|
def _normalize_dict_tool(tool: dict[str, Any]) -> ToolSchema:
|
|
97
144
|
if tool.get("type") == "function" and "function" in tool and isinstance(tool["function"], dict):
|
|
98
145
|
fn = tool["function"]
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: oauth-codex
|
|
3
|
-
Version: 2.0
|
|
3
|
+
Version: 2.1.0
|
|
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
|
|
@@ -64,6 +64,24 @@ text = client.generate(
|
|
|
64
64
|
print(text)
|
|
65
65
|
```
|
|
66
66
|
|
|
67
|
+
Single-parameter Pydantic tool inputs are also supported.
|
|
68
|
+
|
|
69
|
+
```python
|
|
70
|
+
from pydantic import BaseModel
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
class ToolInput(BaseModel):
|
|
74
|
+
query: str
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def tool(input: ToolInput) -> str:
|
|
78
|
+
return f"Tool received query: {input.query}"
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
text = client.generate("Use the tool", tools=[tool])
|
|
82
|
+
print(text)
|
|
83
|
+
```
|
|
84
|
+
|
|
67
85
|
If a tool raises an exception, the SDK forwards it to the model as `{\"error\": ...}` and continues the loop.
|
|
68
86
|
|
|
69
87
|
## Async
|
|
@@ -4,7 +4,7 @@ import pytest
|
|
|
4
4
|
|
|
5
5
|
from conftest import InMemoryTokenStore
|
|
6
6
|
from oauth_codex._engine import OAuthCodexClient as EngineClient
|
|
7
|
-
from oauth_codex.core_types import OAuthTokens, ToolResult
|
|
7
|
+
from oauth_codex.core_types import GenerateResult, OAuthTokens, ToolResult
|
|
8
8
|
from oauth_codex.errors import SDKRequestError
|
|
9
9
|
from oauth_codex.tooling import tool_results_to_response_items
|
|
10
10
|
|
|
@@ -168,3 +168,47 @@ def test_extract_output_items_for_continuation_sanitizes_response_output() -> No
|
|
|
168
168
|
assert [item.get("type") for item in items] == ["message", "function_call"]
|
|
169
169
|
assert all("id" not in item for item in items)
|
|
170
170
|
assert all("status" not in item for item in items)
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
def test_generate_allows_empty_messages_for_tool_continuation(
|
|
174
|
+
monkeypatch: pytest.MonkeyPatch,
|
|
175
|
+
) -> None:
|
|
176
|
+
engine = _engine()
|
|
177
|
+
captured: dict[str, object] = {}
|
|
178
|
+
|
|
179
|
+
def fake_generate_responses_sync(**kwargs):
|
|
180
|
+
captured.update(kwargs)
|
|
181
|
+
return GenerateResult(text="ok", tool_calls=[], finish_reason="stop")
|
|
182
|
+
|
|
183
|
+
monkeypatch.setattr(engine, "_generate_responses_sync", fake_generate_responses_sync)
|
|
184
|
+
|
|
185
|
+
out = engine.generate(
|
|
186
|
+
model="gpt-5.3-codex",
|
|
187
|
+
messages=[],
|
|
188
|
+
tool_results=[ToolResult(tool_call_id="call_1", name="tool", output={"ok": True})],
|
|
189
|
+
previous_response_id="resp_1",
|
|
190
|
+
return_details=True,
|
|
191
|
+
)
|
|
192
|
+
|
|
193
|
+
assert isinstance(out, GenerateResult)
|
|
194
|
+
assert captured["messages"] == []
|
|
195
|
+
|
|
196
|
+
|
|
197
|
+
def test_generate_rejects_empty_messages_without_tool_continuation() -> None:
|
|
198
|
+
engine = _engine()
|
|
199
|
+
|
|
200
|
+
with pytest.raises(ValueError, match="non-empty"):
|
|
201
|
+
engine.generate(
|
|
202
|
+
model="gpt-5.3-codex",
|
|
203
|
+
messages=[],
|
|
204
|
+
tool_results=[ToolResult(tool_call_id="call_1", name="tool", output={"ok": True})],
|
|
205
|
+
previous_response_id=None,
|
|
206
|
+
)
|
|
207
|
+
|
|
208
|
+
with pytest.raises(ValueError, match="non-empty"):
|
|
209
|
+
engine.generate(
|
|
210
|
+
model="gpt-5.3-codex",
|
|
211
|
+
messages=[],
|
|
212
|
+
tool_results=[],
|
|
213
|
+
previous_response_id="resp_1",
|
|
214
|
+
)
|
|
@@ -1,12 +1,17 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
import pytest
|
|
4
|
+
from pydantic import BaseModel
|
|
4
5
|
|
|
5
6
|
from conftest import InMemoryTokenStore
|
|
6
7
|
from oauth_codex import Client
|
|
7
8
|
from oauth_codex.core_types import GenerateResult, OAuthTokens, StreamEvent, ToolCall
|
|
8
9
|
|
|
9
10
|
|
|
11
|
+
class ToolInput(BaseModel):
|
|
12
|
+
query: str
|
|
13
|
+
|
|
14
|
+
|
|
10
15
|
def _client() -> Client:
|
|
11
16
|
return Client(
|
|
12
17
|
token_store=InMemoryTokenStore(
|
|
@@ -40,6 +45,7 @@ async def test_agenerate_auto_function_calling(monkeypatch: pytest.MonkeyPatch)
|
|
|
40
45
|
|
|
41
46
|
assert out == "12"
|
|
42
47
|
assert calls[1]["previous_response_id"] == "resp_1"
|
|
48
|
+
assert calls[1]["messages"] == []
|
|
43
49
|
tool_results = calls[1]["tool_results"]
|
|
44
50
|
assert tool_results[0].output == {"sum": 12}
|
|
45
51
|
|
|
@@ -80,5 +86,34 @@ async def test_astream_supports_tool_calls(monkeypatch: pytest.MonkeyPatch) -> N
|
|
|
80
86
|
|
|
81
87
|
assert out == ["X", "Y"]
|
|
82
88
|
assert calls[1]["previous_response_id"] == "resp_1"
|
|
89
|
+
assert calls[1]["messages"] == []
|
|
83
90
|
tool_results = calls[1]["tool_results"]
|
|
84
91
|
assert tool_results[0].output == {"product": 12}
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
@pytest.mark.asyncio
|
|
95
|
+
async def test_agenerate_supports_single_pydantic_tool_input(monkeypatch: pytest.MonkeyPatch) -> None:
|
|
96
|
+
client = _client()
|
|
97
|
+
calls: list[dict[str, object]] = []
|
|
98
|
+
|
|
99
|
+
async def fake_agenerate(**kwargs):
|
|
100
|
+
calls.append(kwargs)
|
|
101
|
+
if len(calls) == 1:
|
|
102
|
+
return GenerateResult(
|
|
103
|
+
text="",
|
|
104
|
+
tool_calls=[ToolCall(id="call_1", name="tool", arguments_json='{"query":"hello"}')],
|
|
105
|
+
finish_reason="tool_calls",
|
|
106
|
+
response_id="resp_1",
|
|
107
|
+
)
|
|
108
|
+
return GenerateResult(text="done", tool_calls=[], finish_reason="stop", response_id="resp_2")
|
|
109
|
+
|
|
110
|
+
monkeypatch.setattr(client._engine, "agenerate", fake_agenerate)
|
|
111
|
+
|
|
112
|
+
def tool(input: ToolInput) -> str:
|
|
113
|
+
return f"Tool received query: {input.query}"
|
|
114
|
+
|
|
115
|
+
out = await client.agenerate("run", tools=[tool])
|
|
116
|
+
|
|
117
|
+
assert out == "done"
|
|
118
|
+
tool_results = calls[1]["tool_results"]
|
|
119
|
+
assert tool_results[0].output == {"output": "Tool received query: hello"}
|
|
@@ -1,12 +1,21 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
import pytest
|
|
4
|
+
from pydantic import BaseModel, Field
|
|
4
5
|
|
|
5
6
|
from conftest import InMemoryTokenStore
|
|
6
7
|
from oauth_codex import Client
|
|
7
8
|
from oauth_codex.core_types import GenerateResult, OAuthTokens, StreamEvent, ToolCall
|
|
8
9
|
|
|
9
10
|
|
|
11
|
+
class ToolInputWithDescription(BaseModel):
|
|
12
|
+
query: str = Field(..., description="The query to be processed by the tool.")
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class ToolInput(BaseModel):
|
|
16
|
+
query: str
|
|
17
|
+
|
|
18
|
+
|
|
10
19
|
def _client() -> Client:
|
|
11
20
|
return Client(
|
|
12
21
|
token_store=InMemoryTokenStore(
|
|
@@ -86,12 +95,42 @@ def test_generate_auto_function_calling(monkeypatch: pytest.MonkeyPatch) -> None
|
|
|
86
95
|
|
|
87
96
|
assert out == "5"
|
|
88
97
|
assert calls[1]["previous_response_id"] == "resp_1"
|
|
98
|
+
assert calls[1]["messages"] == []
|
|
89
99
|
tool_results = calls[1]["tool_results"]
|
|
90
100
|
assert len(tool_results) == 1
|
|
91
101
|
assert tool_results[0].name == "add"
|
|
92
102
|
assert tool_results[0].output == {"sum": 5}
|
|
93
103
|
|
|
94
104
|
|
|
105
|
+
def test_generate_replays_messages_when_tool_round_has_no_response_id(
|
|
106
|
+
monkeypatch: pytest.MonkeyPatch,
|
|
107
|
+
) -> None:
|
|
108
|
+
client = _client()
|
|
109
|
+
calls: list[dict[str, object]] = []
|
|
110
|
+
|
|
111
|
+
def fake_generate(**kwargs):
|
|
112
|
+
calls.append(kwargs)
|
|
113
|
+
if len(calls) == 1:
|
|
114
|
+
return GenerateResult(
|
|
115
|
+
text="",
|
|
116
|
+
tool_calls=[ToolCall(id="call_1", name="add", arguments_json='{"a":1,"b":2}')],
|
|
117
|
+
finish_reason="tool_calls",
|
|
118
|
+
response_id=None,
|
|
119
|
+
)
|
|
120
|
+
return GenerateResult(text="3", tool_calls=[], finish_reason="stop", response_id="resp_2")
|
|
121
|
+
|
|
122
|
+
monkeypatch.setattr(client._engine, "generate", fake_generate)
|
|
123
|
+
|
|
124
|
+
def add(a: int, b: int) -> dict[str, int]:
|
|
125
|
+
return {"sum": a + b}
|
|
126
|
+
|
|
127
|
+
out = client.generate("1+2", tools=[add])
|
|
128
|
+
|
|
129
|
+
assert out == "3"
|
|
130
|
+
assert calls[1]["previous_response_id"] is None
|
|
131
|
+
assert calls[1]["messages"] == calls[0]["messages"]
|
|
132
|
+
|
|
133
|
+
|
|
95
134
|
def test_generate_tool_failure_is_forwarded_to_model(monkeypatch: pytest.MonkeyPatch) -> None:
|
|
96
135
|
client = _client()
|
|
97
136
|
calls: list[dict[str, object]] = []
|
|
@@ -147,6 +186,75 @@ def test_generate_wraps_string_tool_output_as_dict(monkeypatch: pytest.MonkeyPat
|
|
|
147
186
|
assert tool_results[0].output == {"output": "hello"}
|
|
148
187
|
|
|
149
188
|
|
|
189
|
+
def test_generate_supports_single_pydantic_tool_input_with_flat_payload(
|
|
190
|
+
monkeypatch: pytest.MonkeyPatch,
|
|
191
|
+
) -> None:
|
|
192
|
+
client = _client()
|
|
193
|
+
calls: list[dict[str, object]] = []
|
|
194
|
+
|
|
195
|
+
def fake_generate(**kwargs):
|
|
196
|
+
calls.append(kwargs)
|
|
197
|
+
if len(calls) == 1:
|
|
198
|
+
return GenerateResult(
|
|
199
|
+
text="",
|
|
200
|
+
tool_calls=[ToolCall(id="call_1", name="tool", arguments_json='{"query":"hello"}')],
|
|
201
|
+
finish_reason="tool_calls",
|
|
202
|
+
response_id="resp_1",
|
|
203
|
+
)
|
|
204
|
+
return GenerateResult(text="done", tool_calls=[], finish_reason="stop", response_id="resp_2")
|
|
205
|
+
|
|
206
|
+
monkeypatch.setattr(client._engine, "generate", fake_generate)
|
|
207
|
+
|
|
208
|
+
def tool(input: ToolInputWithDescription) -> str:
|
|
209
|
+
return f"Tool received query: {input.query}"
|
|
210
|
+
|
|
211
|
+
out = client.generate("run", tools=[tool])
|
|
212
|
+
|
|
213
|
+
assert out == "done"
|
|
214
|
+
first_round_tools = calls[0]["tools"]
|
|
215
|
+
assert isinstance(first_round_tools, list)
|
|
216
|
+
assert first_round_tools[0]["parameters"]["type"] == "object"
|
|
217
|
+
assert "query" in first_round_tools[0]["parameters"]["properties"]
|
|
218
|
+
assert "input" not in first_round_tools[0]["parameters"]["properties"]
|
|
219
|
+
tool_results = calls[1]["tool_results"]
|
|
220
|
+
assert tool_results[0].output == {"output": "Tool received query: hello"}
|
|
221
|
+
|
|
222
|
+
|
|
223
|
+
def test_generate_supports_single_pydantic_tool_input_with_nested_payload(
|
|
224
|
+
monkeypatch: pytest.MonkeyPatch,
|
|
225
|
+
) -> None:
|
|
226
|
+
client = _client()
|
|
227
|
+
calls: list[dict[str, object]] = []
|
|
228
|
+
|
|
229
|
+
def fake_generate(**kwargs):
|
|
230
|
+
calls.append(kwargs)
|
|
231
|
+
if len(calls) == 1:
|
|
232
|
+
return GenerateResult(
|
|
233
|
+
text="",
|
|
234
|
+
tool_calls=[
|
|
235
|
+
ToolCall(
|
|
236
|
+
id="call_1",
|
|
237
|
+
name="tool",
|
|
238
|
+
arguments_json='{"input":{"query":"hello"}}',
|
|
239
|
+
)
|
|
240
|
+
],
|
|
241
|
+
finish_reason="tool_calls",
|
|
242
|
+
response_id="resp_1",
|
|
243
|
+
)
|
|
244
|
+
return GenerateResult(text="done", tool_calls=[], finish_reason="stop", response_id="resp_2")
|
|
245
|
+
|
|
246
|
+
monkeypatch.setattr(client._engine, "generate", fake_generate)
|
|
247
|
+
|
|
248
|
+
def tool(input: ToolInput) -> str:
|
|
249
|
+
return f"Tool received query: {input.query}"
|
|
250
|
+
|
|
251
|
+
out = client.generate("run", tools=[tool])
|
|
252
|
+
|
|
253
|
+
assert out == "done"
|
|
254
|
+
tool_results = calls[1]["tool_results"]
|
|
255
|
+
assert tool_results[0].output == {"output": "Tool received query: hello"}
|
|
256
|
+
|
|
257
|
+
|
|
150
258
|
def test_generate_raises_when_tool_round_limit_exceeded(monkeypatch: pytest.MonkeyPatch) -> None:
|
|
151
259
|
client = _client()
|
|
152
260
|
client.max_tool_rounds = 2
|
|
@@ -197,5 +305,6 @@ def test_stream_supports_tool_calls(monkeypatch: pytest.MonkeyPatch) -> None:
|
|
|
197
305
|
|
|
198
306
|
assert out == ["A", "B"]
|
|
199
307
|
assert calls[1]["previous_response_id"] == "resp_1"
|
|
308
|
+
assert calls[1]["messages"] == []
|
|
200
309
|
tool_results = calls[1]["tool_results"]
|
|
201
310
|
assert tool_results[0].output == {"sum": 3}
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{oauth_codex-2.0.2 → oauth_codex-2.1.0}/src/oauth_codex/resources/vector_stores/file_batches.py
RENAMED
|
File without changes
|
|
File without changes
|
{oauth_codex-2.0.2 → oauth_codex-2.1.0}/src/oauth_codex/resources/vector_stores/vector_stores.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{oauth_codex-2.0.2 → oauth_codex-2.1.0}/src/oauth_codex/types/responses/response_stream_event.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{oauth_codex-2.0.2 → oauth_codex-2.1.0}/src/oauth_codex/types/vector_stores/vector_store_deleted.py
RENAMED
|
File without changes
|
{oauth_codex-2.0.2 → oauth_codex-2.1.0}/src/oauth_codex/types/vector_stores/vector_store_file.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|