sourcebot 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- sourcebot/__init__.py +9 -0
- sourcebot/__main__.py +17 -0
- sourcebot/bus/__init__.py +4 -0
- sourcebot/bus/channel_adapter.py +21 -0
- sourcebot/bus/event_bus.py +15 -0
- sourcebot/bus/message_models.py +33 -0
- sourcebot/bus/outbound_dispatcher.py +15 -0
- sourcebot/bus/session_manager.py +20 -0
- sourcebot/cli/commands/core/__init__.py +3 -0
- sourcebot/cli/commands/core/command_line.py +26 -0
- sourcebot/cli/commands/init_commands/__init__.py +3 -0
- sourcebot/cli/commands/init_commands/init_global_config.py +30 -0
- sourcebot/cli/commands/init_commands/init_workspace_config.py +18 -0
- sourcebot/cli/commands/run_commands/__init__.py +3 -0
- sourcebot/cli/commands/run_commands/command_line_tool.py +345 -0
- sourcebot/cli/commands/run_commands/safe_runner.py +47 -0
- sourcebot/cli/main.py +28 -0
- sourcebot/config/__init__.py +15 -0
- sourcebot/config/base.py +13 -0
- sourcebot/config/config_manager.py +367 -0
- sourcebot/config/exceptions.py +4 -0
- sourcebot/config/global_config.py +55 -0
- sourcebot/config/provider_config.py +62 -0
- sourcebot/config/workspace_config.py +106 -0
- sourcebot/context/__init__.py +5 -0
- sourcebot/context/context_builder.py +78 -0
- sourcebot/context/identity.py +19 -0
- sourcebot/context/message_builder.py +154 -0
- sourcebot/context/skill/__init__.py +7 -0
- sourcebot/context/skill/skill.py +11 -0
- sourcebot/context/skill/skill_context.py +10 -0
- sourcebot/context/skill/skill_loader.py +57 -0
- sourcebot/context/skill/skill_metadata.py +27 -0
- sourcebot/context/skill/skill_requirements.py +25 -0
- sourcebot/context/skill/skill_summary.py +31 -0
- sourcebot/conversation/__init__.py +2 -0
- sourcebot/conversation/service.py +191 -0
- sourcebot/docker_sandbox/__init__.py +3 -0
- sourcebot/docker_sandbox/docker_sandbox.py +113 -0
- sourcebot/llm/__init__.py +3 -0
- sourcebot/llm/anthropic/__init__.py +2 -0
- sourcebot/llm/anthropic/adapter.py +30 -0
- sourcebot/llm/anthropic/anthropic_llm_client.py +38 -0
- sourcebot/llm/anthropic/converter.py +59 -0
- sourcebot/llm/core/adapter.py +16 -0
- sourcebot/llm/core/client.py +16 -0
- sourcebot/llm/core/delta.py +12 -0
- sourcebot/llm/core/message.py +53 -0
- sourcebot/llm/core/message_converter.py +33 -0
- sourcebot/llm/core/response.py +30 -0
- sourcebot/llm/core/tool.py +7 -0
- sourcebot/llm/core/tool_converter.py +30 -0
- sourcebot/llm/core/tool_delta_aggregator.py +38 -0
- sourcebot/llm/llm_client_factory.py +13 -0
- sourcebot/llm/openai/__init__.py +2 -0
- sourcebot/llm/openai/adapter.py +27 -0
- sourcebot/llm/openai/converter.py +53 -0
- sourcebot/llm/openai/openai_llm_client.py +47 -0
- sourcebot/logging/__init__.py +3 -0
- sourcebot/logging/setup.py +33 -0
- sourcebot/memory/__init__.py +5 -0
- sourcebot/memory/file_store.py +23 -0
- sourcebot/memory/llm_consolidator.py +79 -0
- sourcebot/memory/service.py +116 -0
- sourcebot/memory/window_policy.py +36 -0
- sourcebot/prompt/__init__.py +4 -0
- sourcebot/prompt/deeomposer_prompt.py +420 -0
- sourcebot/prompt/identity_prompt.py +98 -0
- sourcebot/prompt/subagent_prompt.py +25 -0
- sourcebot/runtime/__init__.py +3 -0
- sourcebot/runtime/agent/__init__.py +3 -0
- sourcebot/runtime/agent/agent.py +130 -0
- sourcebot/runtime/agent/agent_factory.py +83 -0
- sourcebot/runtime/dag/planner/__init__.py +3 -0
- sourcebot/runtime/dag/planner/dag_planner.py +26 -0
- sourcebot/runtime/dag/planner/execution_scheduler.py +35 -0
- sourcebot/runtime/dag/planner/parallelism_optimizer.py +44 -0
- sourcebot/runtime/dag/planner/task_decomposer.py +37 -0
- sourcebot/runtime/dag/scheduler/__init__.py +3 -0
- sourcebot/runtime/dag/scheduler/dag_scheduler.py +319 -0
- sourcebot/runtime/dag/scheduler/retry_policy.py +27 -0
- sourcebot/runtime/dag/scheduler/run_store.py +58 -0
- sourcebot/runtime/dag/scheduler/state_store.py +40 -0
- sourcebot/runtime/dag/scheduler/task_graph.py +29 -0
- sourcebot/runtime/init_system.py +182 -0
- sourcebot/runtime/tool_executor.py +30 -0
- sourcebot/security/policy.py +23 -0
- sourcebot/session/__init__.py +4 -0
- sourcebot/session/jsonl_repository.py +142 -0
- sourcebot/session/repository.py +19 -0
- sourcebot/session/service.py +44 -0
- sourcebot/session/session.py +53 -0
- sourcebot/storage/__init__.py +3 -0
- sourcebot/storage/rules_loader.py +72 -0
- sourcebot/storage/skill_storage.py +51 -0
- sourcebot/tools/__init__.py +7 -0
- sourcebot/tools/base.py +182 -0
- sourcebot/tools/registry.py +81 -0
- sourcebot/tools/rule_detail.py +70 -0
- sourcebot/tools/rule_list.py +57 -0
- sourcebot/tools/shell.py +93 -0
- sourcebot/tools/skill_detail.py +61 -0
- sourcebot/tools/skill_list.py +68 -0
- sourcebot/utils/__init__.py +2 -0
- sourcebot/utils/output.py +79 -0
- sourcebot-0.1.0.dist-info/METADATA +318 -0
- sourcebot-0.1.0.dist-info/RECORD +110 -0
- sourcebot-0.1.0.dist-info/WHEEL +5 -0
- sourcebot-0.1.0.dist-info/entry_points.txt +2 -0
- sourcebot-0.1.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
# sourcebot/prompt/identity_prompt.py
|
|
2
|
+
IDENTITY_PROMPT = """
|
|
3
|
+
|
|
4
|
+
You are **sourcebot**, an autonomous AI assistant capable of solving tasks using tools.
|
|
5
|
+
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# Current Time
|
|
9
|
+
|
|
10
|
+
{now} ({tz})
|
|
11
|
+
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
# Runtime
|
|
15
|
+
|
|
16
|
+
{runtime}
|
|
17
|
+
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
# Workspace
|
|
21
|
+
|
|
22
|
+
Your workspace is located at:
|
|
23
|
+
|
|
24
|
+
{workspace_path}
|
|
25
|
+
|
|
26
|
+
Important directories:
|
|
27
|
+
|
|
28
|
+
Long-term memory
|
|
29
|
+
{workspace_path}/memory/MEMORY.md
|
|
30
|
+
|
|
31
|
+
History log
|
|
32
|
+
{workspace_path}/memory/HISTORY.md
|
|
33
|
+
|
|
34
|
+
Custom skills
|
|
35
|
+
{workspace_path}/skills/{{skill-name}}/SKILL.md
|
|
36
|
+
|
|
37
|
+
---
|
|
38
|
+
|
|
39
|
+
# Available Tools
|
|
40
|
+
|
|
41
|
+
You have access to the following tools. When you need to use a tool, the system will handle the function calling format automatically.
|
|
42
|
+
|
|
43
|
+
## shell
|
|
44
|
+
Execute shell commands in the runtime environment.
|
|
45
|
+
|
|
46
|
+
## skill_list
|
|
47
|
+
Return to the skill list.Directly calling this tool will return to the skill catalog list
|
|
48
|
+
|
|
49
|
+
## skill_detail
|
|
50
|
+
Get the details corresponding to the skill name
|
|
51
|
+
|
|
52
|
+
## rule_list
|
|
53
|
+
Returns the list of available rules. Call this tool to see all rules that must be followed.
|
|
54
|
+
|
|
55
|
+
## rule_detail
|
|
56
|
+
Get detailed information about a specific rule. Provide the rule name to retrieve its full content and requirements.
|
|
57
|
+
---
|
|
58
|
+
|
|
59
|
+
# Execution Strategy
|
|
60
|
+
|
|
61
|
+
When solving tasks:
|
|
62
|
+
|
|
63
|
+
1. Understand the problem
|
|
64
|
+
2. Use tools when necessary (the system will handle the function call format)
|
|
65
|
+
3. Observe tool results
|
|
66
|
+
4. Continue reasoning
|
|
67
|
+
5. Repeat until the task is solved
|
|
68
|
+
|
|
69
|
+
You may call tools multiple times.
|
|
70
|
+
|
|
71
|
+
---
|
|
72
|
+
|
|
73
|
+
# Memory
|
|
74
|
+
|
|
75
|
+
When remembering something important, write to:
|
|
76
|
+
|
|
77
|
+
{workspace_path}/memory/MEMORY.md
|
|
78
|
+
|
|
79
|
+
To recall past events, search:
|
|
80
|
+
|
|
81
|
+
{workspace_path}/memory/HISTORY.md
|
|
82
|
+
|
|
83
|
+
---
|
|
84
|
+
|
|
85
|
+
# Communication Rules
|
|
86
|
+
|
|
87
|
+
When responding to normal user questions:
|
|
88
|
+
|
|
89
|
+
Reply directly with text.
|
|
90
|
+
|
|
91
|
+
Do NOT call tools unless necessary.
|
|
92
|
+
|
|
93
|
+
---
|
|
94
|
+
|
|
95
|
+
# Style
|
|
96
|
+
|
|
97
|
+
Be helpful, accurate, and concise.
|
|
98
|
+
"""
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# sourcebot/prompt/subagent_prompt.py
|
|
2
|
+
SUBAGENT_PROMPT = """# Subagent
|
|
3
|
+
|
|
4
|
+
## Current Time
|
|
5
|
+
{now} ({tz})
|
|
6
|
+
|
|
7
|
+
You are a subagent spawned by the main agent to complete a specific task.
|
|
8
|
+
|
|
9
|
+
## Rules
|
|
10
|
+
1. Stay focused - complete only the assigned task, nothing else
|
|
11
|
+
2. Your final response will be reported back to the main agent
|
|
12
|
+
3. Do not initiate conversations or take on side tasks
|
|
13
|
+
4. Be concise but informative in your findings
|
|
14
|
+
|
|
15
|
+
## What You Can Do
|
|
16
|
+
- Read and write files in the workspace
|
|
17
|
+
- Execute shell commands
|
|
18
|
+
- Complete the task thoroughly
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
## Workspace
|
|
22
|
+
Your workspace is at: {workspace}
|
|
23
|
+
The skills currently assigned may not be sufficient for your current task. If necessary, please use the `skill_list` and `skill_detail` tools to obtain the optimal skills.
|
|
24
|
+
|
|
25
|
+
When you have completed the task, provide a clear summary of your findings or actions."""
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
# sourcebot/runtime/agent_runtime.py
|
|
2
|
+
import json
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
from typing import List, Dict, Any, Callable, Optional
|
|
5
|
+
import logging
|
|
6
|
+
from sourcebot.security.policy import SecurityPolicy
|
|
7
|
+
from sourcebot.tools.registry import ToolRegistry
|
|
8
|
+
from sourcebot.context import MessageBuilder
|
|
9
|
+
from sourcebot.utils import strip_think, parse_tool_args, ensure_string
|
|
10
|
+
from datetime import datetime
|
|
11
|
+
from sourcebot.runtime import ToolExecutor
|
|
12
|
+
from sourcebot.bus import EventBus, OutboundMessage
|
|
13
|
+
|
|
14
|
+
logger = logging.getLogger(__name__)
|
|
15
|
+
class Agent:
|
|
16
|
+
"""
|
|
17
|
+
A unified runtime environment for the main agent and sub-agents.
|
|
18
|
+
Supports:
|
|
19
|
+
- LLM loop
|
|
20
|
+
- Tool execution
|
|
21
|
+
- Policy hooks
|
|
22
|
+
- Message bus # TODO
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
def __init__(
|
|
26
|
+
self,
|
|
27
|
+
bus: EventBus,
|
|
28
|
+
llm,
|
|
29
|
+
tools: ToolRegistry,
|
|
30
|
+
message_builder: MessageBuilder,
|
|
31
|
+
policy: Optional[SecurityPolicy] = None,
|
|
32
|
+
task_id: Optional[str] = None,
|
|
33
|
+
task_description: Optional[str] = None,
|
|
34
|
+
max_tool_calls: int = 40,
|
|
35
|
+
max_iterations: int = 40,
|
|
36
|
+
):
|
|
37
|
+
self.bus = bus
|
|
38
|
+
self.llm = llm
|
|
39
|
+
self.tools = tools
|
|
40
|
+
self.message_builder = message_builder
|
|
41
|
+
self.policy = policy
|
|
42
|
+
self.task_id = task_id
|
|
43
|
+
self.task_description = task_description
|
|
44
|
+
# Limits
|
|
45
|
+
self.max_tool_calls = max_tool_calls
|
|
46
|
+
self.max_iterations = max_iterations
|
|
47
|
+
|
|
48
|
+
self.tool_executor = ToolExecutor(
|
|
49
|
+
tools,
|
|
50
|
+
timeout = 60,
|
|
51
|
+
retries = 2
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
async def run(
|
|
56
|
+
self,
|
|
57
|
+
messages: List[Dict[str, Any]],
|
|
58
|
+
use_tools: bool = True,
|
|
59
|
+
use_policy: bool = True,
|
|
60
|
+
):
|
|
61
|
+
"""
|
|
62
|
+
agent loop
|
|
63
|
+
"""
|
|
64
|
+
tools_used: list[str] = []
|
|
65
|
+
tool_counter = 0
|
|
66
|
+
logger.info(f"{self.task_id} Running")
|
|
67
|
+
for i in range(self.max_iterations):
|
|
68
|
+
|
|
69
|
+
# Policy before llm
|
|
70
|
+
if self.policy and use_policy:
|
|
71
|
+
await self.policy.before_llm(messages)
|
|
72
|
+
|
|
73
|
+
response = await self.llm.complete(
|
|
74
|
+
messages = messages,
|
|
75
|
+
tools = self.tools.get_definitions(),
|
|
76
|
+
)
|
|
77
|
+
# # Response adapter
|
|
78
|
+
llm_tool_calls = response.tool_calls
|
|
79
|
+
|
|
80
|
+
# Tool calls
|
|
81
|
+
if llm_tool_calls and use_tools:
|
|
82
|
+
|
|
83
|
+
messages = self.message_builder.add_assistant_message(
|
|
84
|
+
messages = messages,
|
|
85
|
+
content = response.content,
|
|
86
|
+
tool_calls = llm_tool_calls,
|
|
87
|
+
)
|
|
88
|
+
for tool_call in llm_tool_calls:
|
|
89
|
+
tool_name = tool_call.name
|
|
90
|
+
|
|
91
|
+
tools_used.append(tool_name)
|
|
92
|
+
|
|
93
|
+
tool_counter += 1
|
|
94
|
+
if tool_counter > self.max_tool_calls:
|
|
95
|
+
raise RuntimeError("Exceeded max tool calls")
|
|
96
|
+
args = parse_tool_args(tool_call.arguments)
|
|
97
|
+
|
|
98
|
+
# Policy before tool
|
|
99
|
+
if self.policy and use_policy:
|
|
100
|
+
await self.policy.before_tool(tool_name, args)
|
|
101
|
+
|
|
102
|
+
result = await self.tool_executor.execute(
|
|
103
|
+
tool_name,
|
|
104
|
+
args
|
|
105
|
+
)
|
|
106
|
+
|
|
107
|
+
# Policy after tool
|
|
108
|
+
if self.policy and use_policy:
|
|
109
|
+
await self.policy.after_tool(
|
|
110
|
+
tool_name,
|
|
111
|
+
result,
|
|
112
|
+
)
|
|
113
|
+
|
|
114
|
+
messages = self.message_builder.add_tool_result(
|
|
115
|
+
messages = messages,
|
|
116
|
+
tool_call_id = tool_call.id,
|
|
117
|
+
result = ensure_string(result),
|
|
118
|
+
)
|
|
119
|
+
|
|
120
|
+
# Final response
|
|
121
|
+
else:
|
|
122
|
+
logger.info(f"{self.task_id} Completed")
|
|
123
|
+
|
|
124
|
+
final = strip_think(response.content)
|
|
125
|
+
|
|
126
|
+
return final, self.task_id, tools_used
|
|
127
|
+
|
|
128
|
+
raise RuntimeError("Max iterations exceeded")
|
|
129
|
+
|
|
130
|
+
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
# sourcebot/runtime/agent/agent_factory.py
|
|
2
|
+
from sourcebot.runtime.agent import Agent
|
|
3
|
+
from typing import Optional
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
from sourcebot.tools import ToolRegistry
|
|
6
|
+
from sourcebot.llm import LLMClientFactory
|
|
7
|
+
|
|
8
|
+
import logging
|
|
9
|
+
logger = logging.getLogger(__name__)
|
|
10
|
+
class AgentFactory:
|
|
11
|
+
"""
|
|
12
|
+
Factory responsible for building Agent instances
|
|
13
|
+
for both main agent and subagents.
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
def __init__(
|
|
17
|
+
self,
|
|
18
|
+
bus,
|
|
19
|
+
message_builder,
|
|
20
|
+
policy,
|
|
21
|
+
main_provider_name,
|
|
22
|
+
main_model_name,
|
|
23
|
+
config_manager,
|
|
24
|
+
max_tool_calls: int = 40,
|
|
25
|
+
max_iterations: int = 40,
|
|
26
|
+
sub_provider_name: Optional[str] = None,
|
|
27
|
+
sub_model_name: Optional[str] = None,
|
|
28
|
+
tools: Optional[ToolRegistry] = None,
|
|
29
|
+
):
|
|
30
|
+
self.bus = bus
|
|
31
|
+
self.message_builder = message_builder
|
|
32
|
+
self.policy = policy
|
|
33
|
+
self.main_provider_name = main_provider_name
|
|
34
|
+
self.main_model_name = main_model_name
|
|
35
|
+
self.config_manager = config_manager
|
|
36
|
+
self.sub_provider_name = sub_provider_name
|
|
37
|
+
self.sub_model_name = sub_model_name
|
|
38
|
+
self.max_tool_calls = max_tool_calls
|
|
39
|
+
self.max_iterations = max_iterations
|
|
40
|
+
self.tools = tools
|
|
41
|
+
|
|
42
|
+
def build_main_agent(self) -> Agent:
|
|
43
|
+
"""
|
|
44
|
+
Build main agent.
|
|
45
|
+
"""
|
|
46
|
+
llm = LLMClientFactory.create_client(
|
|
47
|
+
config_manager = self.config_manager,
|
|
48
|
+
provider_name = self.main_provider_name,
|
|
49
|
+
model_name = self.main_model_name
|
|
50
|
+
)
|
|
51
|
+
return Agent(
|
|
52
|
+
bus = self.bus,
|
|
53
|
+
llm = llm,
|
|
54
|
+
tools = self.tools,
|
|
55
|
+
message_builder = self.message_builder,
|
|
56
|
+
policy = self.policy,
|
|
57
|
+
max_tool_calls = self.max_tool_calls,
|
|
58
|
+
max_iterations = self.max_iterations,
|
|
59
|
+
task_id = "main_agent"
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
def build_sub_agent(self, task_id, task_description) -> Agent:
|
|
63
|
+
"""
|
|
64
|
+
Build sub agent.
|
|
65
|
+
"""
|
|
66
|
+
llm = LLMClientFactory.create_client(
|
|
67
|
+
config_manager = self.config_manager,
|
|
68
|
+
provider_name = self.sub_provider_name or self.main_provider_name,
|
|
69
|
+
model_name = self.sub_model_name or self.main_model_name
|
|
70
|
+
)
|
|
71
|
+
return Agent(
|
|
72
|
+
bus = self.bus,
|
|
73
|
+
tools = self.tools,
|
|
74
|
+
llm = llm,
|
|
75
|
+
message_builder = self.message_builder,
|
|
76
|
+
policy = self.policy,
|
|
77
|
+
task_id = task_id,
|
|
78
|
+
task_description = task_description,
|
|
79
|
+
max_tool_calls = self.max_tool_calls,
|
|
80
|
+
max_iterations = self.max_iterations,
|
|
81
|
+
)
|
|
82
|
+
|
|
83
|
+
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# sourcebot/planning/dag_planner.py
|
|
2
|
+
|
|
3
|
+
import uuid
|
|
4
|
+
from typing import List, Dict
|
|
5
|
+
import logging
|
|
6
|
+
from .task_decomposer import TaskDecomposer
|
|
7
|
+
from .parallelism_optimizer import ParallelismOptimizer
|
|
8
|
+
from .execution_scheduler import ExecutionScheduler
|
|
9
|
+
|
|
10
|
+
logger = logging.getLogger(__name__)
|
|
11
|
+
class DAGPlanner:
|
|
12
|
+
|
|
13
|
+
def __init__(self, llm, context_build):
|
|
14
|
+
|
|
15
|
+
self.decomposer = TaskDecomposer(llm, context_build)
|
|
16
|
+
self.optimizer = ParallelismOptimizer()
|
|
17
|
+
self.scheduler = ExecutionScheduler()
|
|
18
|
+
|
|
19
|
+
async def plan(self, query: str):
|
|
20
|
+
steps = await self.decomposer.decompose(query)
|
|
21
|
+
steps = self.optimizer.optimize(steps)
|
|
22
|
+
levels = self.scheduler.build_levels(steps)
|
|
23
|
+
return {
|
|
24
|
+
"tasks": steps,
|
|
25
|
+
"levels": levels
|
|
26
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# sourcebot/planning/execution_scheduler.py
|
|
2
|
+
# DAG → Execution Levels
|
|
3
|
+
|
|
4
|
+
from typing import List, Dict
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class ExecutionScheduler:
|
|
8
|
+
|
|
9
|
+
def build_levels(self, tasks: List[Dict]) -> List[List[Dict]]:
|
|
10
|
+
"""
|
|
11
|
+
Convert DAG tasks into execution levels
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
remaining = {t["id"]: set(t.get("depends_on", [])) for t in tasks}
|
|
15
|
+
task_map = {t["id"]: t for t in tasks}
|
|
16
|
+
|
|
17
|
+
levels = []
|
|
18
|
+
|
|
19
|
+
while remaining:
|
|
20
|
+
|
|
21
|
+
ready = [tid for tid, deps in remaining.items() if not deps]
|
|
22
|
+
|
|
23
|
+
if not ready:
|
|
24
|
+
raise RuntimeError("Cycle detected in DAG")
|
|
25
|
+
|
|
26
|
+
level_tasks = [task_map[r] for r in ready]
|
|
27
|
+
levels.append(level_tasks)
|
|
28
|
+
|
|
29
|
+
for r in ready:
|
|
30
|
+
remaining.pop(r)
|
|
31
|
+
|
|
32
|
+
for deps in remaining.values():
|
|
33
|
+
deps.difference_update(ready)
|
|
34
|
+
|
|
35
|
+
return levels
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# sourcebot/planning/parallelism_optimizer.py
|
|
2
|
+
|
|
3
|
+
from typing import Dict, List
|
|
4
|
+
import re
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class ParallelismOptimizer:
|
|
8
|
+
|
|
9
|
+
def __init__(self):
|
|
10
|
+
pass
|
|
11
|
+
|
|
12
|
+
def build_dependencies(self, steps: List[str]) -> Dict[int, List[int]]:
|
|
13
|
+
"""
|
|
14
|
+
return:
|
|
15
|
+
step_index -> depends_on
|
|
16
|
+
"""
|
|
17
|
+
deps = {}
|
|
18
|
+
for i, step in enumerate(steps):
|
|
19
|
+
deps[i] = []
|
|
20
|
+
for j in range(i):
|
|
21
|
+
if self._has_dependency(steps[j], step):
|
|
22
|
+
deps[i].append(j)
|
|
23
|
+
return deps
|
|
24
|
+
|
|
25
|
+
def _has_dependency(self, a: str, b: str) -> bool:
|
|
26
|
+
keywords = [
|
|
27
|
+
"after",
|
|
28
|
+
"based on",
|
|
29
|
+
"use result",
|
|
30
|
+
"then",
|
|
31
|
+
"verify"
|
|
32
|
+
]
|
|
33
|
+
for k in keywords:
|
|
34
|
+
if k in b.lower():
|
|
35
|
+
return True
|
|
36
|
+
return False
|
|
37
|
+
|
|
38
|
+
def optimize(self, tasks):
|
|
39
|
+
# remove redundant dependencies
|
|
40
|
+
for t in tasks:
|
|
41
|
+
deps = set(t.get("depends_on", []))
|
|
42
|
+
# simple dedup
|
|
43
|
+
t["depends_on"] = list(deps)
|
|
44
|
+
return tasks
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# sourcebot/dag/planner/task_decomposer.py
|
|
2
|
+
|
|
3
|
+
from typing import List
|
|
4
|
+
import json
|
|
5
|
+
import re
|
|
6
|
+
import logging
|
|
7
|
+
from sourcebot.utils.output import extract_json
|
|
8
|
+
from sourcebot.llm.core.message import Message
|
|
9
|
+
logger = logging.getLogger(__name__)
|
|
10
|
+
|
|
11
|
+
class TaskDecomposer:
|
|
12
|
+
|
|
13
|
+
def __init__(self, llm, context_builder):
|
|
14
|
+
self.llm = llm
|
|
15
|
+
self.context_builder = context_builder
|
|
16
|
+
|
|
17
|
+
async def decompose(self, query: str) -> List[str]:
|
|
18
|
+
if not isinstance(query, str):
|
|
19
|
+
query = json.dumps(query, ensure_ascii=False, indent=2)
|
|
20
|
+
skills_summary = self.context_builder.build_skills_summary()
|
|
21
|
+
rules = self.context_builder.build_rulse()
|
|
22
|
+
decomposer_prompt = self.context_builder.build_decomposer_prompt()
|
|
23
|
+
messages = [
|
|
24
|
+
Message(role = "system", content = decomposer_prompt),
|
|
25
|
+
Message(role = "user", content = f"""
|
|
26
|
+
USER REQUEST
|
|
27
|
+
|
|
28
|
+
{query}
|
|
29
|
+
"""),
|
|
30
|
+
]
|
|
31
|
+
|
|
32
|
+
result = await self.llm.complete(messages)
|
|
33
|
+
try:
|
|
34
|
+
data = extract_json(result.content)
|
|
35
|
+
return data["tasks"]
|
|
36
|
+
except Exception as e:
|
|
37
|
+
logger.error(f"Failed to parse DAG JSON: {e}")
|