jupyter-agent 2025.6.104__py3-none-any.whl → 2025.7.100__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.
Files changed (43) hide show
  1. jupyter_agent/bot_actions.py +270 -0
  2. jupyter_agent/bot_agents/__init__.py +0 -42
  3. jupyter_agent/bot_agents/base.py +89 -45
  4. jupyter_agent/bot_agents/master_planner.py +1 -0
  5. jupyter_agent/bot_agents/output_task_result.py +6 -7
  6. jupyter_agent/bot_agents/prepare_next_cell.py +52 -0
  7. jupyter_agent/bot_agents/request_user_supply.py +186 -0
  8. jupyter_agent/bot_agents/task_code_executor.py +3 -2
  9. jupyter_agent/bot_agents/task_planner_v3.py +16 -13
  10. jupyter_agent/bot_agents/task_reasoner.py +3 -2
  11. jupyter_agent/bot_agents/task_structrue_reasoner.py +22 -12
  12. jupyter_agent/bot_agents/task_structrue_summarier.py +22 -18
  13. jupyter_agent/bot_agents/task_summarier.py +3 -2
  14. jupyter_agent/bot_agents/task_verifier.py +2 -1
  15. jupyter_agent/bot_agents/task_verify_summarier.py +6 -6
  16. jupyter_agent/bot_chat.py +2 -2
  17. jupyter_agent/bot_contexts.py +37 -29
  18. jupyter_agent/bot_evaluation.py +262 -143
  19. jupyter_agent/bot_evaluators/__init__.py +0 -0
  20. jupyter_agent/bot_evaluators/base.py +42 -0
  21. jupyter_agent/bot_evaluators/dummy_flow.py +20 -0
  22. jupyter_agent/bot_evaluators/dummy_global.py +20 -0
  23. jupyter_agent/bot_evaluators/dummy_task.py +20 -0
  24. jupyter_agent/bot_evaluators/flow_global_planning.py +88 -0
  25. jupyter_agent/bot_evaluators/flow_task_executor.py +152 -0
  26. jupyter_agent/bot_flows/__init__.py +0 -4
  27. jupyter_agent/bot_flows/base.py +120 -41
  28. jupyter_agent/bot_flows/master_planner.py +15 -4
  29. jupyter_agent/bot_flows/task_executor_v3.py +57 -38
  30. jupyter_agent/bot_magics.py +119 -69
  31. jupyter_agent/bot_outputs.py +37 -43
  32. jupyter_agent/utils.py +20 -31
  33. {jupyter_agent-2025.6.104.dist-info → jupyter_agent-2025.7.100.dist-info}/METADATA +56 -4
  34. jupyter_agent-2025.7.100.dist-info/RECORD +41 -0
  35. jupyter_agent/bot_agents/task_planner_v1.py +0 -158
  36. jupyter_agent/bot_agents/task_planner_v2.py +0 -172
  37. jupyter_agent/bot_flows/task_executor_v1.py +0 -86
  38. jupyter_agent/bot_flows/task_executor_v2.py +0 -84
  39. jupyter_agent-2025.6.104.dist-info/RECORD +0 -35
  40. {jupyter_agent-2025.6.104.dist-info → jupyter_agent-2025.7.100.dist-info}/WHEEL +0 -0
  41. {jupyter_agent-2025.6.104.dist-info → jupyter_agent-2025.7.100.dist-info}/entry_points.txt +0 -0
  42. {jupyter_agent-2025.6.104.dist-info → jupyter_agent-2025.7.100.dist-info}/licenses/LICENSE +0 -0
  43. {jupyter_agent-2025.6.104.dist-info → jupyter_agent-2025.7.100.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,186 @@
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 time
9
+ import uuid
10
+ import json
11
+
12
+ from enum import Enum
13
+ from typing import List, Optional, Dict, Any, Tuple
14
+ from pydantic import BaseModel, Field
15
+ from IPython.display import Markdown
16
+ from .base import BaseChatAgent, AgentOutputFormat
17
+ from ..bot_outputs import _D, _I, _W, _E, _F, _M, _B, _C, _O
18
+ from ..bot_actions import (
19
+ get_action_dispatcher,
20
+ RequestUserSupplyInfo,
21
+ UserSupplyInfoReply,
22
+ ActionRequestUserSupplyInfo,
23
+ RequestUserSupplyInfoParams,
24
+ ActionReceiveUserSupplyInfo,
25
+ ReceiveUserSupplyInfoParams,
26
+ ActionSetCellContent,
27
+ SetCellContentParams,
28
+ )
29
+ from ..utils import get_env_capbilities
30
+
31
+ MOCK_USER_REPLY_PROMPT = """\
32
+ **角色定义**:
33
+
34
+ 你是一个用户需求补充专家,负责补充用户的需求信息,以便于更好的完成任务。
35
+
36
+ **任务要求**:
37
+
38
+ 根据提示补充待确认的信息,以便于更好的完成任务。
39
+
40
+
41
+ {% include "TASK_OUTPUT_FORMAT" %}
42
+
43
+ ---
44
+
45
+ {% include "TASK_CONTEXTS" %}
46
+
47
+ ---
48
+
49
+ {% include "CODE_CONTEXTS" %}
50
+
51
+ ---
52
+
53
+ **当前子任务信息**:
54
+
55
+ ### 当前子任务目标:
56
+ {{ task.subject }}
57
+
58
+ ### 当前子任务代码需求:
59
+ {{ task.coding_prompt }}
60
+
61
+ ### 当前代码:
62
+ ```python
63
+ {{ task.source }}
64
+ ```
65
+
66
+ ### 当前代码执行的输出与结果:
67
+ {{ task.output }}
68
+
69
+ ### 当前任务总结要求:
70
+ {{ task.summary_prompt }}
71
+
72
+ ### 当前任务总结结果:
73
+ {{ task.result }}
74
+
75
+ ---
76
+
77
+ 需要补充确认的信息:
78
+
79
+ {% for issue in request_supply_infos %}
80
+ - {{ issue.prompt }}, (例如: {{ issue.example }})
81
+ {% endfor %}
82
+
83
+ ---
84
+
85
+ 请按要求补充上述待确认的信息:
86
+ """
87
+
88
+
89
+ def format_request_user_supply_info(
90
+ issues: list[RequestUserSupplyInfo], title="用户补充确认信息", use_markdown_block=True
91
+ ) -> str:
92
+ result = f"### {title}\n\n"
93
+ result += "\n".join(
94
+ [f"- **Assistant**: {prompt.prompt} (例如: {prompt.example})\n- **User Reply**: " for prompt in issues]
95
+ )
96
+ if use_markdown_block:
97
+ result = f"```markdown\n{result}\n```\n"
98
+ return result
99
+
100
+
101
+ def format_received_user_supply_info(
102
+ replies: list[UserSupplyInfoReply], title="用户补充确认信息", use_markdown_block=True
103
+ ) -> str:
104
+ result = f"### {title}\n\n"
105
+ result += "\n".join([f"- **Assistant**: {reply.prompt}\n- **User Reply**: {reply.reply}" for reply in replies])
106
+ if use_markdown_block:
107
+ result = f"```markdown\n{result}\n```\n"
108
+ return result
109
+
110
+
111
+ class RequestUserSupplyAgent(BaseChatAgent):
112
+
113
+ PROMPT = MOCK_USER_REPLY_PROMPT
114
+ OUTPUT_FORMAT = AgentOutputFormat.JSON
115
+ OUTPUT_JSON_SCHEMA = ReceiveUserSupplyInfoParams
116
+ DISPLAY_REPLY = True
117
+ MOCK_USER_SUPPLY: bool = False
118
+ WHERE_USER_SUPPLY = "below" # "above" or "below"
119
+
120
+ def on_reply(self, reply: ReceiveUserSupplyInfoParams):
121
+ assert reply, "Reply is empty"
122
+ if get_env_capbilities().set_cell_content:
123
+ insert_cell_idx = -1 if self.WHERE_USER_SUPPLY == "above" else 1
124
+ action = ActionSetCellContent(
125
+ source=self.__class__.__name__,
126
+ params=SetCellContentParams(
127
+ index=insert_cell_idx,
128
+ type="markdown",
129
+ source=format_received_user_supply_info(reply.replies, use_markdown_block=False),
130
+ ),
131
+ )
132
+ get_action_dispatcher().send_action(action, need_reply=False)
133
+ else:
134
+ _M("### 用户补充确认的信息\n\n请将下面的内容保存到单独的单元格中,以便于更好的完成任务\n\n")
135
+ _M(format_received_user_supply_info(reply.replies))
136
+
137
+ def __call__(self, **kwargs) -> Tuple[bool, Any]:
138
+ request_supply_infos = (
139
+ self.task.request_above_supply_infos
140
+ if self.WHERE_USER_SUPPLY == "above"
141
+ else self.task.request_below_supply_infos
142
+ )
143
+ kwargs["request_supply_infos"] = request_supply_infos
144
+ if self.MOCK_USER_SUPPLY:
145
+ return super().__call__(**kwargs)
146
+ else:
147
+ if get_env_capbilities().user_supply_info:
148
+ _I(f"Request User Supply Info: {request_supply_infos}")
149
+ action = ActionRequestUserSupplyInfo(
150
+ source=self.__class__.__name__,
151
+ params=RequestUserSupplyInfoParams(title="用户需求补充确认", issues=request_supply_infos),
152
+ )
153
+ get_action_dispatcher().send_action(action, need_reply=True)
154
+ res = get_action_dispatcher().get_action_reply(action, wait=True)
155
+ return False, self.on_reply(res and res.params) # type: ignore
156
+ elif get_env_capbilities().set_cell_content:
157
+ _M(
158
+ f"**需要用户补充确认信息**,"
159
+ f"请将{'下面' if self.WHERE_USER_SUPPLY == 'below' else '上面'}的单元格中的内容补充完整,"
160
+ f"以便于更好的完成任务"
161
+ )
162
+ insert_cell_idx = -1 if self.WHERE_USER_SUPPLY == "above" else 1
163
+ action = ActionSetCellContent(
164
+ source=self.__class__.__name__,
165
+ params=SetCellContentParams(
166
+ index=insert_cell_idx,
167
+ type="markdown",
168
+ source=format_request_user_supply_info(request_supply_infos, use_markdown_block=False),
169
+ ),
170
+ )
171
+ get_action_dispatcher().send_action(action, need_reply=False)
172
+ else:
173
+ _M(
174
+ "### 需要补充确认的信息\n\n"
175
+ "请将下面的内容保存到单独的单元格中并将其补充完整,以便于更好的完成任务\n\n"
176
+ )
177
+ _M(format_request_user_supply_info(request_supply_infos))
178
+ return False, None
179
+
180
+
181
+ class RequestAboveUserSupplyAgent(RequestUserSupplyAgent):
182
+ WHERE_USER_SUPPLY = "above"
183
+
184
+
185
+ class RequestBelowUserSupplyAgent(RequestUserSupplyAgent):
186
+ WHERE_USER_SUPPLY = "below"
@@ -11,7 +11,7 @@ from IPython.core.getipython import get_ipython
11
11
  from IPython.display import Markdown, clear_output
12
12
  from .base import BaseAgent
13
13
  from ..utils import TeeOutputCapture
14
- from ..bot_outputs import _D, _I, _W, _E, _F, _M, _B, _C, ReplyType
14
+ from ..bot_outputs import _D, _I, _W, _E, _F, _M, _B, _C, flush_output
15
15
 
16
16
 
17
17
  class CodeExecutor(BaseAgent):
@@ -21,6 +21,8 @@ class CodeExecutor(BaseAgent):
21
21
  _D(f"执行代码: {repr(self.task.source)[:80]}")
22
22
  ipython = get_ipython()
23
23
  exec_failed = False
24
+ self.task.cell_output = ""
25
+ self.task.cell_error = ""
24
26
  with TeeOutputCapture() as captured:
25
27
  if ipython is None:
26
28
  exec_failed = True
@@ -39,7 +41,6 @@ class CodeExecutor(BaseAgent):
39
41
  clean_traceback = "\n".join(ansi_escape.sub("", line) for line in exc_info["traceback"])
40
42
  self.task.cell_error = clean_traceback
41
43
  _E(f"执行失败: {clean_traceback}")
42
- self.task.cell_output = ""
43
44
  if captured.stdout:
44
45
  self.task.cell_output += "Stdout:\n" + captured.stdout + "\n"
45
46
  if captured.stderr:
@@ -11,7 +11,7 @@ from pydantic import BaseModel, Field
11
11
  from IPython.display import Markdown
12
12
  from .base import BaseChatAgent, AgentOutputFormat, AgentModelType
13
13
  from ..bot_outputs import ReplyType, _D, _I, _W, _E, _F, _A, _O, _C, _M, _B
14
- from ..utils import RequestUserPrompt, format_user_prompts
14
+ from ..bot_actions import RequestUserSupplyInfo
15
15
 
16
16
 
17
17
  TASK_PLANNER_PROMPT = """\
@@ -131,7 +131,7 @@ class TaskPlannerOutput(BaseModel):
131
131
  ),
132
132
  examples=["请对当前任务的结果进行总结,输出以下要素:..."],
133
133
  )
134
- request_supply_infos: Optional[List[RequestUserPrompt]] = Field(
134
+ request_supply_infos: Optional[List[RequestUserSupplyInfo]] = Field(
135
135
  None, description=f'需要用户补充更详细的信息的 Prompt, 在 state="{TaskPlannerState.REQUEST_INFO}" 时必填'
136
136
  )
137
137
 
@@ -146,13 +146,18 @@ class TaskPlannerAgentV3(BaseChatAgent):
146
146
 
147
147
  def on_reply(self, reply: TaskPlannerOutput):
148
148
  """执行规划逻辑"""
149
+ self.task.agent_data.result = ""
150
+ self.task.agent_data.coding_prompt = ""
151
+ self.task.agent_data.summary_prompt = ""
152
+ self.task.agent_data.important_infos = None
153
+ self.task.agent_data.request_above_supply_infos = None
154
+ self.task.agent_data.request_below_supply_infos = None
149
155
  if reply.state == TaskPlannerState.GLOBAL_FINISHED:
150
156
  _C(Markdown("全局目标已达成,任务完成!"), reply_type=ReplyType.TASK_RESULT)
151
157
  return False, reply.state
152
158
  elif reply.state == TaskPlannerState.REQUEST_INFO:
153
159
  assert reply.request_supply_infos, "Request info prompt is empty"
154
- _O(Markdown(f"### 需要补充更详细的信息\n"))
155
- _O(Markdown(format_user_prompts(reply.request_supply_infos)))
160
+ self.task.agent_data.request_above_supply_infos = reply.request_supply_infos
156
161
  return True, reply.state
157
162
  elif reply.state == TaskPlannerState.CODING_PLANNED:
158
163
  assert reply.subtask_id, "Subtask id is empty"
@@ -165,11 +170,10 @@ class TaskPlannerAgentV3(BaseChatAgent):
165
170
  f"- Coding: {reply.subtask_coding_prompt}\n"
166
171
  f"- Summary: {reply.subtask_summary_prompt}\n"
167
172
  )
168
- self.task.set_data("task_id", reply.subtask_id)
169
- self.task.set_data("subject", reply.subtask_subject)
170
- self.task.set_data("coding_prompt", reply.subtask_coding_prompt)
171
- self.task.set_data("summary_prompt", reply.subtask_summary_prompt)
172
- self.task.set_data("result", "")
173
+ self.task.agent_data.task_id = reply.subtask_id
174
+ self.task.agent_data.subject = reply.subtask_subject
175
+ self.task.agent_data.coding_prompt = reply.subtask_coding_prompt
176
+ self.task.agent_data.summary_prompt = reply.subtask_summary_prompt
173
177
  return False, reply.state
174
178
  elif reply.state == TaskPlannerState.REASONING_PLANNED:
175
179
  assert reply.subtask_id, "Subtask id is empty"
@@ -180,10 +184,9 @@ class TaskPlannerAgentV3(BaseChatAgent):
180
184
  f"- ID: {reply.subtask_id}\n"
181
185
  f"- Reasoning: {reply.subtask_summary_prompt}\n"
182
186
  )
183
- self.task.set_data("task_id", reply.subtask_id)
184
- self.task.set_data("subject", reply.subtask_subject)
185
- self.task.set_data("summary_prompt", reply.subtask_summary_prompt)
186
- self.task.set_data("result", "")
187
+ self.task.agent_data.task_id = reply.subtask_id
188
+ self.task.agent_data.subject = reply.subtask_subject
189
+ self.task.agent_data.summary_prompt = reply.subtask_summary_prompt
187
190
  return False, reply.state
188
191
  else:
189
192
  raise ValueError(f"Unknown task planner state: {reply.state}")
@@ -56,6 +56,7 @@ class TaskReasoningAgent(BaseChatAgent):
56
56
  DISPLAY_REPLY = False
57
57
 
58
58
  def on_reply(self, reply: str):
59
- _C(Markdown("### 任务总结\n" + reply), reply_type=ReplyType.TASK_RESULT)
60
59
  assert reply, "Reply is empty"
61
- self.task.set_data("result", reply)
60
+ _M("### 任务总结\n" + reply)
61
+ self.task.agent_data.issue = ""
62
+ self.task.agent_data.result = reply
@@ -13,7 +13,8 @@ from pydantic import BaseModel, Field
13
13
  from IPython.display import Markdown
14
14
  from .base import BaseChatAgent, AgentOutputFormat
15
15
  from ..bot_outputs import ReplyType, _D, _I, _W, _E, _F, _M, _B, _C, _O, markdown_block
16
- from ..utils import RequestUserPrompt, format_user_prompts
16
+ from ..bot_actions import RequestUserSupplyInfo
17
+
17
18
 
18
19
  TASK_REASONER_PROMPT = """\
19
20
  **角色定义**:
@@ -58,6 +59,11 @@ TASK_REASONER_PROMPT = """\
58
59
  """
59
60
 
60
61
 
62
+ class TaskStructureReasonState(str, Enum):
63
+ DONE = "done"
64
+ REQUEST_INFO = "request_info"
65
+
66
+
61
67
  class TaskStructureReasonOutput(BaseModel):
62
68
 
63
69
  summary: str = Field(description=f"任务总结的详细描述", examples=["..."])
@@ -77,7 +83,7 @@ class TaskStructureReasonOutput(BaseModel):
77
83
  }
