deepy-cli 0.1.1__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.
- deepy/__init__.py +9 -0
- deepy/__main__.py +7 -0
- deepy/cli.py +413 -0
- deepy/config/__init__.py +21 -0
- deepy/config/settings.py +237 -0
- deepy/data/__init__.py +1 -0
- deepy/data/tools/AskUserQuestion.md +10 -0
- deepy/data/tools/WebFetch.md +9 -0
- deepy/data/tools/WebSearch.md +9 -0
- deepy/data/tools/__init__.py +1 -0
- deepy/data/tools/bash.md +7 -0
- deepy/data/tools/edit.md +13 -0
- deepy/data/tools/modify.md +17 -0
- deepy/data/tools/read.md +8 -0
- deepy/data/tools/write.md +12 -0
- deepy/errors.py +63 -0
- deepy/llm/__init__.py +13 -0
- deepy/llm/agent.py +31 -0
- deepy/llm/context.py +109 -0
- deepy/llm/events.py +187 -0
- deepy/llm/model_capabilities.py +7 -0
- deepy/llm/provider.py +81 -0
- deepy/llm/replay.py +120 -0
- deepy/llm/runner.py +412 -0
- deepy/llm/thinking.py +30 -0
- deepy/prompts/__init__.py +6 -0
- deepy/prompts/compact.py +100 -0
- deepy/prompts/rules.py +24 -0
- deepy/prompts/runtime_context.py +98 -0
- deepy/prompts/system.py +72 -0
- deepy/prompts/tool_docs.py +21 -0
- deepy/sessions/__init__.py +17 -0
- deepy/sessions/jsonl.py +306 -0
- deepy/sessions/manager.py +202 -0
- deepy/skills.py +202 -0
- deepy/status.py +65 -0
- deepy/tools/__init__.py +6 -0
- deepy/tools/agents.py +343 -0
- deepy/tools/builtin.py +2113 -0
- deepy/tools/file_state.py +85 -0
- deepy/tools/result.py +54 -0
- deepy/tools/shell_utils.py +83 -0
- deepy/ui/__init__.py +5 -0
- deepy/ui/app.py +118 -0
- deepy/ui/ask_user_question.py +182 -0
- deepy/ui/exit_summary.py +142 -0
- deepy/ui/loading_text.py +87 -0
- deepy/ui/markdown.py +152 -0
- deepy/ui/message_view.py +546 -0
- deepy/ui/prompt_buffer.py +176 -0
- deepy/ui/prompt_input.py +286 -0
- deepy/ui/session_list.py +140 -0
- deepy/ui/session_picker.py +179 -0
- deepy/ui/slash_commands.py +67 -0
- deepy/ui/styles.py +21 -0
- deepy/ui/terminal.py +959 -0
- deepy/ui/thinking_state.py +29 -0
- deepy/ui/welcome.py +195 -0
- deepy/update_check.py +195 -0
- deepy/usage.py +192 -0
- deepy/utils/__init__.py +15 -0
- deepy/utils/debug_logger.py +62 -0
- deepy/utils/error_logger.py +107 -0
- deepy/utils/json.py +29 -0
- deepy/utils/notify.py +66 -0
- deepy_cli-0.1.1.dist-info/METADATA +205 -0
- deepy_cli-0.1.1.dist-info/RECORD +69 -0
- deepy_cli-0.1.1.dist-info/WHEEL +4 -0
- deepy_cli-0.1.1.dist-info/entry_points.txt +3 -0
deepy/tools/agents.py
ADDED
|
@@ -0,0 +1,343 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import Any
|
|
4
|
+
|
|
5
|
+
from deepy.utils import json as json_utils
|
|
6
|
+
|
|
7
|
+
from .builtin import ToolRuntime
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def build_function_tools(runtime: ToolRuntime) -> list[object]:
|
|
11
|
+
from agents.tool import FunctionTool
|
|
12
|
+
|
|
13
|
+
async def invoke_bash(_context: object, raw_input: str) -> str:
|
|
14
|
+
args = _tool_args(raw_input)
|
|
15
|
+
return runtime.bash(_string_arg(args, "command"), timeout_ms=120_000)
|
|
16
|
+
|
|
17
|
+
async def invoke_ask_user_question(_context: object, raw_input: str) -> str:
|
|
18
|
+
args = _tool_args(raw_input)
|
|
19
|
+
questions = args.get("questions")
|
|
20
|
+
return runtime.ask_user_question(questions if isinstance(questions, list) else [])
|
|
21
|
+
|
|
22
|
+
async def invoke_read(_context: object, raw_input: str) -> str:
|
|
23
|
+
args = _tool_args(raw_input)
|
|
24
|
+
return runtime.read(
|
|
25
|
+
_string_arg(args, "file_path"),
|
|
26
|
+
start_line=_int_arg(args, "offset", 1),
|
|
27
|
+
limit=_optional_int_arg(args, "limit"),
|
|
28
|
+
pages=_optional_string_arg(args, "pages"),
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
async def invoke_modify(_context: object, raw_input: str) -> str:
|
|
32
|
+
args = _tool_args(raw_input)
|
|
33
|
+
return runtime.modify(
|
|
34
|
+
_optional_string_arg(args, "file_path"),
|
|
35
|
+
content=args.get("content"),
|
|
36
|
+
old=_nullable_string_arg(args, "old_string"),
|
|
37
|
+
new=_nullable_string_arg(args, "new_string"),
|
|
38
|
+
replace_all=_bool_arg(args, "replace_all", False),
|
|
39
|
+
snippet_id=_optional_string_arg(args, "snippet_id"),
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
async def invoke_web_search(_context: object, raw_input: str) -> str:
|
|
43
|
+
args = _tool_args(raw_input)
|
|
44
|
+
return runtime.web_search(_string_arg(args, "query"))
|
|
45
|
+
|
|
46
|
+
async def invoke_web_fetch(_context: object, raw_input: str) -> str:
|
|
47
|
+
args = _tool_args(raw_input)
|
|
48
|
+
return runtime.web_fetch(_string_arg(args, "url"))
|
|
49
|
+
|
|
50
|
+
return [
|
|
51
|
+
FunctionTool(
|
|
52
|
+
name="bash",
|
|
53
|
+
description="Execute shell commands in a persistent bash session.",
|
|
54
|
+
params_json_schema=BASH_SCHEMA,
|
|
55
|
+
on_invoke_tool=invoke_bash,
|
|
56
|
+
strict_json_schema=False,
|
|
57
|
+
),
|
|
58
|
+
FunctionTool(
|
|
59
|
+
name="AskUserQuestion",
|
|
60
|
+
description=(
|
|
61
|
+
"When the task has ambiguities or multiple implementation approaches, use this tool "
|
|
62
|
+
"to pause execution and ask the user a question to get clarification or make a decision."
|
|
63
|
+
),
|
|
64
|
+
params_json_schema=ASK_USER_QUESTION_SCHEMA,
|
|
65
|
+
on_invoke_tool=invoke_ask_user_question,
|
|
66
|
+
strict_json_schema=False,
|
|
67
|
+
),
|
|
68
|
+
FunctionTool(
|
|
69
|
+
name="read",
|
|
70
|
+
description="Read files from the filesystem (text, images, PDFs, notebooks).",
|
|
71
|
+
params_json_schema=READ_SCHEMA,
|
|
72
|
+
on_invoke_tool=invoke_read,
|
|
73
|
+
strict_json_schema=False,
|
|
74
|
+
),
|
|
75
|
+
FunctionTool(
|
|
76
|
+
name="modify",
|
|
77
|
+
description=(
|
|
78
|
+
"Create new files or edit existing files. Use content only for files that do not "
|
|
79
|
+
"exist. For existing files, read first and use old_string/new_string."
|
|
80
|
+
),
|
|
81
|
+
params_json_schema=MODIFY_SCHEMA,
|
|
82
|
+
on_invoke_tool=invoke_modify,
|
|
83
|
+
strict_json_schema=False,
|
|
84
|
+
),
|
|
85
|
+
FunctionTool(
|
|
86
|
+
name="WebSearch",
|
|
87
|
+
description="Perform web searching using a natural language query.",
|
|
88
|
+
params_json_schema=WEB_SEARCH_SCHEMA,
|
|
89
|
+
on_invoke_tool=invoke_web_search,
|
|
90
|
+
strict_json_schema=False,
|
|
91
|
+
),
|
|
92
|
+
FunctionTool(
|
|
93
|
+
name="WebFetch",
|
|
94
|
+
description="Fetch and extract readable content from a complete http or https URL.",
|
|
95
|
+
params_json_schema=WEB_FETCH_SCHEMA,
|
|
96
|
+
on_invoke_tool=invoke_web_fetch,
|
|
97
|
+
strict_json_schema=False,
|
|
98
|
+
),
|
|
99
|
+
]
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
def _tool_args(raw_input: str) -> dict[str, Any]:
|
|
103
|
+
try:
|
|
104
|
+
parsed = json_utils.loads(raw_input or "{}")
|
|
105
|
+
except json_utils.JSONDecodeError:
|
|
106
|
+
return {}
|
|
107
|
+
return parsed if isinstance(parsed, dict) else {}
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
def _string_arg(args: dict[str, Any], name: str) -> str:
|
|
111
|
+
value = args.get(name)
|
|
112
|
+
return value if isinstance(value, str) else ""
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
def _optional_string_arg(args: dict[str, Any], name: str) -> str | None:
|
|
116
|
+
value = args.get(name)
|
|
117
|
+
return value if isinstance(value, str) and value else None
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
def _nullable_string_arg(args: dict[str, Any], name: str) -> str | None:
|
|
121
|
+
value = args.get(name)
|
|
122
|
+
return value if isinstance(value, str) else None
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
def _int_arg(args: dict[str, Any], name: str, default: int) -> int:
|
|
126
|
+
value = args.get(name)
|
|
127
|
+
if isinstance(value, bool):
|
|
128
|
+
return default
|
|
129
|
+
return value if isinstance(value, int) else default
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
def _optional_int_arg(args: dict[str, Any], name: str) -> int | None:
|
|
133
|
+
value = args.get(name)
|
|
134
|
+
if isinstance(value, bool):
|
|
135
|
+
return None
|
|
136
|
+
return value if isinstance(value, int) else None
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
def _bool_arg(args: dict[str, Any], name: str, default: bool) -> bool:
|
|
140
|
+
value = args.get(name)
|
|
141
|
+
return value if isinstance(value, bool) else default
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
BASH_SCHEMA: dict[str, Any] = {
|
|
145
|
+
"type": "object",
|
|
146
|
+
"properties": {
|
|
147
|
+
"command": {
|
|
148
|
+
"type": "string",
|
|
149
|
+
"description": "The shell command to execute",
|
|
150
|
+
},
|
|
151
|
+
"description": {
|
|
152
|
+
"type": "string",
|
|
153
|
+
"description": (
|
|
154
|
+
"Clear, concise description of what this command does in active voice. Never use "
|
|
155
|
+
'words like "complex" or "risk" in the description - just describe what it does.'
|
|
156
|
+
),
|
|
157
|
+
},
|
|
158
|
+
},
|
|
159
|
+
"required": ["command"],
|
|
160
|
+
"additionalProperties": False,
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
ASK_USER_QUESTION_SCHEMA: dict[str, Any] = {
|
|
164
|
+
"type": "object",
|
|
165
|
+
"properties": {
|
|
166
|
+
"questions": {
|
|
167
|
+
"type": "array",
|
|
168
|
+
"description": "Questions to present to the user. Usually only one question is needed at a time.",
|
|
169
|
+
"items": {
|
|
170
|
+
"type": "object",
|
|
171
|
+
"properties": {
|
|
172
|
+
"question": {
|
|
173
|
+
"type": "string",
|
|
174
|
+
"description": "The question to ask the user.",
|
|
175
|
+
},
|
|
176
|
+
"multiSelect": {
|
|
177
|
+
"type": "boolean",
|
|
178
|
+
"description": "Whether the user may choose multiple options.",
|
|
179
|
+
},
|
|
180
|
+
"options": {
|
|
181
|
+
"type": "array",
|
|
182
|
+
"description": "A list of predefined options for the user to choose from.",
|
|
183
|
+
"items": {
|
|
184
|
+
"type": "object",
|
|
185
|
+
"properties": {
|
|
186
|
+
"label": {
|
|
187
|
+
"type": "string",
|
|
188
|
+
"description": "The display text for the option.",
|
|
189
|
+
},
|
|
190
|
+
"description": {
|
|
191
|
+
"type": "string",
|
|
192
|
+
"description": (
|
|
193
|
+
"A detailed explanation or hint about this option to help the "
|
|
194
|
+
"user understand what happens if they choose it."
|
|
195
|
+
),
|
|
196
|
+
},
|
|
197
|
+
},
|
|
198
|
+
"required": ["label"],
|
|
199
|
+
},
|
|
200
|
+
},
|
|
201
|
+
},
|
|
202
|
+
"required": ["question", "options"],
|
|
203
|
+
},
|
|
204
|
+
},
|
|
205
|
+
},
|
|
206
|
+
"required": ["questions"],
|
|
207
|
+
"additionalProperties": False,
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
READ_SCHEMA: dict[str, Any] = {
|
|
211
|
+
"type": "object",
|
|
212
|
+
"properties": {
|
|
213
|
+
"file_path": {
|
|
214
|
+
"type": "string",
|
|
215
|
+
"description": "UNIX-style path to file",
|
|
216
|
+
},
|
|
217
|
+
"offset": {
|
|
218
|
+
"type": "number",
|
|
219
|
+
"description": "Line number to start reading from",
|
|
220
|
+
},
|
|
221
|
+
"limit": {
|
|
222
|
+
"type": "number",
|
|
223
|
+
"description": "Number of lines to read",
|
|
224
|
+
},
|
|
225
|
+
"pages": {
|
|
226
|
+
"type": "string",
|
|
227
|
+
"description": (
|
|
228
|
+
'Page range for PDF files (e.g., "1-5", "3", "10-20"). Only applicable to PDF files.'
|
|
229
|
+
),
|
|
230
|
+
},
|
|
231
|
+
},
|
|
232
|
+
"required": ["file_path"],
|
|
233
|
+
"additionalProperties": False,
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
MODIFY_SCHEMA: dict[str, Any] = {
|
|
237
|
+
"type": "object",
|
|
238
|
+
"properties": {
|
|
239
|
+
"file_path": {
|
|
240
|
+
"type": "string",
|
|
241
|
+
"description": "Absolute path to file. Optional when snippet_id scopes an existing edit.",
|
|
242
|
+
},
|
|
243
|
+
"snippet_id": {
|
|
244
|
+
"type": "string",
|
|
245
|
+
"description": (
|
|
246
|
+
"Snippet id returned by the Read or Modify tool to scope the search range after "
|
|
247
|
+
"a partial read."
|
|
248
|
+
),
|
|
249
|
+
},
|
|
250
|
+
"content": {
|
|
251
|
+
"type": "string",
|
|
252
|
+
"description": (
|
|
253
|
+
"Complete content for a new file only. Do not use for existing files; read the file "
|
|
254
|
+
"and use old_string/new_string instead."
|
|
255
|
+
),
|
|
256
|
+
},
|
|
257
|
+
"old_string": {
|
|
258
|
+
"type": "string",
|
|
259
|
+
"description": "Exact existing text to replace inside the file or snippet scope",
|
|
260
|
+
},
|
|
261
|
+
"new_string": {
|
|
262
|
+
"type": "string",
|
|
263
|
+
"description": "Replacement text for old_string",
|
|
264
|
+
},
|
|
265
|
+
"replace_all": {
|
|
266
|
+
"type": "boolean",
|
|
267
|
+
"description": "Replace all occurrences of old_string (default false)",
|
|
268
|
+
"default": False,
|
|
269
|
+
},
|
|
270
|
+
"expected_occurrences": {
|
|
271
|
+
"type": "number",
|
|
272
|
+
"description": (
|
|
273
|
+
"Expected number of matches, especially useful as a safety check with replace_all"
|
|
274
|
+
),
|
|
275
|
+
},
|
|
276
|
+
},
|
|
277
|
+
"required": [],
|
|
278
|
+
"additionalProperties": False,
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
EDIT_SCHEMA: dict[str, Any] = {
|
|
282
|
+
"type": "object",
|
|
283
|
+
"properties": {
|
|
284
|
+
"file_path": {
|
|
285
|
+
"type": "string",
|
|
286
|
+
"description": "Absolute path to file. Optional when snippet_id is provided.",
|
|
287
|
+
},
|
|
288
|
+
"snippet_id": {
|
|
289
|
+
"type": "string",
|
|
290
|
+
"description": (
|
|
291
|
+
"Snippet id returned by the Read or Edit tool to scope the search range after a partial read."
|
|
292
|
+
),
|
|
293
|
+
},
|
|
294
|
+
"old_string": {
|
|
295
|
+
"type": "string",
|
|
296
|
+
"description": "Exact text to replace inside the file or snippet scope",
|
|
297
|
+
},
|
|
298
|
+
"new_string": {
|
|
299
|
+
"type": "string",
|
|
300
|
+
"description": "Replacement text (must differ from old_string)",
|
|
301
|
+
},
|
|
302
|
+
"replace_all": {
|
|
303
|
+
"type": "boolean",
|
|
304
|
+
"description": "Replace all occurences of old_string (default false)",
|
|
305
|
+
"default": False,
|
|
306
|
+
},
|
|
307
|
+
"expected_occurrences": {
|
|
308
|
+
"type": "number",
|
|
309
|
+
"description": (
|
|
310
|
+
"Expected number of matches, especially useful as a safety check with replace_all"
|
|
311
|
+
),
|
|
312
|
+
},
|
|
313
|
+
},
|
|
314
|
+
"required": ["old_string", "new_string"],
|
|
315
|
+
"additionalProperties": False,
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
WEB_SEARCH_SCHEMA: dict[str, Any] = {
|
|
319
|
+
"type": "object",
|
|
320
|
+
"properties": {
|
|
321
|
+
"query": {
|
|
322
|
+
"type": "string",
|
|
323
|
+
"description": (
|
|
324
|
+
"A search query phrased as a clear, specific natural language question or statement "
|
|
325
|
+
"that includes key context."
|
|
326
|
+
),
|
|
327
|
+
},
|
|
328
|
+
},
|
|
329
|
+
"required": ["query"],
|
|
330
|
+
"additionalProperties": False,
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
WEB_FETCH_SCHEMA: dict[str, Any] = {
|
|
334
|
+
"type": "object",
|
|
335
|
+
"properties": {
|
|
336
|
+
"url": {
|
|
337
|
+
"type": "string",
|
|
338
|
+
"description": "A complete http or https URL to fetch.",
|
|
339
|
+
},
|
|
340
|
+
},
|
|
341
|
+
"required": ["url"],
|
|
342
|
+
"additionalProperties": False,
|
|
343
|
+
}
|