flock-core 0.4.0b19__py3-none-any.whl → 0.4.0b21__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/cli/utils.py ADDED
@@ -0,0 +1,135 @@
1
+ # src/pilot_rules/collector/utils.py
2
+ import datetime
3
+ from pathlib import Path
4
+ from typing import Any
5
+
6
+ from rich import box
7
+ from rich.console import Console
8
+ from rich.panel import Panel
9
+ from rich.progress import BarColumn, Progress, SpinnerColumn, TextColumn
10
+ from rich.table import Table
11
+
12
+ # Create a shared console instance for consistent styling
13
+ console = Console()
14
+
15
+
16
+ def get_file_metadata(file_path: str) -> dict[str, Any]:
17
+ """Extract metadata from a file."""
18
+ metadata = {
19
+ "path": file_path,
20
+ "size_bytes": 0,
21
+ "line_count": 0,
22
+ "last_modified": "Unknown",
23
+ "created": "Unknown",
24
+ }
25
+
26
+ try:
27
+ p = Path(file_path)
28
+ stats = p.stat()
29
+ metadata["size_bytes"] = stats.st_size
30
+ metadata["last_modified"] = datetime.datetime.fromtimestamp(
31
+ stats.st_mtime
32
+ ).strftime("%Y-%m-%d %H:%M:%S")
33
+ # ctime is platform dependent (creation on Windows, metadata change on Unix)
34
+ # Use mtime as a reliable fallback for "created" if ctime is older than mtime
35
+ ctime = stats.st_ctime
36
+ mtime = stats.st_mtime
37
+ best_ctime = ctime if ctime <= mtime else mtime # Heuristic
38
+ metadata["created"] = datetime.datetime.fromtimestamp(
39
+ best_ctime
40
+ ).strftime("%Y-%m-%d %H:%M:%S")
41
+
42
+ try:
43
+ # Attempt to read as text, fallback for binary or encoding issues
44
+ with p.open("r", encoding="utf-8", errors="ignore") as f:
45
+ content = f.read()
46
+ metadata["line_count"] = len(content.splitlines())
47
+ except (OSError, UnicodeDecodeError) as read_err:
48
+ # Handle cases where reading might fail (binary file, permissions etc.)
49
+ console.print(
50
+ f"[yellow]⚠ Warning:[/yellow] Could not read content/count lines for [cyan]{file_path}[/cyan]: [red]{read_err}[/red]"
51
+ )
52
+ metadata["line_count"] = 0 # Indicate unreadable or binary
53
+
54
+ except Exception as e:
55
+ console.print(
56
+ f"[yellow]⚠ Warning:[/yellow] Could not get complete metadata for [cyan]{file_path}[/cyan]: [red]{e}[/red]"
57
+ )
58
+
59
+ return metadata
60
+
61
+
62
+ # --- Rich Formatting Utilities ---
63
+
64
+
65
+ def print_header(title: str, style: str = "blue") -> None:
66
+ """Print a styled header with a panel."""
67
+ console.rule()
68
+ console.print(
69
+ Panel.fit(f"[bold {style}]{title}[/bold {style}]", border_style=style)
70
+ )
71
+
72
+
73
+ def print_subheader(title: str, style: str = "cyan") -> None:
74
+ """Print a styled subheader."""
75
+ console.print(f"[bold {style}]== {title} ==[/bold {style}]")
76
+
77
+
78
+ def print_success(message: str) -> None:
79
+ """Print a success message."""
80
+ console.print(f"[bold green]✓[/bold green] {message}")
81
+
82
+
83
+ def print_error(message: str, exit_code: int | None = None) -> None:
84
+ """Print an error message and optionally exit."""
85
+ console.print(f"[bold red]✗ ERROR:[/bold red] {message}")
86
+ if exit_code is not None:
87
+ exit(exit_code)
88
+
89
+
90
+ def print_warning(message: str) -> None:
91
+ """Print a warning message."""
92
+ console.print(f"[yellow]⚠ Warning:[/yellow] {message}")
93
+
94
+
95
+ def create_progress() -> Progress:
96
+ """Create a standardized progress bar."""
97
+ return Progress(
98
+ SpinnerColumn(),
99
+ TextColumn("[bold blue]{task.description}"),
100
+ BarColumn(complete_style="green", finished_style="green"),
101
+ TextColumn("[bold]{task.completed}/{task.total}"),
102
+ console=console,
103
+ )
104
+
105
+
106
+ def create_task_table(title: str) -> Table:
107
+ """Create a standardized table for displaying task information."""
108
+ table = Table(
109
+ title=title, show_header=True, header_style="bold cyan", box=box.ROUNDED
110
+ )
111
+ return table
112
+
113
+
114
+ def print_file_stats(files: list[str], title: str = "File Statistics") -> None:
115
+ """Print statistics about a list of files."""
116
+ if not files:
117
+ console.print("[yellow]No files found to display statistics.[/yellow]")
118
+ return
119
+
120
+ table = Table(title=title, show_header=True, header_style="bold magenta")
121
+ table.add_column("Statistic", style="cyan")
122
+ table.add_column("Value", style="green")
123
+
124
+ extensions = {Path(f).suffix.lower() for f in files if Path(f).suffix}
125
+ total_size = sum(get_file_metadata(f).get("size_bytes", 0) for f in files)
126
+ total_lines = sum(get_file_metadata(f).get("line_count", 0) for f in files)
127
+
128
+ table.add_row("Total Files", str(len(files)))
129
+ table.add_row("Total Size", f"{total_size / 1024:.2f} KB")
130
+ table.add_row("Total Lines", str(total_lines))
131
+ table.add_row(
132
+ "Extensions", ", ".join(sorted(extensions)) if extensions else "None"
133
+ )
134
+
135
+ console.print(table)
flock/core/flock.py CHANGED
@@ -150,8 +150,7 @@ class Flock(BaseModel, Serializable):
150
150
  )
151
151
 
152
152
  # Initialize console if needed for banner
153
- if self.show_flock_banner: # Use instance attribute
154
- init_console()
153
+ init_console(clear_screen=True, show_banner=self.show_flock_banner)
155
154
 
156
155
  # Set Temporal debug environment variable
157
156
  self._set_temporal_debug_flag()
@@ -225,7 +224,7 @@ class Flock(BaseModel, Serializable):
225
224
  raise ValueError("Agent must have a name.")
226
225
 
227
226
  if agent.name in self._agents:
228
- logger.warning(f"Agent '{agent.name}' already exists. Overwriting.")
227
+ raise ValueError("Agent with this name already exists.")
229
228
  self._agents[agent.name] = agent
230
229
  FlockRegistry.register_agent(agent) # Register globally
231
230
 
@@ -351,13 +350,9 @@ class Flock(BaseModel, Serializable):
351
350
  try:
352
351
  resolved_start_agent = self._agents.get(start_agent_name)
353
352
  if not resolved_start_agent:
354
- resolved_start_agent = FlockRegistry.get_agent(
355
- start_agent_name
356
- )
353
+ resolved_start_agent = FlockRegistry.get_agent(start_agent_name)
357
354
  if not resolved_start_agent:
358
- raise ValueError(
359
- f"Start agent '{start_agent_name}' not found."
360
- )
355
+ raise ValueError(f"Start agent '{start_agent_name}' not found.")
361
356
  self.add_agent(resolved_start_agent)
362
357
 
363
358
  run_context = context if context else FlockContext()
@@ -389,20 +384,15 @@ class Flock(BaseModel, Serializable):
389
384
 
390
385
  # Execute workflow
391
386
  if not self.enable_temporal:
392
- result = await run_local_workflow(
393
- run_context, box_result=False
394
- )
387
+ result = await run_local_workflow(run_context, box_result=False)
395
388
  else:
396
- result = await run_temporal_workflow(
397
- run_context, box_result=False
398
- )
389
+ result = await run_temporal_workflow(run_context, box_result=False)
399
390
 
400
391
  span.set_attribute("result.type", str(type(result)))
