vibecore 0.3.0b2__py3-none-any.whl → 0.3.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.
Potentially problematic release.
This version of vibecore might be problematic. Click here for more details.
- vibecore/settings.py +5 -0
- vibecore/widgets/tool_message_factory.py +8 -0
- vibecore/widgets/tool_messages.py +112 -0
- vibecore/widgets/tool_messages.tcss +69 -0
- {vibecore-0.3.0b2.dist-info → vibecore-0.3.1.dist-info}/METADATA +1 -1
- {vibecore-0.3.0b2.dist-info → vibecore-0.3.1.dist-info}/RECORD +9 -9
- {vibecore-0.3.0b2.dist-info → vibecore-0.3.1.dist-info}/WHEEL +0 -0
- {vibecore-0.3.0b2.dist-info → vibecore-0.3.1.dist-info}/entry_points.txt +0 -0
- {vibecore-0.3.0b2.dist-info → vibecore-0.3.1.dist-info}/licenses/LICENSE +0 -0
vibecore/settings.py
CHANGED
|
@@ -155,6 +155,11 @@ class Settings(BaseSettings):
|
|
|
155
155
|
description="List of MCP servers to connect to",
|
|
156
156
|
)
|
|
157
157
|
|
|
158
|
+
rich_tool_names: list[str] = Field(
|
|
159
|
+
default_factory=list,
|
|
160
|
+
description="List of tools to render with RichToolMessage (temporary settings)",
|
|
161
|
+
)
|
|
162
|
+
|
|
158
163
|
@property
|
|
159
164
|
def model(self) -> str | Model:
|
|
160
165
|
"""Get the configured model.
|
|
@@ -9,12 +9,14 @@ import contextlib
|
|
|
9
9
|
import json
|
|
10
10
|
from typing import Any
|
|
11
11
|
|
|
12
|
+
from vibecore.settings import settings
|
|
12
13
|
from vibecore.widgets.messages import MessageStatus
|
|
13
14
|
from vibecore.widgets.tool_messages import (
|
|
14
15
|
BaseToolMessage,
|
|
15
16
|
MCPToolMessage,
|
|
16
17
|
PythonToolMessage,
|
|
17
18
|
ReadToolMessage,
|
|
19
|
+
RichToolMessage,
|
|
18
20
|
TaskToolMessage,
|
|
19
21
|
TodoWriteToolMessage,
|
|
20
22
|
ToolMessage,
|
|
@@ -129,6 +131,12 @@ def create_tool_message(
|
|
|
129
131
|
else:
|
|
130
132
|
return WebFetchToolMessage(url=url, status=status)
|
|
131
133
|
|
|
134
|
+
elif tool_name in settings.rich_tool_names:
|
|
135
|
+
if output is not None:
|
|
136
|
+
return RichToolMessage(tool_name=tool_name, arguments=arguments, output=output, status=status)
|
|
137
|
+
else:
|
|
138
|
+
return RichToolMessage(tool_name=tool_name, arguments=arguments, status=status)
|
|
139
|
+
|
|
132
140
|
# Default to generic ToolMessage for all other tools
|
|
133
141
|
else:
|
|
134
142
|
if output is not None:
|
|
@@ -483,6 +483,118 @@ class MCPToolMessage(BaseToolMessage):
|
|
|
483
483
|
)
|
|
484
484
|
|
|
485
485
|
|
|
486
|
+
class RichToolMessage(BaseToolMessage):
|
|
487
|
+
"""A widget to display rich (json/markdown) custom tool execution messages."""
|
|
488
|
+
|
|
489
|
+
SQL_QUERY_KEY = "query"
|
|
490
|
+
tool_name: reactive[str] = reactive("")
|
|
491
|
+
arguments: reactive[str] = reactive("")
|
|
492
|
+
|
|
493
|
+
def __init__(
|
|
494
|
+
self,
|
|
495
|
+
tool_name: str,
|
|
496
|
+
arguments: str,
|
|
497
|
+
output: str = "",
|
|
498
|
+
status: MessageStatus = MessageStatus.EXECUTING,
|
|
499
|
+
**kwargs,
|
|
500
|
+
) -> None:
|
|
501
|
+
"""
|
|
502
|
+
Construct an RichToolMessage.
|
|
503
|
+
|
|
504
|
+
Args:
|
|
505
|
+
tool_name: The name of the tool being called.
|
|
506
|
+
arguments: JSON string of tool arguments.
|
|
507
|
+
output: The output from the tool (optional, can be set later).
|
|
508
|
+
status: The status of execution.
|
|
509
|
+
**kwargs: Additional keyword arguments for Widget.
|
|
510
|
+
"""
|
|
511
|
+
super().__init__(status=status, **kwargs)
|
|
512
|
+
self.tool_name = tool_name
|
|
513
|
+
self.arguments = arguments
|
|
514
|
+
self.output = output
|
|
515
|
+
|
|
516
|
+
def _prettify_json_output(self, output: str) -> tuple[bool, str]:
|
|
517
|
+
"""Try to prettify JSON output.
|
|
518
|
+
|
|
519
|
+
Args:
|
|
520
|
+
output: The raw output string.
|
|
521
|
+
|
|
522
|
+
Returns:
|
|
523
|
+
A tuple of (is_json, formatted_output).
|
|
524
|
+
"""
|
|
525
|
+
if not output or not output.strip():
|
|
526
|
+
return False, output
|
|
527
|
+
|
|
528
|
+
try:
|
|
529
|
+
# Try to parse as JSON
|
|
530
|
+
json_obj = json.loads(output)
|
|
531
|
+
# Pretty print with 2-space indentation
|
|
532
|
+
formatted = json.dumps(json_obj, indent=2, ensure_ascii=False)
|
|
533
|
+
return True, formatted
|
|
534
|
+
except (json.JSONDecodeError, TypeError, ValueError):
|
|
535
|
+
# Not valid JSON, return as-is
|
|
536
|
+
return False, output
|
|
537
|
+
|
|
538
|
+
def on_button_pressed(self, event: Button.Pressed) -> None:
|
|
539
|
+
"""Handle button press events."""
|
|
540
|
+
if event.button.has_class("copy-button"):
|
|
541
|
+
arg_dict = json.loads(self.arguments)
|
|
542
|
+
if self.SQL_QUERY_KEY in arg_dict:
|
|
543
|
+
# XXX(serialx): Special case for SQL queries
|
|
544
|
+
query = arg_dict[self.SQL_QUERY_KEY]
|
|
545
|
+
self.app.copy_to_clipboard(query)
|
|
546
|
+
|
|
547
|
+
def compose(self) -> ComposeResult:
|
|
548
|
+
"""Create child widgets for the rich tool message."""
|
|
549
|
+
# Header line showing MCP server and tool
|
|
550
|
+
# Access the actual values, not the reactive descriptors
|
|
551
|
+
yield MessageHeader("⏺", self.tool_name, status=self.status)
|
|
552
|
+
|
|
553
|
+
# Arguments display (if any)
|
|
554
|
+
if self.arguments and self.arguments != "{}":
|
|
555
|
+
with Horizontal(classes="rich-arguments"):
|
|
556
|
+
yield Static("└─", classes="rich-arguments-prefix")
|
|
557
|
+
with Vertical(classes="rich-arguments-content"):
|
|
558
|
+
# Truncate arguments if too long
|
|
559
|
+
max_args_length = 100
|
|
560
|
+
arg_dict = json.loads(self.arguments)
|
|
561
|
+
if self.SQL_QUERY_KEY in arg_dict:
|
|
562
|
+
# XXX(serialx): Special case for SQL queries
|
|
563
|
+
query = arg_dict[self.SQL_QUERY_KEY]
|
|
564
|
+
yield Button("Copy", classes="copy-button", variant="primary")
|
|
565
|
+
yield ExpandableMarkdown(query, language="sql", truncated_lines=8, classes="rich-output-sql")
|
|
566
|
+
del arg_dict[self.SQL_QUERY_KEY]
|
|
567
|
+
|
|
568
|
+
display_args = arg_dict[:max_args_length] + "…" if len(arg_dict) > max_args_length else arg_dict
|
|
569
|
+
yield Static(f"Args: {display_args}", classes="rich-arguments-text")
|
|
570
|
+
|
|
571
|
+
# Output - check if it's JSON and prettify if so
|
|
572
|
+
if self.output:
|
|
573
|
+
try:
|
|
574
|
+
json_output = json.loads(self.output)
|
|
575
|
+
except json.JSONDecodeError:
|
|
576
|
+
json_output = None
|
|
577
|
+
if json_output:
|
|
578
|
+
assert json_output.get("type") == "text", "Expected JSON output type to be 'text'"
|
|
579
|
+
is_json, processed_output = self._prettify_json_output(json_output.get("text", ""))
|
|
580
|
+
else:
|
|
581
|
+
# output should always be a JSON string, but if not, treat it as plain text
|
|
582
|
+
is_json, processed_output = False, self.output
|
|
583
|
+
with Horizontal(classes="tool-output"):
|
|
584
|
+
yield Static("└─", classes="tool-output-prefix")
|
|
585
|
+
with Vertical(classes="tool-output-content"):
|
|
586
|
+
if is_json:
|
|
587
|
+
# Use ExpandableMarkdown for JSON with syntax highlighting
|
|
588
|
+
yield ExpandableMarkdown(
|
|
589
|
+
processed_output, language="json", truncated_lines=8, classes="rich-output-json"
|
|
590
|
+
)
|
|
591
|
+
else:
|
|
592
|
+
# Use ExpandableMarkdown for non-JSON content (renders as markdown without code block)
|
|
593
|
+
yield ExpandableMarkdown(
|
|
594
|
+
processed_output, language="", truncated_lines=5, classes="rich-output-markdown"
|
|
595
|
+
)
|
|
596
|
+
|
|
597
|
+
|
|
486
598
|
class WebSearchToolMessage(BaseToolMessage):
|
|
487
599
|
"""A widget to display web search results."""
|
|
488
600
|
|
|
@@ -288,6 +288,75 @@ MCPToolMessage {
|
|
|
288
288
|
}
|
|
289
289
|
}
|
|
290
290
|
|
|
291
|
+
RichToolMessage {
|
|
292
|
+
Horizontal.rich-arguments {
|
|
293
|
+
height: auto;
|
|
294
|
+
layers: main button;
|
|
295
|
+
|
|
296
|
+
.copy-button {
|
|
297
|
+
layer: button;
|
|
298
|
+
dock: right;
|
|
299
|
+
height: 1;
|
|
300
|
+
width: 8;
|
|
301
|
+
min-width: 8;
|
|
302
|
+
margin: 0;
|
|
303
|
+
padding: 0;
|
|
304
|
+
background: $secondary;
|
|
305
|
+
color: $text;
|
|
306
|
+
border: none;
|
|
307
|
+
|
|
308
|
+
&:hover {
|
|
309
|
+
background: $secondary-lighten-1;
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
&:focus {
|
|
313
|
+
background: $secondary-lighten-1;
|
|
314
|
+
text-style: bold;
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
&> .rich-arguments-prefix {
|
|
319
|
+
height: 1;
|
|
320
|
+
width: 5;
|
|
321
|
+
padding-left: 2;
|
|
322
|
+
padding-right: 1;
|
|
323
|
+
color: $text-muted;
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
&> Vertical.rich-arguments-content {
|
|
327
|
+
height: auto;
|
|
328
|
+
width: 1fr;
|
|
329
|
+
|
|
330
|
+
.rich-arguments-label {
|
|
331
|
+
color: $text-muted;
|
|
332
|
+
# margin-bottom: 1;
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
.rich-arguments-text {
|
|
336
|
+
color: $text-muted;
|
|
337
|
+
text-overflow: ellipsis;
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
Horizontal.tool-output {
|
|
343
|
+
height: auto;
|
|
344
|
+
|
|
345
|
+
&> .tool-output-prefix {
|
|
346
|
+
height: 1;
|
|
347
|
+
width: 5;
|
|
348
|
+
padding-left: 2;
|
|
349
|
+
padding-right: 1;
|
|
350
|
+
color: $text-muted;
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
&> Vertical.tool-output-content {
|
|
354
|
+
height: auto;
|
|
355
|
+
width: 1fr;
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
|
|
291
360
|
WebSearchToolMessage {
|
|
292
361
|
Horizontal.tool-output {
|
|
293
362
|
height: auto;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: vibecore
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.1
|
|
4
4
|
Summary: Build your own AI-powered automation tools in the terminal with this extensible agent framework
|
|
5
5
|
Project-URL: Homepage, https://github.com/serialx/vibecore
|
|
6
6
|
Project-URL: Repository, https://github.com/serialx/vibecore
|
|
@@ -5,7 +5,7 @@ vibecore/flow.py,sha256=ZaKzMsz4YBvgelVzJOIHnTJzMWTmvkfvudwW_hllq6U,3384
|
|
|
5
5
|
vibecore/main.py,sha256=MIn7Mpg_xO_20c6Mju8PgY-MmVCTER9cepHd5YFbtYs,20175
|
|
6
6
|
vibecore/main.tcss,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
7
7
|
vibecore/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
8
|
-
vibecore/settings.py,sha256=
|
|
8
|
+
vibecore/settings.py,sha256=PwivzoJ-xyJu-Mmnv4E6G_E8up4GNvvz9_j4ttj-GUw,7063
|
|
9
9
|
vibecore/agents/default.py,sha256=wxeP3Hsq9MVBChyMF_sNkVHCtFFXgy5ghDIxy9eH_fQ,2287
|
|
10
10
|
vibecore/agents/prompts.py,sha256=0oO9QzcytIbzgZcKJQjWD9fSRNQqBqqK5ku0fcYZZrA,324
|
|
11
11
|
vibecore/agents/task.py,sha256=cdhZDzKDA8eHHNYPhogFIKBms3oVAMvYeiBB2GqYNuE,1898
|
|
@@ -75,11 +75,11 @@ vibecore/widgets/info.py,sha256=hXtsRUOE13oHbIm9FNe1GCUX_FCht28pgT9SQWeJ69I,1567
|
|
|
75
75
|
vibecore/widgets/info.tcss,sha256=v30IqNt1two-ezIcm18ZEInKRKcRkAW-h-UH2r8QzSo,201
|
|
76
76
|
vibecore/widgets/messages.py,sha256=az4fJtdk3ItSoFZBG_adRDUHdTLttIV8F23E8LOb-mg,8156
|
|
77
77
|
vibecore/widgets/messages.tcss,sha256=WtBbjf5LgFkUhzhVlxJB7NMbagWladJawDizvDm7hBE,1271
|
|
78
|
-
vibecore/widgets/tool_message_factory.py,sha256=
|
|
79
|
-
vibecore/widgets/tool_messages.py,sha256=
|
|
80
|
-
vibecore/widgets/tool_messages.tcss,sha256=
|
|
81
|
-
vibecore-0.3.
|
|
82
|
-
vibecore-0.3.
|
|
83
|
-
vibecore-0.3.
|
|
84
|
-
vibecore-0.3.
|
|
85
|
-
vibecore-0.3.
|
|
78
|
+
vibecore/widgets/tool_message_factory.py,sha256=yrZorT4HKo5b6rWUc0dgQle7q7cvLyq8JllE772RZS0,5730
|
|
79
|
+
vibecore/widgets/tool_messages.py,sha256=hJOolN3iLTAjqfotfH1elXqsdDo1r_UHjsyRVH0GAeo,29415
|
|
80
|
+
vibecore/widgets/tool_messages.tcss,sha256=gdChmHClURqn_sD9GkcOGQcQVYvUUl75mLUYp85sKz8,8442
|
|
81
|
+
vibecore-0.3.1.dist-info/METADATA,sha256=wCQB7NKn35T0W3onSrpV1FNX13zHzO-VEn0WqU6ZIlw,18093
|
|
82
|
+
vibecore-0.3.1.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
83
|
+
vibecore-0.3.1.dist-info/entry_points.txt,sha256=i9mOKvpz07ciV_YYisxNCYZ53_Crjkn9mciiQ3aA6QM,51
|
|
84
|
+
vibecore-0.3.1.dist-info/licenses/LICENSE,sha256=KXxxifvrcreHrZ4aOYgP-vA8DRHHueW389KKOeEbtjc,1069
|
|
85
|
+
vibecore-0.3.1.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|