jupyter-agent 2025.6.103__py3-none-any.whl → 2025.6.105__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 (41) 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 +85 -45
  4. jupyter_agent/bot_agents/master_planner.py +2 -0
  5. jupyter_agent/bot_agents/output_task_result.py +6 -7
  6. jupyter_agent/bot_agents/request_user_supply.py +186 -0
  7. jupyter_agent/bot_agents/task_planner_v3.py +12 -13
  8. jupyter_agent/bot_agents/task_reasoner.py +2 -2
  9. jupyter_agent/bot_agents/task_structrue_reasoner.py +19 -12
  10. jupyter_agent/bot_agents/task_structrue_summarier.py +19 -18
  11. jupyter_agent/bot_agents/task_summarier.py +2 -2
  12. jupyter_agent/bot_agents/task_verifier.py +1 -1
  13. jupyter_agent/bot_agents/task_verify_summarier.py +5 -6
  14. jupyter_agent/bot_chat.py +2 -2
  15. jupyter_agent/bot_contexts.py +28 -23
  16. jupyter_agent/bot_evaluation.py +325 -0
  17. jupyter_agent/bot_evaluators/__init__.py +0 -0
  18. jupyter_agent/bot_evaluators/base.py +42 -0
  19. jupyter_agent/bot_evaluators/dummy_flow.py +20 -0
  20. jupyter_agent/bot_evaluators/dummy_global.py +20 -0
  21. jupyter_agent/bot_evaluators/dummy_task.py +20 -0
  22. jupyter_agent/bot_evaluators/flow_global_planning.py +88 -0
  23. jupyter_agent/bot_evaluators/flow_task_executor.py +152 -0
  24. jupyter_agent/bot_flows/__init__.py +0 -4
  25. jupyter_agent/bot_flows/base.py +114 -10
  26. jupyter_agent/bot_flows/master_planner.py +7 -2
  27. jupyter_agent/bot_flows/task_executor_v3.py +45 -20
  28. jupyter_agent/bot_magics.py +108 -53
  29. jupyter_agent/bot_outputs.py +56 -3
  30. jupyter_agent/utils.py +20 -31
  31. {jupyter_agent-2025.6.103.dist-info → jupyter_agent-2025.6.105.dist-info}/METADATA +39 -8
  32. jupyter_agent-2025.6.105.dist-info/RECORD +40 -0
  33. jupyter_agent-2025.6.105.dist-info/entry_points.txt +2 -0
  34. jupyter_agent/bot_agents/task_planner_v1.py +0 -158
  35. jupyter_agent/bot_agents/task_planner_v2.py +0 -172
  36. jupyter_agent/bot_flows/task_executor_v1.py +0 -86
  37. jupyter_agent/bot_flows/task_executor_v2.py +0 -84
  38. jupyter_agent-2025.6.103.dist-info/RECORD +0 -33
  39. {jupyter_agent-2025.6.103.dist-info → jupyter_agent-2025.6.105.dist-info}/WHEEL +0 -0
  40. {jupyter_agent-2025.6.103.dist-info → jupyter_agent-2025.6.105.dist-info}/licenses/LICENSE +0 -0
  41. {jupyter_agent-2025.6.103.dist-info → jupyter_agent-2025.6.105.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
+ _M(f"**需要用户补充确认信息**,请按要求补充确认信息。")
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 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
 
@@ -151,8 +151,7 @@ class TaskPlannerAgentV3(BaseChatAgent):
151
151
  return False, reply.state
152
152
  elif reply.state == TaskPlannerState.REQUEST_INFO:
153
153
  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)))
154
+ self.task.agent_data.request_above_supply_infos = reply.request_supply_infos
156
155
  return True, reply.state
157
156
  elif reply.state == TaskPlannerState.CODING_PLANNED:
158
157
  assert reply.subtask_id, "Subtask id is empty"
@@ -165,11 +164,11 @@ class TaskPlannerAgentV3(BaseChatAgent):
165
164
  f"- Coding: {reply.subtask_coding_prompt}\n"
166
165
  f"- Summary: {reply.subtask_summary_prompt}\n"
167
166
  )
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", "")
167
+ self.task.agent_data.task_id = reply.subtask_id
168
+ self.task.agent_data.subject = reply.subtask_subject
169
+ self.task.agent_data.coding_prompt = reply.subtask_coding_prompt
170
+ self.task.agent_data.summary_prompt = reply.subtask_summary_prompt
171
+ self.task.agent_data.result = ""
173
172
  return False, reply.state
174
173
  elif reply.state == TaskPlannerState.REASONING_PLANNED:
175
174
  assert reply.subtask_id, "Subtask id is empty"
@@ -180,10 +179,10 @@ class TaskPlannerAgentV3(BaseChatAgent):
180
179
  f"- ID: {reply.subtask_id}\n"
181
180
  f"- Reasoning: {reply.subtask_summary_prompt}\n"
182
181
  )
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", "")
182
+ self.task.agent_data.task_id = reply.subtask_id
183
+ self.task.agent_data.subject = reply.subtask_subject
184
+ self.task.agent_data.summary_prompt = reply.subtask_summary_prompt
185
+ self.task.agent_data.result = ""
187
186
  return False, reply.state
188
187
  else:
189
188
  raise ValueError(f"Unknown task planner state: {reply.state}")
@@ -56,6 +56,6 @@ 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.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,17 @@ 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.result = reply.summary
96
102
  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