401
392
  result_str = str(result)
402
393
  span.set_attribute(
403
394
  "result.preview",
404
- result_str[:1000]
405
- + ("..." if len(result_str) > 1000 else ""),
395
+ result_str[:1000] + ("..." if len(result_str) > 1000 else ""),
406
396
  )
407
397
 
408
398
  if box_result:
@@ -410,17 +400,13 @@ class Flock(BaseModel, Serializable):
410
400
  logger.debug("Boxing final result.")
411
401
  return Box(result)
412
402
  except ImportError:
413
- logger.warning(
414
- "Box library not installed, returning raw dict."
415
- )
403
+ logger.warning("Box library not installed, returning raw dict.")
416
404
  return result
417
405
  else:
418
406
  return result
419
407
 
420
408
  except Exception as e:
421
- logger.error(
422
- f"Flock run '{self.name}' failed: {e}", exc_info=True
423
- )
409
+ logger.error(f"Flock run '{self.name}' failed: {e}", exc_info=True)
424
410
  span.record_exception(e)
425
411
  span.set_status(trace.Status(trace.StatusCode.ERROR, str(e)))
426
412
  return {
@@ -38,6 +38,7 @@ class FlockFactory:
38
38
  print_context: bool = False,
39
39
  write_to_file: bool = False,
40
40
  stream: bool = False,
41
+ include_thought_process: bool = False,
41
42
  ) -> FlockAgent:
