flock-core 0.4.0b19__py3-none-any.whl → 0.4.0b20__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
 
@@ -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)
@@ -336,8 +336,9 @@ class DSPyIntegrationMixin:
336
336
  def _select_task(
337
337
  self,
338
338
  signature: Any,
339
- agent_type_override: AgentType,
339
+ override_evaluator_type: AgentType,
340
340
  tools: list[Any] | None = None,
341
+ kwargs: dict[str, Any] = {},
341
342
  ) -> Any:
342
343
  """Select and instantiate the appropriate DSPy Program/Module."""
343
344
  try:
@@ -360,7 +361,7 @@ class DSPyIntegrationMixin:
360
361
  )
361
362
 
362
363
  dspy_program = None
363
- selected_type = agent_type_override
364
+ selected_type = override_evaluator_type
364
365
 
365
366
  # Determine type if not overridden
366
367
  if not selected_type:
@@ -374,11 +375,12 @@ class DSPyIntegrationMixin:
374
375
 
375
376
  try:
376
377
  if selected_type == "ChainOfThought":
377
- dspy_program = dspy.ChainOfThought(signature)
378
+ dspy_program = dspy.ChainOfThought(signature, **kwargs)
378
379
  elif selected_type == "ReAct":
379
- # ReAct requires tools, even if empty list
380
+ if not kwargs:
381
+ kwargs = {"max_iters": 10}
380
382
  dspy_program = dspy.ReAct(
381
- signature, tools=processed_tools or [], max_iters=10
383
+ signature, tools=processed_tools or [], **kwargs
382
384
  )
383
385
  elif selected_type == "Predict": # Default or explicitly Completion
384
386
  dspy_program = dspy.Predict(signature)
@@ -401,8 +403,10 @@ class DSPyIntegrationMixin:
401
403
 
402
404
  def _process_result(
403
405
  self, result: Any, inputs: dict[str, Any]
404
- ) -> dict[str, Any]:
406
+ ) -> tuple[dict[str, Any], float, list]:
405
407
  """Convert the DSPy result object to a dictionary."""
408
+ import dspy
409
+
406
410
  if result is None:
407
411
  logger.warning("DSPy program returned None result.")
408
412
  return {}
@@ -429,7 +433,12 @@ class DSPyIntegrationMixin:
429
433
  logger.debug(f"Processed DSPy result to dict: {output_dict}")
430
434
  # Optionally merge inputs back if desired (can make result dict large)
431
435
  final_result = {**inputs, **output_dict}
432
- return final_result
436
+
437
+ lm = dspy.settings.get("lm")
438
+ cost = sum([x["cost"] for x in lm.history if x["cost"] is not None])
439
+ lm_history = lm.inspect_history()
440
+
441
+ return final_result, cost, lm_history
433
442
 
434
443
  except Exception as conv_error:
435
444
  logger.error(
@@ -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,7 +60,9 @@ 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
67
  f"[italic]'Magpie'[/] milestone - [bold]white duck GmbH[/] - [cyan]https://whiteduck.de[/]\n"
66
68
  )
