@brandon_9527/tcode 1.0.7 → 1.0.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (59) hide show
  1. package/dist/python-src/.env +5 -3
  2. package/dist/python-src/README.md +40 -1
  3. package/dist/python-src/_workspace/.autodev/config.json +12 -0
  4. package/dist/python-src/_workspace/.autodev/cron/jobs.json +4 -0
  5. package/dist/python-src/entry.py +35 -1
  6. package/dist/python-src/main.py +753 -40
  7. package/dist/python-src/pyproject.toml +1 -0
  8. package/dist/python-src/run.sh +9 -0
  9. package/dist/python-src/src/agents/token_tracker.py +4 -4
  10. package/dist/python-src/src/claw/bus/queue.py +1 -1
  11. package/dist/python-src/src/claw/channels/__init__.py +2 -2
  12. package/dist/python-src/src/claw/channels/base.py +2 -2
  13. package/dist/python-src/src/claw/channels/feishu.py +57 -16
  14. package/dist/python-src/src/claw/channels/manager.py +2 -2
  15. package/dist/python-src/src/claw/config/__init__.py +3 -0
  16. package/dist/python-src/src/claw/config/loader.py +38 -0
  17. package/dist/python-src/src/claw/config/schema.py +14 -29
  18. package/dist/python-src/src/claw/cron/__init__.py +3 -0
  19. package/dist/python-src/src/claw/cron/service.py +171 -0
  20. package/dist/python-src/src/claw/cron/types_.py +14 -0
  21. package/dist/python-src/src/claw/heartbeat/__init__.py +2 -0
  22. package/dist/python-src/src/claw/heartbeat/service.py +55 -0
  23. package/dist/python-src/src/claw/run.py +82 -0
  24. package/dist/python-src/src/claw/tools/base.py +23 -0
  25. package/dist/python-src/src/claw/tools/channel.py +0 -0
  26. package/dist/python-src/src/claw/tools/cron.py +138 -0
  27. package/dist/python-src/src/claw/utils/__init__.py +2 -0
  28. package/dist/python-src/src/claw/utils/helpers.py +27 -0
  29. package/dist/python-src/src/core/context.py +158 -0
  30. package/dist/python-src/src/core/deepagents.py +5 -5
  31. package/dist/python-src/src/managers/manager_agent.py +9 -9
  32. package/dist/python-src/src/managers/manager_command.py +62 -0
  33. package/dist/python-src/src/managers/manager_context.py +1 -1
  34. package/dist/python-src/src/managers/manager_instruction.py +7 -7
  35. package/dist/python-src/src/managers/manager_skill.py +3 -3
  36. package/dist/python-src/src/managers/sandbox.py +3 -3
  37. package/dist/python-src/src/middlewares/dynamic_content.py +2 -2
  38. package/dist/python-src/src/middlewares/hitl.py +3 -3
  39. package/dist/python-src/src/middlewares/memory.py +2 -2
  40. package/dist/python-src/src/middlewares/subagents.py +4 -4
  41. package/dist/python-src/src/middlewares/summary.py +37 -37
  42. package/dist/python-src/src/stream/file_write_parser.py +3 -3
  43. package/dist/python-src/src/stream/formatter.py +19 -19
  44. package/dist/python-src/src/stream/handler.py +4 -4
  45. package/dist/python-src/src/stream/handler_with_tracker.py +10 -10
  46. package/dist/python-src/src/trackers/token/pricing.py +2 -2
  47. package/dist/python-src/src/trackers/token/report.py +4 -4
  48. package/dist/python-src/src/trackers/token/tracker.py +8 -8
  49. package/dist/python-src/src/tui/chatui.py +10 -10
  50. package/dist/python-src/src/tui/clawtui.py +224 -0
  51. package/dist/python-src/src/tui/commands/__init__.py +3 -0
  52. package/dist/python-src/src/tui/commands/base.py +6 -0
  53. package/dist/python-src/src/tui/commands/instruction.py +5 -0
  54. package/dist/python-src/src/tui/components/tlist.py +7 -7
  55. package/dist/python-src/src/tui/components/tscroll_panel.py +73 -44
  56. package/dist/python-src/src/tui/components/tscroll_panel_old.py +58 -0
  57. package/dist/python-src/src/tui/utils/trender.py +21 -21
  58. package/dist/python-src/uv.lock +1969 -1958
  59. package/package.json +1 -1
@@ -39,7 +39,7 @@ class InstructionManager:
39
39
  if A.project_commands_dir and not os.path.exists(A.project_commands_dir):os.makedirs(A.project_commands_dir)
40
40
  if not os.path.exists(A.user_dir):os.makedirs(A.user_dir)
41
41
  A.reload_instructions()
42
- def bx(O,dir_path,source):
42
+ def cr(O,dir_path,source):
43
43
  F=source;C=dir_path;A={}
44
44
  if not os.path.exists(C):return A
45
45
  for(G,P,I)in os.walk(C):
@@ -54,9 +54,9 @@ class InstructionManager:
54
54
  except Exception as N:print(f"从 {F} 加载命令 {B}/{E} 失败: {str(N)}")
55
55
  return A
56
56
  def reload_instructions(A):
57
- A.instructions[_B].clear();A.instructions[_C].clear();A.instructions[_C]=A.bx(A.user_dir,_C)
58
- if A.project_commands_dir:A.instructions[_B]=A.bx(A.project_commands_dir,_B)
59
- def by(A,source,domain,name):
57
+ A.instructions[_B].clear();A.instructions[_C].clear();A.instructions[_C]=A.cr(A.user_dir,_C)
58
+ if A.project_commands_dir:A.instructions[_B]=A.cr(A.project_commands_dir,_B)
59
+ def cs(A,source,domain,name):
60
60
  B=source
61
61
  if B==_B:
62
62
  if not A.project_commands_dir:raise ValueError('项目目录未初始化,无法操作project来源的指令')
@@ -79,7 +79,7 @@ class InstructionManager:
79
79
  def add_instruction(B,domain,name,settings,content,source=_B):
80
80
  E=source;D=name;C=domain
81
81
  if B.exists(C,D,E):raise ValueError(f"指令 '{E}/{C}/{D}' 已存在")
82
- G=B.by(E,C,D);os.makedirs(os.path.dirname(G),exist_ok=_G)
82
+ G=B.cs(E,C,D);os.makedirs(os.path.dirname(G),exist_ok=_G)
83
83
  with open(G,'w',encoding=_I)as A:
84
84
  A.write('---\n')
85
85
  for(H,F)in settings.items():
@@ -98,7 +98,7 @@ class InstructionManager:
98
98
  D=C.instructions[G][A][B]
99
99
  if I is not _A:D.settings=I