42
43
  """Creates a default FlockAgent.
43
44
 
@@ -52,6 +53,7 @@ class FlockFactory:
52
53
  max_tokens=max_tokens,
53
54
  temperature=temperature,
54
55
  stream=stream,
56
+ include_thought_process=include_thought_process,
55
57
  )
56
58
 
57
59
  evaluator = DeclarativeEvaluator(name="default", config=eval_config)
@@ -144,9 +144,7 @@ def custom_format(record):
144
144
  level_name = record["level"].name
145
145
  category = record["extra"].get("category", "unknown")
146
146
  trace_id = record["extra"].get("trace_id", "no-trace")
147
- color_tag = color_for_category(
148
- category
149
- ) # Get the color tag name (e.g., "yellow")
147
+ color_tag = color_for_category(category) # Get the color tag name (e.g., "yellow")
150
148
 
151
149
  message = record["message"]
152
150
  message = message.replace("{", "{{").replace("}", "}}")
@@ -155,8 +153,7 @@ def custom_format(record):
155
153
  if len(message) > MAX_LENGTH:
156
154
  truncated_chars = len(message) - MAX_LENGTH
157
155
  message = (
158
- message[:MAX_LENGTH]
159
- + f"<yellow>...+({truncated_chars} chars)</yellow>"
156
+ message[:MAX_LENGTH] + f"<yellow>...+({truncated_chars} chars)</yellow>"
160
157
  )
161
158
 
162
159
  # Determine if category needs bolding (can refine this logic)
@@ -164,13 +161,9 @@ def custom_format(record):
164
161
 
165
162
  # Apply tags sequentially
166
163
  category_styled = f"[{category}]" # Start with the plain category name
167
- category_styled = (
168
- f"<{color_tag}>{category_styled}</{color_tag}>" # Wrap with color
169
- )
164
+ category_styled = f"<{color_tag}>{category_styled}</{color_tag}>" # Wrap with color
170
165
  if needs_bold:
171
- category_styled = (
172
- f"<bold>{category_styled}</bold>" # Wrap with bold if needed
173
- )
166
+ category_styled = f"<bold>{category_styled}</bold>" # Wrap with bold if needed
174
167
 
175
168
  # Final format string using sequential tags for category
176
169
  return (
@@ -311,8 +304,7 @@ class FlockLogger:
311
304
  if len(message) > max_length:
312
305
  truncated_chars = len(message) - max_length
313
306
  return (
314
- message[:max_length]
315
- + f"...<yellow>+({truncated_chars} chars)</yellow>"
307
+ message[:max_length] + f"...<yellow>+({truncated_chars} chars)</yellow>"
316
308
  )
317
309
  return message
318
310
 
@@ -454,10 +446,7 @@ def get_module_loggers() -> list[FlockLogger]:
454
446
  def truncate_for_logging(obj, max_item_length=100, max_items=10):
455
447
  """Truncate large data structures for logging purposes."""
456
448
  if isinstance(obj, str) and len(obj) > max_item_length:
457
- return (
458
- obj[:max_item_length]
459
- + f"... ({len(obj) - max_item_length} more chars)"
460
- )
449
+ return obj[:max_item_length] + f"... ({len(obj) - max_item_length} more chars)"
461
450
  elif isinstance(obj, dict):
462
451
  if len(obj) > max_items:
463
452
  return {
@@ -93,26 +93,20 @@ def _resolve_type_string(type_str: str) -> type:
93
93
  if generic_match:
94
94
  base_name = generic_match.group(1).strip()
95
95
  args_str = generic_match.group(2).strip()
96
- logger.debug(
97
- f"Detected generic pattern: Base='{base_name}', Args='{args_str}'"
98
- )
96
+ logger.debug(f"Detected generic pattern: Base='{base_name}', Args='{args_str}'")
99
97
 
100
98
  try:
101
99
  # Get the base generic type (e.g., list, dict, Optional) from registry/builtins
102
100
  BaseType = FlockRegistry.get_type(
103
101
  base_name
104
102
  ) # Expects List, Dict etc. to be registered
105
- logger.debug(
106
- f"Resolved base generic type '{base_name}' to: {BaseType}"
107
- )
103
+ logger.debug(f"Resolved base generic type '{base_name}' to: {BaseType}")
108
104
 
109
105
  # Special handling for Literal
110
106
  if BaseType is typing.Literal:
111
107
  # Split literal values, remove quotes, strip whitespace
112
108
  literal_args_raw = split_top_level(args_str)
113
- literal_args = tuple(
114
- s.strip().strip("'\"") for s in literal_args_raw
115
- )
109
+ literal_args = tuple(s.strip().strip("'\"") for s in literal_args_raw)
116
110
  logger.debug(
117
111
  f"Parsing Literal arguments: {literal_args_raw} -> {literal_args}"
118
112
  )
@@ -127,9 +121,7 @@ def _resolve_type_string(type_str: str) -> type:
127
121
  if not arg_strs:
128
122
  raise ValueError("Generic type has no arguments.")
129
123
 
130
- resolved_arg_types = tuple(
131
- _resolve_type_string(arg) for arg in arg_strs
132
- )
124
+ resolved_arg_types = tuple(_resolve_type_string(arg) for arg in arg_strs)
133
125
  logger.debug(f"Resolved generic arguments: {resolved_arg_types}")
134
126
 
135
127
  # Construct the generic type hint
@@ -137,9 +129,7 @@ def _resolve_type_string(type_str: str) -> type:
137
129
  if len(resolved_arg_types) != 1:
138
130
  raise ValueError("Optional requires exactly one argument.")
139
131
  resolved_type = typing.Union[resolved_arg_types[0], type(None)] # type: ignore
140
- logger.debug(
141
- f"Constructed Optional type as Union: {resolved_type}"
142
- )
132
+ logger.debug(f"Constructed Optional type as Union: {resolved_type}")
143
133
  return resolved_type
144
134
  elif BaseType is typing.Union:
145
135
  if not resolved_arg_types:
@@ -151,9 +141,7 @@ def _resolve_type_string(type_str: str) -> type:
151
141
  BaseType, "__getitem__"
152
142
  ): # Check if subscriptable (like list, dict, List, Dict)
153
143
  resolved_type = BaseType[resolved_arg_types] # type: ignore
154
- logger.debug(
155
- f"Constructed subscripted generic type: {resolved_type}"
156
- )
144
+ logger.debug(f"Constructed subscripted generic type: {resolved_type}")
157
145
  return resolved_type
158
146
  else:
159
147
  # Base type found but cannot be subscripted
@@ -244,15 +232,11 @@ class DSPyIntegrationMixin:
244
232
  )
245
233
 
246
234
  FieldClass = (
247
- dspy.InputField
248
- if field_kind == "input"
249
- else dspy.OutputField
235
+ dspy.InputField if field_kind == "input" else dspy.OutputField
250
236
  )
251
237
  # DSPy Fields use 'desc' for description
252
238
  class_dict[name] = (
253
- FieldClass(desc=desc)
254
- if desc is not None
255
- else FieldClass()
239
+ FieldClass(desc=desc) if desc is not None else FieldClass()
256
240
  )
257
241
 
258
242
  try:
@@ -263,15 +247,11 @@ class DSPyIntegrationMixin:
263
247
  f"Error processing fields for DSPy signature '{agent_name}': {e}",
264
248
  exc_info=True,
265
249
  )
266
- raise ValueError(
267
- f"Could not process fields for signature: {e}"
268
- ) from e
250
+ raise ValueError(f"Could not process fields for signature: {e}") from e
269
251
 
270
252
  # Create and return the dynamic class
271
253
  try:
272
- DynamicSignature = type(
273
- "dspy_" + agent_name, (base_class,), class_dict
274
- )
254
+ DynamicSignature = type("dspy_" + agent_name, (base_class,), class_dict)
275
255
  logger.info(
276
256
  f"Successfully created DSPy Signature: {DynamicSignature.__name__} "
277
257
  f"with fields: {DynamicSignature.__annotations__}"
@@ -336,16 +316,15 @@ class DSPyIntegrationMixin:
336
316
  def _select_task(
337
317
  self,
338
318
  signature: Any,
339
- agent_type_override: AgentType,
319
+ override_evaluator_type: AgentType,
340
320
  tools: list[Any] | None = None,
321
+ kwargs: dict[str, Any] = {},
341
322
  ) -> Any:
342
323
  """Select and instantiate the appropriate DSPy Program/Module."""
343
324
  try:
344
325
  import dspy
345
326
  except ImportError:
346
- logger.error(
347
- "DSPy library is not installed. Cannot select DSPy task."
348
- )
327
+ logger.error("DSPy library is not installed. Cannot select DSPy task.")
349
328
  raise ImportError("DSPy is required for this functionality.")
350
329
 
351
330
  processed_tools = []
@@ -360,13 +339,11 @@ class DSPyIntegrationMixin:
360
339
  )
361
340
 
362
341
  dspy_program = None
363
- selected_type = agent_type_override
342
+ selected_type = override_evaluator_type
364
343
 
365
344
  # Determine type if not overridden
366
345
  if not selected_type:
367
- selected_type = (
368
- "ReAct" if processed_tools else "Predict"
369
- ) # Default logic
346
+ selected_type = "ReAct" if processed_tools else "Predict" # Default logic
370
347
 
371
348
  logger.debug(
372
349
  f"Selecting DSPy program type: {selected_type} (Tools provided: {bool(processed_tools)})"
@@ -374,11 +351,12 @@ class DSPyIntegrationMixin:
374
351
 
375
352
  try:
376
353
  if selected_type == "ChainOfThought":
377
- dspy_program = dspy.ChainOfThought(signature)
354
+ dspy_program = dspy.ChainOfThought(signature, **kwargs)
378
355
  elif selected_type == "ReAct":
379
- # ReAct requires tools, even if empty list
356
+ if not kwargs:
357
+ kwargs = {"max_iters": 10}
380
358
  dspy_program = dspy.ReAct(
381
- signature, tools=processed_tools or [], max_iters=10
359
+ signature, tools=processed_tools or [], **kwargs
382
360
  )
383
361
  elif selected_type == "Predict": # Default or explicitly Completion
384
362
  dspy_program = dspy.Predict(signature)
@@ -388,9 +366,7 @@ class DSPyIntegrationMixin:
388
366
  )
389
367
  dspy_program = dspy.Predict(signature)
390
368
 
391
- logger.info(
392
- f"Instantiated DSPy program: {type(dspy_program).__name__}"
393
- )
369
+ logger.info(f"Instantiated DSPy program: {type(dspy_program).__name__}")
394
370
  return dspy_program
395
371
  except Exception as e:
396
372
  logger.error(
@@ -401,8 +377,10 @@ class DSPyIntegrationMixin:
401
377
 
402
378
  def _process_result(
403
379
  self, result: Any, inputs: dict[str, Any]
404
- ) -> dict[str, Any]:
380
+ ) -> tuple[dict[str, Any], float, list]:
405
381
  """Convert the DSPy result object to a dictionary."""
382
+ import dspy
383
+
406
384
  if result is None:
407
385
  logger.warning("DSPy program returned None result.")
408
386
  return {}
@@ -412,9 +390,7 @@ class DSPyIntegrationMixin:
412
390
  output_dict = dict(result.items())
413
391
  elif hasattr(result, "__dict__"): # Fallback for other object types
414
392
  output_dict = {
415
- k: v
416
- for k, v in result.__dict__.items()
417
- if not k.startswith("_")
393
+ k: v for k, v in result.__dict__.items() if not k.startswith("_")
418
394
  }
419
395
  else:
420
396
  # If it's already a dict (less common for DSPy results directly)
@@ -429,7 +405,12 @@ class DSPyIntegrationMixin:
429
405
  logger.debug(f"Processed DSPy result to dict: {output_dict}")
430
406
  # Optionally merge inputs back if desired (can make result dict large)
431
407
  final_result = {**inputs, **output_dict}
432
- return final_result
408
+
409
+ lm = dspy.settings.get("lm")
410
+ cost = sum([x["cost"] for x in lm.history if x["cost"] is not None])
411
+ lm_history = lm.inspect_history()
412
+
413
+ return final_result, cost, lm_history
433
414
 
434
415
  except Exception as conv_error:
435
416
  logger.error(
@@ -57,9 +57,7 @@ def _format_type_to_string(type_hint: type) -> str:
57
57
  inner_type = next(t for t in args if t is not type(None))
58
58
  return _format_type_to_string(inner_type)
59
59
  # return f"Optional[{_format_type_to_string(inner_type)}]"
60
- return (
61
- f"Union[{', '.join(_format_type_to_string(arg) for arg in args)}]"
62
- )
60
+ return f"Union[{', '.join(_format_type_to_string(arg) for arg in args)}]"
63
61
  elif origin is Literal:
64
62
  formatted_args = []
65
63
  for arg in args:
@@ -68,9 +66,7 @@ def _format_type_to_string(type_hint: type) -> str:
68
66
  else:
69
67
  formatted_args.append(str(arg))
70
68
  return f"Literal[{', '.join(formatted_args)}]"
71
- elif hasattr(
72
- type_hint, "__forward_arg__"
73
- ): # Handle ForwardRefs if necessary
69
+ elif hasattr(type_hint, "__forward_arg__"): # Handle ForwardRefs if necessary
74
70
  return type_hint.__forward_arg__
75
71
  elif hasattr(type_hint, "__name__"):
76
72
  # Handle custom types registered in registry (get preferred name)
@@ -87,9 +83,7 @@ def _format_type_to_string(type_hint: type) -> str:
87
83
  type_repr = str(type_hint).replace("typing.", "") # Basic cleanup
88
84
  type_repr = str(type_hint).replace("| None", "")
89
85
  type_repr = type_repr.strip()
90
- logger.debug(
91
- f"Using fallback string representation for type: {type_repr}"
92
- )
86
+ logger.debug(f"Using fallback string representation for type: {type_repr}")
93
87
  return type_repr
94
88
 
95
89
 
@@ -199,9 +193,7 @@ def serialize_item(item: Any) -> Any:
199
193
  dumped = item.model_dump(mode="json", exclude_none=True)
200
194
  return serialize_item(dumped)
201
195
  elif callable(item) and not isinstance(item, type):
202
- path_str = FlockRegistry.get_callable_path_string(
203
- item
204
- ) # Use registry helper
196
+ path_str = FlockRegistry.get_callable_path_string(item) # Use registry helper
205
197
  if path_str:
206
198
  return {"__callable_ref__": path_str}
207
199
  else:
@@ -226,9 +218,7 @@ def serialize_item(item: Any) -> Any:
226
218
  ) # Check regular types/classes by path
227
219
  if type_name:
228
220
  return {"__type_ref__": type_name}
229
- logger.warning(
230
- f"Could not serialize type object {item}, storing as string."
231
- )
221
+ logger.warning(f"Could not serialize type object {item}, storing as string.")
232
222
  return str(item)
233
223
  else:
234
224
  # Return basic types as is
@@ -283,9 +273,7 @@ def deserialize_item(item: Any) -> Any:
283
273
  mod = importlib.import_module(module_name)
284
274
  type_obj = getattr(mod, class_name)
285
275
  if isinstance(type_obj, type):
286
- FlockRegistry.register_type(
287
- type_obj, type_name
288
- ) # Cache it
276
+ FlockRegistry.register_type(type_obj, type_name) # Cache it
289
277
  return type_obj
290
278
  else:
291
279
  raise TypeError()
@@ -306,9 +294,7 @@ def deserialize_item(item: Any) -> Any:
306
294
 
307
295
 
308
296
  # --- Component Deserialization Helper ---
309
- def deserialize_component(
310
- data: dict | None, expected_base_type: type
311
- ) -> Any | None:
297
+ def deserialize_component(data: dict | None, expected_base_type: type) -> Any | None:
312
298
  """Deserializes a component (Module, Evaluator, Router) from its dict representation.
