@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.
@@ -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 cm(model):
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.cl(B,N)for B in B];A.trigger=I;G=I
48
- else:J=A.cl(B,N);A.trigger=J;G=[J]
49
- A._trigger_conditions=G;A.keep=A.cl(F,'keep')
50
- if H is count_tokens_approximately:A.token_counter=cm(A.model)
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.cn()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 ck(B,state):A=state.get(_G,_F);return bool(A)
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.cp(B);E=A.token_counter(B);F=A.ck(C);G=A.co(B,E)
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.cg(B)
60
+ D=A.cn(B)
61
61
  if D<=0:return
62
- H,I=A.cf(B,D);J=A.cj(H);K=A.ch(J);return{_B:[RemoveMessage(id=REMOVE_ALL_MESSAGES),*K,*I],_G:_F}
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.cp(B);E=A.token_counter(B);F=A.ck(C);G=A.co(B,E)
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.cg(B)
67
+ D=A.cn(B)
68
68
  if D<=0:return
69
- H,I=A.cf(B,D);J=await A._acreate_summary(H);K=A.ch(J);return{_B:[RemoveMessage(id=REMOVE_ALL_MESSAGES),*K,*I],_G:_F}
70
- def cr(B,messages,threshold):
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.cr(E,C):return _C
80
+ if B==_E and A.cs(E,C):return _C
81
81
  if B==_D:
82
- G=A.cn()
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.cr(E,D):return _C
87
+ if A.cs(E,D):return _C
88
88
  return _F
89
- def cg(A,messages):
89
+ def cn(A,messages):
90
90
  B=messages;D,E=A.keep
91
91
  if D in{_E,_D}:
92
- C=A.cs(B)
92
+ C=A.cf(B)
93
93
  if C is not _A:return C
94
- return A.ci(B,_DEFAULT_MESSAGES_TO_KEEP)
95
- return A.ci(B,cast('int',E))
96
- def cs(C,messages):
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.cn()
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.cq(A,B)
118
- def cn(C):
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 cl(context,parameter_name):
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 ch(summary):return[HumanMessage(content=f"Here is a summary of the conversation to date:\n\n{summary}",additional_kwargs={'lc_source':'summarization'})]
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 cp(messages):
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 cf(conversation_messages,cutoff_index):B=cutoff_index;A=conversation_messages;C=A[:B];D=A[B:];return C,D
142
- def ci(C,messages,messages_to_keep):
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.cq(A,D)
145
+ D=len(A)-B;return C.ct(A,D)
146
146
  @staticmethod
147
- def cq(messages,cutoff_index):
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 cj(A,messages_to_sumarize):
161
+ def cm(A,messages_to_sumarize):
162
162
  B=messages_to_sumarize
163
163
  if not B:return _H
