python-codex 0.2.7__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 +14 -14
- pycodex/agent.py +465 -499
- pycodex/bootstrap.py +417 -0
- pycodex/cli.py +236 -510
- 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 +324 -253
- pycodex/model_metadata.py +19 -7
- pycodex/portable.py +76 -45
- pycodex/portable_server.py +32 -24
- pycodex/prompts/models.json +245 -983
- pycodex/protocol.py +177 -137
- pycodex/runtime.py +579 -176
- pycodex/runtime_services.py +204 -157
- pycodex/tools/__init__.py +1 -1
- pycodex/tools/apply_patch_tool.py +69 -48
- pycodex/tools/base_tool.py +89 -42
- pycodex/tools/clock_tool.py +58 -25
- 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 +7 -5
- 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 +41 -72
- 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/{image_utils.py → utils/image_utils.py} +8 -11
- pycodex/utils/random_ids.py +1 -2
- pycodex/utils/session_persist.py +217 -163
- 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 +23 -23
- responses_server/messages_api.py +51 -53
- responses_server/payload_processors.py +25 -20
- responses_server/server.py +11 -11
- responses_server/session_store.py +14 -11
- responses_server/stream_router.py +101 -98
- responses_server/tools/custom_adapter.py +17 -16
- responses_server/tools/web_search.py +39 -36
- responses_server/trajectory_dump.py +36 -14
- workspace_server/__main__.py +0 -1
- workspace_server/app.py +461 -375
- workspace_server/workspace.html +852 -228
- workspace_server/workspaces.html +94 -95
- workspace_server/workspaces.py +137 -79
- 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 -560
- python_codex-0.2.7.dist-info/METADATA +0 -455
- python_codex-0.2.7.dist-info/RECORD +0 -93
- {python_codex-0.2.7.dist-info → python_codex-0.3.0.dist-info}/WHEEL +0 -0
- {python_codex-0.2.7.dist-info → python_codex-0.3.0.dist-info}/entry_points.txt +0 -0
- {python_codex-0.2.7.dist-info → python_codex-0.3.0.dist-info}/licenses/LICENSE +0 -0
pycodex/feishu_link.py
CHANGED
|
@@ -3,9 +3,9 @@ import threading
|
|
|
3
3
|
import time
|
|
4
4
|
import typing
|
|
5
5
|
|
|
6
|
+
from .events import SessionStateEvent
|
|
6
7
|
from .feishu_card import PycodexCard
|
|
7
|
-
from .
|
|
8
|
-
from .runtime import CliSubmissionQueue
|
|
8
|
+
from .runtime import AgentRuntime
|
|
9
9
|
|
|
10
10
|
CARD_UPDATE_FLUSH_INTERVAL_SECONDS = 0.5
|
|
11
11
|
_LISTENER = None
|
|
@@ -15,39 +15,39 @@ _LISTENER_LOCK = threading.Lock()
|
|
|
15
15
|
class PycodexRuntimeLink:
|
|
16
16
|
def __init__(
|
|
17
17
|
self,
|
|
18
|
-
|
|
18
|
+
runtime: "AgentRuntime",
|
|
19
19
|
target: str,
|
|
20
20
|
loop=None,
|
|
21
|
-
card:
|
|
21
|
+
card: "typing.Union[PycodexCard, None]" = None,
|
|
22
22
|
) -> None:
|
|
23
|
-
self.
|
|
23
|
+
self.runtime = runtime
|
|
24
24
|
self.target = target
|
|
25
25
|
self.loop = loop
|
|
26
26
|
self.card = card or PycodexCard.from_env()
|
|
27
27
|
self.session_key = None
|
|
28
28
|
self.message_id = None
|
|
29
29
|
self._listener = None
|
|
30
|
-
self.
|
|
31
|
-
self._event_handler = None
|
|
30
|
+
self._frontend_id = None
|
|
32
31
|
self._detached = False
|
|
33
32
|
self._update_thread = None
|
|
34
33
|
self._update_stop = threading.Event()
|
|
35
34
|
self._update_lock = threading.Lock()
|
|
36
35
|
self._update_pending = False
|
|
37
36
|
|
|
38
|
-
async def start_async(self) ->
|
|
37
|
+
async def start_async(self) -> "PycodexRuntimeLink":
|
|
39
38
|
self.loop = asyncio.get_event_loop()
|
|
40
|
-
|
|
39
|
+
self.card.apply_event(SessionStateEvent("attach", self.runtime.snapshot()))
|
|
40
|
+
try:
|
|
41
|
+
await self.loop.run_in_executor(None, self.start)
|
|
42
|
+
except Exception:
|
|
43
|
+
await self.loop.run_in_executor(None, self.stop)
|
|
44
|
+
raise
|
|
45
|
+
self._frontend_id = self.runtime.attach(self._handle_runtime_event)
|
|
46
|
+
return self
|
|
41
47
|
|
|
42
|
-
def start(self) ->
|
|
48
|
+
def start(self) -> "PycodexRuntimeLink":
|
|
43
49
|
if self.loop is None:
|
|
44
50
|
raise RuntimeError("PycodexRuntimeLink.start_async() is required")
|
|
45
|
-
self.card.set_snapshot(*_last_user_assistant_pair(self.queue))
|
|
46
|
-
if self._has_active_turn():
|
|
47
|
-
self.card.status = "Running"
|
|
48
|
-
self.card.status_detail = "Model request started."
|
|
49
|
-
self.card.running = True
|
|
50
|
-
self.card.model_name = getattr(self.queue._agent._model_client, 'model', 'pycodex')
|
|
51
51
|
self.card.send(self.target)
|
|
52
52
|
self.session_key = self.card.session_key
|
|
53
53
|
self.message_id = self.card.message_id
|
|
@@ -55,11 +55,10 @@ class PycodexRuntimeLink:
|
|
|
55
55
|
self._listener.register(self)
|
|
56
56
|
self._listener.start()
|
|
57
57
|
self._start_update_thread()
|
|
58
|
-
self._install_event_handler()
|
|
59
58
|
return self
|
|
60
59
|
|
|
61
60
|
def stop(self) -> None:
|
|
62
|
-
self.
|
|
61
|
+
self._detach_frontend()
|
|
63
62
|
if self._listener is not None:
|
|
64
63
|
_release_feishu_listener(self._listener, self)
|
|
65
64
|
self._listener = None
|
|
@@ -67,7 +66,7 @@ class PycodexRuntimeLink:
|
|
|
67
66
|
|
|
68
67
|
def detach(self) -> None:
|
|
69
68
|
self._detached = True
|
|
70
|
-
self.
|
|
69
|
+
self._detach_frontend()
|
|
71
70
|
if self._listener is not None:
|
|
72
71
|
_release_feishu_listener(self._listener, self)
|
|
73
72
|
self._listener = None
|
|
@@ -75,29 +74,29 @@ class PycodexRuntimeLink:
|
|
|
75
74
|
self._safe_update_card()
|
|
76
75
|
self._stop_update_thread()
|
|
77
76
|
|
|
78
|
-
def
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
self.
|
|
82
|
-
|
|
83
|
-
def _install_event_handler(self) -> None:
|
|
84
|
-
self._previous_event_handler = getattr(self.queue, "_event_handler", None)
|
|
85
|
-
self._event_handler = self._handle_runtime_event
|
|
86
|
-
self.queue.set_event_handler(self._event_handler)
|
|
77
|
+
def _detach_frontend(self) -> None:
|
|
78
|
+
if self._frontend_id is not None:
|
|
79
|
+
self.runtime.detach(self._frontend_id)
|
|
80
|
+
self._frontend_id = None
|
|
87
81
|
|
|
88
82
|
def _handle_runtime_event(self, event) -> None:
|
|
89
|
-
if self._previous_event_handler is not None:
|
|
90
|
-
self._previous_event_handler(event)
|
|
91
83
|
if self._detached:
|
|
92
84
|
return
|
|
93
85
|
self.card.apply_event(event)
|
|
94
86
|
self._update_card()
|
|
95
87
|
|
|
96
|
-
async def _submit(
|
|
88
|
+
async def _submit(
|
|
89
|
+
self, action: "typing.Dict[str, object]"
|
|
90
|
+
) -> "typing.Dict[str, object]":
|
|
97
91
|
if self._detached:
|
|
98
92
|
return {"toast": {"type": "warning", "content": "pycodex is detached."}}
|
|
99
|
-
if
|
|
100
|
-
|
|
93
|
+
if action.get("action") == "history":
|
|
94
|
+
try:
|
|
95
|
+
self.card.show_history(action.get("history_index"))
|
|
96
|
+
except ValueError as exc:
|
|
97
|
+
return {"toast": {"type": "warning", "content": str(exc)}}
|
|
98
|
+
self._update_card()
|
|
99
|
+
return {}
|
|
101
100
|
if action.get("action") != "send":
|
|
102
101
|
return {
|
|
103
102
|
"toast": {
|
|
@@ -106,46 +105,22 @@ class PycodexRuntimeLink:
|
|
|
106
105
|
}
|
|
107
106
|
}
|
|
108
107
|
prompt = str(action.get("prompt") or "").strip()
|
|
109
|
-
if not prompt:
|
|
110
|
-
return {"toast": {"type": "warning", "content": "Prompt is empty."}}
|
|
111
|
-
self.card.set_queued(prompt, sender=str(action.get("sender") or "cli"))
|
|
112
|
-
self._update_card()
|
|
113
|
-
queue_name = "steer" if self._has_active_turn() else "enqueue"
|
|
114
108
|
future = asyncio.run_coroutine_threadsafe(
|
|
115
|
-
self.
|
|
109
|
+
self.runtime.submit_input(
|
|
110
|
+
prompt, sender=str(action.get("sender") or "feishu")
|
|
111
|
+
),
|
|
116
112
|
self.loop,
|
|
117
113
|
)
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
if completed.cancelled():
|
|
124
|
-
self.card.status = "Idle"
|
|
125
|
-
self.card.status_detail = "submission cancelled"
|
|
126
|
-
self.card.running = False
|
|
127
|
-
self._update_card()
|
|
128
|
-
return
|
|
129
|
-
exc = completed.exception()
|
|
130
|
-
if exc is not None:
|
|
131
|
-
self.card.status = "Error"
|
|
132
|
-
self.card.status_detail = str(exc)
|
|
133
|
-
self.card.error = str(exc)
|
|
134
|
-
self.card.running = False
|
|
135
|
-
self._update_card()
|
|
136
|
-
|
|
137
|
-
del submission_id
|
|
138
|
-
self.loop.call_soon_threadsafe(lambda: turn_future.add_done_callback(on_done))
|
|
139
|
-
return {"toast": {"type": "info", "content": "pycodex is running."}}
|
|
114
|
+
try:
|
|
115
|
+
await asyncio.wrap_future(future)
|
|
116
|
+
except (ValueError, RuntimeError) as exc:
|
|
117
|
+
return {"toast": {"type": "warning", "content": str(exc)}}
|
|
118
|
+
return {"toast": {"type": "info", "content": "Input submitted."}}
|
|
140
119
|
|
|
141
120
|
def _update_card(self) -> None:
|
|
142
121
|
with self._update_lock:
|
|
143
122
|
self._update_pending = True
|
|
144
123
|
|
|
145
|
-
def _has_active_turn(self) -> bool:
|
|
146
|
-
task = getattr(self.queue, "_current_task", None)
|
|
147
|
-
return task is not None and not task.done()
|
|
148
|
-
|
|
149
124
|
def _safe_update_card(self) -> None:
|
|
150
125
|
try:
|
|
151
126
|
self.card.update()
|
|
@@ -179,22 +154,7 @@ class PycodexRuntimeLink:
|
|
|
179
154
|
self._safe_update_card()
|
|
180
155
|
|
|
181
156
|
|
|
182
|
-
def
|
|
183
|
-
agent = getattr(queue, "_agent", None)
|
|
184
|
-
history = getattr(agent, "history", ())
|
|
185
|
-
output = ""
|
|
186
|
-
prompt = ""
|
|
187
|
-
for item in reversed(history):
|
|
188
|
-
if not output and isinstance(item, AssistantMessage):
|
|
189
|
-
output = item.text
|
|
190
|
-
continue
|
|
191
|
-
if output and isinstance(item, UserMessage):
|
|
192
|
-
prompt = item.text
|
|
193
|
-
break
|
|
194
|
-
return prompt, output
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
def _feishu_listener(card: PycodexCard) -> '_FeishuCardActionListener':
|
|
157
|
+
def _feishu_listener(card: PycodexCard) -> "_FeishuCardActionListener":
|
|
198
158
|
global _LISTENER
|
|
199
159
|
with _LISTENER_LOCK:
|
|
200
160
|
if _LISTENER is None:
|
|
@@ -205,7 +165,7 @@ def _feishu_listener(card: PycodexCard) -> '_FeishuCardActionListener':
|
|
|
205
165
|
|
|
206
166
|
|
|
207
167
|
def _release_feishu_listener(
|
|
208
|
-
listener:
|
|
168
|
+
listener: "_FeishuCardActionListener",
|
|
209
169
|
link: PycodexRuntimeLink,
|
|
210
170
|
) -> None:
|
|
211
171
|
global _LISTENER
|
|
@@ -343,7 +303,7 @@ class _FeishuCardActionListener:
|
|
|
343
303
|
_close_loop(loop)
|
|
344
304
|
asyncio.set_event_loop(None)
|
|
345
305
|
|
|
346
|
-
def _handle_card_action(self, event) ->
|
|
306
|
+
def _handle_card_action(self, event) -> "typing.Dict[str, object]":
|
|
347
307
|
try:
|
|
348
308
|
message_id = _event_message_id(event)
|
|
349
309
|
link = self._resolve_link(message_id)
|