313
299
  Uses the 'type' field to find the correct class via FlockRegistry.
314
300
  """
@@ -320,14 +306,10 @@ def deserialize_component(
320
306
  if data is None:
321
307
  return None
322
308
  if not isinstance(data, dict):
323
- logger.error(
324
- f"Expected dict for component deserialization, got {type(data)}"
325
- )
309
+ logger.error(f"Expected dict for component deserialization, got {type(data)}")
326
310
  return None
327
311
 
328
- type_name = data.get(
329
- "type"
330
- ) # Assuming 'type' key holds the class name string
312
+ type_name = data.get("type") # Assuming 'type' key holds the class name string
331
313
  if not type_name:
332
314
  logger.error(f"Component data missing 'type' field: {data}")
333
315
  return None
@@ -335,9 +317,7 @@ def deserialize_component(
335
317
  try:
336
318
  ComponentClass = FlockRegistry.get_component(type_name) # Use registry
337
319
  # Optional: Keep the base type check
338
- if COMPONENT_BASE_TYPES and not issubclass(
339
- ComponentClass, expected_base_type
340
- ):
320
+ if COMPONENT_BASE_TYPES and not issubclass(ComponentClass, expected_base_type):
341
321
  raise TypeError(
342
322
  f"Deserialized class {type_name} is not a subclass of {expected_base_type.__name__}"
343
323
  )
@@ -43,7 +43,7 @@ def display_hummingbird():
43
43
  """)
44
44
 
45
45
 
46
- def init_console(clear_screen: bool = True):
46
+ def init_console(clear_screen: bool = True, show_banner: bool = True):
47
47
  """Display the Flock banner."""
