flock-core 0.3.21__py3-none-any.whl → 0.3.23__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/modules/output/output_module.py +214 -0
- {flock_core-0.3.21.dist-info → flock_core-0.3.23.dist-info}/METADATA +2 -1
- {flock_core-0.3.21.dist-info → flock_core-0.3.23.dist-info}/RECORD +6 -5
- {flock_core-0.3.21.dist-info → flock_core-0.3.23.dist-info}/WHEEL +0 -0
- {flock_core-0.3.21.dist-info → flock_core-0.3.23.dist-info}/entry_points.txt +0 -0
- {flock_core-0.3.21.dist-info → flock_core-0.3.23.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
"""Output formatting and display functionality for agents."""
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
import os
|
|
5
|
+
from datetime import datetime
|
|
6
|
+
from typing import Any
|
|
7
|
+
|
|
8
|
+
from pydantic import Field
|
|
9
|
+
|
|
10
|
+
from flock.core import FlockAgent
|
|
11
|
+
from flock.core.context.context import FlockContext
|
|
12
|
+
from flock.core.flock_module import FlockModule, FlockModuleConfig
|
|
13
|
+
from flock.core.logging.formatters.themed_formatter import (
|
|
14
|
+
ThemedAgentResultFormatter,
|
|
15
|
+
)
|
|
16
|
+
from flock.core.logging.formatters.themes import OutputTheme
|
|
17
|
+
from flock.core.logging.logging import get_logger
|
|
18
|
+
from flock.core.serialization.json_encoder import FlockJSONEncoder
|
|
19
|
+
|
|
20
|
+
logger = get_logger("module.output")
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class OutputModuleConfig(FlockModuleConfig):
|
|
24
|
+
"""Configuration for output formatting and display."""
|
|
25
|
+
|
|
26
|
+
theme: OutputTheme = Field(
|
|
27
|
+
default=OutputTheme.afterglow, description="Theme for output formatting"
|
|
28
|
+
)
|
|
29
|
+
render_table: bool = Field(
|
|
30
|
+
default=False, description="Whether to render output as a table"
|
|
31
|
+
)
|
|
32
|
+
max_length: int = Field(
|
|
33
|
+
default=1000, description="Maximum length for displayed output"
|
|
34
|
+
)
|
|
35
|
+
wait_for_input: bool = Field(
|
|
36
|
+
default=False,
|
|
37
|
+
description="Whether to wait for user input after display",
|
|
38
|
+
)
|
|
39
|
+
write_to_file: bool = Field(
|
|
40
|
+
default=False, description="Whether to save output to file"
|
|
41
|
+
)
|
|
42
|
+
output_dir: str = Field(
|
|
43
|
+
default="output/", description="Directory for saving output files"
|
|
44
|
+
)
|
|
45
|
+
truncate_long_values: bool = Field(
|
|
46
|
+
default=True, description="Whether to truncate long values in display"
|
|
47
|
+
)
|
|
48
|
+
show_metadata: bool = Field(
|
|
49
|
+
default=True, description="Whether to show metadata like timestamps"
|
|
50
|
+
)
|
|
51
|
+
format_code_blocks: bool = Field(
|
|
52
|
+
default=True,
|
|
53
|
+
description="Whether to apply syntax highlighting to code blocks",
|
|
54
|
+
)
|
|
55
|
+
custom_formatters: dict[str, str] = Field(
|
|
56
|
+
default_factory=dict,
|
|
57
|
+
description="Custom formatters for specific output types",
|
|
58
|
+
)
|
|
59
|
+
no_output: bool = Field(
|
|
60
|
+
default=False,
|
|
61
|
+
description="Whether to suppress output",
|
|
62
|
+
)
|
|
63
|
+
print_context: bool = Field(
|
|
64
|
+
default=False,
|
|
65
|
+
description="Whether to print the context",
|
|
66
|
+
)
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
class OutputModule(FlockModule):
|
|
70
|
+
"""Module that handles output formatting and display."""
|
|
71
|
+
|
|
72
|
+
name: str = "output"
|
|
73
|
+
config: OutputModuleConfig = Field(
|
|
74
|
+
default_factory=OutputModuleConfig, description="Output configuration"
|
|
75
|
+
)
|
|
76
|
+
|
|
77
|
+
def __init__(self, name: str, config: OutputModuleConfig):
|
|
78
|
+
super().__init__(name=name, config=config)
|
|
79
|
+
if self.config.write_to_file:
|
|
80
|
+
os.makedirs(self.config.output_dir, exist_ok=True)
|
|
81
|
+
self._formatter = ThemedAgentResultFormatter(
|
|
82
|
+
theme=self.config.theme,
|
|
83
|
+
max_length=self.config.max_length,
|
|
84
|
+
render_table=self.config.render_table,
|
|
85
|
+
wait_for_input=self.config.wait_for_input,
|
|
86
|
+
)
|
|
87
|
+
|
|
88
|
+
def _format_value(self, value: Any, key: str) -> str:
|
|
89
|
+
"""Format a single value based on its type and configuration."""
|
|
90
|
+
# Check for custom formatter
|
|
91
|
+
if key in self.config.custom_formatters:
|
|
92
|
+
formatter_name = self.config.custom_formatters[key]
|
|
93
|
+
if hasattr(self, f"_format_{formatter_name}"):
|
|
94
|
+
return getattr(self, f"_format_{formatter_name}")(value)
|
|
95
|
+
|
|
96
|
+
# Default formatting based on type
|
|
97
|
+
if isinstance(value, dict):
|
|
98
|
+
return self._format_dict(value)
|
|
99
|
+
elif isinstance(value, list):
|
|
100
|
+
return self._format_list(value)
|
|
101
|
+
elif isinstance(value, str) and self.config.format_code_blocks:
|
|
102
|
+
return self._format_potential_code(value)
|
|
103
|
+
else:
|
|
104
|
+
return str(value)
|
|
105
|
+
|
|
106
|
+
def _format_dict(self, d: dict[str, Any], indent: int = 0) -> str:
|
|
107
|
+
"""Format a dictionary with proper indentation."""
|
|
108
|
+
lines = []
|
|
109
|
+
for k, v in d.items():
|
|
110
|
+
formatted_value = self._format_value(v, k)
|
|
111
|
+
if (
|
|
112
|
+
self.config.truncate_long_values
|
|
113
|
+
and len(formatted_value) > self.config.max_length
|
|
114
|
+
):
|
|
115
|
+
formatted_value = (
|
|
116
|
+
formatted_value[: self.config.max_length] + "..."
|
|
117
|
+
)
|
|
118
|
+
lines.append(f"{' ' * indent}{k}: {formatted_value}")
|
|
119
|
+
return "\n".join(lines)
|
|
120
|
+
|
|
121
|
+
def _format_list(self, lst: list[Any]) -> str:
|
|
122
|
+
"""Format a list with proper indentation."""
|
|
123
|
+
return "\n".join(f"- {self._format_value(item, '')}" for item in lst)
|
|
124
|
+
|
|
125
|
+
def _format_potential_code(self, text: str) -> str:
|
|
126
|
+
"""Format text that might contain code blocks."""
|
|
127
|
+
import re
|
|
128
|
+
|
|
129
|
+
def replace_code_block(match):
|
|
130
|
+
code = match.group(2)
|
|
131
|
+
lang = match.group(1) if match.group(1) else ""
|
|
132
|
+
# Here you could add syntax highlighting
|
|
133
|
+
return f"```{lang}\n{code}\n```"
|
|
134
|
+
|
|
135
|
+
# Replace code blocks with formatted versions
|
|
136
|
+
text = re.sub(
|
|
137
|
+
r"```(\w+)?\n(.*?)\n```", replace_code_block, text, flags=re.DOTALL
|
|
138
|
+
)
|
|
139
|
+
return text
|
|
140
|
+
|
|
141
|
+
def _save_output(self, agent_name: str, result: dict[str, Any]) -> None:
|
|
142
|
+
"""Save output to file if configured."""
|
|
143
|
+
if not self.config.write_to_file:
|
|
144
|
+
return
|
|
145
|
+
|
|
146
|
+
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
|
|
147
|
+
filename = f"{agent_name}_output_{timestamp}.json"
|
|
148
|
+
filepath = os.path.join(self.config.output_dir, filename)
|
|
149
|
+
|
|
150
|
+
output_data = {
|
|
151
|
+
"agent": agent_name,
|
|
152
|
+
"timestamp": timestamp,
|
|
153
|
+
"output": result,
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
if self.config.show_metadata:
|
|
157
|
+
output_data["metadata"] = {
|
|
158
|
+
"formatted_at": datetime.now().isoformat(),
|
|
159
|
+
"theme": self.config.theme.value,
|
|
160
|
+
"max_length": self.config.max_length,
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
try:
|
|
164
|
+
with open(filepath, "w") as f:
|
|
165
|
+
json.dump(output_data, f, indent=2, cls=FlockJSONEncoder)
|
|
166
|
+
except Exception as e:
|
|
167
|
+
logger.warning(f"Failed to save output to file: {e}")
|
|
168
|
+
|
|
169
|
+
async def post_evaluate(
|
|
170
|
+
self,
|
|
171
|
+
agent: FlockAgent,
|
|
172
|
+
inputs: dict[str, Any],
|
|
173
|
+
result: dict[str, Any],
|
|
174
|
+
context: FlockContext | None = None,
|
|
175
|
+
) -> dict[str, Any]:
|
|
176
|
+
"""Format and display the output."""
|
|
177
|
+
logger.debug("Formatting and displaying output")
|
|
178
|
+
if self.config.no_output:
|
|
179
|
+
return result
|
|
180
|
+
if self.config.print_context:
|
|
181
|
+
result["context"] = context
|
|
182
|
+
# Display the result using the formatter
|
|
183
|
+
self._formatter.display_result(result, agent.name)
|
|
184
|
+
|
|
185
|
+
# Save to file if configured
|
|
186
|
+
self._save_output(agent.name, result)
|
|
187
|
+
|
|
188
|
+
return result
|
|
189
|
+
|
|
190
|
+
def update_theme(self, new_theme: OutputTheme) -> None:
|
|
191
|
+
"""Update the output theme."""
|
|
192
|
+
self.config.theme = new_theme
|
|
193
|
+
self._formatter = ThemedAgentResultFormatter(
|
|
194
|
+
theme=self.config.theme,
|
|
195
|
+
max_length=self.config.max_length,
|
|
196
|
+
render_table=self.config.render_table,
|
|
197
|
+
wait_for_input=self.config.wait_for_input,
|
|
198
|
+
write_to_file=self.config.write_to_file,
|
|
199
|
+
)
|
|
200
|
+
|
|
201
|
+
def add_custom_formatter(self, key: str, formatter_name: str) -> None:
|
|
202
|
+
"""Add a custom formatter for a specific output key."""
|
|
203
|
+
self.config.custom_formatters[key] = formatter_name
|
|
204
|
+
|
|
205
|
+
def get_output_files(self) -> list[str]:
|
|
206
|
+
"""Get list of saved output files."""
|
|
207
|
+
if not self.config.write_to_file:
|
|
208
|
+
return []
|
|
209
|
+
|
|
210
|
+
return [
|
|
211
|
+
f
|
|
212
|
+
for f in os.listdir(self.config.output_dir)
|
|
213
|
+
if f.endswith("_output.json")
|
|
214
|
+
]
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: flock-core
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.23
|
|
4
4
|
Summary: Declarative LLM Orchestration at Scale
|
|
5
5
|
Author-email: Andre Ratzenberger <andre.ratzenberger@whiteduck.de>
|
|
6
6
|
License-File: LICENSE
|
|
@@ -28,6 +28,7 @@ Requires-Dist: opentelemetry-instrumentation-logging>=0.51b0
|
|
|
28
28
|
Requires-Dist: opentelemetry-sdk>=1.30.0
|
|
29
29
|
Requires-Dist: pillow>=10.4.0
|
|
30
30
|
Requires-Dist: prometheus-client>=0.21.1
|
|
31
|
+
Requires-Dist: psutil>=6.1.1
|
|
31
32
|
Requires-Dist: pydantic>=2.10.5
|
|
32
33
|
Requires-Dist: python-box>=7.3.2
|
|
33
34
|
Requires-Dist: python-decouple>=3.8
|
|
@@ -60,6 +60,7 @@ flock/modules/callback/callback_module.py,sha256=volGGgHtY19qj1wHR6m5a_hmXSbV3Ca
|
|
|
60
60
|
flock/modules/memory/memory_module.py,sha256=dZ30eOFqIlAz0a5IKJMoXgJ-VyPEqApAOX0OQjhGA1I,14733
|
|
61
61
|
flock/modules/memory/memory_parser.py,sha256=FLH7GL8XThvHiCMfX3eQH7Sz-f62fzhAUmO6_gaDI7U,4372
|
|
62
62
|
flock/modules/memory/memory_storage.py,sha256=CNcLDMmvv0x7Z3YMKr6VveS_VCa7rKPw8l2d-XgqokA,27246
|
|
63
|
+
flock/modules/output/output_module.py,sha256=kfHlnhdECC2k7uF_0VZ9_152zHI0qHJ2ZqGwYSsnoro,7621
|
|
63
64
|
flock/modules/performance/metrics_module.py,sha256=UD9OjY4-zAvauMD7YyDYqE1gyIhzpdr3JkBT8j9knxY,16790
|
|
64
65
|
flock/modules/zep/zep_module.py,sha256=x7JG6O6xnwwum0RETIqKYbA3xzdcvX2aUuns0Cl0c2Q,6014
|
|
65
66
|
flock/platform/docker_tools.py,sha256=fpA7-6rJBjPOUBLdQP4ny2QPgJ_042nmqRn5GtKnoYw,1445
|
|
@@ -413,8 +414,8 @@ flock/workflow/activities.py,sha256=yah-lHjMW6_Ww1gt7hMXBis1cJRlcbHx0uLsMB9oNZ0,
|
|
|
413
414
|
flock/workflow/agent_activities.py,sha256=NhBZscflEf2IMfSRa_pBM_TRP7uVEF_O0ROvWZ33eDc,963
|
|
414
415
|
flock/workflow/temporal_setup.py,sha256=VWBgmBgfTBjwM5ruS_dVpA5AVxx6EZ7oFPGw4j3m0l0,1091
|
|
415
416
|
flock/workflow/workflow.py,sha256=I9MryXW_bqYVTHx-nl2epbTqeRy27CAWHHA7ZZA0nAk,1696
|
|
416
|
-
flock_core-0.3.
|
|
417
|
-
flock_core-0.3.
|
|
418
|
-
flock_core-0.3.
|
|
419
|
-
flock_core-0.3.
|
|
420
|
-
flock_core-0.3.
|
|
417
|
+
flock_core-0.3.23.dist-info/METADATA,sha256=qwn0OaG4E4j-5vjAPcqSv6jqpxtL8e5NcuhyHgKHkCY,20649
|
|
418
|
+
flock_core-0.3.23.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
419
|
+
flock_core-0.3.23.dist-info/entry_points.txt,sha256=rWaS5KSpkTmWySURGFZk6PhbJ87TmvcFQDi2uzjlagQ,37
|
|
420
|
+
flock_core-0.3.23.dist-info/licenses/LICENSE,sha256=iYEqWy0wjULzM9GAERaybP4LBiPeu7Z1NEliLUdJKSc,1072
|
|
421
|
+
flock_core-0.3.23.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|