python-codex 0.2.6__py3-none-any.whl → 0.3.0__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 +18 -14
- pycodex/agent.py +468 -462
- pycodex/bootstrap.py +417 -0
- pycodex/cli.py +236 -436
- pycodex/compat.py +19 -5
- pycodex/context.py +222 -212
- pycodex/doctor.py +52 -48
- pycodex/events.py +857 -0
- pycodex/feishu_card.py +217 -163
- pycodex/feishu_link.py +43 -83
- pycodex/model.py +329 -252
- pycodex/model_metadata.py +19 -7
- pycodex/portable.py +90 -52
- pycodex/portable_server.py +32 -24
- pycodex/prompts/models.json +235 -803
- pycodex/protocol.py +177 -137
- pycodex/runtime.py +579 -174
- pycodex/runtime_services.py +204 -157
- pycodex/tools/__init__.py +4 -1
- pycodex/tools/apply_patch_tool.py +69 -48
- pycodex/tools/base_tool.py +89 -42
- pycodex/tools/clock_tool.py +201 -0
- pycodex/tools/close_agent_tool.py +2 -2
- pycodex/tools/code_mode_manager.py +77 -64
- pycodex/tools/exec_command_tool.py +26 -11
- pycodex/tools/exec_tool.py +4 -4
- pycodex/tools/grep_files_tool.py +12 -10
- pycodex/tools/ipython_tool.py +10 -13
- pycodex/tools/list_dir_tool.py +13 -9
- pycodex/tools/read_file_tool.py +29 -17
- pycodex/tools/request_permissions_tool.py +15 -5
- pycodex/tools/request_user_input_tool.py +13 -104
- pycodex/tools/resume_agent_tool.py +2 -2
- pycodex/tools/send_input_tool.py +11 -8
- pycodex/tools/shell_command_tool.py +7 -5
- pycodex/tools/shell_tool.py +7 -5
- pycodex/tools/spawn_agent_tool.py +7 -4
- pycodex/tools/unified_exec_manager.py +102 -69
- pycodex/tools/update_plan_tool.py +8 -5
- pycodex/tools/view_image_tool.py +13 -13
- pycodex/tools/wait_agent_tool.py +27 -4
- pycodex/tools/wait_tool.py +5 -4
- pycodex/tools/web_search_tool.py +4 -2
- pycodex/tools/write_stdin_tool.py +12 -11
- pycodex/utils/__init__.py +2 -17
- pycodex/utils/compactor.py +50 -66
- pycodex/utils/debug.py +2 -2
- pycodex/utils/dotenv.py +6 -7
- pycodex/utils/event_helpers.py +190 -0
- pycodex/utils/get_env.py +27 -70
- pycodex/utils/image_utils.py +76 -0
- pycodex/utils/random_ids.py +1 -2
- pycodex/utils/session_persist.py +263 -161
- pycodex/utils/truncation.py +21 -45
- python_codex-0.3.0.dist-info/METADATA +704 -0
- python_codex-0.3.0.dist-info/RECORD +90 -0
- responses_server/__init__.py +1 -5
- responses_server/__main__.py +0 -1
- responses_server/app.py +36 -31
- responses_server/config.py +25 -22
- responses_server/messages_api.py +96 -49
- responses_server/payload_processors.py +25 -19
- responses_server/server.py +11 -11
- responses_server/session_store.py +14 -11
- responses_server/stream_router.py +196 -107
- responses_server/tools/custom_adapter.py +17 -16
- responses_server/tools/web_search.py +39 -36
- responses_server/trajectory_dump.py +51 -13
- workspace_server/__main__.py +0 -1
- workspace_server/app.py +470 -384
- workspace_server/workspace.html +859 -232
- workspace_server/workspaces.html +94 -95
- workspace_server/workspaces.py +168 -100
- pycodex/collaboration.py +0 -20
- pycodex/interactive_session.py +0 -415
- pycodex/prompts/collaboration_default.md +0 -11
- pycodex/prompts/collaboration_plan.md +0 -128
- pycodex/utils/toolcall_visualize.py +0 -713
- pycodex/utils/visualize.py +0 -553
- python_codex-0.2.6.dist-info/METADATA +0 -441
- python_codex-0.2.6.dist-info/RECORD +0 -91
- {python_codex-0.2.6.dist-info → python_codex-0.3.0.dist-info}/WHEEL +0 -0
- {python_codex-0.2.6.dist-info → python_codex-0.3.0.dist-info}/entry_points.txt +0 -0
- {python_codex-0.2.6.dist-info → python_codex-0.3.0.dist-info}/licenses/LICENSE +0 -0
pycodex/doctor.py
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
|
|
2
1
|
import asyncio
|
|
3
2
|
import json
|
|
4
3
|
import socket
|
|
5
4
|
import ssl
|
|
6
5
|
import time
|
|
6
|
+
import typing
|
|
7
7
|
import urllib.parse
|
|
8
8
|
from dataclasses import asdict, dataclass, field
|
|
9
9
|
from pathlib import Path
|
|
@@ -13,32 +13,31 @@ import requests
|
|
|
13
13
|
from .model import ResponsesModelClient, ResponsesProviderConfig
|
|
14
14
|
from .protocol import AssistantMessage, Prompt, UserMessage
|
|
15
15
|
from .utils.dotenv import DOTENV_FILENAME, load_codex_dotenv
|
|
16
|
-
import typing
|
|
17
16
|
|
|
18
17
|
|
|
19
18
|
@dataclass
|
|
20
19
|
class DoctorCheck:
|
|
21
|
-
name:
|
|
22
|
-
ok:
|
|
23
|
-
detail:
|
|
20
|
+
name: "str"
|
|
21
|
+
ok: "bool"
|
|
22
|
+
detail: "str"
|
|
24
23
|
|
|
25
24
|
|
|
26
25
|
@dataclass
|
|
27
26
|
class DoctorReport:
|
|
28
|
-
ok:
|
|
29
|
-
config_path:
|
|
30
|
-
dotenv_path:
|
|
31
|
-
profile:
|
|
32
|
-
provider_name:
|
|
33
|
-
model:
|
|
34
|
-
base_url:
|
|
35
|
-
responses_url:
|
|
36
|
-
api_key_env:
|
|
37
|
-
api_key_loaded:
|
|
38
|
-
checks:
|
|
39
|
-
live_output_text:
|
|
40
|
-
|
|
41
|
-
def to_dict(self) ->
|
|
27
|
+
ok: "bool"
|
|
28
|
+
config_path: "str"
|
|
29
|
+
dotenv_path: "str"
|
|
30
|
+
profile: "typing.Union[str, None]"
|
|
31
|
+
provider_name: "typing.Union[str, None]" = None
|
|
32
|
+
model: "typing.Union[str, None]" = None
|
|
33
|
+
base_url: "typing.Union[str, None]" = None
|
|
34
|
+
responses_url: "typing.Union[str, None]" = None
|
|
35
|
+
api_key_env: "typing.Union[str, None]" = None
|
|
36
|
+
api_key_loaded: "bool" = False
|
|
37
|
+
checks: "typing.List[DoctorCheck]" = field(default_factory=list)
|
|
38
|
+
live_output_text: "typing.Union[str, None]" = None
|
|
39
|
+
|
|
40
|
+
def to_dict(self) -> "typing.Dict[str, object]":
|
|
42
41
|
return asdict(self)
|
|
43
42
|
|
|
44
43
|
|
|
@@ -79,11 +78,11 @@ def build_doctor_parser():
|
|
|
79
78
|
|
|
80
79
|
|
|
81
80
|
async def collect_doctor_report(
|
|
82
|
-
config_path:
|
|
83
|
-
profile:
|
|
84
|
-
timeout_seconds:
|
|
85
|
-
skip_live:
|
|
86
|
-
) ->
|
|
81
|
+
config_path: "typing.Union[str, Path]",
|
|
82
|
+
profile: "typing.Union[str, None]" = None,
|
|
83
|
+
timeout_seconds: "float" = 120.0,
|
|
84
|
+
skip_live: "bool" = False,
|
|
85
|
+
) -> "DoctorReport":
|
|
87
86
|
config_file = Path(config_path).expanduser().resolve()
|
|
88
87
|
dotenv_file = config_file.parent / DOTENV_FILENAME
|
|
89
88
|
report = DoctorReport(
|
|
@@ -118,9 +117,13 @@ async def collect_doctor_report(
|
|
|
118
117
|
load_codex_dotenv(config_file)
|
|
119
118
|
|
|
120
119
|
try:
|
|
121
|
-
provider_config = ResponsesProviderConfig.from_codex_config(
|
|
120
|
+
provider_config = ResponsesProviderConfig.from_codex_config(
|
|
121
|
+
config_file, profile
|
|
122
|
+
)
|
|
122
123
|
except Exception as exc:
|
|
123
|
-
checks.append(
|
|
124
|
+
checks.append(
|
|
125
|
+
DoctorCheck("config_parse", False, f"{type(exc).__name__}: {exc}")
|
|
126
|
+
)
|
|
124
127
|
return report
|
|
125
128
|
|
|
126
129
|
report.provider_name = provider_config.provider_name
|
|
@@ -129,7 +132,9 @@ async def collect_doctor_report(
|
|
|
129
132
|
client = ResponsesModelClient(provider_config)
|
|
130
133
|
report.responses_url = client.responses_url()
|
|
131
134
|
report.api_key_env = provider_config.api_key_env
|
|
132
|
-
report.api_key_loaded = bool(
|
|
135
|
+
report.api_key_loaded = bool(
|
|
136
|
+
provider_config.api_key_env and _loaded_api_key(provider_config)
|
|
137
|
+
)
|
|
133
138
|
|
|
134
139
|
checks.append(
|
|
135
140
|
DoctorCheck(
|
|
@@ -227,7 +232,7 @@ async def collect_doctor_report(
|
|
|
227
232
|
return _finalize_report(report)
|
|
228
233
|
|
|
229
234
|
|
|
230
|
-
async def run_doctor_cli(args) ->
|
|
235
|
+
async def run_doctor_cli(args) -> "int":
|
|
231
236
|
report = await collect_doctor_report(
|
|
232
237
|
args.config,
|
|
233
238
|
args.profile,
|
|
@@ -243,7 +248,7 @@ async def run_doctor_cli(args) -> 'int':
|
|
|
243
248
|
return 0 if report.ok else 1
|
|
244
249
|
|
|
245
250
|
|
|
246
|
-
def format_doctor_report(report:
|
|
251
|
+
def format_doctor_report(report: "DoctorReport") -> "str":
|
|
247
252
|
lines = [
|
|
248
253
|
f"config: {report.config_path}",
|
|
249
254
|
f"dotenv: {report.dotenv_path}",
|
|
@@ -267,7 +272,7 @@ def format_doctor_report(report: 'DoctorReport') -> 'str':
|
|
|
267
272
|
return "\n".join(lines)
|
|
268
273
|
|
|
269
274
|
|
|
270
|
-
def _loaded_api_key(provider_config:
|
|
275
|
+
def _loaded_api_key(provider_config: "ResponsesProviderConfig") -> "str":
|
|
271
276
|
try:
|
|
272
277
|
return provider_config.api_key()
|
|
273
278
|
except Exception:
|
|
@@ -275,11 +280,11 @@ def _loaded_api_key(provider_config: 'ResponsesProviderConfig') -> 'str':
|
|
|
275
280
|
|
|
276
281
|
|
|
277
282
|
def _probe_transport(
|
|
278
|
-
scheme:
|
|
279
|
-
host:
|
|
280
|
-
port:
|
|
281
|
-
timeout_seconds:
|
|
282
|
-
) ->
|
|
283
|
+
scheme: "str",
|
|
284
|
+
host: "str",
|
|
285
|
+
port: "int",
|
|
286
|
+
timeout_seconds: "float",
|
|
287
|
+
) -> "typing.Tuple[bool, str]":
|
|
283
288
|
started = time.perf_counter()
|
|
284
289
|
try:
|
|
285
290
|
with socket.create_connection((host, port), timeout=timeout_seconds) as sock:
|
|
@@ -291,14 +296,17 @@ def _probe_transport(
|
|
|
291
296
|
pass
|
|
292
297
|
except OSError as exc:
|
|
293
298
|
elapsed = time.perf_counter() - started
|
|
294
|
-
return
|
|
299
|
+
return (
|
|
300
|
+
False,
|
|
301
|
+
f"{scheme.upper()} {host}:{port} failed after {elapsed:.2f}s: {exc}",
|
|
302
|
+
)
|
|
295
303
|
|
|
296
304
|
elapsed = time.perf_counter() - started
|
|
297
305
|
label = "tls" if scheme == "https" else "tcp"
|
|
298
306
|
return True, f"{label} {host}:{port} connected in {elapsed:.2f}s"
|
|
299
307
|
|
|
300
308
|
|
|
301
|
-
def _proxy_detail(proxies:
|
|
309
|
+
def _proxy_detail(proxies: "typing.Dict[str, str]") -> "str":
|
|
302
310
|
if not proxies:
|
|
303
311
|
return "not configured"
|
|
304
312
|
return ", ".join(
|
|
@@ -306,7 +314,7 @@ def _proxy_detail(proxies: 'typing.Dict[str, str]') -> 'str':
|
|
|
306
314
|
)
|
|
307
315
|
|
|
308
316
|
|
|
309
|
-
def _redact_proxy_url(value:
|
|
317
|
+
def _redact_proxy_url(value: "str") -> "str":
|
|
310
318
|
parsed = urllib.parse.urlsplit(value)
|
|
311
319
|
if not parsed.scheme or not parsed.netloc:
|
|
312
320
|
return value
|
|
@@ -319,10 +327,10 @@ def _redact_proxy_url(value: 'str') -> 'str':
|
|
|
319
327
|
|
|
320
328
|
|
|
321
329
|
async def _run_live_check(
|
|
322
|
-
config_path:
|
|
323
|
-
profile:
|
|
324
|
-
timeout_seconds:
|
|
325
|
-
) ->
|
|
330
|
+
config_path: "Path",
|
|
331
|
+
profile: "typing.Union[str, None]",
|
|
332
|
+
timeout_seconds: "float",
|
|
333
|
+
) -> "typing.Tuple[bool, str, typing.Union[str, None]]":
|
|
326
334
|
client = ResponsesModelClient.from_codex_config(
|
|
327
335
|
config_path,
|
|
328
336
|
profile,
|
|
@@ -345,16 +353,12 @@ async def _run_live_check(
|
|
|
345
353
|
|
|
346
354
|
elapsed = time.perf_counter() - started
|
|
347
355
|
output_text = next(
|
|
348
|
-
(
|
|
349
|
-
item.text
|
|
350
|
-
for item in response.items
|
|
351
|
-
if isinstance(item, AssistantMessage)
|
|
352
|
-
),
|
|
356
|
+
(item.text for item in response.items if isinstance(item, AssistantMessage)),
|
|
353
357
|
None,
|
|
354
358
|
)
|
|
355
359
|
return True, f"completed in {elapsed:.2f}s", output_text
|
|
356
360
|
|
|
357
361
|
|
|
358
|
-
def _finalize_report(report:
|
|
362
|
+
def _finalize_report(report: "DoctorReport") -> "DoctorReport":
|
|
359
363
|
report.ok = all(check.ok for check in report.checks)
|
|
360
364
|
return report
|