python-codex 0.1.1__py3-none-any.whl → 0.1.3__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- pycodex/__init__.py +5 -1
- pycodex/agent.py +39 -41
- pycodex/cli.py +51 -43
- pycodex/collaboration.py +6 -7
- pycodex/compat.py +99 -0
- pycodex/context.py +87 -87
- pycodex/doctor.py +40 -40
- pycodex/model.py +69 -69
- pycodex/portable.py +33 -33
- pycodex/portable_server.py +22 -21
- pycodex/protocol.py +84 -86
- pycodex/runtime.py +36 -35
- pycodex/runtime_services.py +72 -69
- pycodex/tools/agent_tool_schemas.py +0 -2
- pycodex/tools/apply_patch_tool.py +43 -44
- pycodex/tools/base_tool.py +35 -36
- pycodex/tools/close_agent_tool.py +2 -4
- pycodex/tools/code_mode_manager.py +61 -61
- pycodex/tools/exec_command_tool.py +5 -6
- pycodex/tools/exec_runtime.js +3 -3
- pycodex/tools/exec_tool.py +3 -5
- pycodex/tools/grep_files_tool.py +10 -11
- pycodex/tools/list_dir_tool.py +8 -9
- pycodex/tools/read_file_tool.py +13 -14
- pycodex/tools/request_permissions_tool.py +2 -4
- pycodex/tools/request_user_input_tool.py +13 -14
- pycodex/tools/resume_agent_tool.py +2 -4
- pycodex/tools/send_input_tool.py +8 -9
- pycodex/tools/shell_command_tool.py +5 -6
- pycodex/tools/shell_tool.py +5 -6
- pycodex/tools/spawn_agent_tool.py +4 -5
- pycodex/tools/unified_exec_manager.py +79 -61
- pycodex/tools/update_plan_tool.py +4 -5
- pycodex/tools/view_image_tool.py +4 -5
- pycodex/tools/wait_agent_tool.py +2 -4
- pycodex/tools/wait_tool.py +4 -5
- pycodex/tools/web_search_tool.py +1 -3
- pycodex/tools/write_stdin_tool.py +4 -5
- pycodex/utils/dotenv.py +6 -6
- pycodex/utils/get_env.py +57 -34
- pycodex/utils/random_ids.py +1 -2
- pycodex/utils/visualize.py +79 -79
- {python_codex-0.1.1.dist-info → python_codex-0.1.3.dist-info}/METADATA +15 -9
- python_codex-0.1.3.dist-info/RECORD +74 -0
- {python_codex-0.1.1.dist-info → python_codex-0.1.3.dist-info}/WHEEL +1 -1
- responses_server/__init__.py +17 -0
- responses_server/__main__.py +5 -0
- responses_server/app.py +227 -0
- responses_server/config.py +63 -0
- responses_server/payload_processors.py +86 -0
- responses_server/server.py +63 -0
- responses_server/session_store.py +37 -0
- responses_server/stream_router.py +784 -0
- responses_server/tools/__init__.py +4 -0
- responses_server/tools/custom_adapter.py +235 -0
- responses_server/tools/web_search.py +263 -0
- python_codex-0.1.1.dist-info/RECORD +0 -62
- {python_codex-0.1.1.dist-info → python_codex-0.1.3.dist-info}/entry_points.txt +0 -0
- {python_codex-0.1.1.dist-info → python_codex-0.1.3.dist-info}/licenses/LICENSE +0 -0
|
@@ -11,8 +11,6 @@ Expected behavior:
|
|
|
11
11
|
- Return summaries in the same textual shape Codex tools expect.
|
|
12
12
|
"""
|
|
13
13
|
|
|
14
|
-
from __future__ import annotations
|
|
15
|
-
|
|
16
14
|
import asyncio
|
|
17
15
|
import os
|
|
18
16
|
import shlex
|
|
@@ -22,6 +20,9 @@ from pathlib import Path
|
|
|
22
20
|
|
|
23
21
|
from loguru import logger
|
|
24
22
|
|
|
23
|
+
from ..compat import shlex_join, stream_writer_is_closing
|
|
24
|
+
import typing
|
|
25
|
+
|
|
25
26
|
DEFAULT_EXEC_YIELD_TIME_MS = 10_000
|
|
26
27
|
DEFAULT_WRITE_STDIN_YIELD_TIME_MS = 250
|
|
27
28
|
DEFAULT_MAX_OUTPUT_TOKENS = 10_000
|
|
@@ -63,33 +64,33 @@ UNIFIED_EXEC_OUTPUT_SCHEMA = {
|
|
|
63
64
|
}
|
|
64
65
|
|
|
65
66
|
|
|
66
|
-
def _approx_token_count(text: str) -> int:
|
|
67
|
+
def _approx_token_count(text: 'str') -> 'int':
|
|
67
68
|
if not text:
|
|
68
69
|
return 0
|
|
69
70
|
byte_length = len(text.encode("utf-8"))
|
|
70
71
|
return max(1, (byte_length + APPROX_BYTES_PER_TOKEN - 1) // APPROX_BYTES_PER_TOKEN)
|
|
71
72
|
|
|
72
73
|
|
|
73
|
-
def _approx_bytes_for_tokens(token_count: int) -> int:
|
|
74
|
+
def _approx_bytes_for_tokens(token_count: 'int') -> 'int':
|
|
74
75
|
return max(token_count, 0) * APPROX_BYTES_PER_TOKEN
|
|
75
76
|
|
|
76
77
|
|
|
77
|
-
def _approx_tokens_from_byte_count(byte_count: int) -> int:
|
|
78
|
+
def _approx_tokens_from_byte_count(byte_count: 'int') -> 'int':
|
|
78
79
|
if byte_count <= 0:
|
|
79
80
|
return 0
|
|
80
81
|
return (byte_count + APPROX_BYTES_PER_TOKEN - 1) // APPROX_BYTES_PER_TOKEN
|
|
81
82
|
|
|
82
83
|
|
|
83
|
-
def _split_budget(byte_budget: int) ->
|
|
84
|
+
def _split_budget(byte_budget: 'int') -> 'typing.Tuple[int, int]':
|
|
84
85
|
left_budget = byte_budget // 2
|
|
85
86
|
return left_budget, byte_budget - left_budget
|
|
86
87
|
|
|
87
88
|
|
|
88
89
|
def _split_string(
|
|
89
|
-
text: str,
|
|
90
|
-
beginning_bytes: int,
|
|
91
|
-
end_bytes: int,
|
|
92
|
-
) ->
|
|
90
|
+
text: 'str',
|
|
91
|
+
beginning_bytes: 'int',
|
|
92
|
+
end_bytes: 'int',
|
|
93
|
+
) -> 'typing.Tuple[str, str]':
|
|
93
94
|
if not text:
|
|
94
95
|
return "", ""
|
|
95
96
|
|
|
@@ -122,7 +123,7 @@ def _split_string(
|
|
|
122
123
|
return text[:prefix_end], text[suffix_start:]
|
|
123
124
|
|
|
124
125
|
|
|
125
|
-
def _truncate_text(text: str, max_tokens: int) -> str:
|
|
126
|
+
def _truncate_text(text: 'str', max_tokens: 'int') -> 'str':
|
|
126
127
|
if not text:
|
|
127
128
|
return ""
|
|
128
129
|
|
|
@@ -141,7 +142,7 @@ def _truncate_text(text: str, max_tokens: int) -> str:
|
|
|
141
142
|
return f"{prefix}{marker}{suffix}"
|
|
142
143
|
|
|
143
144
|
|
|
144
|
-
def _formatted_truncate_text(text: str, max_tokens: int) -> str:
|
|
145
|
+
def _formatted_truncate_text(text: 'str', max_tokens: 'int') -> 'str':
|
|
145
146
|
byte_budget = _approx_bytes_for_tokens(max_tokens)
|
|
146
147
|
if len(text.encode("utf-8")) <= byte_budget:
|
|
147
148
|
return text
|
|
@@ -150,13 +151,13 @@ def _formatted_truncate_text(text: str, max_tokens: int) -> str:
|
|
|
150
151
|
return f"Total output lines: {total_lines}\n\n{_truncate_text(text, max_tokens)}"
|
|
151
152
|
|
|
152
153
|
|
|
153
|
-
@dataclass
|
|
154
|
+
@dataclass
|
|
154
155
|
class _HeadTailBuffer:
|
|
155
|
-
max_bytes: int = UNIFIED_EXEC_OUTPUT_MAX_BYTES
|
|
156
|
-
head: bytearray = field(default_factory=bytearray)
|
|
157
|
-
tail: bytearray = field(default_factory=bytearray)
|
|
156
|
+
max_bytes: 'int' = UNIFIED_EXEC_OUTPUT_MAX_BYTES
|
|
157
|
+
head: 'bytearray' = field(default_factory=bytearray)
|
|
158
|
+
tail: 'bytearray' = field(default_factory=bytearray)
|
|
158
159
|
|
|
159
|
-
def push_chunk(self, chunk: bytes) -> None:
|
|
160
|
+
def push_chunk(self, chunk: 'bytes') -> 'None':
|
|
160
161
|
if not chunk or self.max_bytes <= 0:
|
|
161
162
|
return
|
|
162
163
|
|
|
@@ -178,41 +179,45 @@ class _HeadTailBuffer:
|
|
|
178
179
|
excess = len(self.tail) - tail_budget
|
|
179
180
|
del self.tail[:excess]
|
|
180
181
|
|
|
181
|
-
def drain_bytes(self) -> bytes:
|
|
182
|
+
def drain_bytes(self) -> 'bytes':
|
|
182
183
|
combined = bytes(self.head) + bytes(self.tail)
|
|
183
184
|
self.head.clear()
|
|
184
185
|
self.tail.clear()
|
|
185
186
|
return combined
|
|
186
187
|
|
|
188
|
+
def has_data(self) -> 'bool':
|
|
189
|
+
return bool(self.head or self.tail)
|
|
190
|
+
|
|
187
191
|
|
|
188
|
-
@dataclass
|
|
192
|
+
@dataclass
|
|
189
193
|
class UnifiedExecSession:
|
|
190
|
-
session_id: int
|
|
191
|
-
process: asyncio.subprocess.Process
|
|
192
|
-
start_time: float
|
|
193
|
-
command_display: str
|
|
194
|
-
tty: bool
|
|
195
|
-
unread_output: _HeadTailBuffer = field(default_factory=_HeadTailBuffer)
|
|
196
|
-
reader_task: asyncio.Task
|
|
194
|
+
session_id: 'int'
|
|
195
|
+
process: 'asyncio.subprocess.Process'
|
|
196
|
+
start_time: 'float'
|
|
197
|
+
command_display: 'str'
|
|
198
|
+
tty: 'bool'
|
|
199
|
+
unread_output: '_HeadTailBuffer' = field(default_factory=_HeadTailBuffer)
|
|
200
|
+
reader_task: 'typing.Union[asyncio.Task, None]' = None
|
|
201
|
+
output_event: 'asyncio.Event' = field(default_factory=asyncio.Event)
|
|
197
202
|
|
|
198
203
|
|
|
199
204
|
class UnifiedExecManager:
|
|
200
|
-
def __init__(self, cwd: str
|
|
205
|
+
def __init__(self, cwd: 'typing.Union[typing.Union[str, Path], None]' = None) -> 'None':
|
|
201
206
|
self._default_cwd = Path(cwd or Path.cwd()).resolve()
|
|
202
207
|
self._next_session_id = DEFAULT_SESSION_ID_START
|
|
203
|
-
self._sessions:
|
|
208
|
+
self._sessions: 'typing.Dict[int, UnifiedExecSession]' = {}
|
|
204
209
|
self._lock = asyncio.Lock()
|
|
205
210
|
|
|
206
211
|
async def exec_command(
|
|
207
212
|
self,
|
|
208
|
-
cmd: str,
|
|
209
|
-
workdir: str
|
|
210
|
-
shell: str
|
|
211
|
-
login: bool = DEFAULT_LOGIN,
|
|
212
|
-
tty: bool = DEFAULT_TTY,
|
|
213
|
-
yield_time_ms: int = DEFAULT_EXEC_YIELD_TIME_MS,
|
|
214
|
-
max_output_tokens: int
|
|
215
|
-
) -> str:
|
|
213
|
+
cmd: 'str',
|
|
214
|
+
workdir: 'typing.Union[str, None]' = None,
|
|
215
|
+
shell: 'typing.Union[str, None]' = None,
|
|
216
|
+
login: 'bool' = DEFAULT_LOGIN,
|
|
217
|
+
tty: 'bool' = DEFAULT_TTY,
|
|
218
|
+
yield_time_ms: 'int' = DEFAULT_EXEC_YIELD_TIME_MS,
|
|
219
|
+
max_output_tokens: 'typing.Union[int, None]' = None,
|
|
220
|
+
) -> 'str':
|
|
216
221
|
session_id = await self._allocate_session_id()
|
|
217
222
|
command = self._build_shell_command(cmd, shell, login)
|
|
218
223
|
cwd = self._resolve_workdir(workdir)
|
|
@@ -234,7 +239,7 @@ class UnifiedExecManager:
|
|
|
234
239
|
session_id=session_id,
|
|
235
240
|
process=process,
|
|
236
241
|
start_time=asyncio.get_running_loop().time(),
|
|
237
|
-
command_display=
|
|
242
|
+
command_display=shlex_join(command),
|
|
238
243
|
tty=tty,
|
|
239
244
|
)
|
|
240
245
|
session.reader_task = asyncio.create_task(self._pump_output(session))
|
|
@@ -250,11 +255,11 @@ class UnifiedExecManager:
|
|
|
250
255
|
|
|
251
256
|
async def write_stdin(
|
|
252
257
|
self,
|
|
253
|
-
session_id: int,
|
|
254
|
-
chars: str = "",
|
|
255
|
-
yield_time_ms: int = DEFAULT_WRITE_STDIN_YIELD_TIME_MS,
|
|
256
|
-
max_output_tokens: int
|
|
257
|
-
) -> str:
|
|
258
|
+
session_id: 'int',
|
|
259
|
+
chars: 'str' = "",
|
|
260
|
+
yield_time_ms: 'int' = DEFAULT_WRITE_STDIN_YIELD_TIME_MS,
|
|
261
|
+
max_output_tokens: 'typing.Union[int, None]' = None,
|
|
262
|
+
) -> 'str':
|
|
258
263
|
session = await self._get_session(session_id)
|
|
259
264
|
if session is None:
|
|
260
265
|
return f"Error: session_id {session_id} is not running."
|
|
@@ -274,31 +279,42 @@ class UnifiedExecManager:
|
|
|
274
279
|
max_output_tokens,
|
|
275
280
|
)
|
|
276
281
|
|
|
277
|
-
async def _allocate_session_id(self) -> int:
|
|
282
|
+
async def _allocate_session_id(self) -> 'int':
|
|
278
283
|
async with self._lock:
|
|
279
284
|
session_id = self._next_session_id
|
|
280
285
|
self._next_session_id += 1
|
|
281
286
|
return session_id
|
|
282
287
|
|
|
283
|
-
async def _get_session(self, session_id: int) -> UnifiedExecSession
|
|
288
|
+
async def _get_session(self, session_id: 'int') -> 'typing.Union[UnifiedExecSession, None]':
|
|
284
289
|
async with self._lock:
|
|
285
290
|
return self._sessions.get(session_id)
|
|
286
291
|
|
|
287
292
|
async def _wait_and_snapshot(
|
|
288
293
|
self,
|
|
289
|
-
session_id: int,
|
|
290
|
-
yield_time_ms: int,
|
|
291
|
-
max_output_tokens: int
|
|
292
|
-
) -> str:
|
|
294
|
+
session_id: 'int',
|
|
295
|
+
yield_time_ms: 'int',
|
|
296
|
+
max_output_tokens: 'typing.Union[int, None]',
|
|
297
|
+
) -> 'str':
|
|
293
298
|
session = await self._get_session(session_id)
|
|
294
299
|
if session is None:
|
|
295
300
|
return f"Error: session_id {session_id} is not running."
|
|
296
301
|
|
|
297
|
-
|
|
302
|
+
loop = asyncio.get_running_loop()
|
|
303
|
+
start_wait = loop.time()
|
|
298
304
|
try:
|
|
299
305
|
await asyncio.wait_for(session.process.wait(), timeout=yield_time_ms / 1000.0)
|
|
300
306
|
except asyncio.TimeoutError:
|
|
301
|
-
|
|
307
|
+
remaining_seconds = (yield_time_ms / 1000.0) - (loop.time() - start_wait)
|
|
308
|
+
if (
|
|
309
|
+
session.process.returncode is None
|
|
310
|
+
and not session.unread_output.has_data()
|
|
311
|
+
and remaining_seconds > 0
|
|
312
|
+
):
|
|
313
|
+
session.output_event.clear()
|
|
314
|
+
try:
|
|
315
|
+
await asyncio.wait_for(session.output_event.wait(), timeout=remaining_seconds)
|
|
316
|
+
except asyncio.TimeoutError:
|
|
317
|
+
pass
|
|
302
318
|
|
|
303
319
|
if session.reader_task is not None and session.process.returncode is not None:
|
|
304
320
|
await session.reader_task
|
|
@@ -328,15 +344,15 @@ class UnifiedExecManager:
|
|
|
328
344
|
|
|
329
345
|
return "\n".join(lines)
|
|
330
346
|
|
|
331
|
-
async def _close_session(self, session_id: int) -> None:
|
|
347
|
+
async def _close_session(self, session_id: 'int') -> 'None':
|
|
332
348
|
async with self._lock:
|
|
333
349
|
session = self._sessions.pop(session_id, None)
|
|
334
350
|
if session is None:
|
|
335
351
|
return
|
|
336
|
-
if session.process.stdin is not None and not session.process.stdin
|
|
352
|
+
if session.process.stdin is not None and not stream_writer_is_closing(session.process.stdin):
|
|
337
353
|
session.process.stdin.close()
|
|
338
354
|
|
|
339
|
-
async def _pump_output(self, session: UnifiedExecSession) -> None:
|
|
355
|
+
async def _pump_output(self, session: 'UnifiedExecSession') -> 'None':
|
|
340
356
|
stream = session.process.stdout
|
|
341
357
|
if stream is None:
|
|
342
358
|
return
|
|
@@ -345,8 +361,10 @@ class UnifiedExecManager:
|
|
|
345
361
|
if not chunk:
|
|
346
362
|
break
|
|
347
363
|
session.unread_output.push_chunk(chunk)
|
|
364
|
+
session.output_event.set()
|
|
365
|
+
session.output_event.set()
|
|
348
366
|
|
|
349
|
-
def _resolve_workdir(self, workdir: str
|
|
367
|
+
def _resolve_workdir(self, workdir: 'typing.Union[str, None]') -> 'Path':
|
|
350
368
|
if not workdir:
|
|
351
369
|
return self._default_cwd
|
|
352
370
|
path = Path(workdir)
|
|
@@ -356,10 +374,10 @@ class UnifiedExecManager:
|
|
|
356
374
|
|
|
357
375
|
def _build_shell_command(
|
|
358
376
|
self,
|
|
359
|
-
cmd: str,
|
|
360
|
-
shell: str
|
|
361
|
-
login: bool,
|
|
362
|
-
) ->
|
|
377
|
+
cmd: 'str',
|
|
378
|
+
shell: 'typing.Union[str, None]',
|
|
379
|
+
login: 'bool',
|
|
380
|
+
) -> 'typing.List[str]':
|
|
363
381
|
shell_path = shell or os.environ.get("SHELL") or "/bin/bash"
|
|
364
382
|
shell_name = Path(shell_path).name.lower()
|
|
365
383
|
if shell_name in {"cmd", "cmd.exe"}:
|
|
@@ -368,13 +386,13 @@ class UnifiedExecManager:
|
|
|
368
386
|
return [shell_path, "-NoProfile", "-Command", cmd]
|
|
369
387
|
return [shell_path, "-lc" if login else "-c", cmd]
|
|
370
388
|
|
|
371
|
-
def _estimate_token_count(self, output: str) -> int
|
|
389
|
+
def _estimate_token_count(self, output: 'str') -> 'typing.Union[int, None]':
|
|
372
390
|
return _approx_token_count(output)
|
|
373
391
|
|
|
374
|
-
def _truncate_output(self, output: str, max_output_tokens: int
|
|
392
|
+
def _truncate_output(self, output: 'str', max_output_tokens: 'typing.Union[int, None]') -> 'str':
|
|
375
393
|
token_budget = DEFAULT_MAX_OUTPUT_TOKENS if max_output_tokens is None else max_output_tokens
|
|
376
394
|
return _formatted_truncate_text(output, max(token_budget, 0))
|
|
377
395
|
|
|
378
|
-
def _tty_echo(self, chars: str) -> bytes:
|
|
396
|
+
def _tty_echo(self, chars: 'str') -> 'bytes':
|
|
379
397
|
normalized = chars.replace("\n", "\r\n")
|
|
380
398
|
return normalized.encode("utf-8")
|
|
@@ -10,11 +10,10 @@ Expected behavior:
|
|
|
10
10
|
confirmation text Codex uses.
|
|
11
11
|
"""
|
|
12
12
|
|
|
13
|
-
from __future__ import annotations
|
|
14
|
-
|
|
15
13
|
from ..protocol import JSONDict, JSONValue
|
|
16
14
|
from ..runtime_services import PlanItem, PlanStore
|
|
17
15
|
from .base_tool import BaseTool, ToolContext
|
|
16
|
+
import typing
|
|
18
17
|
|
|
19
18
|
VALID_PLAN_STATUSES = {"pending", "in_progress", "completed"}
|
|
20
19
|
|
|
@@ -52,16 +51,16 @@ class UpdatePlanTool(BaseTool):
|
|
|
52
51
|
}
|
|
53
52
|
supports_parallel = False
|
|
54
53
|
|
|
55
|
-
def __init__(self, plan_store: PlanStore) -> None:
|
|
54
|
+
def __init__(self, plan_store: 'PlanStore') -> 'None':
|
|
56
55
|
self._plan_store = plan_store
|
|
57
56
|
|
|
58
|
-
async def run(self, context: ToolContext, args: JSONDict) -> JSONValue:
|
|
57
|
+
async def run(self, context: 'ToolContext', args: 'JSONDict') -> 'JSONValue':
|
|
59
58
|
del context
|
|
60
59
|
raw_plan = args.get("plan")
|
|
61
60
|
if not isinstance(raw_plan, list):
|
|
62
61
|
return "Error: `plan` must be a list."
|
|
63
62
|
|
|
64
|
-
plan_items:
|
|
63
|
+
plan_items: 'typing.List[PlanItem]' = []
|
|
65
64
|
for item in raw_plan:
|
|
66
65
|
if not isinstance(item, dict):
|
|
67
66
|
return "Error: each `plan` item must be an object."
|
pycodex/tools/view_image_tool.py
CHANGED
|
@@ -12,14 +12,13 @@ Expected behavior:
|
|
|
12
12
|
item that Codex uses when feeding image tool output back to the model.
|
|
13
13
|
"""
|
|
14
14
|
|
|
15
|
-
from __future__ import annotations
|
|
16
|
-
|
|
17
15
|
import base64
|
|
18
16
|
import mimetypes
|
|
19
17
|
from pathlib import Path
|
|
20
18
|
|
|
21
19
|
from ..protocol import JSONDict, JSONValue
|
|
22
20
|
from .base_tool import BaseTool, StructuredToolOutput, ToolContext
|
|
21
|
+
import typing
|
|
23
22
|
|
|
24
23
|
VIEW_IMAGE_OUTPUT_SCHEMA = {
|
|
25
24
|
"type": "object",
|
|
@@ -62,10 +61,10 @@ class ViewImageTool(BaseTool):
|
|
|
62
61
|
}
|
|
63
62
|
output_schema = VIEW_IMAGE_OUTPUT_SCHEMA
|
|
64
63
|
|
|
65
|
-
def __init__(self, cwd: str
|
|
64
|
+
def __init__(self, cwd: 'typing.Union[typing.Union[str, Path], None]' = None) -> 'None':
|
|
66
65
|
self._workspace_root = Path(cwd or Path.cwd()).resolve()
|
|
67
66
|
|
|
68
|
-
async def run(self, context: ToolContext, args: JSONDict) -> JSONValue:
|
|
67
|
+
async def run(self, context: 'ToolContext', args: 'JSONDict') -> 'JSONValue':
|
|
69
68
|
del context
|
|
70
69
|
path_value = str(args.get("path", "")).strip()
|
|
71
70
|
if not path_value:
|
|
@@ -102,7 +101,7 @@ class ViewImageTool(BaseTool):
|
|
|
102
101
|
"image_url": image_url,
|
|
103
102
|
"detail": detail,
|
|
104
103
|
}
|
|
105
|
-
image_item: JSONDict = {
|
|
104
|
+
image_item: 'JSONDict' = {
|
|
106
105
|
"type": "input_image",
|
|
107
106
|
"image_url": image_url,
|
|
108
107
|
}
|
pycodex/tools/wait_agent_tool.py
CHANGED
|
@@ -9,8 +9,6 @@ Expected behavior:
|
|
|
9
9
|
wait times out.
|
|
10
10
|
"""
|
|
11
11
|
|
|
12
|
-
from __future__ import annotations
|
|
13
|
-
|
|
14
12
|
from ..protocol import JSONDict, JSONValue
|
|
15
13
|
from ..runtime_services import SubAgentManager
|
|
16
14
|
from .agent_tool_schemas import AGENT_STATUS_SCHEMA
|
|
@@ -60,10 +58,10 @@ class WaitAgentTool(BaseTool):
|
|
|
60
58
|
output_schema = WAIT_AGENT_OUTPUT_SCHEMA
|
|
61
59
|
supports_parallel = False
|
|
62
60
|
|
|
63
|
-
def __init__(self, subagent_manager: SubAgentManager) -> None:
|
|
61
|
+
def __init__(self, subagent_manager: 'SubAgentManager') -> 'None':
|
|
64
62
|
self._subagent_manager = subagent_manager
|
|
65
63
|
|
|
66
|
-
async def run(self, context: ToolContext, args: JSONDict) -> JSONValue:
|
|
64
|
+
async def run(self, context: 'ToolContext', args: 'JSONDict') -> 'JSONValue':
|
|
67
65
|
del context
|
|
68
66
|
ids = args.get("ids")
|
|
69
67
|
if not isinstance(ids, list) or not ids:
|
pycodex/tools/wait_tool.py
CHANGED
|
@@ -9,11 +9,10 @@ Expected behavior:
|
|
|
9
9
|
- Return only the new output since the previous `exec` / `wait` snapshot.
|
|
10
10
|
"""
|
|
11
11
|
|
|
12
|
-
from __future__ import annotations
|
|
13
|
-
|
|
14
12
|
from ..protocol import JSONDict, JSONValue
|
|
15
13
|
from .base_tool import BaseTool, ToolContext
|
|
16
14
|
from .code_mode_manager import DEFAULT_WAIT_YIELD_TIME_MS, CodeModeManager
|
|
15
|
+
import typing
|
|
17
16
|
|
|
18
17
|
|
|
19
18
|
class WaitTool(BaseTool):
|
|
@@ -46,10 +45,10 @@ class WaitTool(BaseTool):
|
|
|
46
45
|
}
|
|
47
46
|
supports_parallel = False
|
|
48
47
|
|
|
49
|
-
def __init__(self, manager: CodeModeManager) -> None:
|
|
48
|
+
def __init__(self, manager: 'CodeModeManager') -> 'None':
|
|
50
49
|
self._manager = manager
|
|
51
50
|
|
|
52
|
-
async def run(self, context: ToolContext, args: JSONDict) -> JSONValue:
|
|
51
|
+
async def run(self, context: 'ToolContext', args: 'JSONDict') -> 'JSONValue':
|
|
53
52
|
del context
|
|
54
53
|
cell_id = str(args.get("cell_id", "")).strip()
|
|
55
54
|
if not cell_id:
|
|
@@ -61,7 +60,7 @@ class WaitTool(BaseTool):
|
|
|
61
60
|
terminate=bool(args.get("terminate", False)),
|
|
62
61
|
)
|
|
63
62
|
|
|
64
|
-
def _optional_int(self, args: JSONDict, key: str) -> int
|
|
63
|
+
def _optional_int(self, args: 'JSONDict', key: 'str') -> 'typing.Union[int, None]':
|
|
65
64
|
value = args.get(key)
|
|
66
65
|
if value in (None, ""):
|
|
67
66
|
return None
|
pycodex/tools/web_search_tool.py
CHANGED
|
@@ -10,8 +10,6 @@ Expected behavior:
|
|
|
10
10
|
- Never expect a local tool-call round-trip result from the model.
|
|
11
11
|
"""
|
|
12
12
|
|
|
13
|
-
from __future__ import annotations
|
|
14
|
-
|
|
15
13
|
from ..protocol import JSONDict, JSONValue
|
|
16
14
|
from .base_tool import BaseTool, ToolContext
|
|
17
15
|
|
|
@@ -25,6 +23,6 @@ class WebSearchTool(BaseTool):
|
|
|
25
23
|
}
|
|
26
24
|
supports_parallel = False
|
|
27
25
|
|
|
28
|
-
async def run(self, context: ToolContext, args: JSONValue) -> JSONValue:
|
|
26
|
+
async def run(self, context: 'ToolContext', args: 'JSONValue') -> 'JSONValue':
|
|
29
27
|
del context, args
|
|
30
28
|
return "Error: web_search is provider-native and should not be executed locally."
|
|
@@ -10,8 +10,6 @@ Expected behavior:
|
|
|
10
10
|
- Reuse the same `session_id` until the process exits.
|
|
11
11
|
"""
|
|
12
12
|
|
|
13
|
-
from __future__ import annotations
|
|
14
|
-
|
|
15
13
|
from ..protocol import JSONDict, JSONValue
|
|
16
14
|
from .base_tool import BaseTool, ToolContext
|
|
17
15
|
from .unified_exec_manager import (
|
|
@@ -19,6 +17,7 @@ from .unified_exec_manager import (
|
|
|
19
17
|
UNIFIED_EXEC_OUTPUT_SCHEMA,
|
|
20
18
|
UnifiedExecManager,
|
|
21
19
|
)
|
|
20
|
+
import typing
|
|
22
21
|
|
|
23
22
|
|
|
24
23
|
class WriteStdinTool(BaseTool):
|
|
@@ -50,10 +49,10 @@ class WriteStdinTool(BaseTool):
|
|
|
50
49
|
output_schema = UNIFIED_EXEC_OUTPUT_SCHEMA
|
|
51
50
|
supports_parallel = False
|
|
52
51
|
|
|
53
|
-
def __init__(self, manager: UnifiedExecManager) -> None:
|
|
52
|
+
def __init__(self, manager: 'UnifiedExecManager') -> 'None':
|
|
54
53
|
self._manager = manager
|
|
55
54
|
|
|
56
|
-
async def run(self, context: ToolContext, args: JSONDict) -> JSONValue:
|
|
55
|
+
async def run(self, context: 'ToolContext', args: 'JSONDict') -> 'JSONValue':
|
|
57
56
|
del context
|
|
58
57
|
session_id = args.get("session_id")
|
|
59
58
|
if session_id is None:
|
|
@@ -68,7 +67,7 @@ class WriteStdinTool(BaseTool):
|
|
|
68
67
|
max_output_tokens=self._optional_int(args, "max_output_tokens"),
|
|
69
68
|
)
|
|
70
69
|
|
|
71
|
-
def _optional_int(self, args: JSONDict, key: str) -> int
|
|
70
|
+
def _optional_int(self, args: 'JSONDict', key: 'str') -> 'typing.Union[int, None]':
|
|
72
71
|
value = args.get(key)
|
|
73
72
|
if value in (None, ""):
|
|
74
73
|
return None
|
pycodex/utils/dotenv.py
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
|
-
from __future__ import annotations
|
|
2
1
|
|
|
3
2
|
import os
|
|
4
3
|
from pathlib import Path
|
|
4
|
+
import typing
|
|
5
5
|
|
|
6
6
|
ILLEGAL_ENV_VAR_PREFIX = "CODEX_"
|
|
7
7
|
DOTENV_FILENAME = ".env"
|
|
8
|
-
_LOADED_CODEX_DOTENV_HOMES:
|
|
8
|
+
_LOADED_CODEX_DOTENV_HOMES: 'typing.Set[str]' = set()
|
|
9
9
|
|
|
10
10
|
|
|
11
|
-
def load_codex_dotenv(config_path: str
|
|
11
|
+
def load_codex_dotenv(config_path: 'typing.Union[str, Path]') -> 'None':
|
|
12
12
|
codex_home = str(Path(config_path).resolve().parent)
|
|
13
13
|
if codex_home in _LOADED_CODEX_DOTENV_HOMES:
|
|
14
14
|
return
|
|
@@ -26,8 +26,8 @@ def load_codex_dotenv(config_path: str | Path) -> None:
|
|
|
26
26
|
_LOADED_CODEX_DOTENV_HOMES.add(codex_home)
|
|
27
27
|
|
|
28
28
|
|
|
29
|
-
def parse_dotenv(text: str) ->
|
|
30
|
-
values:
|
|
29
|
+
def parse_dotenv(text: 'str') -> 'typing.Dict[str, str]':
|
|
30
|
+
values: 'typing.Dict[str, str]' = {}
|
|
31
31
|
for raw_line in text.splitlines():
|
|
32
32
|
line = raw_line.strip()
|
|
33
33
|
if not line or line.startswith("#"):
|
|
@@ -45,7 +45,7 @@ def parse_dotenv(text: str) -> dict[str, str]:
|
|
|
45
45
|
return values
|
|
46
46
|
|
|
47
47
|
|
|
48
|
-
def parse_dotenv_value(raw_value: str) -> str:
|
|
48
|
+
def parse_dotenv_value(raw_value: 'str') -> 'str':
|
|
49
49
|
if not raw_value:
|
|
50
50
|
return ""
|
|
51
51
|
|