oats-coder 1.0.2__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.
- oats/AGENT.dir.python.tools.json +1 -0
- oats/AGENT.python.tools.md +131 -0
- oats/agent/AGENT.dir.python.tools.json +1 -0
- oats/agent/AGENT.python.tools.md +19 -0
- oats/agent/agent.py +176 -0
- oats/agent/agent.py.AGENT.python.tools.json +7 -0
- oats/agent_get_tool_choices_for_prompt.py +32 -0
- oats/agent_get_tool_choices_for_prompt.py.AGENT.python.tools.json +7 -0
- oats/call_tool_with_loader1.py +430 -0
- oats/call_tool_with_loader1.py.AGENT.python.tools.json +7 -0
- oats/cli/AGENT.dir.python.tools.json +1 -0
- oats/cli/AGENT.python.tools.md +33 -0
- oats/cli/approval.py +154 -0
- oats/cli/approval.py.AGENT.python.tools.json +7 -0
- oats/cli/check_providers.py +25 -0
- oats/cli/check_providers.py.AGENT.python.tools.json +7 -0
- oats/cli/interactive.py +550 -0
- oats/cli/interactive.py.AGENT.python.tools.json +7 -0
- oats/cli/process_message.py +153 -0
- oats/cli/process_message.py.AGENT.python.tools.json +7 -0
- oats/cli/tui/AGENT.dir.python.tools.json +1 -0
- oats/cli/tui/AGENT.python.tools.md +19 -0
- oats/cli/tui/tui_banner.py +114 -0
- oats/cli/tui/tui_banner.py.AGENT.python.tools.json +7 -0
- oats/cli/tui/tui_consts.py +120 -0
- oats/cli/tui/tui_consts.py.AGENT.python.tools.json +7 -0
- oats/cli/tui/tui_utils.py +492 -0
- oats/cli/tui/tui_utils.py.AGENT.python.tools.json +7 -0
- oats/config/coder.json +27 -0
- oats/core/AGENT.dir.python.tools.json +1 -0
- oats/core/AGENT.python.tools.md +117 -0
- oats/core/__init__.py +19 -0
- oats/core/bus.py +170 -0
- oats/core/bus.py.AGENT.python.tools.json +7 -0
- oats/core/config.py +270 -0
- oats/core/config.py.AGENT.python.tools.json +7 -0
- oats/core/features.py +118 -0
- oats/core/features.py.AGENT.python.tools.json +7 -0
- oats/core/id.py +17 -0
- oats/core/id.py.AGENT.python.tools.json +7 -0
- oats/core/offline.py +93 -0
- oats/core/offline.py.AGENT.python.tools.json +7 -0
- oats/core/profiles.py +179 -0
- oats/core/profiles.py.AGENT.python.tools.json +7 -0
- oats/core/storage.py +207 -0
- oats/core/storage.py.AGENT.python.tools.json +7 -0
- oats/core/tokens.py +80 -0
- oats/core/tokens.py.AGENT.python.tools.json +7 -0
- oats/date.py +83 -0
- oats/date.py.AGENT.python.tools.json +7 -0
- oats/determine_best_tools1.py +311 -0
- oats/determine_best_tools1.py.AGENT.python.tools.json +7 -0
- oats/get_oat_config.py +12 -0
- oats/get_oat_config.py.AGENT.python.tools.json +3 -0
- oats/git/AGENT.dir.python.tools.json +1 -0
- oats/git/AGENT.python.tools.md +89 -0
- oats/git/__init__.py +6 -0
- oats/git/build_git_repo_to_dataset.py +185 -0
- oats/git/build_git_repo_to_dataset.py.AGENT.python.tools.json +7 -0
- oats/git/coauthor.py +75 -0
- oats/git/coauthor.py.AGENT.python.tools.json +7 -0
- oats/git/git_commit_search1.py +537 -0
- oats/git/git_commit_search1.py.AGENT.python.tools.json +7 -0
- oats/git/git_diff_extractor.py +221 -0
- oats/git/git_diff_extractor.py.AGENT.python.tools.json +7 -0
- oats/git/git_to_df_converter.py +115 -0
- oats/git/git_to_df_converter.py.AGENT.python.tools.json +7 -0
- oats/git/repo_to_parquet.py +81 -0
- oats/git/repo_to_parquet.py.AGENT.python.tools.json +7 -0
- oats/git/walk_up_dir_path_to_find_git_config.py +56 -0
- oats/git/walk_up_dir_path_to_find_git_config.py.AGENT.python.tools.json +7 -0
- oats/git/worktree.py +184 -0
- oats/git/worktree.py.AGENT.python.tools.json +7 -0
- oats/hook/AGENT.dir.python.tools.json +1 -0
- oats/hook/AGENT.python.tools.md +19 -0
- oats/hook/__init__.py +23 -0
- oats/hook/engine.py +225 -0
- oats/hook/engine.py.AGENT.python.tools.json +7 -0
- oats/load_tools_from_source1.py +594 -0
- oats/load_tools_from_source1.py.AGENT.python.tools.json +7 -0
- oats/log.py +233 -0
- oats/log.py.AGENT.python.tools.json +7 -0
- oats/lsp/AGENT.dir.python.tools.json +1 -0
- oats/lsp/AGENT.python.tools.md +19 -0
- oats/lsp/__init__.py +1 -0
- oats/lsp/client.py +381 -0
- oats/lsp/client.py.AGENT.python.tools.json +7 -0
- oats/mcp/AGENT.dir.python.tools.json +1 -0
- oats/mcp/AGENT.python.tools.md +159 -0
- oats/mcp/config.py +201 -0
- oats/mcp/config.py.AGENT.python.tools.json +7 -0
- oats/mcp/example_mcp_config.json +58 -0
- oats/mcp/fetch.py +405 -0
- oats/mcp/fetch.py.AGENT.python.tools.json +7 -0
- oats/mcp/index.py +381 -0
- oats/mcp/index.py.AGENT.python.tools.json +7 -0
- oats/mcp/intent.py +427 -0
- oats/mcp/intent.py.AGENT.python.tools.json +7 -0
- oats/mcp/models.py +304 -0
- oats/mcp/models.py.AGENT.python.tools.json +7 -0
- oats/mcp/orchestrator.py +653 -0
- oats/mcp/orchestrator.py.AGENT.python.tools.json +7 -0
- oats/mcp/ranking.py +243 -0
- oats/mcp/ranking.py.AGENT.python.tools.json +7 -0
- oats/mcp/registry.py +567 -0
- oats/mcp/registry.py.AGENT.python.tools.json +7 -0
- oats/mcp/resolver.py +588 -0
- oats/mcp/resolver.py.AGENT.python.tools.json +7 -0
- oats/mcp/tools.py +574 -0
- oats/mcp/tools.py.AGENT.python.tools.json +7 -0
- oats/mcp/tracker.py +254 -0
- oats/mcp/tracker.py.AGENT.python.tools.json +7 -0
- oats/memory/AGENT.dir.python.tools.json +1 -0
- oats/memory/AGENT.python.tools.md +19 -0
- oats/memory/__init__.py +14 -0
- oats/memory/manager.py +180 -0
- oats/memory/manager.py.AGENT.python.tools.json +7 -0
- oats/memory/models.py +97 -0
- oats/memory/models.py.AGENT.python.tools.json +7 -0
- oats/models.py +332 -0
- oats/models.py.AGENT.python.tools.json +7 -0
- oats/oweb/AGENT.dir.python.tools.json +1 -0
- oats/oweb/AGENT.python.tools.md +33 -0
- oats/oweb/get_auth.py +56 -0
- oats/oweb/get_auth.py.AGENT.python.tools.json +7 -0
- oats/oweb/login.py +72 -0
- oats/oweb/login.py.AGENT.python.tools.json +7 -0
- oats/plugins/AGENT.dir.python.tools.json +1 -0
- oats/plugins/AGENT.python.tools.md +33 -0
- oats/plugins/__init__.py +24 -0
- oats/plugins/loader.py +278 -0
- oats/plugins/loader.py.AGENT.python.tools.json +7 -0
- oats/plugins/manifest.py +171 -0
- oats/plugins/manifest.py.AGENT.python.tools.json +7 -0
- oats/pp.py +8 -0
- oats/pp.py.AGENT.python.tools.json +7 -0
- oats/provider/AGENT.dir.python.tools.json +1 -0
- oats/provider/AGENT.python.tools.md +33 -0
- oats/provider/models.py +249 -0
- oats/provider/models.py.AGENT.python.tools.json +7 -0
- oats/provider/provider.py +822 -0
- oats/provider/provider.py.AGENT.python.tools.json +7 -0
- oats/session/AGENT.dir.python.tools.json +1 -0
- oats/session/AGENT.python.tools.md +201 -0
- oats/session/__init__.py +35 -0
- oats/session/build_system_prompt.py +184 -0
- oats/session/build_system_prompt.py.AGENT.python.tools.json +7 -0
- oats/session/caveman.py +177 -0
- oats/session/caveman.py.AGENT.python.tools.json +7 -0
- oats/session/compaction.py +463 -0
- oats/session/compaction.py.AGENT.python.tools.json +7 -0
- oats/session/debug_trace.py +41 -0
- oats/session/debug_trace.py.AGENT.python.tools.json +7 -0
- oats/session/file_cache.py +108 -0
- oats/session/file_cache.py.AGENT.python.tools.json +7 -0
- oats/session/message.py +214 -0
- oats/session/message.py.AGENT.python.tools.json +7 -0
- oats/session/metrics.py +52 -0
- oats/session/metrics.py.AGENT.python.tools.json +7 -0
- oats/session/models.py +43 -0
- oats/session/models.py.AGENT.python.tools.json +5 -0
- oats/session/modes.py +107 -0
- oats/session/modes.py.AGENT.python.tools.json +7 -0
- oats/session/processor.py +1600 -0
- oats/session/processor.py.AGENT.python.tools.json +7 -0
- oats/session/screenshot_store.py +157 -0
- oats/session/screenshot_store.py.AGENT.python.tools.json +7 -0
- oats/session/session.py +224 -0
- oats/session/session.py.AGENT.python.tools.json +7 -0
- oats/session/skill_selector.py +156 -0
- oats/session/skill_selector.py.AGENT.python.tools.json +7 -0
- oats/session/task_budget.py +159 -0
- oats/session/task_budget.py.AGENT.python.tools.json +7 -0
- oats/session/token_budget.py +90 -0
- oats/session/token_budget.py.AGENT.python.tools.json +7 -0
- oats/session/tool_retention.py +80 -0
- oats/session/tool_retention.py.AGENT.python.tools.json +7 -0
- oats/session/usage.py +139 -0
- oats/session/usage.py.AGENT.python.tools.json +7 -0
- oats/tool/AGENT.dir.python.tools.json +1 -0
- oats/tool/AGENT.python.tools.md +299 -0
- oats/tool/agent_tool.py +447 -0
- oats/tool/agent_tool.py.AGENT.python.tools.json +7 -0
- oats/tool/aws_safety.py +189 -0
- oats/tool/aws_safety.py.AGENT.python.tools.json +7 -0
- oats/tool/bash.py +188 -0
- oats/tool/bash.py.AGENT.python.tools.json +7 -0
- oats/tool/edit.py +437 -0
- oats/tool/generate_readme.py +280 -0
- oats/tool/generate_readme.py.AGENT.python.tools.json +7 -0
- oats/tool/glob_tool.py +183 -0
- oats/tool/glob_tool.py.AGENT.python.tools.json +7 -0
- oats/tool/grep.py +337 -0
- oats/tool/grep.py.AGENT.python.tools.json +7 -0
- oats/tool/init_tools.py +152 -0
- oats/tool/init_tools.py.AGENT.python.tools.json +7 -0
- oats/tool/lsp_tool.py +315 -0
- oats/tool/lsp_tool.py.AGENT.python.tools.json +7 -0
- oats/tool/memory_tool.py +241 -0
- oats/tool/memory_tool.py.AGENT.python.tools.json +7 -0
- oats/tool/multiedit.py +198 -0
- oats/tool/multiedit.py.AGENT.python.tools.json +7 -0
- oats/tool/patch.py +343 -0
- oats/tool/patch.py.AGENT.python.tools.json +7 -0
- oats/tool/plan.py +318 -0
- oats/tool/plan.py.AGENT.python.tools.json +7 -0
- oats/tool/playwright_search.py +227 -0
- oats/tool/playwright_search.py.AGENT.python.tools.json +7 -0
- oats/tool/question.py +245 -0
- oats/tool/question.py.AGENT.python.tools.json +7 -0
- oats/tool/read.py +199 -0
- oats/tool/read.py.AGENT.python.tools.json +7 -0
- oats/tool/registry.py +184 -0
- oats/tool/registry.py.AGENT.python.tools.json +7 -0
- oats/tool/todowrite.py +224 -0
- oats/tool/todowrite.py.AGENT.python.tools.json +7 -0
- oats/tool/tool_search.py +176 -0
- oats/tool/tool_search.py.AGENT.python.tools.json +7 -0
- oats/tool/webfetch.py +200 -0
- oats/tool/webfetch.py.AGENT.python.tools.json +7 -0
- oats/tool/websearch.py +277 -0
- oats/tool/websearch.py.AGENT.python.tools.json +7 -0
- oats/tool/write.py +154 -0
- oats/tool/write.py.AGENT.python.tools.json +7 -0
- oats/trajectory/AGENT.dir.python.tools.json +1 -0
- oats/trajectory/AGENT.python.tools.md +61 -0
- oats/trajectory/__init__.py +17 -0
- oats/trajectory/logger.py +119 -0
- oats/trajectory/logger.py.AGENT.python.tools.json +7 -0
- oats/trajectory/metrics.py +222 -0
- oats/trajectory/metrics.py.AGENT.python.tools.json +7 -0
- oats/trajectory/report.py +37 -0
- oats/trajectory/report.py.AGENT.python.tools.json +7 -0
- oats/trajectory/retrieval.py +140 -0
- oats/trajectory/retrieval.py.AGENT.python.tools.json +7 -0
- oats/trajectory/store.py +366 -0
- oats/trajectory/store.py.AGENT.python.tools.json +7 -0
- oats_coder-1.0.2.dist-info/METADATA +271 -0
- oats_coder-1.0.2.dist-info/RECORD +242 -0
- oats_coder-1.0.2.dist-info/WHEEL +4 -0
- oats_coder-1.0.2.dist-info/entry_points.txt +4 -0
- oats_coder-1.0.2.dist-info/licenses/LICENSE +1 -0
oats/agent/agent.py
ADDED
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Agent definitions and management.
|
|
3
|
+
"""
|
|
4
|
+
from __future__ import annotations
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
from dataclasses import dataclass, field
|
|
8
|
+
from enum import Enum
|
|
9
|
+
from typing import Any
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class AgentType(str, Enum):
|
|
13
|
+
"""Types of sub-agents with different tool access levels."""
|
|
14
|
+
|
|
15
|
+
GENERAL = "general"
|
|
16
|
+
EXPLORE = "explore"
|
|
17
|
+
PLAN = "plan"
|
|
18
|
+
VERIFY = "verify"
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
# Per-type tool restrictions. None means all tools allowed.
|
|
22
|
+
AGENT_TYPE_TOOLS: dict[AgentType, set[str] | None] = {
|
|
23
|
+
AgentType.GENERAL: None, # all tools
|
|
24
|
+
AgentType.EXPLORE: {
|
|
25
|
+
"read", "glob", "grep", "bash", "webfetch", "websearch",
|
|
26
|
+
"question", "todowrite", "todoread",
|
|
27
|
+
},
|
|
28
|
+
AgentType.PLAN: {
|
|
29
|
+
"read", "glob", "grep", "question",
|
|
30
|
+
"plan_enter", "plan_exit", "plan_status",
|
|
31
|
+
"todowrite", "todoread", "webfetch", "websearch",
|
|
32
|
+
},
|
|
33
|
+
AgentType.VERIFY: {
|
|
34
|
+
"read", "glob", "grep", "bash", "question",
|
|
35
|
+
"todowrite", "todoread",
|
|
36
|
+
},
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
# Max iterations per agent type
|
|
40
|
+
AGENT_TYPE_MAX_ITERATIONS: dict[AgentType, int] = {
|
|
41
|
+
AgentType.GENERAL: 200,
|
|
42
|
+
AgentType.EXPLORE: 100,
|
|
43
|
+
AgentType.PLAN: 100,
|
|
44
|
+
AgentType.VERIFY: 100,
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
@dataclass
|
|
49
|
+
class Agent:
|
|
50
|
+
"""Definition of an AI agent."""
|
|
51
|
+
|
|
52
|
+
name: str
|
|
53
|
+
description: str = ""
|
|
54
|
+
prompt: str = ""
|
|
55
|
+
agent_type: AgentType = AgentType.GENERAL
|
|
56
|
+
model_id: str | None = None
|
|
57
|
+
provider_id: str | None = None
|
|
58
|
+
temperature: float | None = None
|
|
59
|
+
top_p: float | None = None
|
|
60
|
+
tools: list[str] = field(default_factory=list) # Tool names to enable
|
|
61
|
+
allowed_tools: set[str] | None = None # Restrict tool access
|
|
62
|
+
max_iterations: int = 200
|
|
63
|
+
options: dict[str, Any] = field(default_factory=dict)
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
# Built-in agents
|
|
67
|
+
BUILTIN_AGENTS: list[Agent] = [
|
|
68
|
+
Agent(
|
|
69
|
+
name="default",
|
|
70
|
+
description="General-purpose coding assistant",
|
|
71
|
+
prompt="You are a helpful AI coding assistant.",
|
|
72
|
+
agent_type=AgentType.GENERAL,
|
|
73
|
+
),
|
|
74
|
+
Agent(
|
|
75
|
+
name="coder",
|
|
76
|
+
description="Focused on writing and modifying code",
|
|
77
|
+
prompt="""You are an expert programmer. Focus on:
|
|
78
|
+
- Writing clean, efficient code
|
|
79
|
+
- Following best practices
|
|
80
|
+
- Using appropriate design patterns
|
|
81
|
+
- Adding helpful comments when needed""",
|
|
82
|
+
agent_type=AgentType.GENERAL,
|
|
83
|
+
),
|
|
84
|
+
Agent(
|
|
85
|
+
name="explorer",
|
|
86
|
+
description="Read-only codebase exploration specialist",
|
|
87
|
+
prompt="""You are a codebase exploration specialist. Focus on:
|
|
88
|
+
- Reading and understanding code structure
|
|
89
|
+
- Finding files, classes, and functions
|
|
90
|
+
- Answering questions about the codebase
|
|
91
|
+
- Do NOT modify any files""",
|
|
92
|
+
agent_type=AgentType.EXPLORE,
|
|
93
|
+
allowed_tools=AGENT_TYPE_TOOLS[AgentType.EXPLORE],
|
|
94
|
+
max_iterations=100,
|
|
95
|
+
),
|
|
96
|
+
Agent(
|
|
97
|
+
name="planner",
|
|
98
|
+
description="Plans implementation approaches",
|
|
99
|
+
prompt="""You are a software architect. Focus on:
|
|
100
|
+
- Designing implementation approaches
|
|
101
|
+
- Identifying files that need changes
|
|
102
|
+
- Considering trade-offs between approaches
|
|
103
|
+
- Creating step-by-step plans""",
|
|
104
|
+
agent_type=AgentType.PLAN,
|
|
105
|
+
allowed_tools=AGENT_TYPE_TOOLS[AgentType.PLAN],
|
|
106
|
+
max_iterations=100,
|
|
107
|
+
),
|
|
108
|
+
Agent(
|
|
109
|
+
name="reviewer",
|
|
110
|
+
description="Code review specialist",
|
|
111
|
+
prompt="""You are a code review expert. Focus on:
|
|
112
|
+
- Finding bugs and issues
|
|
113
|
+
- Suggesting improvements
|
|
114
|
+
- Checking for security vulnerabilities
|
|
115
|
+
- Ensuring code quality""",
|
|
116
|
+
agent_type=AgentType.VERIFY,
|
|
117
|
+
allowed_tools=AGENT_TYPE_TOOLS[AgentType.VERIFY],
|
|
118
|
+
max_iterations=100,
|
|
119
|
+
),
|
|
120
|
+
Agent(
|
|
121
|
+
name="explainer",
|
|
122
|
+
description="Explains code and concepts",
|
|
123
|
+
prompt="""You are a technical educator. Focus on:
|
|
124
|
+
- Clear, simple explanations
|
|
125
|
+
- Breaking down complex concepts
|
|
126
|
+
- Providing examples
|
|
127
|
+
- Answering follow-up questions""",
|
|
128
|
+
agent_type=AgentType.EXPLORE,
|
|
129
|
+
allowed_tools=AGENT_TYPE_TOOLS[AgentType.EXPLORE],
|
|
130
|
+
max_iterations=100,
|
|
131
|
+
),
|
|
132
|
+
]
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
class AgentRegistry:
|
|
136
|
+
"""Registry of available agents."""
|
|
137
|
+
|
|
138
|
+
def __init__(self) -> None:
|
|
139
|
+
self._agents: dict[str, Agent] = {}
|
|
140
|
+
# Load built-in agents
|
|
141
|
+
for agent in BUILTIN_AGENTS:
|
|
142
|
+
self.register(agent)
|
|
143
|
+
|
|
144
|
+
def register(self, agent: Agent) -> None:
|
|
145
|
+
"""Register an agent."""
|
|
146
|
+
self._agents[agent.name] = agent
|
|
147
|
+
|
|
148
|
+
def get(self, name: str) -> Agent | None:
|
|
149
|
+
"""Get an agent by name."""
|
|
150
|
+
return self._agents.get(name)
|
|
151
|
+
|
|
152
|
+
def list(self) -> list[Agent]:
|
|
153
|
+
"""List all agents."""
|
|
154
|
+
return list(self._agents.values())
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
# Global agent registry
|
|
158
|
+
_registry: AgentRegistry | None = None
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
def get_agent_registry() -> AgentRegistry:
|
|
162
|
+
"""Get the global agent registry."""
|
|
163
|
+
global _registry
|
|
164
|
+
if _registry is None:
|
|
165
|
+
_registry = AgentRegistry()
|
|
166
|
+
return _registry
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
def get_agent(name: str) -> Agent | None:
|
|
170
|
+
"""Get an agent by name."""
|
|
171
|
+
return get_agent_registry().get(name)
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
def list_agents() -> list[Agent]:
|
|
175
|
+
"""List all available agents."""
|
|
176
|
+
return get_agent_registry().list()
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"create_custom_agent": "create a new Agent dataclass instance with a custom name, prompt, and agent_type",
|
|
3
|
+
"register_agent": "register a custom Agent into the AgentRegistry so it can be retrieved by name",
|
|
4
|
+
"get_agent": "get an Agent by name from the global registry using the get_agent function",
|
|
5
|
+
"list_agents": "list all available agents from the global registry using the list_agents function",
|
|
6
|
+
"get_agent_registry": "get the global AgentRegistry singleton instance using the get_agent_registry function"
|
|
7
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
|
|
3
|
+
import argparse
|
|
4
|
+
|
|
5
|
+
from oats.get_oat_config import get_oat_config
|
|
6
|
+
from oats.models import OatPromptChoices
|
|
7
|
+
from oats.log import gl
|
|
8
|
+
|
|
9
|
+
log = gl('oat.get_choices')
|
|
10
|
+
|
|
11
|
+
def agent_get_tool_choices_for_prompt(prompt: str, top_k: int = 5, verbose: bool = False) -> OatPromptChoices:
|
|
12
|
+
oat_config = get_oat_config()
|
|
13
|
+
# multi-tool resolution depending on need/use case
|
|
14
|
+
choices = oat_config.get_prompt_choices(prompt=prompt, verbose=verbose)
|
|
15
|
+
if not choices.status:
|
|
16
|
+
choices = oat_config.get_best_matches_bm25(prompt=prompt, top_k=top_k, verbose=verbose)
|
|
17
|
+
return choices
|
|
18
|
+
|
|
19
|
+
def parse_args(args=None) -> argparse.Namespace:
|
|
20
|
+
parser = argparse.ArgumentParser(description='Get prompt choices from the OAT index.')
|
|
21
|
+
parser.add_argument('-p', '--prompt', type=str, required=True, help='The prompt text to extract choices for.')
|
|
22
|
+
parser.add_argument('-t', '--top-k', type=int, default=5, help='Number of top results to return when using BM25 (default: 5).')
|
|
23
|
+
parser.add_argument('-v', '--verbose', action='store_true', help='Enable verbose logging which will break jq command line piping for debugging')
|
|
24
|
+
return parser.parse_args(args)
|
|
25
|
+
|
|
26
|
+
def main(args=None):
|
|
27
|
+
args = parse_args(args)
|
|
28
|
+
result = agent_get_tool_choices_for_prompt(args.prompt, top_k=args.top_k, verbose=args.verbose)
|
|
29
|
+
print(result.model_dump_json(indent=2))
|
|
30
|
+
|
|
31
|
+
if __name__ == '__main__':
|
|
32
|
+
main()
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"get_tool_choices_for_prompt": "get tool choices for a prompt using the OAT index with multi-tool resolution",
|
|
3
|
+
"get_prompt_choices": "get prompt choices from the OAT config using the get_prompt_choices method",
|
|
4
|
+
"get_best_matches_bm25": "get the best BM25 matches for a prompt when prompt choices are unavailable",
|
|
5
|
+
"run_agent_get_tool_choices_cli": "run the CLI tool to get prompt choices from the OAT index with a prompt argument",
|
|
6
|
+
"parse_args": "parse command line arguments for the OAT prompt choices CLI including prompt top-k and verbose"
|
|
7
|
+
}
|
|
@@ -0,0 +1,430 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Call tool with loader — dynamically load tools from source and wrap them as LocalTool instances.
|
|
4
|
+
"""
|
|
5
|
+
from __future__ import annotations
|
|
6
|
+
|
|
7
|
+
import os
|
|
8
|
+
import sys
|
|
9
|
+
import traceback
|
|
10
|
+
import argparse
|
|
11
|
+
import asyncio
|
|
12
|
+
from pathlib import Path
|
|
13
|
+
from typing import Any, Tuple
|
|
14
|
+
import ujson as json
|
|
15
|
+
from oats.load_tools_from_source1 import get_best_tools_for_prompt
|
|
16
|
+
from oats.tool.registry import Tool, ToolContext, ToolResult
|
|
17
|
+
from oats.pp import pp
|
|
18
|
+
from oats.log import gl
|
|
19
|
+
|
|
20
|
+
log = gl(__name__)
|
|
21
|
+
|
|
22
|
+
DEFAULT_PROMPT = "get utc"
|
|
23
|
+
|
|
24
|
+
# ---------------------------------------------------------------------------
|
|
25
|
+
# LocalTool — a mutable Tool subclass backed by a real callable
|
|
26
|
+
# ---------------------------------------------------------------------------
|
|
27
|
+
|
|
28
|
+
class LocalTool(Tool):
|
|
29
|
+
"""A Tool whose attributes are set via setters rather than hard-coded properties."""
|
|
30
|
+
|
|
31
|
+
def __init__(self):
|
|
32
|
+
self._name: str = ""
|
|
33
|
+
self._aliases: list[str] = []
|
|
34
|
+
self._keywords: list[str] = []
|
|
35
|
+
self._always_load: bool = False
|
|
36
|
+
self._strict: bool = False
|
|
37
|
+
self._description: str = ""
|
|
38
|
+
self._parameters: dict[str, Any] = {}
|
|
39
|
+
self._requires_permission_msg: str | None = None
|
|
40
|
+
self._output: str = ""
|
|
41
|
+
self.tool_context: ToolContext | None = None
|
|
42
|
+
self._impl: Any = None # the underlying callable
|
|
43
|
+
|
|
44
|
+
# -- setters ----------------------------------------------------------
|
|
45
|
+
|
|
46
|
+
def set_name(self, value: str) -> None:
|
|
47
|
+
self._name = value
|
|
48
|
+
|
|
49
|
+
def set_aliases(self, value: list[str]) -> None:
|
|
50
|
+
self._aliases = value
|
|
51
|
+
|
|
52
|
+
def set_keywords(self, value: list[str]) -> None:
|
|
53
|
+
self._keywords = value
|
|
54
|
+
|
|
55
|
+
def set_always_load(self, value: bool) -> None:
|
|
56
|
+
self._always_load = value
|
|
57
|
+
|
|
58
|
+
def set_strict(self, value: bool) -> None:
|
|
59
|
+
self._strict = value
|
|
60
|
+
|
|
61
|
+
def set_description(self, value: str) -> None:
|
|
62
|
+
self._description = value
|
|
63
|
+
|
|
64
|
+
def set_parameters(self, value: dict[str, Any]) -> None:
|
|
65
|
+
self._parameters = value
|
|
66
|
+
|
|
67
|
+
def set_requires_permission(self, value: str | None) -> None:
|
|
68
|
+
self._requires_permission_msg = value
|
|
69
|
+
|
|
70
|
+
def set_output(self, value: str) -> None:
|
|
71
|
+
self._output = value
|
|
72
|
+
|
|
73
|
+
def set_tool_context(self, value: ToolContext) -> None:
|
|
74
|
+
self.tool_context = value
|
|
75
|
+
|
|
76
|
+
def set_impl(self, impl: Any) -> None:
|
|
77
|
+
self._impl = impl
|
|
78
|
+
|
|
79
|
+
# -- abstract properties (required by Tool ABC) -----------------------
|
|
80
|
+
|
|
81
|
+
@property
|
|
82
|
+
def name(self) -> str:
|
|
83
|
+
return self._name
|
|
84
|
+
|
|
85
|
+
@property
|
|
86
|
+
def description(self) -> str:
|
|
87
|
+
return self._description
|
|
88
|
+
|
|
89
|
+
@property
|
|
90
|
+
def parameters(self) -> dict[str, Any]:
|
|
91
|
+
return self._parameters
|
|
92
|
+
|
|
93
|
+
# -- optional overrides -----------------------------------------------
|
|
94
|
+
|
|
95
|
+
@property
|
|
96
|
+
def aliases(self) -> list[str]:
|
|
97
|
+
return self._aliases
|
|
98
|
+
|
|
99
|
+
@property
|
|
100
|
+
def keywords(self) -> list[str]:
|
|
101
|
+
return self._keywords
|
|
102
|
+
|
|
103
|
+
@property
|
|
104
|
+
def always_load(self) -> bool:
|
|
105
|
+
return self._always_load
|
|
106
|
+
|
|
107
|
+
@property
|
|
108
|
+
def strict(self) -> bool:
|
|
109
|
+
return self._strict
|
|
110
|
+
|
|
111
|
+
@property
|
|
112
|
+
def output(self) -> str:
|
|
113
|
+
return self._output
|
|
114
|
+
|
|
115
|
+
def requires_permission(self, args: dict[str, Any], ctx: ToolContext) -> str | None:
|
|
116
|
+
return self._requires_permission_msg
|
|
117
|
+
|
|
118
|
+
async def execute(self, args: dict[str, Any], ctx: ToolContext) -> ToolResult:
|
|
119
|
+
"""Execute the underlying callable and return a ToolResult."""
|
|
120
|
+
self.tool_context = ctx
|
|
121
|
+
try:
|
|
122
|
+
if self._impl is None:
|
|
123
|
+
return ToolResult(
|
|
124
|
+
title=self._name,
|
|
125
|
+
output="",
|
|
126
|
+
error=f"No implementation set for tool '{self._name}'",
|
|
127
|
+
)
|
|
128
|
+
result = self._impl(**args)
|
|
129
|
+
self._output = str(result)
|
|
130
|
+
return ToolResult(
|
|
131
|
+
title=self._name,
|
|
132
|
+
output=str(result),
|
|
133
|
+
)
|
|
134
|
+
except Exception as e:
|
|
135
|
+
return ToolResult(
|
|
136
|
+
title=f"{self._name} (error)",
|
|
137
|
+
output="",
|
|
138
|
+
error=str(e),
|
|
139
|
+
)
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
# ---------------------------------------------------------------------------
|
|
143
|
+
# Loader
|
|
144
|
+
# ---------------------------------------------------------------------------
|
|
145
|
+
|
|
146
|
+
def load_tools_from_repo_uses_index(prompt: str, file_path: str | None = None, min_score: float = 0.0, verbose: bool = False) -> Tuple[bool, list, dict, list, list[dict], dict, list, list]:
|
|
147
|
+
"""
|
|
148
|
+
Import source code using get_best_tools_for_prompt(), then create a
|
|
149
|
+
LocalTool for each matched tool.
|
|
150
|
+
|
|
151
|
+
Args:
|
|
152
|
+
file_path: Path to the tool-uses index JSON file (or None for default).
|
|
153
|
+
prompt: The prompt to match tools against.
|
|
154
|
+
|
|
155
|
+
Returns:
|
|
156
|
+
A list of LocalTool instances ready for use.
|
|
157
|
+
"""
|
|
158
|
+
if verbose:
|
|
159
|
+
log.info(f"load_tools_from_repo_uses_index: file_path={file_path!r}, prompt={prompt[0:2000]}")
|
|
160
|
+
|
|
161
|
+
local_tool_impls = []
|
|
162
|
+
local_tool_names = []
|
|
163
|
+
local_tools: list[LocalTool] = []
|
|
164
|
+
found_best_tool, all_tools, all_tool_impls, best_files, best_tools, best_impls = get_best_tools_for_prompt(prompt=prompt, min_score=min_score, tool_schema=file_path, verbose=verbose)
|
|
165
|
+
|
|
166
|
+
if verbose:
|
|
167
|
+
log.info(
|
|
168
|
+
f"found_best_tool={found_best_tool}, "
|
|
169
|
+
f"len(best_tools)={len(best_tools)}, "
|
|
170
|
+
f"len(best_impls)={len(best_impls)}, "
|
|
171
|
+
f"best_files={best_files}")
|
|
172
|
+
|
|
173
|
+
if not found_best_tool:
|
|
174
|
+
if verbose:
|
|
175
|
+
log.info(f"### Sorry!! No best tools found in file: {__file__} for prompt — returning empty list")
|
|
176
|
+
return found_best_tool, all_tools, all_tool_impls, best_files, best_tools, best_impls, local_tools, local_tool_names
|
|
177
|
+
|
|
178
|
+
for tool_schema in best_tools:
|
|
179
|
+
if "function" not in tool_schema:
|
|
180
|
+
continue
|
|
181
|
+
func_info = tool_schema["function"]
|
|
182
|
+
fname = func_info.get("name", "")
|
|
183
|
+
if not fname:
|
|
184
|
+
continue
|
|
185
|
+
|
|
186
|
+
impl = best_impls.get(fname)
|
|
187
|
+
|
|
188
|
+
lt = LocalTool()
|
|
189
|
+
lt.set_name(fname)
|
|
190
|
+
lt.set_description(func_info.get("description", ""))
|
|
191
|
+
lt.set_parameters(func_info.get("parameters", {}))
|
|
192
|
+
lt.set_aliases([])
|
|
193
|
+
lt.set_keywords([])
|
|
194
|
+
lt.set_always_load(True)
|
|
195
|
+
lt.set_strict(False)
|
|
196
|
+
lt.set_requires_permission(f"Execute tool: {fname}")
|
|
197
|
+
lt.set_output("")
|
|
198
|
+
if impl is not None:
|
|
199
|
+
lt.set_impl(impl)
|
|
200
|
+
if verbose:
|
|
201
|
+
log.info(f"Created LocalTool '{fname}' with impl")
|
|
202
|
+
else:
|
|
203
|
+
log.warning(f"Created LocalTool '{fname}' WITHOUT impl")
|
|
204
|
+
|
|
205
|
+
local_tool_names.append(fname)
|
|
206
|
+
local_tools.append(lt)
|
|
207
|
+
|
|
208
|
+
if verbose:
|
|
209
|
+
log.info(f"load_tools_from_repo_uses_index: created {len(local_tools)} LocalTool(s)")
|
|
210
|
+
log.debug('# Local Tools\n\n')
|
|
211
|
+
print(all_tools)
|
|
212
|
+
print(all_tool_impls)
|
|
213
|
+
print(best_files)
|
|
214
|
+
print(best_tools)
|
|
215
|
+
print(best_impls)
|
|
216
|
+
return found_best_tool, all_tools, all_tool_impls, best_files, best_tools, best_impls, local_tools, local_tool_names
|
|
217
|
+
|
|
218
|
+
|
|
219
|
+
def run_tool_call(
|
|
220
|
+
prompt: str,
|
|
221
|
+
tools: list,
|
|
222
|
+
tool_impls: dict,
|
|
223
|
+
provider_id: str | None = None,
|
|
224
|
+
api_base: str | None = None,
|
|
225
|
+
api_key: str | None = None,
|
|
226
|
+
model: str | None = None,
|
|
227
|
+
verbose: bool = False,
|
|
228
|
+
) -> Tuple[bool, str]:
|
|
229
|
+
|
|
230
|
+
if model is None:
|
|
231
|
+
model = 'openai/google/functiongemma-270m-it'
|
|
232
|
+
if api_base is None:
|
|
233
|
+
api_base = 'http://0.0.0.0:20700/v1'
|
|
234
|
+
if prompt == '{}':
|
|
235
|
+
raise Exception(f'# Sorry!! call_tool_with_loader1.run_tool_call requires a valid prompt: {prompt}')
|
|
236
|
+
messages = [{"role": "user", "content": prompt}]
|
|
237
|
+
api_key = os.getenv('TOOL_API_KEY', 'CHANGE_PASSWORD')
|
|
238
|
+
call_kwargs = dict(
|
|
239
|
+
model=model,
|
|
240
|
+
messages=messages,
|
|
241
|
+
tools=tools,
|
|
242
|
+
tool_choice="auto",
|
|
243
|
+
api_base=api_base,
|
|
244
|
+
api_key=api_key,
|
|
245
|
+
)
|
|
246
|
+
|
|
247
|
+
if verbose:
|
|
248
|
+
log.debug('-------\nTools - Start\n-------')
|
|
249
|
+
for tool_name in tools:
|
|
250
|
+
print(tool_name)
|
|
251
|
+
log.debug('-------\nTools - End\n-------')
|
|
252
|
+
log.debug('-------\nTools Impls\n-------')
|
|
253
|
+
num_tool_impls = len(tool_impls)
|
|
254
|
+
for tidx, tool_name in enumerate(tool_impls):
|
|
255
|
+
tool_data = tool_impls[tool_name]
|
|
256
|
+
log.info(f'## Tool {tidx + 1}/{num_tool_impls}\nname: **{tool_name}**\nimplementation:\n```\n{tool_data}\n```\n')
|
|
257
|
+
print('---')
|
|
258
|
+
log.debug('-------\nTools Impls - End\n-------')
|
|
259
|
+
|
|
260
|
+
# print(f"\n{'='*80}")
|
|
261
|
+
# print(f"Prompt : {prompt}")
|
|
262
|
+
# print(f"Model : {model}")
|
|
263
|
+
# print(f"API : {api_base}")
|
|
264
|
+
# print(f"Tools : {[t['function']['name'] for t in tools]}")
|
|
265
|
+
# print(f"{'='*80}\n")
|
|
266
|
+
|
|
267
|
+
if verbose:
|
|
268
|
+
log.info(f'loading litellm')
|
|
269
|
+
import litellm
|
|
270
|
+
if verbose:
|
|
271
|
+
log.info(f'### First turn calling tool with args:\n\n```\n{pp(call_kwargs)}\n```\n')
|
|
272
|
+
resp = litellm.completion(**call_kwargs)
|
|
273
|
+
msg = resp.choices[0].message
|
|
274
|
+
messages.append(msg.model_dump(exclude_none=True))
|
|
275
|
+
if not msg.tool_calls:
|
|
276
|
+
err = f'### Sorry!! {__file__} - no_tool_calls_call_tool_with_loader1 detected. model response:\n```\n{msg.content}\n```\n'
|
|
277
|
+
log.error(err)
|
|
278
|
+
print(err)
|
|
279
|
+
log.error('call_kwargs')
|
|
280
|
+
print(pp(call_kwargs))
|
|
281
|
+
log.error('msg')
|
|
282
|
+
print(msg)
|
|
283
|
+
return False, err
|
|
284
|
+
|
|
285
|
+
# print(f"Tool calls:\n{pp([tc.model_dump() for tc in msg.tool_calls])}\n")
|
|
286
|
+
|
|
287
|
+
for tc in msg.tool_calls:
|
|
288
|
+
fname = str(tc.function.name)
|
|
289
|
+
try:
|
|
290
|
+
fargs = json.loads(tc.function.arguments or "{}")
|
|
291
|
+
except Exception:
|
|
292
|
+
log.error(f"### Sorry failed to json.loads Tool '{fname}' raised:\n```\n{traceback.format_exc()}\n```\n")
|
|
293
|
+
fargs = {}
|
|
294
|
+
if fname not in tool_impls:
|
|
295
|
+
result = f"Error: tool '{fname}' not found"
|
|
296
|
+
log.error(result)
|
|
297
|
+
else:
|
|
298
|
+
try:
|
|
299
|
+
result = tool_impls[fname](**fargs)
|
|
300
|
+
if verbose:
|
|
301
|
+
log.info(f"Tool '{fname}' → {result}")
|
|
302
|
+
except Exception as exc:
|
|
303
|
+
result = f"### Sorry!! Error:\n```\n{traceback.format_exc()}\n```\n"
|
|
304
|
+
log.error(f"### Sorry failed to json.loads Tool '{fname}' raised:\n```\n{traceback.format_exc()}\n```\n")
|
|
305
|
+
|
|
306
|
+
messages.append({
|
|
307
|
+
"role": "tool",
|
|
308
|
+
"tool_call_id": tc.id,
|
|
309
|
+
"name": fname,
|
|
310
|
+
"content": str(result),
|
|
311
|
+
})
|
|
312
|
+
|
|
313
|
+
# print(f"Tool results:\n{pp(messages[-len(msg.tool_calls):])}\n")
|
|
314
|
+
|
|
315
|
+
call_kwargs["messages"] = messages
|
|
316
|
+
if verbose:
|
|
317
|
+
log.info("Second turn ...")
|
|
318
|
+
# print(pp(call_kwargs))
|
|
319
|
+
resp2 = litellm.completion(**call_kwargs)
|
|
320
|
+
if resp2 is None:
|
|
321
|
+
err = f'### Sorry!! Failed second_tool_call_turn with error:\n```\n{resp2}\n```\n'
|
|
322
|
+
log.error(err)
|
|
323
|
+
return False, err
|
|
324
|
+
if not hasattr(resp2, 'choices'):
|
|
325
|
+
err = f'### Sorry!! Failed second_tool_call_turn with error_missing_choices:\n```\n{resp2}\n```\n'
|
|
326
|
+
log.error(err)
|
|
327
|
+
return False, err
|
|
328
|
+
answer = str(resp2.choices[0].message.content)
|
|
329
|
+
# messages.append({"role": "assistant", "content": answer})
|
|
330
|
+
|
|
331
|
+
if verbose:
|
|
332
|
+
log.info(f"run_tool_call_answers: {answer}\n")
|
|
333
|
+
# print(f"\n{'='*80}")
|
|
334
|
+
# print("Full conversation:")
|
|
335
|
+
# print(f"{'='*80}\n")
|
|
336
|
+
# print(pp(messages))
|
|
337
|
+
return True, answer
|
|
338
|
+
|
|
339
|
+
# ---------------------------------------------------------------------------
|
|
340
|
+
# Entry point
|
|
341
|
+
# ---------------------------------------------------------------------------
|
|
342
|
+
|
|
343
|
+
def main():
|
|
344
|
+
api_base = os.getenv("TOOL_FUNCTION_1", "http://0.0.0.0:20700/v1")
|
|
345
|
+
|
|
346
|
+
parser = argparse.ArgumentParser(
|
|
347
|
+
description="Load tools from source index and wrap them as LocalTool instances"
|
|
348
|
+
)
|
|
349
|
+
parser.add_argument(
|
|
350
|
+
"-p", "--prompt",
|
|
351
|
+
default=DEFAULT_PROMPT,
|
|
352
|
+
help=f"Prompt to match tools against (default: {DEFAULT_PROMPT})",
|
|
353
|
+
)
|
|
354
|
+
parser.add_argument(
|
|
355
|
+
"-s", "--schema",
|
|
356
|
+
default=None,
|
|
357
|
+
help="Path to JSON tool-uses index file (default: $CODER_TOOL_USES_INDEX)",
|
|
358
|
+
)
|
|
359
|
+
parser.add_argument(
|
|
360
|
+
"-t", "--top-k",
|
|
361
|
+
type=int,
|
|
362
|
+
default=5,
|
|
363
|
+
help="Number of top candidate files (default: 5)",
|
|
364
|
+
)
|
|
365
|
+
parser.add_argument(
|
|
366
|
+
"-m", "--min-score",
|
|
367
|
+
type=float,
|
|
368
|
+
default=0.0,
|
|
369
|
+
help="Minimum retrieval score threshold (default: 0.0)",
|
|
370
|
+
)
|
|
371
|
+
parser.add_argument(
|
|
372
|
+
"-r", "--rerank",
|
|
373
|
+
action="store_true",
|
|
374
|
+
help="Apply cross-encoder reranker after BM25 retrieval",
|
|
375
|
+
)
|
|
376
|
+
parser.add_argument(
|
|
377
|
+
"--rerank-model",
|
|
378
|
+
default="cross-encoder/ms-marco-MiniLM-L-6-v2",
|
|
379
|
+
help="Cross-encoder model for reranking",
|
|
380
|
+
)
|
|
381
|
+
parser.add_argument(
|
|
382
|
+
"--bm25-model",
|
|
383
|
+
default="bm25",
|
|
384
|
+
help="Retrieval model: bm25 | tfidf | <sentence-transformer> (default: bm25)",
|
|
385
|
+
)
|
|
386
|
+
args = parser.parse_args()
|
|
387
|
+
|
|
388
|
+
log.info(f"prompt={args.prompt!r}, schema={args.schema!r}, top_k={args.top_k}")
|
|
389
|
+
|
|
390
|
+
found_best_tool, all_tools, all_tool_impls, best_files, best_tools, best_impls, local_tools, local_tool_names = load_tools_from_repo_uses_index(file_path=args.schema, prompt=args.prompt)
|
|
391
|
+
|
|
392
|
+
if not found_best_tool:
|
|
393
|
+
log.error("No LocalTools were created — check your prompt and schema.")
|
|
394
|
+
sys.exit(1)
|
|
395
|
+
|
|
396
|
+
# Print summary
|
|
397
|
+
print(f"\n{'='*80}")
|
|
398
|
+
print(f"Loaded {len(all_tools)} LocalTool(s) for prompt: {args.prompt!r}")
|
|
399
|
+
print(f"{'='*80}\n")
|
|
400
|
+
|
|
401
|
+
for lt in local_tools:
|
|
402
|
+
print(f" Tool: {lt.name}")
|
|
403
|
+
print(f" description : {lt.description[:100]}")
|
|
404
|
+
print(f" parameters : {lt.parameters}")
|
|
405
|
+
print(f" aliases : {lt.aliases}")
|
|
406
|
+
print(f" keywords : {lt.keywords}")
|
|
407
|
+
print(f" always_load : {lt.always_load}")
|
|
408
|
+
print(f" strict : {lt.strict}")
|
|
409
|
+
print()
|
|
410
|
+
|
|
411
|
+
# Demo: execute the first tool with empty args
|
|
412
|
+
if local_tools:
|
|
413
|
+
ctx = ToolContext(
|
|
414
|
+
session_id="test-session",
|
|
415
|
+
project_dir=Path("./.coder"),
|
|
416
|
+
working_dir=Path("./.coder"),
|
|
417
|
+
)
|
|
418
|
+
lt = local_tools[0]
|
|
419
|
+
log.info(f"Executing LocalTool '{lt.name}' with args={{}}")
|
|
420
|
+
result = asyncio.run(lt.execute({}, ctx))
|
|
421
|
+
print(f"Execution result for '{lt.name}':")
|
|
422
|
+
print(f" title : {result.title}")
|
|
423
|
+
print(f" output : {result.output}")
|
|
424
|
+
print(f" error : {result.error}")
|
|
425
|
+
|
|
426
|
+
print("\ndone")
|
|
427
|
+
|
|
428
|
+
|
|
429
|
+
if __name__ == "__main__":
|
|
430
|
+
main()
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"create_LocalTool": "create a LocalTool instance and set its name, description, parameters, and implementation callable",
|
|
3
|
+
"run_LocalTool_execute": "run the LocalTool execute method with args and a ToolContext to get a ToolResult",
|
|
4
|
+
"load_tools_from_repo_uses_index": "load tools from a source index file matched against a prompt and wrap them as LocalTool instances",
|
|
5
|
+
"run_tool_call": "run an LLM tool-calling loop using LiteLLM with dynamically loaded tool implementations and a user prompt",
|
|
6
|
+
"run_main_cli": "run the CLI to load tools from a repo index by prompt and execute the first matched tool"
|
|
7
|
+
}
|