jupyter-agent 2025.6.100__py3-none-any.whl → 2025.6.101__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.
- jupyter_agent/__init__.py +0 -0
- jupyter_agent/bot_agents/__init__.py +42 -0
- jupyter_agent/bot_agents/base.py +324 -0
- jupyter_agent/bot_agents/master_planner.py +45 -0
- jupyter_agent/bot_agents/output_task_result.py +29 -0
- jupyter_agent/bot_agents/task_code_executor.py +53 -0
- jupyter_agent/bot_agents/task_coder.py +71 -0
- jupyter_agent/bot_agents/task_debuger.py +69 -0
- jupyter_agent/bot_agents/task_planner_v1.py +158 -0
- jupyter_agent/bot_agents/task_planner_v2.py +172 -0
- jupyter_agent/bot_agents/task_planner_v3.py +189 -0
- jupyter_agent/bot_agents/task_reasoner.py +61 -0
- jupyter_agent/bot_agents/task_structrue_reasoner.py +106 -0
- jupyter_agent/bot_agents/task_structrue_summarier.py +123 -0
- jupyter_agent/bot_agents/task_summarier.py +76 -0
- jupyter_agent/bot_agents/task_verifier.py +99 -0
- jupyter_agent/bot_agents/task_verify_summarier.py +134 -0
- jupyter_agent/bot_chat.py +218 -0
- jupyter_agent/bot_contexts.py +466 -0
- jupyter_agent/bot_flows/__init__.py +20 -0
- jupyter_agent/bot_flows/base.py +209 -0
- jupyter_agent/bot_flows/master_planner.py +16 -0
- jupyter_agent/bot_flows/task_executor_v1.py +86 -0
- jupyter_agent/bot_flows/task_executor_v2.py +84 -0
- jupyter_agent/bot_flows/task_executor_v3.py +89 -0
- jupyter_agent/bot_magics.py +127 -0
- jupyter_agent/bot_outputs.py +480 -0
- jupyter_agent/utils.py +138 -0
- {jupyter_agent-2025.6.100.dist-info → jupyter_agent-2025.6.101.dist-info}/METADATA +13 -7
- jupyter_agent-2025.6.101.dist-info/RECORD +33 -0
- jupyter_agent-2025.6.101.dist-info/top_level.txt +1 -0
- jupyter_agent-2025.6.100.dist-info/RECORD +0 -5
- jupyter_agent-2025.6.100.dist-info/top_level.txt +0 -1
- {jupyter_agent-2025.6.100.dist-info → jupyter_agent-2025.6.101.dist-info}/WHEEL +0 -0
- {jupyter_agent-2025.6.100.dist-info → jupyter_agent-2025.6.101.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,466 @@
|
|
1
|
+
"""
|
2
|
+
Copyright (c) 2025 viewstar000
|
3
|
+
|
4
|
+
This software is released under the MIT License.
|
5
|
+
https://opensource.org/licenses/MIT
|
6
|
+
"""
|
7
|
+
|
8
|
+
import os
|
9
|
+
import re
|
10
|
+
import json
|
11
|
+
import yaml
|
12
|
+
import time
|
13
|
+
import shlex
|
14
|
+
import argparse
|
15
|
+
import traceback
|
16
|
+
import nbformat
|
17
|
+
|
18
|
+
|
19
|
+
from typing import Optional
|
20
|
+
from enum import Enum
|
21
|
+
from pydantic import BaseModel, Field
|
22
|
+
from IPython.core.getipython import get_ipython
|
23
|
+
from .bot_outputs import _D, _I, _W, _E, _F, _A, ReplyType
|
24
|
+
|
25
|
+
|
26
|
+
class CellType(str, Enum):
|
27
|
+
CODE = "code"
|
28
|
+
MARKDOWN = "markdown"
|
29
|
+
PLANNING = "planning"
|
30
|
+
TASK = "task"
|
31
|
+
|
32
|
+
|
33
|
+
class CellContext:
|
34
|
+
|
35
|
+
def __init__(self, idx: int, cell: dict):
|
36
|
+
self.cell_idx = idx
|
37
|
+
self.cell_id = cell.get("id")
|
38
|
+
self.cell_type: Optional[CellType] = cell["cell_type"]
|
39
|
+
self.cell_source = cell["source"].strip()
|
40
|
+
self.cell_tags = set(cell.get("metadata", {}).get("tags", []))
|
41
|
+
if mo := re.match(r"^#\s*BOT_CONTEXT:(.+)$", self.cell_source.split("\n", 1)[0]):
|
42
|
+
context_tags = ["CTX_" + t.strip().upper() for t in mo.group(1).strip().split(",")]
|
43
|
+
self.cell_tags |= set(context_tags)
|
44
|
+
self.cell_source = self.cell_source.split("\n", 1)[1].strip()
|
45
|
+
|
46
|
+
@property
|
47
|
+
def type(self):
|
48
|
+
return self.cell_type
|
49
|
+
|
50
|
+
@property
|
51
|
+
def source(self):
|
52
|
+
return self.cell_source
|
53
|
+
|
54
|
+
@property
|
55
|
+
def is_code_context(self):
|
56
|
+
return (
|
57
|
+
self.cell_type == CellType.CODE or "CTX_CODE" in self.cell_tags
|
58
|
+
) and "CTX_EXCLUDE" not in self.cell_tags
|
59
|
+
|
60
|
+
@property
|
61
|
+
def is_task_context(self):
|
62
|
+
return (
|
63
|
+
self.cell_type in (CellType.TASK, CellType.PLANNING, CellType.MARKDOWN) or "CTX_TASK" in self.cell_tags
|
64
|
+
) and "CTX_EXCLUDE" not in self.cell_tags
|
65
|
+
|
66
|
+
|
67
|
+
class CodeCellContext(CellContext):
|
68
|
+
"""任务单元格上下文类"""
|
69
|
+
|
70
|
+
max_output_size = 24 * 1024
|
71
|
+
max_result_size = 24 * 1024
|
72
|
+
max_error_size = 4 * 1024
|
73
|
+
|
74
|
+
def __init__(self, idx: int, cell: dict):
|
75
|
+
"""初始化任务单元格上下文"""
|
76
|
+
super().__init__(idx, cell)
|
77
|
+
assert self.cell_type == CellType.CODE
|
78
|
+
self._cell_output = ""
|
79
|
+
self._cell_result = ""
|
80
|
+
self._cell_error = ""
|
81
|
+
self.load_cell_outputs(cell)
|
82
|
+
|
83
|
+
def get_cell_output(self):
|
84
|
+
"""获取任务单元格的输出"""
|
85
|
+
if len(self._cell_output) > self.max_output_size:
|
86
|
+
half_size = self.max_output_size // 2
|
87
|
+
self._cell_output = self._cell_output[:half_size] + "..." + self._cell_output[-half_size:]
|
88
|
+
return self._cell_output
|
89
|
+
|
90
|
+
def set_cell_output(self, output):
|
91
|
+
"""设置任务单元格的输出"""
|
92
|
+
self._cell_output = output
|
93
|
+
if len(self._cell_output) > self.max_output_size:
|
94
|
+
half_size = self.max_output_size // 2
|
95
|
+
self._cell_output = self._cell_output[:half_size] + "..." + self._cell_output[-half_size:]
|
96
|
+
|
97
|
+
cell_output = property(get_cell_output, set_cell_output)
|
98
|
+
|
99
|
+
def get_cell_result(self):
|
100
|
+
"""获取任务单元格的结果"""
|
101
|
+
if len(self._cell_result) > self.max_result_size:
|
102
|
+
half_size = self.max_result_size // 2
|
103
|
+
self._cell_result = self._cell_result[:half_size] + "..." + self._cell_result[-half_size:]
|
104
|
+
return self._cell_result
|
105
|
+
|
106
|
+
def set_cell_result(self, result):
|
107
|
+
"""设置任务单元格的结果"""
|
108
|
+
self._cell_result = result
|
109
|
+
if len(self._cell_result) > self.max_result_size:
|
110
|
+
half_size = self.max_result_size // 2
|
111
|
+
self._cell_result = self._cell_result[:half_size] + "..." + self._cell_result[-half_size:]
|
112
|
+
|
113
|
+
cell_result = property(get_cell_result, set_cell_result)
|
114
|
+
|
115
|
+
def get_cell_error(self):
|
116
|
+
"""获取任务单元格的错误信息"""
|
117
|
+
if len(self._cell_error) > self.max_error_size:
|
118
|
+
half_size = self.max_error_size // 2
|
119
|
+
self._cell_error = self._cell_error[:half_size] + "..." + self._cell_error[-half_size:]
|
120
|
+
return self._cell_error
|
121
|
+
|
122
|
+
def set_cell_error(self, error):
|
123
|
+
"""设置任务单元格的错误信息"""
|
124
|
+
self._cell_error = error
|
125
|
+
if len(self._cell_error) > self.max_error_size:
|
126
|
+
half_size = self.max_error_size // 2
|
127
|
+
self._cell_error = self._cell_error[:half_size] + "..." + self._cell_error[-half_size:]
|
128
|
+
|
129
|
+
cell_error = property(get_cell_error, set_cell_error)
|
130
|
+
|
131
|
+
def load_cell_outputs(self, cell):
|
132
|
+
"""加载当前任务单元格的上下文"""
|
133
|
+
try:
|
134
|
+
self.cell_output = ""
|
135
|
+
self.cell_result = ""
|
136
|
+
self.cell_error = ""
|
137
|
+
for output in cell.get("outputs", []):
|
138
|
+
# Available output types: stream, error, execute_result, display_data
|
139
|
+
if output["output_type"] == "stream":
|
140
|
+
_D(f"CELL[{self.cell_idx}] Stream output: {output["name"]}:{repr(output['text'])[:50]}")
|
141
|
+
self.cell_output += output["name"] + ":\n" + output["text"] + "\n"
|
142
|
+
if output["output_type"] == "error":
|
143
|
+
_D(f"CELL[{self.cell_idx}] Error output: {output.get("ename", "")} {output.get('evalue', "")}")
|
144
|
+
self.cell_error += output.get("ename", "") + ": " + output.get("evalue", "") + "\n"
|
145
|
+
if "traceback" in output:
|
146
|
+
self.cell_error += "Traceback:\n" + "\n".join(output.get("traceback", [])) + "\n"
|
147
|
+
if output["output_type"] == "execute_result":
|
148
|
+
output_data = output.get("data", {})
|
149
|
+
output_text = output_data.get("text/markdown") or output_data.get("text/plain")
|
150
|
+
_D(f"CELL[{self.cell_idx}] Execute result: {repr(output_text)[:50]}")
|
151
|
+
self.cell_result += output_text + "\n"
|
152
|
+
if output["output_type"] == "display_data":
|
153
|
+
output_meta = output.get("metadata", {})
|
154
|
+
if not output_meta.get("exclude_from_context", False):
|
155
|
+
output_data = output.get("data", {})
|
156
|
+
output_text = output_data.get("text/markdown") or output_data.get("text/plain")
|
157
|
+
reply_type = output_meta.get("reply_type")
|
158
|
+
if reply_type == ReplyType.CELL_ERROR:
|
159
|
+
_D(f"CELL[{self.cell_idx}] Display error data: {repr(output_text)[:50]}")
|
160
|
+
self.cell_error += output_text + "\n"
|
161
|
+
else:
|
162
|
+
_D(f"CELL[{self.cell_idx}] Display output data: {repr(output_text)[:50]}")
|
163
|
+
self.cell_output += output_text + "\n"
|
164
|
+
except Exception as e:
|
165
|
+
_W("Failed to load notebook cells {}: {}".format(type(e), str(e)))
|
166
|
+
_W(traceback.format_exc(limit=2))
|
167
|
+
|
168
|
+
|
169
|
+
class AgentData(BaseModel):
|
170
|
+
task_id: str = Field("", description="任务ID")
|
171
|
+
subject: str = Field("", description="任务目标")
|
172
|
+
coding_prompt: str = Field("", description="Agent编程提示")
|
173
|
+
verify_prompt: str = Field("", description="Agent验证提示")
|
174
|
+
summary_prompt: str = Field("", description="Agent总结提示")
|
175
|
+
issue: str = Field("", description="Agent验证不通过的问题")
|
176
|
+
result: str = Field("", description="Agent执行结果")
|
177
|
+
important_infos: Optional[dict] = Field(None, description="重要信息[JSON]")
|
178
|
+
|
179
|
+
@classmethod
|
180
|
+
def default(cls):
|
181
|
+
model_fields = getattr(cls, "model_fields", None)
|
182
|
+
if model_fields and hasattr(model_fields, "items"):
|
183
|
+
return {
|
184
|
+
name: field.examples[0] if getattr(field, "examples", None) else getattr(field, "default", None)
|
185
|
+
for name, field in model_fields.items()
|
186
|
+
}
|
187
|
+
else:
|
188
|
+
return {}
|
189
|
+
|
190
|
+
|
191
|
+
class AgentCellContext(CodeCellContext):
|
192
|
+
"""任务单元格上下文类"""
|
193
|
+
|
194
|
+
SUPPORT_SAVE_META: bool = False
|
195
|
+
|
196
|
+
def __init__(self, idx: int, cell: dict):
|
197
|
+
"""初始化任务单元格上下文"""
|
198
|
+
super().__init__(idx, cell)
|
199
|
+
self.agent_flow = None
|
200
|
+
self.agent_stage = None
|
201
|
+
self.magic_line, self.magic_code = self.cell_source.split("\n", 1)
|
202
|
+
self.magic_argv = shlex.split(self.magic_line)
|
203
|
+
self.magic_name = self.magic_argv[0]
|
204
|
+
self.magic_argv = self.magic_argv[1:]
|
205
|
+
self._remain_args = []
|
206
|
+
self._agent_data = AgentData.default()
|
207
|
+
self._cell_code = ""
|
208
|
+
self.parse_magic_argv()
|
209
|
+
self.load_result_from_outputs(cell)
|
210
|
+
self.load_data_from_metadata(cell)
|
211
|
+
self.load_data_from_source()
|
212
|
+
|
213
|
+
def get_source(self):
|
214
|
+
return self._cell_code
|
215
|
+
|
216
|
+
def set_source(self, value):
|
217
|
+
self._cell_code = value
|
218
|
+
|
219
|
+
source = property(get_source, set_source)
|
220
|
+
|
221
|
+
@property
|
222
|
+
def output(self):
|
223
|
+
return self.cell_output + "\n" + self.cell_result
|
224
|
+
|
225
|
+
@property
|
226
|
+
def result(self):
|
227
|
+
return self._agent_data["result"]
|
228
|
+
|
229
|
+
def __getattr__(self, name):
|
230
|
+
return self._agent_data[name]
|
231
|
+
|
232
|
+
def get_data(self, name):
|
233
|
+
return self._agent_data[name]
|
234
|
+
|
235
|
+
def set_data(self, name, value):
|
236
|
+
self._agent_data[name] = value
|
237
|
+
|
238
|
+
def parse_magic_argv(self):
|
239
|
+
"""解析任务单元格的magic命令参数"""
|
240
|
+
parser = argparse.ArgumentParser()
|
241
|
+
parser.add_argument("-P", "--planning", action="store_true", default=False, help="Run in planning mode")
|
242
|
+
parser.add_argument("-f", "--flow", type=str, default=None, help="Task stage")
|
243
|
+
parser.add_argument("-s", "--stage", type=str, default=None, help="Task stage")
|
244
|
+
options, self._remain_args = parser.parse_known_args(self.magic_argv)
|
245
|
+
_D(
|
246
|
+
"CELL[{}] Magic Name: {}, Magic Args: {}, Remain Args: {}".format(
|
247
|
+
self.cell_idx, self.magic_name, options, self._remain_args
|
248
|
+
)
|
249
|
+
)
|
250
|
+
self.agent_flow = options.flow
|
251
|
+
self.agent_stage = options.stage
|
252
|
+
if options.planning and not self.agent_flow:
|
253
|
+
self.agent_flow = "planning"
|
254
|
+
if self.agent_flow and self.agent_flow.startswith("planning"):
|
255
|
+
self.cell_type = CellType.PLANNING
|
256
|
+
else:
|
257
|
+
self.cell_type = CellType.TASK
|
258
|
+
|
259
|
+
def load_data_from_source(self):
|
260
|
+
"""解析任务单元格的选项"""
|
261
|
+
cell_options = ""
|
262
|
+
cell_code = ""
|
263
|
+
is_option = False
|
264
|
+
for line in self.magic_code.split("\n"):
|
265
|
+
if line.strip() == "## Task Options:":
|
266
|
+
is_option = True
|
267
|
+
continue
|
268
|
+
if line.strip() == "## ---":
|
269
|
+
is_option = False
|
270
|
+
continue
|
271
|
+
if is_option:
|
272
|
+
if line.startswith("# "):
|
273
|
+
cell_options += line[2:] + "\n"
|
274
|
+
else:
|
275
|
+
is_option = False
|
276
|
+
cell_code += line + "\n"
|
277
|
+
else:
|
278
|
+
cell_code += line + "\n"
|
279
|
+
self._cell_code = cell_code.strip()
|
280
|
+
_D("CELL[{}] Cell Options: {} ...".format(self.cell_idx, repr(cell_options)[:80]))
|
281
|
+
_D("CELL[{}] Cell Code: {} ...".format(self.cell_idx, repr(self._cell_code)[:80]))
|
282
|
+
if cell_options:
|
283
|
+
try:
|
284
|
+
cell_options = yaml.safe_load(cell_options)
|
285
|
+
for key, value in cell_options.items():
|
286
|
+
if key in self._agent_data:
|
287
|
+
if isinstance(self._agent_data[key], (dict, list)) and isinstance(value, str):
|
288
|
+
value = json.loads(value)
|
289
|
+
_D("CELL[{}] Load task option {}: {}".format(self.cell_idx, key, value))
|
290
|
+
self._agent_data[key] = value
|
291
|
+
except Exception as e:
|
292
|
+
_W("Failed to load task options {}: {}".format(type(e), str(e)))
|
293
|
+
_W(traceback.format_exc(limit=2))
|
294
|
+
|
295
|
+
def load_result_from_outputs(self, cell):
|
296
|
+
task_result = ""
|
297
|
+
for output in cell.get("outputs", []):
|
298
|
+
# Available output types: stream, error, execute_result, display_data
|
299
|
+
if output["output_type"] == "display_data":
|
300
|
+
output_data = output.get("data", {})
|
301
|
+
output_meta = output.get("metadata", {})
|
302
|
+
if output_meta.get("reply_type") == ReplyType.TASK_RESULT:
|
303
|
+
output_text = output_data.get("text/markdown") or output_data.get("text/plain")
|
304
|
+
_D(f"CELL[{self.cell_idx}] Task result: {repr(output_text)[:80]}")
|
305
|
+
task_result += "\n" + output_text
|
306
|
+
if task_result.strip():
|
307
|
+
self._agent_data["result"] = task_result
|
308
|
+
|
309
|
+
def load_data_from_metadata(self, cell):
|
310
|
+
agent_meta_infos = cell.get("metadata", {}).get("jupyter-agent-data", {})
|
311
|
+
_D("CELL[{}] Agent Meta Data: {}".format(self.cell_idx, repr(agent_meta_infos)[:80]))
|
312
|
+
for k, v in agent_meta_infos.items():
|
313
|
+
if k in self._agent_data:
|
314
|
+
_D(f"CELL[{self.cell_idx}] Load agent meta data: {k}: {repr(v)[:80]}")
|
315
|
+
self._agent_data[k] = v
|
316
|
+
|
317
|
+
def format_magic_line(self):
|
318
|
+
magic_args = ["%%bot"]
|
319
|
+
if self.agent_stage:
|
320
|
+
magic_args += ["-s", self.agent_stage]
|
321
|
+
if self.agent_flow:
|
322
|
+
if self.agent_flow == "planning":
|
323
|
+
magic_args += ["-P"]
|
324
|
+
else:
|
325
|
+
magic_args += ["-f", self.agent_flow]
|
326
|
+
magic_args += self._remain_args
|
327
|
+
magic_line = shlex.join(magic_args) + "\n"
|
328
|
+
return magic_line
|
329
|
+
|
330
|
+
def _format_yaml_element(self, e, level=0, indent=4):
|
331
|
+
|
332
|
+
space = " " * indent * level
|
333
|
+
result = ""
|
334
|
+
if isinstance(e, dict):
|
335
|
+
result += "\n"
|
336
|
+
for k, v in e.items():
|
337
|
+
if not v:
|
338
|
+
continue
|
339
|
+
result += f"{space}{k}: "
|
340
|
+
result += self._format_yaml_element(v, level + 1, indent)
|
341
|
+
elif isinstance(e, list):
|
342
|
+
result += "\n"
|
343
|
+
for v in e:
|
344
|
+
result += f"{space}- "
|
345
|
+
result += self._format_yaml_element(v, level + 1, indent)
|
346
|
+
elif isinstance(e, BaseModel):
|
347
|
+
result += self._format_yaml_element(e.model_dump(), level, indent)
|
348
|
+
elif isinstance(e, str):
|
349
|
+
if "\n" in e:
|
350
|
+
if e.endswith("\n"):
|
351
|
+
result += f"|\n"
|
352
|
+
else:
|
353
|
+
result += f"|-\n"
|
354
|
+
for line in e.split("\n"):
|
355
|
+
result += f"{space}{line}\n"
|
356
|
+
elif ":" in e or '"' in e or "'" in e:
|
357
|
+
result += f"'{e.replace("'", "''")}'\n"
|
358
|
+
else:
|
359
|
+
result += f"{e}\n"
|
360
|
+
elif e is None:
|
361
|
+
result += "null\n"
|
362
|
+
else:
|
363
|
+
result += f"{e}\n"
|
364
|
+
return result
|
365
|
+
|
366
|
+
def format_cell_options(self):
|
367
|
+
cell_options = {}
|
368
|
+
if self.SUPPORT_SAVE_META:
|
369
|
+
if self._agent_data.get("task_id"):
|
370
|
+
cell_options["task_id"] = self._agent_data["task_id"]
|
371
|
+
if self._agent_data.get("subject"):
|
372
|
+
cell_options["subject"] = self._agent_data["subject"]
|
373
|
+
else:
|
374
|
+
for key, value in self._agent_data.items():
|
375
|
+
if key == "result" and self.type == CellType.PLANNING:
|
376
|
+
continue
|
377
|
+
if value:
|
378
|
+
if (
|
379
|
+
isinstance(value, (dict, list))
|
380
|
+
and AgentData.model_fields[key]
|
381
|
+
and AgentData.model_fields[key].description is not None
|
382
|
+
and "[JSON]" in AgentData.model_fields[key].description # type: ignore
|
383
|
+
):
|
384
|
+
value = json.dumps(value, ensure_ascii=False, indent=4)
|
385
|
+
cell_options[key] = value
|
386
|
+
if cell_options:
|
387
|
+
cell_options["update_time"] = time.strftime("%Y-%m-%d %H:%M:%S")
|
388
|
+
if cell_options:
|
389
|
+
result = "\n".join(f"# {line}" for line in self._format_yaml_element(cell_options).strip().split("\n"))
|
390
|
+
result = f"\n## Task Options:\n{result}\n## ---\n"
|
391
|
+
return result
|
392
|
+
return ""
|
393
|
+
|
394
|
+
def update_cell(self):
|
395
|
+
"""生成Cell内容"""
|
396
|
+
_I("Updating Cell ...")
|
397
|
+
_A(**self._agent_data)
|
398
|
+
cell_source = ""
|
399
|
+
cell_source += self.format_magic_line()
|
400
|
+
cell_source += "\n" + self.format_cell_options()
|
401
|
+
cell_source += "\n" + self.source
|
402
|
+
ipython = get_ipython()
|
403
|
+
if ipython is not None:
|
404
|
+
_D("Updating Cell Source: {} ...".format(repr(cell_source)[:80]))
|
405
|
+
ipython.set_next_input(cell_source, replace=True)
|
406
|
+
|
407
|
+
|
408
|
+
class NotebookContext:
|
409
|
+
"""Notebook上下文类"""
|
410
|
+
|
411
|
+
def __init__(self, cur_line, cur_content, notebook_path):
|
412
|
+
"""初始化Notebook上下文"""
|
413
|
+
self.cur_line = cur_line.strip()
|
414
|
+
self.cur_content = cur_content.strip()
|
415
|
+
self.notebook_path = notebook_path
|
416
|
+
self.notebook_state = None
|
417
|
+
self._cells = []
|
418
|
+
self._current_cell = None
|
419
|
+
|
420
|
+
@property
|
421
|
+
def cells(self):
|
422
|
+
"""获取当前cell之前的所有cell内容"""
|
423
|
+
try:
|
424
|
+
if (
|
425
|
+
not self._cells
|
426
|
+
or self.notebook_state is None
|
427
|
+
or self.notebook_state != os.stat(self.notebook_path).st_mtime
|
428
|
+
):
|
429
|
+
_I(f"Loading Notebook Context: {self.notebook_path}")
|
430
|
+
with open(self.notebook_path, "r", encoding="utf-8") as f:
|
431
|
+
nb = nbformat.read(f, as_version=4)
|
432
|
+
self._cells = []
|
433
|
+
for idx, cell in enumerate(nb.cells):
|
434
|
+
_D(f"CELL[{idx}] {cell['cell_type']} {repr(cell['source'])[:80]}")
|
435
|
+
if cell["cell_type"] == "code":
|
436
|
+
if cell["source"].strip().startswith("%%bot"):
|
437
|
+
cell_ctx = AgentCellContext(idx, cell)
|
438
|
+
if (
|
439
|
+
self.cur_line.strip() == cell_ctx.magic_line[len(cell_ctx.magic_name) :].strip()
|
440
|
+
and self.cur_content.strip() == cell_ctx.magic_code.strip()
|
441
|
+
):
|
442
|
+
if self._current_cell is None:
|
443
|
+
_I(f"CELL[{idx}] Reach current cell, RETURN!")
|
444
|
+
self._current_cell = cell_ctx
|
445
|
+
else:
|
446
|
+
_I(f"CELL[{idx}] Reach current cell, SKIP!")
|
447
|
+
break
|
448
|
+
else:
|
449
|
+
cell_ctx = CodeCellContext(idx, cell)
|
450
|
+
else:
|
451
|
+
cell_ctx = CellContext(idx, cell)
|
452
|
+
self._cells.append(cell_ctx)
|
453
|
+
self.notebook_state = os.stat(self.notebook_path).st_mtime
|
454
|
+
_I(f"Got {len(self._cells)} notebook cells")
|
455
|
+
except Exception as e:
|
456
|
+
_E("Failed to get notebook cells {}: {}".format(type(e), str(e)))
|
457
|
+
_E(traceback.format_exc(limit=2))
|
458
|
+
self._cells = []
|
459
|
+
return self._cells
|
460
|
+
|
461
|
+
@property
|
462
|
+
def cur_task(self):
|
463
|
+
"""获取当前任务单元格的上下文"""
|
464
|
+
if self._current_cell is None:
|
465
|
+
len(self.cells)
|
466
|
+
return self._current_cell
|
@@ -0,0 +1,20 @@
|
|
1
|
+
"""
|
2
|
+
Copyright (c) 2025 viewstar000
|
3
|
+
|
4
|
+
This software is released under the MIT License.
|
5
|
+
https://opensource.org/licenses/MIT
|
6
|
+
"""
|
7
|
+
|
8
|
+
from .base import BaseTaskFlow
|
9
|
+
from .master_planner import MasterPlannerFlow
|
10
|
+
from .task_executor_v1 import TaskExecutorFlowV1
|
11
|
+
from .task_executor_v2 import TaskExecutorFlowV2
|
12
|
+
from .task_executor_v3 import TaskExecutorFlowV3
|
13
|
+
|
14
|
+
__all__ = [
|
15
|
+
"BaseTaskFlow",
|
16
|
+
"MasterPlannerFlow",
|
17
|
+
"TaskExecutorFlowV1",
|
18
|
+
"TaskExecutorFlowV2",
|
19
|
+
"TaskExecutorFlowV3",
|
20
|
+
]
|