@@ -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,6 +28,11 @@ 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
38
  class DeclarativeEvaluator(
@@ -40,6 +45,9 @@ class DeclarativeEvaluator(
40
45
  description="Evaluator configuration",
41
46
  )
42
47
 
48
+ cost: float = 0.0
49
+ lm_history: list = Field(default_factory=list)
50
+
43
51
  async def evaluate(
44
52
  self, agent: FlockAgent, inputs: dict[str, Any], tools: list[Any]
45
53
  ) -> dict[str, Any]:
@@ -68,8 +76,9 @@ class DeclarativeEvaluator(
68
76
  )
69
77
  agent_task = self._select_task(
70
78
  _dspy_signature,
71
- agent_type_override=self.config.agent_type_override,
79
+ override_evaluator_type=self.config.override_evaluator_type,
72
80
  tools=tools,
81
+ kwargs=self.config.kwargs,
73
82
  )
74
83
  except Exception as setup_error:
75
84
  logger.error(
@@ -109,21 +118,46 @@ class DeclarativeEvaluator(
109
118
  if delta_content:
110
119
  console.print(delta_content, end="")
111
120
 
112
- result_dict = self._process_result(chunk, inputs)
121
+ result_dict, cost, lm_history = self._process_result(
122
+ chunk, inputs
123
+ )
124
+ self.cost = cost
125
+ self.lm_history = lm_history
113
126
 
114
127
  console.print("\n")
115
- return result_dict
128
+ return self.filter_thought_process(
129
+ result_dict, self.config.include_thought_process
130
+ )
116
131
 
117
132
  else: # Non-streaming path
118
133
  logger.info(f"Evaluating agent '{agent.name}' without streaming.")
119
134
  try:
120
135
  # Ensure the call is awaited if the underlying task is async
121
136
  result_obj = agent_task(**inputs)
122
- result_dict = self._process_result(result_obj, inputs)
123
- return result_dict
137
+ result_dict, cost, lm_history = self._process_result(
138
+ result_obj, inputs
139
+ )
140
+ self.cost = cost
141
+ self.lm_history = lm_history
142
+ return self.filter_thought_process(
143
+ result_dict, self.config.include_thought_process
144
+ )
124
145
  except Exception as e:
125
146
  logger.error(
126
147
  f"Error during non-streaming evaluation for agent '{agent.name}': {e}",
127
148
  exc_info=True,
128
149
  )
129
150
  raise RuntimeError(f"Evaluation failed: {e}") from e
151
+
152
+ def filter_thought_process(
153
+ result_dict: dict[str, Any], include_thought_process: bool
154
+ ) -> dict[str, Any]:
155
+ """Filter out thought process from the result dictionary."""
156
+ if include_thought_process:
157
+ return result_dict
158
+ else:
159
+ return {
160
+ k: v
161
+ for k, v in result_dict.items()
162
+ if not (k.startswith("reasoning") or k.startswith("trajectory"))
163
+ }
@@ -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.0b20
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=K7EsY7K8avH57mJ3I_y0Ehlk0G8bYt4ZpBFYPtjgZ4o,25420
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
@@ -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=MplWCkJZymtmf1646yUYlpBxaz39SOenW7EQ1SpSQnE,17681
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
@@ -70,12 +71,12 @@ flock/core/tools/llm_tools.py,sha256=Bdt4Dpur5dGpxd2KFEQyxjfZazvW1HCDKY6ydMj6UgQ
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=nbA3n7zpVopV7bDf_aB3HHRYXmIBSwT1yqpjiVl4r9g,49848
74
75
  flock/core/util/file_path_utils.py,sha256=Odf7uU32C-x1KNighbNERSiMtkzW4h8laABIoFK7A5M,6246
75
76
  flock/core/util/hydrator.py,sha256=z0kvqr4j3piExOb_T5EgATuNm77ZlqIKvLpt4PJusO8,10949
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=Pi1GsQUGL8eJrl6GFWYpu7O9kXI11xIskdBVg08n8mo,6055
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.0b20.dist-info/METADATA,sha256=gHmRUklybXUkMM1Z_XDaMxCcgVzOpSeS9ksy1MUZfIw,12970
445
+ flock_core-0.4.0b20.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
446
+ flock_core-0.4.0b20.dist-info/entry_points.txt,sha256=rWaS5KSpkTmWySURGFZk6PhbJ87TmvcFQDi2uzjlagQ,37
447
+ flock_core-0.4.0b20.dist-info/licenses/LICENSE,sha256=iYEqWy0wjULzM9GAERaybP4LBiPeu7Z1NEliLUdJKSc,1072
448
+ flock_core-0.4.0b20.dist-info/RECORD,,