100
100
  if J is not _A:D.original_content=J
101
- K=C.by(G,A,B);os.makedirs(os.path.dirname(K),exist_ok=_G)
101
+ K=C.cs(G,A,B);os.makedirs(os.path.dirname(K),exist_ok=_G)
102
102
  with open(K,'w',encoding=_I)as E:
103
103
  E.write('---\n')
104
104
  for(L,H)in D.settings.items():
@@ -114,7 +114,7 @@ class InstructionManager:
114
114
  else:
115
115
  G,E=C.get_instruction_with_source(A,B)
116
116
  if not E:raise ValueError(f"指令 '{A}/{B}' 不存在于任何来源")
117
- F=C.by(E,A,B)
117
+ F=C.cs(E,A,B)
118
118
  if os.path.exists(F):os.remove(F)
119
119
  C.reload_instructions()
120
120
  def load_all_instructions(A,domain=_D,source=_A):
@@ -13,13 +13,13 @@ class Skill:
13
13
  def from_skill_md(C,skill_md_path,location=_A):
14
14
  B=skill_md_path
15
15
  if not B.exists():return
16
- F=B.read_text(encoding='utf-8');A,G=C.br(F)
16
+ F=B.read_text(encoding='utf-8');A,G=C.cl(F)
17
17
  if not A:return
18
18
  D=A.get('name');E=A.get('description')
19
19
  if not D or not E:return
20
20
  return C(name=D,description=E,content=G.strip(),path=B.parent,license=A.get('license'),allowed_tools=A.get('allowed-tools',[])or[],metadata=A.get('metadata',{})or{},location=location)
21
21
  @staticmethod
22
- def br(content):
22
+ def cl(content):
23
23
  A=content;C='^---\\s*\\n(.*?)\\n---\\s*\\n(.*)$';B=re.match(C,A,re.DOTALL)
24
24
  if not B:return{},A
25
25
  D=B.group(1);E=B.group(2)
@@ -60,7 +60,7 @@ class SkillManager:
60
60
  When users ask you to perform tasks, check if any of the available skills below can help complete the task more effectively. Skills provide specialized capabilities and domain knowledge.
61
61
 
62
62
  How to use skills:
63
- - Invoke skills using skill tool with the skill name only (no arguments)
63
+ - Invoke skills using Skill tool with the skill name only (no arguments)
64
64
  - When you invoke a skill, you will see <command-message>The "{{name}}" skill is loading</command-message>
65
65
  - The skill\'s prompt will expand and provide detailed instructions on how to complete the task
66
66
  - Base directory provided in output for resolving bundled resources (references/, scripts/, assets/)
@@ -21,14 +21,14 @@ sys.path.append(PROJ_PATH)
21
21
  logger=logging.getLogger(__file__)
22
22
  class Container:
23
23
  def __init__(A,user_id=_C,workspace_path=_B,dockerfile_path='.',image_name=_J,container_basename='sandbox'):A.client=docker.from_env();A.container_basename=container_basename;A.workspace_path=workspace_path;A.dockerfile_path=dockerfile_path;A.image_name=image_name;A.sock=_B;A.queue=_B;A.container=_B;A.user_id=user_id;A.images={_G:'ubuntu:22.04',_H:'python:3.12-slim','node':'node:20-bullseye-slim',_C:_J}
24
- def bs(A):
24
+ def cm(A):
25
25
  try:
26
26
  B=A.client.images.get(A.image_name)
27
27
  if B:logger.info(f"Image {A.image_name} exists.");return _A
28
28
  else:logger.info(f"Image {A.image_name} not exists.");return _E
29
29
  except docker.errors.ImageNotFound:logger.error(f"Image {A.image_name} not exists.");return _E
30
30
  except Exception as C:logger.error(f"检查镜像时出错: {str(C)}");return _E
31
- def bt(B):
31
+ def cn(B):
32
32
  F='arm64';print('🔧 Building Docker image...')
33
33
  try:
34
34
  C=platform.machine()
@@ -44,7 +44,7 @@ class Container:
44
44
  except Exception as D:logger.error(f"An unexpected error occurred: {D}");raise
45
45
  def get_or_create_container(A,container_name,image_type=_C):
46
46
  E='running';C=image_type;B=container_name;D=_F;F=A.workspace_path;B=f"{A.container_basename}_{A.user_id}"
47
- if C==_C and not A.bs():A.bt()
47
+ if C==_C and not A.cm():A.cn()
48
48
  try:
49
49
  A.container=A.client.containers.get(B)
50
50
  if A.container.status==E:logger.info(f"容器 {B} 已存在且运行中,直接复用")
@@ -23,7 +23,7 @@ SECURITY_SYSTEM_PROMPT=' \n<security_rules>\n {security_rules}\n</security_rule
23
23
  class DynamicContentMiddleware(AgentMiddleware[DynamicContentState,ContextT,ResponseT]):
24
24
  state_schema=DynamicContentState
25
25
  def __init__(A,*,home_path=os.path.expanduser('~'),project_path=os.getcwd()):A.home_path=home_path;A.project_path=project_path
26
- def cv(G,prompt,content_type,contents):
26
+ def dp(G,prompt,content_type,contents):
27
27
  E='( No rules loaded)';C=contents;B=content_type;A=prompt
28
28
  if not C:return A.format(**{B:E})
29
29
  D=[f"{A}\n{C[A]}"for A in C]
