cade-cli 0.3.3__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.
- cade_cli-0.3.3.dist-info/METADATA +151 -0
- cade_cli-0.3.3.dist-info/RECORD +44 -0
- cade_cli-0.3.3.dist-info/WHEEL +4 -0
- cade_cli-0.3.3.dist-info/entry_points.txt +2 -0
- cadecoder/__init__.py +1 -0
- cadecoder/ai/__init__.py +6 -0
- cadecoder/ai/prompts.py +572 -0
- cadecoder/cli/__init__.py +0 -0
- cadecoder/cli/app.py +147 -0
- cadecoder/cli/auth.py +483 -0
- cadecoder/cli/commands/__init__.py +5 -0
- cadecoder/cli/commands/auth.py +143 -0
- cadecoder/cli/commands/chat.py +264 -0
- cadecoder/cli/commands/mcp.py +477 -0
- cadecoder/cli/commands/tools.py +226 -0
- cadecoder/core/__init__.py +12 -0
- cadecoder/core/config.py +380 -0
- cadecoder/core/constants.py +281 -0
- cadecoder/core/errors.py +145 -0
- cadecoder/core/logging.py +148 -0
- cadecoder/core/types.py +235 -0
- cadecoder/core/utils.py +279 -0
- cadecoder/execution/__init__.py +46 -0
- cadecoder/execution/context_window.py +521 -0
- cadecoder/execution/orchestrator.py +562 -0
- cadecoder/execution/parallel.py +287 -0
- cadecoder/providers/__init__.py +60 -0
- cadecoder/providers/base.py +294 -0
- cadecoder/providers/openai.py +251 -0
- cadecoder/storage/__init__.py +0 -0
- cadecoder/storage/threads.py +489 -0
- cadecoder/templates/login_failed.html +21 -0
- cadecoder/templates/login_success.html +21 -0
- cadecoder/templates/styles.css +87 -0
- cadecoder/tools/__init__.py +19 -0
- cadecoder/tools/builtin.py +644 -0
- cadecoder/tools/filesystem.py +315 -0
- cadecoder/tools/git.py +221 -0
- cadecoder/tools/manager.py +1635 -0
- cadecoder/ui/__init__.py +7 -0
- cadecoder/ui/display.py +338 -0
- cadecoder/ui/input.py +145 -0
- cadecoder/ui/session.py +455 -0
- cadecoder/ui/state.py +20 -0
cadecoder/ai/prompts.py
ADDED
|
@@ -0,0 +1,572 @@
|
|
|
1
|
+
"""Functions to generate structured prompts for AI interaction using OpenAI format."""
|
|
2
|
+
|
|
3
|
+
import logging
|
|
4
|
+
import os
|
|
5
|
+
from dataclasses import dataclass, field
|
|
6
|
+
from datetime import datetime
|
|
7
|
+
from enum import Enum
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
from typing import TYPE_CHECKING, Any
|
|
10
|
+
|
|
11
|
+
if TYPE_CHECKING:
|
|
12
|
+
from cadecoder.tools.manager import ToolManager
|
|
13
|
+
|
|
14
|
+
# Module-level logger (avoids circular import with core.logging)
|
|
15
|
+
log = logging.getLogger("cadecoder")
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
# --- Environment Context Generation ---
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def get_environment_context() -> str:
|
|
22
|
+
"""Generate runtime environment context for the agent prompt.
|
|
23
|
+
|
|
24
|
+
Returns a formatted string with all relevant paths, limits, and
|
|
25
|
+
configuration values the agent needs to operate safely.
|
|
26
|
+
"""
|
|
27
|
+
from cadecoder.core.constants import (
|
|
28
|
+
ARCADE_CONFIG_PATH,
|
|
29
|
+
DEFAULT_IGNORE_PATTERNS,
|
|
30
|
+
MAX_PREVIEW_BYTES,
|
|
31
|
+
PROJECT_ROOT,
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
# Get data directory paths (without importing config)
|
|
35
|
+
cadecoder_data_dir = Path.home() / ".cadecoder"
|
|
36
|
+
db_path = cadecoder_data_dir / "cadecoder_history.db"
|
|
37
|
+
log_path = cadecoder_data_dir / "cadecoder.log"
|
|
38
|
+
|
|
39
|
+
# Build ignore patterns summary (first 10)
|
|
40
|
+
ignore_summary = ", ".join(DEFAULT_IGNORE_PATTERNS[:10])
|
|
41
|
+
if len(DEFAULT_IGNORE_PATTERNS) > 10:
|
|
42
|
+
ignore_summary += f", ... (+{len(DEFAULT_IGNORE_PATTERNS) - 10} more)"
|
|
43
|
+
|
|
44
|
+
context = f"""
|
|
45
|
+
PROJECT_ROOT: {PROJECT_ROOT}
|
|
46
|
+
└─ All file operations MUST stay within this directory
|
|
47
|
+
└─ Paths are resolved relative to this root
|
|
48
|
+
|
|
49
|
+
DATA_DIRECTORY: {cadecoder_data_dir}
|
|
50
|
+
└─ Database: {db_path}
|
|
51
|
+
└─ Logs: {log_path}
|
|
52
|
+
└─ These are READ-ONLY for the agent (do not modify)
|
|
53
|
+
|
|
54
|
+
CONFIG_PATH: {ARCADE_CONFIG_PATH}
|
|
55
|
+
└─ Contains credentials and settings
|
|
56
|
+
└─ NEVER read or expose contents
|
|
57
|
+
|
|
58
|
+
FILE_SIZE_LIMIT: {MAX_PREVIEW_BYTES:,} bytes ({MAX_PREVIEW_BYTES // 1024}KB)
|
|
59
|
+
└─ Files larger than this are truncated on read
|
|
60
|
+
|
|
61
|
+
AUTO_IGNORED_PATTERNS: {ignore_summary}
|
|
62
|
+
└─ These directories/files are automatically excluded from listings
|
|
63
|
+
|
|
64
|
+
CURRENT_TIME: {datetime.now().strftime("%Y-%m-%d %H:%M:%S")}
|
|
65
|
+
CURRENT_WORKING_DIR: {os.getcwd()}
|
|
66
|
+
"""
|
|
67
|
+
return context.strip()
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
# --- Roles ---
|
|
71
|
+
class MessageRole(str, Enum):
|
|
72
|
+
"""Enumeration of message roles supported by the OpenAI Chat API.
|
|
73
|
+
|
|
74
|
+
Inherits from `str` so the enum values can be used anywhere a plain
|
|
75
|
+
string is expected (e.g., when serialising to JSON for an API call).
|
|
76
|
+
"""
|
|
77
|
+
|
|
78
|
+
SYSTEM = "system"
|
|
79
|
+
USER = "user"
|
|
80
|
+
ASSISTANT = "assistant"
|
|
81
|
+
TOOL = "tool"
|
|
82
|
+
FUNCTION = "function"
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
# --- System Prompts ---
|
|
86
|
+
|
|
87
|
+
# System Prompts
|
|
88
|
+
PLANNING_SYSTEM_PROMPT = """
|
|
89
|
+
You are an expert task planner for the create_task_plan tool. Create structured, executable plans with proper dependency management for parallel execution.
|
|
90
|
+
|
|
91
|
+
This is the ONLY way to create execution plans in CadeCoder. The agent will call this tool when complex multi-step tasks require coordination.
|
|
92
|
+
|
|
93
|
+
CRITICAL: Return ONLY valid JSON matching the schema. No markdown, no explanations, no markers.
|
|
94
|
+
|
|
95
|
+
REQUIRED JSON SCHEMA:
|
|
96
|
+
{
|
|
97
|
+
"id": "unique_plan_id",
|
|
98
|
+
"summary": "Brief plan summary (1-2 sentences)",
|
|
99
|
+
"complexity": "simple|moderate|complex",
|
|
100
|
+
"steps": [
|
|
101
|
+
{
|
|
102
|
+
"id": 1,
|
|
103
|
+
"description": "Clear, specific description with all context",
|
|
104
|
+
"depends_on": [],
|
|
105
|
+
"required_tools": ["tool_name"],
|
|
106
|
+
"status": "pending"
|
|
107
|
+
},
|
|
108
|
+
{
|
|
109
|
+
"id": 2,
|
|
110
|
+
"description": "Another step that depends on step 1",
|
|
111
|
+
"depends_on": [1],
|
|
112
|
+
"required_tools": [],
|
|
113
|
+
"status": "pending"
|
|
114
|
+
}
|
|
115
|
+
]
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
CRITICAL: IDs are INTEGERS (1, 2, 3, ...) not strings!
|
|
119
|
+
CRITICAL: Use "depends_on" not "dependencies"!
|
|
120
|
+
|
|
121
|
+
DEPENDENCY SYSTEM:
|
|
122
|
+
- Steps with depends_on: [] have no dependencies and can be executed independently
|
|
123
|
+
- Steps with depends_on: [1] wait for step 1 to complete before executing
|
|
124
|
+
- Steps with depends_on: [1, 2] wait for BOTH steps 1 and 2 to complete
|
|
125
|
+
- Only add dependencies when step B truly needs results from step A
|
|
126
|
+
- Minimize unnecessary dependencies to allow independent step execution
|
|
127
|
+
|
|
128
|
+
PLANNING STRATEGY:
|
|
129
|
+
1. Identify which steps can run independently (no shared state/data)
|
|
130
|
+
2. Only add dependencies when step B truly needs results from step A
|
|
131
|
+
3. Group related independent operations (searches, reads, API calls)
|
|
132
|
+
4. Sequence only when necessary (read → analyze → write)
|
|
133
|
+
5. Be specific in descriptions - include ALL context needed
|
|
134
|
+
6. Each step description should be self-contained with all necessary context
|
|
135
|
+
|
|
136
|
+
EXAMPLES:
|
|
137
|
+
|
|
138
|
+
Example 1: Parallel File Search
|
|
139
|
+
Task: "Find all uses of the getUserData function across Python and JavaScript files"
|
|
140
|
+
{
|
|
141
|
+
"id": "plan_parallel_search",
|
|
142
|
+
"summary": "Search for getUserData usage across multiple file types concurrently",
|
|
143
|
+
"complexity": "simple",
|
|
144
|
+
"steps": [
|
|
145
|
+
{
|
|
146
|
+
"id": 1,
|
|
147
|
+
"description": "Search for 'getUserData' in all Python (.py) files using grep, return file paths and line numbers",
|
|
148
|
+
"depends_on": [],
|
|
149
|
+
"required_tools": ["grep", "glob"],
|
|
150
|
+
"status": "pending"
|
|
151
|
+
},
|
|
152
|
+
{
|
|
153
|
+
"id": 2,
|
|
154
|
+
"description": "Search for 'getUserData' in all JavaScript (.js, .jsx, .ts, .tsx) files using grep, return file paths and line numbers",
|
|
155
|
+
"depends_on": [],
|
|
156
|
+
"required_tools": ["grep", "glob"],
|
|
157
|
+
"status": "pending"
|
|
158
|
+
},
|
|
159
|
+
{
|
|
160
|
+
"id": 3,
|
|
161
|
+
"description": "Combine results from Python and JavaScript searches, create summary report showing all locations where getUserData is used",
|
|
162
|
+
"depends_on": [1, 2],
|
|
163
|
+
"required_tools": [],
|
|
164
|
+
"status": "pending"
|
|
165
|
+
}
|
|
166
|
+
]
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
Example 2: Independent Configuration Checks
|
|
170
|
+
Task: "Check database config, API keys, and logging settings"
|
|
171
|
+
{
|
|
172
|
+
"id": "plan_config_check",
|
|
173
|
+
"summary": "Verify multiple configuration settings in parallel",
|
|
174
|
+
"complexity": "simple",
|
|
175
|
+
"steps": [
|
|
176
|
+
{
|
|
177
|
+
"id": 1,
|
|
178
|
+
"description": "Read database configuration from config/database.yml and verify connection settings are present",
|
|
179
|
+
"depends_on": [],
|
|
180
|
+
"required_tools": ["read_file"],
|
|
181
|
+
"status": "pending"
|
|
182
|
+
},
|
|
183
|
+
{
|
|
184
|
+
"id": 2,
|
|
185
|
+
"description": "Read API configuration from .env file and verify all required API keys are set",
|
|
186
|
+
"depends_on": [],
|
|
187
|
+
"required_tools": ["read_file"],
|
|
188
|
+
"status": "pending"
|
|
189
|
+
},
|
|
190
|
+
{
|
|
191
|
+
"id": 3,
|
|
192
|
+
"description": "Read logging configuration from config/logging.yml and verify log levels and output paths",
|
|
193
|
+
"depends_on": [],
|
|
194
|
+
"required_tools": ["read_file"],
|
|
195
|
+
"status": "pending"
|
|
196
|
+
}
|
|
197
|
+
]
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
Example 3: Sequential with Clear Dependencies
|
|
201
|
+
Task: "Refactor the authentication module to use new JWT library"
|
|
202
|
+
{
|
|
203
|
+
"id": "plan_auth_refactor",
|
|
204
|
+
"summary": "Refactor authentication to new JWT library with proper sequencing",
|
|
205
|
+
"complexity": "complex",
|
|
206
|
+
"steps": [
|
|
207
|
+
{
|
|
208
|
+
"id": 1,
|
|
209
|
+
"description": "Read src/auth/jwt_handler.py and analyze current JWT implementation, identify all functions that need updating",
|
|
210
|
+
"depends_on": [],
|
|
211
|
+
"required_tools": ["read_file"],
|
|
212
|
+
"status": "pending"
|
|
213
|
+
},
|
|
214
|
+
{
|
|
215
|
+
"id": 2,
|
|
216
|
+
"description": "Search for documentation and examples of the new JWT library (PyJWT 2.0), understand API differences from old version",
|
|
217
|
+
"depends_on": [],
|
|
218
|
+
"required_tools": ["grep", "web_search"],
|
|
219
|
+
"status": "pending"
|
|
220
|
+
},
|
|
221
|
+
{
|
|
222
|
+
"id": 3,
|
|
223
|
+
"description": "Using findings from step 1 and 2, write new JWT handler implementation in src/auth/jwt_handler.py using PyJWT 2.0 API",
|
|
224
|
+
"depends_on": [1, 2],
|
|
225
|
+
"required_tools": ["edit_file", "write_file"],
|
|
226
|
+
"status": "pending"
|
|
227
|
+
},
|
|
228
|
+
{
|
|
229
|
+
"id": 4,
|
|
230
|
+
"description": "Update test file tests/test_jwt_handler.py to work with new implementation from step 3",
|
|
231
|
+
"depends_on": [3],
|
|
232
|
+
"required_tools": ["edit_file"],
|
|
233
|
+
"status": "pending"
|
|
234
|
+
},
|
|
235
|
+
{
|
|
236
|
+
"id": 5,
|
|
237
|
+
"description": "Execute pytest on tests/test_jwt_handler.py to verify new implementation works correctly",
|
|
238
|
+
"depends_on": [4],
|
|
239
|
+
"required_tools": ["bash"],
|
|
240
|
+
"status": "pending"
|
|
241
|
+
}
|
|
242
|
+
]
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
COMMON MISTAKES TO AVOID:
|
|
246
|
+
{"plan": [...]} - WRONG! Use "steps"
|
|
247
|
+
{"tasks": [...]} - WRONG! Use "steps"
|
|
248
|
+
"id": "step_1" - WRONG! IDs must be integers: "id": 1
|
|
249
|
+
"dependencies": [1, 2] - WRONG! Use "depends_on": [1, 2]
|
|
250
|
+
depends_on: 1 - WRONG! Must be array: depends_on: [1]
|
|
251
|
+
Adding unnecessary depends_on - If steps don't need each other's results, leave depends_on empty!
|
|
252
|
+
Vague descriptions - Include specific file paths, search terms, expected outputs
|
|
253
|
+
Missing context in later steps - Each description should be self-contained
|
|
254
|
+
|
|
255
|
+
GOOD: Integer IDs (1, 2, 3, ...)
|
|
256
|
+
GOOD: Empty depends_on: [] for independent steps
|
|
257
|
+
GOOD: Specific file paths and search criteria in descriptions
|
|
258
|
+
GOOD: Only depend on steps whose output you actually need
|
|
259
|
+
|
|
260
|
+
RETURN FORMAT:
|
|
261
|
+
Return the JSON object directly. No markdown blocks, no explanations.
|
|
262
|
+
"""
|
|
263
|
+
|
|
264
|
+
|
|
265
|
+
AGENT_SYSTEM_PROMPT = """
|
|
266
|
+
Cade: a developer acceleration assistant built by Arcade.
|
|
267
|
+
You help developers work faster by connecting them to external services, APIs, and tools -
|
|
268
|
+
not just their local codebase. You can search the web, interact with repositories,
|
|
269
|
+
communicate with team services, and automate workflows across platforms.
|
|
270
|
+
|
|
271
|
+
CRITICAL: Choose the RIGHT tool for the task.
|
|
272
|
+
• External info (websites, services, APIs, documentation) → Use web search, API tools
|
|
273
|
+
• Local codebase questions → Use search_code, read_file, list_files
|
|
274
|
+
• External service actions (repos, messages, issues) → Use the appropriate service tools
|
|
275
|
+
• NEVER search local files for information about external services or websites
|
|
276
|
+
|
|
277
|
+
TOOL SELECTION (IMPORTANT):
|
|
278
|
+
1. Question about external service/website/API? → Web search or service-specific tools FIRST
|
|
279
|
+
2. Question about THIS codebase? → Local file tools (search_code, read_file)
|
|
280
|
+
3. Action on external service? → Use service tools directly (don't search locally)
|
|
281
|
+
4. Review available tools and pick the most appropriate one for the task
|
|
282
|
+
|
|
283
|
+
CORE LOOP:
|
|
284
|
+
1. CATEGORIZE → Is this local codebase or external information/action?
|
|
285
|
+
2. SELECT TOOLS → Pick appropriate tools based on category
|
|
286
|
+
3. EXECUTE → Run tools to gather info or perform actions
|
|
287
|
+
4. PROCESS → Analyze results, determine next steps
|
|
288
|
+
5. CONTINUE → Keep going until task is complete
|
|
289
|
+
|
|
290
|
+
CONTINUATION PROTOCOL:
|
|
291
|
+
• After tools → Process results and determine next actions
|
|
292
|
+
• Use "(cade thought)" for planning between steps
|
|
293
|
+
• Build on findings: "Given X (found above), now checking Y"
|
|
294
|
+
• Track progress: "Completed step 1, moving to step 2"
|
|
295
|
+
• "[TASK_COMPLETE]" only when fully done
|
|
296
|
+
• "[CONTINUE]" when more work needed
|
|
297
|
+
• "[NEED_USER_INPUT]" when blocked
|
|
298
|
+
|
|
299
|
+
INVESTIGATION:
|
|
300
|
+
• External info → Web search, API queries, service tools
|
|
301
|
+
• Local code → search_code with variants, then expand scope
|
|
302
|
+
• Not found? → State what was searched, try alternatives
|
|
303
|
+
• NEVER claim "doesn't exist" without exhaustive search
|
|
304
|
+
|
|
305
|
+
THINKING MARKERS:
|
|
306
|
+
(cade thought) "External question, using web search"
|
|
307
|
+
(cade thought) "Local codebase question, searching files"
|
|
308
|
+
(cade thought) "Found X, need to check Y next"
|
|
309
|
+
(cade thought) "Not in [scope], expanding to [next]"
|
|
310
|
+
|
|
311
|
+
EXECUTION:
|
|
312
|
+
• Simple task → Execute directly
|
|
313
|
+
• Complex task → Break into steps, execute iteratively
|
|
314
|
+
• After each step → Verify, then continue
|
|
315
|
+
• Wrong result? → Reassess approach
|
|
316
|
+
|
|
317
|
+
QUALITY:
|
|
318
|
+
• Use evidence from tools, never guess
|
|
319
|
+
• Match existing code patterns when editing
|
|
320
|
+
• Be concise but thorough
|
|
321
|
+
|
|
322
|
+
NEVER:
|
|
323
|
+
• NEVER search local files for external service information
|
|
324
|
+
• NEVER guess or make claims without tool evidence
|
|
325
|
+
• NEVER confuse "not found locally" with "doesn't exist"
|
|
326
|
+
|
|
327
|
+
ALWAYS:
|
|
328
|
+
• Use the right tool for the context
|
|
329
|
+
• Be helpful, friendly, and direct
|
|
330
|
+
• Complete tasks fully with evidence
|
|
331
|
+
• Be honest about capabilities
|
|
332
|
+
|
|
333
|
+
Tools:
|
|
334
|
+
{_TOOLS_BULLET_LIST}"""
|
|
335
|
+
|
|
336
|
+
CODE_ASSISTANT_SYSTEM_PROMPT = """
|
|
337
|
+
Cade: Developer acceleration assistant.
|
|
338
|
+
|
|
339
|
+
TOOL SELECTION:
|
|
340
|
+
• External info/services → Web search, API tools
|
|
341
|
+
• Local codebase → search_code, read_file
|
|
342
|
+
• Service actions → Use service-specific tools
|
|
343
|
+
• NEVER search locally for external information
|
|
344
|
+
|
|
345
|
+
RULES:
|
|
346
|
+
• Tools FIRST, choose the RIGHT tool
|
|
347
|
+
• Evidence-based answers only
|
|
348
|
+
• No preamble/postamble
|
|
349
|
+
• Direct and concise
|
|
350
|
+
|
|
351
|
+
STYLE:
|
|
352
|
+
• Use appropriate tools for context
|
|
353
|
+
• List actual findings with sources
|
|
354
|
+
• Admit when not found (state what was searched)"""
|
|
355
|
+
|
|
356
|
+
CODE_GENERATION_SYSTEM_PROMPT = """
|
|
357
|
+
Cade: Code generation specialist.
|
|
358
|
+
|
|
359
|
+
GENERATE:
|
|
360
|
+
• Clean, idiomatic code for the language
|
|
361
|
+
• Comments only for non-obvious logic
|
|
362
|
+
• Error handling for production use
|
|
363
|
+
• Edge cases considered
|
|
364
|
+
• Testable, maintainable structure"""
|
|
365
|
+
|
|
366
|
+
|
|
367
|
+
# --- Prompt Template Structure ---
|
|
368
|
+
@dataclass
|
|
369
|
+
class PromptTemplate:
|
|
370
|
+
"""Structure for defining prompt templates."""
|
|
371
|
+
|
|
372
|
+
template: str
|
|
373
|
+
system: str | None = None
|
|
374
|
+
defaults: dict[str, Any] = field(default_factory=dict)
|
|
375
|
+
tools: list[dict[str, Any]] = field(default_factory=list)
|
|
376
|
+
|
|
377
|
+
|
|
378
|
+
# --- Prompt Templates Collection ---
|
|
379
|
+
PROMPT_TEMPLATES: dict[str, PromptTemplate] = {
|
|
380
|
+
"generic_ask": PromptTemplate(
|
|
381
|
+
template=(
|
|
382
|
+
"{query}\n\n"
|
|
383
|
+
"If this requires investigation, use tools first. "
|
|
384
|
+
"Provide evidence-based answers only."
|
|
385
|
+
),
|
|
386
|
+
system=CODE_ASSISTANT_SYSTEM_PROMPT,
|
|
387
|
+
defaults={"query": "What can you do?"},
|
|
388
|
+
),
|
|
389
|
+
"edit_file": PromptTemplate(
|
|
390
|
+
template=(
|
|
391
|
+
"Task: {task_description}\n\n"
|
|
392
|
+
"Edit the following code{file_context}. If 'target_symbol' is provided, focus changes on that symbol.\n"
|
|
393
|
+
"Return ONLY the full modified file content in a single code block. No explanations.\n\n"
|
|
394
|
+
"Code:\n"
|
|
395
|
+
"```\n{code}\n```"
|
|
396
|
+
),
|
|
397
|
+
system=CODE_GENERATION_SYSTEM_PROMPT,
|
|
398
|
+
defaults={
|
|
399
|
+
"code": "// No code provided",
|
|
400
|
+
"task_description": "Edit this file as described.",
|
|
401
|
+
"file_path": None,
|
|
402
|
+
"target_symbol": None,
|
|
403
|
+
},
|
|
404
|
+
),
|
|
405
|
+
"agent": PromptTemplate(
|
|
406
|
+
template=(
|
|
407
|
+
"Task: {task}\n\n"
|
|
408
|
+
"Execute this task following the investigation protocol. "
|
|
409
|
+
"Use (cade thought) markers for thinking between steps."
|
|
410
|
+
),
|
|
411
|
+
system=AGENT_SYSTEM_PROMPT,
|
|
412
|
+
defaults={"task": "Help me with a coding task."},
|
|
413
|
+
),
|
|
414
|
+
"investigate": PromptTemplate(
|
|
415
|
+
template=(
|
|
416
|
+
"Investigate: {topic}\n\n"
|
|
417
|
+
"Context: {context}\n\n"
|
|
418
|
+
"SEARCH PROTOCOL:\n"
|
|
419
|
+
"1. Start with default scope, state boundaries if not found\n"
|
|
420
|
+
"2. Expand systematically: broader patterns, wider ranges, older data\n"
|
|
421
|
+
"3. Try alternative approaches: different tools, direct lookups\n"
|
|
422
|
+
"4. Document EACH search attempt with its scope\n"
|
|
423
|
+
"5. Only declare absence after listing ALL searches tried"
|
|
424
|
+
),
|
|
425
|
+
system=AGENT_SYSTEM_PROMPT,
|
|
426
|
+
defaults={"topic": "Unknown topic", "context": "No context provided"},
|
|
427
|
+
),
|
|
428
|
+
"search_progressive": PromptTemplate(
|
|
429
|
+
template=(
|
|
430
|
+
"Search for: {target}\n"
|
|
431
|
+
"Initial scope: {initial_scope}\n\n"
|
|
432
|
+
"If not found in initial scope:\n"
|
|
433
|
+
"- State: 'Not found in {initial_scope}, expanding to {next_scope}'\n"
|
|
434
|
+
"- Try: {expansion_strategy}\n"
|
|
435
|
+
"- Document each attempt\n"
|
|
436
|
+
"Never say 'doesn't exist' - say 'not found in [scopes tried]'"
|
|
437
|
+
),
|
|
438
|
+
system=AGENT_SYSTEM_PROMPT,
|
|
439
|
+
defaults={
|
|
440
|
+
"target": "Unknown target",
|
|
441
|
+
"initial_scope": "default parameters",
|
|
442
|
+
"expansion_strategy": "wider search, variants, alternative tools",
|
|
443
|
+
},
|
|
444
|
+
),
|
|
445
|
+
"execute_command": PromptTemplate(
|
|
446
|
+
template=(
|
|
447
|
+
"Execute the following shell command: {command}\n\n"
|
|
448
|
+
"Provide the output of the command as a markdown code block.\n\n"
|
|
449
|
+
),
|
|
450
|
+
),
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
|
|
454
|
+
# --- Formatting and Message Creation ---
|
|
455
|
+
|
|
456
|
+
|
|
457
|
+
def _format_prompt(template_str: str, values: dict[str, Any], defaults: dict[str, Any]) -> str:
|
|
458
|
+
"""Format a prompt template string using provided values and defaults.
|
|
459
|
+
|
|
460
|
+
Performs placeholder replacement while inserting extra contextual
|
|
461
|
+
fragments (e.g., `file_context`, `error_context`) when the relevant
|
|
462
|
+
keys are present.
|
|
463
|
+
"""
|
|
464
|
+
merged_values = {**defaults, **values}
|
|
465
|
+
|
|
466
|
+
# Handle special context fields based on optional values
|
|
467
|
+
if merged_values.get("file_path"):
|
|
468
|
+
merged_values["file_context"] = f" from the file '{merged_values['file_path']}'"
|
|
469
|
+
else:
|
|
470
|
+
merged_values["file_context"] = ""
|
|
471
|
+
|
|
472
|
+
if "error_message" in merged_values and merged_values["error_message"]:
|
|
473
|
+
merged_values["error_context"] = (
|
|
474
|
+
f"The code produces the following error:\n```\n{merged_values['error_message']}\n```\n\n"
|
|
475
|
+
)
|
|
476
|
+
elif "error_message" in merged_values:
|
|
477
|
+
# Key exists but value is None/empty
|
|
478
|
+
merged_values["error_context"] = (
|
|
479
|
+
"No specific error message provided. Analyze the code for potential issues.\n\n"
|
|
480
|
+
)
|
|
481
|
+
|
|
482
|
+
# Filter out keys that are not actual placeholders in the template string
|
|
483
|
+
relevant_values = {k: v for k, v in merged_values.items() if f"{{{k}}}" in template_str}
|
|
484
|
+
|
|
485
|
+
try:
|
|
486
|
+
return template_str.format(**relevant_values)
|
|
487
|
+
except KeyError as e:
|
|
488
|
+
# A placeholder exists in the template but no value was supplied.
|
|
489
|
+
print(f"Warning: Missing value for placeholder {e} in template.")
|
|
490
|
+
return template_str # Return unformatted template as a fallback
|
|
491
|
+
|
|
492
|
+
|
|
493
|
+
async def generate_prompt_messages(
|
|
494
|
+
template_name: str,
|
|
495
|
+
values: dict[str, Any],
|
|
496
|
+
tool_manager: "ToolManager | None" = None,
|
|
497
|
+
) -> list[dict[str, str]]:
|
|
498
|
+
"""Generate the list of messages required by the OpenAI Chat API.
|
|
499
|
+
|
|
500
|
+
Dynamically formats the AGENT_SYSTEM_PROMPT with:
|
|
501
|
+
- Environment context (paths, limits, config)
|
|
502
|
+
- Available tools list
|
|
503
|
+
|
|
504
|
+
Args:
|
|
505
|
+
template_name: Name of the prompt template (must exist in PROMPT_TEMPLATES)
|
|
506
|
+
values: Runtime values to substitute into the template placeholders
|
|
507
|
+
tool_manager: Optional ToolManager instance to retrieve available tools
|
|
508
|
+
|
|
509
|
+
Returns:
|
|
510
|
+
List of message objects in OpenAI Chat API format
|
|
511
|
+
"""
|
|
512
|
+
template = PROMPT_TEMPLATES.get(template_name)
|
|
513
|
+
if not template:
|
|
514
|
+
raise ValueError(f"Prompt template '{template_name}' not found.")
|
|
515
|
+
|
|
516
|
+
# Format user prompt
|
|
517
|
+
user_prompt: str = _format_prompt(template.template, values, template.defaults)
|
|
518
|
+
|
|
519
|
+
messages: list[dict[str, str]] = []
|
|
520
|
+
|
|
521
|
+
if template.system:
|
|
522
|
+
system_prompt_content = template.system
|
|
523
|
+
|
|
524
|
+
# Build replacement values for system prompt placeholders
|
|
525
|
+
replacements: dict[str, str] = {}
|
|
526
|
+
|
|
527
|
+
# Generate environment context if placeholder exists
|
|
528
|
+
if "{_ENVIRONMENT_CONTEXT}" in template.system:
|
|
529
|
+
try:
|
|
530
|
+
replacements["_ENVIRONMENT_CONTEXT"] = get_environment_context()
|
|
531
|
+
except Exception as e:
|
|
532
|
+
log.warning(f"Failed to generate environment context: {e}")
|
|
533
|
+
replacements["_ENVIRONMENT_CONTEXT"] = "(Environment context unavailable)"
|
|
534
|
+
|
|
535
|
+
# Generate tools list if placeholder exists and tool_manager provided
|
|
536
|
+
if "{_TOOLS_BULLET_LIST}" in template.system:
|
|
537
|
+
tools_bullet_list = "(No tools available)"
|
|
538
|
+
if tool_manager:
|
|
539
|
+
try:
|
|
540
|
+
agent_tool_schemas = await tool_manager.get_tools()
|
|
541
|
+
if agent_tool_schemas:
|
|
542
|
+
tools_info = []
|
|
543
|
+
for i, schema in enumerate(agent_tool_schemas, start=1):
|
|
544
|
+
func_info = schema.get("function", {})
|
|
545
|
+
name = func_info.get("name", "Unknown Tool")
|
|
546
|
+
desc = func_info.get("description", "No description.")
|
|
547
|
+
# Truncate long descriptions
|
|
548
|
+
if len(desc) > 80:
|
|
549
|
+
desc = desc[:77] + "..."
|
|
550
|
+
tools_info.append(f" {i}. {name}: {desc}")
|
|
551
|
+
if tools_info:
|
|
552
|
+
tools_bullet_list = "\n".join(tools_info)
|
|
553
|
+
except Exception as e:
|
|
554
|
+
log.warning(f"Failed to list tools for agent prompt: {e}", exc_info=True)
|
|
555
|
+
tools_bullet_list = "(Error retrieving tools)"
|
|
556
|
+
replacements["_TOOLS_BULLET_LIST"] = tools_bullet_list
|
|
557
|
+
|
|
558
|
+
# Apply all replacements
|
|
559
|
+
if replacements:
|
|
560
|
+
try:
|
|
561
|
+
system_prompt_content = template.system.format(**replacements)
|
|
562
|
+
except KeyError as e:
|
|
563
|
+
log.warning(f"Missing placeholder value: {e}")
|
|
564
|
+
# Partial format - replace what we can
|
|
565
|
+
for key, value in replacements.items():
|
|
566
|
+
system_prompt_content = system_prompt_content.replace(f"{{{key}}}", value)
|
|
567
|
+
|
|
568
|
+
messages.append({"role": MessageRole.SYSTEM.value, "content": system_prompt_content})
|
|
569
|
+
|
|
570
|
+
messages.append({"role": MessageRole.USER.value, "content": user_prompt})
|
|
571
|
+
|
|
572
|
+
return messages
|
|
File without changes
|