@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.
- package/dist/python-src/.env +5 -3
- package/dist/python-src/README.md +40 -1
- package/dist/python-src/_workspace/.autodev/config.json +12 -0
- package/dist/python-src/_workspace/.autodev/cron/jobs.json +4 -0
- package/dist/python-src/entry.py +35 -1
- package/dist/python-src/main.py +753 -40
- package/dist/python-src/pyproject.toml +1 -0
- package/dist/python-src/run.sh +9 -0
- package/dist/python-src/src/agents/token_tracker.py +4 -4
- package/dist/python-src/src/claw/bus/queue.py +1 -1
- package/dist/python-src/src/claw/channels/__init__.py +2 -2
- package/dist/python-src/src/claw/channels/base.py +2 -2
- package/dist/python-src/src/claw/channels/feishu.py +57 -16
- package/dist/python-src/src/claw/channels/manager.py +2 -2
- package/dist/python-src/src/claw/config/__init__.py +3 -0
- package/dist/python-src/src/claw/config/loader.py +38 -0
- package/dist/python-src/src/claw/config/schema.py +14 -29
- package/dist/python-src/src/claw/cron/__init__.py +3 -0
- package/dist/python-src/src/claw/cron/service.py +171 -0
- package/dist/python-src/src/claw/cron/types_.py +14 -0
- package/dist/python-src/src/claw/heartbeat/__init__.py +2 -0
- package/dist/python-src/src/claw/heartbeat/service.py +55 -0
- package/dist/python-src/src/claw/run.py +82 -0
- package/dist/python-src/src/claw/tools/base.py +23 -0
- package/dist/python-src/src/claw/tools/channel.py +0 -0
- package/dist/python-src/src/claw/tools/cron.py +138 -0
- package/dist/python-src/src/claw/utils/__init__.py +2 -0
- package/dist/python-src/src/claw/utils/helpers.py +27 -0
- package/dist/python-src/src/core/context.py +158 -0
- package/dist/python-src/src/core/deepagents.py +5 -5
- package/dist/python-src/src/managers/manager_agent.py +9 -9
- package/dist/python-src/src/managers/manager_command.py +62 -0
- package/dist/python-src/src/managers/manager_context.py +1 -1
- package/dist/python-src/src/managers/manager_instruction.py +7 -7
- package/dist/python-src/src/managers/manager_skill.py +3 -3
- package/dist/python-src/src/managers/sandbox.py +3 -3
- package/dist/python-src/src/middlewares/dynamic_content.py +2 -2
- package/dist/python-src/src/middlewares/hitl.py +3 -3
- package/dist/python-src/src/middlewares/memory.py +2 -2
- package/dist/python-src/src/middlewares/subagents.py +4 -4
- package/dist/python-src/src/middlewares/summary.py +37 -37
- package/dist/python-src/src/stream/file_write_parser.py +3 -3
- package/dist/python-src/src/stream/formatter.py +19 -19
- package/dist/python-src/src/stream/handler.py +4 -4
- package/dist/python-src/src/stream/handler_with_tracker.py +10 -10
- package/dist/python-src/src/trackers/token/pricing.py +2 -2
- package/dist/python-src/src/trackers/token/report.py +4 -4
- package/dist/python-src/src/trackers/token/tracker.py +8 -8
- package/dist/python-src/src/tui/chatui.py +10 -10
- package/dist/python-src/src/tui/clawtui.py +224 -0
- package/dist/python-src/src/tui/commands/__init__.py +3 -0
- package/dist/python-src/src/tui/commands/base.py +6 -0
- package/dist/python-src/src/tui/commands/instruction.py +5 -0
- package/dist/python-src/src/tui/components/tlist.py +7 -7
- package/dist/python-src/src/tui/components/tscroll_panel.py +73 -44
- package/dist/python-src/src/tui/components/tscroll_panel_old.py +58 -0
- package/dist/python-src/src/tui/utils/trender.py +21 -21
- package/dist/python-src/uv.lock +1969 -1958
- package/package.json +1 -1
package/dist/python-src/main.py
CHANGED
|
@@ -11,7 +11,8 @@ import asyncio
|
|
|
11
11
|
import json
|
|
12
12
|
import os
|
|
13
13
|
|
|
14
|
-
from src.tui.chatui import LiveChatUI
|
|
14
|
+
# from src.tui.chatui import LiveChatUI
|
|
15
|
+
from src.tui.clawtui import LiveChatUI
|
|
15
16
|
from src.middlewares.hitl import HumanInTheLoopMiddleware
|
|
16
17
|
|
|
17
18
|
_ = load_dotenv(find_dotenv())
|
|
@@ -23,18 +24,33 @@ import warnings
|
|
|
23
24
|
warnings.filterwarnings("ignore", category=DeprecationWarning)
|
|
24
25
|
warnings.filterwarnings("ignore", message="Pydantic serializer warnings")
|
|
25
26
|
|
|
27
|
+
from src.claw.config.loader import load_config, get_data_dir
|
|
28
|
+
from src.claw.bus.queue import MessageBus, InboundMessage, OutboundMessage
|
|
29
|
+
from src.claw.channels.manager import ChannelManager
|
|
30
|
+
from src.claw.cron.service import CronService
|
|
31
|
+
from src.claw.cron.types_ import CronJob
|
|
32
|
+
from src.claw.heartbeat.service import HeartbeatService
|
|
26
33
|
|
|
34
|
+
from src.claw.tools.base import AgentContext
|
|
35
|
+
from pathlib import Path
|
|
27
36
|
|
|
37
|
+
from src.middlewares.skill import SkillMiddleware
|
|
38
|
+
from src.core.deepagents import create_deep_agent, get_default_model
|
|
39
|
+
from src.core.context import build_system_prompt
|
|
28
40
|
|
|
29
|
-
# # ========== 关键修改1:配置logging,让INFO级别日志能输出 ==========
|
|
30
|
-
# import logging
|
|
31
|
-
# logging.basicConfig(
|
|
32
|
-
# level=logging.CRITICAL, # 设置日志级别为WARNING(显示WARNING/ERROR等)
|
|
33
|
-
# format="%(asctime)s - %(name)s - %(levelname)s - %(message)s", # 日志格式(可选,更易读)
|
|
34
|
-
# handlers=[logging.StreamHandler()] # 输出到控制台
|
|
35
|
-
# )
|
|
36
41
|
|
|
37
|
-
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
# ========== 关键修改1:配置logging,让INFO级别日志能输出 ==========
|
|
46
|
+
import logging
|
|
47
|
+
logging.basicConfig(
|
|
48
|
+
level=logging.CRITICAL, # 设置日志级别为WARNING(显示WARNING/ERROR等)
|
|
49
|
+
format="%(asctime)s - %(name)s - %(levelname)s - %(message)s", # 日志格式(可选,更易读)
|
|
50
|
+
handlers=[logging.StreamHandler()] # 输出到控制台
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
logger = logging.getLogger(__name__)
|
|
38
54
|
|
|
39
55
|
|
|
40
56
|
|
|
@@ -66,26 +82,30 @@ def get_weather(city: str) -> str:
|
|
|
66
82
|
|
|
67
83
|
|
|
68
84
|
|
|
69
|
-
llm = ChatOpenAI(
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
85
|
+
# llm = ChatOpenAI(
|
|
86
|
+
# model=os.getenv("DEFAULT_MODEL"),
|
|
87
|
+
# base_url=os.getenv("OPENAI_API_BASE"),
|
|
88
|
+
# api_key=os.getenv("OPENAI_API_KEY"),
|
|
89
|
+
# streaming=True,
|
|
90
|
+
# temperature=0.7,
|
|
91
|
+
# # 开启百炼的 thinking 模式
|
|
92
|
+
# # 新版 LangChain 推荐用 extra_body 直接作为参数(而非嵌套在 model_kwargs)
|
|
93
|
+
# # extra_body = {
|
|
94
|
+
# # "enable_thinking": True,
|
|
95
|
+
# # "thinking_budget": 1000, # 增大预算,适配 Agent 多步思考
|
|
96
|
+
# # },
|
|
97
|
+
# model_kwargs={
|
|
98
|
+
# # 使流式返回的最后一个数据包包含Token消耗信息
|
|
99
|
+
# "stream_options": {"include_usage": True}
|
|
100
|
+
# }
|
|
101
|
+
# )
|
|
102
|
+
|
|
103
|
+
# llm = get_default_model(streaming=True)
|
|
86
104
|
|
|
87
105
|
async def agent_ui():
|
|
88
106
|
|
|
107
|
+
llm = get_default_model(streaming=True)
|
|
108
|
+
|
|
89
109
|
agent = create_agent(
|
|
90
110
|
model=llm,
|
|
91
111
|
tools=[get_weather],
|
|
@@ -179,7 +199,7 @@ async def asingle_agent():
|
|
|
179
199
|
"""
|
|
180
200
|
|
|
181
201
|
saver = InMemorySaver()
|
|
182
|
-
|
|
202
|
+
llm = get_default_model(streaming=True)
|
|
183
203
|
agent = create_agent(
|
|
184
204
|
model=llm,
|
|
185
205
|
tools=tools,
|
|
@@ -311,11 +331,15 @@ async def team_main():
|
|
|
311
331
|
agents_conf = agent_manager.get_all_conf()
|
|
312
332
|
|
|
313
333
|
toolkits = {
|
|
314
|
-
"
|
|
315
|
-
"
|
|
334
|
+
"read-only": [ read_file, glob, grep, list_dir],
|
|
335
|
+
"edit": [ edit, write_file],
|
|
336
|
+
"search": [ web_search, web_fetch],
|
|
337
|
+
"bash": [ shell],
|
|
316
338
|
} if run_mode == "sandbox" else {
|
|
317
|
-
"
|
|
318
|
-
"
|
|
339
|
+
"read-only": [ read_file, glob, grep, list_dir],
|
|
340
|
+
"edit": [ edit, write_file],
|
|
341
|
+
"search": [ web_search, web_fetch],
|
|
342
|
+
"bash": [ bash ],
|
|
319
343
|
}
|
|
320
344
|
|
|
321
345
|
context = SkillAgentContext(
|
|
@@ -376,7 +400,8 @@ async def team_main():
|
|
|
376
400
|
# prompt_name="leader_",
|
|
377
401
|
# WORKSPACE=workspace
|
|
378
402
|
# ),
|
|
379
|
-
system_prompt=apply_prompt(leader, WORKSPACE=workspace),
|
|
403
|
+
# system_prompt=apply_prompt(leader, WORKSPACE=workspace),
|
|
404
|
+
system_prompt=build_system_prompt(),
|
|
380
405
|
checkpointer=saver
|
|
381
406
|
).with_config({"recursion_limit": recursion_limit})
|
|
382
407
|
|
|
@@ -386,7 +411,7 @@ async def team_main():
|
|
|
386
411
|
workspace = os.getcwd()
|
|
387
412
|
run_mode = "local"
|
|
388
413
|
session_id = "1"
|
|
389
|
-
|
|
414
|
+
llm = get_default_model(streaming=True)
|
|
390
415
|
# agents_conf, toolkits, context, saver, instruction_manager
|
|
391
416
|
agents_config, toolkits, context, saver, instruction_manager = _prepare(workspace, run_mode, session_id)
|
|
392
417
|
agent = _build_team(
|
|
@@ -476,11 +501,15 @@ def build_team():
|
|
|
476
501
|
agents_conf = agent_manager.get_all_conf()
|
|
477
502
|
|
|
478
503
|
toolkits = {
|
|
479
|
-
"
|
|
480
|
-
"
|
|
504
|
+
"read-only": [ read_file, glob, grep, list_dir],
|
|
505
|
+
"edit": [ edit, write_file],
|
|
506
|
+
"search": [ web_search, web_fetch],
|
|
507
|
+
"bash": [ shell],
|
|
481
508
|
} if run_mode == "sandbox" else {
|
|
482
|
-
"
|
|
483
|
-
"
|
|
509
|
+
"read-only": [ read_file, glob, grep, list_dir],
|
|
510
|
+
"edit": [ edit, write_file],
|
|
511
|
+
"search": [ web_search, web_fetch],
|
|
512
|
+
"bash": [ bash ],
|
|
484
513
|
}
|
|
485
514
|
|
|
486
515
|
context = SkillAgentContext(
|
|
@@ -543,7 +572,8 @@ def build_team():
|
|
|
543
572
|
middleware=[
|
|
544
573
|
DynamicContentMiddleware(),
|
|
545
574
|
],
|
|
546
|
-
system_prompt=apply_prompt(leader, WORKSPACE=workspace),
|
|
575
|
+
# system_prompt=apply_prompt(leader, WORKSPACE=workspace),
|
|
576
|
+
system_prompt=build_system_prompt(),
|
|
547
577
|
checkpointer=saver
|
|
548
578
|
).with_config({"recursion_limit": recursion_limit})
|
|
549
579
|
|
|
@@ -553,7 +583,7 @@ def build_team():
|
|
|
553
583
|
workspace = os.getcwd()
|
|
554
584
|
run_mode = "local"
|
|
555
585
|
session_id = "1"
|
|
556
|
-
|
|
586
|
+
llm = get_default_model(streaming=True)
|
|
557
587
|
# agents_conf, toolkits, context, saver, instruction_manager
|
|
558
588
|
agents_config, toolkits, context, saver, instruction_manager = _prepare(workspace, run_mode, session_id)
|
|
559
589
|
agent = _build_team(
|
|
@@ -653,18 +683,701 @@ async def run_once(prompt:str = None, verbose: bool=True):
|
|
|
653
683
|
|
|
654
684
|
finally:
|
|
655
685
|
pass
|
|
686
|
+
|
|
687
|
+
|
|
688
|
+
def claw_main():
|
|
689
|
+
""" 以 claw mode 运行 """
|
|
690
|
+
|
|
691
|
+
from functools import partial
|
|
692
|
+
from pathlib import Path
|
|
693
|
+
import asyncio
|
|
694
|
+
import sys
|
|
695
|
+
import os
|
|
696
|
+
|
|
697
|
+
from src.claw.config.loader import load_config, get_data_dir
|
|
698
|
+
from src.claw.bus.queue import MessageBus, InboundMessage, OutboundMessage
|
|
699
|
+
from src.claw.channels.manager import ChannelManager
|
|
700
|
+
from src.claw.cron.service import CronService
|
|
701
|
+
from src.claw.cron.types_ import CronJob
|
|
702
|
+
from src.claw.heartbeat.service import HeartbeatService
|
|
703
|
+
|
|
704
|
+
from src.claw.tools.base import AgentContext
|
|
705
|
+
from src.claw.tools.cron import (
|
|
706
|
+
add_cron_job,
|
|
707
|
+
list_cron_jobs,
|
|
708
|
+
remove_cron_job
|
|
709
|
+
)
|
|
710
|
+
from src.tools.tools import (
|
|
711
|
+
SkillAgentContext,
|
|
712
|
+
shell,
|
|
713
|
+
bash,
|
|
714
|
+
write_file,
|
|
715
|
+
read_file,
|
|
716
|
+
list_dir,
|
|
717
|
+
glob,
|
|
718
|
+
grep,
|
|
719
|
+
edit
|
|
720
|
+
)
|
|
721
|
+
|
|
722
|
+
from src.tools.web import (
|
|
723
|
+
web_search,
|
|
724
|
+
web_fetch
|
|
725
|
+
)
|
|
726
|
+
|
|
727
|
+
from src.managers.sandbox import Container
|
|
728
|
+
|
|
729
|
+
from dotenv import load_dotenv, find_dotenv
|
|
730
|
+
from langchain.agents import create_agent
|
|
731
|
+
from langchain_openai import ChatOpenAI
|
|
732
|
+
from langgraph.checkpoint.memory import InMemorySaver
|
|
733
|
+
from langchain_core.tools import BaseTool
|
|
734
|
+
|
|
735
|
+
|
|
736
|
+
from src.core.deepagents import create_deep_agent, get_default_model
|
|
737
|
+
from src.managers.manager_context import ContextManager
|
|
738
|
+
from src.utils.prompt import apply_template, apply_prompt
|
|
739
|
+
from src.prompts.prompts import leader
|
|
740
|
+
|
|
741
|
+
from src.tui.clawtui import LiveChatUI
|
|
742
|
+
|
|
743
|
+
_ = load_dotenv(find_dotenv())
|
|
744
|
+
|
|
745
|
+
WORKSPACE = Path.cwd()
|
|
746
|
+
|
|
747
|
+
|
|
748
|
+
"""
|
|
749
|
+
"appId": "cli_a909847e1278dcbd",
|
|
750
|
+
"appSecret": "bE0usuE0MKUJVDWOo9olib5X8PKv66pK",
|
|
751
|
+
"""
|
|
752
|
+
channel_conf_path = WORKSPACE / "channel_config.json"
|
|
753
|
+
if not channel_conf_path.exists():
|
|
754
|
+
with open(channel_conf_path, "w") as f:
|
|
755
|
+
data = {
|
|
756
|
+
"channels": {
|
|
757
|
+
"feishu": {
|
|
758
|
+
"enabled": True,
|
|
759
|
+
"appId": "",
|
|
760
|
+
"appSecret": "",
|
|
761
|
+
"encryptKey": "",
|
|
762
|
+
"verificationToken": "",
|
|
763
|
+
"allowFrom": []
|
|
764
|
+
}
|
|
765
|
+
}
|
|
766
|
+
}
|
|
767
|
+
json.dump(data, f)
|
|
768
|
+
|
|
769
|
+
config = load_config(
|
|
770
|
+
config_path=channel_conf_path
|
|
771
|
+
)
|
|
772
|
+
config.channels.feishu.app_id = os.getenv("APP_ID", "")
|
|
773
|
+
config.channels.feishu.app_secret = os.getenv("APP_SECRET", "")
|
|
774
|
+
|
|
775
|
+
def _prepare(workspace, run_mode, session_id):
|
|
776
|
+
saver = InMemorySaver()
|
|
777
|
+
|
|
778
|
+
# 1. 构建上下文管理器
|
|
779
|
+
dockerfile_path = os.path.join(Path(__file__).parent, "resources", "dockerfiles", "sandbox", "Dockerfile")
|
|
780
|
+
context_manager = ContextManager(
|
|
781
|
+
dockerfile_path=dockerfile_path,
|
|
782
|
+
mode=run_mode
|
|
783
|
+
)
|
|
784
|
+
|
|
785
|
+
context_manager.create_environment(
|
|
786
|
+
session_id=session_id,
|
|
787
|
+
workspace_path=workspace
|
|
788
|
+
)
|
|
789
|
+
|
|
790
|
+
# 2. 指令管理器
|
|
791
|
+
instruction_manager = context_manager.get_instruction_manager(
|
|
792
|
+
session_id=session_id
|
|
793
|
+
)
|
|
794
|
+
|
|
795
|
+
# 3. 代理管理器
|
|
796
|
+
agent_manager = context_manager.get_agent_manager(
|
|
797
|
+
session_id=session_id
|
|
798
|
+
)
|
|
799
|
+
|
|
800
|
+
# 4. 沙箱
|
|
801
|
+
sandbox = context_manager.get_container(
|
|
802
|
+
session_id=session_id
|
|
803
|
+
)
|
|
804
|
+
|
|
805
|
+
# 5. 工具箱
|
|
806
|
+
|
|
807
|
+
toolkits = {
|
|
808
|
+
"read-only": [ read_file, glob, grep, list_dir],
|
|
809
|
+
"edit": [ edit, write_file],
|
|
810
|
+
"search": [ web_search, web_fetch],
|
|
811
|
+
"bash": [ shell],
|
|
812
|
+
} if run_mode == "sandbox" else {
|
|
813
|
+
"read-only": [ read_file, glob, grep, list_dir],
|
|
814
|
+
"edit": [ edit, write_file],
|
|
815
|
+
"search": [ web_search, web_fetch],
|
|
816
|
+
"bash": [ bash ],
|
|
817
|
+
}
|
|
656
818
|
|
|
819
|
+
# 6. 配置
|
|
820
|
+
agents_conf = agent_manager.get_all_conf()
|
|
821
|
+
|
|
822
|
+
context = AgentContext(
|
|
823
|
+
working_directory=workspace,
|
|
824
|
+
sandbox=sandbox,
|
|
825
|
+
channel=None,
|
|
826
|
+
chat_id=None,
|
|
827
|
+
cron_service=None,
|
|
828
|
+
workspace=workspace,
|
|
829
|
+
)
|
|
830
|
+
|
|
831
|
+
return agents_conf, toolkits, context, saver, instruction_manager
|
|
832
|
+
|
|
833
|
+
def _build_team(agents_conf: Dict[str, Any], domain: str, llm: ChatOpenAI, toolkits: Dict[str, List[BaseTool]], workspace: str, saver=None, store=None, recursion_limit=1000):
|
|
834
|
+
|
|
835
|
+
team_conf = agents_conf.setdefault(domain, {"members": {}})
|
|
836
|
+
|
|
837
|
+
agents = []
|
|
838
|
+
for member_name, member in team_conf["members"].items():
|
|
839
|
+
|
|
840
|
+
tools = []
|
|
841
|
+
for tool_name in member["tools"]:
|
|
842
|
+
tools.extend(toolkits.get(tool_name, []))
|
|
843
|
+
|
|
844
|
+
# print(f"Building agent {member_name}")
|
|
845
|
+
# print(f"Tools: {tools}")
|
|
846
|
+
# print(f"Member: {member}")
|
|
847
|
+
# sys.exit()
|
|
848
|
+
|
|
849
|
+
llm_conf = member.get("llm", None)
|
|
850
|
+
model = ChatOpenAI(**llm_conf) if llm_conf else get_default_model(streaming=False)
|
|
851
|
+
|
|
852
|
+
agent = {
|
|
853
|
+
"name": member_name,
|
|
854
|
+
"description": member["description"],
|
|
855
|
+
"system_prompt": apply_template(
|
|
856
|
+
agents_config=agents_conf,
|
|
857
|
+
domain=domain,
|
|
858
|
+
prompt_name=member_name,
|
|
859
|
+
WORKSPACE=workspace
|
|
860
|
+
),
|
|
861
|
+
"context_schema": SkillAgentContext,
|
|
862
|
+
"tools": tools,
|
|
863
|
+
"model": model
|
|
864
|
+
}
|
|
865
|
+
|
|
866
|
+
agents.append(agent)
|
|
867
|
+
|
|
868
|
+
tools = []
|
|
869
|
+
for tool_name in ["filetools", "web"]:
|
|
870
|
+
tools.extend([tool for tool in toolkits.get(tool_name, [])])
|
|
871
|
+
|
|
872
|
+
tools.extend([add_cron_job, list_cron_jobs, remove_cron_job])
|
|
873
|
+
|
|
874
|
+
team = create_deep_agent(
|
|
875
|
+
model=llm,
|
|
876
|
+
subagents=agents,
|
|
877
|
+
tools=tools,
|
|
878
|
+
middleware=[SkillMiddleware(workspace=workspace, home_path=str(Path.home()))],
|
|
879
|
+
context_schema=AgentContext,
|
|
880
|
+
system_prompt=apply_prompt(leader, WORKSPACE=workspace),
|
|
881
|
+
# system_prompt=build_system_prompt(),
|
|
882
|
+
checkpointer=saver
|
|
883
|
+
).with_config({"recursion_limit": recursion_limit})
|
|
884
|
+
|
|
885
|
+
return team
|
|
657
886
|
|
|
658
887
|
|
|
888
|
+
workspace = os.getcwd()
|
|
889
|
+
run_mode = "local"
|
|
890
|
+
session_id = "1"
|
|
891
|
+
|
|
892
|
+
llm = get_default_model(streaming=True)
|
|
893
|
+
agents_config, toolkits, context, saver, instruction_manager = _prepare(workspace, run_mode, session_id)
|
|
894
|
+
agent = _build_team(
|
|
895
|
+
agents_conf=agents_config,
|
|
896
|
+
domain="coding",
|
|
897
|
+
llm=llm,
|
|
898
|
+
toolkits=toolkits,
|
|
899
|
+
workspace=workspace,
|
|
900
|
+
saver=saver,
|
|
901
|
+
store=None,
|
|
902
|
+
recursion_limit=1000
|
|
903
|
+
)
|
|
904
|
+
|
|
905
|
+
# 创建事件总线
|
|
906
|
+
bus = MessageBus()
|
|
907
|
+
# 创建周期性任务
|
|
908
|
+
cron_store_path = Path(os.getcwd()) /".autodev"/"cron"/"jobs.json"
|
|
909
|
+
cron_store_path.parent.mkdir(parents=True, exist_ok=True)
|
|
910
|
+
cron = CronService(cron_store_path)
|
|
911
|
+
|
|
912
|
+
# 设置周期回调函数
|
|
913
|
+
async def on_cron_job(job: CronJob) -> str | None:
|
|
914
|
+
""" Execute a cron job through the agent. """
|
|
915
|
+
session_key = f"cron:{job.id}"
|
|
916
|
+
channel = job.payload.channel or "cli"
|
|
917
|
+
chat_id = job.payload.to or "direct"
|
|
918
|
+
response = await agent.ainvoke(
|
|
919
|
+
{"messages": [{"role": "user", "content": job.payload.message}]},
|
|
920
|
+
context=AgentContext(
|
|
921
|
+
working_directory=workspace,
|
|
922
|
+
sandbox=None,
|
|
923
|
+
channel=channel,
|
|
924
|
+
chat_id=chat_id,
|
|
925
|
+
cron_service=cron,
|
|
926
|
+
workspace=workspace
|
|
927
|
+
)
|
|
928
|
+
)
|
|
659
929
|
|
|
930
|
+
resp = response["messages"][-1].content
|
|
931
|
+
|
|
932
|
+
if job.payload.deliver and job.payload.to:
|
|
933
|
+
from src.claw.bus.events import OutboundMessage
|
|
934
|
+
await bus.publish_outbound(
|
|
935
|
+
OutboundMessage(
|
|
936
|
+
channel=channel,
|
|
937
|
+
chat_id=chat_id,
|
|
938
|
+
content=resp or ""
|
|
939
|
+
))
|
|
940
|
+
|
|
941
|
+
return resp
|
|
942
|
+
|
|
943
|
+
cron.on_job = on_cron_job
|
|
944
|
+
|
|
945
|
+
# 创建心跳服务
|
|
946
|
+
async def on_heartbeat(prompt: str) -> str:
|
|
947
|
+
""" Execute a heartbeat through the agent. """
|
|
948
|
+
print(f"execute Heartbeat prompt: {prompt}")
|
|
949
|
+
response = await agent.ainvoke(
|
|
950
|
+
{"messages": [{"role": "user", "content": prompt}]},
|
|
951
|
+
context=AgentContext(
|
|
952
|
+
working_directory=workspace,
|
|
953
|
+
sandbox=None,
|
|
954
|
+
channel="cli",
|
|
955
|
+
chat_id="direct",
|
|
956
|
+
cron_service=cron,
|
|
957
|
+
workspace=workspace,
|
|
958
|
+
)
|
|
959
|
+
)
|
|
960
|
+
|
|
961
|
+
resp = response["messages"][-1].content
|
|
962
|
+
return resp
|
|
963
|
+
|
|
964
|
+
heartbeat = HeartbeatService(
|
|
965
|
+
workspace=Path(workspace),
|
|
966
|
+
on_heartbeat=on_heartbeat,
|
|
967
|
+
interval_s=10,
|
|
968
|
+
enabled=True
|
|
969
|
+
)
|
|
970
|
+
|
|
971
|
+
# 创建渠道管理器
|
|
972
|
+
channels = ChannelManager(
|
|
973
|
+
config,
|
|
974
|
+
bus=bus,
|
|
975
|
+
)
|
|
976
|
+
|
|
977
|
+
ui = LiveChatUI(
|
|
978
|
+
agent=agent,
|
|
979
|
+
saver=saver,
|
|
980
|
+
workspace=str(workspace),
|
|
981
|
+
instruction_manager=instruction_manager,
|
|
982
|
+
channels=channels,
|
|
983
|
+
heartbeat=heartbeat,
|
|
984
|
+
bus=bus,
|
|
985
|
+
cron=cron,
|
|
986
|
+
context=context
|
|
987
|
+
)
|
|
988
|
+
|
|
989
|
+
asyncio.run(ui.alisten())
|
|
990
|
+
|
|
991
|
+
# await ui.alisten()
|
|
992
|
+
|
|
993
|
+
|
|
994
|
+
|
|
995
|
+
async def _process_message(msg: InboundMessage, agent:Any, bus: MessageBus, cron: CronService) -> OutboundMessage | None:
|
|
996
|
+
""" """
|
|
997
|
+
if msg.channel == "system":
|
|
998
|
+
response = await _process_system_message(msg, agent, cron)
|
|
999
|
+
await bus.publish_outbound(response)
|
|
1000
|
+
|
|
1001
|
+
print(f"user>{msg.content}")
|
|
1002
|
+
session_key = f"{msg.channel}:{msg.chat_id}"
|
|
1003
|
+
config = {"configurable": {"thread_id": session_key}}
|
|
1004
|
+
|
|
1005
|
+
response = await agent.ainvoke(
|
|
1006
|
+
{"messages": [{"role": "user", "content": msg.content}]},
|
|
1007
|
+
config=config,
|
|
1008
|
+
context=AgentContext(
|
|
1009
|
+
working_directory=Path.cwd(),
|
|
1010
|
+
sandbox=None,
|
|
1011
|
+
channel=msg.channel,
|
|
1012
|
+
chat_id=msg.chat_id,
|
|
1013
|
+
cron_service=cron,
|
|
1014
|
+
workspace=Path.cwd(),
|
|
1015
|
+
)
|
|
1016
|
+
)
|
|
1017
|
+
print(f"Bot>{response['messages'][-1].content}")
|
|
1018
|
+
|
|
1019
|
+
response = OutboundMessage(
|
|
1020
|
+
channel=msg.channel,
|
|
1021
|
+
chat_id=msg.chat_id,
|
|
1022
|
+
content=response["messages"][-1].content,
|
|
1023
|
+
metadata=msg.metadata or {}
|
|
1024
|
+
)
|
|
1025
|
+
|
|
1026
|
+
await bus.publish_outbound(response)
|
|
1027
|
+
|
|
1028
|
+
|
|
1029
|
+
async def _process_system_message(msg: InboundMessage, agent: Any, cron: CronService) -> OutboundMessage | None:
|
|
1030
|
+
"""
|
|
1031
|
+
Process a system message (e.g. subagent announce).
|
|
1032
|
+
"""
|
|
1033
|
+
# logger.info(f"Processing system message from {msg.sender_id}")
|
|
1034
|
+
|
|
1035
|
+
# Parse origin from chat_id (format: "channel:chat_id")
|
|
1036
|
+
if ":" in msg.chat_id:
|
|
1037
|
+
parts = msg.chat_id.split(":", 1)
|
|
1038
|
+
origin_channel = parts[0]
|
|
1039
|
+
origin_chat_id = parts[1]
|
|
1040
|
+
else:
|
|
1041
|
+
# Fallback
|
|
1042
|
+
origin_channel = "cli"
|
|
1043
|
+
origin_chat_id = msg.chat_id
|
|
1044
|
+
|
|
1045
|
+
# Use the origin session for context
|
|
1046
|
+
session_key = f"{origin_channel}:{origin_chat_id}"
|
|
1047
|
+
config = {"configurable": {"thread_id": session_key}}
|
|
1048
|
+
response = await agent.ainvoke(
|
|
1049
|
+
{"message": msg.content},
|
|
1050
|
+
config=config,
|
|
1051
|
+
context=AgentContext(
|
|
1052
|
+
working_directory=Path.cwd(),
|
|
1053
|
+
sandbox=None,
|
|
1054
|
+
channel=origin_channel,
|
|
1055
|
+
chat_id=origin_chat_id,
|
|
1056
|
+
cron_service=cron,
|
|
1057
|
+
workspace=Path.cwd(),
|
|
1058
|
+
)
|
|
1059
|
+
)
|
|
1060
|
+
|
|
1061
|
+
return OutboundMessage(
|
|
1062
|
+
channel=origin_channel,
|
|
1063
|
+
chat_id=origin_chat_id,
|
|
1064
|
+
content=response["messages"][-1].content,
|
|
1065
|
+
metadata=msg.metadata or {}
|
|
1066
|
+
)
|
|
1067
|
+
|
|
1068
|
+
def claw_terminal():
|
|
1069
|
+
""" 以 claw mode 运行 """
|
|
1070
|
+
|
|
1071
|
+
from functools import partial
|
|
1072
|
+
from pathlib import Path
|
|
1073
|
+
import asyncio
|
|
1074
|
+
import sys
|
|
1075
|
+
import os
|
|
1076
|
+
|
|
1077
|
+
from src.claw.config.loader import load_config, get_data_dir
|
|
1078
|
+
from src.claw.bus.queue import MessageBus, InboundMessage, OutboundMessage
|
|
1079
|
+
from src.claw.channels.manager import ChannelManager
|
|
1080
|
+
from src.claw.cron.service import CronService
|
|
1081
|
+
from src.claw.cron.types_ import CronJob
|
|
1082
|
+
from src.claw.heartbeat.service import HeartbeatService
|
|
1083
|
+
|
|
1084
|
+
from src.claw.tools.base import AgentContext
|
|
1085
|
+
from src.claw.tools.cron import (
|
|
1086
|
+
add_cron_job,
|
|
1087
|
+
list_cron_jobs,
|
|
1088
|
+
remove_cron_job
|
|
1089
|
+
)
|
|
1090
|
+
from src.tools.tools import (
|
|
1091
|
+
SkillAgentContext,
|
|
1092
|
+
shell,
|
|
1093
|
+
bash,
|
|
1094
|
+
write_file,
|
|
1095
|
+
read_file,
|
|
1096
|
+
list_dir,
|
|
1097
|
+
glob,
|
|
1098
|
+
grep,
|
|
1099
|
+
edit
|
|
1100
|
+
)
|
|
1101
|
+
|
|
1102
|
+
from src.tools.web import (
|
|
1103
|
+
web_search,
|
|
1104
|
+
web_fetch
|
|
1105
|
+
)
|
|
1106
|
+
|
|
1107
|
+
from src.managers.sandbox import Container
|
|
1108
|
+
|
|
1109
|
+
from dotenv import load_dotenv, find_dotenv
|
|
1110
|
+
from langchain.agents import create_agent
|
|
1111
|
+
from langchain_openai import ChatOpenAI
|
|
1112
|
+
from langgraph.checkpoint.memory import InMemorySaver
|
|
1113
|
+
from langchain_core.tools import BaseTool
|
|
1114
|
+
|
|
1115
|
+
|
|
1116
|
+
from src.core.deepagents import create_deep_agent, get_default_model
|
|
1117
|
+
from src.managers.manager_context import ContextManager
|
|
1118
|
+
from src.utils.prompt import apply_template, apply_prompt
|
|
1119
|
+
from src.prompts.prompts import leader
|
|
1120
|
+
from functools import partial
|
|
1121
|
+
|
|
1122
|
+
from src.tui.clawtui import LiveChatUI
|
|
1123
|
+
|
|
1124
|
+
_ = load_dotenv(find_dotenv())
|
|
1125
|
+
|
|
1126
|
+
WORKSPACE = Path.cwd()
|
|
1127
|
+
|
|
1128
|
+
conf_path = WORKSPACE / "config.json"
|
|
1129
|
+
if not conf_path.exists():
|
|
1130
|
+
with open(conf_path, "w") as f:
|
|
1131
|
+
data = {
|
|
1132
|
+
"channels": {
|
|
1133
|
+
"feishu": {
|
|
1134
|
+
"enabled": True,
|
|
1135
|
+
"appId": "cli_a909847e1278dcbd",
|
|
1136
|
+
"appSecret": "bE0usuE0MKUJVDWOo9olib5X8PKv66pK",
|
|
1137
|
+
"encryptKey": "",
|
|
1138
|
+
"verificationToken": "",
|
|
1139
|
+
"allowFrom": []
|
|
1140
|
+
}
|
|
1141
|
+
}
|
|
1142
|
+
}
|
|
1143
|
+
json.dump(data, f)
|
|
1144
|
+
|
|
1145
|
+
config = load_config(
|
|
1146
|
+
config_path=conf_path
|
|
1147
|
+
)
|
|
1148
|
+
|
|
1149
|
+
def _prepare(workspace, run_mode, session_id):
|
|
1150
|
+
saver = InMemorySaver()
|
|
1151
|
+
|
|
1152
|
+
# 1. 构建上下文管理器
|
|
1153
|
+
dockerfile_path = os.path.join(Path(__file__).parent, "resources", "dockerfiles", "sandbox", "Dockerfile")
|
|
1154
|
+
context_manager = ContextManager(
|
|
1155
|
+
dockerfile_path=dockerfile_path,
|
|
1156
|
+
mode=run_mode
|
|
1157
|
+
)
|
|
1158
|
+
|
|
1159
|
+
context_manager.create_environment(
|
|
1160
|
+
session_id=session_id,
|
|
1161
|
+
workspace_path=workspace
|
|
1162
|
+
)
|
|
1163
|
+
|
|
1164
|
+
# 2. 指令管理器
|
|
1165
|
+
instruction_manager = context_manager.get_instruction_manager(
|
|
1166
|
+
session_id=session_id
|
|
1167
|
+
)
|
|
1168
|
+
|
|
1169
|
+
# 3. 代理管理器
|
|
1170
|
+
agent_manager = context_manager.get_agent_manager(
|
|
1171
|
+
session_id=session_id
|
|
1172
|
+
)
|
|
1173
|
+
|
|
1174
|
+
# 4. 沙箱
|
|
1175
|
+
sandbox = context_manager.get_container(
|
|
1176
|
+
session_id=session_id
|
|
1177
|
+
)
|
|
1178
|
+
|
|
1179
|
+
# 5. 工具箱
|
|
1180
|
+
toolkits = {
|
|
1181
|
+
"read-only": [ read_file, glob, grep, list_dir],
|
|
1182
|
+
"edit": [ edit, write_file],
|
|
1183
|
+
"search": [ web_search, web_fetch],
|
|
1184
|
+
"bash": [ shell],
|
|
1185
|
+
} if run_mode == "sandbox" else {
|
|
1186
|
+
"read-only": [ read_file, glob, grep, list_dir],
|
|
1187
|
+
"edit": [ edit, write_file],
|
|
1188
|
+
"search": [ web_search, web_fetch],
|
|
1189
|
+
"bash": [ bash ],
|
|
1190
|
+
}
|
|
1191
|
+
|
|
1192
|
+
# 6. 配置
|
|
1193
|
+
agents_conf = agent_manager.get_all_conf()
|
|
1194
|
+
|
|
1195
|
+
context = AgentContext(
|
|
1196
|
+
working_directory=workspace,
|
|
1197
|
+
sandbox=sandbox,
|
|
1198
|
+
channel=None,
|
|
1199
|
+
chat_id=None,
|
|
1200
|
+
cron_service=None,
|
|
1201
|
+
workspace=workspace,
|
|
1202
|
+
)
|
|
1203
|
+
|
|
1204
|
+
return agents_conf, toolkits, context, saver, instruction_manager
|
|
1205
|
+
|
|
1206
|
+
def _build_team(agents_conf: Dict[str, Any], domain: str, llm: ChatOpenAI, toolkits: Dict[str, List[BaseTool]], workspace: str, saver=None, store=None, recursion_limit=1000):
|
|
1207
|
+
|
|
1208
|
+
team_conf = agents_conf.setdefault(domain, {"members": {}})
|
|
1209
|
+
|
|
1210
|
+
agents = []
|
|
1211
|
+
for member_name, member in team_conf["members"].items():
|
|
1212
|
+
|
|
1213
|
+
tools = []
|
|
1214
|
+
for tool_name in member["tools"]:
|
|
1215
|
+
tools.extend(toolkits.get(tool_name, []))
|
|
1216
|
+
|
|
1217
|
+
llm_conf = member.get("llm", None)
|
|
1218
|
+
model = ChatOpenAI(**llm_conf) if llm_conf else get_default_model(streaming=False)
|
|
1219
|
+
|
|
1220
|
+
agent = {
|
|
1221
|
+
"name": member_name,
|
|
1222
|
+
"description": member["description"],
|
|
1223
|
+
"system_prompt": apply_template(
|
|
1224
|
+
agents_config=agents_conf,
|
|
1225
|
+
domain=domain,
|
|
1226
|
+
prompt_name=member_name,
|
|
1227
|
+
WORKSPACE=workspace
|
|
1228
|
+
),
|
|
1229
|
+
"context_schema": SkillAgentContext,
|
|
1230
|
+
"tools": tools,
|
|
1231
|
+
"model": model
|
|
1232
|
+
}
|
|
1233
|
+
|
|
1234
|
+
agents.append(agent)
|
|
1235
|
+
|
|
1236
|
+
tools = []
|
|
1237
|
+
for tool_name in ["filetools", "web"]:
|
|
1238
|
+
tools.extend([tool for tool in toolkits.get(tool_name, [])])
|
|
1239
|
+
|
|
1240
|
+
team = create_deep_agent(
|
|
1241
|
+
model=llm,
|
|
1242
|
+
subagents=agents,
|
|
1243
|
+
tools=tools,
|
|
1244
|
+
context_schema=AgentContext,
|
|
1245
|
+
# system_prompt=apply_prompt(leader, WORKSPACE=workspace),
|
|
1246
|
+
system_prompt=build_system_prompt(),
|
|
1247
|
+
checkpointer=saver
|
|
1248
|
+
).with_config({"recursion_limit": recursion_limit})
|
|
1249
|
+
|
|
1250
|
+
return team
|
|
1251
|
+
|
|
1252
|
+
|
|
1253
|
+
workspace = os.getcwd()
|
|
1254
|
+
run_mode = "local"
|
|
1255
|
+
session_id = "1"
|
|
1256
|
+
llm = get_default_model(streaming=True)
|
|
1257
|
+
agents_config, toolkits, context, saver, instruction_manager = _prepare(workspace, run_mode, session_id)
|
|
1258
|
+
agent = _build_team(
|
|
1259
|
+
agents_conf=agents_config,
|
|
1260
|
+
domain="coding",
|
|
1261
|
+
llm=llm,
|
|
1262
|
+
toolkits=toolkits,
|
|
1263
|
+
workspace=workspace,
|
|
1264
|
+
saver=saver,
|
|
1265
|
+
store=None,
|
|
1266
|
+
recursion_limit=1000
|
|
1267
|
+
)
|
|
1268
|
+
|
|
1269
|
+
# 创建事件总线
|
|
1270
|
+
bus = MessageBus()
|
|
1271
|
+
# 创建周期性任务
|
|
1272
|
+
cron_store_path = Path(os.getcwd()) /".autodev"/"cron"/"jobs.json"
|
|
1273
|
+
cron_store_path.parent.mkdir(parents=True, exist_ok=True)
|
|
1274
|
+
cron = CronService(cron_store_path)
|
|
1275
|
+
|
|
1276
|
+
# 设置周期回调函数
|
|
1277
|
+
async def on_cron_job(job: CronJob) -> str | None:
|
|
1278
|
+
""" Execute a cron job through the agent. """
|
|
1279
|
+
session_key = f"cron:{job.id}"
|
|
1280
|
+
channel = job.payload.channel or "cli"
|
|
1281
|
+
chat_id = job.payload.to or "direct"
|
|
1282
|
+
response = await agent.ainvoke(
|
|
1283
|
+
{"messages": [{"role": "user", "content": job.payload.message}]},
|
|
1284
|
+
context=AgentContext(
|
|
1285
|
+
working_directory=workspace,
|
|
1286
|
+
sandbox=None,
|
|
1287
|
+
channel=channel,
|
|
1288
|
+
chat_id=chat_id,
|
|
1289
|
+
cron_service=cron,
|
|
1290
|
+
workspace=workspace
|
|
1291
|
+
)
|
|
1292
|
+
)
|
|
1293
|
+
|
|
1294
|
+
resp = response["messages"][-1].content
|
|
1295
|
+
|
|
1296
|
+
if job.payload.deliver and job.payload.to:
|
|
1297
|
+
from src.claw.bus.events import OutboundMessage
|
|
1298
|
+
await bus.publish_outbound(
|
|
1299
|
+
OutboundMessage(
|
|
1300
|
+
channel=channel,
|
|
1301
|
+
chat_id=chat_id,
|
|
1302
|
+
content=resp or ""
|
|
1303
|
+
))
|
|
1304
|
+
|
|
1305
|
+
return resp
|
|
1306
|
+
|
|
1307
|
+
cron.on_job = on_cron_job
|
|
1308
|
+
|
|
1309
|
+
# 创建心跳服务
|
|
1310
|
+
async def on_heartbeat(prompt: str) -> str:
|
|
1311
|
+
""" Execute a heartbeat through the agent. """
|
|
1312
|
+
print(f"execute Heartbeat prompt: {prompt}")
|
|
1313
|
+
response = await agent.ainvoke(
|
|
1314
|
+
{"messages": [{"role": "user", "content": prompt}]},
|
|
1315
|
+
context=AgentContext(
|
|
1316
|
+
working_directory=workspace,
|
|
1317
|
+
sandbox=None,
|
|
1318
|
+
channel="cli",
|
|
1319
|
+
chat_id="direct",
|
|
1320
|
+
cron_service=cron,
|
|
1321
|
+
workspace=workspace,
|
|
1322
|
+
)
|
|
1323
|
+
)
|
|
1324
|
+
|
|
1325
|
+
resp = response["messages"][-1].content
|
|
1326
|
+
return resp
|
|
1327
|
+
|
|
1328
|
+
heartbeat = HeartbeatService(
|
|
1329
|
+
workspace=Path(workspace),
|
|
1330
|
+
on_heartbeat=on_heartbeat,
|
|
1331
|
+
interval_s=10,
|
|
1332
|
+
enabled=True
|
|
1333
|
+
)
|
|
1334
|
+
|
|
1335
|
+
# 创建渠道管理器
|
|
1336
|
+
channels = ChannelManager(
|
|
1337
|
+
config,
|
|
1338
|
+
bus=bus,
|
|
1339
|
+
)
|
|
1340
|
+
|
|
1341
|
+
|
|
1342
|
+
bus.subscribe_inbound("feishu", partial(_process_message, agent=agent, bus=bus, cron=cron))
|
|
1343
|
+
|
|
1344
|
+
async def run():
|
|
1345
|
+
try:
|
|
1346
|
+
await cron.start()
|
|
1347
|
+
await heartbeat.start()
|
|
1348
|
+
await asyncio.gather(
|
|
1349
|
+
bus.dispatch_inbound(),
|
|
1350
|
+
channels.start_all()
|
|
1351
|
+
)
|
|
1352
|
+
except KeyboardInterrupt:
|
|
1353
|
+
print("\nShutting down ...")
|
|
1354
|
+
heartbeat.stop()
|
|
1355
|
+
await cron.stop()
|
|
1356
|
+
await channels.stop_all()
|
|
1357
|
+
bus.stop()
|
|
1358
|
+
|
|
1359
|
+
asyncio.run(run())
|
|
1360
|
+
|
|
1361
|
+
|
|
1362
|
+
|
|
1363
|
+
|
|
1364
|
+
|
|
1365
|
+
|
|
1366
|
+
|
|
1367
|
+
|
|
660
1368
|
|
|
661
1369
|
if __name__ == "__main__":
|
|
662
1370
|
# pass
|
|
663
|
-
asyncio.run(teminal_chat())
|
|
1371
|
+
# asyncio.run(teminal_chat())
|
|
664
1372
|
# asyncio.run(agent_ui())
|
|
665
1373
|
# asyncio.run(team_main())
|
|
666
1374
|
# asyncio.run(asingle_agent())
|
|
667
1375
|
|
|
1376
|
+
# asyncio.run(claw_main())
|
|
1377
|
+
claw_main()
|
|
1378
|
+
# claw_terminal()
|
|
1379
|
+
|
|
1380
|
+
|
|
668
1381
|
|
|
669
1382
|
|
|
670
1383
|
|