@@ -58,7 +58,7 @@ class DynamicContentMiddleware(AgentMiddleware[DynamicContentState,ContextT,Resp
58
58
  with open(H,_A,encoding=_B)as B:F[H]=B.read()
59
59
  return DynamicContentUpdate(review_contents=C,security_contents=F)
60
60
  def modify_request(C,request):
61
- B=request;F=B.state.get(_C,{});G=B.state.get(_D,{});D=C.cv(REVIEW_SYSTEM_PROMPT,'review_rules',F);E=C.cv(SECURITY_SYSTEM_PROMPT,'security_rules',G);A=B.system_message
61
+ B=request;F=B.state.get(_C,{});G=B.state.get(_D,{});D=C.dp(REVIEW_SYSTEM_PROMPT,'review_rules',F);E=C.dp(SECURITY_SYSTEM_PROMPT,'security_rules',G);A=B.system_message
62
62
  if D:A=append_to_system_message(A,D)
63
63
  if E:A=append_to_system_message(A,E)
64
64
  return B.override(system_message=A)
@@ -21,12 +21,12 @@ class ReviewConfig(TypedDict):action_name:str;allowed_decisions:list[DecisionTyp
21
21
  class HITLRequest(TypedDict):action_requests:list[ActionRequest];review_configs:list[ReviewConfig]
22
22
  class HumanInTheLoopMiddleware(AgentMiddleware[StateT,ContextT]):
23
23
  def __init__(A,interrupt_on=[],*,description_perfix='Tool execution requires approval'):super().__init__();A.interrupt_on=interrupt_on;A.description_prefix=description_perfix
24
- def ce(D,tool_call):A=tool_call;B=A[_E];C=A[_F];E=A['id'];F=f"{D.description_prefix}\n\nTool: {B}\nArgs: {C}";G=ActionRequest(name=B,id=E,args=C,description=F);H=ReviewConfig(action_name=B,allowed_decisions=[_B,_A,_C]);return G,H
24
+ def cy(D,tool_call):A=tool_call;B=A[_E];C=A[_F];E=A['id'];F=f"{D.description_prefix}\n\nTool: {B}\nArgs: {C}";G=ActionRequest(name=B,id=E,args=C,description=F);H=ReviewConfig(action_name=B,allowed_decisions=[_B,_A,_C]);return G,H
25
25
  async def awrap_tool_call(E,request,handler):
26
26
  C=handler;A=request;F=A.tool_call['id'];D=A.tool_call[_E];R=A.tool_call[_F];G=A.runtime;H=G.context;I=H.tool_mode
27
27
  if I==_A:return await C(A)
28
28
  if len(E.interrupt_on)>0 and not D.strip().lower()in E.interrupt_on:return await C(A)
29
- J,K=E.ce(A.tool_call);L=HITLRequest(action_requests=[J],review_configs=[K]);M=interrupt(L);B=M[_G][0][_D]
29
+ J,K=E.cy(A.tool_call);L=HITLRequest(action_requests=[J],review_configs=[K]);M=interrupt(L);B=M[_G][0][_D]
30
30
  if B==_B:return await C(A)
31
31
  if B==_A:return await C(A)
32
32
  if B==_C:N=f"User rejected the tool call for `{D}` with id {F}";O=ToolMessage(content=N,name=D,tool_call_id=F,status='error');return O
@@ -35,7 +35,7 @@ class HumanInTheLoopMiddleware(AgentMiddleware[StateT,ContextT]):
35
35
  C=handler;A=request;F=A.tool_call['id'];D=A.tool_call[_E];R=A.tool_call[_F];H=A.runtime;I=H.context;J=I.tool_mode
36
36
  if J==_A:return C(A)
37
37
  if len(E.interrupt_on)>0 and not D.strip().lower()in E.interrupt_on:return C(A)
38
- K,L=E.ce(A.tool_call);M=HITLRequest(action_requests=[K],review_configs=[L]);G=interrupt(M);print(f"[HITL] -> Human decision: {G}");B=G[_G][0][_D]
38
+ K,L=E.cy(A.tool_call);M=HITLRequest(action_requests=[K],review_configs=[L]);G=interrupt(M);print(f"[HITL] -> Human decision: {G}");B=G[_G][0][_D]
39
39
  if B==_B:return C(A)
40
40
  if B==_A:return C(A)
41
41
  if B==_C:N=f"User rejected the tool call for `{D}` with id {F}";O=ToolMessage(content=N,name=D,tool_call_id=F,status='error');return O
@@ -14,7 +14,7 @@ MEMORY_SYSTEM_PROMPT=' \n<agent_memory>\n{agent_memory}\n</agent_memory>\n\n<mem
14
14
  class MemoryMiddleware(AgentMiddleware[MemoryState,ContextT,ResponseT]):
15
15
  state_schema=MemoryState
16
16
  def __init__(A,*,home_path=os.path.expanduser('~'),project_path=os.getcwd()):A.home_path=home_path;A.project_path=project_path
17
- def cf(D,contents):
17
+ def cz(D,contents):
18
18
  C='(No memory loaded)';A=contents
19
19
  if not A:return MEMORY_SYSTEM_PROMPT.format(agent_memory=C)
20
20
  B=[f"{B}\n{A[B]}"for B in D.sources if A.get(B)]
@@ -39,6 +39,6 @@ class MemoryMiddleware(AgentMiddleware[MemoryState,ContextT,ResponseT]):
39
39
  H=f"Failed to download {C}: {A.error}";raise ValueError(H)
40
40
  if A.content is not None:E[C]=A.content.decode(_B);logger.debug('Loaded memory from: %s',C)
41
41
  return MemoryStateUpdate(memory_contents=E)
42
- def modify_request(B,request):A=request;C=A.state.get(_A,{});D=B.cf(C);E=append_to_system_message(A.system_message,D);return A.override(system_message=E)
42
+ def modify_request(B,request):A=request;C=A.state.get(_A,{});D=B.cz(C);E=append_to_system_message(A.system_message,D);return A.override(system_message=E)
43
43
  def wrap_model_call(A,request,handler):B=A.modify_request(request);return handler(B)
44
44
  async def awrap_model_call(A,request,handler):B=A.modify_request(request);return await handler(B)
@@ -34,7 +34,7 @@ _EXCLUDED_STATE_KEYS=_B,'todos'
34
34
  TASK_TOOL_DESCRIPTION='Launch an ephemeral subagent to handle complex, multi-step independent tasks with isolated context windows.\n\nAvailable agent types and the tools they have access to:\n{available_agents}\n\nWhen using the Task tool, you must specify a subagent_type parameter to select which agent type to use.\n\n## Usage notes:\n1. Launch multiple agents concurrently whenever possible, to maximize performance; to do that, use a single message with multiple tool uses\n2. When the agent is done, it will return a single message back to you. The result returned by the agent is not visible to the user. To show the user the result, you should send a text message back to the user with a concise summary of the result.\n3. Each agent invocation is stateless. You will not be able to send additional messages to the agent, nor will the agent be able to communicate with you outside of its final report. Therefore, your prompt should contain a highly detailed task description for the agent to perform autonomously and you should specify exactly what information the agent should return back to you in its final and only message to you.\n4. The agent\'s outputs should generally be trusted\n5. Clearly tell the agent whether you expect it to create content, perform analysis, or just do research (search, file reads, web fetches, etc.), since it is not aware of the user\'s intent\n6. If the agent description mentions that it should be used proactively, then you should try your best to use it without the user having to ask for it first. Use your judgement.\n7. When only the general-purpose agent is provided, you should use it for all tasks. It is great for isolating context and token usage, and completing specific, complex tasks, as it has all the same capabilities as the main agent.\n\n### Example usage of the general-purpose agent:\n\n<example_agent_descriptions>\n"general-purpose": use this agent for general purpose tasks, it has access to all tools as the main agent.\n</example_agent_descriptions>\n\n<example>\nUser: "I want to conduct research on the accomplishments of Lebron James, Michael Jordan, and Kobe Bryant, and then compare them."\nAssistant: *Uses the task tool in parallel to conduct isolated research on each of the three players*\nAssistant: *Synthesizes the results of the three isolated research tasks and responds to the User*\n<commentary>\nResearch is a complex, multi-step task in it of itself.\nThe research of each individual player is not dependent on the research of the other players.\nThe assistant uses the task tool to break down the complex objective into three isolated tasks.\nEach research task only needs to worry about context and tokens about one player, then returns synthesized information about each player as the Tool Result.\nThis means each research task can dive deep and spend tokens and context deeply researching each player, but the final result is synthesized information, and saves us tokens in the long run when comparing the players to each other.\n</commentary>\n</example>\n\n<example>\nUser: "Analyze a single large code repository for security vulnerabilities and generate a report."\nAssistant: *Launches a single `task` subagent for the repository analysis*\nAssistant: *Receives report and integrates results into final summary*\n<commentary>\nSubagent is used to isolate a large, context-heavy task, even though there is only one. This prevents the main thread from being overloaded with details.\nIf the user then asks followup questions, we have a concise report to reference instead of the entire history of analysis and tool calls, which is good and saves us time and money.\n</commentary>\n</example>\n\n<example>\nUser: "Schedule two meetings for me and prepare agendas for each."\nAssistant: *Calls the task tool in parallel to launch two `task` subagents (one per meeting) to prepare agendas*\nAssistant: *Returns final schedules and agendas*\n<commentary>\nTasks are simple individually, but subagents help silo agenda preparation.\nEach subagent only needs to worry about the agenda for one meeting.\n</commentary>\n</example>\n\n<example>\nUser: "I want to order a pizza from Dominos, order a burger from McDonald\'s, and order a salad from Subway."\nAssistant: *Calls tools directly in parallel to order a pizza from Dominos, a burger from McDonald\'s, and a salad from Subway*\n<commentary>\nThe assistant did not use the task tool because the objective is super simple and clear and only requires a few trivial tool calls.\nIt is better to just complete the task directly and NOT use the `task`tool.\n</commentary>\n</example>\n\n### Example usage with custom agents:\n\n<example_agent_descriptions>\n"content-reviewer": use this agent after you are done creating significant content or documents\n"greeting-responder": use this agent when to respond to user greetings with a friendly joke\n"research-analyst": use this agent to conduct thorough research on complex topics\n</example_agent_description>\n\n<example>\nuser: "Please write a function that checks if a number is prime"\nassistant: Sure let me write a function that checks if a number is prime\nassistant: First let me use the Write tool to write a function that checks if a number is prime\nassistant: I\'m going to use the Write tool to write the following code:\n<code>\nfunction isPrime(n) {{\n if (n <= 1) return false\n for (let i = 2; i * i <= n; i++) {{\n if (n % i === 0) return false\n }}\n return true\n}}\n</code>\n<commentary>\nSince significant content was created and the task was completed, now use the content-reviewer agent to review the work\n</commentary>\nassistant: Now let me use the content-reviewer agent to review the code\nassistant: Uses the Task tool to launch with the content-reviewer agent\n</example>\n\n<example>\nuser: "Can you help me research the environmental impact of different renewable energy sources and create a comprehensive report?"\n<commentary>\nThis is a complex research task that would benefit from using the research-analyst agent to conduct thorough analysis\n</commentary>\nassistant: I\'ll help you research the environmental impact of renewable energy sources. Let me use the research-analyst agent to conduct comprehensive research on this topic.\nassistant: Uses the Task tool to launch with the research-analyst agent, providing detailed instructions about what research to conduct and what format the report should take\n</example>\n\n<example>\nuser: "Hello"\n<commentary>\nSince the user is greeting, use the greeting-responder agent to respond with a friendly joke\n</commentary>\nassistant: "I\'m going to use the Task tool to launch with the greeting-responder agent"\n</example>'
35
35
  TASK_SYSTEM_PROMPT='## `task` (subagent spawner)\n\nYou have access to a `task` tool to launch short-lived subagents that handle isolated tasks. These agents are ephemeral — they live only for the duration of the task and return a single result.\n\nWhen to use the task tool:\n- When a task is complex and multi-step, and can be fully delegated in isolation\n- When a task is independent of other tasks and can run in parallel\n- When a task requires focused reasoning or heavy token/context usage that would bloat the orchestrator thread\n- When sandboxing improves reliability (e.g. code execution, structured searches, data formatting)\n- When you only care about the output of the subagent, and not the intermediate steps (ex. performing a lot of research and then returned a synthesized report, performing a series of computations or lookups to achieve a concise, relevant answer.)\n\nSubagent lifecycle:\n1. **Spawn** → Provide clear role, instructions, and expected output\n2. **Run** → The subagent completes the task autonomously\n3. **Return** → The subagent provides a single structured result\n4. **Reconcile** → Incorporate or synthesize the result into the main thread\n\nWhen NOT to use the task tool:\n- If you need to see the intermediate reasoning or steps after the subagent has completed (the task tool hides them)\n- If the task is trivial (a few tool calls or simple lookup)\n- If delegating does not reduce token usage, complexity, or context switching\n- If splitting would add latency without benefit\n- 用户仅仅时普通寒暄时,不应该使用任务工具。\n\n## Important Task Tool Usage Notes to Remember\n- Whenever possible, parallelize the work that you do. This is true for both tool_calls, and for tasks. Whenever you have independent steps to complete - make tool_calls, or kick off tasks (subagents) in parallel to accomplish them faster. This saves time for the user, which is incredibly important.\n- Remember to use the `task` tool to silo independent tasks within a multi-part objective.\n- You should use the `task` tool whenever you have a complex task that will take multiple steps, and is independent from other tasks that the agent needs to complete. These agents are highly competent and efficient.'
36
36
  DEFAULT_GENERAL_PURPOSE_DESCRIPTION='General-purpose agent for researching complex questions, searching for files and content, and executing multi-step tasks. When you are searching for a keyword or file and are not confident that you will find the right match in the first few tries use this agent to perform the search for you. This agent has access to all tools as the main agent.'
37
- def cc(*,default_model,default_tools,default_middleware,default_interrupt_on,subagents,general_purpose_agent):
37
+ def cv(*,default_model,default_tools,default_middleware,default_interrupt_on,subagents,general_purpose_agent):
38
38
  M='middleware';L='runnable';F=default_tools;E=default_model;C=default_interrupt_on;D=default_middleware or[];B={};G=[]
39
39
  if general_purpose_agent:
40
40
  H=[*D]
@@ -79,8 +79,8 @@ async def astream_handler(stream,runtime):
79
79
  if isinstance(A,AIMessage)and len(A.tool_calls)==0:C=A
80
80
  if isinstance(A,ToolMessage):G=A.name;N=A.content;H=A.tool_call_id;I={_E:'tool_result',_G:D.tool_call_id,_H:H,_I:'',_C:G,_D:{},_J:N,_K:_O};J(json.dumps(I,indent=2,ensure_ascii=_L))
81
81
  O={_B:[C]if C and isinstance(C,AIMessage)else[]};return O
82
- def cd(*,default_model,default_tools,default_middleware,default_interrupt_on,subagents,general_purpose_agent,task_description=_A):
83
- G='Tool call ID is required for subagent invocation';F='custom';A=task_description;B,H=cc(default_model=default_model,default_tools=default_tools,default_middleware=default_middleware,default_interrupt_on=default_interrupt_on,subagents=subagents,general_purpose_agent=general_purpose_agent);C='\n'.join(H)
82
+ def cw(*,default_model,default_tools,default_middleware,default_interrupt_on,subagents,general_purpose_agent,task_description=_A):
83
+ G='Tool call ID is required for subagent invocation';F='custom';A=task_description;B,H=cv(default_model=default_model,default_tools=default_tools,default_middleware=default_middleware,default_interrupt_on=default_interrupt_on,subagents=subagents,general_purpose_agent=general_purpose_agent);C='\n'.join(H)
84
84
  def D(result,tool_call_id):A=result;B={A:B for(A,B)in A.items()if A not in _EXCLUDED_STATE_KEYS};C=A[_B][-1];D=I(C);return Command(update={**B,_B:[ToolMessage(D,tool_call_id=tool_call_id)]})
85
85
  def I(msg):
86
86
  D='text';B=msg.content
@@ -110,7 +110,7 @@ def cd(*,default_model,default_tools,default_middleware,default_interrupt_on,sub
110
110
  return D(L,A.tool_call_id)
111
111
  return StructuredTool.from_function(name='task',func=J,coroutine=K,description=A)
112
112
  class SubAgentMiddleware(AgentMiddleware):
113
- def __init__(A,*,default_model,default_tools=_A,default_middleware=_A,default_interrupt_on=_A,subagents=_A,system_prompt=TASK_SYSTEM_PROMPT,general_purpose_agent=True,task_description=_A):super().__init__();A.system_prompt=system_prompt;B=cd(default_model=default_model,default_tools=default_tools or[],default_middleware=default_middleware,default_interrupt_on=default_interrupt_on,subagents=subagents or[],general_purpose_agent=general_purpose_agent,task_description=task_description);A.tools=[B]
113
+ def __init__(A,*,default_model,default_tools=_A,default_middleware=_A,default_interrupt_on=_A,subagents=_A,system_prompt=TASK_SYSTEM_PROMPT,general_purpose_agent=True,task_description=_A):super().__init__();A.system_prompt=system_prompt;B=cw(default_model=default_model,default_tools=default_tools or[],default_middleware=default_middleware,default_interrupt_on=default_interrupt_on,subagents=subagents or[],general_purpose_agent=general_purpose_agent,task_description=task_description);A.tools=[B]
114
114
  def wrap_model_call(B,request,handler):
115
115
  C=handler;A=request
116
116
  if B.system_prompt is not _A:D=A.system_prompt+'\n\n'+B.system_prompt if A.system_prompt else B.system_prompt;return C(A.override(system_prompt=D))
@@ -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 dk(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,61 +44,61 @@ 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.cq(B,N)for B in B];A.trigger=I;G=I
48
- else:J=A.cq(B,N);A.trigger=J;G=[J]
49
- A._trigger_conditions=G;A.keep=A.cq(F,'keep')
50
- if H is count_tokens_approximately:A.token_counter=cm(A.model)
47
+ elif isinstance(B,list):I=[A.df(B,N)for B in B];A.trigger=I;G=I
48
+ else:J=A.df(B,N);A.trigger=J;G=[J]
49
+ A._trigger_conditions=G;A.keep=A.df(F,'keep')
50
+ if H is count_tokens_approximately:A.token_counter=dk(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.cj()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 ct(B,state):A=state.get(_G,_F);return bool(A)
54
+ if K and A.dn()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 do(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.co(B);E=A.token_counter(B);F=A.ct(C);G=A.ch(B,E)
58
+ C=state;A=self;B=C[_B];A.da(B);E=A.token_counter(B);F=A.do(C);G=A.db(B,E)
59
59
  if not G and not F:return
60
- D=A.cu(B)
60
+ D=A.dh(B)
61
61
  if D<=0:return
62
- H,I=A.cn(B,D);J=A.ck(H);K=A.cl(J);return{_B:[RemoveMessage(id=REMOVE_ALL_MESSAGES),*K,*I],_G:_F}
62
+ H,I=A.di(B,D);J=A.de(H);K=A.dg(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.co(B);E=A.token_counter(B);F=A.ct(C);G=A.ch(B,E)
65
+ C=state;A=self;B=C[_B];A.da(B);E=A.token_counter(B);F=A.do(C);G=A.db(B,E)
66
66
  if not G and not F:return
67
- D=A.cu(B)
67
+ D=A.dh(B)
68
68
  if D<=0:return
69
- H,I=A.cn(B,D);J=await A._acreate_summary(H);K=A.cl(J);return{_B:[RemoveMessage(id=REMOVE_ALL_MESSAGES),*K,*I],_G:_F}
70
- def ci(B,messages,threshold):
69
+ H,I=A.di(B,D);J=await A._acreate_summary(H);K=A.dg(J);return{_B:[RemoveMessage(id=REMOVE_ALL_MESSAGES),*K,*I],_G:_F}
70
+ def dj(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
74
- def ch(A,messages,total_tokens):
74
+ def db(A,messages,total_tokens):
75
75
  F=total_tokens;E=messages
76
76
  if not A._trigger_conditions:return _F
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.ci(E,C):return _C
80
+ if B==_E and A.dj(E,C):return _C
81
81
  if B==_D:
82
- G=A.cj()
82
+ G=A.dn()
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.ci(E,D):return _C
87
+ if A.dj(E,D):return _C
88
88
  return _F
89
- def cu(A,messages):
89
+ def dh(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.dd(B)
93
93
  if C is not _A:return C
94
- return A.cp(B,_DEFAULT_MESSAGES_TO_KEEP)
95
- return A.cp(B,cast('int',E))
96
- def cs(C,messages):
94
+ return A.dl(B,_DEFAULT_MESSAGES_TO_KEEP)
95
+ return A.dl(B,cast('int',E))
96
+ def dd(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.cj()
101
+ J=C.dn()
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.cr(A,B)
118
- def cj(C):
117
+ return C.dc(A,B)
118
+ def dn(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 cq(context,parameter_name):
126
+ def df(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 cl(summary):return[HumanMessage(content=f"Here is a summary of the conversation to date:\n\n{summary}",additional_kwargs={'lc_source':'summarization'})]
135
+ def dg(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 co(messages):
137
+ def da(messages):
138
138
  for A in messages:
139
139
  if A.id is _A:A.id=str(uuid.uuid4())
140
140
  @staticmethod
141
- def cn(conversation_messages,cutoff_index):B=cutoff_index;A=conversation_messages;C=A[:B];D=A[B:];return C,D
142
- def cp(C,messages,messages_to_keep):
141
+ def di(conversation_messages,cutoff_index):B=cutoff_index;A=conversation_messages;C=A[:B];D=A[B:];return C,D
142
+ def dl(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.cr(A,D)
145
+ D=len(A)-B;return C.dc(A,D)
146
146
  @staticmethod
147
- def cr(messages,cutoff_index):
147
+ def dc(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 ck(A,messages_to_sumarize):
161
+ def de(A,messages_to_sumarize):
162
162
  B=messages_to_sumarize
163
163
  if not B:return _H
164
- C=A.cg(B)
164
+ C=A.dm(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.cg(B)
172
+ C=A.dm(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 cg(A,messages):
177
+ def dm(A,messages):
178
178
  B=messages
179
179
  try:
180
180
  if A.trim_tokens_to_summarize is _A:return B
@@ -4,7 +4,7 @@ import json,re
4
4
  def safe_unescape(s):return re.sub('\\\\(n|t|r|"|\\\\)',lambda m:{'n':'\n','t':'\t','r':'\r',_A:_A,'\\':'\\'}[m.group(1)],s)
5
5
  class StreamParser:
6
6
  def __init__(A,top_fields=None):B=top_fields;A.top_fields=set(B)if B else None;A.buffer=''
7
- def bm(J,s):
7
+ def cg(J,s):
8
8
  P=False;O='"\\s*:|$))';C={}
9
9
  if J.top_fields:K='|'.join(re.escape(A)for A in J.top_fields);G='('+K+')';H='('+K+')'
10
10
  else:G='([^"]+)';H='[^"]+'
@@ -33,7 +33,7 @@ class StreamParser:
33
33
  E='[';D='{';B=']';A='}'
34
34
  try:return json.loads(s)
35
35
  except(json.JSONDecodeError,ValueError):pass
36
- I=H.bm(s)
36
+ I=H.cg(s)
37
37
  for(J,M)in I.items():
38
38
  K=re.compile(rf'"{re.escape(J)}"\s*:\s*"(.*)$',re.DOTALL);F=K.search(s)
39
39
  if F and F.group(1).count(_A)%2==1:
@@ -48,5 +48,5 @@ class StreamParser:
48
48
  def feed(A,chunk):
49
49
  A.buffer+=chunk;B=A.try_parse_outer_json(A.buffer)
50
50
  if B:return B
51
- return A.bm(A.buffer)
51
+ return A.cg(A.buffer)
52
52
  def reset(A):A.buffer=''
@@ -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.bh(A)
20
- if B.bi(C):return ContentType.JSON
19
+ C=B.by(A)
20
+ if B.cd(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.bd(A):return ContentType.ERROR
25
- if B.bg(A):return ContentType.MARKDOWN
23
+ if B.cd(A):return ContentType.JSON
24
+ if B.bx(A):return ContentType.ERROR
25
+ if B.bv(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.be,ContentType.ERROR:A.bc,ContentType.JSON:A.ba,ContentType.MARKDOWN:A.bj,ContentType.TEXT:A.bb};F=E.get(C,A.bb);G=F(name,B,max_length);return FormattedResult(content_type=C,elements=G,success=D)
29
- def bh(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.bz,ContentType.ERROR:A.cb,ContentType.JSON:A.bw,ContentType.MARKDOWN:A.ca,ContentType.TEXT:A.bu};F=E.get(C,A.bu);G=F(name,B,max_length);return FormattedResult(content_type=C,elements=G,success=D)
29
+ def by(B,content):A=content.split('\n',2);return A[2].strip()if len(A)>2 else''
30
+ def cd(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 bd(B,content):A=['Traceback (most recent call last)','Exception:','Error:'];return any(A in content for A in A)
38
- def bg(C,content):A=content;B=['```','**','##','- **'];return A.startswith('#')or any(B in A for B in B)
39
- def be(B,name,content,max_length):A='green';C=B.bf(content,max_length);return[Panel(Text(C,style=A),title=f"📤 {name} ✓",border_style=A)]
40
- def bc(B,name,content,max_length):A='red';C=B.bf(content,max_length);return[Panel(Text(C,style=A),title=f"📤 {name} ✗",border_style=A)]
41
- def ba(B,name,content,max_length):
37
+ def bx(B,content):A=['Traceback (most recent call last)','Exception:','Error:'];return any(A in content for A in A)
38
+ def bv(C,content):A=content;B=['```','**','##','- **'];return A.startswith('#')or any(B in A for B in B)
39
+ def bz(B,name,content,max_length):A='green';C=B.cc(content,max_length);return[Panel(Text(C,style=A),title=f"📤 {name} ✓",border_style=A)]
40
+ def cb(B,name,content,max_length):A='red';C=B.cc(content,max_length);return[Panel(Text(C,style=A),title=f"📤 {name} ✗",border_style=A)]
41
+ def bw(B,name,content,max_length):
42
42
  D=max_length;A=content;E=A
43
- if A.startswith(SUCCESS_PREFIX):E=B.bh(A)
44
- try:F=json.loads(E);C=json.dumps(F,indent=2,ensure_ascii=_A);C=B.bf(C,D);return[Text(f"📤 {name} ✓",style=_B),Syntax(C,'json',theme='monokai',line_numbers=_A)]
45
- except(json.JSONDecodeError,ValueError):return B.bb(name,A,D)
46
- def bj(A,name,content,max_length):B=A.bf(content,max_length);return[Panel(Markdown(B),title=f"📤 {name}",border_style='cyan dim')]
47
- def bb(A,name,content,max_length):B=A.bf(content,max_length);return[Text(f"📤 {name}:",style=_B),Text(f" {B}",style='dim')]
48
- def bf(A,content,max_length):return truncate(content,max_length)
43
+ if A.startswith(SUCCESS_PREFIX):E=B.by(A)
44
+ try:F=json.loads(E);C=json.dumps(F,indent=2,ensure_ascii=_A);C=B.cc(C,D);return[Text(f"📤 {name} ✓",style=_B),Syntax(C,'json',theme='monokai',line_numbers=_A)]
45
+ except(json.JSONDecodeError,ValueError):return B.bu(name,A,D)
46
+ def ca(A,name,content,max_length):B=A.cc(content,max_length);return[Panel(Markdown(B),title=f"📤 {name}",border_style='cyan dim')]
47
+ def bu(A,name,content,max_length):B=A.cc(content,max_length);return[Text(f"📤 {name}:",style=_B),Text(f" {B}",style='dim')]
48
+ def cc(A,content,max_length):return truncate(content,max_length)
@@ -87,7 +87,7 @@ async def astream_handler(stream,debug=_M,interrupt_tools=[],tool_mode=_I):
87
87
  else:F=D
88
88
  if J:b=type(F).__name__;print(f"[DEBUG] Event: {b}")
89
89
  if isinstance(F,(AIMessageChunk,AIMessage)):
90
- for A in bk(F,T):
90
+ for A in cf(F,T):
91
91
  if A.type==_L:S+=A.data.get(_E,'')
92
92
  if J:print(f"[DEBUG] Yielding: {A.type}")
93
93
  yield A.data
@@ -124,7 +124,7 @@ def stream_handler(stream,debug=_M,interrupts=[],tool_mode=_I):
124
124
  else:E=C
125
125
  if J:W=type(E).__name__;print(f"[DEBUG] Event: {W}")
126
126
  if isinstance(E,(AIMessageChunk,AIMessage)):
127
- for A in bk(E,T):
127
+ for A in cf(E,T):
128
128
  if A.type==_L:S+=A.data.get(_E,'')
129
129
  if J:print(f"[DEBUG] Yielding: {A.type}")
130
130
  yield A.data
@@ -133,7 +133,7 @@ def stream_handler(stream,debug=_M,interrupts=[],tool_mode=_I):
133
133
  if P==_T:A=emitter.tool_call(F,L,H,Q);yield A.data
134
134
  elif P==_U:A=emitter.tool_result(F,M,X,H,Q);yield A.data
135
135
  if J:print(_b)
136
- def bk(chunk,stream_parser):
136
+ def cf(chunk,stream_parser):
137
137
  M='reasoning';L='thinking';I='write_file';F=stream_parser;E=chunk;B=E.content
138
138
  if isinstance(B,str):
139
139
  if B:yield emitter.text(B);return
@@ -166,7 +166,7 @@ def bk(chunk,stream_parser):
166
166
  if isinstance(D,str)and D:
167
167
  if H==I:F.feed(D)
168
168
  if H==I and D=='':F.reset()
169
- def bl(chunk):
169
+ def ce(chunk):
170
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]
171
171
  if len(B)>DisplayLimits.TOOL_RESULT_MAX:C+='\n... (truncated)'
172
172
  yield emitter.tool_result(D,C,F,E)
@@ -59,17 +59,17 @@ async def astream_handler(stream,emitter,tracker,debug=False):
59
59
  else:A=B
60
60
  if F:Y=type(A).__name__;print(f"[DEBUG] Event: {Y}")
61
61
  if isinstance(A,(AIMessageChunk,AIMessage)):
62
- for C in bn(A,E,J,S):
62
+ for C in cj(A,E,J,S):
63
63
  if C.type==_J:R+=C.data.get(_C,'')
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 bq(A.tool_calls,E,J):
67
+ for C in ck(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 bo(A,E,J):
72
+ for C in ci(A,E,J):
73
73
  if F:print(f"[DEBUG] Yielding: {C.type}")
74
74
  yield C.data
75
75
  elif K==_Z:
@@ -99,17 +99,17 @@ def stream_handler(stream,emitter,tracker,debug=False):
99
99
  else:A=B
100
100
  if F:Y=type(A).__name__;print(f"[DEBUG] Event: {Y}")
101
101
  if isinstance(A,(AIMessageChunk,AIMessage)):
102
- for C in bn(A,E,J,S):
102
+ for C in cj(A,E,J,S):
103
103
  if C.type==_J:R+=C.data.get(_C,'')
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 bq(A.tool_calls,E,J):
107
+ for C in ck(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 bo(A,E,J):
112
+ for C in ci(A,E,J):
113
113
  if F:print(f"[DEBUG] Yielding: {C.type}")
114
114
  yield C.data
115
115
  elif K==_Z:
@@ -117,7 +117,7 @@ def stream_handler(stream,emitter,tracker,debug=False):
117
117
  if O==_N:yield E.tool_call(I,Z,Q,P)
118
118
  elif O==_e:yield E.tool_result(I,a,b,Q,P)
119
119
  if F:print(_f)
120
- def bn(chunk,emitter,tracker,stream_parser):
120
+ def cj(chunk,emitter,tracker,stream_parser):
121
121
  U='index';T='tool_use';S='reasoning';R='thinking';L='write_file';K=stream_parser;J=chunk;I=emitter;E=tracker;C=J.content
122
122
  if isinstance(C,str):
123
123
  if C:yield I.text(C);return
@@ -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 bp(block,emitter,tracker):
163
+ def ch(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 bq(tool_calls,emitter,tracker):
168
+ def ck(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 bo(chunk,emitter,tracker):
175
+ def ci(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]
@@ -12,11 +12,11 @@ from typing import Optional
12
12
  @dataclass(frozen=True)
13
13
  class ModelRate:provider:str;model:str;input_per_1m:float;output_per_1m:float;notes:str=''
14
14
  _PRICING={}
15
- def cy(*C):
15
+ def ds(*C):
16
16
  for A in C:
17
17
  _PRICING[A.model]=A;B=A.model.split('/')[-1]if'/'in A.model else _D
18
18
  if B and B not in _PRICING:_PRICING[B]=A
19
- cy(ModelRate(_A,'gpt-4o',2.5,1e1),ModelRate(_A,'gpt-4o-mini',.15,.6),ModelRate(_A,'gpt-4o-2024-11-20',2.5,1e1),ModelRate(_A,'gpt-4-turbo',1e1,3e1),ModelRate(_A,'gpt-4',3e1,6e1),ModelRate(_A,'gpt-3.5-turbo',.5,1.5),ModelRate(_A,'o1',15.,6e1),ModelRate(_A,'o1-mini',3.,12.),ModelRate(_A,'o3-mini',1.1,4.4),ModelRate(_A,'o3',1e1,4e1),ModelRate(_B,'claude-3-5-sonnet-20241022',3.,15.),ModelRate(_B,'claude-3-5-haiku-20241022',.8,4.),ModelRate(_B,'claude-3-opus-20240229',15.,75.),ModelRate(_B,'claude-3-sonnet-20240229',3.,15.),ModelRate(_B,'claude-3-haiku-20240307',.25,1.25),ModelRate(_B,'claude-sonnet-4-5',3.,15.),ModelRate(_B,'claude-sonnet-4-6',3.,15.),ModelRate(_B,'claude-opus-4',15.,75.),ModelRate(_C,'gemini-1.5-pro',3.5,10.5,'up to 128k ctx'),ModelRate(_C,'gemini-1.5-flash',.075,.3),ModelRate(_C,'gemini-2.0-flash',.1,.4),ModelRate(_C,'gemini-2.0-flash-lite',.075,.3),ModelRate(_C,'gemini-2.0-pro',3.5,10.5),ModelRate(_E,'command-r-plus',2.5,1e1),ModelRate(_E,'command-r',.15,.6),ModelRate(_E,'command-r7b',.0375,.15),ModelRate(_F,'llama-3.3-70b-instruct',.23,.4,_G),ModelRate(_F,'llama-3.1-405b-instruct',3.,3.,_G),ModelRate(_F,'llama-3.1-70b-instruct',.2,.2,_G),ModelRate(_H,'mistral-large-2411',2.,6.),ModelRate(_H,'mistral-small-2501',.1,.3),ModelRate(_H,'codestral-2501',.1,.3),ModelRate(_I,'qwen-plus',2.,6.),ModelRate(_I,'qwen-max-3',2.,6.),ModelRate(_I,'qwen-max-3.5',2.,6.))
19
+ ds(ModelRate(_A,'gpt-4o',2.5,1e1),ModelRate(_A,'gpt-4o-mini',.15,.6),ModelRate(_A,'gpt-4o-2024-11-20',2.5,1e1),ModelRate(_A,'gpt-4-turbo',1e1,3e1),ModelRate(_A,'gpt-4',3e1,6e1),ModelRate(_A,'gpt-3.5-turbo',.5,1.5),ModelRate(_A,'o1',15.,6e1),ModelRate(_A,'o1-mini',3.,12.),ModelRate(_A,'o3-mini',1.1,4.4),ModelRate(_A,'o3',1e1,4e1),ModelRate(_B,'claude-3-5-sonnet-20241022',3.,15.),ModelRate(_B,'claude-3-5-haiku-20241022',.8,4.),ModelRate(_B,'claude-3-opus-20240229',15.,75.),ModelRate(_B,'claude-3-sonnet-20240229',3.,15.),ModelRate(_B,'claude-3-haiku-20240307',.25,1.25),ModelRate(_B,'claude-sonnet-4-5',3.,15.),ModelRate(_B,'claude-sonnet-4-6',3.,15.),ModelRate(_B,'claude-opus-4',15.,75.),ModelRate(_C,'gemini-1.5-pro',3.5,10.5,'up to 128k ctx'),ModelRate(_C,'gemini-1.5-flash',.075,.3),ModelRate(_C,'gemini-2.0-flash',.1,.4),ModelRate(_C,'gemini-2.0-flash-lite',.075,.3),ModelRate(_C,'gemini-2.0-pro',3.5,10.5),ModelRate(_E,'command-r-plus',2.5,1e1),ModelRate(_E,'command-r',.15,.6),ModelRate(_E,'command-r7b',.0375,.15),ModelRate(_F,'llama-3.3-70b-instruct',.23,.4,_G),ModelRate(_F,'llama-3.1-405b-instruct',3.,3.,_G),ModelRate(_F,'llama-3.1-70b-instruct',.2,.2,_G),ModelRate(_H,'mistral-large-2411',2.,6.),ModelRate(_H,'mistral-small-2501',.1,.3),ModelRate(_H,'codestral-2501',.1,.3),ModelRate(_I,'qwen-plus',2.,6.),ModelRate(_I,'qwen-max-3',2.,6.),ModelRate(_I,'qwen-max-3.5',2.,6.))
20
20
  class ModelPricing:
21
21
  def __init__(A,custom_rates=_D):
22
22
  B=custom_rates;A._db=dict(_PRICING)
@@ -19,7 +19,7 @@ from collections import defaultdict
19
19
  from datetime import datetime
20
20
  from typing import Optional
21
21
  from.tracker import CostTracker
22
- def dc(since,until,tag=_A,user_id=_A,session_id=_A):
22
+ def dx(since,until,tag=_A,user_id=_A,session_id=_A):
23
23
  F=session_id;E=user_id;D=until;C=since;A=[];B=[]
24
24
  if C:A.append('timestamp >= ?');B.append(C.isoformat())
25
25
  if D:A.append('timestamp <= ?');B.append(D.isoformat())
@@ -27,7 +27,7 @@ def dc(since,until,tag=_A,user_id=_A,session_id=_A):
27
27
  if E:A.append('user_id = ?');B.append(E)
28
28
  if F:A.append('session_id = ?');B.append(F)
29
29
  G=' AND '.join(A)if A else'1=1';return G,B
30
- def dd(period):
30
+ def dw(period):
31
31
  A=period
32
32
  if A=='daily':return"strftime('%Y-%m-%d', timestamp)"
33
33
  elif A=='weekly':return"strftime('%Y-%W', timestamp)"
@@ -35,7 +35,7 @@ def dd(period):
35
35
  else:raise ValueError(f"Unknown period: {A}")
36
36
  class CostReport:
37
37
  def __init__(A,tracker):A.tracker=tracker
38
- def build(L,since=_A,until=_A,period='daily',tag=_A,user_id=_A,session_id=_A):I=period;H=until;G=since;F='output_tokens';E='input_tokens';B,C=dc(G,H,tag,user_id,session_id);A=L.tracker._conn.cursor();A.execute(f"""
38
+ def build(L,since=_A,until=_A,period='daily',tag=_A,user_id=_A,session_id=_A):I=period;H=until;G=since;F='output_tokens';E='input_tokens';B,C=dx(G,H,tag,user_id,session_id);A=L.tracker._conn.cursor();A.execute(f"""
39
39
  SELECT
40
40
  COUNT(*),
41
41
  SUM(cost),
@@ -87,7 +87,7 @@ class CostReport:
87
87
  WHERE {B}
88
88
  GROUP BY session_id
89
89
  ORDER BY SUM(cost) DESC
90
- """,C);R={A:{_B:round(C or 0,6),_C:B,E:D or 0,F:G or 0}for(A,B,C,D,G)in A.fetchall()};S=dd(I);A.execute(f"""
90
+ """,C);R={A:{_B:round(C or 0,6),_C:B,E:D or 0,F:G or 0}for(A,B,C,D,G)in A.fetchall()};S=dw(I);A.execute(f"""
91
91
  SELECT
92
92
  {S} as bucket,
93
93
  COUNT(*),