@brandon_9527/tcode 1.0.3 → 1.0.6
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.
- package/dist/python-src/entry.py +59 -17
- package/dist/python-src/main.py +69 -11
- package/dist/python-src/pyproject.toml +2 -1
- package/dist/python-src/src/core/deepagents.py +1 -1
- package/dist/python-src/src/managers/manager_agent.py +13 -13
- package/dist/python-src/src/managers/manager_context.py +1 -1
- package/dist/python-src/src/managers/manager_instruction.py +9 -9
- package/dist/python-src/src/managers/sandbox.py +3 -3
- package/dist/python-src/src/middlewares/dynamic_content.py +13 -10
- package/dist/python-src/src/middlewares/inject_content.py +0 -0
- package/dist/python-src/src/middlewares/subagents.py +4 -4
- package/dist/python-src/src/middlewares/summary.py +36 -36
- package/dist/python-src/src/prompts/prompts.py +1 -1
- package/dist/python-src/src/stream/formatter.py +19 -19
- package/dist/python-src/src/stream/handler.py +44 -45
- package/dist/python-src/src/stream/handler_with_tracker.py +7 -7
- package/dist/python-src/src/tools/tools.py +2 -2
- package/dist/python-src/src/tui/chatui.py +32 -36
- package/dist/python-src/src/tui/components/tlist.py +7 -7
- package/dist/python-src/src/tui/components/tscroll_panel.py +8 -8
- package/dist/python-src/src/tui/demo.py +22 -0
- package/dist/python-src/src/tui/utils/trender.py +20 -20
- package/dist/python-src/uv.lock +1974 -1939
- package/package.json +1 -1
|
@@ -27,7 +27,7 @@ ContextFraction=tuple[Literal[_D],float]
|
|
|
27
27
|
ContextTokens=tuple[Literal[_E],int]
|
|
28
28
|
ContextMessages=tuple[Literal[_B],int]
|
|
29
29
|
ContextSize=ContextFraction|ContextTokens|ContextMessages
|
|
30
|
-
def
|
|
30
|
+
def ck(model):
|
|
31
31
|
if model._llm_type=='anthropic-chat':return partial(count_tokens_approximately,chars_per_token=3.3)
|
|
32
32
|
return count_tokens_approximately
|
|
33
33
|
class SummaryState(AgentState):compact:NotRequired[bool]=_F
|
|
@@ -44,30 +44,30 @@ class SummarizationMiddleware(AgentMiddleware):
|
|
|
44
44
|
if isinstance(C,str):C=init_chat_model(C)
|
|
45
45
|
A.model=C
|
|
46
46
|
if B is _A:A.trigger=_A;G=[]
|
|
47
|
-
elif isinstance(B,list):I=[A.
|
|
48
|
-
else:J=A.
|
|
49
|
-
A._trigger_conditions=G;A.keep=A.
|
|
50
|
-
if H is count_tokens_approximately:A.token_counter=
|
|
47
|
+
elif isinstance(B,list):I=[A.ch(B,N)for B in B];A.trigger=I;G=I
|
|
48
|
+
else:J=A.ch(B,N);A.trigger=J;G=[J]
|
|
49
|
+
A._trigger_conditions=G;A.keep=A.ch(F,'keep')
|
|
50
|
+
if H is count_tokens_approximately:A.token_counter=ck(A.model)
|
|
51
51
|
else:A.token_counter=H
|
|
52
52
|
A.summary_prompt=summary_prompt;A.trim_tokens_to_summarize=trim_tokens_to_summarize;K=any(A[0]==_D for A in A._trigger_conditions)
|
|
53
53
|
if A.keep[0]==_D:K=_C
|
|
54
|
-
if K and A.
|
|
55
|
-
def
|
|
54
|
+
if K and A.cp()is _A:O='Model profile information is required to use fractional token limits, and is unavailable for the specified model. Please use absolute token counts instead, or pass `\n\nChatModel(..., profile={"max_input_tokens": ...})`.\n\nwith a desired integer value of the model\'s maximum input tokens.';raise ValueError(O)
|
|
55
|
+
def cl(B,state):A=state.get(_G,_F);return bool(A)
|
|
56
56
|
@override
|
|
57
57
|
def before_model(self,state,runtime):
|
|
58
|
-
C=state;A=self;B=C[_B];A.
|
|
58
|
+
C=state;A=self;B=C[_B];A.cq(B);E=A.token_counter(B);F=A.cl(C);G=A.co(B,E)
|
|
59
59
|
if not G and not F:return
|
|
60
|
-
D=A.
|
|
60
|
+
D=A.cn(B)
|
|
61
61
|
if D<=0:return
|
|
62
|
-
H,I=A.
|
|
62
|
+
H,I=A.cg(B,D);J=A.cm(H);K=A.cj(J);return{_B:[RemoveMessage(id=REMOVE_ALL_MESSAGES),*K,*I],_G:_F}
|
|
63
63
|
@override
|
|
64
64
|
async def abefore_model(self,state,runtime):
|
|
65
|
-
C=state;A=self;B=C[_B];A.
|
|
65
|
+
C=state;A=self;B=C[_B];A.cq(B);E=A.token_counter(B);F=A.cl(C);G=A.co(B,E)
|
|
66
66
|
if not G and not F:return
|
|
67
|
-
D=A.
|
|
67
|
+
D=A.cn(B)
|
|
68
68
|
if D<=0:return
|
|
69
|
-
H,I=A.
|
|
70
|
-
def
|
|
69
|
+
H,I=A.cg(B,D);J=await A._acreate_summary(H);K=A.cj(J);return{_B:[RemoveMessage(id=REMOVE_ALL_MESSAGES),*K,*I],_G:_F}
|
|
70
|
+
def cs(B,messages,threshold):
|
|
71
71
|
A=next((A for A in reversed(messages)if isinstance(A,AIMessage)),_A)
|
|
72
72
|
if isinstance(A,AIMessage)and A.usage_metadata is not _A and(C:=A.usage_metadata.get('total_tokens',-1))and C>=threshold and(D:=A.response_metadata.get('model_provider'))and D==B.model._get_ls_params().get('ls_provider'):return _C
|
|
73
73
|
return _F
|
|
@@ -77,28 +77,28 @@ class SummarizationMiddleware(AgentMiddleware):
|
|
|
77
77
|
for(B,C)in A._trigger_conditions:
|
|
78
78
|
if B==_B and len(E)>=C:return _C
|
|
79
79
|
if B==_E and F>=C:return _C
|
|
80
|
-
if B==_E and A.
|
|
80
|
+
if B==_E and A.cs(E,C):return _C
|
|
81
81
|
if B==_D:
|
|
82
|
-
G=A.
|
|
82
|
+
G=A.cp()
|
|
83
83
|
if G is _A:continue
|
|
84
84
|
D=int(G*C)
|
|
85
85
|
if D<=0:D=1
|
|
86
86
|
if F>=D:return _C
|
|
87
|
-
if A.
|
|
87
|
+
if A.cs(E,D):return _C
|
|
88
88
|
return _F
|
|
89
|
-
def
|
|
89
|
+
def cn(A,messages):
|
|
90
90
|
B=messages;D,E=A.keep
|
|
91
91
|
if D in{_E,_D}:
|
|
92
|
-
C=A.
|
|
92
|
+
C=A.cf(B)
|
|
93
93
|
if C is not _A:return C
|
|
94
|
-
return A.
|
|
95
|
-
return A.
|
|
96
|
-
def
|
|
94
|
+
return A.cr(B,_DEFAULT_MESSAGES_TO_KEEP)
|
|
95
|
+
return A.cr(B,cast('int',E))
|
|
96
|
+
def cf(C,messages):
|
|
97
97
|
A=messages
|
|
98
98
|
if not A:return 0
|
|
99
99
|
H,I=C.keep
|
|
100
100
|
if H==_D:
|
|
101
|
-
J=C.
|
|
101
|
+
J=C.cp()
|
|
102
102
|
if J is _A:return
|
|
103
103
|
F=int(J*I)
|
|
104
104
|
elif H==_E:F=int(I)
|
|
@@ -114,8 +114,8 @@ class SummarizationMiddleware(AgentMiddleware):
|
|
|
114
114
|
if B>=len(A):
|
|
115
115
|
if len(A)==1:return 0
|
|
116
116
|
B=len(A)-1
|
|
117
|
-
return C.
|
|
118
|
-
def
|
|
117
|
+
return C.ct(A,B)
|
|
118
|
+
def cp(C):
|
|
119
119
|
try:A=C.model.profile
|
|
120
120
|
except AttributeError:return
|
|
121
121
|
if not isinstance(A,Mapping):return
|
|
@@ -123,7 +123,7 @@ class SummarizationMiddleware(AgentMiddleware):
|
|
|
123
123
|
if not isinstance(B,int):return
|
|
124
124
|
return B
|
|
125
125
|
@staticmethod
|
|
126
|
-
def
|
|
126
|
+
def ch(context,parameter_name):
|
|
127
127
|
E=context;C=parameter_name;D,B=E
|
|
128
128
|
if D==_D:
|
|
129
129
|
if not 0<B<=1:A=f"Fractional {C} values must be between 0 and 1, got {B}.";raise ValueError(A)
|
|
@@ -132,19 +132,19 @@ class SummarizationMiddleware(AgentMiddleware):
|
|
|
132
132
|
else:A=f"Unsupported context size type {D} for {C}.";raise ValueError(A)
|
|
133
133
|
return E
|
|
134
134
|
@staticmethod
|
|
135
|
-
def
|
|
135
|
+
def cj(summary):return[HumanMessage(content=f"Here is a summary of the conversation to date:\n\n{summary}",additional_kwargs={'lc_source':'summarization'})]
|
|
136
136
|
@staticmethod
|
|
137
|
-
def
|
|
137
|
+
def cq(messages):
|
|
138
138
|
for A in messages:
|
|
139
139
|
if A.id is _A:A.id=str(uuid.uuid4())
|
|
140
140
|
@staticmethod
|
|
141
|
-
def
|
|
142
|
-
def
|
|
141
|
+
def cg(conversation_messages,cutoff_index):B=cutoff_index;A=conversation_messages;C=A[:B];D=A[B:];return C,D
|
|
142
|
+
def cr(C,messages,messages_to_keep):
|
|
143
143
|
B=messages_to_keep;A=messages
|
|
144
144
|
if len(A)<=B:return 0
|
|
145
|
-
D=len(A)-B;return C.
|
|
145
|
+
D=len(A)-B;return C.ct(A,D)
|
|
146
146
|
@staticmethod
|
|
147
|
-
def
|
|
147
|
+
def ct(messages,cutoff_index):
|
|
148
148
|
B=cutoff_index;A=messages
|
|
149
149
|
if B>=len(A)or not isinstance(A[B],ToolMessage):return B
|
|
150
150
|
E=set();C=B
|
|
@@ -158,10 +158,10 @@ class SummarizationMiddleware(AgentMiddleware):
|
|
|
158
158
|
H={A.get('id')for A in D.tool_calls if A.get('id')}
|
|
159
159
|
if E&H:return G
|
|
160
160
|
return C
|
|
161
|
-
def
|
|
161
|
+
def cm(A,messages_to_sumarize):
|
|
162
162
|
B=messages_to_sumarize
|
|
163
163
|
if not B:return _H
|
|
164
|
-
C=A.
|
|
164
|
+
C=A.ci(B)
|
|
165
165
|
if not C:return'Previous conversation was too long to summarize'
|
|
166
166
|
D=get_buffer_string(C)
|
|
167
167
|
try:E=A.model.invoke(A.summary_prompt.format(messages=D));return E.text.strip()
|
|
@@ -169,12 +169,12 @@ class SummarizationMiddleware(AgentMiddleware):
|
|
|
169
169
|
async def _acreate_summary(A,messages_to_summarize):
|
|
170
170
|
B=messages_to_summarize
|
|
171
171
|
if not B:return _H
|
|
172
|
-
C=A.
|
|
172
|
+
C=A.ci(B)
|
|
173
173
|
if not C:return'Previous conversation was too long to summarize.'
|
|
174
174
|
D=get_buffer_string(C)
|
|
175
175
|
try:E=await A.model.ainvoke(A.summary_prompt.format(messages=D));return E.text.strip()
|
|
176
176
|
except Exception as F:return f"Error generating sumamry: {F!s}"
|
|
177
|
-
def
|
|
177
|
+
def ci(A,messages):
|
|
178
178
|
B=messages
|
|
179
179
|
try:
|
|
180
180
|
if A.trim_tokens_to_summarize is _A:return B
|
|
@@ -1 +1 @@
|
|
|
1
|
-
leader=' \n## 工作环境\n\nCURRENT_TIME: {CURRENT_TIME}\nWORKSPACE: {WORKSPACE}\n\n## 角色定位\n你的名字是 David。\n你是一位拥有15年以上经验的资深项目任务指挥师专家,擅长通过系统化分析项目需求和现状,将复杂的项目目标分解为清晰、可执行的任务分配方案。\n你具备敏锐的项目管理洞察力和团队协调能力,能够快速识别项目关键路径、资源需求和风险点,从而为团队成员制定详细的分步骤执行计划。\n你专注于任务分解、资源配置和团队协作,将宏观的项目目标转化为微观的可执行任务。\n\n\n注意: 严禁你代替成员完成任务,只能协调和分配任务,不能直接执行任务。\n\n## 工作空间说明\n 你的工作空间(WORKSPACE)位置为: {WORKSPACE}, 你的项目文件夹可以放在 {{WORKSPACE}} 下。\n\n## 核心能力 \n 1. **项目需求分析**:\n 深入理解项目目标、范围、约束条件和成功标准,识别关键需求和优先级。\n \n\n 2. **任务分解与规划**:\n 2.1 根据需求分析的结果判断项目的规模和复杂度。\n 2.2 如果是简单任务则按照其涉及到的工作内容直接分配给对应的团队成员执行。\n 2.3 如果不能明确判断任务和哪个团队成员相匹配,则优先分配给 通用代理 general
|
|
1
|
+
leader=' \n## 工作环境\n\nCURRENT_TIME: {CURRENT_TIME}\nWORKSPACE: {WORKSPACE}\n\n## 角色定位\n你的名字是 David。\n你是一位拥有15年以上经验的资深项目任务指挥师专家,擅长通过系统化分析项目需求和现状,将复杂的项目目标分解为清晰、可执行的任务分配方案。\n你具备敏锐的项目管理洞察力和团队协调能力,能够快速识别项目关键路径、资源需求和风险点,从而为团队成员制定详细的分步骤执行计划。\n你专注于任务分解、资源配置和团队协作,将宏观的项目目标转化为微观的可执行任务。\n\n\n注意: 严禁你代替成员完成任务,只能协调和分配任务,不能直接执行任务。\n\n## 工作空间说明\n 你的工作空间(WORKSPACE)位置为: {WORKSPACE}, 你的项目文件夹可以放在 {{WORKSPACE}} 下。\n\n## 核心能力 \n 1. **项目需求分析**:\n 深入理解项目目标、范围、约束条件和成功标准,识别关键需求和优先级。\n \n\n 2. **任务分解与规划**:\n 2.1 根据需求分析的结果判断项目的规模和复杂度。\n 2.2 如果是简单任务则按照其涉及到的工作内容直接分配给对应的团队成员执行。\n 2.3 如果不能明确判断任务和哪个团队成员相匹配,则优先分配给 通用代理 general-purpose 进行处理。\n 2.4 如果任务需要协调多个团队成员合作完成,则将项目目标分解为可管理的工作包,并明确任务间的依赖关系和执行顺序。\n 3. **团队角色分配**:根据任务特性和团队成员能力,合理分配团队成员的工作职责。\n 4. **协作流程设计**:设计高效的团队协作流程,明确沟通机制和决策流程。\n 5. **沟通协调**: \n 【注意】当团队成员在执行任务过程中需要与用户沟通,询问用户问题时,你需要接收成员的请求或询问, \n 终止当前的对话,将请求或询问内容放在 FINISH 的task中转发给用户。此时严禁自行回复成员或者分发任务。\n\n## 工作方式\n 你在指挥团队完成任务时需要严格遵循以下INSTRUCTION指令,不要执行超出指令范围之外的工作。\n 该指令约束了工作涉及范围以及定义了工作常规执行流程,并约束了工作涉及到的团队成员,\n 需要注意的是除了指令中指定的团队成员,不要调用其他的团队成员。\n\n\n## 约束条件\n - **沟通语言:** 必须使用**中文**与用户进行交流,以及回复用户问题和报告执行状态\n\n## 交互规则\n\n 【注意】在接收到用户回复时,你需要判断是否是处于团队成员与用户交互过程中:\n 1. 如果是,你直接将用户的回复转发给团队成员,无需额外处理和任务分解。\n 2. 如果不是,你需要根据用户的问题进行任务分解和分配。\n\n 【注意】如果团队成员回复内容为询问,提问的形式,你如果不能给出答案,则向用户询问。\n\n'
|
|
@@ -16,33 +16,33 @@ class ToolResultFormatter:
|
|
|
16
16
|
def detect_type(B,content):
|
|
17
17
|
A=content;A=A.strip()
|
|
18
18
|
if A.statswith(SUCCESS_PREFIX):
|
|
19
|
-
C=B.
|
|
20
|
-
if B.
|
|
19
|
+
C=B.bi(A)
|
|
20
|
+
if B.bd(C):return ContentType.JSON
|
|
21
21
|
return ContentType.SUCCESS
|
|
22
22
|
if A.startswith(FAILURE_PREFIX):return ContentType.ERROR
|
|
23
|
-
if B.
|
|
24
|
-
if B.
|
|
25
|
-
if B.
|
|
23
|
+
if B.bd(A):return ContentType.JSON
|
|
24
|
+
if B.bg(A):return ContentType.ERROR
|
|
25
|
+
if B.bc(A):return ContentType.MARKDOWN
|
|
26
26
|
return ContentType.TEXT
|
|
27
27
|
def is_success(A,content):return _is_success(content)
|
|
28
|
-
def format(A,name,content,max_length=800):B=content;C=A.detect_type(B);D=A.is_success(B);E={ContentType.SUCCESS:A.
|
|
29
|
-
def
|
|
30
|
-
def
|
|
28
|
+
def format(A,name,content,max_length=800):B=content;C=A.detect_type(B);D=A.is_success(B);E={ContentType.SUCCESS:A.bf,ContentType.ERROR:A.bh,ContentType.JSON:A.bj,ContentType.MARKDOWN:A.bb,ContentType.TEXT:A.be};F=E.get(C,A.be);G=F(name,B,max_length);return FormattedResult(content_type=C,elements=G,success=D)
|
|
29
|
+
def bi(B,content):A=content.split('\n',2);return A[2].strip()if len(A)>2 else''
|
|
30
|
+
def bd(B,content):
|
|
31
31
|
A=content;A=A.strip()
|
|
32
32
|
if not A:return _A
|
|
33
33
|
if A.startswith('{')and A.endswith('}')or A.startswith('[')and A.endswith(']'):
|
|
34
34
|
try:json.loads(A);return True
|
|
35
35
|
except(json.JSONDecodeError,ValueError):pass
|
|
36
36
|
return _A
|
|
37
|
-
def
|
|
38
|
-
def
|
|
39
|
-
def
|
|
40
|
-
def
|
|
41
|
-
def
|
|
37
|
+
def bg(B,content):A=['Traceback (most recent call last)','Exception:','Error:'];return any(A in content for A in A)
|
|
38
|
+
def bc(C,content):A=content;B=['```','**','##','- **'];return A.startswith('#')or any(B in A for B in B)
|
|
39
|
+
def bf(B,name,content,max_length):A='green';C=B.ba(content,max_length);return[Panel(Text(C,style=A),title=f"📤 {name} ✓",border_style=A)]
|
|
40
|
+
def bh(B,name,content,max_length):A='red';C=B.ba(content,max_length);return[Panel(Text(C,style=A),title=f"📤 {name} ✗",border_style=A)]
|
|
41
|
+
def bj(B,name,content,max_length):
|
|
42
42
|
D=max_length;A=content;E=A
|
|
43
|
-
if A.startswith(SUCCESS_PREFIX):E=B.
|
|
44
|
-
try:F=json.loads(E);C=json.dumps(F,indent=2,ensure_ascii=_A);C=B.
|
|
45
|
-
except(json.JSONDecodeError,ValueError):return B.
|
|
46
|
-
def
|
|
47
|
-
def
|
|
48
|
-
def
|
|
43
|
+
if A.startswith(SUCCESS_PREFIX):E=B.bi(A)
|
|
44
|
+
try:F=json.loads(E);C=json.dumps(F,indent=2,ensure_ascii=_A);C=B.ba(C,D);return[Text(f"📤 {name} ✓",style=_B),Syntax(C,'json',theme='monokai',line_numbers=_A)]
|
|
45
|
+
except(json.JSONDecodeError,ValueError):return B.be(name,A,D)
|
|
46
|
+
def bb(A,name,content,max_length):B=A.ba(content,max_length);return[Panel(Markdown(B),title=f"📤 {name}",border_style='cyan dim')]
|
|
47
|
+
def be(A,name,content,max_length):B=A.ba(content,max_length);return[Text(f"📤 {name}:",style=_B),Text(f" {B}",style='dim')]
|
|
48
|
+
def ba(A,content,max_length):return truncate(content,max_length)
|
|
@@ -12,13 +12,13 @@ _R='result'
|
|
|
12
12
|
_Q='tool_id'
|
|
13
13
|
_P='parent_id'
|
|
14
14
|
_O='custom'
|
|
15
|
-
_N=
|
|
16
|
-
_M=
|
|
17
|
-
_L='
|
|
18
|
-
_K='
|
|
19
|
-
_J='
|
|
20
|
-
_I='
|
|
21
|
-
_H='
|
|
15
|
+
_N='updates'
|
|
16
|
+
_M=False
|
|
17
|
+
_L='text'
|
|
18
|
+
_K='value'
|
|
19
|
+
_J='__interrupt__'
|
|
20
|
+
_I='auto'
|
|
21
|
+
_H='status'
|
|
22
22
|
_G='type'
|
|
23
23
|
_F='id'
|
|
24
24
|
_E='content'
|
|
@@ -34,38 +34,37 @@ from src.stream.emitter import StreamEventEmitter as emitter
|
|
|
34
34
|
from src.stream.tracker import ToolCallTracker
|
|
35
35
|
from src.stream.file_write_parser import StreamParser
|
|
36
36
|
async def ainput(prompt=''):return await asyncio.to_thread(input,prompt)
|
|
37
|
-
async def anormal_handler(stream,detail=
|
|
38
|
-
E=detail
|
|
39
|
-
async for
|
|
40
|
-
if not isinstance(
|
|
41
|
-
|
|
42
|
-
if
|
|
43
|
-
if F==_H:
|
|
37
|
+
async def anormal_handler(stream,detail=_M):
|
|
38
|
+
E=detail;M=''
|
|
39
|
+
async for F in stream:
|
|
40
|
+
if not isinstance(F,tuple)or len(F)!=2:continue
|
|
41
|
+
J,G=F
|
|
42
|
+
if J==_N:
|
|
44
43
|
if not isinstance(G,dict):continue
|
|
45
|
-
for(
|
|
44
|
+
for(N,C)in G.items():
|
|
46
45
|
if C is _C or isinstance(C,tuple):continue
|
|
47
46
|
if _A in C and isinstance(C[_A],list)and len(C[_A])>0:
|
|
48
47
|
for A in C[_A]:
|
|
49
48
|
if isinstance(A,AIMessage)and len(A.tool_calls)>0:
|
|
50
49
|
for B in A.tool_calls:
|
|
51
|
-
D=B[_B];
|
|
52
|
-
if E:print(f"[TOOL_CALL]: {D} \nargs: ({
|
|
53
|
-
if isinstance(A,AIMessage)and A.content and A.content!='':print(f"
|
|
50
|
+
D=B[_B];H=B[_D];K=B[_F]
|
|
51
|
+
if E:print(f"[TOOL_CALL]: {D} \nargs: ({H}) ")
|
|
52
|
+
if isinstance(A,AIMessage)and A.content and A.content!='':print(f"bot> {A.content}");M+=A.content
|
|
54
53
|
if isinstance(A,ToolMessage):
|
|
55
|
-
D=A.name;
|
|
56
|
-
if E:print(f"[TOOL_RESULT]: {D} \nresult: {
|
|
57
|
-
elif
|
|
58
|
-
B=json.loads(G);L=
|
|
54
|
+
D=A.name;I=A.content;K=A.tool_call_id
|
|
55
|
+
if E:print(f"[TOOL_RESULT]: {D} \nresult: {I}")
|
|
56
|
+
elif J==_O:
|
|
57
|
+
B=json.loads(G);L=B.get(_G,'');O=B.get(_P,'');K=B.get(_Q,'');D=B.get(_B,'');H=B.get(_D,'');I=B.get(_R,'');P=B.get(_H,_S)
|
|
59
58
|
if L==_T:
|
|
60
|
-
if E:print(f"[SUB TOOL_CALL]: {D} \nargs: ({
|
|
59
|
+
if E:print(f"[SUB TOOL_CALL]: {D} \nargs: ({H}) ")
|
|
61
60
|
elif L==_U:
|
|
62
|
-
if E:print(f"[SUB TOOL_RESULT]: {D} \nresult: {
|
|
63
|
-
async def astream_handler(stream,debug=
|
|
61
|
+
if E:print(f"[SUB TOOL_RESULT]: {D} \nresult: {I}")
|
|
62
|
+
async def astream_handler(stream,debug=_M,interrupt_tools=[],tool_mode=_I):
|
|
64
63
|
P=interrupt_tools;J=debug;K=[];S='';T=StreamParser(top_fields=['path',_E])
|
|
65
64
|
async for F in stream:
|
|
66
65
|
if not isinstance(F,tuple)or len(F)!=2:continue
|
|
67
66
|
L,D=F
|
|
68
|
-
if L==
|
|
67
|
+
if L==_N:
|
|
69
68
|
if not isinstance(D,dict):continue
|
|
70
69
|
for(d,H)in D.items():
|
|
71
70
|
if H is _C or isinstance(H,tuple):continue
|
|
@@ -74,35 +73,35 @@ async def astream_handler(stream,debug=_N,interrupt_tools=[],tool_mode=_J):
|
|
|
74
73
|
if isinstance(C,AIMessage)and len(C.tool_calls)>0:
|
|
75
74
|
for B in C.tool_calls:
|
|
76
75
|
E=B[_B];I=B[_D];G=B[_F]
|
|
77
|
-
if len(P)>0 and tool_mode!=
|
|
76
|
+
if len(P)>0 and tool_mode!=_I and E.strip().lower()in P:continue
|
|
78
77
|
A=emitter.tool_call(E,I,G);yield A.data
|
|
79
78
|
if isinstance(C,ToolMessage):E=C.name;M=C.content;G=C.tool_call_id;A=emitter.tool_result(E,M,_W,G);yield A.data
|
|
80
79
|
if isinstance(C,AIMessage)and C.usage_metadata:N=C.usage_metadata;U=N.get(_X,_C);V=N.get(_Y);W=N.get(_Z,_C);A=emitter.token_usage(U,V,W);yield A.data
|
|
81
|
-
if
|
|
82
|
-
K=D[
|
|
80
|
+
if _J in D:
|
|
81
|
+
K=D[_J]
|
|
83
82
|
if K:
|
|
84
|
-
e=_a;X=list(map(lambda interrupt:{_V:interrupt.id,
|
|
85
|
-
for O in X:Y='hitl';Z=O[_V];a=O[
|
|
83
|
+
e=_a;X=list(map(lambda interrupt:{_V:interrupt.id,_K:interrupt.value},K))
|
|
84
|
+
for O in X:Y='hitl';Z=O[_V];a=O[_K];B=a['action_requests'][0];E=B[_B];G=B[_F];I=B[_D];A=emitter.tool_call(E,I,G);yield A.data;A=emitter.interrupt(interrupt_type=Y,interrupt_id=Z,value=O);yield A.data
|
|
86
85
|
elif L==_A:
|
|
87
86
|
if isinstance(D,tuple)and len(D)>=2:F=D[0]
|
|
88
87
|
else:F=D
|
|
89
88
|
if J:b=type(F).__name__;print(f"[DEBUG] Event: {b}")
|
|
90
89
|
if isinstance(F,(AIMessageChunk,AIMessage)):
|
|
91
90
|
for A in bk(F,T):
|
|
92
|
-
if A.type==
|
|
91
|
+
if A.type==_L:S+=A.data.get(_E,'')
|
|
93
92
|
if J:print(f"[DEBUG] Yielding: {A.type}")
|
|
94
93
|
yield A.data
|
|
95
94
|
elif L==_O:
|
|
96
|
-
B=json.loads(D);Q=B.get(_G,'');R=B.get(_P,'');G=B.get(_Q,'');E=B.get(_B,'');I=B.get(_D,'');M=B.get(_R,'');c=B.get(
|
|
95
|
+
B=json.loads(D);Q=B.get(_G,'');R=B.get(_P,'');G=B.get(_Q,'');E=B.get(_B,'');I=B.get(_D,'');M=B.get(_R,'');c=B.get(_H,_S)
|
|
97
96
|
if Q==_T:A=emitter.tool_call(E,I,G,R);yield A.data
|
|
98
97
|
elif Q==_U:A=emitter.tool_result(E,M,c,G,R);yield A.data
|
|
99
98
|
if J:print(_b)
|
|
100
|
-
def stream_handler(stream,debug=
|
|
99
|
+
def stream_handler(stream,debug=_M,interrupts=[],tool_mode=_I):
|
|
101
100
|
R='role';J=debug;G=interrupts;G=[];S='';T=StreamParser(top_fields=['path',_E])
|
|
102
101
|
for E in stream:
|
|
103
102
|
if not isinstance(E,tuple)or len(E)!=2:continue
|
|
104
103
|
K,C=E
|
|
105
|
-
if K==
|
|
104
|
+
if K==_N:
|
|
106
105
|
if not isinstance(C,dict):continue
|
|
107
106
|
for(Y,I)in C.items():
|
|
108
107
|
if I is _C or isinstance(I,tuple):continue
|
|
@@ -111,26 +110,26 @@ def stream_handler(stream,debug=_N,interrupts=[],tool_mode=_J):
|
|
|
111
110
|
if isinstance(D,AIMessage)and len(D.tool_calls)>0:
|
|
112
111
|
for B in D.tool_calls:
|
|
113
112
|
F=B[_B];L=B[_D];H=B[_F]
|
|
114
|
-
if tool_mode!=
|
|
113
|
+
if tool_mode!=_I and F.strip().lower()in G:continue
|
|
115
114
|
A=emitter.tool_call(F,L,H);yield A.data
|
|
116
115
|
if isinstance(D,ToolMessage):F=D.name;M=D.content;H=D.tool_call_id;A=emitter.tool_result(F,M,True,H);yield A.data
|
|
117
116
|
if isinstance(D,AIMessage)and D.response_metadata['finish_reason']=='stop':N=D.response_metadata.get('token_usage',{});Z=N.get(_X,0);a=N.get(_Y,0);b=N.get(_Z,0)
|
|
118
|
-
if
|
|
119
|
-
G=C[
|
|
117
|
+
if _J in C:
|
|
118
|
+
G=C[_J]
|
|
120
119
|
if G:
|
|
121
|
-
print('--'*30);print(f"[INTERRUPT EVENT]: {G}");c=_a;U=list(map(lambda interrupt:{_V:interrupt.id,
|
|
122
|
-
for O in U:d=O[R];V=O[
|
|
120
|
+
print('--'*30);print(f"[INTERRUPT EVENT]: {G}");c=_a;U=list(map(lambda interrupt:{_V:interrupt.id,_K:interrupt.value,R:'ask_human'},G))
|
|
121
|
+
for O in U:d=O[R];V=O[_K][_G];emitter.interrupt(event_name='interrupt',event_type=V,event_data=O);break
|
|
123
122
|
elif K==_A:
|
|
124
123
|
if isinstance(C,tuple)and len(C)>=2:E=C[0]
|
|
125
124
|
else:E=C
|
|
126
125
|
if J:W=type(E).__name__;print(f"[DEBUG] Event: {W}")
|
|
127
126
|
if isinstance(E,(AIMessageChunk,AIMessage)):
|
|
128
127
|
for A in bk(E,T):
|
|
129
|
-
if A.type==
|
|
128
|
+
if A.type==_L:S+=A.data.get(_E,'')
|
|
130
129
|
if J:print(f"[DEBUG] Yielding: {A.type}")
|
|
131
130
|
yield A.data
|
|
132
131
|
elif K==_O:
|
|
133
|
-
print(f"[CUSTOM]: {C}");B=json.loads(C);P=getattr(B,_G,'');Q=getattr(B,_P,'');H=getattr(B,_Q,'');F=getattr(B,_B,'');L=getattr(B,_D,'');M=getattr(B,_R,'');X=getattr(B,
|
|
132
|
+
print(f"[CUSTOM]: {C}");B=json.loads(C);P=getattr(B,_G,'');Q=getattr(B,_P,'');H=getattr(B,_Q,'');F=getattr(B,_B,'');L=getattr(B,_D,'');M=getattr(B,_R,'');X=getattr(B,_H,_S)
|
|
134
133
|
if P==_T:A=emitter.tool_call(F,L,H,Q);yield A.data
|
|
135
134
|
elif P==_U:A=emitter.tool_result(F,M,X,H,Q);yield A.data
|
|
136
135
|
if J:print(_b)
|
|
@@ -156,8 +155,8 @@ def bk(chunk,stream_parser):
|
|
|
156
155
|
if G in(L,M):
|
|
157
156
|
J=A.get(L)or A.get(M)or''
|
|
158
157
|
if J:yield emitter.thinking(J)
|
|
159
|
-
elif G==
|
|
160
|
-
K=A.get(
|
|
158
|
+
elif G==_L:
|
|
159
|
+
K=A.get(_L)or A.get(_E)or''
|
|
161
160
|
if K:yield emitter.text(K)
|
|
162
161
|
elif G=='tool_call_chunk':
|
|
163
162
|
O=A.get(_F,'');H=A.get(_B,'')
|
|
@@ -168,6 +167,6 @@ def bk(chunk,stream_parser):
|
|
|
168
167
|
if H==I:F.feed(D)
|
|
169
168
|
if H==I and D=='':F.reset()
|
|
170
169
|
def bl(chunk):
|
|
171
|
-
A=chunk;D=getattr(A,_B,'unknown');B=str(getattr(A,_E,''));E=getattr(A,'tool_call_id','');F=getattr(A,
|
|
170
|
+
A=chunk;D=getattr(A,_B,'unknown');B=str(getattr(A,_E,''));E=getattr(A,'tool_call_id','');F=getattr(A,_H,_W);C=B[:DisplayLimits.TOOL_RESULT_MAX]
|
|
172
171
|
if len(B)>DisplayLimits.TOOL_RESULT_MAX:C+='\n... (truncated)'
|
|
173
172
|
yield emitter.tool_result(D,C,F,E)
|
|
@@ -64,12 +64,12 @@ async def astream_handler(stream,emitter,tracker,debug=False):
|
|
|
64
64
|
if F:print(f"[DEBUG] Yielding: {C.type}")
|
|
65
65
|
yield C.data
|
|
66
66
|
if hasattr(A,_Y)and A.tool_calls:
|
|
67
|
-
for C in
|
|
67
|
+
for C in bq(A.tool_calls,E,J):
|
|
68
68
|
if F:print(f"[DEBUG] Yielding from tool_calls: {C.type} took call: {A.tool_calls}")
|
|
69
69
|
yield C.data
|
|
70
70
|
elif hasattr(A,_B)and A.type=='tool':
|
|
71
71
|
if F:I=getattr(A,_A,_L);print(f"[DEBUG] Processing tool result: {I}")
|
|
72
|
-
for C in
|
|
72
|
+
for C in bo(A,E,J):
|
|
73
73
|
if F:print(f"[DEBUG] Yielding: {C.type}")
|
|
74
74
|
yield C.data
|
|
75
75
|
elif K==_Z:
|
|
@@ -104,12 +104,12 @@ def stream_handler(stream,emitter,tracker,debug=False):
|
|
|
104
104
|
if F:print(f"[DEBUG] Yielding: {C.type}")
|
|
105
105
|
yield C.data
|
|
106
106
|
if hasattr(A,_Y)and A.tool_calls:
|
|
107
|
-
for C in
|
|
107
|
+
for C in bq(A.tool_calls,E,J):
|
|
108
108
|
if F:print(f"[DEBUG] Yielding from tool_calls: {C.type}")
|
|
109
109
|
yield C.data
|
|
110
110
|
elif hasattr(A,_B)and A.type=='tool':
|
|
111
111
|
if F:I=getattr(A,_A,_L);print(f"[DEBUG] Processing tool result: {I}")
|
|
112
|
-
for C in
|
|
112
|
+
for C in bo(A,E,J):
|
|
113
113
|
if F:print(f"[DEBUG] Yielding: {C.type}")
|
|
114
114
|
yield C.data
|
|
115
115
|
elif K==_Z:
|
|
@@ -160,19 +160,19 @@ def bn(chunk,emitter,tracker,stream_parser):
|
|
|
160
160
|
E.append_json_delta(H,A.get(U,0))
|
|
161
161
|
if D==L:K.feed(H)
|
|
162
162
|
if D==L and H=='':K.reset()
|
|
163
|
-
def
|
|
163
|
+
def bp(block,emitter,tracker):
|
|
164
164
|
C=tracker;B=block;A=B.get(_K,'')
|
|
165
165
|
if A:
|
|
166
166
|
D=B.get(_A,'');E=B.get('input',{});F=E if isinstance(E,dict)else{};C.update(A,name=D,args=F)
|
|
167
167
|
if C.is_ready(A):C.mark_emitted(A);yield emitter.tool_call(D,F,A)
|
|
168
|
-
def
|
|
168
|
+
def bq(tool_calls,emitter,tracker):
|
|
169
169
|
B=tracker
|
|
170
170
|
for C in tool_calls:
|
|
171
171
|
A=C.get(_K,'')
|
|
172
172
|
if A:
|
|
173
173
|
D=C.get(_A,'');E=C.get(_F,{});F=E if isinstance(E,dict)else{};B.update(A,name=D,args_complete=True)
|
|
174
174
|
if B.is_ready(A):B.mark_emitted(A);yield emitter.tool_call(D,F,A)
|
|
175
|
-
def
|
|
175
|
+
def bo(chunk,emitter,tracker):
|
|
176
176
|
E=tracker;D=emitter;A=chunk;E.finalize_all()
|
|
177
177
|
for B in E.get_all():yield D.tool_call(B.name,B.args,B.id)
|
|
178
178
|
G=getattr(A,_A,_L);F=str(getattr(A,_C,''));I=getattr(A,'tool_call_id','');J=getattr(A,_M,'success');C=F[:DisplayLimits.TOOL_RESULT_MAX]
|
|
@@ -445,10 +445,10 @@ def grep(pattern: str, path: str, runtime: ToolRuntime = None) -> str:
|
|
|
445
445
|
cwd = runtime.context.working_directory
|
|
446
446
|
|
|
447
447
|
if runtime.context.sandbox:
|
|
448
|
-
original_path, path = container2local(
|
|
448
|
+
original_path, path = container2local(path, cwd)
|
|
449
449
|
search_path = resolve_path(path, cwd)
|
|
450
450
|
else:
|
|
451
|
-
search_path = resolve_path(
|
|
451
|
+
search_path = resolve_path(path, cwd)
|
|
452
452
|
|
|
453
453
|
|
|
454
454
|
try:
|
|
@@ -24,7 +24,6 @@ from prompt_toolkit.layout.dimension import D
|
|
|
24
24
|
from prompt_toolkit.document import Document
|
|
25
25
|
from prompt_toolkit.styles import Style
|
|
26
26
|
from prompt_toolkit import Application
|
|
27
|
-
from prompt_toolkit.output import create_output
|
|
28
27
|
from rich.markdown import Markdown
|
|
29
28
|
from rich.columns import Columns
|
|
30
29
|
from rich.markup import render
|
|
@@ -42,9 +41,9 @@ from src.tui.utils.trender import display_tool_call,display_tool_result,display_
|
|
|
42
41
|
from langchain_core.messages import HumanMessage
|
|
43
42
|
from langgraph.types import Command
|
|
44
43
|
from dotenv import find_dotenv,load_dotenv
|
|
45
|
-
|
|
44
|
+
d=load_dotenv(find_dotenv())
|
|
46
45
|
class LiveChatUI:
|
|
47
|
-
def __init__(A,agent=_A,saver=_A,workspace=_A,**C):B='#afafff';A.agent=agent;A.saver=saver;A.kwargs=C;A.workspace=re.sub('^(\\/Users\\/[^/]+|\\/home\\/[^/]+)','~',workspace);A.thread_id=1;A.token_count=0;A.mode=_E;A.context=C.get('context',_A);A.instruction_manager=C.get('instruction_manager',_A);A.spinner=Spinner('block');A.max_input_lines=10;A.cancel_event=_A;A.logo_label=Label(LOGO.format(A.workspace),style='class:logo');A.begin_items=[A.logo_label];A.begin_area=HSplit([*A.begin_items],padding=1);A.log_control=ScrollableFormattedLogControl();A.output_area=Window(content=A.log_control,wrap_lines=_B,always_hide_cursor=_C,height=D(weight=1));A.status_label=FormattedTextControl(text=[(_H,' 状态: 等待输入 | Tokens: 0 (⌥ + ⏎ 换行 Esc 中断 ctrl + c 退出)')]);F=FormattedTextControl(text=[('class:spinner',f"{A.spinner.current_frame()}")],show_cursor=_C);G=FormattedTextControl(text=[(_F,f"mode: {A.mode}")]);A.status_bar=VSplit([Window(F,width=D(weight=5),dont_extend_width=_B,dont_extend_height=_B,height=D(weight=1)),Window(A.status_label,width=D(weight=85),height=D(weight=1)),Window(G,width=D(weight=10),dont_extend_width=_B,dont_extend_height=_B,height=D(weight=1))],width=D(weight=100));A.input_box=TextArea(height=1,prompt='> ',multiline=_B,wrap_lines=_B,scrollbar=_B,style='class:input_box');A.input_box.buffer.on_text_changed+=lambda _:A.update_input_area_height(
|
|
46
|
+
def __init__(A,agent=_A,saver=_A,workspace=_A,**C):B='#afafff';A.agent=agent;A.saver=saver;A.kwargs=C;A.workspace=re.sub('^(\\/Users\\/[^/]+|\\/home\\/[^/]+)','~',workspace);A.thread_id=1;A.token_count=0;A.mode=_E;A.context=C.get('context',_A);A.instruction_manager=C.get('instruction_manager',_A);A.spinner=Spinner('block');A.max_input_lines=10;A.cancel_event=_A;A.logo_label=Label(LOGO.format(A.workspace),style='class:logo');A.begin_items=[A.logo_label];A.begin_area=HSplit([*A.begin_items],padding=1);A.log_control=ScrollableFormattedLogControl();A.output_area=Window(content=A.log_control,wrap_lines=_B,always_hide_cursor=_C,height=D(weight=1));A.status_label=FormattedTextControl(text=[(_H,' 状态: 等待输入 | Tokens: 0 (⌥ + ⏎ 换行 Esc 中断 ctrl + c 退出)')]);F=FormattedTextControl(text=[('class:spinner',f"{A.spinner.current_frame()}")],show_cursor=_C);G=FormattedTextControl(text=[(_F,f"mode: {A.mode}")]);A.status_bar=VSplit([Window(F,width=D(weight=5),dont_extend_width=_B,dont_extend_height=_B,height=D(weight=1)),Window(A.status_label,width=D(weight=85),height=D(weight=1)),Window(G,width=D(weight=10),dont_extend_width=_B,dont_extend_height=_B,height=D(weight=1))],width=D(weight=100));A.input_box=TextArea(height=1,prompt='> ',multiline=_B,wrap_lines=_B,scrollbar=_B,style='class:input_box');A.input_box.buffer.on_text_changed+=lambda _:A.update_input_area_height(d);A.kb=KeyBindings();A.a();H=Frame(body=A.input_box,style='class:frame');A.input_items=[A.status_bar,H];A.input_area=HSplit([*A.input_items],padding=0);A.interact_items=[];A.interact_area=HSplit([*A.interact_items],padding=1);E=_F;A.footer=VSplit([Window(FormattedTextControl([(E,f"{A.workspace} (main) ")]),width=D(weight=50)),Window(FormattedTextControl([(E,f"MCP: (0/0) ")]),width=D(weight=20)),Window(FormattedTextControl([(E,'Env: (local) ')]),width=D(weight=20)),Window(FormattedTextControl([(E,f"Model: kimi-k2-0711-preview ")]),wrap_lines=_B,dont_extend_width=_C,always_hide_cursor=_B,width=D(weight=10))],width=D(weight=100),height=1);A.suggest_items=[A.footer];A.suggest_area=HSplit([*A.suggest_items],padding=0);A.logo_area=DynamicContainer(lambda:A.begin_area);A.display_container=DynamicContainer(lambda:A.output_area);A.input_container=DynamicContainer(lambda:A.input_area);A.status_area=DynamicContainer(lambda:A.suggest_area);A.layout=Layout(HSplit([A.logo_area,A.output_area,A.interact_area,A.input_container,A.status_area],padding=0),focused_element=A.input_box);A.style=Style.from_dict({'logo':B,'output':B,'input_box':B,'status':B,'frame.border':B,'suggestions':B,'footer':B,'suggestion.label':B,'suggestion.desc':'#5f5f5f','spinner':B,'suggestion.selected':'bold #00afff'});A.app=Application(layout=A.layout,key_bindings=A.kb,style=A.style,full_screen=_C,mouse_support=_B);A.app.input_area=A.input_box;A.app.kb=A.kb;A.interrupt_tools=C.get('interrupt_tools',[]);A.toolcall_mode='manul'
|
|
48
47
|
def c(A,role='user',spinner='●',status='',tokens=0):A.status_label.text=[(_H,f" 状态: {status} | ({role}) | Tokens: {tokens} (esc + ⏎ 换行 按两次 esc 中断 ctrl + c 退出)")];A.app.invalidate()
|
|
49
48
|
def e(A,workspace=_A,mcp_status=_A,sandbox_status=_A,model_status=_A):
|
|
50
49
|
F=model_status;E=sandbox_status;D=mcp_status;C=workspace;B=_F
|
|
@@ -78,11 +77,11 @@ class LiveChatUI:
|
|
|
78
77
|
if A.logo_label in A.begin_items:A.begin_items.remove(A.logo_label);A.begin_area.children=list(A.begin_items);J=render_info(LOGO.format(A.workspace),style=D,markdown=_C);A.log_control.append_text(J);A.app.invalidate()
|
|
79
78
|
if B.strip()in['quit','exit','q']:get_app().exit();return
|
|
80
79
|
if B.strip()in['/clear','clear']:A.clear();return
|
|
81
|
-
A.spinner.start();A.
|
|
80
|
+
A.spinner.start();A.b('● user',f"● {B}",style='light_salmon3');await asyncio.sleep(.05)
|
|
82
81
|
if B.strip()in['/commands']and A.instruction_manager:
|
|
83
82
|
E=[]
|
|
84
83
|
for F in A.instruction_manager.list_instructions():E.append(f"/{F.name}: - {F.settings[_D]}")
|
|
85
|
-
K='\n'.join(E);A.
|
|
84
|
+
K='\n'.join(E);A.b('● bot',K,style=D,markdown=_B);return
|
|
86
85
|
if A.instruction_manager:G=A.instruction_manager.parse(B);H,I=G['executed_instruction'],G['message'];B=f"""
|
|
87
86
|
[注意]: 执行用户请求必须严格遵循如下准则:
|
|
88
87
|
{H}
|
|
@@ -90,7 +89,7 @@ class LiveChatUI:
|
|
|
90
89
|
用户请求:
|
|
91
90
|
{I}"""if H else I
|
|
92
91
|
C=A.context if C is _A else C;L=await A._handle_stream('○ bot',A._stream_generate(B,C),style=D,markdown=_B,context=C);A.spinner.stop();A.c(spinner='',status='等待输入',tokens=A.token_count);A.app.layout.focus(A.input_box);return L
|
|
93
|
-
def
|
|
92
|
+
def b(A,sender,message,style='green',markdown=_C):D=markdown;C=style;B=message;E=Markdown(B)if D else Text(B,style=C);F=render_panel(sender,E,C,D);A.log_control.append_text(F);A.app.invalidate()
|
|
94
93
|
async def _stream_generate(A,prompt,context=_A):
|
|
95
94
|
B=A.agent.astream({_G:[HumanMessage(content=prompt)]},config={_I:{_J:A.thread_id}},stream_mode=[_G,_K,_L],context=context);A.cancel_event=asyncio.Event()
|
|
96
95
|
async for C in astream_handler(B,interrupt_tools=A.interrupt_tools,tool_mode=A.toolcall_mode):
|
|
@@ -102,52 +101,49 @@ class LiveChatUI:
|
|
|
102
101
|
if A.cancel_event and A.cancel_event.is_set():A.cancel_event.clear();A.cancel_event=_A
|
|
103
102
|
yield C
|
|
104
103
|
async def _handle_stream(D,sender,stream,style='green',markdown=_C,items=_A,context=_A):
|
|
105
|
-
m='value';l='parent_id';i=context;h='label';g='tool_result';f='text';e='thinking';c=markdown;b=style;a=sender;Z='success';Y='tool_call';
|
|
104
|
+
m='value';l='parent_id';i=context;h='label';g='tool_result';f='text';e='thinking';c=markdown;b=style;a=sender;Z='success';Y='tool_call';Q='args';P='name';I='type';G='content';A=items;R,U='','';t=A is not _A
|
|
106
105
|
if A is _A:D.log_control.append_text(render_panel(a,'',b,c))
|
|
107
106
|
A=A if A else[]
|
|
108
107
|
async for B in stream:
|
|
109
|
-
if B[
|
|
110
|
-
if len(A)>0 and A[-1][1]==e:
|
|
111
|
-
else:
|
|
112
|
-
elif B[
|
|
113
|
-
if len(A)>0 and A[-1][1]==f:
|
|
114
|
-
else:
|
|
115
|
-
elif B[
|
|
116
|
-
elif B[
|
|
117
|
-
|
|
118
|
-
if
|
|
108
|
+
if B[I]==e:
|
|
109
|
+
if len(A)>0 and A[-1][1]==e:U+=B[G];J=markdown_to_wrapped_text(U);A[-1][0]=J
|
|
110
|
+
else:U=B[G];J=markdown_to_wrapped_text(U);A.append([J,e,_A,B,[]])
|
|
111
|
+
elif B[I]==f:
|
|
112
|
+
if len(A)>0 and A[-1][1]==f:R+=B[G];J=markdown_to_wrapped_text(f"{R}");A[-1][0]=J
|
|
113
|
+
else:R=B[G];J=markdown_to_wrapped_text(f"{R}");A.append([J,f,_A,B,[]])
|
|
114
|
+
elif B[I]=='token_usage':u=B['input_toks'];v=B['output_toks'];n=B['total_toks'];D.token_count=n
|
|
115
|
+
elif B[I]==Y:
|
|
116
|
+
V=B[P];W=B[Q];H=B['id'];K=B[l]
|
|
117
|
+
if K is _A:F=display_tool_call(V,W,[]);A.append([F,Y,H,B,[]]);A.append([Text('',''),'margin',_A,_A,[]])
|
|
119
118
|
else:
|
|
120
|
-
C=index_(A,lambda x:x[2]==
|
|
119
|
+
C=index_(A,lambda x:x[2]==K and x[1]==Y)
|
|
121
120
|
if C is _A:continue
|
|
122
|
-
E=A[C];S=E[3][
|
|
123
|
-
elif B[
|
|
124
|
-
|
|
125
|
-
try:L=index_(A,lambda x:x[2]==H);o=A[L][3]
|
|
126
|
-
except Exception:D.log_control.append_text(f"[DEBUG] tool_result: {R}, {H}, {I}")
|
|
127
|
-
W=o[P];X=B[Z]
|
|
121
|
+
E=A[C];S=E[3][P];T=E[3][Q];A[C][-1].append([_A,Y,H,B,[]]);F=display_tool_call(S,T,A[C][-1]);A[C][0]=F
|
|
122
|
+
elif B[I]==g:
|
|
123
|
+
V=B[P];H=B['id'];K=B[l];X=B[Z]
|
|
128
124
|
if X==Z:
|
|
129
|
-
if
|
|
125
|
+
if K is _A:O=B[G];L=index_(A,lambda x:x[2]==H);o=A[L][3];W=o[Q];F=display_tool_result(V,W,O,A[L][-1]);A[L]=[F,g,H,B,A[L][-1]]
|
|
130
126
|
else:
|
|
131
|
-
C=index_(A,lambda x:x[2]==
|
|
127
|
+
C=index_(A,lambda x:x[2]==K)
|
|
132
128
|
if C is _A:continue
|
|
133
|
-
E=A[C];S=E[3][
|
|
129
|
+
E=A[C];S=E[3][P];T=E[3][Q];M=index_(E[-1],lambda x:x[2]==H)
|
|
134
130
|
if M is _A:continue
|
|
135
|
-
N=E[-1][M];N[3][Z]=X;N[3][G]=B[G];A[C][-1][M]=N;F=display_tool_result(S,T,
|
|
131
|
+
N=E[-1][M];N[3][Z]=X;N[3][G]=B[G];A[C][-1][M]=N;O='';F=display_tool_result(S,T,O,A[C][-1]);A[C][0]=F
|
|
136
132
|
elif X=='error':
|
|
137
|
-
if
|
|
133
|
+
if K is _A:O=B[G];F=display_tool_error(V,W,O,A[L][-1]);A[L]=[F,g,H,B,A[L][-1]]
|
|
138
134
|
else:
|
|
139
|
-
C=index_(A,lambda x:x[2]==
|
|
135
|
+
C=index_(A,lambda x:x[2]==K)
|
|
140
136
|
if C is _A:continue
|
|
141
|
-
E=A[C];S=E[3][
|
|
137
|
+
E=A[C];S=E[3][P];T=E[3][Q];M=index_(E[-1],lambda x:x[2]==H)
|
|
142
138
|
if M is _A:continue
|
|
143
|
-
N=E[-1][M];N[Z]=X;N[G]=B[G];A[C][-1][M]=N;F=display_tool_error(S,T,
|
|
144
|
-
elif B[
|
|
145
|
-
elif B[
|
|
139
|
+
N=E[-1][M];N[Z]=X;N[G]=B[G];A[C][-1][M]=N;F=display_tool_error(S,T,O,A[C][-1]);A[C][0]=F
|
|
140
|
+
elif B[I]=='done':0
|
|
141
|
+
elif B[I]=='interrupt':
|
|
146
142
|
D.spinner.stop();j=[];p=B['interrupt_id']
|
|
147
|
-
for d in B[m][m]['action_requests']:w=d[
|
|
143
|
+
for d in B[m][m]['action_requests']:w=d[P];x=d[Q];y=d[_D];q=await D._handle_human_interrupt(message=f" 允许执行当前函数么? ",options=[{h:'是的,允许当前函数执行',_D:''},{h:'是的,总是允许执行,当前对话过程中不再提示',_D:''},{h:'不, 不允许当前函数执行',_D:''}]);k=['approve',_E,'reject'][q];D.toolcall_mode=_E if k==_E else'manual';j.append({I:k})
|
|
148
144
|
D.spinner.start();await D._handle_stream(a,D._resume_generate(p,j,i),style=b,markdown=c,items=A,context=i);break
|
|
149
145
|
r=Columns([A[0]for A in A],column_first=_B,expand=_B);D.log_control.update_last(render_panel(a,r,b,c));D.app.invalidate();await asyncio.sleep(.03);s='';D.c(spinner=s,status='正在生成 ...',tokens=D.token_count)
|
|
150
|
-
return
|
|
146
|
+
return R
|
|
151
147
|
async def _handle_human_interrupt(A,message,options):
|
|
152
148
|
E=asyncio.get_event_loop();C=E.create_future();D=A.app.key_bindings
|
|
153
149
|
def F(index):
|