78
84
  ],
79
85
  )
80
- request_confirm_infos: Optional[List[RequestUserPrompt]] = Field(
86
+ request_confirm_infos: Optional[List[RequestUserSupplyInfo]] = Field(
81
87
  None, description="需要用户补充确认的信息,问题应尽量简单,只需要用户回答是/否或在备选项中选择即可"
82
88
  )
83
89
 
@@ -91,16 +97,20 @@ class TaskStructureReasoningAgent(BaseChatAgent):
91
97
 
92
98
  def on_reply(self, reply: TaskStructureReasonOutput):
93
99
  assert reply.summary, "Reply is empty"
94
- _C(Markdown("### 任务总结\n\n" + reply.summary), reply_type=ReplyType.TASK_RESULT)
95
- self.task.set_data("result", reply.summary)
100
+ _M("### 任务总结\n\n" + reply.summary)
101
+ self.task.agent_data.issue = ""
102
+ self.task.agent_data.result = reply.summary
103
+ self.task.agent_data.important_infos = None
104
+ self.task.agent_data.request_below_supply_infos = None
96
105
  if reply.important_infos:
97
- self.task.set_data("important_infos", reply.important_infos)
98
- _O(
99
- markdown_block(
100
- f"```json\n{json.dumps(reply.important_infos, indent=4, ensure_ascii=False)}\n```",
101
- title="重要信息",
102
- )
106
+ self.task.agent_data.important_infos = reply.important_infos
107
+ _B(
108
+ json.dumps(reply.important_infos, indent=4, ensure_ascii=False),
109
+ title="重要信息",
110
+ format="code",
111
+ code_language="json",
103
112
  )
104
113
  if reply.request_confirm_infos:
105
- _O(Markdown(f"### 需要补充确认的信息\n"))
106
- _O(Markdown(format_user_prompts(reply.request_confirm_infos, title="用户补充确认信息")))
114
+ self.task.agent_data.request_below_supply_infos = reply.request_confirm_infos
115
+ return TaskStructureReasonState.REQUEST_INFO
116
+ return TaskStructureReasonState.DONE
@@ -13,7 +13,7 @@ from pydantic import BaseModel, Field
13
13
  from IPython.display import Markdown
14
14
  from .base import BaseChatAgent, AgentOutputFormat
15
15
  from ..bot_outputs import ReplyType, _D, _I, _W, _E, _F, _M, _B, _C, _O, markdown_block
16
- from ..utils import RequestUserPrompt, format_user_prompts
16
+ from ..bot_actions import RequestUserSupplyInfo
17
17
 
18
18
  TASK_SUMMARY_PROMPT = """\
19
19
  **角色定义**:
@@ -70,12 +70,12 @@ TASK_SUMMARY_PROMPT = """\
70
70
  """
71
71
 
72
72
 
73
- class RequestInfo(BaseModel):
74
- prompt: str = Field(description="需要用户补充更详细的信息的 Prompt", examples=["请补充与...相关的详细的信息"])
75
- example: Optional[str] = Field(None, description="示例", examples=["..."])
73
+ class TaskStructureSummaryState(str, Enum):
74
+ DONE = "done"
75
+ REQUEST_INFO = "request_info"
76
76
 
77
77
 
78
- class TaskStructureSumaryOutput(BaseModel):
78
+ class TaskStructureSummaryOutput(BaseModel):
79
79
 
80
80
  summary: str = Field(description=f"任务总结的详细描述", examples=["..."])
81
81
  important_infos: Optional[Dict[str, Any]] = Field(
@@ -94,7 +94,7 @@ class TaskStructureSumaryOutput(BaseModel):
94
94
  }
95
95
  ],
96
96
  )
97
- request_confirm_infos: Optional[List[RequestUserPrompt]] = Field(
97
+ request_confirm_infos: Optional[List[RequestUserSupplyInfo]] = Field(
98
98
  None, description="需要用户补充确认的信息,问题应尽量简单,只需要用户回答是/否或在备选项中选择即可"
99
99
  )
100
100
 
@@ -103,21 +103,25 @@ class TaskStructureSummaryAgent(BaseChatAgent):
103
103
 
104
104
  PROMPT = TASK_SUMMARY_PROMPT
105
105
  OUTPUT_FORMAT = AgentOutputFormat.JSON
106
- OUTPUT_JSON_SCHEMA = TaskStructureSumaryOutput
106
+ OUTPUT_JSON_SCHEMA = TaskStructureSummaryOutput
107
107
  DISPLAY_REPLY = True
108
108
 
109
- def on_reply(self, reply: TaskStructureSumaryOutput):
109
+ def on_reply(self, reply: TaskStructureSummaryOutput):
110
110
  assert reply.summary, "Reply is empty"
111
- _C(Markdown("### 任务总结\n\n" + reply.summary), reply_type=ReplyType.TASK_RESULT)
112
- self.task.set_data("result", reply.summary)
111
+ _M("### 任务总结\n\n" + reply.summary)
112
+ self.task.agent_data.issue = ""
113
+ self.task.agent_data.result = reply.summary
114
+ self.task.agent_data.important_infos = None
115
+ self.task.agent_data.request_below_supply_infos = None
113
116
  if reply.important_infos:
114
- self.task.set_data("important_infos", reply.important_infos)
115
- _O(
116
- markdown_block(
117
- f"```json\n{json.dumps(reply.important_infos, indent=4, ensure_ascii=False)}\n```",
118
- title="重要信息",
119
- )
117
+ self.task.agent_data.important_infos = reply.important_infos
118
+ _B(
119
+ json.dumps(reply.important_infos, indent=4, ensure_ascii=False),
120
+ format="code",
121
+ code_language="json",
122
+ title="重要信息",
120
123
  )
121
124
  if reply.request_confirm_infos:
122
- _O(Markdown(f"### 需要补充确认的信息\n"))
123
- _O(Markdown(format_user_prompts(reply.request_confirm_infos, title="用户补充确认信息")))
125
+ self.task.agent_data.request_below_supply_infos = reply.request_confirm_infos
126
+ return TaskStructureSummaryState.REQUEST_INFO
127
+ return TaskStructureSummaryState.DONE
@@ -71,6 +71,7 @@ class TaskSummaryAgent(BaseChatAgent):
71
71
  DISPLAY_REPLY = False
72
72
 
73
73
  def on_reply(self, reply: str):
74
- _C(Markdown("### 任务总结\n" + reply), reply_type=ReplyType.TASK_RESULT)
75
74
  assert reply, "Reply is empty"
76
- self.task.set_data("result", reply)
75
+ _M("### 任务总结\n" + reply)
76
+ self.task.agent_data.issue = ""
77
+ self.task.agent_data.result = reply
@@ -87,6 +87,7 @@ class TaskVerifyAgent(BaseChatAgent):
87
87
 
88
88
  if reply.state == TaskVerifyState.PASSED:
89
89
  _M("### 任务验证通过!")
90
+ self.task.agent_data.issue = ""
90
91
  return False, reply.state
91
92
  else:
92
93
  _M("### 任务验证不通过!\n")
@@ -94,6 +95,6 @@ class TaskVerifyAgent(BaseChatAgent):
94
95
  if reply.issues:
95
96
  for issue in reply.issues:
96
97
  task_issue += "- {}\n".format(issue)
97
- self.task.set_data("issue", task_issue)
98
98
  _M(task_issue)
99
+ self.task.agent_data.issue = task_issue
99
100
  return True, reply.state
@@ -109,10 +109,10 @@ class TaskVerifySummaryAgent(BaseChatAgent):
109
109
  def on_reply(self, reply: TaskSummaryOutput):
110
110
 
111
111
  if reply.state == TaskSummaryState.SUCCESS:
112
- _O(Markdown("### 任务总结"))
113
112
  assert reply.summary, "Summary is empty"
114
- _C(Markdown(reply.summary), reply_type=ReplyType.TASK_RESULT)
115
- self.task.set_data("result", reply.summary)
113
+ _M("### 任务总结\n\n" + reply.summary)
114
+ self.task.agent_data.issue = ""
115
+ self.task.agent_data.result = reply.summary
116
116
  return False, reply.state
117
117
  else:
118
118
  _M("### 任务验证不通过!\n")
@@ -124,9 +124,9 @@ class TaskVerifySummaryAgent(BaseChatAgent):
124
124
  if reply.enhancement.issues:
125
125
  for issue in reply.enhancement.issues:
126
126
  task_issue += "- {}\n".format(issue)
127
- self.task.set_data("issue", task_issue)
128
- self.task.set_data("coding_prompt", reply.enhancement.code_prompt)
129
- self.task.set_data("summary_prompt", reply.enhancement.summary_prompt)
127
+ self.task.agent_data.issue = task_issue
128
+ self.task.agent_data.coding_prompt = reply.enhancement.code_prompt
129
+ self.task.agent_data.summary_prompt = reply.enhancement.summary_prompt
130
130
  _M(task_issue)
131
131
  _M("### 修改后的子任务信息\n")
132
132
  _M(f"### 当前子任务代码需求:\n\n{reply.enhancement.code_prompt}")
jupyter_agent/bot_chat.py CHANGED
@@ -63,7 +63,7 @@ class BotChat:
63
63
  self.base_url = base_url
64
64
  self.api_key = api_key
65
65
  self.model_name = model_name
66
- self.dispaly_think = chat_kwargs.get("dispaly_think", self.display_think)
66
+ self.display_think = chat_kwargs.get("display_think", self.display_think)
67
67
  self.display_message = chat_kwargs.get("display_message", self.display_message)
68
68
  self.display_response = chat_kwargs.get("display_response", self.display_response)
69
69
 
@@ -143,7 +143,7 @@ class BotChat:
143
143
  if token == "<think>":
144
144
  think_block = _read_think_block(iter_tokens)
145
145
  raw_think_block = token + think_block + "</think>"
146
- if (self.dispaly_think or display_reply) and think_block and think_block.strip():
146
+ if (self.display_think or display_reply) and think_block and think_block.strip():
147
147
  _B(think_block, title="Thought Block")
148
148
  if ret_think_block and (ret_empty_block or think_block and think_block.strip()):
149
149
  yield {"type": "think", "content": think_block, "raw": raw_think_block}
@@ -21,6 +21,7 @@ from enum import Enum
21
21
  from pydantic import BaseModel, Field
22
22
  from IPython.core.getipython import get_ipython
23
23
  from .bot_outputs import _D, _I, _W, _E, _F, _A, ReplyType
24
+ from .utils import get_env_capbilities
24
25
 
25
26
 
26
27
  class CellType(str, Enum):
@@ -175,24 +176,25 @@ class AgentData(BaseModel):
175
176
  issue: str = Field("", description="Agent验证不通过的问题")
176
177
  result: str = Field("", description="Agent执行结果")
177
178
  important_infos: Optional[dict] = Field(None, description="重要信息[JSON]")
179
+ request_above_supply_infos: Optional[list] = Field(None, description="前置用户需求补充[JSON]")
180
+ request_below_supply_infos: Optional[list] = Field(None, description="后置用户需求补充[JSON]")
178
181
 
179
182
  @classmethod
180
- def default(cls):
183
+ def default(cls) -> "AgentData":
181
184
  model_fields = getattr(cls, "model_fields", None)
182
185
  if model_fields and hasattr(model_fields, "items"):
183
- return {
186
+ default = {
184
187
  name: field.examples[0] if getattr(field, "examples", None) else getattr(field, "default", None)
185
188
  for name, field in model_fields.items()
186
189
  }
187
190
  else:
188
- return {}
191
+ default = {}
192
+ return cls(**default) # type: ignore
189
193
 
190
194
 
191
195
  class AgentCellContext(CodeCellContext):
192
196
  """任务单元格上下文类"""
193
197
 
194
- SUPPORT_SAVE_META: bool = False
195
-
196
198
  def __init__(self, idx: int, cell: dict):
197
199
  """初始化任务单元格上下文"""
198
200
  super().__init__(idx, cell)
@@ -202,8 +204,8 @@ class AgentCellContext(CodeCellContext):
202
204
  self.magic_argv = shlex.split(self.magic_line)
203
205
  self.magic_name = self.magic_argv[0]
204
206
  self.magic_argv = self.magic_argv[1:]
207
+ self.agent_data = AgentData.default()
205
208
  self._remain_args = []
206
- self._agent_data = AgentData.default()
207
209
  self._cell_code = ""
208
210
  self.parse_magic_argv()
209
211
  self.load_result_from_outputs(cell)
@@ -224,16 +226,27 @@ class AgentCellContext(CodeCellContext):
224
226
 
225
227
  @property
226
228
  def result(self):
227
- return self._agent_data["result"]
229
+ return self.agent_data.result
228
230
 
229
231
  def __getattr__(self, name):
230
- return self._agent_data[name]
232
+ return getattr(self.agent_data, name)
233
+
234
+ def has_data(self, name):
235
+ return hasattr(self.agent_data, name)
231
236
 
232
237
  def get_data(self, name):
233
- return self._agent_data[name]
238
+ return getattr(self.agent_data, name)
234
239
 
235
240
  def set_data(self, name, value):
236
- self._agent_data[name] = value
241
+ setattr(self.agent_data, name, value)
242
+
243
+ def is_json_field(self, name):
244
+
245
+ return (
246
+ AgentData.model_fields[name]
247
+ and AgentData.model_fields[name].description is not None
248
+ and "[JSON]" in AgentData.model_fields[name].description # type: ignore
249
+ )
237
250
 
238
251
  def parse_magic_argv(self):
239
252
  """解析任务单元格的magic命令参数"""
@@ -283,11 +296,11 @@ class AgentCellContext(CodeCellContext):
283
296
  try:
284
297
  cell_options = yaml.safe_load(cell_options)
285
298
  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):
299
+ if self.has_data(key):
300
+ if self.is_json_field(key) and isinstance(value, str):
288
301
  value = json.loads(value)
289
302
  _D("CELL[{}] Load task option {}: {}".format(self.cell_idx, key, value))
290
- self._agent_data[key] = value
303
+ self.set_data(key, value)
291
304
  except Exception as e:
292
305
  _W("Failed to load task options {}: {}".format(type(e), str(e)))
293
306
  _W(traceback.format_exc(limit=2))
@@ -304,15 +317,15 @@ class AgentCellContext(CodeCellContext):
304
317
  _D(f"CELL[{self.cell_idx}] Task result: {repr(output_text)[:80]}")
305
318
  task_result += "\n" + output_text
306
319
  if task_result.strip():
307
- self._agent_data["result"] = task_result
320
+ self.agent_data.result = task_result
308
321
 
309
322
  def load_data_from_metadata(self, cell):
310
323
  agent_meta_infos = cell.get("metadata", {}).get("jupyter-agent-data", {})
311
324
  _D("CELL[{}] Agent Meta Data: {}".format(self.cell_idx, repr(agent_meta_infos)[:80]))
312
325
  for k, v in agent_meta_infos.items():
313
- if k in self._agent_data:
326
+ if self.has_data(k):
314
327
  _D(f"CELL[{self.cell_idx}] Load agent meta data: {k}: {repr(v)[:80]}")
315
- self._agent_data[k] = v
328
+ self.set_data(k, v)
316
329
 
317
330
  def format_magic_line(self):
318
331
  magic_args = ["%%bot"]
@@ -365,22 +378,17 @@ class AgentCellContext(CodeCellContext):
365
378
 
366
379
  def format_cell_options(self):
367
380
  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"]
381
+ if get_env_capbilities().save_metadata:
382
+ if self.agent_data.task_id:
383
+ cell_options["task_id"] = self.agent_data.task_id
384
+ if self.agent_data.subject:
385
+ cell_options["subject"] = self.agent_data.subject
373
386
  else:
374
- for key, value in self._agent_data.items():
387
+ for key, value in self.agent_data.model_dump().items():
375
388
  if key == "result" and self.type == CellType.PLANNING:
376
389
  continue
377
390
  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
- ):
391
+ if isinstance(value, (dict, list)) and self.is_json_field(key):
384
392
  value = json.dumps(value, ensure_ascii=False, indent=4)
385
393
  cell_options[key] = value
386
394
  if cell_options:
@@ -394,7 +402,7 @@ class AgentCellContext(CodeCellContext):
394
402
  def update_cell(self):
395
403
  """生成Cell内容"""
396
404
  _I("Updating Cell ...")
397
- _A(**self._agent_data)
405
+ _A(**self.agent_data.model_dump())
398
406
  cell_source = ""
399
407
  cell_source += self.format_magic_line()
400
408
  cell_source += "\n" + self.format_cell_options()