164
- C=A.ct(B)
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.ct(B)
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 ct(A,messages):
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 agent 进行处理。\n 2.4 如果任务需要协调多个团队成员合作完成,则将项目目标分解为可管理的工作包,并明确任务间的依赖关系和执行顺序。\n 3. **团队角色分配**:根据任务特性和团队成员能力,合理分配团队成员的工作职责。\n 4. **协作流程设计**:设计高效的团队协作流程,明确沟通机制和决策流程。\n 5. **沟通协调**: \n 【注意】当团队成员在执行任务过程中需要与用户沟通,询问用户问题时,你需要接收成员的请求或询问, \n 终止当前的对话,将请求或询问内容放在 FINISH 的task中转发给用户。此时严禁自行回复成员或者分发任务。\n\n\n\n\n## 工作方式\n 你在指挥团队完成任务时需要严格遵循以下INSTRUCTION指令,不要执行超出指令范围之外的工作。\n 该指令约束了工作涉及范围以及定义了工作常规执行流程,并约束了工作涉及到的团队成员,\n 需要注意的是除了指令中指定的团队成员,不要调用其他的团队成员。\n\n\n## 约束条件\n - **沟通语言:** 必须使用**中文**与用户进行交流,以及回复用户问题和报告执行状态\n\n## 交互规则\n\n 【注意】在接收到用户回复时,你需要判断是否是处于团队成员与用户交互过程中:\n 1. 如果是,你直接将用户的回复转发给团队成员,无需额外处理和任务分解。\n 2. 如果不是,你需要根据用户的问题进行任务分解和分配。\n\n 【注意】如果团队成员回复内容为询问,提问的形式,你如果不能给出答案,则向用户询问。\n\n'
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.ba(A)
20
- if B.bi(C):return ContentType.JSON
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.bi(A):return ContentType.JSON
24
- if B.bh(A):return ContentType.ERROR
25
- if B.bd(A):return ContentType.MARKDOWN
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.bb,ContentType.ERROR:A.bj,ContentType.JSON:A.be,ContentType.MARKDOWN:A.bf,ContentType.TEXT:A.bc};F=E.get(C,A.bc);G=F(name,B,max_length);return FormattedResult(content_type=C,elements=G,success=D)
29
- def ba(B,content):A=content.split('\n',2);return A[2].strip()if len(A)>2 else''
30
- def bi(B,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.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 bh(B,content):A=['Traceback (most recent call last)','Exception:','Error:'];return any(A in content for A in A)
38
- def bd(C,content):A=content;B=['```','**','##','- **'];return A.startswith('#')or any(B in A for B in B)
39
- def bb(B,name,content,max_length):A='green';C=B.bg(content,max_length);return[Panel(Text(C,style=A),title=f"📤 {name} ✓",border_style=A)]
40
- def bj(B,name,content,max_length):A='red';C=B.bg(content,max_length);return[Panel(Text(C,style=A),title=f"📤 {name} ✗",border_style=A)]
41
- def be(B,name,content,max_length):
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.ba(A)
44
- try:F=json.loads(E);C=json.dumps(F,indent=2,ensure_ascii=_A);C=B.bg(C,D);return[Text(f"📤 {name} ✓",style=_B),Syntax(C,'json',theme='monokai',line_numbers=_A)]
45
- except(json.JSONDecodeError,ValueError):return B.bc(name,A,D)
46
- def bf(A,name,content,max_length):B=A.bg(content,max_length);return[Panel(Markdown(B),title=f"📤 {name}",border_style='cyan dim')]
47
- def bc(A,name,content,max_length):B=A.bg(content,max_length);return[Text(f"📤 {name}:",style=_B),Text(f" {B}",style='dim')]
48
- def bg(A,content,max_length):return truncate(content,max_length)
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=False
16
- _M='text'
17
- _L='value'
18
- _K='__interrupt__'
19
- _J='auto'
20
- _I='status'
21
- _H='updates'
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=_N):
38
- E=detail
39
- async for H in stream:
40
- if not isinstance(H,tuple)or len(H)!=2:continue
41
- F,G=H
42
- if F!=_H:print(f"[STREAM_MODE]: {F} {G}")
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(M,C)in G.items():
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];I=B[_D];K=B[_F]
52
- if E:print(f"[TOOL_CALL]: {D} \nargs: ({I}) ")
53
- if isinstance(A,AIMessage)and A.content and A.content!='':print(f"[AIMESSAGE]: {A.content}")
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;J=A.content;K=A.tool_call_id
56
- if E:print(f"[TOOL_RESULT]: {D} \nresult: {J}")
57
- elif F==_O:
58
- B=json.loads(G);L=getattr(B,_G,'');N=getattr(B,_P,'');K=getattr(B,_Q,'');D=getattr(B,_B,'');I=getattr(B,_D,'');J=getattr(B,_R,'');O=getattr(B,_I,_S)
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: ({I}) ")
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: {J}")
63
- async def astream_handler(stream,debug=_N,interrupt_tools=[],tool_mode=_J):
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==_H:
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!=_J and E.strip().lower()in P:continue
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 _K in D:
82
- K=D[_K]
80
+ if _J in D:
81
+ K=D[_J]
83
82
  if K:
84
- e=_a;X=list(map(lambda interrupt:{_V:interrupt.id,_L:interrupt.value},K))
85
- for O in X:Y='hitl';Z=O[_V];a=O[_L];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
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==_M:S+=A.data.get(_E,'')
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(_I,_S)
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=_N,interrupts=[],tool_mode=_J):
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==_H:
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!=_J and F.strip().lower()in G:continue
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 _K in C:
119
- G=C[_K]
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,_L:interrupt.value,R:'ask_human'},G))
122
- for O in U:d=O[R];V=O[_L][_G];emitter.interrupt(event_name='interrupt',event_type=V,event_data=O);break
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==_M:S+=A.data.get(_E,'')
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,_I,_S)
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==_M:
160
- K=A.get(_M)or A.get(_E)or''
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,_I,_W);C=B[:DisplayLimits.TOOL_RESULT_MAX]
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 bo(A.tool_calls,E,J):
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 bp(A,E,J):
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 bo(A.tool_calls,E,J):
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 bp(A,E,J):
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 bq(block,emitter,tracker):
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 bo(tool_calls,emitter,tracker):
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 bp(chunk,emitter,tracker):
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(file_path, cwd)
448
+ original_path, path = container2local(path, cwd)
449
449
  search_path = resolve_path(path, cwd)
450
450
  else:
451
- search_path = resolve_path(file_path, cwd)
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
- b=load_dotenv(find_dotenv())
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(b);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'});I=create_output();A.app=Application(layout=A.layout,key_bindings=A.kb,style=A.style,output=I,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'
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.d('● user',f"● {B}",style='light_salmon3');await asyncio.sleep(.05)
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.d('● bot',K,style=D,markdown=_B);return
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 d(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()
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';P='args';O='name';J='type';G='content';A=items;Q,V='','';t=A is not _A
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[J]==e:
110
- if len(A)>0 and A[-1][1]==e:V+=B[G];K=markdown_to_wrapped_text(V);A[-1][0]=K
111
- else:V=B[G];K=markdown_to_wrapped_text(V);A.append([K,e,_A,B,[]])
112
- elif B[J]==f:
113
- if len(A)>0 and A[-1][1]==f:Q+=B[G];K=markdown_to_wrapped_text(f"{Q}");A[-1][0]=K
114
- else:Q=B[G];K=markdown_to_wrapped_text(f"{Q}");A.append([K,f,_A,B,[]])
115
- elif B[J]=='token_usage':u=B['input_toks'];v=B['output_toks'];n=B['total_toks'];D.token_count=n
116
- elif B[J]==Y:
117
- R=B[O];W=B[P];H=B['id'];I=B[l]
118
- if I is _A:F=display_tool_call(R,W,[]);A.append([F,Y,H,B,[]]);A.append([Text('',''),'margin',_A,_A,[]])
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]==I and x[1]==Y)
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][O];T=E[3][P];A[C][-1].append([_A,Y,H,B,[]]);F=display_tool_call(S,T,A[C][-1]);A[C][0]=F
123
- elif B[J]==g:
124
- R=B[O];H=B['id'];I=B[l]
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 I is _A:U=B[G];F=display_tool_result(R,W,U,A[L][-1]);A[L]=[F,g,H,B,A[L][-1]]
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]==I)
127
+ C=index_(A,lambda x:x[2]==K)
132
128
  if C is _A:continue
133
- E=A[C];S=E[3][O];T=E[3][P];M=index_(E[-1],lambda x:x[2]==H)
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,U,A[C][-1]);A[C][0]=F
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 I is _A:U=B[G];F=display_tool_error(R,W,U,A[L][-1]);A[L]=[F,g,H,B,A[L][-1]]
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]==I)
135
+ C=index_(A,lambda x:x[2]==K)
140
136
  if C is _A:continue
141
- E=A[C];S=E[3][O];T=E[3][P];M=index_(E[-1],lambda x:x[2]==H)
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,U,A[C][-1]);A[C][0]=F
144
- elif B[J]=='done':0
145
- elif B[J]=='interrupt':
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[O];x=d[P];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({J:k})
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 Q
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):