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.
- jupyter_agent/bot_actions.py +270 -0
- jupyter_agent/bot_agents/__init__.py +0 -42
- jupyter_agent/bot_agents/base.py +89 -45
- jupyter_agent/bot_agents/master_planner.py +1 -0
- jupyter_agent/bot_agents/output_task_result.py +6 -7
- jupyter_agent/bot_agents/prepare_next_cell.py +52 -0
- jupyter_agent/bot_agents/request_user_supply.py +186 -0
- jupyter_agent/bot_agents/task_code_executor.py +3 -2
- jupyter_agent/bot_agents/task_planner_v3.py +16 -13
- jupyter_agent/bot_agents/task_reasoner.py +3 -2
- jupyter_agent/bot_agents/task_structrue_reasoner.py +22 -12
- jupyter_agent/bot_agents/task_structrue_summarier.py +22 -18
- jupyter_agent/bot_agents/task_summarier.py +3 -2
- jupyter_agent/bot_agents/task_verifier.py +2 -1
- jupyter_agent/bot_agents/task_verify_summarier.py +6 -6
- jupyter_agent/bot_chat.py +2 -2
- jupyter_agent/bot_contexts.py +37 -29
- jupyter_agent/bot_evaluation.py +262 -143
- jupyter_agent/bot_evaluators/__init__.py +0 -0
- jupyter_agent/bot_evaluators/base.py +42 -0
- jupyter_agent/bot_evaluators/dummy_flow.py +20 -0
- jupyter_agent/bot_evaluators/dummy_global.py +20 -0
- jupyter_agent/bot_evaluators/dummy_task.py +20 -0
- jupyter_agent/bot_evaluators/flow_global_planning.py +88 -0
- jupyter_agent/bot_evaluators/flow_task_executor.py +152 -0
- jupyter_agent/bot_flows/__init__.py +0 -4
- jupyter_agent/bot_flows/base.py +120 -41
- jupyter_agent/bot_flows/master_planner.py +15 -4
- jupyter_agent/bot_flows/task_executor_v3.py +57 -38
- jupyter_agent/bot_magics.py +119 -69
- jupyter_agent/bot_outputs.py +37 -43
- jupyter_agent/utils.py +20 -31
- {jupyter_agent-2025.6.104.dist-info → jupyter_agent-2025.7.100.dist-info}/METADATA +56 -4
- jupyter_agent-2025.7.100.dist-info/RECORD +41 -0
- jupyter_agent/bot_agents/task_planner_v1.py +0 -158
- jupyter_agent/bot_agents/task_planner_v2.py +0 -172
- jupyter_agent/bot_flows/task_executor_v1.py +0 -86
- jupyter_agent/bot_flows/task_executor_v2.py +0 -84
- jupyter_agent-2025.6.104.dist-info/RECORD +0 -35
- {jupyter_agent-2025.6.104.dist-info → jupyter_agent-2025.7.100.dist-info}/WHEEL +0 -0
- {jupyter_agent-2025.6.104.dist-info → jupyter_agent-2025.7.100.dist-info}/entry_points.txt +0 -0
- {jupyter_agent-2025.6.104.dist-info → jupyter_agent-2025.7.100.dist-info}/licenses/LICENSE +0 -0
- {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,
|
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 ..
|
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[
|
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
|
-
|
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.
|
169
|
-
self.task.
|
170
|
-
self.task.
|
171
|
-
self.task.
|
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.
|
184
|
-
self.task.
|
185
|
-
self.task.
|
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
|
-
|
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 ..
|
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[
|
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
|
-
|
95
|
-
self.task.
|
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.
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
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
|
-
|
106
|
-
|
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 ..
|
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
|
74
|
-
|
75
|
-
|
73
|
+
class TaskStructureSummaryState(str, Enum):
|
74
|
+
DONE = "done"
|
75
|
+
REQUEST_INFO = "request_info"
|
76
76
|
|
77
77
|
|
78
|
-
class
|
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[
|
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 =
|
106
|
+
OUTPUT_JSON_SCHEMA = TaskStructureSummaryOutput
|
107
107
|
DISPLAY_REPLY = True
|
108
108
|
|
109
|
-
def on_reply(self, reply:
|
109
|
+
def on_reply(self, reply: TaskStructureSummaryOutput):
|
110
110
|
assert reply.summary, "Reply is empty"
|
111
|
-
|
112
|
-
self.task.
|
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.
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
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
|
-
|
123
|
-
|
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
|
-
|
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
|
-
|
115
|
-
self.task.
|
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.
|
128
|
-
self.task.
|
129
|
-
self.task.
|
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.
|
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.
|
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}
|
jupyter_agent/bot_contexts.py
CHANGED
@@ -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
|
-
|
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
|
-
|
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.
|
229
|
+
return self.agent_data.result
|
228
230
|
|
229
231
|
def __getattr__(self, name):
|
230
|
-
return self.
|
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.
|
238
|
+
return getattr(self.agent_data, name)
|
234
239
|
|
235
240
|
def set_data(self, name, value):
|
236
|
-
self.
|
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
|
287
|
-
if
|
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.
|
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.
|
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
|
326
|
+
if self.has_data(k):
|
314
327
|
_D(f"CELL[{self.cell_idx}] Load agent meta data: {k}: {repr(v)[:80]}")
|
315
|
-
self.
|
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
|
369
|
-
if self.
|
370
|
-
cell_options["task_id"] = self.
|
371
|
-
if self.
|
372
|
-
cell_options["subject"] = self.
|
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.
|
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.
|
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()
|