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,209 @@
|
|
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 traceback
|
9
|
+
|
10
|
+
from pydantic import BaseModel
|
11
|
+
from enum import Enum
|
12
|
+
from typing import List, Dict, Optional, Type
|
13
|
+
from IPython.display import Markdown
|
14
|
+
from ..bot_agents.base import BaseAgent
|
15
|
+
from ..bot_outputs import _D, _I, _W, _E, _F, _M, _B, set_stage, flush_output
|
16
|
+
|
17
|
+
TASK_AGENT_STATE_ERROR = "_AGENT_STATE_ERROR_32534526_"
|
18
|
+
TASK_STAGE_START = "start"
|
19
|
+
TASK_STAGE_COMPLETED = "completed"
|
20
|
+
|
21
|
+
|
22
|
+
class TaskAction(str, Enum):
|
23
|
+
DEFAULT = "*"
|
24
|
+
CONTINUE = "continue"
|
25
|
+
RETRY = "retry"
|
26
|
+
SKIP = "skip"
|
27
|
+
STOP = "stop"
|
28
|
+
|
29
|
+
|
30
|
+
class StageNext[ST](BaseModel):
|
31
|
+
action: Optional[TaskAction] = None
|
32
|
+
stage: ST | str
|
33
|
+
message: str = ""
|
34
|
+
|
35
|
+
|
36
|
+
class StageTransition[ST, AS](BaseModel):
|
37
|
+
stage: ST | str
|
38
|
+
agent: Type[BaseAgent] | str
|
39
|
+
states: Dict[AS | str, StageNext[ST] | List[StageNext[ST]] | Dict[TaskAction, StageNext[ST]] | ST | str] = {}
|
40
|
+
next_stage: Optional[StageNext[ST] | List[StageNext[ST]] | Dict[TaskAction, StageNext[ST]] | ST | str] = None
|
41
|
+
|
42
|
+
|
43
|
+
class BaseTaskFlow:
|
44
|
+
"""
|
45
|
+
基础任务流程
|
46
|
+
"""
|
47
|
+
|
48
|
+
STAGE_TRANSITIONS: List[StageTransition] = []
|
49
|
+
START_STAGE = TASK_STAGE_START
|
50
|
+
STOP_STAGES = [TASK_STAGE_COMPLETED]
|
51
|
+
|
52
|
+
def __init__(self, notebook_context, agent_factory):
|
53
|
+
self.notebook_context = notebook_context
|
54
|
+
self.agent_factory = agent_factory
|
55
|
+
self.stage_transitions = {}
|
56
|
+
self.prepare_stage_transitions()
|
57
|
+
|
58
|
+
@property
|
59
|
+
def task(self):
|
60
|
+
return self.notebook_context.cur_task
|
61
|
+
|
62
|
+
@property
|
63
|
+
def cells(self):
|
64
|
+
return self.notebook_context.cells
|
65
|
+
|
66
|
+
def prepare_stage_transitions(self):
|
67
|
+
for st in self.STAGE_TRANSITIONS:
|
68
|
+
assert not (st.next_stage and st.states), "next_stage and states are mutually exclusive"
|
69
|
+
self.stage_transitions[st.stage] = st
|
70
|
+
if st.next_stage:
|
71
|
+
st.states[TaskAction.DEFAULT] = st.next_stage
|
72
|
+
st.next_stage = None
|
73
|
+
for state, ns in st.states.items():
|
74
|
+
st.states[state] = {}
|
75
|
+
if isinstance(ns, str):
|
76
|
+
st.states[state] = {TaskAction.DEFAULT: StageNext(stage=ns)}
|
77
|
+
elif isinstance(ns, StageNext):
|
78
|
+
action = ns.action or TaskAction.DEFAULT
|
79
|
+
st.states[state] = {action: ns}
|
80
|
+
elif isinstance(ns, list):
|
81
|
+
state_dict = {}
|
82
|
+
for n in ns:
|
83
|
+
action = n.action or TaskAction.DEFAULT
|
84
|
+
state_dict[action] = n
|
85
|
+
st.states[state] = state_dict
|
86
|
+
elif isinstance(ns, dict):
|
87
|
+
st.states[state] = ns
|
88
|
+
else:
|
89
|
+
raise ValueError(f"Unknown next stage: {ns}")
|
90
|
+
state_ns: Dict = st.states[state] # type: ignore
|
91
|
+
state_ns[TaskAction.CONTINUE] = state_ns.get(TaskAction.CONTINUE) or state_ns.get(TaskAction.DEFAULT)
|
92
|
+
state_ns[TaskAction.RETRY] = state_ns.get(TaskAction.RETRY) or StageNext(stage=st.stage)
|
93
|
+
state_ns[TaskAction.STOP] = state_ns.get(TaskAction.STOP) or StageNext(stage=st.stage)
|
94
|
+
state_ns[TaskAction.SKIP] = state_ns.get(TaskAction.SKIP) or state_ns.get(TaskAction.CONTINUE)
|
95
|
+
if TASK_AGENT_STATE_ERROR not in st.states:
|
96
|
+
st.states[TASK_AGENT_STATE_ERROR] = {"*": StageNext(stage=st.stage)}
|
97
|
+
|
98
|
+
def get_stage_agent(self, stage):
|
99
|
+
for t in self.STAGE_TRANSITIONS:
|
100
|
+
if t.stage == stage:
|
101
|
+
return self.agent_factory(t.agent)
|
102
|
+
raise ValueError(f"No agent for stage `{stage}`")
|
103
|
+
|
104
|
+
def _get_next_stage_trans(self, stage, state, action=TaskAction.CONTINUE):
|
105
|
+
|
106
|
+
st = self.stage_transitions.get(stage)
|
107
|
+
if st:
|
108
|
+
state_ns = st.states.get(state) or st.states.get("*")
|
109
|
+
assert state_ns, f"No next stage for stage `{stage}` and state `{state}`"
|
110
|
+
act_ns = state_ns.get(action) or state_ns.get("*")
|
111
|
+
assert act_ns, f"No next stage for stage `{stage}`, state `{state}`, action `{action}`"
|
112
|
+
return act_ns
|
113
|
+
else:
|
114
|
+
raise ValueError(f"No next stage for stage `{stage}`")
|
115
|
+
|
116
|
+
def get_prompt_message(self, stage, state, failed):
|
117
|
+
|
118
|
+
ns = self._get_next_stage_trans(stage, state)
|
119
|
+
if failed:
|
120
|
+
msg = ns.message or f"Staget `{stage}` FAILED!"
|
121
|
+
return (
|
122
|
+
f"{msg}\n Continue from stage `{ns.stage}`? \n"
|
123
|
+
f"(C)ontinue, (R)etry, s(K)ip, (S)top, default `continue`"
|
124
|
+
)
|
125
|
+
else:
|
126
|
+
return (
|
127
|
+
f"{ns.message}\n Continue to stage `{ns.stage}`? \n"
|
128
|
+
f"(C)ontinue, (R)etry, s(K)ip, (S)top, default `continue`"
|
129
|
+
)
|
130
|
+
|
131
|
+
def match_action(self, input):
|
132
|
+
input = input.lower().strip()
|
133
|
+
if input == "" or input == "c" or (len(input) > 1 and TaskAction.CONTINUE.value.startswith(input)):
|
134
|
+
return TaskAction.CONTINUE
|
135
|
+
elif input == "r" or (len(input) > 1 and TaskAction.RETRY.value.startswith(input)):
|
136
|
+
return TaskAction.RETRY
|
137
|
+
elif input == "k" or (len(input) > 1 and TaskAction.SKIP.value.startswith(input)):
|
138
|
+
return TaskAction.SKIP
|
139
|
+
elif input == "s" or (len(input) > 1 and TaskAction.STOP.value.startswith(input)):
|
140
|
+
return TaskAction.STOP
|
141
|
+
else:
|
142
|
+
raise ValueError(f"Unknown action: {input}")
|
143
|
+
|
144
|
+
def get_next_stage(self, stage, state, action):
|
145
|
+
|
146
|
+
ns = self._get_next_stage_trans(stage, state, action)
|
147
|
+
return ns.stage
|
148
|
+
|
149
|
+
def __call__(self, stage, max_tries=3, stage_continue=True, stage_confirm=True):
|
150
|
+
|
151
|
+
n_tries = 0
|
152
|
+
stage = stage or self.START_STAGE
|
153
|
+
while n_tries <= max_tries:
|
154
|
+
try:
|
155
|
+
stage_name = stage.value if isinstance(stage, Enum) else stage
|
156
|
+
stage_name = stage_name.replace(".", "-").capitalize()
|
157
|
+
set_stage(stage_name)
|
158
|
+
agent = self.get_stage_agent(stage)
|
159
|
+
_M(f"**Executing** stage `{stage}` with agent `{type(agent).__name__}` ...")
|
160
|
+
failed, state = agent()
|
161
|
+
except Exception as e:
|
162
|
+
_M(f"**Error** during task execution stage `{stage}`: `{type(e)}`: `{e}`")
|
163
|
+
_M(f"```python\n{traceback.format_exc()}\n```")
|
164
|
+
state = TASK_AGENT_STATE_ERROR
|
165
|
+
failed = True
|
166
|
+
|
167
|
+
if state != TASK_AGENT_STATE_ERROR:
|
168
|
+
# Agent did not fail, check if we have reached the final stage
|
169
|
+
next_stage = self.get_next_stage(stage, state, TaskAction.CONTINUE)
|
170
|
+
self.task.agent_stage = next_stage
|
171
|
+
self.task.update_cell()
|
172
|
+
if next_stage in self.STOP_STAGES:
|
173
|
+
_M(f"Task execution **Stopped** at stage `{next_stage}`")
|
174
|
+
break
|
175
|
+
|
176
|
+
if failed:
|
177
|
+
# Agent failed
|
178
|
+
n_tries += 1
|
179
|
+
|
180
|
+
if failed or stage_confirm:
|
181
|
+
# Agent failed or we need to confirm
|
182
|
+
message = self.get_prompt_message(stage, state, failed)
|
183
|
+
_M("**Confirm**: " + message)
|
184
|
+
flush_output()
|
185
|
+
action = self.match_action(input(message))
|
186
|
+
next_stage = self.get_next_stage(stage, state, action)
|
187
|
+
self.task.agent_stage = next_stage
|
188
|
+
self.task.update_cell()
|
189
|
+
if action == TaskAction.STOP:
|
190
|
+
_M(f"Task execution **Stopped**, and set next stage to `{next_stage}`")
|
191
|
+
break
|
192
|
+
elif n_tries > max_tries:
|
193
|
+
_M(f"**Max tries reached** during task execution stage `{stage}`, **Stop!**")
|
194
|
+
break
|
195
|
+
else:
|
196
|
+
_M(f"**Action**: `{action}` transits stage to `{next_stage}`")
|
197
|
+
stage = next_stage
|
198
|
+
else:
|
199
|
+
# Agent succeeded, transit to the next stage without confirmation
|
200
|
+
next_stage = self.get_next_stage(stage, state, TaskAction.CONTINUE)
|
201
|
+
self.task.agent_stage = next_stage
|
202
|
+
self.task.update_cell()
|
203
|
+
_M(f"**Transits** stage to `{next_stage}`")
|
204
|
+
stage = next_stage
|
205
|
+
|
206
|
+
if not stage_continue:
|
207
|
+
break
|
208
|
+
flush_output()
|
209
|
+
return stage
|
@@ -0,0 +1,16 @@
|
|
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, StageTransition, TASK_STAGE_START, TASK_STAGE_COMPLETED
|
9
|
+
|
10
|
+
|
11
|
+
class MasterPlannerFlow(BaseTaskFlow):
|
12
|
+
|
13
|
+
STAGE_TRANSITIONS = [
|
14
|
+
StageTransition(stage=TASK_STAGE_START, agent="MasterPlannerAgent", next_stage=TASK_STAGE_START)
|
15
|
+
]
|
16
|
+
STOP_STAGES = [TASK_STAGE_START]
|
@@ -0,0 +1,86 @@
|
|
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 enum import Enum
|
9
|
+
from .base import BaseTaskFlow, StageTransition, StageNext, TaskAction
|
10
|
+
from ..bot_agents import (
|
11
|
+
TaskPlannerAgentV1,
|
12
|
+
TaskCodingAgent,
|
13
|
+
CodeDebugerAgent,
|
14
|
+
CodeExecutor,
|
15
|
+
TaskVerifyAgent,
|
16
|
+
TaskSummaryAgent,
|
17
|
+
TaskVerifyState,
|
18
|
+
)
|
19
|
+
from ..bot_agents.task_planner_v1 import TaskPlannerState
|
20
|
+
|
21
|
+
|
22
|
+
class TaskStage(str, Enum):
|
23
|
+
PLANNING = "planning"
|
24
|
+
PLANNING_PAUSED = "planning_paused"
|
25
|
+
CODING = "coding"
|
26
|
+
EXECUTING = "executing"
|
27
|
+
DEBUGGING = "debugging"
|
28
|
+
VERIFYING = "verifying"
|
29
|
+
SUMMARY = "summary"
|
30
|
+
COMPLETED = "completed"
|
31
|
+
|
32
|
+
|
33
|
+
class TaskExecutorFlowV1(BaseTaskFlow):
|
34
|
+
|
35
|
+
START_STAGE = TaskStage.PLANNING
|
36
|
+
STOP_STAGES = [TaskStage.COMPLETED, TaskStage.PLANNING_PAUSED]
|
37
|
+
STAGE_TRANSITIONS = [
|
38
|
+
StageTransition[TaskStage, TaskPlannerState](
|
39
|
+
stage=TaskStage.PLANNING,
|
40
|
+
agent=TaskPlannerAgentV1,
|
41
|
+
states={
|
42
|
+
TaskPlannerState.PLANNED: TaskStage.CODING,
|
43
|
+
TaskPlannerState.REQUEST_INFO: TaskStage.PLANNING_PAUSED,
|
44
|
+
TaskPlannerState.GLOBAL_FINISHED: TaskStage.COMPLETED,
|
45
|
+
},
|
46
|
+
),
|
47
|
+
StageTransition[TaskStage, TaskPlannerState](
|
48
|
+
stage=TaskStage.PLANNING_PAUSED,
|
49
|
+
agent=TaskPlannerAgentV1,
|
50
|
+
states={
|
51
|
+
TaskPlannerState.PLANNED: TaskStage.CODING,
|
52
|
+
TaskPlannerState.REQUEST_INFO: TaskStage.PLANNING_PAUSED,
|
53
|
+
TaskPlannerState.GLOBAL_FINISHED: TaskStage.COMPLETED,
|
54
|
+
},
|
55
|
+
),
|
56
|
+
StageTransition[TaskStage, None](
|
57
|
+
stage=TaskStage.CODING, agent=TaskCodingAgent, next_stage=TaskStage.EXECUTING
|
58
|
+
),
|
59
|
+
StageTransition[TaskStage, bool](
|
60
|
+
stage=TaskStage.EXECUTING,
|
61
|
+
agent=CodeExecutor,
|
62
|
+
states={True: TaskStage.VERIFYING, False: TaskStage.DEBUGGING},
|
63
|
+
),
|
64
|
+
StageTransition[TaskStage, None](
|
65
|
+
stage=TaskStage.DEBUGGING, agent=CodeDebugerAgent, next_stage=TaskStage.EXECUTING
|
66
|
+
),
|
67
|
+
StageTransition[TaskStage, TaskVerifyState](
|
68
|
+
stage=TaskStage.VERIFYING,
|
69
|
+
agent=TaskVerifyAgent,
|
70
|
+
states={
|
71
|
+
TaskVerifyState.PASSED: TaskStage.SUMMARY,
|
72
|
+
TaskVerifyState.FAILED: [
|
73
|
+
StageNext[TaskStage](action=TaskAction.CONTINUE, stage=TaskStage.PLANNING),
|
74
|
+
StageNext[TaskStage](action=TaskAction.SKIP, stage=TaskStage.SUMMARY),
|
75
|
+
],
|
76
|
+
},
|
77
|
+
),
|
78
|
+
StageTransition[TaskStage, None](
|
79
|
+
stage=TaskStage.SUMMARY, agent=TaskSummaryAgent, next_stage=TaskStage.COMPLETED
|
80
|
+
),
|
81
|
+
StageTransition[TaskStage, bool](
|
82
|
+
stage=TaskStage.COMPLETED,
|
83
|
+
agent=CodeExecutor,
|
84
|
+
states={True: TaskStage.COMPLETED, False: TaskStage.DEBUGGING},
|
85
|
+
),
|
86
|
+
]
|
@@ -0,0 +1,84 @@
|
|
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 enum import Enum
|
9
|
+
from .base import BaseTaskFlow, StageTransition, StageNext, TaskAction
|
10
|
+
from ..bot_agents import (
|
11
|
+
TaskPlannerAgentV2,
|
12
|
+
TaskCodingAgent,
|
13
|
+
CodeDebugerAgent,
|
14
|
+
CodeExecutor,
|
15
|
+
TaskSummaryAgent,
|
16
|
+
TaskReasoningAgent,
|
17
|
+
)
|
18
|
+
from ..bot_agents.task_planner_v2 import TaskPlannerState
|
19
|
+
|
20
|
+
|
21
|
+
class TaskStage(str, Enum):
|
22
|
+
PLANNING = "planning"
|
23
|
+
PLANNING_PAUSED = "planning_paused"
|
24
|
+
CODING = "coding"
|
25
|
+
EXECUTING = "executing"
|
26
|
+
DEBUGGING = "debugging"
|
27
|
+
REASONING = "reasoning"
|
28
|
+
SUMMARY = "summary"
|
29
|
+
COMPLETED = "completed"
|
30
|
+
|
31
|
+
|
32
|
+
class TaskExecutorFlowV2(BaseTaskFlow):
|
33
|
+
|
34
|
+
START_STAGE = TaskStage.PLANNING
|
35
|
+
STOP_STAGES = [TaskStage.COMPLETED, TaskStage.PLANNING_PAUSED]
|
36
|
+
STAGE_TRANSITIONS = [
|
37
|
+
StageTransition[TaskStage, TaskPlannerState](
|
38
|
+
stage=TaskStage.PLANNING,
|
39
|
+
agent=TaskPlannerAgentV2,
|
40
|
+
states={
|
41
|
+
TaskPlannerState.CODING_PLANNED: TaskStage.CODING,
|
42
|
+
TaskPlannerState.REASONING_PLANNED: TaskStage.REASONING,
|
43
|
+
TaskPlannerState.REQUEST_INFO: TaskStage.PLANNING_PAUSED,
|
44
|
+
TaskPlannerState.GLOBAL_FINISHED: TaskStage.COMPLETED,
|
45
|
+
},
|
46
|
+
),
|
47
|
+
StageTransition[TaskStage, TaskPlannerState](
|
48
|
+
stage=TaskStage.PLANNING_PAUSED,
|
49
|
+
agent=TaskPlannerAgentV2,
|
50
|
+
states={
|
51
|
+
TaskPlannerState.CODING_PLANNED: TaskStage.CODING,
|
52
|
+
TaskPlannerState.REASONING_PLANNED: TaskStage.REASONING,
|
53
|
+
TaskPlannerState.REQUEST_INFO: TaskStage.PLANNING_PAUSED,
|
54
|
+
TaskPlannerState.GLOBAL_FINISHED: TaskStage.COMPLETED,
|
55
|
+
},
|
56
|
+
),
|
57
|
+
StageTransition[TaskStage, None](
|
58
|
+
stage=TaskStage.CODING, agent=TaskCodingAgent, next_stage=TaskStage.EXECUTING
|
59
|
+
),
|
60
|
+
StageTransition[TaskStage, bool](
|
61
|
+
stage=TaskStage.EXECUTING,
|
62
|
+
agent=CodeExecutor,
|
63
|
+
states={True: TaskStage.SUMMARY, False: TaskStage.DEBUGGING},
|
64
|
+
),
|
65
|
+
StageTransition[TaskStage, None](
|
66
|
+
stage=TaskStage.DEBUGGING, agent=CodeDebugerAgent, next_stage=TaskStage.EXECUTING
|
67
|
+
),
|
68
|
+
StageTransition[TaskStage, None](
|
69
|
+
stage=TaskStage.REASONING, agent=TaskReasoningAgent, next_stage=TaskStage.COMPLETED
|
70
|
+
),
|
71
|
+
StageTransition[TaskStage, None](
|
72
|
+
stage=TaskStage.SUMMARY,
|
73
|
+
agent=TaskSummaryAgent,
|
74
|
+
next_stage={
|
75
|
+
TaskAction.DEFAULT: StageNext(stage=TaskStage.COMPLETED),
|
76
|
+
TaskAction.STOP: StageNext(stage=TaskStage.EXECUTING),
|
77
|
+
},
|
78
|
+
),
|
79
|
+
StageTransition[TaskStage, bool](
|
80
|
+
stage=TaskStage.COMPLETED,
|
81
|
+
agent=CodeExecutor,
|
82
|
+
states={True: TaskStage.COMPLETED, False: TaskStage.DEBUGGING},
|
83
|
+
),
|
84
|
+
]
|
@@ -0,0 +1,89 @@
|
|
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 enum import Enum
|
9
|
+
from .base import BaseTaskFlow, StageTransition, StageNext, TaskAction
|
10
|
+
from ..bot_agents import (
|
11
|
+
TaskPlannerAgentV3,
|
12
|
+
TaskCodingAgent,
|
13
|
+
CodeDebugerAgent,
|
14
|
+
CodeExecutor,
|
15
|
+
TaskStructureSummaryAgent,
|
16
|
+
TaskStructureReasoningAgent,
|
17
|
+
OutputTaskResult,
|
18
|
+
)
|
19
|
+
from ..bot_agents.task_planner_v3 import TaskPlannerState
|
20
|
+
|
21
|
+
|
22
|
+
class TaskStage(str, Enum):
|
23
|
+
PLANNING = "planning"
|
24
|
+
PLANNING_PAUSED = "planning_paused"
|
25
|
+
CODING = "coding"
|
26
|
+
EXECUTING = "executing"
|
27
|
+
DEBUGGING = "debugging"
|
28
|
+
REASONING = "reasoning"
|
29
|
+
SUMMARY = "summary"
|
30
|
+
COMPLETED = "completed"
|
31
|
+
OUTPUT_RESULT = "output_result"
|
32
|
+
|
33
|
+
|
34
|
+
class TaskExecutorFlowV3(BaseTaskFlow):
|
35
|
+
|
36
|
+
START_STAGE = TaskStage.PLANNING
|
37
|
+
STOP_STAGES = [TaskStage.COMPLETED, TaskStage.PLANNING_PAUSED]
|
38
|
+
STAGE_TRANSITIONS = [
|
39
|
+
StageTransition[TaskStage, TaskPlannerState](
|
40
|
+
stage=TaskStage.PLANNING,
|
41
|
+
agent=TaskPlannerAgentV3,
|
42
|
+
states={
|
43
|
+
TaskPlannerState.CODING_PLANNED: TaskStage.CODING,
|
44
|
+
TaskPlannerState.REASONING_PLANNED: TaskStage.REASONING,
|
45
|
+
TaskPlannerState.REQUEST_INFO: TaskStage.PLANNING_PAUSED,
|
46
|
+
TaskPlannerState.GLOBAL_FINISHED: TaskStage.COMPLETED,
|
47
|
+
},
|
48
|
+
),
|
49
|
+
StageTransition[TaskStage, TaskPlannerState](
|
50
|
+
stage=TaskStage.PLANNING_PAUSED,
|
51
|
+
agent=TaskPlannerAgentV3,
|
52
|
+
states={
|
53
|
+
TaskPlannerState.CODING_PLANNED: TaskStage.CODING,
|
54
|
+
TaskPlannerState.REASONING_PLANNED: TaskStage.REASONING,
|
55
|
+
TaskPlannerState.REQUEST_INFO: TaskStage.PLANNING_PAUSED,
|
56
|
+
TaskPlannerState.GLOBAL_FINISHED: TaskStage.COMPLETED,
|
57
|
+
},
|
58
|
+
),
|
59
|
+
StageTransition[TaskStage, None](
|
60
|
+
stage=TaskStage.CODING, agent=TaskCodingAgent, next_stage=TaskStage.EXECUTING
|
61
|
+
),
|
62
|
+
StageTransition[TaskStage, bool](
|
63
|
+
stage=TaskStage.EXECUTING,
|
64
|
+
agent=CodeExecutor,
|
65
|
+
states={True: TaskStage.SUMMARY, False: TaskStage.DEBUGGING},
|
66
|
+
),
|
67
|
+
StageTransition[TaskStage, None](
|
68
|
+
stage=TaskStage.DEBUGGING, agent=CodeDebugerAgent, next_stage=TaskStage.EXECUTING
|
69
|
+
),
|
70
|
+
StageTransition[TaskStage, None](
|
71
|
+
stage=TaskStage.REASONING, agent=TaskStructureReasoningAgent, next_stage=TaskStage.COMPLETED
|
72
|
+
),
|
73
|
+
StageTransition[TaskStage, None](
|
74
|
+
stage=TaskStage.SUMMARY,
|
75
|
+
agent=TaskStructureSummaryAgent,
|
76
|
+
next_stage={
|
77
|
+
TaskAction.DEFAULT: StageNext(stage=TaskStage.COMPLETED),
|
78
|
+
TaskAction.STOP: StageNext(stage=TaskStage.EXECUTING),
|
79
|
+
},
|
80
|
+
),
|
81
|
+
StageTransition[TaskStage, bool](
|
82
|
+
stage=TaskStage.COMPLETED,
|
83
|
+
agent=CodeExecutor,
|
84
|
+
states={True: TaskStage.OUTPUT_RESULT, False: TaskStage.DEBUGGING},
|
85
|
+
),
|
86
|
+
StageTransition[TaskStage, None](
|
87
|
+
stage=TaskStage.OUTPUT_RESULT, agent=OutputTaskResult, next_stage=TaskStage.COMPLETED
|
88
|
+
),
|
89
|
+
]
|
@@ -0,0 +1,127 @@
|
|
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 shlex
|
10
|
+
import argparse
|
11
|
+
import ipynbname
|
12
|
+
import traceback
|
13
|
+
|
14
|
+
from IPython.display import Markdown
|
15
|
+
from IPython.core.magic import Magics, magics_class, cell_magic
|
16
|
+
from traitlets import Unicode, Int, Bool
|
17
|
+
from traitlets.config.configurable import Configurable
|
18
|
+
from .bot_contexts import NotebookContext, AgentCellContext
|
19
|
+
from .bot_agents import AgentFactory
|
20
|
+
from .bot_agents.base import AgentModelType
|
21
|
+
from .bot_flows import MasterPlannerFlow, TaskExecutorFlowV1, TaskExecutorFlowV2, TaskExecutorFlowV3
|
22
|
+
from .bot_outputs import _D, _I, _W, _E, _F, _M, _B, _O, reset_output, set_logging_level
|
23
|
+
|
24
|
+
|
25
|
+
@magics_class
|
26
|
+
class BotMagics(Magics, Configurable):
|
27
|
+
"""Jupyter cell magic for OpenAI bot integration"""
|
28
|
+
|
29
|
+
# 配置项
|
30
|
+
logging_level = Unicode("INFO", help="Debug level for logging").tag(config=True)
|
31
|
+
default_api_url = Unicode(None, allow_none=True, help="Default API URL").tag(config=True)
|
32
|
+
default_api_key = Unicode("API_KEY", help="Default API Key").tag(config=True)
|
33
|
+
default_model_name = Unicode("", help="Default Model Name").tag(config=True)
|
34
|
+
planner_api_url = Unicode(None, allow_none=True, help="Planner API URL").tag(config=True)
|
35
|
+
planner_api_key = Unicode("API_KEY", help="Planner API Key").tag(config=True)
|
36
|
+
planner_model_name = Unicode("", help="Planner Model Name").tag(config=True)
|
37
|
+
coding_api_url = Unicode(None, allow_none=True, help="Coding API URL").tag(config=True)
|
38
|
+
coding_api_key = Unicode("API_KEY", help="Coding API Key").tag(config=True)
|
39
|
+
coding_model_name = Unicode("", help="Coding Model Name").tag(config=True)
|
40
|
+
reasoning_api_url = Unicode(None, allow_none=True, help="Reasoning API URL").tag(config=True)
|
41
|
+
reasoning_api_key = Unicode("API_KEY", help="Reasoning API Key").tag(config=True)
|
42
|
+
reasoning_model_name = Unicode("", help="Reasoning Model Name").tag(config=True)
|
43
|
+
display_message = Bool(False, help="Display chat message").tag(config=True)
|
44
|
+
display_think = Bool(True, help="Display chatthink response").tag(config=True)
|
45
|
+
display_response = Bool(False, help="Display chat full response").tag(config=True)
|
46
|
+
notebook_path = Unicode(None, allow_none=True, help="Path to Notebook file").tag(config=True)
|
47
|
+
default_task_flow = Unicode("v3", allow_none=True, help="Default task flow").tag(config=True)
|
48
|
+
support_save_meta = Bool(False, help="Support save metadata to cell").tag(config=True)
|
49
|
+
|
50
|
+
def parse_args(self, line):
|
51
|
+
"""解析命令行参数"""
|
52
|
+
parser = argparse.ArgumentParser()
|
53
|
+
parser.add_argument("-l", "--logging-level", type=str, default=self.logging_level, help="level for logging")
|
54
|
+
parser.add_argument("-P", "--planning", action="store_true", default=False, help="Run in planning mode")
|
55
|
+
parser.add_argument("-s", "--stage", type=str, default=None, help="Task stage")
|
56
|
+
parser.add_argument("-f", "--flow", type=str, default=self.default_task_flow, help="Flow name")
|
57
|
+
parser.add_argument("-m", "--max-tries", type=int, default=3, help="Max tries")
|
58
|
+
parser.add_argument("-S", "--step-mode", action="store_true", default=False, help="Run in single step mode")
|
59
|
+
parser.add_argument("-Y", "--auto-confirm", action="store_true", default=False, help="Run without confirm")
|
60
|
+
options, _ = parser.parse_known_args(shlex.split(line.strip()))
|
61
|
+
|
62
|
+
return options
|
63
|
+
|
64
|
+
@cell_magic
|
65
|
+
def bot(self, line, cell):
|
66
|
+
"""Jupyter cell magic: %%bot"""
|
67
|
+
try:
|
68
|
+
AgentCellContext.SUPPORT_SAVE_META = self.support_save_meta
|
69
|
+
reset_output(stage="Logging", logging_level=self.logging_level)
|
70
|
+
_I("Cell magic %%bot executing ...")
|
71
|
+
_D(f"Cell magic called with line: {line}")
|
72
|
+
_D(f"Cell magic called with cell: {repr(cell)[:50]} ...")
|
73
|
+
if not cell.strip():
|
74
|
+
_O(
|
75
|
+
Markdown(
|
76
|
+
"The cell is **empty**, we can't do anything.\n\n"
|
77
|
+
"We will fill it with some random characters, please **RERUN** the cell again."
|
78
|
+
)
|
79
|
+
)
|
80
|
+
if self.shell is not None:
|
81
|
+
self.shell.set_next_input(
|
82
|
+
"%%bot {}\n\n# {}".format(line.strip(), time.strftime("%Y-%m-%d %H:%M:%S")), replace=True
|
83
|
+
)
|
84
|
+
return
|
85
|
+
options = self.parse_args(line)
|
86
|
+
_D(f"Cell magic called with options: {options}")
|
87
|
+
set_logging_level(options.logging_level)
|
88
|
+
self.notebook_path = self.notebook_path or ipynbname.path()
|
89
|
+
_D(f"Cell magic called with notebook path: {self.notebook_path}")
|
90
|
+
nb_context = NotebookContext(line, cell, notebook_path=self.notebook_path)
|
91
|
+
agent_factory = AgentFactory(
|
92
|
+
nb_context,
|
93
|
+
display_think=self.display_think,
|
94
|
+
display_message=self.display_message,
|
95
|
+
display_response=self.display_response,
|
96
|
+
)
|
97
|
+
agent_factory.config_model(
|
98
|
+
AgentModelType.DEFAULT, self.default_api_url, self.default_api_key, self.default_model_name
|
99
|
+
)
|
100
|
+
agent_factory.config_model(
|
101
|
+
AgentModelType.PLANNER, self.planner_api_url, self.planner_api_key, self.planner_model_name
|
102
|
+
)
|
103
|
+
agent_factory.config_model(
|
104
|
+
AgentModelType.CODING, self.coding_api_url, self.coding_api_key, self.coding_model_name
|
105
|
+
)
|
106
|
+
agent_factory.config_model(
|
107
|
+
AgentModelType.REASONING, self.reasoning_api_url, self.reasoning_api_key, self.reasoning_model_name
|
108
|
+
)
|
109
|
+
if options.planning:
|
110
|
+
flow = MasterPlannerFlow(nb_context, agent_factory)
|
111
|
+
else:
|
112
|
+
if options.flow == "v1":
|
113
|
+
flow = TaskExecutorFlowV1(nb_context, agent_factory)
|
114
|
+
elif options.flow == "v2":
|
115
|
+
flow = TaskExecutorFlowV2(nb_context, agent_factory)
|
116
|
+
elif options.flow == "v3":
|
117
|
+
flow = TaskExecutorFlowV3(nb_context, agent_factory)
|
118
|
+
else:
|
119
|
+
raise ValueError(f"Unknown flow: {options.flow}")
|
120
|
+
flow(options.stage, options.max_tries, not options.step_mode, not options.auto_confirm)
|
121
|
+
except Exception as e:
|
122
|
+
traceback.print_exc()
|
123
|
+
|
124
|
+
|
125
|
+
def load_ipython_extension(ipython):
|
126
|
+
"""Load the bot magic extension."""
|
127
|
+
ipython.register_magics(BotMagics)
|