tunacode-cli 0.0.44__py3-none-any.whl → 0.0.45__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.
Potentially problematic release.
This version of tunacode-cli might be problematic. Click here for more details.
- tunacode/cli/repl.py +13 -5
- tunacode/configuration/defaults.py +0 -1
- tunacode/constants.py +1 -1
- tunacode/core/agents/main.py +3 -72
- tunacode/core/recursive/aggregator.py +2 -2
- tunacode/core/recursive/decomposer.py +1 -1
- tunacode/core/recursive/executor.py +11 -8
- tunacode/ui/panels.py +9 -2
- {tunacode_cli-0.0.44.dist-info → tunacode_cli-0.0.45.dist-info}/METADATA +1 -3
- {tunacode_cli-0.0.44.dist-info → tunacode_cli-0.0.45.dist-info}/RECORD +14 -18
- tunacode/core/agents/dspy_integration.py +0 -223
- tunacode/core/agents/dspy_tunacode.py +0 -458
- tunacode/prompts/dspy_task_planning.md +0 -45
- tunacode/prompts/dspy_tool_selection.md +0 -58
- {tunacode_cli-0.0.44.dist-info → tunacode_cli-0.0.45.dist-info}/WHEEL +0 -0
- {tunacode_cli-0.0.44.dist-info → tunacode_cli-0.0.45.dist-info}/entry_points.txt +0 -0
- {tunacode_cli-0.0.44.dist-info → tunacode_cli-0.0.45.dist-info}/licenses/LICENSE +0 -0
- {tunacode_cli-0.0.44.dist-info → tunacode_cli-0.0.45.dist-info}/top_level.txt +0 -0
tunacode/cli/repl.py
CHANGED
|
@@ -385,9 +385,13 @@ async def repl(state_manager: StateManager):
|
|
|
385
385
|
|
|
386
386
|
state_manager.session.update_token_count()
|
|
387
387
|
context_display = get_context_window_display(state_manager.session.total_tokens, max_tokens)
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
388
|
+
|
|
389
|
+
# Only show startup info if thoughts are enabled or on first run
|
|
390
|
+
if state_manager.session.show_thoughts or not hasattr(state_manager.session, "_startup_shown"):
|
|
391
|
+
await ui.muted(f"• Model: {model_name} • {context_display}")
|
|
392
|
+
await ui.success("Ready to assist")
|
|
393
|
+
await ui.line()
|
|
394
|
+
state_manager.session._startup_shown = True
|
|
391
395
|
|
|
392
396
|
instance = agent.get_or_create_agent(state_manager.session.current_model, state_manager)
|
|
393
397
|
|
|
@@ -465,7 +469,11 @@ async def repl(state_manager: StateManager):
|
|
|
465
469
|
context_display = get_context_window_display(
|
|
466
470
|
state_manager.session.total_tokens, state_manager.session.max_tokens
|
|
467
471
|
)
|
|
468
|
-
|
|
472
|
+
# Only show model/context info if thoughts are enabled
|
|
473
|
+
if state_manager.session.show_thoughts:
|
|
474
|
+
await ui.muted(
|
|
475
|
+
f"• Model: {state_manager.session.current_model} • {context_display}"
|
|
476
|
+
)
|
|
469
477
|
|
|
470
478
|
if action == "restart":
|
|
471
479
|
await repl(state_manager)
|
|
@@ -480,7 +488,7 @@ async def repl(state_manager: StateManager):
|
|
|
480
488
|
total_cost = float(session_total.get("cost", 0) or 0)
|
|
481
489
|
|
|
482
490
|
# Only show summary if we have actual token usage
|
|
483
|
-
if total_tokens > 0 or total_cost > 0:
|
|
491
|
+
if state_manager.session.show_thoughts and (total_tokens > 0 or total_cost > 0):
|
|
484
492
|
summary = (
|
|
485
493
|
f"\n[bold cyan]TunaCode Session Summary[/bold cyan]\n"
|
|
486
494
|
f" - Total Tokens: {total_tokens:,}\n"
|
|
@@ -24,7 +24,6 @@ DEFAULT_USER_CONFIG: UserConfig = {
|
|
|
24
24
|
"fallback_response": True,
|
|
25
25
|
"fallback_verbosity": "normal", # Options: minimal, normal, detailed
|
|
26
26
|
"context_window_size": 200000,
|
|
27
|
-
"use_dspy_optimization": True, # Enable DSPy tool selection optimization
|
|
28
27
|
},
|
|
29
28
|
"mcpServers": {},
|
|
30
29
|
}
|
tunacode/constants.py
CHANGED
tunacode/core/agents/main.py
CHANGED
|
@@ -30,7 +30,6 @@ except ImportError:
|
|
|
30
30
|
STREAMING_AVAILABLE = False
|
|
31
31
|
|
|
32
32
|
from tunacode.constants import READ_ONLY_TOOLS
|
|
33
|
-
from tunacode.core.agents.dspy_integration import DSPyIntegration
|
|
34
33
|
from tunacode.core.recursive import RecursiveTaskExecutor
|
|
35
34
|
from tunacode.core.state import StateManager
|
|
36
35
|
from tunacode.core.token_usage.api_response_parser import ApiResponseParser
|
|
@@ -519,18 +518,6 @@ def get_or_create_agent(model: ModelName, state_manager: StateManager) -> Pydant
|
|
|
519
518
|
# Use a default system prompt if neither file exists
|
|
520
519
|
system_prompt = "You are a helpful AI assistant for software development tasks."
|
|
521
520
|
|
|
522
|
-
# Enhance with DSPy optimization if enabled
|
|
523
|
-
use_dspy = state_manager.session.user_config.get("settings", {}).get(
|
|
524
|
-
"use_dspy_optimization", True
|
|
525
|
-
)
|
|
526
|
-
if use_dspy:
|
|
527
|
-
try:
|
|
528
|
-
dspy_integration = DSPyIntegration(state_manager)
|
|
529
|
-
system_prompt = dspy_integration.enhance_system_prompt(system_prompt)
|
|
530
|
-
logger.info("Enhanced system prompt with DSPy optimizations")
|
|
531
|
-
except Exception as e:
|
|
532
|
-
logger.warning(f"Failed to enhance prompt with DSPy: {e}")
|
|
533
|
-
|
|
534
521
|
# Load TUNACODE.md context
|
|
535
522
|
# Use sync version of get_code_style to avoid nested event loop issues
|
|
536
523
|
try:
|
|
@@ -773,13 +760,6 @@ async def process_request(
|
|
|
773
760
|
"fallback_response", True
|
|
774
761
|
)
|
|
775
762
|
|
|
776
|
-
# Check if DSPy optimization is enabled and if this is a complex task
|
|
777
|
-
use_dspy = state_manager.session.user_config.get("settings", {}).get(
|
|
778
|
-
"use_dspy_optimization", True
|
|
779
|
-
)
|
|
780
|
-
dspy_integration = None
|
|
781
|
-
task_breakdown = None
|
|
782
|
-
|
|
783
763
|
# Check if recursive execution is enabled
|
|
784
764
|
use_recursive = state_manager.session.user_config.get("settings", {}).get(
|
|
785
765
|
"use_recursive_execution", True
|
|
@@ -788,39 +768,6 @@ async def process_request(
|
|
|
788
768
|
"recursive_complexity_threshold", 0.7
|
|
789
769
|
)
|
|
790
770
|
|
|
791
|
-
if use_dspy:
|
|
792
|
-
try:
|
|
793
|
-
dspy_integration = DSPyIntegration(state_manager)
|
|
794
|
-
|
|
795
|
-
# Check if this is a complex task that needs planning
|
|
796
|
-
if dspy_integration.should_use_task_planner(message):
|
|
797
|
-
task_breakdown = dspy_integration.get_task_breakdown(message)
|
|
798
|
-
if task_breakdown and task_breakdown.get("requires_todo"):
|
|
799
|
-
# Auto-create todos for complex tasks
|
|
800
|
-
from tunacode.tools.todo import TodoTool
|
|
801
|
-
|
|
802
|
-
todo_tool = TodoTool(state_manager=state_manager)
|
|
803
|
-
|
|
804
|
-
if state_manager.session.show_thoughts:
|
|
805
|
-
from tunacode.ui import console as ui
|
|
806
|
-
|
|
807
|
-
await ui.muted("DSPy: Detected complex task - creating todo list")
|
|
808
|
-
|
|
809
|
-
# Create todos from subtasks
|
|
810
|
-
todos = []
|
|
811
|
-
for subtask in task_breakdown["subtasks"][:5]: # Limit to first 5
|
|
812
|
-
todos.append(
|
|
813
|
-
{
|
|
814
|
-
"content": subtask["task"],
|
|
815
|
-
"priority": subtask.get("priority", "medium"),
|
|
816
|
-
}
|
|
817
|
-
)
|
|
818
|
-
|
|
819
|
-
if todos:
|
|
820
|
-
await todo_tool._execute(action="add_multiple", todos=todos)
|
|
821
|
-
except Exception as e:
|
|
822
|
-
logger.warning(f"DSPy task planning failed: {e}")
|
|
823
|
-
|
|
824
771
|
# Check if recursive execution should be used
|
|
825
772
|
if use_recursive and state_manager.session.current_recursion_depth == 0:
|
|
826
773
|
try:
|
|
@@ -853,25 +800,9 @@ async def process_request(
|
|
|
853
800
|
request=message, parent_task_id=None, depth=0
|
|
854
801
|
)
|
|
855
802
|
|
|
856
|
-
#
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
if success:
|
|
860
|
-
return AgentRun(
|
|
861
|
-
messages=[{"role": "assistant", "content": str(result)}],
|
|
862
|
-
timestamp=datetime.now(),
|
|
863
|
-
model=model,
|
|
864
|
-
iterations=1,
|
|
865
|
-
status="success",
|
|
866
|
-
)
|
|
867
|
-
else:
|
|
868
|
-
return AgentRun(
|
|
869
|
-
messages=[{"role": "assistant", "content": f"Task failed: {error}"}],
|
|
870
|
-
timestamp=datetime.now(),
|
|
871
|
-
model=model,
|
|
872
|
-
iterations=1,
|
|
873
|
-
status="error",
|
|
874
|
-
)
|
|
803
|
+
# For now, fall back to normal execution
|
|
804
|
+
# TODO: Properly integrate recursive execution results
|
|
805
|
+
pass
|
|
875
806
|
except Exception as e:
|
|
876
807
|
logger.warning(f"Recursive execution failed, falling back to normal: {e}")
|
|
877
808
|
# Continue with normal execution
|
|
@@ -39,7 +39,7 @@ class TaskResult:
|
|
|
39
39
|
|
|
40
40
|
task_id: str
|
|
41
41
|
task_title: str
|
|
42
|
-
result_data:
|
|
42
|
+
result_data: object
|
|
43
43
|
status: str # completed, failed, partial
|
|
44
44
|
timestamp: datetime = field(default_factory=datetime.now)
|
|
45
45
|
error: Optional[str] = None
|
|
@@ -50,7 +50,7 @@ class TaskResult:
|
|
|
50
50
|
class AggregatedResult:
|
|
51
51
|
"""Aggregated result from multiple tasks."""
|
|
52
52
|
|
|
53
|
-
primary_result:
|
|
53
|
+
primary_result: object
|
|
54
54
|
task_results: List[TaskResult]
|
|
55
55
|
strategy_used: AggregationStrategy
|
|
56
56
|
conflicts_resolved: int = 0
|
|
@@ -73,7 +73,7 @@ class TaskDecomposer:
|
|
|
73
73
|
# Get the main agent
|
|
74
74
|
agent = self.state_manager.session.agents.get("main")
|
|
75
75
|
if not agent:
|
|
76
|
-
logger.
|
|
76
|
+
logger.debug("Main agent not available, using heuristic decomposition")
|
|
77
77
|
return self._heuristic_decomposition(task_description)
|
|
78
78
|
|
|
79
79
|
# Build context-aware prompt
|
|
@@ -32,7 +32,7 @@ class TaskNode:
|
|
|
32
32
|
iteration_budget: int = 10
|
|
33
33
|
subtasks: List["TaskNode"] = field(default_factory=list)
|
|
34
34
|
status: str = "pending" # pending, in_progress, completed, failed
|
|
35
|
-
result: Optional[
|
|
35
|
+
result: Optional[object] = None
|
|
36
36
|
error: Optional[str] = None
|
|
37
37
|
context: Dict[str, Any] = field(default_factory=dict)
|
|
38
38
|
depth: int = 0
|
|
@@ -128,15 +128,17 @@ class RecursiveTaskExecutor:
|
|
|
128
128
|
and depth < self.max_depth - 1
|
|
129
129
|
):
|
|
130
130
|
# Decompose into subtasks
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
131
|
+
if self.state_manager.session.show_thoughts:
|
|
132
|
+
logger.info(
|
|
133
|
+
f"Decomposing complex task (score: {complexity_result.complexity_score:.2f})"
|
|
134
|
+
)
|
|
134
135
|
return await self._execute_with_decomposition(task_node, complexity_result)
|
|
135
136
|
else:
|
|
136
137
|
# Execute directly
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
138
|
+
if self.state_manager.session.show_thoughts:
|
|
139
|
+
logger.info(
|
|
140
|
+
f"Executing task directly (score: {complexity_result.complexity_score:.2f})"
|
|
141
|
+
)
|
|
140
142
|
return await self._execute_directly(task_node)
|
|
141
143
|
|
|
142
144
|
except Exception as e:
|
|
@@ -255,7 +257,8 @@ Respond in JSON format:
|
|
|
255
257
|
)
|
|
256
258
|
|
|
257
259
|
for i, (subtask_desc, budget) in enumerate(zip(subtasks, subtask_budgets)):
|
|
258
|
-
|
|
260
|
+
if self.state_manager.session.show_thoughts:
|
|
261
|
+
logger.info(f"Executing subtask {i + 1}/{len(subtasks)}: {subtask_desc[:50]}...")
|
|
259
262
|
|
|
260
263
|
# Create subtask context
|
|
261
264
|
subtask_context = {
|
tunacode/ui/panels.py
CHANGED
|
@@ -9,6 +9,7 @@ from rich.padding import Padding
|
|
|
9
9
|
from rich.panel import Panel
|
|
10
10
|
from rich.pretty import Pretty
|
|
11
11
|
from rich.table import Table
|
|
12
|
+
from rich.text import Text
|
|
12
13
|
|
|
13
14
|
from tunacode.configuration.models import ModelRegistry
|
|
14
15
|
from tunacode.constants import (
|
|
@@ -89,9 +90,15 @@ class StreamingAgentPanel:
|
|
|
89
90
|
# Use the UI_THINKING_MESSAGE constant instead of hardcoded text
|
|
90
91
|
from tunacode.constants import UI_THINKING_MESSAGE
|
|
91
92
|
|
|
92
|
-
|
|
93
|
+
# If no content, show thinking message with Rich markup
|
|
94
|
+
if not self.content:
|
|
95
|
+
content_to_display = Text.from_markup(UI_THINKING_MESSAGE)
|
|
96
|
+
else:
|
|
97
|
+
# Normal content is markdown
|
|
98
|
+
content_to_display = Markdown(self.content)
|
|
99
|
+
|
|
93
100
|
panel_obj = Panel(
|
|
94
|
-
Padding(
|
|
101
|
+
Padding(content_to_display, (0, 1, 0, 1)),
|
|
95
102
|
title=f"[bold]{self.title}[/bold]",
|
|
96
103
|
title_align="left",
|
|
97
104
|
border_style=colors.primary,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: tunacode-cli
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.45
|
|
4
4
|
Summary: Your agentic CLI developer.
|
|
5
5
|
Author-email: larock22 <noreply@github.com>
|
|
6
6
|
License-Expression: MIT
|
|
@@ -25,8 +25,6 @@ Requires-Dist: pydantic-ai[logfire]==0.2.6
|
|
|
25
25
|
Requires-Dist: pygments==2.19.1
|
|
26
26
|
Requires-Dist: rich==14.0.0
|
|
27
27
|
Requires-Dist: tiktoken>=0.5.2
|
|
28
|
-
Requires-Dist: dspy-ai>=0.1.0
|
|
29
|
-
Requires-Dist: python-dotenv>=1.0.0
|
|
30
28
|
Provides-Extra: dev
|
|
31
29
|
Requires-Dist: build; extra == "dev"
|
|
32
30
|
Requires-Dist: ruff; extra == "dev"
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
tunacode/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
2
|
-
tunacode/constants.py,sha256=
|
|
2
|
+
tunacode/constants.py,sha256=RivEbBYCMFVkeAO8WwknYut7dIRzAbDjtpVIWMYhdS8,5168
|
|
3
3
|
tunacode/context.py,sha256=_gXVCyjU052jlyRAl9tklZSwl5U_zI_EIX8XN87VVWE,2786
|
|
4
4
|
tunacode/exceptions.py,sha256=oDO1SVKOgjcKIylwqdbqh_g5my4roU5mB9Nv4n_Vb0s,3877
|
|
5
5
|
tunacode/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
@@ -7,7 +7,7 @@ tunacode/setup.py,sha256=XPt4eAK-qcIZQv64jGZ_ryxcImDwps9OmXjJfIS1xcs,1899
|
|
|
7
7
|
tunacode/types.py,sha256=Czq7jYXHq7fZQtyqkCN5_7eEu1wyYUcB50C6v3sTWDw,8188
|
|
8
8
|
tunacode/cli/__init__.py,sha256=zgs0UbAck8hfvhYsWhWOfBe5oK09ug2De1r4RuQZREA,55
|
|
9
9
|
tunacode/cli/main.py,sha256=erP6jNXcxVQOVn8sm6uNaLEAYevniVXl6Sem872mW68,2755
|
|
10
|
-
tunacode/cli/repl.py,sha256=
|
|
10
|
+
tunacode/cli/repl.py,sha256=58UJbKUcKCd1D1x0uOA5ipudZiiYOr-NXuJ6RmEn5_w,18962
|
|
11
11
|
tunacode/cli/commands/__init__.py,sha256=zmE9JcJ9Qd2xJhgdS4jMDJOoZsrAZmL5MAFxbKkk7F8,1670
|
|
12
12
|
tunacode/cli/commands/base.py,sha256=GxUuDsDSpz0iXryy8MrEw88UM3C3yxL__kDK1QhshoA,2517
|
|
13
13
|
tunacode/cli/commands/registry.py,sha256=XVuLpp5S4Fw7GfIZfLrVZFo4jMLMNmYNpYN7xWgXyOk,8223
|
|
@@ -19,7 +19,7 @@ tunacode/cli/commands/implementations/model.py,sha256=uthx6IX9KwgwywNTDklkJpqCba
|
|
|
19
19
|
tunacode/cli/commands/implementations/system.py,sha256=2cGw5iCJO3aNhXTFF28CgAIyLgslvHmpfyL2ZHVB6oQ,7903
|
|
20
20
|
tunacode/cli/commands/implementations/todo.py,sha256=Dtz5bgcuK2VXGPWEBBZQgnWUMYkRXNzTGf_qkVPLF2U,8125
|
|
21
21
|
tunacode/configuration/__init__.py,sha256=MbVXy8bGu0yKehzgdgZ_mfWlYGvIdb1dY2Ly75nfuPE,17
|
|
22
|
-
tunacode/configuration/defaults.py,sha256=
|
|
22
|
+
tunacode/configuration/defaults.py,sha256=lK_qf3BexmoQh7lbtxYG_ef0Kq3WyiLGOYmiVDO_amQ,840
|
|
23
23
|
tunacode/configuration/models.py,sha256=buH8ZquvcYI3OQBDIZeJ08cu00rSCeNABtUwl3VQS0E,4103
|
|
24
24
|
tunacode/configuration/settings.py,sha256=KoN0u6GG3Hh_TWt02D_wpRfbACYri3gCDTXHtJfHl2w,994
|
|
25
25
|
tunacode/core/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
@@ -27,18 +27,16 @@ tunacode/core/code_index.py,sha256=jgAx3lSWP_DwnyiP5Jkm1YvX4JJyI4teMzlNrJSpEOA,1
|
|
|
27
27
|
tunacode/core/state.py,sha256=2lsDgq0uIIc_MnXPE9SG_1fYFBDWWlgqgqm2Ik1iFBs,5599
|
|
28
28
|
tunacode/core/tool_handler.py,sha256=BPjR013OOO0cLXPdLeL2FDK0ixUwOYu59FfHdcdFhp4,2277
|
|
29
29
|
tunacode/core/agents/__init__.py,sha256=UUJiPYb91arwziSpjd7vIk7XNGA_4HQbsOIbskSqevA,149
|
|
30
|
-
tunacode/core/agents/
|
|
31
|
-
tunacode/core/agents/dspy_tunacode.py,sha256=tIluqDsHAv3nbNYtqFh4oyZA9yqMrnyY2G-QOpkTLCs,17398
|
|
32
|
-
tunacode/core/agents/main.py,sha256=xYzstmOJgxC1aJ2VbMWw5cevPp87IjpdWU45xydAKMQ,48882
|
|
30
|
+
tunacode/core/agents/main.py,sha256=jaEGGyqlBdu_GWGAQSIz0jUMkBxUebcgza4VrlrdQls,45967
|
|
33
31
|
tunacode/core/agents/utils.py,sha256=7kJAiUlkyWO3-b4T07XsGgycVrcNhv3NEPLdaktBnP4,12847
|
|
34
32
|
tunacode/core/background/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
35
33
|
tunacode/core/background/manager.py,sha256=rJdl3eDLTQwjbT7VhxXcJbZopCNR3M8ZGMbmeVnwwMc,1126
|
|
36
34
|
tunacode/core/llm/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
37
35
|
tunacode/core/recursive/__init__.py,sha256=S9_dN0faJqam3Pnaum9PRC8Hd90bpES8syFgAD8-QbI,446
|
|
38
|
-
tunacode/core/recursive/aggregator.py,sha256=
|
|
36
|
+
tunacode/core/recursive/aggregator.py,sha256=KlWajEyT8OokWeY6ZwQ4EsVu05V4KeH3o2cYUz-Ce1Y,15725
|
|
39
37
|
tunacode/core/recursive/budget.py,sha256=0wY6xSrZKmudUwthwX1mlcF5yat2_y3fNu5shX_IzvA,14131
|
|
40
|
-
tunacode/core/recursive/decomposer.py,sha256=
|
|
41
|
-
tunacode/core/recursive/executor.py,sha256=
|
|
38
|
+
tunacode/core/recursive/decomposer.py,sha256=k8rgaPEMMb2mZ9W0m_Yg0oEBqbYpqDnK0zyTkx6eTPs,14550
|
|
39
|
+
tunacode/core/recursive/executor.py,sha256=7taFsjYjSfL8OUQLM1ZVn9Xix7YoputFBAZSo9tDtDM,16646
|
|
42
40
|
tunacode/core/recursive/hierarchy.py,sha256=67tdDCKB5mTSn6VIwm-knEHB_VKUkeljFdwH62U5chE,15648
|
|
43
41
|
tunacode/core/setup/__init__.py,sha256=lzdpY6rIGf9DDlDBDGFvQZaSOQeFsNglHbkpq1-GtU8,376
|
|
44
42
|
tunacode/core/setup/agent_setup.py,sha256=trELO8cPnWo36BBnYmXDEnDPdhBg0p-VLnx9A8hSSSQ,1401
|
|
@@ -50,8 +48,6 @@ tunacode/core/setup/git_safety_setup.py,sha256=CRIqrQt0QUJQRS344njty_iCqTorrDhHl
|
|
|
50
48
|
tunacode/core/token_usage/api_response_parser.py,sha256=CTtqGaFaxpkzkW3TEbe00QJzyRULpWN1EQxIYMleseg,1622
|
|
51
49
|
tunacode/core/token_usage/cost_calculator.py,sha256=oQPMphGhqIt7NKdOg1o5Zbo59_nwFWmRJMQ30ViiCWs,1835
|
|
52
50
|
tunacode/core/token_usage/usage_tracker.py,sha256=uKCpdZgmDfLauwsawSCifMu0kJE3lAnK7Sjd-0KYUgA,3894
|
|
53
|
-
tunacode/prompts/dspy_task_planning.md,sha256=RNimkmnFcNgskwQrguGb3wB8A-Zngp6Qc9lXfPj61OU,2512
|
|
54
|
-
tunacode/prompts/dspy_tool_selection.md,sha256=CPBHrI4QlWD2mzNdoVkFK7GydgCLW9Bi20sD8ZEysRo,3070
|
|
55
51
|
tunacode/prompts/system.md,sha256=hXpjZ8Yiv2Acr2_6EmC2uOklP8FbmvyYR9oais-1KLk,16290
|
|
56
52
|
tunacode/services/__init__.py,sha256=w_E8QK6RnvKSvU866eDe8BCRV26rAm4d3R-Yg06OWCU,19
|
|
57
53
|
tunacode/services/mcp.py,sha256=R48X73KQjQ9vwhBrtbWHSBl-4K99QXmbIhh5J_1Gezo,3046
|
|
@@ -76,7 +72,7 @@ tunacode/ui/input.py,sha256=E_zAJqNYoAVFA-j4xE9Qgs22y-GrdSZNqiseX-Or0ho,2955
|
|
|
76
72
|
tunacode/ui/keybindings.py,sha256=h0MlD73CW_3i2dQzb9EFSPkqy0raZ_isgjxUiA9u6ts,691
|
|
77
73
|
tunacode/ui/lexers.py,sha256=tmg4ic1enyTRLzanN5QPP7D_0n12YjX_8ZhsffzhXA4,1340
|
|
78
74
|
tunacode/ui/output.py,sha256=51O0VHajte4dXHK5Az5SSP4IOb2q5SbCwvqdAoxyg7c,5665
|
|
79
|
-
tunacode/ui/panels.py,sha256=
|
|
75
|
+
tunacode/ui/panels.py,sha256=dBZEVIJqliWreY-hz6HpW5rdBmPOJZ6sPrv8KQ3eNhk,8570
|
|
80
76
|
tunacode/ui/prompt_manager.py,sha256=U2cntB34vm-YwOj3gzFRUK362zccrz8pigQfpxr5sv8,4650
|
|
81
77
|
tunacode/ui/recursive_progress.py,sha256=V0dGpJWt19TVArOYcQ3Lki8cR3ZepFT6iGwnChSFhFI,12906
|
|
82
78
|
tunacode/ui/tool_ui.py,sha256=qp1aZUpLO5UOdJziY8tw0URC8gjoWoSKdGu5y2wuTUU,7013
|
|
@@ -95,9 +91,9 @@ tunacode/utils/system.py,sha256=FSoibTIH0eybs4oNzbYyufIiV6gb77QaeY2yGqW39AY,1138
|
|
|
95
91
|
tunacode/utils/text_utils.py,sha256=6YBD9QfkDO44-6jxnwRWIpmfIifPG-NqMzy_O2NAouc,7277
|
|
96
92
|
tunacode/utils/token_counter.py,sha256=lLbkrNUraRQn5RMhwnGurqq1RHFDyn4AaFhruONWIxo,2690
|
|
97
93
|
tunacode/utils/user_configuration.py,sha256=Ilz8dpGVJDBE2iLWHAPT0xR8D51VRKV3kIbsAz8Bboc,3275
|
|
98
|
-
tunacode_cli-0.0.
|
|
99
|
-
tunacode_cli-0.0.
|
|
100
|
-
tunacode_cli-0.0.
|
|
101
|
-
tunacode_cli-0.0.
|
|
102
|
-
tunacode_cli-0.0.
|
|
103
|
-
tunacode_cli-0.0.
|
|
94
|
+
tunacode_cli-0.0.45.dist-info/licenses/LICENSE,sha256=Btzdu2kIoMbdSp6OyCLupB1aRgpTCJ_szMimgEnpkkE,1056
|
|
95
|
+
tunacode_cli-0.0.45.dist-info/METADATA,sha256=ZEVJDRAxDDjMG8DR64Hj9_Txw7h0lD9YwrhAAwft_nY,5137
|
|
96
|
+
tunacode_cli-0.0.45.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
97
|
+
tunacode_cli-0.0.45.dist-info/entry_points.txt,sha256=hbkytikj4dGu6rizPuAd_DGUPBGF191RTnhr9wdhORY,51
|
|
98
|
+
tunacode_cli-0.0.45.dist-info/top_level.txt,sha256=lKy2P6BWNi5XSA4DHFvyjQ14V26lDZctwdmhEJrxQbU,9
|
|
99
|
+
tunacode_cli-0.0.45.dist-info/RECORD,,
|
|
@@ -1,223 +0,0 @@
|
|
|
1
|
-
"""DSPy Integration for TunaCode - Enhanced Tool Selection and Task Planning.
|
|
2
|
-
|
|
3
|
-
This module integrates DSPy's optimized prompts and tool selection logic
|
|
4
|
-
into TunaCode's agent system for 3x performance improvements.
|
|
5
|
-
"""
|
|
6
|
-
|
|
7
|
-
import logging
|
|
8
|
-
import os
|
|
9
|
-
import re
|
|
10
|
-
from pathlib import Path
|
|
11
|
-
from typing import Any, Dict, List, Optional
|
|
12
|
-
|
|
13
|
-
from tunacode.core.state import StateManager
|
|
14
|
-
|
|
15
|
-
logger = logging.getLogger(__name__)
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
class DSPyIntegration:
|
|
19
|
-
"""Integrates DSPy optimization into TunaCode's agent system."""
|
|
20
|
-
|
|
21
|
-
def __init__(self, state_manager: StateManager):
|
|
22
|
-
self.state_manager = state_manager
|
|
23
|
-
self._dspy_agent = None
|
|
24
|
-
self._tool_selection_prompt = None
|
|
25
|
-
self._task_planning_prompt = None
|
|
26
|
-
self._load_prompts()
|
|
27
|
-
|
|
28
|
-
def _load_prompts(self):
|
|
29
|
-
"""Load DSPy-optimized prompts from files."""
|
|
30
|
-
prompts_dir = Path(__file__).parent.parent.parent / "prompts"
|
|
31
|
-
|
|
32
|
-
try:
|
|
33
|
-
# Load tool selection prompt
|
|
34
|
-
tool_selection_path = prompts_dir / "dspy_tool_selection.md"
|
|
35
|
-
if tool_selection_path.exists():
|
|
36
|
-
self._tool_selection_prompt = tool_selection_path.read_text(encoding="utf-8")
|
|
37
|
-
logger.debug("Loaded DSPy tool selection prompt")
|
|
38
|
-
|
|
39
|
-
# Load task planning prompt
|
|
40
|
-
task_planning_path = prompts_dir / "dspy_task_planning.md"
|
|
41
|
-
if task_planning_path.exists():
|
|
42
|
-
self._task_planning_prompt = task_planning_path.read_text(encoding="utf-8")
|
|
43
|
-
logger.debug("Loaded DSPy task planning prompt")
|
|
44
|
-
except Exception as e:
|
|
45
|
-
logger.error(f"Failed to load DSPy prompts: {e}")
|
|
46
|
-
|
|
47
|
-
def get_dspy_agent(self, api_key: Optional[str] = None):
|
|
48
|
-
"""Get or create the DSPy agent instance."""
|
|
49
|
-
if self._dspy_agent is None:
|
|
50
|
-
try:
|
|
51
|
-
# Import DSPy components
|
|
52
|
-
from tunacode.core.agents.dspy_tunacode import create_optimized_agent
|
|
53
|
-
|
|
54
|
-
# Use API key from environment or config
|
|
55
|
-
if not api_key:
|
|
56
|
-
api_key = os.getenv(
|
|
57
|
-
"OPENROUTER_API_KEY"
|
|
58
|
-
) or self.state_manager.session.user_config.get("env", {}).get(
|
|
59
|
-
"OPENROUTER_API_KEY"
|
|
60
|
-
)
|
|
61
|
-
|
|
62
|
-
if api_key:
|
|
63
|
-
self._dspy_agent = create_optimized_agent(api_key)
|
|
64
|
-
logger.info("DSPy agent initialized successfully")
|
|
65
|
-
else:
|
|
66
|
-
logger.warning("No OpenRouter API key found for DSPy optimization")
|
|
67
|
-
except Exception as e:
|
|
68
|
-
logger.error(f"Failed to initialize DSPy agent: {e}")
|
|
69
|
-
|
|
70
|
-
return self._dspy_agent
|
|
71
|
-
|
|
72
|
-
def enhance_system_prompt(self, base_prompt: str) -> str:
|
|
73
|
-
"""Enhance the system prompt with DSPy optimizations."""
|
|
74
|
-
if not self._tool_selection_prompt:
|
|
75
|
-
return base_prompt
|
|
76
|
-
|
|
77
|
-
# Extract the learned patterns from DSPy prompts
|
|
78
|
-
enhanced_sections = []
|
|
79
|
-
|
|
80
|
-
# Add DSPy tool selection insights
|
|
81
|
-
enhanced_sections.append("\n\n# DSPy-Optimized Tool Selection Patterns\n")
|
|
82
|
-
enhanced_sections.append("**Based on learned optimization patterns:**\n")
|
|
83
|
-
enhanced_sections.append("- Always batch 3-4 read-only tools for parallel execution")
|
|
84
|
-
enhanced_sections.append("- Group grep, list_dir, glob, read_file operations together")
|
|
85
|
-
enhanced_sections.append("- Execute write/modify operations sequentially")
|
|
86
|
-
enhanced_sections.append("- Use Chain of Thought reasoning for tool selection\n")
|
|
87
|
-
|
|
88
|
-
# Add specific examples from DSPy prompt
|
|
89
|
-
if "Example" in self._tool_selection_prompt:
|
|
90
|
-
enhanced_sections.append("\n## Optimal Tool Batching Examples:\n")
|
|
91
|
-
# Extract examples section
|
|
92
|
-
examples_match = re.search(
|
|
93
|
-
r"### Example.*?(?=###|\Z)", self._tool_selection_prompt, re.DOTALL
|
|
94
|
-
)
|
|
95
|
-
if examples_match:
|
|
96
|
-
enhanced_sections.append(examples_match.group(0))
|
|
97
|
-
|
|
98
|
-
return base_prompt + "".join(enhanced_sections)
|
|
99
|
-
|
|
100
|
-
def should_use_task_planner(self, user_request: str) -> bool:
|
|
101
|
-
"""Determine if the request is complex enough for task planning."""
|
|
102
|
-
complex_indicators = [
|
|
103
|
-
"implement",
|
|
104
|
-
"create",
|
|
105
|
-
"build",
|
|
106
|
-
"refactor",
|
|
107
|
-
"add feature",
|
|
108
|
-
"fix all",
|
|
109
|
-
"update multiple",
|
|
110
|
-
"migrate",
|
|
111
|
-
"integrate",
|
|
112
|
-
"debug",
|
|
113
|
-
"optimize performance",
|
|
114
|
-
"authentication",
|
|
115
|
-
"setup",
|
|
116
|
-
]
|
|
117
|
-
|
|
118
|
-
request_lower = user_request.lower()
|
|
119
|
-
|
|
120
|
-
# Check for multiple files
|
|
121
|
-
file_pattern = r"\b\w+\.\w+\b"
|
|
122
|
-
files_mentioned = len(re.findall(file_pattern, user_request)) > 2
|
|
123
|
-
|
|
124
|
-
# Check for complex keywords
|
|
125
|
-
has_complex_keyword = any(indicator in request_lower for indicator in complex_indicators)
|
|
126
|
-
|
|
127
|
-
# Check for multiple operations
|
|
128
|
-
operation_words = ["and", "then", "also", "after", "before", "plus"]
|
|
129
|
-
has_multiple_ops = sum(1 for word in operation_words if word in request_lower) >= 2
|
|
130
|
-
|
|
131
|
-
return files_mentioned or has_complex_keyword or has_multiple_ops
|
|
132
|
-
|
|
133
|
-
def optimize_tool_selection(
|
|
134
|
-
self, user_request: str, tools_to_execute: List[Dict[str, Any]]
|
|
135
|
-
) -> List[List[Dict[str, Any]]]:
|
|
136
|
-
"""Optimize tool selection using DSPy patterns.
|
|
137
|
-
|
|
138
|
-
Returns tool calls organized in optimal batches for parallel execution.
|
|
139
|
-
"""
|
|
140
|
-
if not tools_to_execute:
|
|
141
|
-
return []
|
|
142
|
-
|
|
143
|
-
# Try to use DSPy agent if available
|
|
144
|
-
dspy_agent = self.get_dspy_agent()
|
|
145
|
-
if dspy_agent:
|
|
146
|
-
try:
|
|
147
|
-
result = dspy_agent(user_request, self.state_manager.session.cwd or ".")
|
|
148
|
-
if hasattr(result, "tool_batches") and result.tool_batches:
|
|
149
|
-
return result.tool_batches
|
|
150
|
-
except Exception as e:
|
|
151
|
-
logger.debug(f"DSPy optimization failed, using fallback: {e}")
|
|
152
|
-
|
|
153
|
-
# Fallback: Apply DSPy-learned patterns manually
|
|
154
|
-
return self._apply_dspy_patterns(tools_to_execute)
|
|
155
|
-
|
|
156
|
-
def _apply_dspy_patterns(self, tools: List[Dict[str, Any]]) -> List[List[Dict[str, Any]]]:
|
|
157
|
-
"""Apply DSPy-learned batching patterns manually."""
|
|
158
|
-
from tunacode.constants import READ_ONLY_TOOLS
|
|
159
|
-
|
|
160
|
-
batches = []
|
|
161
|
-
current_batch = []
|
|
162
|
-
|
|
163
|
-
for tool in tools:
|
|
164
|
-
tool_name = tool.get("tool", "")
|
|
165
|
-
|
|
166
|
-
if tool_name in READ_ONLY_TOOLS:
|
|
167
|
-
current_batch.append(tool)
|
|
168
|
-
# Optimal batch size is 3-4 tools
|
|
169
|
-
if len(current_batch) >= 4:
|
|
170
|
-
batches.append(current_batch)
|
|
171
|
-
current_batch = []
|
|
172
|
-
else:
|
|
173
|
-
# Flush current batch if any
|
|
174
|
-
if current_batch:
|
|
175
|
-
batches.append(current_batch)
|
|
176
|
-
current_batch = []
|
|
177
|
-
# Add write/execute tool as single batch
|
|
178
|
-
batches.append([tool])
|
|
179
|
-
|
|
180
|
-
# Add remaining tools
|
|
181
|
-
if current_batch:
|
|
182
|
-
batches.append(current_batch)
|
|
183
|
-
|
|
184
|
-
return batches
|
|
185
|
-
|
|
186
|
-
def get_task_breakdown(self, complex_request: str) -> Optional[Dict[str, Any]]:
|
|
187
|
-
"""Get task breakdown for complex requests using DSPy."""
|
|
188
|
-
dspy_agent = self.get_dspy_agent()
|
|
189
|
-
if not dspy_agent:
|
|
190
|
-
return None
|
|
191
|
-
|
|
192
|
-
try:
|
|
193
|
-
result = dspy_agent(complex_request, self.state_manager.session.cwd or ".")
|
|
194
|
-
if result.get("is_complex") and result.get("subtasks"):
|
|
195
|
-
return {
|
|
196
|
-
"subtasks": result["subtasks"],
|
|
197
|
-
"total_tool_calls": result.get("total_tool_calls", 0),
|
|
198
|
-
"requires_todo": result.get("requires_todo", False),
|
|
199
|
-
"parallelization_opportunities": result.get("parallelization_opportunities", 0),
|
|
200
|
-
}
|
|
201
|
-
except Exception as e:
|
|
202
|
-
logger.error(f"Failed to get task breakdown: {e}")
|
|
203
|
-
|
|
204
|
-
return None
|
|
205
|
-
|
|
206
|
-
def format_chain_of_thought(self, request: str, tools: List[str]) -> str:
|
|
207
|
-
"""Format a Chain of Thought reasoning for tool selection."""
|
|
208
|
-
reasoning = f"Let's think step by step about '{request}':\n"
|
|
209
|
-
|
|
210
|
-
if "search" in request.lower() or "find" in request.lower():
|
|
211
|
-
reasoning += "1. This requires searching for information\n"
|
|
212
|
-
reasoning += "2. I'll use grep for content search and glob for file patterns\n"
|
|
213
|
-
reasoning += "3. These read-only tools can be executed in parallel\n"
|
|
214
|
-
elif "read" in request.lower() or "show" in request.lower():
|
|
215
|
-
reasoning += "1. This requires reading file contents\n"
|
|
216
|
-
reasoning += "2. I'll batch multiple read_file operations together\n"
|
|
217
|
-
reasoning += "3. All reads can happen in parallel for speed\n"
|
|
218
|
-
elif "fix" in request.lower() or "update" in request.lower():
|
|
219
|
-
reasoning += "1. First, I need to understand the current state\n"
|
|
220
|
-
reasoning += "2. I'll search and read relevant files in parallel\n"
|
|
221
|
-
reasoning += "3. Then make modifications sequentially for safety\n"
|
|
222
|
-
|
|
223
|
-
return reasoning
|
|
@@ -1,458 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env python3
|
|
2
|
-
"""
|
|
3
|
-
TunaCode DSPy Production Module
|
|
4
|
-
|
|
5
|
-
Optimizes tool selection and task planning for TunaCode using DSPy.
|
|
6
|
-
Includes 3-4 tool batching optimization for 3x performance gains.
|
|
7
|
-
"""
|
|
8
|
-
|
|
9
|
-
import json
|
|
10
|
-
import logging
|
|
11
|
-
import os
|
|
12
|
-
import re
|
|
13
|
-
from typing import Dict, List
|
|
14
|
-
|
|
15
|
-
import dspy
|
|
16
|
-
from dotenv import load_dotenv
|
|
17
|
-
|
|
18
|
-
# Load environment variables
|
|
19
|
-
load_dotenv()
|
|
20
|
-
|
|
21
|
-
# Configure logging
|
|
22
|
-
logger = logging.getLogger(__name__)
|
|
23
|
-
|
|
24
|
-
# Tool Categories for TunaCode
|
|
25
|
-
TOOL_CATEGORIES = {
|
|
26
|
-
"read_only": ["read_file", "grep", "list_dir", "glob"], # Parallel-executable
|
|
27
|
-
"task_management": ["todo"], # Fast, sequential
|
|
28
|
-
"write_execute": [
|
|
29
|
-
"write_file",
|
|
30
|
-
"update_file",
|
|
31
|
-
"run_command",
|
|
32
|
-
"bash",
|
|
33
|
-
], # Sequential, needs confirmation
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
ALL_TOOLS = [tool for category in TOOL_CATEGORIES.values() for tool in category]
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
class ToolSelectionSignature(dspy.Signature):
|
|
40
|
-
"""Select optimal tools with batching awareness."""
|
|
41
|
-
|
|
42
|
-
user_request: str = dspy.InputField(desc="The user's request or task")
|
|
43
|
-
current_directory: str = dspy.InputField(desc="Current working directory context")
|
|
44
|
-
tools_json: str = dspy.OutputField(
|
|
45
|
-
desc="JSON array of tool calls with batch grouping, e.g. [[tool1, tool2, tool3], [tool4]]"
|
|
46
|
-
)
|
|
47
|
-
requires_confirmation: bool = dspy.OutputField(
|
|
48
|
-
desc="Whether any tools require user confirmation"
|
|
49
|
-
)
|
|
50
|
-
reasoning: str = dspy.OutputField(desc="Explanation of tool choice and batching strategy")
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
class TaskPlanningSignature(dspy.Signature):
|
|
54
|
-
"""Break down complex tasks with tool hints."""
|
|
55
|
-
|
|
56
|
-
complex_request: str = dspy.InputField(desc="A complex task that needs breakdown")
|
|
57
|
-
subtasks_with_tools: str = dspy.OutputField(
|
|
58
|
-
desc="JSON array of {task, tools, priority} objects"
|
|
59
|
-
)
|
|
60
|
-
total_tool_calls: int = dspy.OutputField(desc="Estimated total number of tool calls")
|
|
61
|
-
requires_todo: bool = dspy.OutputField(desc="Whether todo tool should be used")
|
|
62
|
-
parallelization_opportunities: int = dspy.OutputField(
|
|
63
|
-
desc="Number of parallel execution opportunities"
|
|
64
|
-
)
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
class PathValidationSignature(dspy.Signature):
|
|
68
|
-
"""Validate and convert paths to relative format."""
|
|
69
|
-
|
|
70
|
-
path: str = dspy.InputField(desc="Path to validate")
|
|
71
|
-
current_directory: str = dspy.InputField(desc="Current working directory")
|
|
72
|
-
is_valid: bool = dspy.OutputField(desc="Whether path is valid relative path")
|
|
73
|
-
relative_path: str = dspy.OutputField(desc="Converted relative path")
|
|
74
|
-
reason: str = dspy.OutputField(desc="Validation result explanation")
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
class OptimizedToolSelector(dspy.Module):
|
|
78
|
-
"""Tool selection with batching optimization"""
|
|
79
|
-
|
|
80
|
-
def __init__(self):
|
|
81
|
-
self.predict = dspy.ChainOfThought(ToolSelectionSignature)
|
|
82
|
-
|
|
83
|
-
def forward(self, user_request: str, current_directory: str = "."):
|
|
84
|
-
logger.debug(f"Tool Selection for: {user_request}")
|
|
85
|
-
result = self.predict(user_request=user_request, current_directory=current_directory)
|
|
86
|
-
|
|
87
|
-
# Parse and validate tool batches
|
|
88
|
-
try:
|
|
89
|
-
tool_batches = json.loads(result.tools_json)
|
|
90
|
-
validated_batches = self._validate_batches(tool_batches)
|
|
91
|
-
result.tool_batches = validated_batches
|
|
92
|
-
except Exception as e:
|
|
93
|
-
logger.error(f"Failed to parse tool batches: {e}")
|
|
94
|
-
result.tool_batches = []
|
|
95
|
-
|
|
96
|
-
return result
|
|
97
|
-
|
|
98
|
-
def _validate_batches(self, batches: List[List[str]]) -> List[List[Dict]]:
|
|
99
|
-
"""Validate and optimize tool batches for 3-4 tool rule"""
|
|
100
|
-
validated = []
|
|
101
|
-
|
|
102
|
-
for batch in batches:
|
|
103
|
-
# Check if batch contains only read-only tools
|
|
104
|
-
all_read_only = all(
|
|
105
|
-
any(tool_name in tool for tool_name in TOOL_CATEGORIES["read_only"])
|
|
106
|
-
for tool in batch
|
|
107
|
-
)
|
|
108
|
-
|
|
109
|
-
# Optimize batch size (3-4 tools is optimal)
|
|
110
|
-
if all_read_only and len(batch) > 4:
|
|
111
|
-
# Split large batches
|
|
112
|
-
for i in range(0, len(batch), 4):
|
|
113
|
-
sub_batch = batch[i : i + 4]
|
|
114
|
-
validated.append(self._parse_tools(sub_batch))
|
|
115
|
-
else:
|
|
116
|
-
validated_batch = self._parse_tools(batch)
|
|
117
|
-
if validated_batch:
|
|
118
|
-
validated.append(validated_batch)
|
|
119
|
-
|
|
120
|
-
return validated
|
|
121
|
-
|
|
122
|
-
def _parse_tools(self, tools: List[str]) -> List[Dict]:
|
|
123
|
-
"""Parse tool strings into proper format"""
|
|
124
|
-
parsed = []
|
|
125
|
-
for tool_str in tools:
|
|
126
|
-
# Extract tool name and args from string like "read_file('main.py')"
|
|
127
|
-
match = re.match(r"(\w+)\((.*)\)", tool_str)
|
|
128
|
-
if match:
|
|
129
|
-
tool_name = match.group(1)
|
|
130
|
-
args_str = match.group(2)
|
|
131
|
-
|
|
132
|
-
tool_dict = {"tool": tool_name, "args": {}}
|
|
133
|
-
if args_str:
|
|
134
|
-
# Handle simple cases like 'file.py' or "pattern", "dir"
|
|
135
|
-
args = [arg.strip().strip("\"'") for arg in args_str.split(",")]
|
|
136
|
-
if tool_name == "read_file":
|
|
137
|
-
tool_dict["args"]["filepath"] = args[0]
|
|
138
|
-
elif tool_name == "grep":
|
|
139
|
-
tool_dict["args"]["pattern"] = args[0]
|
|
140
|
-
if len(args) > 1:
|
|
141
|
-
tool_dict["args"]["directory"] = args[1]
|
|
142
|
-
elif tool_name == "list_dir":
|
|
143
|
-
tool_dict["args"]["directory"] = args[0] if args else "."
|
|
144
|
-
elif tool_name == "glob":
|
|
145
|
-
tool_dict["args"]["pattern"] = args[0]
|
|
146
|
-
if len(args) > 1:
|
|
147
|
-
tool_dict["args"]["directory"] = args[1]
|
|
148
|
-
elif tool_name == "todo":
|
|
149
|
-
tool_dict["args"]["action"] = args[0] if args else "list"
|
|
150
|
-
elif tool_name in ["write_file", "update_file"]:
|
|
151
|
-
if args:
|
|
152
|
-
tool_dict["args"]["filepath"] = args[0]
|
|
153
|
-
elif tool_name in ["run_command", "bash"]:
|
|
154
|
-
if args:
|
|
155
|
-
tool_dict["args"]["command"] = args[0]
|
|
156
|
-
|
|
157
|
-
parsed.append(tool_dict)
|
|
158
|
-
|
|
159
|
-
return parsed
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
class EnhancedTaskPlanner(dspy.Module):
|
|
163
|
-
"""Task planning with tool awareness"""
|
|
164
|
-
|
|
165
|
-
def __init__(self):
|
|
166
|
-
self.predict = dspy.ChainOfThought(TaskPlanningSignature)
|
|
167
|
-
|
|
168
|
-
def forward(self, complex_request: str):
|
|
169
|
-
logger.debug(f"Task Planning for: {complex_request}")
|
|
170
|
-
result = self.predict(complex_request=complex_request)
|
|
171
|
-
|
|
172
|
-
# Parse subtasks
|
|
173
|
-
try:
|
|
174
|
-
result.subtasks = json.loads(result.subtasks_with_tools)
|
|
175
|
-
except Exception as e:
|
|
176
|
-
logger.error(f"Failed to parse subtasks: {e}")
|
|
177
|
-
result.subtasks = []
|
|
178
|
-
|
|
179
|
-
return result
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
class PathValidator(dspy.Module):
|
|
183
|
-
"""Ensure all paths are relative"""
|
|
184
|
-
|
|
185
|
-
def __init__(self):
|
|
186
|
-
self.predict = dspy.Predict(PathValidationSignature)
|
|
187
|
-
|
|
188
|
-
def forward(self, path: str, current_directory: str = "."):
|
|
189
|
-
return self.predict(path=path, current_directory=current_directory)
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
class TunaCodeDSPy(dspy.Module):
|
|
193
|
-
"""Main TunaCode DSPy agent"""
|
|
194
|
-
|
|
195
|
-
def __init__(self):
|
|
196
|
-
self.tool_selector = OptimizedToolSelector()
|
|
197
|
-
self.task_planner = EnhancedTaskPlanner()
|
|
198
|
-
self.path_validator = PathValidator()
|
|
199
|
-
|
|
200
|
-
def forward(self, user_request: str, current_directory: str = "."):
|
|
201
|
-
"""Process request with optimization"""
|
|
202
|
-
|
|
203
|
-
# Detect request complexity
|
|
204
|
-
is_complex = self._is_complex_task(user_request)
|
|
205
|
-
|
|
206
|
-
result = {
|
|
207
|
-
"request": user_request,
|
|
208
|
-
"is_complex": is_complex,
|
|
209
|
-
"current_directory": current_directory,
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
if is_complex:
|
|
213
|
-
# Use task planner for complex tasks
|
|
214
|
-
task_plan = self.task_planner(complex_request=user_request)
|
|
215
|
-
result["subtasks"] = task_plan.subtasks
|
|
216
|
-
result["total_tool_calls"] = task_plan.total_tool_calls
|
|
217
|
-
result["requires_todo"] = task_plan.requires_todo
|
|
218
|
-
result["parallelization_opportunities"] = task_plan.parallelization_opportunities
|
|
219
|
-
|
|
220
|
-
if task_plan.requires_todo:
|
|
221
|
-
result["initial_action"] = "Use todo tool to create task list"
|
|
222
|
-
else:
|
|
223
|
-
# Direct tool selection with batching
|
|
224
|
-
tool_selection = self.tool_selector(
|
|
225
|
-
user_request=user_request, current_directory=current_directory
|
|
226
|
-
)
|
|
227
|
-
result["tool_batches"] = tool_selection.tool_batches
|
|
228
|
-
result["requires_confirmation"] = tool_selection.requires_confirmation
|
|
229
|
-
result["reasoning"] = tool_selection.reasoning
|
|
230
|
-
|
|
231
|
-
return result
|
|
232
|
-
|
|
233
|
-
def _is_complex_task(self, request: str) -> bool:
|
|
234
|
-
"""Detect if task is complex based on keywords and patterns"""
|
|
235
|
-
complex_indicators = [
|
|
236
|
-
"implement",
|
|
237
|
-
"create",
|
|
238
|
-
"build",
|
|
239
|
-
"refactor",
|
|
240
|
-
"add feature",
|
|
241
|
-
"fix all",
|
|
242
|
-
"update multiple",
|
|
243
|
-
"migrate",
|
|
244
|
-
"integrate",
|
|
245
|
-
"debug",
|
|
246
|
-
"optimize performance",
|
|
247
|
-
"add authentication",
|
|
248
|
-
"setup",
|
|
249
|
-
"configure",
|
|
250
|
-
"test suite",
|
|
251
|
-
]
|
|
252
|
-
|
|
253
|
-
request_lower = request.lower()
|
|
254
|
-
|
|
255
|
-
# Check for multiple files mentioned
|
|
256
|
-
file_pattern = r"\b\w+\.\w+\b"
|
|
257
|
-
files_mentioned = len(re.findall(file_pattern, request)) > 2
|
|
258
|
-
|
|
259
|
-
# Check for complex keywords
|
|
260
|
-
has_complex_keyword = any(indicator in request_lower for indicator in complex_indicators)
|
|
261
|
-
|
|
262
|
-
# Check for multiple operations
|
|
263
|
-
operation_words = ["and", "then", "also", "after", "before", "plus"]
|
|
264
|
-
has_multiple_ops = sum(1 for word in operation_words if word in request_lower) >= 2
|
|
265
|
-
|
|
266
|
-
return files_mentioned or has_complex_keyword or has_multiple_ops
|
|
267
|
-
|
|
268
|
-
def validate_path(self, path: str, current_directory: str = ".") -> Dict:
|
|
269
|
-
"""Validate a path is relative and safe"""
|
|
270
|
-
return self.path_validator(path, current_directory)
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
def get_tool_selection_examples():
|
|
274
|
-
"""Training examples for tool selection"""
|
|
275
|
-
return [
|
|
276
|
-
dspy.Example(
|
|
277
|
-
user_request="Show me the authentication system implementation",
|
|
278
|
-
current_directory=".",
|
|
279
|
-
tools_json='[["grep(\\"auth\\", \\"src/\\")", "list_dir(\\"src/auth/\\")", "glob(\\"**/*auth*.py\\")"]]',
|
|
280
|
-
requires_confirmation=False,
|
|
281
|
-
reasoning="Batch 3 read-only tools for parallel search - optimal performance",
|
|
282
|
-
).with_inputs("user_request", "current_directory"),
|
|
283
|
-
dspy.Example(
|
|
284
|
-
user_request="Read all config files and the main module",
|
|
285
|
-
current_directory=".",
|
|
286
|
-
tools_json='[["read_file(\\"config.json\\")", "read_file(\\"settings.py\\")", "read_file(\\".env\\")", "read_file(\\"main.py\\")"]]',
|
|
287
|
-
requires_confirmation=False,
|
|
288
|
-
reasoning="Batch 4 file reads together - maximum optimal batch size",
|
|
289
|
-
).with_inputs("user_request", "current_directory"),
|
|
290
|
-
dspy.Example(
|
|
291
|
-
user_request="Find the bug in validation and fix it",
|
|
292
|
-
current_directory=".",
|
|
293
|
-
tools_json='[["grep(\\"error\\", \\"logs/\\")", "grep(\\"validation\\", \\"src/\\")", "list_dir(\\"src/validators/\\")"], ["read_file(\\"src/validators/user.py\\")"], ["update_file(\\"src/validators/user.py\\", \\"old\\", \\"new\\")"]]',
|
|
294
|
-
requires_confirmation=True,
|
|
295
|
-
reasoning="Search tools batched, then read, then sequential write operation",
|
|
296
|
-
).with_inputs("user_request", "current_directory"),
|
|
297
|
-
]
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
def get_task_planning_examples():
|
|
301
|
-
"""Training examples for task planning"""
|
|
302
|
-
return [
|
|
303
|
-
dspy.Example(
|
|
304
|
-
complex_request="Implement user authentication system with JWT tokens",
|
|
305
|
-
subtasks_with_tools='[{"task": "Analyze current app structure", "tools": ["list_dir", "grep", "read_file"], "priority": "high"}, {"task": "Design user model", "tools": ["write_file"], "priority": "high"}, {"task": "Create auth endpoints", "tools": ["write_file", "update_file"], "priority": "high"}, {"task": "Add JWT tokens", "tools": ["write_file", "grep"], "priority": "high"}, {"task": "Write tests", "tools": ["write_file", "run_command"], "priority": "medium"}]',
|
|
306
|
-
total_tool_calls=15,
|
|
307
|
-
requires_todo=True,
|
|
308
|
-
parallelization_opportunities=3,
|
|
309
|
-
).with_inputs("complex_request"),
|
|
310
|
-
]
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
def tool_selection_metric(example, prediction):
|
|
314
|
-
"""Metric for tool selection evaluation"""
|
|
315
|
-
score = 0.0
|
|
316
|
-
|
|
317
|
-
# Tool accuracy (40%)
|
|
318
|
-
if hasattr(prediction, "tool_batches") and hasattr(example, "tools_json"):
|
|
319
|
-
try:
|
|
320
|
-
expected = json.loads(example.tools_json)
|
|
321
|
-
predicted = prediction.tool_batches
|
|
322
|
-
|
|
323
|
-
# Check tool selection accuracy
|
|
324
|
-
expected_tools = set()
|
|
325
|
-
predicted_tools = set()
|
|
326
|
-
|
|
327
|
-
for batch in expected:
|
|
328
|
-
for tool in batch:
|
|
329
|
-
tool_name = re.match(r"(\w+)\(", tool)
|
|
330
|
-
if tool_name:
|
|
331
|
-
expected_tools.add(tool_name.group(1))
|
|
332
|
-
|
|
333
|
-
for batch in predicted:
|
|
334
|
-
for tool in batch:
|
|
335
|
-
if isinstance(tool, dict):
|
|
336
|
-
predicted_tools.add(tool.get("tool", ""))
|
|
337
|
-
|
|
338
|
-
if expected_tools == predicted_tools:
|
|
339
|
-
score += 0.4
|
|
340
|
-
else:
|
|
341
|
-
overlap = len(expected_tools & predicted_tools)
|
|
342
|
-
total = len(expected_tools | predicted_tools)
|
|
343
|
-
if total > 0:
|
|
344
|
-
score += 0.4 * (overlap / total)
|
|
345
|
-
except Exception:
|
|
346
|
-
pass
|
|
347
|
-
|
|
348
|
-
# Batching optimization (30%)
|
|
349
|
-
if hasattr(prediction, "tool_batches"):
|
|
350
|
-
batches = prediction.tool_batches
|
|
351
|
-
optimal_batching = True
|
|
352
|
-
|
|
353
|
-
for batch in batches:
|
|
354
|
-
if len(batch) > 0:
|
|
355
|
-
batch_tools = [tool.get("tool", "") for tool in batch if isinstance(tool, dict)]
|
|
356
|
-
if all(tool in TOOL_CATEGORIES["read_only"] for tool in batch_tools):
|
|
357
|
-
if len(batch) < 3 or len(batch) > 4:
|
|
358
|
-
optimal_batching = False
|
|
359
|
-
break
|
|
360
|
-
|
|
361
|
-
if optimal_batching:
|
|
362
|
-
score += 0.3
|
|
363
|
-
|
|
364
|
-
# Confirmation accuracy (20%)
|
|
365
|
-
if hasattr(prediction, "requires_confirmation") and hasattr(example, "requires_confirmation"):
|
|
366
|
-
if prediction.requires_confirmation == example.requires_confirmation:
|
|
367
|
-
score += 0.2
|
|
368
|
-
|
|
369
|
-
# Reasoning quality (10%)
|
|
370
|
-
if hasattr(prediction, "reasoning") and prediction.reasoning and len(prediction.reasoning) > 20:
|
|
371
|
-
score += 0.1
|
|
372
|
-
|
|
373
|
-
return score
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
def task_planning_metric(example, prediction):
|
|
377
|
-
"""Metric for task planning evaluation"""
|
|
378
|
-
score = 0.0
|
|
379
|
-
|
|
380
|
-
# Subtask quality (30%)
|
|
381
|
-
if hasattr(prediction, "subtasks") and hasattr(example, "subtasks_with_tools"):
|
|
382
|
-
try:
|
|
383
|
-
expected = json.loads(example.subtasks_with_tools)
|
|
384
|
-
predicted = prediction.subtasks
|
|
385
|
-
|
|
386
|
-
if abs(len(expected) - len(predicted)) <= 1:
|
|
387
|
-
score += 0.3
|
|
388
|
-
elif abs(len(expected) - len(predicted)) <= 2:
|
|
389
|
-
score += 0.15
|
|
390
|
-
except Exception:
|
|
391
|
-
pass
|
|
392
|
-
|
|
393
|
-
# Tool estimation accuracy (30%)
|
|
394
|
-
if hasattr(prediction, "total_tool_calls") and hasattr(example, "total_tool_calls"):
|
|
395
|
-
if abs(prediction.total_tool_calls - example.total_tool_calls) <= 5:
|
|
396
|
-
score += 0.3
|
|
397
|
-
elif abs(prediction.total_tool_calls - example.total_tool_calls) <= 10:
|
|
398
|
-
score += 0.15
|
|
399
|
-
|
|
400
|
-
# Todo requirement (20%)
|
|
401
|
-
if hasattr(prediction, "requires_todo") and hasattr(example, "requires_todo"):
|
|
402
|
-
if prediction.requires_todo == example.requires_todo:
|
|
403
|
-
score += 0.2
|
|
404
|
-
|
|
405
|
-
# Parallelization awareness (20%)
|
|
406
|
-
if hasattr(prediction, "parallelization_opportunities") and hasattr(
|
|
407
|
-
example, "parallelization_opportunities"
|
|
408
|
-
):
|
|
409
|
-
if (
|
|
410
|
-
abs(prediction.parallelization_opportunities - example.parallelization_opportunities)
|
|
411
|
-
<= 2
|
|
412
|
-
):
|
|
413
|
-
score += 0.2
|
|
414
|
-
|
|
415
|
-
return score
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
def create_optimized_agent(api_key: str = None, model: str = "openrouter/openai/gpt-4.1-mini"):
|
|
419
|
-
"""Create and optimize the TunaCode DSPy agent"""
|
|
420
|
-
|
|
421
|
-
# Configure DSPy
|
|
422
|
-
if not api_key:
|
|
423
|
-
api_key = os.getenv("OPENROUTER_API_KEY")
|
|
424
|
-
if not api_key:
|
|
425
|
-
raise ValueError("Please set OPENROUTER_API_KEY environment variable")
|
|
426
|
-
|
|
427
|
-
lm = dspy.LM(
|
|
428
|
-
model,
|
|
429
|
-
api_base="https://openrouter.ai/api/v1",
|
|
430
|
-
api_key=api_key,
|
|
431
|
-
temperature=0.3,
|
|
432
|
-
)
|
|
433
|
-
dspy.configure(lm=lm)
|
|
434
|
-
|
|
435
|
-
# Create agent
|
|
436
|
-
agent = TunaCodeDSPy()
|
|
437
|
-
|
|
438
|
-
# Optimize tool selector
|
|
439
|
-
tool_examples = get_tool_selection_examples()
|
|
440
|
-
tool_optimizer = dspy.BootstrapFewShot(
|
|
441
|
-
metric=lambda ex, pred, trace: tool_selection_metric(ex, pred),
|
|
442
|
-
max_bootstrapped_demos=3,
|
|
443
|
-
)
|
|
444
|
-
agent.tool_selector = tool_optimizer.compile(agent.tool_selector, trainset=tool_examples)
|
|
445
|
-
|
|
446
|
-
# Optimize task planner
|
|
447
|
-
task_examples = get_task_planning_examples()
|
|
448
|
-
task_optimizer = dspy.BootstrapFewShot(
|
|
449
|
-
metric=lambda ex, pred, trace: task_planning_metric(ex, pred),
|
|
450
|
-
max_bootstrapped_demos=2,
|
|
451
|
-
)
|
|
452
|
-
agent.task_planner = task_optimizer.compile(agent.task_planner, trainset=task_examples)
|
|
453
|
-
|
|
454
|
-
return agent
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
agent = create_optimized_agent()
|
|
458
|
-
result = agent("Show me the authentication implementation", ".")
|
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
# DSPy Task Planning Prompt
|
|
2
|
-
|
|
3
|
-
This prompt is automatically generated and optimized by DSPy for TunaCode's complex task planning.
|
|
4
|
-
|
|
5
|
-
## Format
|
|
6
|
-
|
|
7
|
-
Given the fields `complex_request`, produce the fields `subtasks_with_tools`, `total_tool_calls`, `requires_todo`, `parallelization_opportunities`.
|
|
8
|
-
|
|
9
|
-
---
|
|
10
|
-
|
|
11
|
-
Follow the following format.
|
|
12
|
-
|
|
13
|
-
Complex Request: A complex task that needs breakdown
|
|
14
|
-
Reasoning: Let's think step by step in order to ${produce the answer}. We need to break this down into manageable subtasks...
|
|
15
|
-
Subtasks With Tools: JSON array of {task, tools, priority} objects
|
|
16
|
-
Total Tool Calls: Estimated total number of tool calls
|
|
17
|
-
Requires Todo: Whether todo tool should be used
|
|
18
|
-
Parallelization Opportunities: Number of parallel execution opportunities
|
|
19
|
-
|
|
20
|
-
---
|
|
21
|
-
|
|
22
|
-
## Learned Example (Selected by DSPy)
|
|
23
|
-
|
|
24
|
-
### Example: Authentication System Implementation
|
|
25
|
-
Complex Request: Implement user authentication system with JWT tokens
|
|
26
|
-
Reasoning: Let's think step by step. This is a complex implementation task that requires multiple components. First, I need to analyze the current application structure to understand how to integrate authentication. Then I'll design the user model and database schema. Next, I'll create the authentication endpoints for registration and login. I'll need to implement JWT token generation and validation. Finally, I'll add tests to ensure everything works correctly. Several of these tasks can be done in parallel once the initial analysis is complete.
|
|
27
|
-
Subtasks With Tools: [{"task": "Analyze current app structure", "tools": ["list_dir", "grep", "read_file"], "priority": "high"}, {"task": "Design user model", "tools": ["write_file"], "priority": "high"}, {"task": "Create auth endpoints", "tools": ["write_file", "update_file"], "priority": "high"}, {"task": "Add JWT tokens", "tools": ["write_file", "grep"], "priority": "high"}, {"task": "Write tests", "tools": ["write_file", "run_command"], "priority": "medium"}]
|
|
28
|
-
Total Tool Calls: 15
|
|
29
|
-
Requires Todo: true
|
|
30
|
-
Parallelization Opportunities: 3
|
|
31
|
-
|
|
32
|
-
---
|
|
33
|
-
|
|
34
|
-
## Key Patterns for Complex Tasks
|
|
35
|
-
|
|
36
|
-
1. **Break Down First**: Start with analysis/exploration before implementation
|
|
37
|
-
2. **Priority Levels**: High for core functionality, medium for tests/docs, low for nice-to-haves
|
|
38
|
-
3. **Tool Grouping**: Group related tools together for each subtask
|
|
39
|
-
4. **Todo Usage**: Use todo tool for tasks with 5+ subtasks
|
|
40
|
-
5. **Parallelization**: Identify independent subtasks that can run concurrently
|
|
41
|
-
|
|
42
|
-
---
|
|
43
|
-
|
|
44
|
-
Complex Request: ${complex_request}
|
|
45
|
-
Reasoning: Let's think step by step...
|
|
@@ -1,58 +0,0 @@
|
|
|
1
|
-
# DSPy Tool Selection Prompt
|
|
2
|
-
|
|
3
|
-
This prompt is automatically generated and optimized by DSPy for TunaCode's tool selection.
|
|
4
|
-
|
|
5
|
-
## Format
|
|
6
|
-
|
|
7
|
-
Given the fields `user_request`, `current_directory`, produce the fields `tools_json`, `requires_confirmation`, `reasoning`.
|
|
8
|
-
|
|
9
|
-
---
|
|
10
|
-
|
|
11
|
-
Follow the following format.
|
|
12
|
-
|
|
13
|
-
User Request: The user's request or task
|
|
14
|
-
Current Directory: Current working directory context
|
|
15
|
-
Reasoning: Let's think step by step in order to ${produce the answer}. We ...
|
|
16
|
-
Tools Json: JSON array of tool calls with batch grouping, e.g. [[tool1, tool2, tool3], [tool4]]
|
|
17
|
-
Requires Confirmation: Whether any tools require user confirmation
|
|
18
|
-
|
|
19
|
-
---
|
|
20
|
-
|
|
21
|
-
## Learned Examples (Automatically Selected by DSPy)
|
|
22
|
-
|
|
23
|
-
### Example 1: Searching for Implementation
|
|
24
|
-
User Request: Show me the authentication system implementation
|
|
25
|
-
Current Directory: .
|
|
26
|
-
Reasoning: Let's think step by step. To show the authentication implementation, I need to search for auth-related files across the codebase. I'll use grep to find files containing 'auth', list the auth directory if it exists, and use glob to find all auth-related Python files. These are all read-only operations that can be executed in parallel.
|
|
27
|
-
Tools Json: [["grep(\"auth\", \"src/\")", "list_dir(\"src/auth/\")", "glob(\"**/*auth*.py\")"]]
|
|
28
|
-
Requires Confirmation: false
|
|
29
|
-
|
|
30
|
-
### Example 2: Reading Multiple Files (Optimal Batching)
|
|
31
|
-
User Request: Read all config files and the main module
|
|
32
|
-
Current Directory: .
|
|
33
|
-
Reasoning: Let's think step by step. I need to read multiple specific files. All of these are read operations that can be batched together for parallel execution. I'll batch them in a group of 4 for optimal performance.
|
|
34
|
-
Tools Json: [["read_file(\"config.json\")", "read_file(\"settings.py\")", "read_file(\".env\")", "read_file(\"main.py\")"]]
|
|
35
|
-
Requires Confirmation: false
|
|
36
|
-
|
|
37
|
-
### Example 3: Search, Read, then Modify Pattern
|
|
38
|
-
User Request: Find the bug in validation and fix it
|
|
39
|
-
Current Directory: .
|
|
40
|
-
Reasoning: Let's think step by step. First, I need to search for validation-related code and errors. I'll use grep to search for error patterns and validation code, and list the validators directory. These search operations can be parallelized. After finding the issue, I'll need to read the specific file and then update it to fix the bug.
|
|
41
|
-
Tools Json: [["grep(\"error\", \"logs/\")", "grep(\"validation\", \"src/\")", "list_dir(\"src/validators/\")"], ["read_file(\"src/validators/user.py\")"], ["update_file(\"src/validators/user.py\", \"old\", \"new\")"]]
|
|
42
|
-
Requires Confirmation: true
|
|
43
|
-
|
|
44
|
-
---
|
|
45
|
-
|
|
46
|
-
## Key Patterns Learned by DSPy
|
|
47
|
-
|
|
48
|
-
1. **3-4 Tool Batching**: Optimal batch size for parallel read-only operations
|
|
49
|
-
2. **Read-Only Parallelization**: grep, list_dir, glob, read_file can run in parallel
|
|
50
|
-
3. **Sequential Writes**: write_file, update_file, run_command, bash must run sequentially
|
|
51
|
-
4. **Confirmation Required**: Any write/execute operation needs confirmation
|
|
52
|
-
5. **Search → Read → Modify**: Common pattern for debugging and fixes
|
|
53
|
-
|
|
54
|
-
---
|
|
55
|
-
|
|
56
|
-
User Request: ${user_request}
|
|
57
|
-
Current Directory: ${current_directory}
|
|
58
|
-
Reasoning: Let's think step by step...
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|