- )
103
+ self.task.agent_data.important_infos = reply.important_infos
104
+ _B(
105
+ json.dumps(reply.important_infos, indent=4, ensure_ascii=False),
106
+ title="重要信息",
107
+ format="code",
108
+ code_language="json",
103
109
  )
104
110
  if reply.request_confirm_infos:
105
- _O(Markdown(f"### 需要补充确认的信息\n"))
106
- _O(Markdown(format_user_prompts(reply.request_confirm_infos, title="用户补充确认信息")))
111
+ self.task.agent_data.request_below_supply_infos = reply.request_confirm_infos
112
+ return TaskStructureReasonState.REQUEST_INFO
113
+ 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,22 @@ 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.result = reply.summary
113
113
  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
- )
114
+ self.task.agent_data.important_infos = reply.important_infos
115
+ _B(
116
+ json.dumps(reply.important_infos, indent=4, ensure_ascii=False),
117
+ format="code",
118
+ code_language="json",
119
+ title="重要信息",
120
120
  )
121
121
  if reply.request_confirm_infos:
122
- _O(Markdown(f"### 需要补充确认的信息\n"))
123
- _O(Markdown(format_user_prompts(reply.request_confirm_infos, title="用户补充确认信息")))
122
+ self.task.agent_data.request_below_supply_infos = reply.request_confirm_infos
123
+ return TaskStructureSummaryState.REQUEST_INFO
124
+ return TaskStructureSummaryState.DONE
@@ -71,6 +71,6 @@ 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.result = reply
@@ -94,6 +94,6 @@ class TaskVerifyAgent(BaseChatAgent):
94
94
  if reply.issues:
95
95
  for issue in reply.issues:
96
96
  task_issue += "- {}\n".format(issue)
97
- self.task.set_data("issue", task_issue)
97
+ self.task.agent_data.issue = task_issue
98
98
  _M(task_issue)
99
99
  return True, reply.state
@@ -109,10 +109,9 @@ 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.result = reply.summary
116
115
  return False, reply.state
117
116
  else:
118
117
  _M("### 任务验证不通过!\n")
@@ -124,9 +123,9 @@ class TaskVerifySummaryAgent(BaseChatAgent):
124
123
  if reply.enhancement.issues:
125
124
  for issue in reply.enhancement.issues:
126
125
  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)
126
+ self.task.agent_data.issue = task_issue
127
+ self.task.agent_data.coding_prompt = reply.enhancement.code_prompt
128
+ self.task.agent_data.summary_prompt = reply.enhancement.summary_prompt
130
129
  _M(task_issue)
131
130
  _M("### 修改后的子任务信息\n")
132
131
  _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,19 @@ 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)
237
242
 
238
243
  def parse_magic_argv(self):
239
244
  """解析任务单元格的magic命令参数"""
@@ -283,11 +288,11 @@ class AgentCellContext(CodeCellContext):
283
288
  try:
284
289
  cell_options = yaml.safe_load(cell_options)
285
290
  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):
291
+ if self.has_data(key):
292
+ if isinstance(self.get_data(key), (dict, list)) and isinstance(value, str):
288
293
  value = json.loads(value)
289
294
  _D("CELL[{}] Load task option {}: {}".format(self.cell_idx, key, value))
290
- self._agent_data[key] = value
295
+ self.set_data(key, value)
291
296
  except Exception as e:
292
297
  _W("Failed to load task options {}: {}".format(type(e), str(e)))
293
298
  _W(traceback.format_exc(limit=2))
@@ -304,15 +309,15 @@ class AgentCellContext(CodeCellContext):
304
309
  _D(f"CELL[{self.cell_idx}] Task result: {repr(output_text)[:80]}")
305
310
  task_result += "\n" + output_text
306
311
  if task_result.strip():
307
- self._agent_data["result"] = task_result
312
+ self.agent_data.result = task_result
308
313
 
309
314
  def load_data_from_metadata(self, cell):
310
315
  agent_meta_infos = cell.get("metadata", {}).get("jupyter-agent-data", {})
311
316
  _D("CELL[{}] Agent Meta Data: {}".format(self.cell_idx, repr(agent_meta_infos)[:80]))
312
317
  for k, v in agent_meta_infos.items():
313
- if k in self._agent_data:
318
+ if self.has_data(k):
314
319
  _D(f"CELL[{self.cell_idx}] Load agent meta data: {k}: {repr(v)[:80]}")
315
- self._agent_data[k] = v
320
+ self.set_data(k, v)
316
321
 
317
322
  def format_magic_line(self):
318
323
  magic_args = ["%%bot"]
@@ -365,13 +370,13 @@ class AgentCellContext(CodeCellContext):
365
370
 
366
371
  def format_cell_options(self):
367
372
  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
+ if get_env_capbilities().save_metadata:
374
+ if self.agent_data.task_id:
375
+ cell_options["task_id"] = self.agent_data.task_id
376
+ if self.agent_data.subject:
377
+ cell_options["subject"] = self.agent_data.subject
373
378
  else:
374
- for key, value in self._agent_data.items():
379
+ for key, value in self.agent_data.model_dump().items():
375
380
  if key == "result" and self.type == CellType.PLANNING:
376
381
  continue
377
382
  if value:
@@ -394,7 +399,7 @@ class AgentCellContext(CodeCellContext):
394
399
  def update_cell(self):
395
400
  """生成Cell内容"""
396
401
  _I("Updating Cell ...")
397
- _A(**self._agent_data)
402
+ _A(**self.agent_data.model_dump())
398
403
  cell_source = ""
399
404
  cell_source += self.format_magic_line()
400
405
  cell_source += "\n" + self.format_cell_options()