48
48
  banner_text = Text(
49
49
  f"""
@@ -60,16 +60,18 @@ def init_console(clear_screen: bool = True):
60
60
  )
61
61
  if clear_screen:
62
62
  console.clear()
63
- console.print(banner_text)
63
+
64
+ if show_banner:
65
+ console.print(banner_text)
64
66
  console.print(
65
- f"[italic]'Magpie'[/] milestone - [bold]white duck GmbH[/] - [cyan]https://whiteduck.de[/]\n"
67
+ "[italic]'Magpie'[/] milestone - [bold]white duck GmbH[/] - [cyan]https://whiteduck.de[/]\n"
66
68
  )
67
69
 
68
70
 
69
71
  def display_banner_no_version():
70
72
  """Display the Flock banner."""
71
73
  banner_text = Text(
72
- f"""
74
+ """
73
75
  🦆 🐓 🐤 🐧
74
76
  ╭━━━━━━━━━━━━━━━━━━━━━━━━╮
75
77
  │ ▒█▀▀▀ █░░ █▀▀█ █▀▀ █░█ │
@@ -82,4 +84,4 @@ def display_banner_no_version():
82
84
  style="bold orange3",
83
85
  )
84
86
  console.print(banner_text)
85
- console.print(f"[bold]white duck GmbH[/] - [cyan]https://whiteduck.de[/]\n")
87
+ console.print("[bold]white duck GmbH[/] - [cyan]https://whiteduck.de[/]\n")
@@ -21,9 +21,7 @@ logger = get_logger("hydrator")
21
21
  T = TypeVar("T", bound=BaseModel)
22
22
 
23
23
 
24
- def flockclass(
25
- model: str = "openai/gpt-4o", agent_description: str | None = None
26
- ):
24
+ def flockclass(model: str = "openai/gpt-4o", agent_description: str | None = None):
27
25
  """Decorator to add a .hydrate() method to a Pydantic class.
28
26
  Leverages a dynamic Flock agent to fill missing (None) fields.
29
27
 
@@ -90,9 +88,7 @@ def flockclass(
90
88
  return self # Return early if agent run failed
91
89
 
92
90
  # Update object fields with results
93
- _update_fields_with_results(
94
- self, result, missing_fields, class_name
95
- )
91
+ _update_fields_with_results(self, result, missing_fields, class_name)
96
92
 
97
93
  return self
98
94
 
@@ -109,9 +105,7 @@ def flockclass(
109
105
  import concurrent.futures
110
106
 
111
107
  with concurrent.futures.ThreadPoolExecutor() as executor:
112
- future = executor.submit(
113
- asyncio.run, hydrate_async(self)
114
- )
108
+ future = executor.submit(asyncio.run, hydrate_async(self))
115
109
  return future.result()
116
110
  else:
117
111
  # There's a loop but it's not running
@@ -124,9 +118,7 @@ def flockclass(
124
118
  # Attach the methods to the class
125
119
  setattr(cls, "hydrate_async", hydrate_async)
126
120
  setattr(cls, "hydrate", hydrate)
127
- setattr(
128
- cls, "hydrate_sync", hydrate
129
- ) # Alias for backward compatibility
121
+ setattr(cls, "hydrate_sync", hydrate) # Alias for backward compatibility
130
122
 
131
123
  logger.debug(f"Attached hydrate methods to class {cls.__name__}")
132
124
  return cls
@@ -141,14 +133,10 @@ def _get_model_fields(
141
133
  try:
142
134
  if hasattr(obj, "model_fields"): # Pydantic v2
143
135
  all_fields = obj.model_fields
144
- type_hints = {
145
- name: field.annotation for name, field in all_fields.items()
146
- }
136
+ type_hints = {name: field.annotation for name, field in all_fields.items()}
147
137
  else: # Pydantic v1 fallback
148
138
  type_hints = get_type_hints(obj.__class__)
149
- all_fields = getattr(
150
- obj, "__fields__", {name: None for name in type_hints}
151
- )
139
+ all_fields = getattr(obj, "__fields__", {name: None for name in type_hints})
152
140
  return all_fields, type_hints
153
141
  except Exception as e:
154
142
  logger.error(
@@ -268,9 +256,7 @@ async def _run_hydration_agent(
268
256
  else {"context_info": {"object_type": class_name}}
269
257
  )
270
258
 
271
- logger.info(
272
- f"Running hydration agent '{agent_name}' for {class_name}..."
273
- )
259
+ logger.info(f"Running hydration agent '{agent_name}' for {class_name}...")
274
260
 
275
261
  # Run agent
276
262
  result = await temp_flock.run_async(
@@ -278,9 +264,7 @@ async def _run_hydration_agent(
278
264
  input=agent_input_data,
279
265
  box_result=False,
280
266
  )
281
- logger.info(
282
- f"Hydration agent returned for {class_name}: {list(result.keys())}"
283
- )
267
+ logger.info(f"Hydration agent returned for {class_name}: {list(result.keys())}")
284
268
 
285
269
  return result
286
270
 
@@ -19,7 +19,7 @@ logger = get_logger("evaluators.declarative")
19
19
  class DeclarativeEvaluatorConfig(FlockEvaluatorConfig):
20
20
  """Configuration for the DeclarativeEvaluator."""
21
21
 
22
- agent_type_override: str | None = None
22
+ override_evaluator_type: str | None = None
23
23
  model: str | None = "openai/gpt-4o"
24
24
  use_cache: bool = True
25
25
  temperature: float = 0.0
@@ -28,11 +28,14 @@ class DeclarativeEvaluatorConfig(FlockEvaluatorConfig):
28
28
  default=False,
29
29
  description="Enable streaming output from the underlying DSPy program.",
30
30
  )
31
+ include_thought_process: bool = Field(
32
+ default=False,
33
+ description="Include the thought process in the output.",
34
+ )
35
+ kwargs: dict[str, Any] = Field(default_factory=dict)
31
36
 
32
37
 
33
- class DeclarativeEvaluator(
34
- FlockEvaluator, DSPyIntegrationMixin, PromptParserMixin
35
- ):
38
+ class DeclarativeEvaluator(FlockEvaluator, DSPyIntegrationMixin, PromptParserMixin):
36
39
  """Evaluator that uses DSPy for generation."""
37
40
 
38
41
  config: DeclarativeEvaluatorConfig = Field(
@@ -40,6 +43,9 @@ class DeclarativeEvaluator(
40
43
  description="Evaluator configuration",
41
44
  )
42
45
 
46
+ cost: float = 0.0
47
+ lm_history: list = Field(default_factory=list)
48
+
43
49
  async def evaluate(
44
50
  self, agent: FlockAgent, inputs: dict[str, Any], tools: list[Any]
45
51
  ) -> dict[str, Any]:
@@ -68,8 +74,9 @@ class DeclarativeEvaluator(
68
74
  )
69
75
  agent_task = self._select_task(
70
76
  _dspy_signature,
71
- agent_type_override=self.config.agent_type_override,
77
+ override_evaluator_type=self.config.override_evaluator_type,
72
78
  tools=tools,
79
+ kwargs=self.config.kwargs,
73
80
  )
74
81
  except Exception as setup_error:
75
82
  logger.error(
@@ -82,14 +89,10 @@ class DeclarativeEvaluator(
82
89
 
83
90
  # --- Conditional Evaluation (Stream vs No Stream) ---
84
91
  if self.config.stream:
85
- logger.info(
86
- f"Evaluating agent '{agent.name}' with async streaming."
87
- )
92
+ logger.info(f"Evaluating agent '{agent.name}' with async streaming.")
88
93
  if not callable(agent_task):
89
94
  logger.error("agent_task is not callable, cannot stream.")
90
- raise TypeError(
91
- "DSPy task could not be created or is not callable."
92
- )
95
+ raise TypeError("DSPy task could not be created or is not callable.")
93
96
 
94
97
  streaming_task = dspy.streamify(agent_task)
95
98
  stream_generator: Generator = streaming_task(**inputs)
@@ -109,21 +112,42 @@ class DeclarativeEvaluator(
109
112
  if delta_content:
110
113
  console.print(delta_content, end="")
111
114
 
112
- result_dict = self._process_result(chunk, inputs)
115
+ result_dict, cost, lm_history = self._process_result(chunk, inputs)
116
+ self.cost = cost
117
+ self.lm_history = lm_history
113
118
 
114
119
  console.print("\n")
115
- return result_dict
120
+ return self.filter_thought_process(
121
+ result_dict, self.config.include_thought_process
122
+ )
116
123
 
117
124
  else: # Non-streaming path
118
125
  logger.info(f"Evaluating agent '{agent.name}' without streaming.")
119
126
  try:
120
127
  # Ensure the call is awaited if the underlying task is async
121
128
  result_obj = agent_task(**inputs)
122
- result_dict = self._process_result(result_obj, inputs)
123
- return result_dict
129
+ result_dict, cost, lm_history = self._process_result(result_obj, inputs)
130
+ self.cost = cost
131
+ self.lm_history = lm_history
132
+ return self.filter_thought_process(
133
+ result_dict, self.config.include_thought_process
134
+ )
124
135
  except Exception as e:
125
136
  logger.error(
126
137
  f"Error during non-streaming evaluation for agent '{agent.name}': {e}",
127
138
  exc_info=True,
128
139
  )
129
140
  raise RuntimeError(f"Evaluation failed: {e}") from e
141
+
142
+ def filter_thought_process(
143
+ result_dict: dict[str, Any], include_thought_process: bool
144
+ ) -> dict[str, Any]:
145
+ """Filter out thought process from the result dictionary."""
146
+ if include_thought_process:
147
+ return result_dict
148
+ else:
149
+ return {
150
+ k: v
151
+ for k, v in result_dict.items()
152
+ if not (k.startswith("reasoning") or k.startswith("trajectory"))
153
+ }
@@ -0,0 +1,194 @@
1
+ """Output formatting and display functionality for agents."""
2
+
3
+ from typing import TYPE_CHECKING, Any
4
+
5
+ from pydantic import Field
6
+
7
+ from flock.core.context.context_vars import FLOCK_BATCH_SILENT_MODE
8
+
9
+ if TYPE_CHECKING:
10
+ from flock.core import FlockAgent
11
+
12
+ from flock.core.context.context import FlockContext
13
+ from flock.core.flock_module import FlockModule, FlockModuleConfig
14
+ from flock.core.logging.formatters.themed_formatter import (
15
+ ThemedAgentResultFormatter,
16
+ )
17
+ from flock.core.logging.formatters.themes import OutputTheme
18
+ from flock.core.logging.logging import get_logger
19
+
20
+ # from flock.core.logging.formatters.themes import OutputTheme
21
+ # from flock.core.logging.logging import get_logger
22
+ # from flock.core.serialization.json_encoder import FlockJSONEncoder
23
+
24
+ logger = get_logger("module.output")
25
+
26
+
27
+ class OutputModuleConfig(FlockModuleConfig):
28
+ """Configuration for output formatting and display."""
29
+
30
+ theme: OutputTheme = Field(
31
+ default=OutputTheme.afterglow, description="Theme for output formatting"
32
+ )
33
+ render_table: bool = Field(
34
+ default=False, description="Whether to render output as a table"
35
+ )
36
+ max_length: int = Field(
37
+ default=1000, description="Maximum length for displayed output"
38
+ )
39
+ truncate_long_values: bool = Field(
40
+ default=True, description="Whether to truncate long values in display"
41
+ )
42
+ show_metadata: bool = Field(
43
+ default=True, description="Whether to show metadata like timestamps"
44
+ )
45
+ format_code_blocks: bool = Field(
46
+ default=True,
47
+ description="Whether to apply syntax highlighting to code blocks",
48
+ )
49
+ custom_formatters: dict[str, str] = Field(
50
+ default_factory=dict,
51
+ description="Custom formatters for specific output types",
52
+ )
53
+ no_output: bool = Field(
54
+ default=False,
55
+ description="Whether to suppress output",
56
+ )
57
+ print_context: bool = Field(
58
+ default=False,
59
+ description="Whether to print the context",
60
+ )
61
+
62
+
63
+ class OutputModule(FlockModule):
64
+ """Module that handles output formatting and display."""
65
+
66
+ name: str = "output"
67
+ config: OutputModuleConfig = Field(
68
+ default_factory=OutputModuleConfig, description="Output configuration"
69
+ )
70
+
71
+ def __init__(self, name: str, config: OutputModuleConfig):
72
+ super().__init__(name=name, config=config)
73
+ self._formatter = ThemedAgentResultFormatter(
74
+ theme=self.config.theme,
75
+ max_length=self.config.max_length,
76
+ render_table=self.config.render_table,
77
+ )
78
+
79
+ def _format_value(self, value: Any, key: str) -> str:
80
+ """Format a single value based on its type and configuration."""
81
+ # Check for custom formatter
82
+ if key in self.config.custom_formatters:
83
+ formatter_name = self.config.custom_formatters[key]
84
+ if hasattr(self, f"_format_{formatter_name}"):
85
+ return getattr(self, f"_format_{formatter_name}")(value)
86
+
87
+ # Default formatting based on type
88
+ if isinstance(value, dict):
89
+ return self._format_dict(value)
90
+ elif isinstance(value, list):
91
+ return self._format_list(value)
92
+ elif isinstance(value, str) and self.config.format_code_blocks:
93
+ return self._format_potential_code(value)
94
+ else:
95
+ return str(value)
96
+
97
+ def _format_dict(self, d: dict[str, Any], indent: int = 0) -> str:
98
+ """Format a dictionary with proper indentation."""
99
+ lines = []
100
+ for k, v in d.items():
101
+ formatted_value = self._format_value(v, k)
102
+ if (
103
+ self.config.truncate_long_values
104
+ and len(formatted_value) > self.config.max_length
105
+ ):
106
+ formatted_value = (
107
+ formatted_value[: self.config.max_length] + "..."
108
+ )
109
+ lines.append(f"{' ' * indent}{k}: {formatted_value}")
110
+ return "\n".join(lines)
111
+
112
+ def _format_list(self, lst: list[Any]) -> str:
113
+ """Format a list with proper indentation."""
114
+ return "\n".join(f"- {self._format_value(item, '')}" for item in lst)
115
+
116
+ def _format_potential_code(self, text: str) -> str:
117
+ """Format text that might contain code blocks."""
118
+ import re
119
+
120
+ def replace_code_block(match):
121
+ code = match.group(2)
122
+ lang = match.group(1) if match.group(1) else ""
123
+ # Here you could add syntax highlighting
124
+ return f"```{lang}\n{code}\n```"
125
+
126
+ # Replace code blocks with formatted versions
127
+ text = re.sub(
128
+ r"```(\w+)?\n(.*?)\n```", replace_code_block, text, flags=re.DOTALL
129
+ )
130
+ return text
131
+
132
+ async def post_evaluate(
133
+ self,
134
+ agent: "FlockAgent",
135
+ inputs: dict[str, Any],
136
+ result: dict[str, Any],
137
+ context: FlockContext | None = None,
138
+ ) -> dict[str, Any]:
139
+ """Format and display the output."""
140
+ logger.debug("Formatting and displaying output")
141
+
142
+ # Determine if output should be suppressed
143
+ is_silent = self.config.no_output or (
144
+ context and context.get_variable(FLOCK_BATCH_SILENT_MODE, False)
145
+ )
146
+
147
+ if is_silent:
148
+ logger.debug("Output suppressed (config or batch silent mode).")
149
+ return result # Skip console output
150
+
151
+ logger.debug("Formatting and displaying output to console.")
152
+
153
+ if self.config.print_context and context:
154
+ # Add context snapshot if requested (be careful with large contexts)
155
+ try:
156
+ # Create a copy or select relevant parts to avoid modifying original result dict directly
157
+ display_result = result.copy()
158
+ display_result["context_snapshot"] = (
159
+ context.to_dict()
160
+ ) # Potential performance hit
161
+ except Exception:
162
+ display_result = result.copy()
163
+ display_result["context_snapshot"] = (
164
+ "[Error serializing context]"
165
+ )
166
+ result_to_display = display_result
167
+ else:
168
+ result_to_display = result
169
+
170
+ if not hasattr(self, "_formatter") or self._formatter is None:
171
+ self._formatter = ThemedAgentResultFormatter(
172
+ theme=self.config.theme,
173
+ max_length=self.config.max_length,
174
+ render_table=self.config.render_table,
175
+ wait_for_input=self.config.wait_for_input,
176
+ )
177
+ self._formatter.display_result(result_to_display, agent.name)
178
+
179
+ return result # Return the original, unmodified result
180
+
181
+ def update_theme(self, new_theme: OutputTheme) -> None:
182
+ """Update the output theme."""
183
+ self.config.theme = new_theme
184
+ self._formatter = ThemedAgentResultFormatter(
185
+ theme=self.config.theme,
186
+ max_length=self.config.max_length,
187
+ render_table=self.config.render_table,
188
+ wait_for_input=self.config.wait_for_input,
189
+ write_to_file=self.config.write_to_file,
190
+ )
191
+
192
+ def add_custom_formatter(self, key: str, formatter_name: str) -> None:
193
+ """Add a custom formatter for a specific output key."""
194
+ self.config.custom_formatters[key] = formatter_name
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: flock-core
3
- Version: 0.4.0b19
3
+ Version: 0.4.0b21
4
4
  Summary: Declarative LLM Orchestration at Scale
5
5
  Author-email: Andre Ratzenberger <andre.ratzenberger@whiteduck.de>
6
6
  License-File: LICENSE
@@ -58,8 +58,6 @@ Requires-Dist: markdownify>=0.14.1; extra == 'tools'
58
58
  Requires-Dist: tavily-python>=0.5.0; extra == 'tools'
59
59
  Description-Content-Type: text/markdown
60
60
 
61
- # 🚀 Flock: The Declarative AI Agent Framework 🚀
62
-
63
61
  <p align="center">
64
62
  <!-- Placeholder for your Flock Logo/Banner - Replace URL -->
65
63
  <img alt="Flock Banner" src="https://raw.githubusercontent.com/whiteducksoftware/flock/master/docs/assets/images/flock.png" width="600">
@@ -95,20 +93,25 @@ Built with real-world deployment in mind, Flock integrates seamlessly with tools
95
93
 
96
94
  Flock offers a different way to build agentic systems:
97
95
 
98
- | Traditional Agent Frameworks 😟 | Flock Framework 🐤🐧🐓🦆 |
99
- | :------------------------------------- | :------------------------------------ |
100
- | 🤯 **Prompt Nightmare** | ✅ **Declarative Simplicity** |
101
- | *Long, brittle, hard-to-tune prompts* | *Clear input/output specs (typed!)* |
102
- | 💥 **Fragile & Unpredictable** | ⚡ **Robust & Production-Ready** |
103
- | *Single errors can halt everything* | *Fault-tolerant via Temporal option* |
104
- | 🧩 **Monolithic & Rigid** | 🔧 **Modular & Flexible** |
105
- | *Hard to extend or modify logic* | *Pluggable Evaluators, Modules, Tools*|
106
- | ⛓️ **Basic Chaining** | 🚀 **Advanced Orchestration** |
107
- | *Often just linear workflows* | *Dynamic Routing, Batch Processing* |
108
- | 🧪 **Difficult Testing** | ✅ **Testable Components** |
109
- | *Hard to unit test prompt logic* | *Clear I/O contracts aid testing* |
110
- | 📄 **Unstructured Output** | ✨ **Structured Data Handling** |
111
- | *Parsing unreliable LLM text output* | *Native Pydantic/Typed Dict support* |
96
+ | Traditional Agent Frameworks 😟 | Flock Framework 🐤🐧🐓🦆 |
97
+ | :------------------------------------ | :------------------------------------- |
98
+ | 🤯 **Prompt Nightmare** | ✅ **Declarative Simplicity** |
99
+ | *Long, brittle, hard-to-tune prompts* | *Clear input/output specs (typed!)* |
100
+ | 💥 **Fragile & Unpredictable** | ⚡ **Robust & Production-Ready** |
101
+ | *Single errors can halt everything* | *Fault-tolerant via Temporal option* |
102
+ | 🧩 **Monolithic & Rigid** | 🔧 **Modular & Flexible** |
103
+ | *Hard to extend or modify logic* | *Pluggable Evaluators, Modules, Tools* |
104
+ | ⛓️ **Basic Chaining** | 🚀 **Advanced Orchestration** |
105
+ | *Often just linear workflows* | *Dynamic Routing, Batch Processing* |
106
+ | 🧪 **Difficult Testing** | ✅ **Testable Components** |
107
+ | *Hard to unit test prompt logic* | *Clear I/O contracts aid testing* |
108
+ | 📄 **Unstructured Output** | ✨ **Structured Data Handling** |
109
+ | *Parsing unreliable LLM text output* | *Native Pydantic/Typed Dict support* |
110
+
111
+
112
+ ## 📹 Video Demo
113
+
114
+ https://github.com/user-attachments/assets/bdab4786-d532-459f-806a-024727164dcc
112
115
 
113
116
  ## 💡 Core Concepts
114
117
 
@@ -210,7 +213,17 @@ if __name__ == "__main__":
210
213
  print("Ensure your LLM API key (e.g., OPENAI_API_KEY) is set in your .env file!")
211
214
  ```
212
215
 
213
- ## Utility: @flockclass Hydrator
216
+ ## 🐤 New in Flock 0.4.0 `Magpie` 🐤
217
+
218
+ ### REST API - Deploy Flock Agents as REST API Endpoints
219
+
220
+ ### Web UI - Test Flock Agents in the Browser
221
+
222
+ ### CLI Tool - Manage Flock Agents via the Command Line
223
+
224
+ ### Serialization - Share, Deploy, and Run Flock Agents by human readable yaml files
225
+
226
+ ### ✨ Utility: @flockclass Hydrator
214
227
 
215
228
  Flock also provides conveniences. The @flockclass decorator allows you to easily populate Pydantic models using an LLM:
216
229
 
@@ -269,11 +282,10 @@ Ways to contribute:
269
282
  - Improve documentation.
270
283
  - Contribute new Modules, Evaluators, or Routers.
271
284
  - Add examples to the flock-showcase repository.
272
- - Join our Discord Community to discuss development! <!-- Add Discord link -->
273
285
 
274
286
  ## 📜 License
275
287
 
276
- Flock is licensed under the Apache License 2.0. See the LICENSE file for details.
288
+ Flock is licensed under the MIT License. See the LICENSE file for details.
277
289
 
278
290
  ## 🏢 About
279
291
 
@@ -14,14 +14,15 @@ flock/cli/manage_agents.py,sha256=Psl014LCrJmBgwrjsp7O3WNlWvQmVd_IDud3rd0lnLI,12
14
14
  flock/cli/registry_management.py,sha256=mAHy3wT97YgODR0gVOkTXDqR5NIPzM-E-z9dEtw9-tw,29790
15
15
  flock/cli/runner.py,sha256=TgiuhRLkpa6dn3C-3eCmWx-bWUlTjaH0sD7Y-O7MrYM,1122
16
16
  flock/cli/settings.py,sha256=Z_TXBzCYlCmSaKrJ_CQCdYy-Cj29gpI4kbC_2KzoKqg,27025
17
+ flock/cli/utils.py,sha256=JJrvM-1D2tbWkicrtkhOgRqVqYb0MdA2XtHYGOYuPRw,4644
17
18
  flock/cli/view_results.py,sha256=dOzK0O1FHSIDERnx48y-2Xke9BkOHS7pcOhs64AyIg0,781
18
19
  flock/cli/yaml_editor.py,sha256=K3N0bh61G1TSDAZDnurqW9e_-hO6CtSQKXQqlDhCjVo,12527
19
20
  flock/cli/assets/release_notes.md,sha256=bqnk50jxM3w5uY44Dc7MkdT8XmRREFxrVBAG9XCOSSU,4896
20
21
  flock/core/__init__.py,sha256=p7lmQULRu9ejIAELfanZiyMhW0CougIPvyFHW2nqBFQ,847
21
- flock/core/flock.py,sha256=g67jyWozK52ymqGEmYxMgXSkiL9ZpTXoDZMxwtWmRo4,25444
22
+ flock/core/flock.py,sha256=RcDUc__ukjUy9aSA_n0t8SxXCex4BV_nagTcg22peJ0,25116
22
23
  flock/core/flock_agent.py,sha256=ZmkiHd2oLao_263b7nmf26TQfyevX9_HNlhHPIkd3UM,33497
23
24
  flock/core/flock_evaluator.py,sha256=dOXZeDOGZcAmJ9ahqq_2bdGUU1VOXY4skmwTVpAjiVw,1685
24
- flock/core/flock_factory.py,sha256=qfeuAr6e028gbkxjj5jsq_HHqrWsmKHreJD6-QT8ets,2885
25
+ flock/core/flock_factory.py,sha256=vLkCASLh7Vrb5NFjb4ZQT5xN3zsUDud51hAQxb82oTk,2993
25
26
  flock/core/flock_module.py,sha256=96aFVYAgwpKN53xGbivQDUpikOYGFCxK5mqhclOcxY0,3003
26
27
  flock/core/flock_registry.py,sha256=ekYpQgSkZVnbyPbl8gA7nf54brt94rYZZBe2RwEGtUc,20828
27
28
  flock/core/flock_router.py,sha256=A5GaxcGvtiFlRLHBTW7okh5RDm3BdKam2uXvRHRaj7k,2187
@@ -44,7 +45,7 @@ flock/core/execution/local_executor.py,sha256=rnIQvaJOs6zZORUcR3vvyS6LPREDJTjayg
44
45
  flock/core/execution/temporal_executor.py,sha256=OF_uXgQsoUGp6U1ZkcuaidAEKyH7XDtbfrtdF10XQ_4,1675
45
46
  flock/core/interpreter/python_interpreter.py,sha256=RaUMZuufsKBNQ4FAeSaOgUuxzs8VYu5TgUUs-xwaxxM,26376
46
47
  flock/core/logging/__init__.py,sha256=Q8hp9-1ilPIUIV0jLgJ3_cP7COrea32cVwL7dicPnlM,82
47
- flock/core/logging/logging.py,sha256=-Mzft9GOahe0cOQtNbdlh0yLH-00ZAl713xHugtRFeQ,15527
48
+ flock/core/logging/logging.py,sha256=JcgABQ8QJU1hhzhfF93eqnE0jhyXGZ2oObZst68sKR8,15409
48
49
  flock/core/logging/telemetry.py,sha256=3E9Tyj6AUR6A5RlIufcdCdWm5BAA7tbOsCa7lHoUQaU,5404
49
50
  flock/core/logging/trace_and_logged.py,sha256=5vNrK1kxuPMoPJ0-QjQg-EDJL1oiEzvU6UNi6X8FiMs,2117
50
51
  flock/core/logging/formatters/enum_builder.py,sha256=LgEYXUv84wK5vwHflZ5h8HBGgvLH3sByvUQe8tZiyY0,981
@@ -55,7 +56,7 @@ flock/core/logging/span_middleware/baggage_span_processor.py,sha256=gJfRl8FeB6jd
55
56
  flock/core/logging/telemetry_exporter/base_exporter.py,sha256=rQJJzS6q9n2aojoSqwCnl7ZtHrh5LZZ-gkxUuI5WfrQ,1124
56
57
  flock/core/logging/telemetry_exporter/file_exporter.py,sha256=nKAjJSZtA7FqHSTuTiFtYYepaxOq7l1rDvs8U8rSBlA,3023
57
58
  flock/core/logging/telemetry_exporter/sqlite_exporter.py,sha256=CDsiMb9QcqeXelZ6ZqPSS56ovMPGqOu6whzBZRK__Vg,3498
58
- flock/core/mixin/dspy_integration.py,sha256=vlf6rJnR9EsfZi5KyFLEXIbUvhpBhodctn-mirYo7Pk,17382
59
+ flock/core/mixin/dspy_integration.py,sha256=lB3VLBa5dnBJQOgdJCFGWC_0-XMyXmqCHmnCPFyrZuY,17197
59
60
  flock/core/mixin/prompt_parser.py,sha256=eOqI-FK3y17gVqpc_y5GF-WmK1Jv8mFlkZxTcgweoxI,5121
60
61
  flock/core/serialization/__init__.py,sha256=CML7fPgG6p4c0CDBlJ_uwV1aZZhJKK9uy3IoIHfO87w,431
61
62
  flock/core/serialization/callable_registry.py,sha256=sUZECTZWsM3fJ8FDRQ-FgLNW9hF26nY17AD6fJKADMc,1419
@@ -63,19 +64,19 @@ flock/core/serialization/flock_serializer.py,sha256=TEePKaJqU-_XWHTMWyMHloDNwmkK
63
64
  flock/core/serialization/json_encoder.py,sha256=gAKj2zU_8wQiNvdkby2hksSA4fbPNwTjup_yz1Le1Vw,1229
64
65
  flock/core/serialization/secure_serializer.py,sha256=n5-zRvvXddgJv1FFHsaQ2wuYdL3WUSGPvG_LGaffEJo,6144
65
66
  flock/core/serialization/serializable.py,sha256=qlv8TsTqRuklXiNuCMrvro5VKz764xC2i3FlgLJSkdk,12129
66
- flock/core/serialization/serialization_utils.py,sha256=n5q6P18dEK1JHejgFhAv-87mODjKuz36GYV1n6p-VO4,12924
67
+ flock/core/serialization/serialization_utils.py,sha256=63CV41Jba29EEqS85h_6ADgbeYvVmfILmbXv742LiXQ,12702
67
68
  flock/core/tools/azure_tools.py,sha256=hwLnI2gsEq6QzUoWj5eCGDKTdXY1XUf6K-H5Uwva2MY,17093
68
69
  flock/core/tools/basic_tools.py,sha256=Ye7nlI4RRkqWRy8nH9CKuItBmh_ZXxUpouGnCOfx0s0,9050
69
70
  flock/core/tools/llm_tools.py,sha256=Bdt4Dpur5dGpxd2KFEQyxjfZazvW1HCDKY6ydMj6UgQ,21811
70
71
  flock/core/tools/markdown_tools.py,sha256=W6fGM48yGHbifVlaOk1jOtVcybfRbRmf20VbDOZv8S4,6031
71
72
  flock/core/tools/zendesk_tools.py,sha256=deZAyUi9j-_yZaTayLQVJaFXIqIct-P6C8IGN5UU_tM,3528
72
73
  flock/core/tools/dev_tools/github.py,sha256=a2OTPXS7kWOVA4zrZHynQDcsmEi4Pac5MfSjQOLePzA,5308
73
- flock/core/util/cli_helper.py,sha256=P3-lncS1J78MuPpDQ0D8qmfxMTCRxKkGdo9QRkzrp6A,49797
74
+ flock/core/util/cli_helper.py,sha256=EnK4X-FgaO71JsSXLdy0R-cxE3UctlFZtzldursw-Bw,49845
74
75
  flock/core/util/file_path_utils.py,sha256=Odf7uU32C-x1KNighbNERSiMtkzW4h8laABIoFK7A5M,6246
75
- flock/core/util/hydrator.py,sha256=z0kvqr4j3piExOb_T5EgATuNm77ZlqIKvLpt4PJusO8,10949
76
+ flock/core/util/hydrator.py,sha256=QJvCA8F4nkSP5akp3yg0cT6oaajOr1n7sldW5dCs6Lo,10733
76
77
  flock/core/util/input_resolver.py,sha256=g9vDPdY4OH-G7qjas5ksGEHueokHGFPMoLOvC-ngeLo,5984
77
78
  flock/core/util/loader.py,sha256=j3q2qem5bFMP2SmMuYjb-ISxsNGNZd1baQmpvAnRUUk,2244
78
- flock/evaluators/declarative/declarative_evaluator.py,sha256=j3xCEOhKHMvYtI43K2uqqJk068Lw94ch8GtWOmmJX7g,4803
79
+ flock/evaluators/declarative/declarative_evaluator.py,sha256=nagYTKqoduQ968Pij4bZh0584RYH4MDPdYnh9dtaYHc,5905
79
80
  flock/evaluators/memory/azure_search_evaluator.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
80
81
  flock/evaluators/memory/memory_evaluator.py,sha256=SmerXyNaqm8DTV0yw-WqWkn9DXIf6x-nPG1eyTV6NY8,3452
81
82
  flock/evaluators/natural_language/natural_language_evaluator.py,sha256=6nVEeh8_uwv_h-d3FWlA0GbzDzRtdhvxCGKirHtyvOU,2012
@@ -86,6 +87,7 @@ flock/modules/callback/callback_module.py,sha256=volGGgHtY19qj1wHR6m5a_hmXSbV3Ca
86
87
  flock/modules/memory/memory_module.py,sha256=bSkdFBW-Pp5ldHhXi8v4kfRM7zknfLR2fsOtbTosucI,14916
87
88
  flock/modules/memory/memory_parser.py,sha256=FLH7GL8XThvHiCMfX3eQH7Sz-f62fzhAUmO6_gaDI7U,4372
88
89
  flock/modules/memory/memory_storage.py,sha256=CNcLDMmvv0x7Z3YMKr6VveS_VCa7rKPw8l2d-XgqokA,27246
90
+ flock/modules/output/output_module.py,sha256=tpNqkL8cB0cvikr1fmEJ-5E2SAeb-rYhht8IP90mEF0,7281
89
91
  flock/modules/performance/metrics_module.py,sha256=UD9OjY4-zAvauMD7YyDYqE1gyIhzpdr3JkBT8j9knxY,16790
90
92
  flock/modules/zep/zep_module.py,sha256=x7JG6O6xnwwum0RETIqKYbA3xzdcvX2aUuns0Cl0c2Q,6014
91
93
  flock/platform/docker_tools.py,sha256=fpA7-6rJBjPOUBLdQP4ny2QPgJ_042nmqRn5GtKnoYw,1445
@@ -439,8 +441,8 @@ flock/workflow/activities.py,sha256=eVZDnxGJl_quNO-UTV3YgvTV8LrRaHN3QDAA1ANKzac,
439
441
  flock/workflow/agent_activities.py,sha256=NhBZscflEf2IMfSRa_pBM_TRP7uVEF_O0ROvWZ33eDc,963
440
442
  flock/workflow/temporal_setup.py,sha256=VWBgmBgfTBjwM5ruS_dVpA5AVxx6EZ7oFPGw4j3m0l0,1091
441
443
  flock/workflow/workflow.py,sha256=I9MryXW_bqYVTHx-nl2epbTqeRy27CAWHHA7ZZA0nAk,1696
442
- flock_core-0.4.0b19.dist-info/METADATA,sha256=AxxdPGiZnMvkFqmHKNmVLN4FH07NWjUSPXHGhamhF54,12697
443
- flock_core-0.4.0b19.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
444
- flock_core-0.4.0b19.dist-info/entry_points.txt,sha256=rWaS5KSpkTmWySURGFZk6PhbJ87TmvcFQDi2uzjlagQ,37
445
- flock_core-0.4.0b19.dist-info/licenses/LICENSE,sha256=iYEqWy0wjULzM9GAERaybP4LBiPeu7Z1NEliLUdJKSc,1072
446
- flock_core-0.4.0b19.dist-info/RECORD,,
444
+ flock_core-0.4.0b21.dist-info/METADATA,sha256=q4rcJrfPWmenOfchiag5UQhLaLmdr68TE-f7G6p7Hd8,12970
445
+ flock_core-0.4.0b21.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
446
+ flock_core-0.4.0b21.dist-info/entry_points.txt,sha256=rWaS5KSpkTmWySURGFZk6PhbJ87TmvcFQDi2uzjlagQ,37
447
+ flock_core-0.4.0b21.dist-info/licenses/LICENSE,sha256=iYEqWy0wjULzM9GAERaybP4LBiPeu7Z1NEliLUdJKSc,1072
448
+ flock_core-0.4.0b21.dist-info/RECORD,,