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.
Files changed (110) hide show
  1. sourcebot/__init__.py +9 -0
  2. sourcebot/__main__.py +17 -0
  3. sourcebot/bus/__init__.py +4 -0
  4. sourcebot/bus/channel_adapter.py +21 -0
  5. sourcebot/bus/event_bus.py +15 -0
  6. sourcebot/bus/message_models.py +33 -0
  7. sourcebot/bus/outbound_dispatcher.py +15 -0
  8. sourcebot/bus/session_manager.py +20 -0
  9. sourcebot/cli/commands/core/__init__.py +3 -0
  10. sourcebot/cli/commands/core/command_line.py +26 -0
  11. sourcebot/cli/commands/init_commands/__init__.py +3 -0
  12. sourcebot/cli/commands/init_commands/init_global_config.py +30 -0
  13. sourcebot/cli/commands/init_commands/init_workspace_config.py +18 -0
  14. sourcebot/cli/commands/run_commands/__init__.py +3 -0
  15. sourcebot/cli/commands/run_commands/command_line_tool.py +345 -0
  16. sourcebot/cli/commands/run_commands/safe_runner.py +47 -0
  17. sourcebot/cli/main.py +28 -0
  18. sourcebot/config/__init__.py +15 -0
  19. sourcebot/config/base.py +13 -0
  20. sourcebot/config/config_manager.py +367 -0
  21. sourcebot/config/exceptions.py +4 -0
  22. sourcebot/config/global_config.py +55 -0
  23. sourcebot/config/provider_config.py +62 -0
  24. sourcebot/config/workspace_config.py +106 -0
  25. sourcebot/context/__init__.py +5 -0
  26. sourcebot/context/context_builder.py +78 -0
  27. sourcebot/context/identity.py +19 -0
  28. sourcebot/context/message_builder.py +154 -0
  29. sourcebot/context/skill/__init__.py +7 -0
  30. sourcebot/context/skill/skill.py +11 -0
  31. sourcebot/context/skill/skill_context.py +10 -0
  32. sourcebot/context/skill/skill_loader.py +57 -0
  33. sourcebot/context/skill/skill_metadata.py +27 -0
  34. sourcebot/context/skill/skill_requirements.py +25 -0
  35. sourcebot/context/skill/skill_summary.py +31 -0
  36. sourcebot/conversation/__init__.py +2 -0
  37. sourcebot/conversation/service.py +191 -0
  38. sourcebot/docker_sandbox/__init__.py +3 -0
  39. sourcebot/docker_sandbox/docker_sandbox.py +113 -0
  40. sourcebot/llm/__init__.py +3 -0
  41. sourcebot/llm/anthropic/__init__.py +2 -0
  42. sourcebot/llm/anthropic/adapter.py +30 -0
  43. sourcebot/llm/anthropic/anthropic_llm_client.py +38 -0
  44. sourcebot/llm/anthropic/converter.py +59 -0
  45. sourcebot/llm/core/adapter.py +16 -0
  46. sourcebot/llm/core/client.py +16 -0
  47. sourcebot/llm/core/delta.py +12 -0
  48. sourcebot/llm/core/message.py +53 -0
  49. sourcebot/llm/core/message_converter.py +33 -0
  50. sourcebot/llm/core/response.py +30 -0
  51. sourcebot/llm/core/tool.py +7 -0
  52. sourcebot/llm/core/tool_converter.py +30 -0
  53. sourcebot/llm/core/tool_delta_aggregator.py +38 -0
  54. sourcebot/llm/llm_client_factory.py +13 -0
  55. sourcebot/llm/openai/__init__.py +2 -0
  56. sourcebot/llm/openai/adapter.py +27 -0
  57. sourcebot/llm/openai/converter.py +53 -0
  58. sourcebot/llm/openai/openai_llm_client.py +47 -0
  59. sourcebot/logging/__init__.py +3 -0
  60. sourcebot/logging/setup.py +33 -0
  61. sourcebot/memory/__init__.py +5 -0
  62. sourcebot/memory/file_store.py +23 -0
  63. sourcebot/memory/llm_consolidator.py +79 -0
  64. sourcebot/memory/service.py +116 -0
  65. sourcebot/memory/window_policy.py +36 -0
  66. sourcebot/prompt/__init__.py +4 -0
  67. sourcebot/prompt/deeomposer_prompt.py +420 -0
  68. sourcebot/prompt/identity_prompt.py +98 -0
  69. sourcebot/prompt/subagent_prompt.py +25 -0
  70. sourcebot/runtime/__init__.py +3 -0
  71. sourcebot/runtime/agent/__init__.py +3 -0
  72. sourcebot/runtime/agent/agent.py +130 -0
  73. sourcebot/runtime/agent/agent_factory.py +83 -0
  74. sourcebot/runtime/dag/planner/__init__.py +3 -0
  75. sourcebot/runtime/dag/planner/dag_planner.py +26 -0
  76. sourcebot/runtime/dag/planner/execution_scheduler.py +35 -0
  77. sourcebot/runtime/dag/planner/parallelism_optimizer.py +44 -0
  78. sourcebot/runtime/dag/planner/task_decomposer.py +37 -0
  79. sourcebot/runtime/dag/scheduler/__init__.py +3 -0
  80. sourcebot/runtime/dag/scheduler/dag_scheduler.py +319 -0
  81. sourcebot/runtime/dag/scheduler/retry_policy.py +27 -0
  82. sourcebot/runtime/dag/scheduler/run_store.py +58 -0
  83. sourcebot/runtime/dag/scheduler/state_store.py +40 -0
  84. sourcebot/runtime/dag/scheduler/task_graph.py +29 -0
  85. sourcebot/runtime/init_system.py +182 -0
  86. sourcebot/runtime/tool_executor.py +30 -0
  87. sourcebot/security/policy.py +23 -0
  88. sourcebot/session/__init__.py +4 -0
  89. sourcebot/session/jsonl_repository.py +142 -0
  90. sourcebot/session/repository.py +19 -0
  91. sourcebot/session/service.py +44 -0
  92. sourcebot/session/session.py +53 -0
  93. sourcebot/storage/__init__.py +3 -0
  94. sourcebot/storage/rules_loader.py +72 -0
  95. sourcebot/storage/skill_storage.py +51 -0
  96. sourcebot/tools/__init__.py +7 -0
  97. sourcebot/tools/base.py +182 -0
  98. sourcebot/tools/registry.py +81 -0
  99. sourcebot/tools/rule_detail.py +70 -0
  100. sourcebot/tools/rule_list.py +57 -0
  101. sourcebot/tools/shell.py +93 -0
  102. sourcebot/tools/skill_detail.py +61 -0
  103. sourcebot/tools/skill_list.py +68 -0
  104. sourcebot/utils/__init__.py +2 -0
  105. sourcebot/utils/output.py +79 -0
  106. sourcebot-0.1.0.dist-info/METADATA +318 -0
  107. sourcebot-0.1.0.dist-info/RECORD +110 -0
  108. sourcebot-0.1.0.dist-info/WHEEL +5 -0
  109. sourcebot-0.1.0.dist-info/entry_points.txt +2 -0
  110. 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,3 @@
1
+ from sourcebot.runtime.tool_executor import ToolExecutor
2
+ from sourcebot.runtime.init_system import InitSystem
3
+ __all__ = ["ToolExecutor", "InitSystem"]
@@ -0,0 +1,3 @@
1
+ from sourcebot.runtime.agent.agent import Agent
2
+ from sourcebot.runtime.agent.agent_factory import AgentFactory
3
+ __all__ = ["AgentFactory", "Agent"]
@@ -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,3 @@
1
+ from sourcebot.runtime.dag.planner.dag_planner import DAGPlanner
2
+
3
+ __all__ = ["DAGPlanner"]
@@ -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}")
@@ -0,0 +1,3 @@
1
+ from sourcebot.runtime.dag.scheduler.dag_scheduler import DAGScheduler
2
+
3
+ __all__ = ["DAGScheduler"]