flock-core 0.2.13__py3-none-any.whl → 0.2.15__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 flock-core might be problematic. Click here for more details.
- flock/__init__.py +56 -0
- flock/cli/constants.py +23 -0
- flock/cli/create_agent.py +1 -0
- flock/cli/create_flock.py +1 -0
- flock/cli/load_agent.py +1 -0
- flock/cli/load_examples.py +1 -0
- flock/cli/load_flock.py +38 -0
- flock/cli/settings.py +1 -0
- flock/config.py +0 -1
- flock/core/execution/local_executor.py +7 -3
- flock/core/execution/temporal_executor.py +1 -8
- flock/core/flock.py +197 -34
- flock/core/flock_agent.py +89 -9
- flock/core/logging/formatters/enum_builder.py +38 -0
- flock/core/logging/formatters/theme_builder.py +2 -6
- flock/core/logging/formatters/themed_formatter.py +127 -19
- flock/core/logging/formatters/themes.py +340 -0
- flock/core/logging/telemetry.py +1 -1
- flock/core/mixin/dspy_integration.py +6 -1
- flock/workflow/activities.py +2 -19
- {flock_core-0.2.13.dist-info → flock_core-0.2.15.dist-info}/METADATA +2 -1
- {flock_core-0.2.13.dist-info → flock_core-0.2.15.dist-info}/RECORD +25 -20
- flock/core/logging/formatters/base_formatter.py +0 -36
- flock/core/logging/formatters/formatter_factory.py +0 -38
- flock/core/logging/formatters/pprint_formatter.py +0 -25
- flock/core/logging/formatters/rich_formatters.py +0 -132
- {flock_core-0.2.13.dist-info → flock_core-0.2.15.dist-info}/WHEEL +0 -0
- {flock_core-0.2.13.dist-info → flock_core-0.2.15.dist-info}/entry_points.txt +0 -0
- {flock_core-0.2.13.dist-info → flock_core-0.2.15.dist-info}/licenses/LICENSE +0 -0
flock/core/flock_agent.py
CHANGED
|
@@ -1,14 +1,21 @@
|
|
|
1
1
|
"""FlockAgent is the core, declarative base class for all agents in the Flock framework."""
|
|
2
2
|
|
|
3
|
+
import asyncio
|
|
4
|
+
import json
|
|
5
|
+
import os
|
|
3
6
|
from abc import ABC
|
|
4
7
|
from collections.abc import Awaitable, Callable
|
|
5
8
|
from dataclasses import dataclass, field
|
|
6
|
-
from typing import Any,
|
|
9
|
+
from typing import Any, TypeVar, Union
|
|
7
10
|
|
|
8
11
|
import cloudpickle
|
|
9
12
|
from pydantic import BaseModel, Field
|
|
10
13
|
|
|
11
14
|
from flock.core.context.context import FlockContext
|
|
15
|
+
from flock.core.logging.formatters.themed_formatter import (
|
|
16
|
+
ThemedAgentResultFormatter,
|
|
17
|
+
)
|
|
18
|
+
from flock.core.logging.formatters.themes import OutputTheme
|
|
12
19
|
from flock.core.logging.logging import get_logger
|
|
13
20
|
from flock.core.mixin.dspy_integration import AgentType, DSPyIntegrationMixin
|
|
14
21
|
from flock.core.mixin.prompt_parser import PromptParserMixin
|
|
@@ -21,6 +28,9 @@ from opentelemetry import trace
|
|
|
21
28
|
tracer = trace.get_tracer(__name__)
|
|
22
29
|
|
|
23
30
|
|
|
31
|
+
T = TypeVar("T", bound="FlockAgent")
|
|
32
|
+
|
|
33
|
+
|
|
24
34
|
@dataclass
|
|
25
35
|
class FlockAgentConfig:
|
|
26
36
|
"""Configuration options for a FlockAgent."""
|
|
@@ -34,13 +44,31 @@ class FlockAgentConfig:
|
|
|
34
44
|
disable_output: bool = field(
|
|
35
45
|
default=False, metadata={"description": "Disables the agent's output."}
|
|
36
46
|
)
|
|
37
|
-
|
|
38
|
-
default=
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
}
|
|
47
|
+
temperature: float = field(
|
|
48
|
+
default=0.0, metadata={"description": "Temperature for the LLM"}
|
|
49
|
+
)
|
|
50
|
+
max_tokens: int = field(
|
|
51
|
+
default=2000, metadata={"description": "Max tokens for the LLM"}
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
@dataclass
|
|
56
|
+
class FlockAgentOutputConfig:
|
|
57
|
+
"""Configuration options for a FlockAgent."""
|
|
58
|
+
|
|
59
|
+
render_table: bool = field(
|
|
60
|
+
default=False, metadata={"description": "Renders a table."}
|
|
61
|
+
)
|
|
62
|
+
theme: OutputTheme = field( # type: ignore
|
|
63
|
+
default=OutputTheme.afterglow,
|
|
64
|
+
metadata={"description": "Disables the agent's output."},
|
|
65
|
+
)
|
|
66
|
+
max_length: int = field(
|
|
67
|
+
default=1000, metadata={"description": "Disables the agent's output."}
|
|
68
|
+
)
|
|
69
|
+
wait_for_input: bool = field(
|
|
70
|
+
default=False, metadata={"description": "Wait for input."}
|
|
42
71
|
)
|
|
43
|
-
data_type: Literal["json", "cloudpickle", "msgpack"] = "cloudpickle"
|
|
44
72
|
|
|
45
73
|
|
|
46
74
|
@dataclass
|
|
@@ -181,6 +209,11 @@ class FlockAgent(BaseModel, ABC, PromptParserMixin, DSPyIntegrationMixin):
|
|
|
181
209
|
description="Configuration options for the agent, such as serialization settings.",
|
|
182
210
|
)
|
|
183
211
|
|
|
212
|
+
output_config: FlockAgentOutputConfig = Field(
|
|
213
|
+
default_factory=FlockAgentOutputConfig,
|
|
214
|
+
description="Configuration options for the agent's output.",
|
|
215
|
+
)
|
|
216
|
+
|
|
184
217
|
# Lifecycle callback fields: if provided, these callbacks are used instead of overriding the methods.
|
|
185
218
|
initialize_callback: Callable[[dict[str, Any]], Awaitable[None]] | None = (
|
|
186
219
|
Field(
|
|
@@ -188,6 +221,12 @@ class FlockAgent(BaseModel, ABC, PromptParserMixin, DSPyIntegrationMixin):
|
|
|
188
221
|
description="Optional callback function for initialization. If provided, this async function is called with the inputs.",
|
|
189
222
|
)
|
|
190
223
|
)
|
|
224
|
+
evaluate_callback: (
|
|
225
|
+
Callable[[dict[str, Any]], Awaitable[dict[str, Any]]] | None
|
|
226
|
+
) = Field(
|
|
227
|
+
default=None,
|
|
228
|
+
description="Optional callback function for evaluate. If provided, this async function is called with the inputs instead of the internal evaluate",
|
|
229
|
+
)
|
|
191
230
|
terminate_callback: (
|
|
192
231
|
Callable[[dict[str, Any], dict[str, Any]], Awaitable[None]] | None
|
|
193
232
|
) = Field(
|
|
@@ -296,6 +335,8 @@ class FlockAgent(BaseModel, ABC, PromptParserMixin, DSPyIntegrationMixin):
|
|
|
296
335
|
with tracer.start_as_current_span("agent.evaluate") as span:
|
|
297
336
|
span.set_attribute("agent.name", self.name)
|
|
298
337
|
span.set_attribute("inputs", str(inputs))
|
|
338
|
+
if self.evaluate_callback is not None:
|
|
339
|
+
return await self.evaluate_callback(self, inputs)
|
|
299
340
|
try:
|
|
300
341
|
# Create and configure the signature and language model.
|
|
301
342
|
self.__dspy_signature = self.create_dspy_signature_class(
|
|
@@ -323,7 +364,33 @@ class FlockAgent(BaseModel, ABC, PromptParserMixin, DSPyIntegrationMixin):
|
|
|
323
364
|
span.record_exception(eval_error)
|
|
324
365
|
raise
|
|
325
366
|
|
|
326
|
-
|
|
367
|
+
def save_to_file(self, file_path: str | None = None) -> None:
|
|
368
|
+
"""Save the serialized agent to a file."""
|
|
369
|
+
if file_path is None:
|
|
370
|
+
file_path = f"{self.name}.json"
|
|
371
|
+
dict_data = self.to_dict()
|
|
372
|
+
|
|
373
|
+
# create all needed directories
|
|
374
|
+
path = os.path.dirname(file_path)
|
|
375
|
+
if path:
|
|
376
|
+
os.makedirs(os.path.dirname(file_path), exist_ok=True)
|
|
377
|
+
|
|
378
|
+
with open(file_path, "w") as file:
|
|
379
|
+
file.write(json.dumps(dict_data))
|
|
380
|
+
|
|
381
|
+
@classmethod
|
|
382
|
+
def load_from_file(cls: type[T], file_path: str) -> T:
|
|
383
|
+
"""Load a serialized agent from a file."""
|
|
384
|
+
with open(file_path) as file:
|
|
385
|
+
data = json.load(file)
|
|
386
|
+
# Fallback: use the current class.
|
|
387
|
+
return cls.from_dict(data)
|
|
388
|
+
|
|
389
|
+
def run(self, inputs: dict[str, Any]) -> dict[str, Any]:
|
|
390
|
+
"""Run the agent with the given inputs and return its generated output."""
|
|
391
|
+
return asyncio.run(self.run_async(inputs))
|
|
392
|
+
|
|
393
|
+
async def run_async(self, inputs: dict[str, Any]) -> dict[str, Any]:
|
|
327
394
|
"""Run the agent with the given inputs and return its generated output.
|
|
328
395
|
|
|
329
396
|
This method represents the primary execution flow for a FlockAgent and performs the following
|
|
@@ -375,6 +442,7 @@ class FlockAgent(BaseModel, ABC, PromptParserMixin, DSPyIntegrationMixin):
|
|
|
375
442
|
try:
|
|
376
443
|
await self.initialize(inputs)
|
|
377
444
|
result = await self.evaluate(inputs)
|
|
445
|
+
self.display_output(result)
|
|
378
446
|
await self.terminate(inputs, result)
|
|
379
447
|
span.set_attribute("result", str(result))
|
|
380
448
|
logger.info("Agent run completed", agent=self.name)
|
|
@@ -387,6 +455,18 @@ class FlockAgent(BaseModel, ABC, PromptParserMixin, DSPyIntegrationMixin):
|
|
|
387
455
|
span.record_exception(run_error)
|
|
388
456
|
raise
|
|
389
457
|
|
|
458
|
+
def display_info(self) -> None:
|
|
459
|
+
pass
|
|
460
|
+
|
|
461
|
+
def display_output(self, result: dict[str, Any]) -> None:
|
|
462
|
+
"""Display the agent's output using the configured output formatter."""
|
|
463
|
+
ThemedAgentResultFormatter(
|
|
464
|
+
self.output_config.theme,
|
|
465
|
+
self.output_config.max_length,
|
|
466
|
+
self.output_config.render_table,
|
|
467
|
+
self.output_config.wait_for_input,
|
|
468
|
+
).display_result(result, self.name)
|
|
469
|
+
|
|
390
470
|
async def run_temporal(self, inputs: dict[str, Any]) -> dict[str, Any]:
|
|
391
471
|
"""Execute this agent via a Temporal workflow for enhanced fault tolerance and asynchronous processing.
|
|
392
472
|
|
|
@@ -526,7 +606,7 @@ class FlockAgent(BaseModel, ABC, PromptParserMixin, DSPyIntegrationMixin):
|
|
|
526
606
|
return convert_callable(data)
|
|
527
607
|
|
|
528
608
|
@classmethod
|
|
529
|
-
def from_dict(cls, data: dict[str, Any]) ->
|
|
609
|
+
def from_dict(cls: type[T], data: dict[str, Any]) -> T:
|
|
530
610
|
"""Deserialize a FlockAgent instance from a dictionary.
|
|
531
611
|
|
|
532
612
|
This class method reconstructs a FlockAgent from its serialized dictionary representation, as produced
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
"""Enum Builder."""
|
|
2
|
+
|
|
3
|
+
import os
|
|
4
|
+
import pathlib
|
|
5
|
+
import re
|
|
6
|
+
|
|
7
|
+
theme_folder = pathlib.Path(__file__).parent.parent.parent.parent / "themes"
|
|
8
|
+
|
|
9
|
+
if not theme_folder.exists():
|
|
10
|
+
raise FileNotFoundError(f"Theme folder not found: {theme_folder}")
|
|
11
|
+
|
|
12
|
+
theme_files = [
|
|
13
|
+
pathlib.Path(f.path).stem for f in os.scandir(theme_folder) if f.is_file()
|
|
14
|
+
]
|
|
15
|
+
|
|
16
|
+
theme_enum_entries = {}
|
|
17
|
+
for theme in theme_files:
|
|
18
|
+
safe_name = (
|
|
19
|
+
theme.replace("-", "_")
|
|
20
|
+
.replace(" ", "_")
|
|
21
|
+
.replace("(", "_")
|
|
22
|
+
.replace(")", "_")
|
|
23
|
+
.replace("+", "_")
|
|
24
|
+
.replace(".", "_")
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
if re.match(r"^\d", safe_name):
|
|
28
|
+
safe_name = f"_{safe_name}"
|
|
29
|
+
|
|
30
|
+
theme_enum_entries[safe_name] = theme
|
|
31
|
+
|
|
32
|
+
with open("theme_enum.py", "w") as f:
|
|
33
|
+
f.write("from enum import Enum\n\n")
|
|
34
|
+
f.write("class OutputOptionsTheme(Enum):\n")
|
|
35
|
+
for safe_name, original_name in theme_enum_entries.items():
|
|
36
|
+
f.write(f' {safe_name} = "{original_name}"\n')
|
|
37
|
+
|
|
38
|
+
print("Generated theme_enum.py ✅")
|
|
@@ -371,7 +371,7 @@ def save_theme(theme: dict, filename: pathlib.Path) -> None:
|
|
|
371
371
|
# --- Main Interactive Loop --- #
|
|
372
372
|
|
|
373
373
|
|
|
374
|
-
def
|
|
374
|
+
def theme_builder():
|
|
375
375
|
console = Console(force_terminal=True, color_system="truecolor")
|
|
376
376
|
themes_dir = pathlib.Path(__file__).parent.parent.parent.parent / "themes"
|
|
377
377
|
theme_files = load_theme_files(themes_dir)
|
|
@@ -458,7 +458,7 @@ def main():
|
|
|
458
458
|
)
|
|
459
459
|
if sel2.lower() == "r":
|
|
460
460
|
console.print("Regenerating samples...")
|
|
461
|
-
|
|
461
|
+
theme_builder() # restart the builder
|
|
462
462
|
return
|
|
463
463
|
try:
|
|
464
464
|
sel2 = int(sel2)
|
|
@@ -474,7 +474,3 @@ def main():
|
|
|
474
474
|
save_path = themes_dir / filename
|
|
475
475
|
save_theme(chosen_sample_theme, save_path)
|
|
476
476
|
console.print(f"\n[green]Theme saved as {save_path}.[/green]")
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
if __name__ == "__main__":
|
|
480
|
-
main()
|
|
@@ -5,16 +5,19 @@ import random
|
|
|
5
5
|
import re
|
|
6
6
|
from typing import Any
|
|
7
7
|
|
|
8
|
-
from devtools import pprint
|
|
9
8
|
from temporalio import workflow
|
|
10
9
|
|
|
11
|
-
from flock.core.logging.formatters.
|
|
10
|
+
from flock.core.logging.formatters.themes import OutputTheme
|
|
12
11
|
|
|
13
12
|
with workflow.unsafe.imports_passed_through():
|
|
13
|
+
from pygments.style import Style
|
|
14
|
+
from pygments.token import Token
|
|
14
15
|
from rich import box
|
|
15
16
|
from rich.console import Console, Group
|
|
16
17
|
from rich.panel import Panel
|
|
18
|
+
from rich.syntax import PygmentsSyntaxTheme, Syntax
|
|
17
19
|
from rich.table import Table
|
|
20
|
+
from rich.theme import Theme
|
|
18
21
|
|
|
19
22
|
import toml # install with: pip install toml
|
|
20
23
|
|
|
@@ -335,18 +338,101 @@ def create_rich_renderable(
|
|
|
335
338
|
return s
|
|
336
339
|
|
|
337
340
|
|
|
338
|
-
|
|
341
|
+
def load_syntax_theme_from_file(filepath: str) -> dict:
|
|
342
|
+
"""Load a syntax highlighting theme from a TOML file and map it to Rich styles."""
|
|
343
|
+
with open(filepath) as f:
|
|
344
|
+
theme = toml.load(f)
|
|
345
|
+
|
|
346
|
+
if "colors" not in theme:
|
|
347
|
+
raise ValueError(
|
|
348
|
+
f"Theme file {filepath} does not contain a 'colors' section."
|
|
349
|
+
)
|
|
350
|
+
|
|
351
|
+
# Map theme colors to syntax categories
|
|
352
|
+
syntax_theme = {
|
|
353
|
+
"background": theme["colors"]["primary"].get("background", "#161719"),
|
|
354
|
+
"text": theme["colors"]["primary"].get("foreground", "#c5c8c6"),
|
|
355
|
+
"comment": theme["colors"]["normal"].get("black", "#666666"),
|
|
356
|
+
"keyword": theme["colors"]["bright"].get("magenta", "#ff79c6"),
|
|
357
|
+
"builtin": theme["colors"]["bright"].get("cyan", "#8be9fd"),
|
|
358
|
+
"string": theme["colors"]["bright"].get("green", "#50fa7b"),
|
|
359
|
+
"name": theme["colors"]["bright"].get("blue", "#6272a4"),
|
|
360
|
+
"number": theme["colors"]["bright"].get("yellow", "#f1fa8c"),
|
|
361
|
+
"operator": theme["colors"]["bright"].get("red", "#ff5555"),
|
|
362
|
+
"punctuation": theme["colors"]["normal"].get("white", "#bbbbbb"),
|
|
363
|
+
"error": theme["colors"]["bright"].get("red", "#ff5555"),
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
return syntax_theme
|
|
367
|
+
|
|
368
|
+
|
|
369
|
+
def create_rich_syntax_theme(syntax_theme: dict) -> Theme:
|
|
370
|
+
"""Convert a syntax theme dict to a Rich-compatible Theme."""
|
|
371
|
+
return Theme(
|
|
372
|
+
{
|
|
373
|
+
"background": f"on {syntax_theme['background']}",
|
|
374
|
+
"text": syntax_theme["text"],
|
|
375
|
+
"keyword": f"bold {syntax_theme['keyword']}",
|
|
376
|
+
"builtin": f"bold {syntax_theme['builtin']}",
|
|
377
|
+
"string": syntax_theme["string"],
|
|
378
|
+
"name": syntax_theme["name"],
|
|
379
|
+
"number": syntax_theme["number"],
|
|
380
|
+
"operator": syntax_theme["operator"],
|
|
381
|
+
"punctuation": syntax_theme["punctuation"],
|
|
382
|
+
"error": f"bold {syntax_theme['error']}",
|
|
383
|
+
}
|
|
384
|
+
)
|
|
385
|
+
|
|
386
|
+
|
|
387
|
+
def create_pygments_syntax_theme(syntax_theme: dict) -> PygmentsSyntaxTheme:
|
|
388
|
+
"""Convert a syntax theme dict to a Pygments-compatible Rich syntax theme."""
|
|
389
|
+
|
|
390
|
+
class CustomSyntaxStyle(Style):
|
|
391
|
+
"""Dynamically generated Pygments style based on the loaded theme."""
|
|
392
|
+
|
|
393
|
+
background_color = syntax_theme["background"]
|
|
394
|
+
styles = {
|
|
395
|
+
Token.Text: syntax_theme["text"],
|
|
396
|
+
Token.Comment: f"italic {syntax_theme['comment']}",
|
|
397
|
+
Token.Keyword: f"bold {syntax_theme['keyword']}",
|
|
398
|
+
Token.Name.Builtin: f"bold {syntax_theme['builtin']}",
|
|
399
|
+
Token.String: syntax_theme["string"],
|
|
400
|
+
Token.Name: syntax_theme["name"],
|
|
401
|
+
Token.Number: syntax_theme["number"],
|
|
402
|
+
Token.Operator: syntax_theme["operator"],
|
|
403
|
+
Token.Punctuation: syntax_theme["punctuation"],
|
|
404
|
+
Token.Error: f"bold {syntax_theme['error']}",
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
return PygmentsSyntaxTheme(CustomSyntaxStyle)
|
|
408
|
+
|
|
409
|
+
|
|
410
|
+
class ThemedAgentResultFormatter:
|
|
339
411
|
"""Formats agent results in a Rich table with nested subtables and theme support."""
|
|
340
412
|
|
|
341
|
-
def __init__(
|
|
413
|
+
def __init__(
|
|
414
|
+
self,
|
|
415
|
+
theme: OutputTheme = OutputTheme.afterglow,
|
|
416
|
+
max_length: int = -1,
|
|
417
|
+
render_table: bool = True,
|
|
418
|
+
wait_for_input: bool = False,
|
|
419
|
+
):
|
|
342
420
|
"""Initialize the formatter with a theme and optional max length."""
|
|
343
421
|
self.theme = theme
|
|
344
422
|
self.styles = None
|
|
345
423
|
self.max_length = max_length
|
|
424
|
+
self.render_table = render_table
|
|
425
|
+
self.wait_for_input = wait_for_input
|
|
346
426
|
|
|
347
427
|
def format_result(
|
|
348
|
-
self,
|
|
428
|
+
self,
|
|
429
|
+
result: dict[str, Any],
|
|
430
|
+
agent_name: str,
|
|
431
|
+
theme,
|
|
432
|
+
styles,
|
|
349
433
|
) -> Panel:
|
|
434
|
+
from devtools import pformat
|
|
435
|
+
|
|
350
436
|
"""Format an agent's result as a Rich Panel containing a table."""
|
|
351
437
|
box_style = (
|
|
352
438
|
getattr(box, styles["table_box"])
|
|
@@ -394,14 +480,32 @@ class ThemedAgentResultFormatter(BaseFormatter):
|
|
|
394
480
|
)
|
|
395
481
|
table.add_row(key, rich_renderable)
|
|
396
482
|
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
483
|
+
s = pformat(result, highlight=False)
|
|
484
|
+
|
|
485
|
+
if self.render_table:
|
|
486
|
+
return Panel(
|
|
487
|
+
table,
|
|
488
|
+
title="🐤🐧🐓🦆",
|
|
489
|
+
title_align=styles["panel_title_align"],
|
|
490
|
+
border_style=styles["panel_border_style"],
|
|
491
|
+
padding=styles["panel_padding"],
|
|
492
|
+
style=styles["panel_style"],
|
|
493
|
+
)
|
|
494
|
+
else:
|
|
495
|
+
syntax = Syntax(
|
|
496
|
+
s, # The formatted string
|
|
497
|
+
"python", # Highlight as Python (change this for other formats)
|
|
498
|
+
theme=self.syntax_style, # Choose a Rich theme (matches your color setup)
|
|
499
|
+
line_numbers=False,
|
|
500
|
+
)
|
|
501
|
+
return Panel(
|
|
502
|
+
syntax,
|
|
503
|
+
title=agent_name,
|
|
504
|
+
title_align=styles["panel_title_align"],
|
|
505
|
+
border_style=styles["panel_border_style"],
|
|
506
|
+
padding=styles["panel_padding"],
|
|
507
|
+
style=styles["panel_style"],
|
|
508
|
+
)
|
|
405
509
|
|
|
406
510
|
def display_result(self, result: dict[str, Any], agent_name: str) -> None:
|
|
407
511
|
"""Print an agent's result using Rich formatting."""
|
|
@@ -410,7 +514,11 @@ class ThemedAgentResultFormatter(BaseFormatter):
|
|
|
410
514
|
pathlib.Path(__file__).parent.parent.parent.parent / "themes"
|
|
411
515
|
)
|
|
412
516
|
all_themes = list(themes_dir.glob("*.toml"))
|
|
413
|
-
theme =
|
|
517
|
+
theme = (
|
|
518
|
+
theme.value + ".toml"
|
|
519
|
+
if not theme.value.endswith(".toml")
|
|
520
|
+
else theme.value
|
|
521
|
+
)
|
|
414
522
|
theme = (
|
|
415
523
|
pathlib.Path(__file__).parent.parent.parent.parent
|
|
416
524
|
/ "themes"
|
|
@@ -426,6 +534,9 @@ class ThemedAgentResultFormatter(BaseFormatter):
|
|
|
426
534
|
|
|
427
535
|
styles = get_default_styles(theme_dict)
|
|
428
536
|
self.styles = styles
|
|
537
|
+
self.syntax_style = create_pygments_syntax_theme(
|
|
538
|
+
load_syntax_theme_from_file(theme)
|
|
539
|
+
)
|
|
429
540
|
|
|
430
541
|
console = Console()
|
|
431
542
|
panel = self.format_result(
|
|
@@ -435,8 +546,5 @@ class ThemedAgentResultFormatter(BaseFormatter):
|
|
|
435
546
|
styles=styles,
|
|
436
547
|
)
|
|
437
548
|
console.print(panel)
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
def display_data(data: dict[str, Any]) -> None:
|
|
441
|
-
"""Print agent data using Rich formatting."""
|
|
442
|
-
pprint(data)
|
|
549
|
+
if self.wait_for_input:
|
|
550
|
+
console.input(prompt="Press Enter to continue...")
|