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 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
- await ui.muted(f"• Model: {model_name} • {context_display}")
389
- await ui.success("Ready to assist")
390
- await ui.line()
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
- await ui.muted(f"• Model: {state_manager.session.current_model} {context_display}")
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
@@ -7,7 +7,7 @@ Centralizes all magic strings, UI text, error messages, and application constant
7
7
 
8
8
  # Application info
9
9
  APP_NAME = "TunaCode"
10
- APP_VERSION = "0.0.44"
10
+ APP_VERSION = "0.0.45"
11
11
 
12
12
  # File patterns
13
13
  GUIDE_FILE_PATTERN = "{name}.md"
@@ -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
- # Create AgentRun response
857
- from datetime import datetime
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: Any
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: Any
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.warning("Main agent not available, using heuristic decomposition")
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[Any] = None
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
- logger.info(
132
- f"Decomposing complex task (score: {complexity_result.complexity_score:.2f})"
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
- logger.info(
138
- f"Executing task directly (score: {complexity_result.complexity_score:.2f})"
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
- logger.info(f"Executing subtask {i + 1}/{len(subtasks)}: {subtask_desc[:50]}...")
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
- markdown_content = Markdown(self.content or UI_THINKING_MESSAGE)
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(markdown_content, (0, 1, 0, 1)),
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.44
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=e5ew-znjhA7bItbVdVDnF51SR7xhvENeVOR541rc9_E,5168
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=CMvl5wlxj7hr45mxil26fwbmWpDYxRsRVS9aIGJvKK0,18525
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=Qny32BhbSeRBrE9baWZcAit3XFXgWuXFMDJUZudj55M,922
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/dspy_integration.py,sha256=h3gJ-qr0fXpB2CRaU-MVv_8xG--ah-8nra7WO960Gbo,9152
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=iKKKd_tZT1sXaPjpjRVVoCG8zFfuxilktSGUdalzTUM,15719
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=7JbRPPXK083jipaaMSpnWIH4wtJC_yjhxBY-ZSkKqr0,14552
41
- tunacode/core/recursive/executor.py,sha256=5UcFPK57KP2WhtUeHrBPE3BxkKsZ7QxoMaSUHTYrsoE,16436
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=ckL-TYxYWlpBAFj8SC9Od8vrW7Kf5N92bZRYBWR14jE,8338
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.44.dist-info/licenses/LICENSE,sha256=Btzdu2kIoMbdSp6OyCLupB1aRgpTCJ_szMimgEnpkkE,1056
99
- tunacode_cli-0.0.44.dist-info/METADATA,sha256=MNphC8b7Fo9Q2kGx7pWgCkqM5k9dNaON0d02wKOu-9o,5203
100
- tunacode_cli-0.0.44.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
101
- tunacode_cli-0.0.44.dist-info/entry_points.txt,sha256=hbkytikj4dGu6rizPuAd_DGUPBGF191RTnhr9wdhORY,51
102
- tunacode_cli-0.0.44.dist-info/top_level.txt,sha256=lKy2P6BWNi5XSA4DHFvyjQ14V26lDZctwdmhEJrxQbU,9
103
- tunacode_cli-0.0.44.dist-info/RECORD,,
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...