soothe-cli 0.1.0__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.
- soothe_cli/__init__.py +5 -0
- soothe_cli/cli/__init__.py +1 -0
- soothe_cli/cli/commands/__init__.py +1 -0
- soothe_cli/cli/commands/autopilot_cmd.py +410 -0
- soothe_cli/cli/commands/config_cmd.py +277 -0
- soothe_cli/cli/commands/run_cmd.py +87 -0
- soothe_cli/cli/commands/status_cmd.py +121 -0
- soothe_cli/cli/commands/subagent_names.py +17 -0
- soothe_cli/cli/commands/thread_cmd.py +657 -0
- soothe_cli/cli/execution/__init__.py +6 -0
- soothe_cli/cli/execution/daemon.py +194 -0
- soothe_cli/cli/execution/headless.py +99 -0
- soothe_cli/cli/execution/launcher.py +31 -0
- soothe_cli/cli/main.py +509 -0
- soothe_cli/cli/renderer.py +444 -0
- soothe_cli/cli/stream/__init__.py +17 -0
- soothe_cli/cli/stream/context.py +138 -0
- soothe_cli/cli/stream/display_line.py +83 -0
- soothe_cli/cli/stream/formatter.py +412 -0
- soothe_cli/cli/stream/pipeline.py +521 -0
- soothe_cli/cli/utils.py +46 -0
- soothe_cli/config/__init__.py +5 -0
- soothe_cli/config/cli_config.py +155 -0
- soothe_cli/plan/__init__.py +5 -0
- soothe_cli/plan/rich_tree.py +54 -0
- soothe_cli/shared/__init__.py +107 -0
- soothe_cli/shared/command_router.py +246 -0
- soothe_cli/shared/config_loader.py +68 -0
- soothe_cli/shared/display_policy.py +413 -0
- soothe_cli/shared/essential_events.py +68 -0
- soothe_cli/shared/event_processor.py +823 -0
- soothe_cli/shared/message_processing.py +393 -0
- soothe_cli/shared/presentation_engine.py +173 -0
- soothe_cli/shared/processor_state.py +80 -0
- soothe_cli/shared/renderer_protocol.py +158 -0
- soothe_cli/shared/rendering.py +43 -0
- soothe_cli/shared/slash_commands.py +354 -0
- soothe_cli/shared/subagent_routing.py +63 -0
- soothe_cli/shared/suppression_state.py +188 -0
- soothe_cli/shared/tool_formatters/__init__.py +27 -0
- soothe_cli/shared/tool_formatters/base.py +109 -0
- soothe_cli/shared/tool_formatters/execution.py +297 -0
- soothe_cli/shared/tool_formatters/fallback.py +128 -0
- soothe_cli/shared/tool_formatters/file_ops.py +299 -0
- soothe_cli/shared/tool_formatters/goal_formatter.py +331 -0
- soothe_cli/shared/tool_formatters/media.py +291 -0
- soothe_cli/shared/tool_formatters/structured.py +202 -0
- soothe_cli/shared/tool_formatters/web.py +143 -0
- soothe_cli/shared/tool_output_formatter.py +227 -0
- soothe_cli/shared/tui_trace_log.py +40 -0
- soothe_cli/tui/__init__.py +5 -0
- soothe_cli/tui/_ask_user_types.py +50 -0
- soothe_cli/tui/_cli_context.py +27 -0
- soothe_cli/tui/_env_vars.py +56 -0
- soothe_cli/tui/_session_stats.py +114 -0
- soothe_cli/tui/_version.py +21 -0
- soothe_cli/tui/app.py +4992 -0
- soothe_cli/tui/app.tcss +302 -0
- soothe_cli/tui/command_registry.py +310 -0
- soothe_cli/tui/config.py +2381 -0
- soothe_cli/tui/daemon_session.py +233 -0
- soothe_cli/tui/file_ops.py +409 -0
- soothe_cli/tui/formatting.py +28 -0
- soothe_cli/tui/hooks.py +23 -0
- soothe_cli/tui/input.py +782 -0
- soothe_cli/tui/media_utils.py +471 -0
- soothe_cli/tui/model_config.py +518 -0
- soothe_cli/tui/output.py +69 -0
- soothe_cli/tui/project_utils.py +188 -0
- soothe_cli/tui/sessions.py +1248 -0
- soothe_cli/tui/skills/__init__.py +5 -0
- soothe_cli/tui/skills/invocation.py +74 -0
- soothe_cli/tui/skills/load.py +93 -0
- soothe_cli/tui/textual_adapter.py +1430 -0
- soothe_cli/tui/theme.py +838 -0
- soothe_cli/tui/tool_display.py +297 -0
- soothe_cli/tui/unicode_security.py +502 -0
- soothe_cli/tui/update_check.py +447 -0
- soothe_cli/tui/widgets/__init__.py +9 -0
- soothe_cli/tui/widgets/_links.py +63 -0
- soothe_cli/tui/widgets/approval.py +430 -0
- soothe_cli/tui/widgets/ask_user.py +392 -0
- soothe_cli/tui/widgets/autocomplete.py +666 -0
- soothe_cli/tui/widgets/autopilot_dashboard.py +308 -0
- soothe_cli/tui/widgets/autopilot_screen.py +64 -0
- soothe_cli/tui/widgets/chat_input.py +1834 -0
- soothe_cli/tui/widgets/clipboard.py +128 -0
- soothe_cli/tui/widgets/diff.py +240 -0
- soothe_cli/tui/widgets/editor.py +140 -0
- soothe_cli/tui/widgets/history.py +221 -0
- soothe_cli/tui/widgets/loading.py +194 -0
- soothe_cli/tui/widgets/mcp_viewer.py +352 -0
- soothe_cli/tui/widgets/message_store.py +693 -0
- soothe_cli/tui/widgets/messages.py +1720 -0
- soothe_cli/tui/widgets/model_selector.py +988 -0
- soothe_cli/tui/widgets/notification_settings.py +155 -0
- soothe_cli/tui/widgets/status.py +403 -0
- soothe_cli/tui/widgets/theme_selector.py +158 -0
- soothe_cli/tui/widgets/thread_selector.py +1865 -0
- soothe_cli/tui/widgets/tool_renderers.py +148 -0
- soothe_cli/tui/widgets/tool_widgets.py +254 -0
- soothe_cli/tui/widgets/tools.py +165 -0
- soothe_cli/tui/widgets/welcome.py +330 -0
- soothe_cli-0.1.0.dist-info/METADATA +100 -0
- soothe_cli-0.1.0.dist-info/RECORD +107 -0
- soothe_cli-0.1.0.dist-info/WHEEL +4 -0
- soothe_cli-0.1.0.dist-info/entry_points.txt +2 -0
|
@@ -0,0 +1,331 @@
|
|
|
1
|
+
"""Formatter for goal management tools."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import Any
|
|
6
|
+
|
|
7
|
+
from soothe_cli.shared.tool_formatters.base import BaseFormatter
|
|
8
|
+
from soothe_cli.shared.tool_output_formatter import ToolBrief
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class GoalFormatter(BaseFormatter):
|
|
12
|
+
"""Formatter for goal management tools.
|
|
13
|
+
|
|
14
|
+
Handles: create_goal, list_goals, complete_goal, fail_goal
|
|
15
|
+
|
|
16
|
+
Provides semantic summaries with goal IDs, counts, and status.
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
def format(self, tool_name: str, result: Any) -> ToolBrief:
|
|
20
|
+
"""Format goal management tool result.
|
|
21
|
+
|
|
22
|
+
Args:
|
|
23
|
+
tool_name: Name of the goal tool.
|
|
24
|
+
result: Tool result (dict with goal data).
|
|
25
|
+
|
|
26
|
+
Returns:
|
|
27
|
+
ToolBrief with goal summary.
|
|
28
|
+
|
|
29
|
+
Raises:
|
|
30
|
+
ValueError: If tool_name is not a recognized goal tool.
|
|
31
|
+
|
|
32
|
+
Example:
|
|
33
|
+
>>> formatter = GoalFormatter()
|
|
34
|
+
>>> brief = formatter.format("create_goal", {"created": {"id": "g1"}})
|
|
35
|
+
>>> brief.to_display()
|
|
36
|
+
'✓ Created goal g1'
|
|
37
|
+
"""
|
|
38
|
+
# Normalize tool name
|
|
39
|
+
normalized = tool_name.lower().replace("-", "_").replace(" ", "_")
|
|
40
|
+
|
|
41
|
+
# Route to specific formatter
|
|
42
|
+
if normalized == "create_goal":
|
|
43
|
+
return self._format_create_goal(result)
|
|
44
|
+
if normalized == "list_goals":
|
|
45
|
+
return self._format_list_goals(result)
|
|
46
|
+
if normalized == "complete_goal":
|
|
47
|
+
return self._format_complete_goal(result)
|
|
48
|
+
if normalized == "fail_goal":
|
|
49
|
+
return self._format_fail_goal(result)
|
|
50
|
+
msg = f"Unknown goal tool: {tool_name}"
|
|
51
|
+
raise ValueError(msg)
|
|
52
|
+
|
|
53
|
+
def _format_create_goal(self, result: dict[str, Any]) -> ToolBrief:
|
|
54
|
+
"""Format create_goal result.
|
|
55
|
+
|
|
56
|
+
Shows created goal ID.
|
|
57
|
+
|
|
58
|
+
Args:
|
|
59
|
+
result: Dict with 'created' field containing goal object.
|
|
60
|
+
|
|
61
|
+
Returns:
|
|
62
|
+
ToolBrief with goal ID.
|
|
63
|
+
|
|
64
|
+
Example:
|
|
65
|
+
>>> brief = formatter._format_create_goal({"created": {"id": "g1", "priority": 80}})
|
|
66
|
+
>>> brief.summary
|
|
67
|
+
'Created goal g1'
|
|
68
|
+
"""
|
|
69
|
+
# Handle dict result
|
|
70
|
+
if isinstance(result, dict):
|
|
71
|
+
# Check for error
|
|
72
|
+
if "error" in result:
|
|
73
|
+
error_msg = str(result["error"])
|
|
74
|
+
return ToolBrief(
|
|
75
|
+
icon="✗",
|
|
76
|
+
summary="Create failed",
|
|
77
|
+
detail=self._truncate_text(error_msg, 80),
|
|
78
|
+
metrics={"error": True},
|
|
79
|
+
)
|
|
80
|
+
|
|
81
|
+
# Extract goal data
|
|
82
|
+
created = result.get("created", {})
|
|
83
|
+
goal_id = created.get("id", "unknown")
|
|
84
|
+
priority = created.get("priority")
|
|
85
|
+
|
|
86
|
+
# Build summary
|
|
87
|
+
summary = f"Created goal {goal_id}"
|
|
88
|
+
|
|
89
|
+
# Build detail
|
|
90
|
+
detail = None
|
|
91
|
+
if priority is not None:
|
|
92
|
+
detail = f"priority: {priority}"
|
|
93
|
+
|
|
94
|
+
return ToolBrief(
|
|
95
|
+
icon="✓",
|
|
96
|
+
summary=summary,
|
|
97
|
+
detail=detail,
|
|
98
|
+
metrics={"goal_id": goal_id, "priority": priority},
|
|
99
|
+
)
|
|
100
|
+
|
|
101
|
+
# Handle string result (fallback)
|
|
102
|
+
if isinstance(result, str):
|
|
103
|
+
if "error" in result.lower() or "failed" in result.lower():
|
|
104
|
+
return ToolBrief(
|
|
105
|
+
icon="✗",
|
|
106
|
+
summary="Create failed",
|
|
107
|
+
detail=self._truncate_text(result, 80),
|
|
108
|
+
metrics={"error": True},
|
|
109
|
+
)
|
|
110
|
+
|
|
111
|
+
return ToolBrief(
|
|
112
|
+
icon="✓",
|
|
113
|
+
summary="Created goal",
|
|
114
|
+
detail=None,
|
|
115
|
+
metrics={},
|
|
116
|
+
)
|
|
117
|
+
|
|
118
|
+
# Unknown type
|
|
119
|
+
return ToolBrief(
|
|
120
|
+
icon="✓",
|
|
121
|
+
summary="Created goal",
|
|
122
|
+
detail=None,
|
|
123
|
+
metrics={},
|
|
124
|
+
)
|
|
125
|
+
|
|
126
|
+
def _format_list_goals(self, result: dict[str, Any]) -> ToolBrief:
|
|
127
|
+
"""Format list_goals result.
|
|
128
|
+
|
|
129
|
+
Shows count of goals.
|
|
130
|
+
|
|
131
|
+
Args:
|
|
132
|
+
result: Dict with 'goals' field containing list of goal objects.
|
|
133
|
+
|
|
134
|
+
Returns:
|
|
135
|
+
ToolBrief with goal count.
|
|
136
|
+
|
|
137
|
+
Example:
|
|
138
|
+
>>> brief = formatter._format_list_goals({"goals": [{"id": "g1"}, {"id": "g2"}]})
|
|
139
|
+
>>> brief.summary
|
|
140
|
+
'Found 2 goals'
|
|
141
|
+
"""
|
|
142
|
+
# Handle dict result
|
|
143
|
+
if isinstance(result, dict):
|
|
144
|
+
# Check for error
|
|
145
|
+
if "error" in result:
|
|
146
|
+
error_msg = str(result["error"])
|
|
147
|
+
return ToolBrief(
|
|
148
|
+
icon="✗",
|
|
149
|
+
summary="List failed",
|
|
150
|
+
detail=self._truncate_text(error_msg, 80),
|
|
151
|
+
metrics={"error": True},
|
|
152
|
+
)
|
|
153
|
+
|
|
154
|
+
# Extract goals
|
|
155
|
+
goals = result.get("goals", [])
|
|
156
|
+
count = len(goals)
|
|
157
|
+
|
|
158
|
+
# Build summary
|
|
159
|
+
summary = f"Found {count} goal{'s' if count != 1 else ''}"
|
|
160
|
+
|
|
161
|
+
return ToolBrief(
|
|
162
|
+
icon="✓",
|
|
163
|
+
summary=summary,
|
|
164
|
+
detail=None,
|
|
165
|
+
metrics={"count": count},
|
|
166
|
+
)
|
|
167
|
+
|
|
168
|
+
# Handle string result (fallback)
|
|
169
|
+
if isinstance(result, str):
|
|
170
|
+
if "error" in result.lower() or "failed" in result.lower():
|
|
171
|
+
return ToolBrief(
|
|
172
|
+
icon="✗",
|
|
173
|
+
summary="List failed",
|
|
174
|
+
detail=self._truncate_text(result, 80),
|
|
175
|
+
metrics={"error": True},
|
|
176
|
+
)
|
|
177
|
+
|
|
178
|
+
return ToolBrief(
|
|
179
|
+
icon="✓",
|
|
180
|
+
summary="Listed goals",
|
|
181
|
+
detail=None,
|
|
182
|
+
metrics={},
|
|
183
|
+
)
|
|
184
|
+
|
|
185
|
+
# Unknown type
|
|
186
|
+
return ToolBrief(
|
|
187
|
+
icon="✓",
|
|
188
|
+
summary="Listed goals",
|
|
189
|
+
detail=None,
|
|
190
|
+
metrics={},
|
|
191
|
+
)
|
|
192
|
+
|
|
193
|
+
def _format_complete_goal(self, result: dict[str, Any]) -> ToolBrief:
|
|
194
|
+
"""Format complete_goal result.
|
|
195
|
+
|
|
196
|
+
Shows completed goal ID.
|
|
197
|
+
|
|
198
|
+
Args:
|
|
199
|
+
result: Dict with 'completed' field containing goal object.
|
|
200
|
+
|
|
201
|
+
Returns:
|
|
202
|
+
ToolBrief with goal ID.
|
|
203
|
+
|
|
204
|
+
Example:
|
|
205
|
+
>>> brief = formatter._format_complete_goal({"completed": {"id": "g1"}})
|
|
206
|
+
>>> brief.summary
|
|
207
|
+
'Completed goal g1'
|
|
208
|
+
"""
|
|
209
|
+
# Handle dict result
|
|
210
|
+
if isinstance(result, dict):
|
|
211
|
+
# Check for error
|
|
212
|
+
if "error" in result:
|
|
213
|
+
error_msg = str(result["error"])
|
|
214
|
+
return ToolBrief(
|
|
215
|
+
icon="✗",
|
|
216
|
+
summary="Complete failed",
|
|
217
|
+
detail=self._truncate_text(error_msg, 80),
|
|
218
|
+
metrics={"error": True},
|
|
219
|
+
)
|
|
220
|
+
|
|
221
|
+
# Extract goal data
|
|
222
|
+
completed = result.get("completed", {})
|
|
223
|
+
goal_id = completed.get("id", "unknown")
|
|
224
|
+
|
|
225
|
+
# Build summary
|
|
226
|
+
summary = f"Completed goal {goal_id}"
|
|
227
|
+
|
|
228
|
+
return ToolBrief(
|
|
229
|
+
icon="✓",
|
|
230
|
+
summary=summary,
|
|
231
|
+
detail=None,
|
|
232
|
+
metrics={"goal_id": goal_id},
|
|
233
|
+
)
|
|
234
|
+
|
|
235
|
+
# Handle string result (fallback)
|
|
236
|
+
if isinstance(result, str):
|
|
237
|
+
if "error" in result.lower() or "failed" in result.lower():
|
|
238
|
+
return ToolBrief(
|
|
239
|
+
icon="✗",
|
|
240
|
+
summary="Complete failed",
|
|
241
|
+
detail=self._truncate_text(result, 80),
|
|
242
|
+
metrics={"error": True},
|
|
243
|
+
)
|
|
244
|
+
|
|
245
|
+
return ToolBrief(
|
|
246
|
+
icon="✓",
|
|
247
|
+
summary="Completed goal",
|
|
248
|
+
detail=None,
|
|
249
|
+
metrics={},
|
|
250
|
+
)
|
|
251
|
+
|
|
252
|
+
# Unknown type
|
|
253
|
+
return ToolBrief(
|
|
254
|
+
icon="✓",
|
|
255
|
+
summary="Completed goal",
|
|
256
|
+
detail=None,
|
|
257
|
+
metrics={},
|
|
258
|
+
)
|
|
259
|
+
|
|
260
|
+
def _format_fail_goal(self, result: dict[str, Any]) -> ToolBrief:
|
|
261
|
+
"""Format fail_goal result.
|
|
262
|
+
|
|
263
|
+
Shows failed goal ID and reason.
|
|
264
|
+
|
|
265
|
+
Args:
|
|
266
|
+
result: Dict with 'failed' field containing goal object.
|
|
267
|
+
|
|
268
|
+
Returns:
|
|
269
|
+
ToolBrief with goal ID and failure reason.
|
|
270
|
+
|
|
271
|
+
Example:
|
|
272
|
+
>>> brief = formatter._format_fail_goal({"failed": {"id": "g1", "reason": "blocked"}})
|
|
273
|
+
>>> brief.summary
|
|
274
|
+
'Failed goal g1'
|
|
275
|
+
>>> brief.detail
|
|
276
|
+
'reason: blocked'
|
|
277
|
+
"""
|
|
278
|
+
# Handle dict result
|
|
279
|
+
if isinstance(result, dict):
|
|
280
|
+
# Check for error
|
|
281
|
+
if "error" in result:
|
|
282
|
+
error_msg = str(result["error"])
|
|
283
|
+
return ToolBrief(
|
|
284
|
+
icon="✗",
|
|
285
|
+
summary="Fail operation failed",
|
|
286
|
+
detail=self._truncate_text(error_msg, 80),
|
|
287
|
+
metrics={"error": True},
|
|
288
|
+
)
|
|
289
|
+
|
|
290
|
+
# Extract goal data
|
|
291
|
+
failed = result.get("failed", {})
|
|
292
|
+
goal_id = failed.get("id", "unknown")
|
|
293
|
+
reason = failed.get("reason", "unknown reason")
|
|
294
|
+
|
|
295
|
+
# Build summary
|
|
296
|
+
summary = f"Failed goal {goal_id}"
|
|
297
|
+
|
|
298
|
+
# Build detail
|
|
299
|
+
detail = f"reason: {reason}"
|
|
300
|
+
|
|
301
|
+
return ToolBrief(
|
|
302
|
+
icon="✗",
|
|
303
|
+
summary=summary,
|
|
304
|
+
detail=detail,
|
|
305
|
+
metrics={"goal_id": goal_id, "reason": reason},
|
|
306
|
+
)
|
|
307
|
+
|
|
308
|
+
# Handle string result (fallback)
|
|
309
|
+
if isinstance(result, str):
|
|
310
|
+
if "error" in result.lower() or "failed" in result.lower():
|
|
311
|
+
return ToolBrief(
|
|
312
|
+
icon="✗",
|
|
313
|
+
summary="Fail operation failed",
|
|
314
|
+
detail=self._truncate_text(result, 80),
|
|
315
|
+
metrics={"error": True},
|
|
316
|
+
)
|
|
317
|
+
|
|
318
|
+
return ToolBrief(
|
|
319
|
+
icon="✗",
|
|
320
|
+
summary="Failed goal",
|
|
321
|
+
detail=None,
|
|
322
|
+
metrics={},
|
|
323
|
+
)
|
|
324
|
+
|
|
325
|
+
# Unknown type
|
|
326
|
+
return ToolBrief(
|
|
327
|
+
icon="✗",
|
|
328
|
+
summary="Failed goal",
|
|
329
|
+
detail=None,
|
|
330
|
+
metrics={},
|
|
331
|
+
)
|
|
@@ -0,0 +1,291 @@
|
|
|
1
|
+
"""Formatter for media tools."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import Any
|
|
6
|
+
|
|
7
|
+
from soothe_cli.shared.tool_formatters.base import BaseFormatter
|
|
8
|
+
from soothe_cli.shared.tool_output_formatter import ToolBrief
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class MediaFormatter(BaseFormatter):
|
|
12
|
+
"""Formatter for media tools.
|
|
13
|
+
|
|
14
|
+
Handles: transcribe_audio, get_video_info, analyze_image
|
|
15
|
+
|
|
16
|
+
Provides semantic summaries with duration, resolution, and format metrics.
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
def format(self, tool_name: str, result: Any) -> ToolBrief:
|
|
20
|
+
"""Format media tool result.
|
|
21
|
+
|
|
22
|
+
Args:
|
|
23
|
+
tool_name: Name of the media tool.
|
|
24
|
+
result: Tool result (dict with media metadata).
|
|
25
|
+
|
|
26
|
+
Returns:
|
|
27
|
+
ToolBrief with media summary.
|
|
28
|
+
|
|
29
|
+
Raises:
|
|
30
|
+
ValueError: If tool_name is not a recognized media tool.
|
|
31
|
+
|
|
32
|
+
Example:
|
|
33
|
+
>>> formatter = MediaFormatter()
|
|
34
|
+
>>> brief = formatter.format("transcribe_audio", {"duration": 45.2, "language": "en"})
|
|
35
|
+
>>> brief.to_display()
|
|
36
|
+
'✓ Transcribed 45.2s (en)'
|
|
37
|
+
"""
|
|
38
|
+
# Normalize tool name
|
|
39
|
+
normalized = tool_name.lower().replace("-", "_").replace(" ", "_")
|
|
40
|
+
|
|
41
|
+
# Route to specific formatter
|
|
42
|
+
if normalized == "transcribe_audio":
|
|
43
|
+
return self._format_transcribe_audio(result)
|
|
44
|
+
if normalized == "get_video_info":
|
|
45
|
+
return self._format_get_video_info(result)
|
|
46
|
+
if normalized == "analyze_image":
|
|
47
|
+
return self._format_analyze_image(result)
|
|
48
|
+
msg = f"Unknown media tool: {tool_name}"
|
|
49
|
+
raise ValueError(msg)
|
|
50
|
+
|
|
51
|
+
def _format_transcribe_audio(self, result: dict[str, Any]) -> ToolBrief:
|
|
52
|
+
"""Format transcribe_audio result.
|
|
53
|
+
|
|
54
|
+
Shows duration and language.
|
|
55
|
+
|
|
56
|
+
Args:
|
|
57
|
+
result: Dict with 'text', 'duration', 'language', and optional 'error'.
|
|
58
|
+
|
|
59
|
+
Returns:
|
|
60
|
+
ToolBrief with transcription summary.
|
|
61
|
+
|
|
62
|
+
Example:
|
|
63
|
+
>>> brief = formatter._format_transcribe_audio(
|
|
64
|
+
... {"duration": 45.2, "language": "en", "text": "hello"}
|
|
65
|
+
... )
|
|
66
|
+
>>> brief.summary
|
|
67
|
+
'Transcribed 45.2s'
|
|
68
|
+
>>> brief.detail
|
|
69
|
+
'language: en'
|
|
70
|
+
"""
|
|
71
|
+
# Handle dict result
|
|
72
|
+
if isinstance(result, dict):
|
|
73
|
+
# Check for error
|
|
74
|
+
if "error" in result:
|
|
75
|
+
error_msg = str(result["error"])
|
|
76
|
+
return ToolBrief(
|
|
77
|
+
icon="✗",
|
|
78
|
+
summary="Transcription failed",
|
|
79
|
+
detail=self._truncate_text(error_msg, 80),
|
|
80
|
+
metrics={"error": True},
|
|
81
|
+
)
|
|
82
|
+
|
|
83
|
+
# Extract metadata
|
|
84
|
+
duration = result.get("duration", 0.0)
|
|
85
|
+
language = result.get("language", "unknown")
|
|
86
|
+
text_length = len(result.get("text", ""))
|
|
87
|
+
|
|
88
|
+
# Build summary
|
|
89
|
+
summary = f"Transcribed {duration:.1f}s"
|
|
90
|
+
|
|
91
|
+
# Build detail
|
|
92
|
+
detail = f"language: {language}"
|
|
93
|
+
|
|
94
|
+
return ToolBrief(
|
|
95
|
+
icon="✓",
|
|
96
|
+
summary=summary,
|
|
97
|
+
detail=detail,
|
|
98
|
+
metrics={
|
|
99
|
+
"duration": duration,
|
|
100
|
+
"language": language,
|
|
101
|
+
"text_length": text_length,
|
|
102
|
+
},
|
|
103
|
+
)
|
|
104
|
+
|
|
105
|
+
# Handle string result (fallback)
|
|
106
|
+
if isinstance(result, str):
|
|
107
|
+
if "error" in result.lower() or "failed" in result.lower():
|
|
108
|
+
return ToolBrief(
|
|
109
|
+
icon="✗",
|
|
110
|
+
summary="Transcription failed",
|
|
111
|
+
detail=self._truncate_text(result, 80),
|
|
112
|
+
metrics={"error": True},
|
|
113
|
+
)
|
|
114
|
+
|
|
115
|
+
return ToolBrief(
|
|
116
|
+
icon="✓",
|
|
117
|
+
summary="Transcribed",
|
|
118
|
+
detail=f"{len(result)} chars",
|
|
119
|
+
metrics={},
|
|
120
|
+
)
|
|
121
|
+
|
|
122
|
+
# Unknown type
|
|
123
|
+
return ToolBrief(
|
|
124
|
+
icon="✓",
|
|
125
|
+
summary="Transcribed",
|
|
126
|
+
detail=None,
|
|
127
|
+
metrics={},
|
|
128
|
+
)
|
|
129
|
+
|
|
130
|
+
def _format_get_video_info(self, result: dict[str, Any]) -> ToolBrief:
|
|
131
|
+
"""Format get_video_info result.
|
|
132
|
+
|
|
133
|
+
Shows duration, resolution, and format.
|
|
134
|
+
|
|
135
|
+
Args:
|
|
136
|
+
result: Dict with 'duration_seconds', 'format', 'codec', and optional 'error'.
|
|
137
|
+
|
|
138
|
+
Returns:
|
|
139
|
+
ToolBrief with video info summary.
|
|
140
|
+
|
|
141
|
+
Example:
|
|
142
|
+
>>> brief = formatter._format_get_video_info({"duration_seconds": 120, "format": "mp4"})
|
|
143
|
+
>>> brief.summary
|
|
144
|
+
'Video: 120s'
|
|
145
|
+
"""
|
|
146
|
+
# Handle dict result
|
|
147
|
+
if isinstance(result, dict):
|
|
148
|
+
# Check for error
|
|
149
|
+
if "error" in result:
|
|
150
|
+
error_msg = str(result["error"])
|
|
151
|
+
return ToolBrief(
|
|
152
|
+
icon="✗",
|
|
153
|
+
summary="Video info failed",
|
|
154
|
+
detail=self._truncate_text(error_msg, 80),
|
|
155
|
+
metrics={"error": True},
|
|
156
|
+
)
|
|
157
|
+
|
|
158
|
+
# Extract metadata
|
|
159
|
+
duration = result.get("duration_seconds", 0.0)
|
|
160
|
+
video_format = result.get("format", "unknown")
|
|
161
|
+
codec = result.get("codec", "unknown")
|
|
162
|
+
size_bytes = result.get("size_bytes", 0)
|
|
163
|
+
|
|
164
|
+
# Build summary
|
|
165
|
+
summary = f"Video: {duration:.0f}s"
|
|
166
|
+
|
|
167
|
+
# Build detail with resolution if available (not in basic schema)
|
|
168
|
+
# Just show format for now
|
|
169
|
+
detail = f"{video_format}, {codec}"
|
|
170
|
+
|
|
171
|
+
return ToolBrief(
|
|
172
|
+
icon="✓",
|
|
173
|
+
summary=summary,
|
|
174
|
+
detail=detail,
|
|
175
|
+
metrics={
|
|
176
|
+
"duration": duration,
|
|
177
|
+
"format": video_format,
|
|
178
|
+
"codec": codec,
|
|
179
|
+
"size_bytes": size_bytes,
|
|
180
|
+
},
|
|
181
|
+
)
|
|
182
|
+
|
|
183
|
+
# Handle string result (fallback)
|
|
184
|
+
if isinstance(result, str):
|
|
185
|
+
if "error" in result.lower() or "failed" in result.lower():
|
|
186
|
+
return ToolBrief(
|
|
187
|
+
icon="✗",
|
|
188
|
+
summary="Video info failed",
|
|
189
|
+
detail=self._truncate_text(result, 80),
|
|
190
|
+
metrics={"error": True},
|
|
191
|
+
)
|
|
192
|
+
|
|
193
|
+
return ToolBrief(
|
|
194
|
+
icon="✓",
|
|
195
|
+
summary="Video info retrieved",
|
|
196
|
+
detail=self._truncate_text(result, 80),
|
|
197
|
+
metrics={},
|
|
198
|
+
)
|
|
199
|
+
|
|
200
|
+
# Unknown type
|
|
201
|
+
return ToolBrief(
|
|
202
|
+
icon="✓",
|
|
203
|
+
summary="Video info retrieved",
|
|
204
|
+
detail=None,
|
|
205
|
+
metrics={},
|
|
206
|
+
)
|
|
207
|
+
|
|
208
|
+
def _format_analyze_image(self, result: dict[str, Any]) -> ToolBrief:
|
|
209
|
+
"""Format analyze_image result.
|
|
210
|
+
|
|
211
|
+
Shows size and format.
|
|
212
|
+
|
|
213
|
+
Args:
|
|
214
|
+
result: Dict with image metadata and optional 'error'.
|
|
215
|
+
|
|
216
|
+
Returns:
|
|
217
|
+
ToolBrief with image analysis summary.
|
|
218
|
+
|
|
219
|
+
Example:
|
|
220
|
+
>>> brief = formatter._format_analyze_image({"size_bytes": 2400000, "format": "PNG"})
|
|
221
|
+
>>> brief.summary
|
|
222
|
+
'Analyzed image'
|
|
223
|
+
>>> brief.detail
|
|
224
|
+
'2.3 MB, PNG'
|
|
225
|
+
"""
|
|
226
|
+
# Handle dict result
|
|
227
|
+
if isinstance(result, dict):
|
|
228
|
+
# Check for error
|
|
229
|
+
if "error" in result:
|
|
230
|
+
error_msg = str(result["error"])
|
|
231
|
+
return ToolBrief(
|
|
232
|
+
icon="✗",
|
|
233
|
+
summary="Image analysis failed",
|
|
234
|
+
detail=self._truncate_text(error_msg, 80),
|
|
235
|
+
metrics={"error": True},
|
|
236
|
+
)
|
|
237
|
+
|
|
238
|
+
# Extract metadata
|
|
239
|
+
size_bytes = result.get("size_bytes", 0)
|
|
240
|
+
image_format = result.get("format", "unknown")
|
|
241
|
+
width = result.get("width")
|
|
242
|
+
height = result.get("height")
|
|
243
|
+
|
|
244
|
+
# Build summary
|
|
245
|
+
summary = "Analyzed image"
|
|
246
|
+
|
|
247
|
+
# Build detail
|
|
248
|
+
size_str = self._format_size(size_bytes)
|
|
249
|
+
detail_parts = [size_str, image_format]
|
|
250
|
+
|
|
251
|
+
if width and height:
|
|
252
|
+
detail_parts.append(f"{width}x{height}")
|
|
253
|
+
|
|
254
|
+
detail = ", ".join(detail_parts)
|
|
255
|
+
|
|
256
|
+
return ToolBrief(
|
|
257
|
+
icon="✓",
|
|
258
|
+
summary=summary,
|
|
259
|
+
detail=detail,
|
|
260
|
+
metrics={
|
|
261
|
+
"size_bytes": size_bytes,
|
|
262
|
+
"format": image_format,
|
|
263
|
+
"width": width,
|
|
264
|
+
"height": height,
|
|
265
|
+
},
|
|
266
|
+
)
|
|
267
|
+
|
|
268
|
+
# Handle string result (fallback)
|
|
269
|
+
if isinstance(result, str):
|
|
270
|
+
if "error" in result.lower() or "failed" in result.lower():
|
|
271
|
+
return ToolBrief(
|
|
272
|
+
icon="✗",
|
|
273
|
+
summary="Image analysis failed",
|
|
274
|
+
detail=self._truncate_text(result, 80),
|
|
275
|
+
metrics={"error": True},
|
|
276
|
+
)
|
|
277
|
+
|
|
278
|
+
return ToolBrief(
|
|
279
|
+
icon="✓",
|
|
280
|
+
summary="Analyzed image",
|
|
281
|
+
detail=self._truncate_text(result, 80),
|
|
282
|
+
metrics={},
|
|
283
|
+
)
|
|
284
|
+
|
|
285
|
+
# Unknown type
|
|
286
|
+
return ToolBrief(
|
|
287
|
+
icon="✓",
|
|
288
|
+
summary="Analyzed image",
|
|
289
|
+
detail=None,
|
|
290
|
+
metrics={},
|
|
291
|
+
)
|