flock-core 0.4.520__py3-none-any.whl → 0.5.0b2__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/manage_agents.py +3 -3
- flock/components/__init__.py +28 -0
- flock/components/evaluation/__init__.py +9 -0
- flock/components/evaluation/declarative_evaluation_component.py +198 -0
- flock/components/routing/__init__.py +15 -0
- flock/{routers/conditional/conditional_router.py → components/routing/conditional_routing_component.py} +60 -49
- flock/components/routing/default_routing_component.py +103 -0
- flock/components/routing/llm_routing_component.py +208 -0
- flock/components/utility/__init__.py +15 -0
- flock/{modules/enterprise_memory/enterprise_memory_module.py → components/utility/memory_utility_component.py} +195 -173
- flock/{modules/performance/metrics_module.py → components/utility/metrics_utility_component.py} +101 -86
- flock/{modules/output/output_module.py → components/utility/output_utility_component.py} +49 -49
- flock/core/__init__.py +2 -8
- flock/core/agent/__init__.py +16 -0
- flock/core/agent/flock_agent_components.py +104 -0
- flock/core/agent/flock_agent_execution.py +101 -0
- flock/core/agent/flock_agent_integration.py +147 -0
- flock/core/agent/flock_agent_lifecycle.py +177 -0
- flock/core/agent/flock_agent_serialization.py +378 -0
- flock/core/component/__init__.py +15 -0
- flock/core/{flock_module.py → component/agent_component_base.py} +136 -35
- flock/core/component/evaluation_component_base.py +56 -0
- flock/core/component/routing_component_base.py +75 -0
- flock/core/component/utility_component_base.py +69 -0
- flock/core/config/flock_agent_config.py +49 -2
- flock/core/evaluation/utils.py +1 -1
- flock/core/execution/evaluation_executor.py +1 -1
- flock/core/flock.py +137 -483
- flock/core/flock_agent.py +151 -1018
- flock/core/flock_factory.py +94 -73
- flock/core/{flock_registry.py → flock_registry.py.backup} +3 -17
- flock/core/logging/logging.py +1 -0
- flock/core/mcp/flock_mcp_server.py +42 -37
- flock/core/mixin/dspy_integration.py +5 -5
- flock/core/orchestration/__init__.py +18 -0
- flock/core/orchestration/flock_batch_processor.py +94 -0
- flock/core/orchestration/flock_evaluator.py +113 -0
- flock/core/orchestration/flock_execution.py +288 -0
- flock/core/orchestration/flock_initialization.py +125 -0
- flock/core/orchestration/flock_server_manager.py +65 -0
- flock/core/orchestration/flock_web_server.py +117 -0
- flock/core/registry/__init__.py +39 -0
- flock/core/registry/agent_registry.py +69 -0
- flock/core/registry/callable_registry.py +139 -0
- flock/core/registry/component_discovery.py +142 -0
- flock/core/registry/component_registry.py +64 -0
- flock/core/registry/config_mapping.py +64 -0
- flock/core/registry/decorators.py +137 -0
- flock/core/registry/registry_hub.py +202 -0
- flock/core/registry/server_registry.py +57 -0
- flock/core/registry/type_registry.py +86 -0
- flock/core/serialization/flock_serializer.py +33 -30
- flock/core/serialization/serialization_utils.py +28 -25
- flock/core/util/input_resolver.py +29 -2
- flock/platform/docker_tools.py +3 -3
- flock/tools/markdown_tools.py +1 -2
- flock/tools/text_tools.py +1 -2
- flock/webapp/app/main.py +9 -5
- flock/workflow/activities.py +59 -84
- flock/workflow/activities_unified.py +230 -0
- flock/workflow/agent_execution_activity.py +6 -6
- flock/workflow/flock_workflow.py +1 -1
- {flock_core-0.4.520.dist-info → flock_core-0.5.0b2.dist-info}/METADATA +2 -2
- {flock_core-0.4.520.dist-info → flock_core-0.5.0b2.dist-info}/RECORD +67 -68
- flock/core/flock_evaluator.py +0 -60
- flock/core/flock_router.py +0 -83
- flock/evaluators/__init__.py +0 -1
- flock/evaluators/declarative/__init__.py +0 -1
- flock/evaluators/declarative/declarative_evaluator.py +0 -194
- flock/evaluators/memory/memory_evaluator.py +0 -90
- flock/evaluators/test/test_case_evaluator.py +0 -38
- flock/evaluators/zep/zep_evaluator.py +0 -59
- flock/modules/__init__.py +0 -1
- flock/modules/assertion/__init__.py +0 -1
- flock/modules/assertion/assertion_module.py +0 -286
- flock/modules/callback/__init__.py +0 -1
- flock/modules/callback/callback_module.py +0 -91
- flock/modules/enterprise_memory/README.md +0 -99
- flock/modules/mem0/__init__.py +0 -1
- flock/modules/mem0/mem0_module.py +0 -126
- flock/modules/mem0_async/__init__.py +0 -1
- flock/modules/mem0_async/async_mem0_module.py +0 -126
- flock/modules/memory/__init__.py +0 -1
- flock/modules/memory/memory_module.py +0 -429
- flock/modules/memory/memory_parser.py +0 -125
- flock/modules/memory/memory_storage.py +0 -736
- flock/modules/output/__init__.py +0 -1
- flock/modules/performance/__init__.py +0 -1
- flock/modules/zep/__init__.py +0 -1
- flock/modules/zep/zep_module.py +0 -192
- flock/routers/__init__.py +0 -1
- flock/routers/agent/__init__.py +0 -1
- flock/routers/agent/agent_router.py +0 -236
- flock/routers/agent/handoff_agent.py +0 -58
- flock/routers/default/__init__.py +0 -1
- flock/routers/default/default_router.py +0 -80
- flock/routers/feedback/feedback_router.py +0 -114
- flock/routers/list_generator/list_generator_router.py +0 -166
- flock/routers/llm/__init__.py +0 -1
- flock/routers/llm/llm_router.py +0 -365
- {flock_core-0.4.520.dist-info → flock_core-0.5.0b2.dist-info}/WHEEL +0 -0
- {flock_core-0.4.520.dist-info → flock_core-0.5.0b2.dist-info}/entry_points.txt +0 -0
- {flock_core-0.4.520.dist-info → flock_core-0.5.0b2.dist-info}/licenses/LICENSE +0 -0
flock/{modules/performance/metrics_module.py → components/utility/metrics_utility_component.py}
RENAMED
|
@@ -1,22 +1,26 @@
|
|
|
1
|
-
|
|
1
|
+
# src/flock/components/utility/metrics_utility_component.py
|
|
2
|
+
"""Performance and metrics tracking for Flock agents using unified component architecture."""
|
|
2
3
|
|
|
3
4
|
import json
|
|
4
5
|
import os
|
|
5
6
|
import time
|
|
6
7
|
from collections import defaultdict
|
|
7
8
|
from datetime import datetime
|
|
8
|
-
from typing import Any, Literal
|
|
9
|
+
from typing import TYPE_CHECKING, Any, Literal
|
|
9
10
|
|
|
10
11
|
import numpy as np
|
|
11
12
|
import psutil
|
|
12
|
-
from pydantic import BaseModel, Field,
|
|
13
|
+
from pydantic import BaseModel, Field, field_validator
|
|
13
14
|
|
|
15
|
+
from flock.core.component.agent_component_base import AgentComponentConfig
|
|
16
|
+
from flock.core.component.utility_component_base import UtilityComponentBase
|
|
14
17
|
from flock.core.context.context import FlockContext
|
|
15
|
-
from flock.core.
|
|
16
|
-
from flock.core.flock_module import FlockModule, FlockModuleConfig
|
|
17
|
-
from flock.core.flock_registry import flock_component
|
|
18
|
+
from flock.core.registry import flock_component
|
|
18
19
|
from flock.core.mcp.flock_mcp_server import FlockMCPServerBase
|
|
19
20
|
|
|
21
|
+
if TYPE_CHECKING:
|
|
22
|
+
from flock.core.flock_agent import FlockAgent
|
|
23
|
+
|
|
20
24
|
|
|
21
25
|
class MetricPoint(BaseModel):
|
|
22
26
|
"""Single metric measurement."""
|
|
@@ -25,11 +29,10 @@ class MetricPoint(BaseModel):
|
|
|
25
29
|
value: int | float | str
|
|
26
30
|
tags: dict[str, str] = {}
|
|
27
31
|
|
|
28
|
-
|
|
29
|
-
arbitrary_types_allowed = True
|
|
32
|
+
model_config = {"arbitrary_types_allowed": True}
|
|
30
33
|
|
|
31
34
|
|
|
32
|
-
class
|
|
35
|
+
class MetricsUtilityConfig(AgentComponentConfig):
|
|
33
36
|
"""Configuration for performance metrics collection."""
|
|
34
37
|
|
|
35
38
|
# Collection settings
|
|
@@ -66,7 +69,8 @@ class MetricsModuleConfig(FlockModuleConfig):
|
|
|
66
69
|
default=1000, description="Threshold for latency alerts"
|
|
67
70
|
)
|
|
68
71
|
|
|
69
|
-
@
|
|
72
|
+
@field_validator("aggregation_interval")
|
|
73
|
+
@classmethod
|
|
70
74
|
def validate_interval(cls, v):
|
|
71
75
|
"""Validate time interval format."""
|
|
72
76
|
if v[-1] not in ["s", "m", "h", "d"]:
|
|
@@ -74,23 +78,30 @@ class MetricsModuleConfig(FlockModuleConfig):
|
|
|
74
78
|
return v
|
|
75
79
|
|
|
76
80
|
|
|
77
|
-
@flock_component(config_class=
|
|
78
|
-
class
|
|
79
|
-
"""
|
|
81
|
+
@flock_component(config_class=MetricsUtilityConfig)
|
|
82
|
+
class MetricsUtilityComponent(UtilityComponentBase):
|
|
83
|
+
"""Utility component for collecting and analyzing agent performance metrics."""
|
|
80
84
|
|
|
81
85
|
# --- Singleton holder for convenient static access ---
|
|
82
|
-
_INSTANCE: "
|
|
86
|
+
_INSTANCE: "MetricsUtilityComponent | None" = None
|
|
83
87
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
default_factory=MetricsModuleConfig,
|
|
88
|
+
config: MetricsUtilityConfig = Field(
|
|
89
|
+
default_factory=MetricsUtilityConfig,
|
|
87
90
|
description="Performance metrics configuration",
|
|
88
91
|
)
|
|
89
92
|
|
|
90
|
-
def __init__(
|
|
91
|
-
|
|
93
|
+
def __init__(
|
|
94
|
+
self,
|
|
95
|
+
name: str = "metrics",
|
|
96
|
+
config: MetricsUtilityConfig | None = None,
|
|
97
|
+
**data,
|
|
98
|
+
):
|
|
99
|
+
if config is None:
|
|
100
|
+
config = MetricsUtilityConfig()
|
|
101
|
+
super().__init__(name=name, config=config, **data)
|
|
102
|
+
|
|
92
103
|
# Register singleton for static helpers
|
|
93
|
-
|
|
104
|
+
MetricsUtilityComponent._INSTANCE = self
|
|
94
105
|
self._metrics = defaultdict(list)
|
|
95
106
|
self._start_time: float | None = None
|
|
96
107
|
self._server_start_time: float | None = None
|
|
@@ -130,8 +141,6 @@ class MetricsModule(FlockModule):
|
|
|
130
141
|
except ImportError:
|
|
131
142
|
self.config.storage_type = "json"
|
|
132
143
|
|
|
133
|
-
"""Fixes for metrics summary calculation."""
|
|
134
|
-
|
|
135
144
|
def _load_metrics_from_files(
|
|
136
145
|
self, metric_name: str = None
|
|
137
146
|
) -> dict[str, list[MetricPoint]]:
|
|
@@ -236,40 +245,6 @@ class MetricsModule(FlockModule):
|
|
|
236
245
|
|
|
237
246
|
return stats
|
|
238
247
|
|
|
239
|
-
async def on_terminate(
|
|
240
|
-
self,
|
|
241
|
-
agent: FlockAgent,
|
|
242
|
-
inputs: dict[str, Any],
|
|
243
|
-
context: FlockContext | None = None,
|
|
244
|
-
result: dict[str, Any] | None = None,
|
|
245
|
-
) -> None:
|
|
246
|
-
"""Clean up and final metric recording."""
|
|
247
|
-
if self.config.storage_type == "json":
|
|
248
|
-
# Save aggregated metrics
|
|
249
|
-
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
|
|
250
|
-
summary_file = os.path.join(
|
|
251
|
-
self.config.metrics_dir,
|
|
252
|
-
f"summary_{agent.name}_{timestamp}.json",
|
|
253
|
-
)
|
|
254
|
-
|
|
255
|
-
# Calculate summary for all metrics
|
|
256
|
-
summary = {
|
|
257
|
-
"agent": agent.name,
|
|
258
|
-
"timestamp": timestamp,
|
|
259
|
-
"metrics": {},
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
# Get all unique metric names from files
|
|
263
|
-
all_metrics = self._load_metrics_from_files()
|
|
264
|
-
|
|
265
|
-
for metric_name in all_metrics:
|
|
266
|
-
stats = self.get_statistics(metric_name)
|
|
267
|
-
if stats: # Only include metrics that have data
|
|
268
|
-
summary["metrics"][metric_name] = stats
|
|
269
|
-
|
|
270
|
-
with open(summary_file, "w") as f:
|
|
271
|
-
json.dump(summary, f, indent=2)
|
|
272
|
-
|
|
273
248
|
def _record_metric(
|
|
274
249
|
self, name: str, value: int | float | str, tags: dict[str, str] = None
|
|
275
250
|
) -> None:
|
|
@@ -342,29 +317,7 @@ class MetricsModule(FlockModule):
|
|
|
342
317
|
print(
|
|
343
318
|
f"Warning: Using estimated token count. Install tiktoken for accurate counting."
|
|
344
319
|
)
|
|
345
|
-
|
|
346
|
-
def _should_alert(self, metric: str, value: float) -> bool:
|
|
347
|
-
"""Check if metric should trigger alert."""
|
|
348
|
-
if metric == "latency" and self.config.alert_on_high_latency:
|
|
349
|
-
return value * 1000 > self.config.latency_threshold_ms
|
|
350
|
-
return False
|
|
351
|
-
|
|
352
|
-
async def on_initialize(
|
|
353
|
-
self,
|
|
354
|
-
agent: FlockAgent,
|
|
355
|
-
inputs: dict[str, Any],
|
|
356
|
-
context: FlockContext | None = None,
|
|
357
|
-
) -> None:
|
|
358
|
-
"""Initialize metrics collection."""
|
|
359
|
-
self._start_time = time.time()
|
|
360
|
-
|
|
361
|
-
if self.config.collect_memory:
|
|
362
|
-
self._start_memory = psutil.Process().memory_info().rss
|
|
363
|
-
self._record_metric(
|
|
364
|
-
"memory",
|
|
365
|
-
self._start_memory,
|
|
366
|
-
{"agent": agent.name, "phase": "start"},
|
|
367
|
-
)
|
|
320
|
+
return token_estimate
|
|
368
321
|
|
|
369
322
|
def _calculate_cost(
|
|
370
323
|
self, text: str, model: str, is_completion: bool = False
|
|
@@ -391,9 +344,32 @@ class MetricsModule(FlockModule):
|
|
|
391
344
|
total_cost = 0.0
|
|
392
345
|
return token_count, total_cost
|
|
393
346
|
|
|
347
|
+
def _should_alert(self, metric: str, value: float) -> bool:
|
|
348
|
+
"""Check if metric should trigger alert."""
|
|
349
|
+
if metric == "latency" and self.config.alert_on_high_latency:
|
|
350
|
+
return value * 1000 > self.config.latency_threshold_ms
|
|
351
|
+
return False
|
|
352
|
+
|
|
353
|
+
async def on_initialize(
|
|
354
|
+
self,
|
|
355
|
+
agent: "FlockAgent",
|
|
356
|
+
inputs: dict[str, Any],
|
|
357
|
+
context: FlockContext | None = None,
|
|
358
|
+
) -> None:
|
|
359
|
+
"""Initialize metrics collection."""
|
|
360
|
+
self._start_time = time.time()
|
|
361
|
+
|
|
362
|
+
if self.config.collect_memory:
|
|
363
|
+
self._start_memory = psutil.Process().memory_info().rss
|
|
364
|
+
self._record_metric(
|
|
365
|
+
"memory",
|
|
366
|
+
self._start_memory,
|
|
367
|
+
{"agent": agent.name, "phase": "start"},
|
|
368
|
+
)
|
|
369
|
+
|
|
394
370
|
async def on_pre_evaluate(
|
|
395
371
|
self,
|
|
396
|
-
agent: FlockAgent,
|
|
372
|
+
agent: "FlockAgent",
|
|
397
373
|
inputs: dict[str, Any],
|
|
398
374
|
context: FlockContext | None = None,
|
|
399
375
|
) -> dict[str, Any]:
|
|
@@ -434,7 +410,7 @@ class MetricsModule(FlockModule):
|
|
|
434
410
|
|
|
435
411
|
async def on_post_evaluate(
|
|
436
412
|
self,
|
|
437
|
-
agent: FlockAgent,
|
|
413
|
+
agent: "FlockAgent",
|
|
438
414
|
inputs: dict[str, Any],
|
|
439
415
|
context: FlockContext | None = None,
|
|
440
416
|
result: dict[str, Any] | None = None,
|
|
@@ -449,7 +425,7 @@ class MetricsModule(FlockModule):
|
|
|
449
425
|
# In practice, you'd want to integrate with a proper alerting system
|
|
450
426
|
print(f"ALERT: High latency detected: {latency * 1000:.2f}ms")
|
|
451
427
|
|
|
452
|
-
if self.config.collect_token_usage:
|
|
428
|
+
if self.config.collect_token_usage and result:
|
|
453
429
|
# Calculate output tokens and cost
|
|
454
430
|
total_output_tokens = 0
|
|
455
431
|
total_output_cost = 0.0
|
|
@@ -493,7 +469,7 @@ class MetricsModule(FlockModule):
|
|
|
493
469
|
|
|
494
470
|
async def on_error(
|
|
495
471
|
self,
|
|
496
|
-
agent: FlockAgent,
|
|
472
|
+
agent: "FlockAgent",
|
|
497
473
|
error: Exception,
|
|
498
474
|
inputs: dict[str, Any],
|
|
499
475
|
context: FlockContext | None = None,
|
|
@@ -505,16 +481,55 @@ class MetricsModule(FlockModule):
|
|
|
505
481
|
{"agent": agent.name, "error_type": type(error).__name__},
|
|
506
482
|
)
|
|
507
483
|
|
|
484
|
+
async def on_terminate(
|
|
485
|
+
self,
|
|
486
|
+
agent: "FlockAgent",
|
|
487
|
+
inputs: dict[str, Any],
|
|
488
|
+
context: FlockContext | None = None,
|
|
489
|
+
result: dict[str, Any] | None = None,
|
|
490
|
+
) -> None:
|
|
491
|
+
"""Clean up and final metric recording."""
|
|
492
|
+
if self.config.storage_type == "json":
|
|
493
|
+
# Save aggregated metrics
|
|
494
|
+
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
|
|
495
|
+
summary_file = os.path.join(
|
|
496
|
+
self.config.metrics_dir,
|
|
497
|
+
f"summary_{agent.name}_{timestamp}.json",
|
|
498
|
+
)
|
|
499
|
+
|
|
500
|
+
# Calculate summary for all metrics
|
|
501
|
+
summary = {
|
|
502
|
+
"agent": agent.name,
|
|
503
|
+
"timestamp": timestamp,
|
|
504
|
+
"metrics": {},
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
# Get all unique metric names from files
|
|
508
|
+
all_metrics = self._load_metrics_from_files()
|
|
509
|
+
|
|
510
|
+
for metric_name in all_metrics:
|
|
511
|
+
stats = self.get_statistics(metric_name)
|
|
512
|
+
if stats: # Only include metrics that have data
|
|
513
|
+
summary["metrics"][metric_name] = stats
|
|
514
|
+
|
|
515
|
+
with open(summary_file, "w") as f:
|
|
516
|
+
json.dump(summary, f, indent=2)
|
|
517
|
+
|
|
508
518
|
# --------------------------------------------------
|
|
509
519
|
# Public helper for external modules
|
|
510
520
|
# --------------------------------------------------
|
|
511
521
|
@classmethod
|
|
512
|
-
def record(
|
|
522
|
+
def record(
|
|
523
|
+
cls,
|
|
524
|
+
name: str,
|
|
525
|
+
value: int | float | str,
|
|
526
|
+
tags: dict[str, str] | None = None,
|
|
527
|
+
):
|
|
513
528
|
"""Record a metric from anywhere in the codebase.
|
|
514
529
|
|
|
515
530
|
Example:
|
|
516
|
-
|
|
517
|
-
The call will forward to the *first* instantiated
|
|
531
|
+
MetricsUtilityComponent.record("custom_latency", 123, {"stage": "inference"})
|
|
532
|
+
The call will forward to the *first* instantiated MetricsUtilityComponent. If no
|
|
518
533
|
instance exists in the current run the call is a no-op so that importing
|
|
519
534
|
this helper never crashes test-code.
|
|
520
535
|
"""
|
|
@@ -1,31 +1,27 @@
|
|
|
1
|
-
|
|
1
|
+
# src/flock/components/utility/output_utility_component.py
|
|
2
|
+
"""Output formatting and display functionality for agents using unified component architecture."""
|
|
2
3
|
|
|
4
|
+
import re
|
|
3
5
|
from typing import TYPE_CHECKING, Any
|
|
4
6
|
|
|
5
7
|
from pydantic import Field
|
|
6
8
|
|
|
7
|
-
from flock.core.
|
|
8
|
-
from flock.core.
|
|
9
|
-
|
|
10
|
-
if TYPE_CHECKING:
|
|
11
|
-
from flock.core import FlockAgent
|
|
12
|
-
|
|
9
|
+
from flock.core.component.agent_component_base import AgentComponentConfig
|
|
10
|
+
from flock.core.component.utility_component_base import UtilityComponentBase
|
|
13
11
|
from flock.core.context.context import FlockContext
|
|
14
|
-
from flock.core.
|
|
15
|
-
from flock.core.
|
|
16
|
-
|
|
17
|
-
)
|
|
12
|
+
from flock.core.context.context_vars import FLOCK_BATCH_SILENT_MODE
|
|
13
|
+
from flock.core.registry import flock_component
|
|
14
|
+
from flock.core.logging.formatters.themed_formatter import ThemedAgentResultFormatter
|
|
18
15
|
from flock.core.logging.formatters.themes import OutputTheme
|
|
19
16
|
from flock.core.logging.logging import get_logger
|
|
20
17
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
# from flock.core.serialization.json_encoder import FlockJSONEncoder
|
|
18
|
+
if TYPE_CHECKING:
|
|
19
|
+
from flock.core.flock_agent import FlockAgent
|
|
24
20
|
|
|
25
|
-
logger = get_logger("
|
|
21
|
+
logger = get_logger("components.utility.output")
|
|
26
22
|
|
|
27
23
|
|
|
28
|
-
class
|
|
24
|
+
class OutputUtilityConfig(AgentComponentConfig):
|
|
29
25
|
"""Configuration for output formatting and display."""
|
|
30
26
|
|
|
31
27
|
theme: OutputTheme = Field(
|
|
@@ -61,17 +57,18 @@ class OutputModuleConfig(FlockModuleConfig):
|
|
|
61
57
|
)
|
|
62
58
|
|
|
63
59
|
|
|
64
|
-
@flock_component(config_class=
|
|
65
|
-
class
|
|
66
|
-
"""
|
|
60
|
+
@flock_component(config_class=OutputUtilityConfig)
|
|
61
|
+
class OutputUtilityComponent(UtilityComponentBase):
|
|
62
|
+
"""Utility component that handles output formatting and display."""
|
|
67
63
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
default_factory=OutputModuleConfig, description="Output configuration"
|
|
64
|
+
config: OutputUtilityConfig = Field(
|
|
65
|
+
default_factory=OutputUtilityConfig, description="Output configuration"
|
|
71
66
|
)
|
|
72
67
|
|
|
73
|
-
def __init__(self, name: str, config:
|
|
74
|
-
|
|
68
|
+
def __init__(self, name: str = "output", config: OutputUtilityConfig | None = None, **data):
|
|
69
|
+
if config is None:
|
|
70
|
+
config = OutputUtilityConfig()
|
|
71
|
+
super().__init__(name=name, config=config, **data)
|
|
75
72
|
self._formatter = ThemedAgentResultFormatter(
|
|
76
73
|
theme=self.config.theme,
|
|
77
74
|
max_length=self.config.max_length,
|
|
@@ -98,34 +95,40 @@ class OutputModule(FlockModule):
|
|
|
98
95
|
|
|
99
96
|
def _format_dict(self, d: dict[str, Any], indent: int = 0) -> str:
|
|
100
97
|
"""Format a dictionary with proper indentation."""
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
):
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
return "\n".join(
|
|
98
|
+
if not d:
|
|
99
|
+
return "{}"
|
|
100
|
+
|
|
101
|
+
items = []
|
|
102
|
+
prefix = " " * indent
|
|
103
|
+
for key, value in d.items():
|
|
104
|
+
if self.config.truncate_long_values and isinstance(value, str) and len(value) > 100:
|
|
105
|
+
value = value[:97] + "..."
|
|
106
|
+
formatted_value = self._format_value(value, key)
|
|
107
|
+
items.append(f"{prefix} {key}: {formatted_value}")
|
|
108
|
+
|
|
109
|
+
return "{\n" + "\n".join(items) + f"\n{prefix}}}"
|
|
113
110
|
|
|
114
111
|
def _format_list(self, lst: list[Any]) -> str:
|
|
115
|
-
"""Format a list with proper
|
|
116
|
-
|
|
112
|
+
"""Format a list with proper structure."""
|
|
113
|
+
if not lst:
|
|
114
|
+
return "[]"
|
|
115
|
+
|
|
116
|
+
if len(lst) <= 3:
|
|
117
|
+
return str(lst)
|
|
118
|
+
|
|
119
|
+
# For longer lists, show first few items and count
|
|
120
|
+
preview = [str(item) for item in lst[:3]]
|
|
121
|
+
return f"[{', '.join(preview)}, ... ({len(lst)} total)]"
|
|
117
122
|
|
|
118
123
|
def _format_potential_code(self, text: str) -> str:
|
|
119
|
-
"""
|
|
120
|
-
|
|
121
|
-
|
|
124
|
+
"""Apply syntax highlighting to potential code blocks."""
|
|
125
|
+
# Simple pattern matching for code blocks
|
|
122
126
|
def replace_code_block(match):
|
|
127
|
+
language = match.group(1) or "text"
|
|
123
128
|
code = match.group(2)
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
# Replace code blocks with formatted versions
|
|
129
|
+
return f"[CODE:{language}]\n{code}\n[/CODE]"
|
|
130
|
+
|
|
131
|
+
# Replace markdown-style code blocks
|
|
129
132
|
text = re.sub(
|
|
130
133
|
r"```(\w+)?\n(.*?)\n```", replace_code_block, text, flags=re.DOTALL
|
|
131
134
|
)
|
|
@@ -174,7 +177,6 @@ class OutputModule(FlockModule):
|
|
|
174
177
|
theme=self.config.theme,
|
|
175
178
|
max_length=self.config.max_length,
|
|
176
179
|
render_table=self.config.render_table,
|
|
177
|
-
wait_for_input=self.config.wait_for_input,
|
|
178
180
|
)
|
|
179
181
|
self._formatter.display_result(result_to_display, agent.name)
|
|
180
182
|
|
|
@@ -187,8 +189,6 @@ class OutputModule(FlockModule):
|
|
|
187
189
|
theme=self.config.theme,
|
|
188
190
|
max_length=self.config.max_length,
|
|
189
191
|
render_table=self.config.render_table,
|
|
190
|
-
wait_for_input=self.config.wait_for_input,
|
|
191
|
-
write_to_file=self.config.write_to_file,
|
|
192
192
|
)
|
|
193
193
|
|
|
194
194
|
def add_custom_formatter(self, key: str, formatter_name: str) -> None:
|
flock/core/__init__.py
CHANGED
|
@@ -3,11 +3,9 @@
|
|
|
3
3
|
from flock.core.context.context import FlockContext
|
|
4
4
|
from flock.core.flock import Flock
|
|
5
5
|
from flock.core.flock_agent import FlockAgent
|
|
6
|
-
from flock.core.flock_evaluator import FlockEvaluator, FlockEvaluatorConfig
|
|
7
6
|
from flock.core.flock_factory import FlockFactory
|
|
8
|
-
from flock.core.
|
|
9
|
-
|
|
10
|
-
FlockRegistry,
|
|
7
|
+
from flock.core.registry import (
|
|
8
|
+
RegistryHub as FlockRegistry, # Keep FlockRegistry name for API compatibility
|
|
11
9
|
flock_callable,
|
|
12
10
|
flock_component,
|
|
13
11
|
flock_tool,
|
|
@@ -25,16 +23,12 @@ __all__ = [
|
|
|
25
23
|
"Flock",
|
|
26
24
|
"FlockAgent",
|
|
27
25
|
"FlockContext",
|
|
28
|
-
"FlockEvaluator",
|
|
29
|
-
"FlockEvaluatorConfig",
|
|
30
26
|
"FlockFactory",
|
|
31
27
|
"FlockMCPClientBase",
|
|
32
28
|
"FlockMCPClientManagerBase",
|
|
33
29
|
"FlockMCPServerBase",
|
|
34
30
|
"FlockMCPServerConfig",
|
|
35
31
|
"FlockMCPToolBase",
|
|
36
|
-
"FlockModule",
|
|
37
|
-
"FlockModuleConfig",
|
|
38
32
|
"FlockRegistry",
|
|
39
33
|
"flock_callable",
|
|
40
34
|
"flock_component",
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# src/flock/core/agent/__init__.py
|
|
2
|
+
"""Agent components package."""
|
|
3
|
+
|
|
4
|
+
from .flock_agent_components import FlockAgentComponents
|
|
5
|
+
from .flock_agent_execution import FlockAgentExecution
|
|
6
|
+
from .flock_agent_integration import FlockAgentIntegration
|
|
7
|
+
from .flock_agent_lifecycle import FlockAgentLifecycle
|
|
8
|
+
from .flock_agent_serialization import FlockAgentSerialization
|
|
9
|
+
|
|
10
|
+
__all__ = [
|
|
11
|
+
"FlockAgentComponents",
|
|
12
|
+
"FlockAgentExecution",
|
|
13
|
+
"FlockAgentIntegration",
|
|
14
|
+
"FlockAgentLifecycle",
|
|
15
|
+
"FlockAgentSerialization",
|
|
16
|
+
]
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
"""Component management functionality for FlockAgent."""
|
|
2
|
+
|
|
3
|
+
from typing import TYPE_CHECKING, Any
|
|
4
|
+
|
|
5
|
+
from flock.core.component.evaluation_component_base import EvaluationComponentBase
|
|
6
|
+
from flock.core.component.routing_component_base import RoutingComponentBase
|
|
7
|
+
from flock.core.component.utility_component_base import UtilityComponentBase
|
|
8
|
+
from flock.core.logging.logging import get_logger
|
|
9
|
+
|
|
10
|
+
if TYPE_CHECKING:
|
|
11
|
+
from flock.core.flock_agent import FlockAgent
|
|
12
|
+
from flock.core.component.agent_component_base import AgentComponent
|
|
13
|
+
|
|
14
|
+
logger = get_logger("agent.components")
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class FlockAgentComponents:
|
|
18
|
+
"""Helper class for managing unified components on FlockAgent."""
|
|
19
|
+
|
|
20
|
+
def __init__(self, agent: "FlockAgent"):
|
|
21
|
+
self.agent = agent
|
|
22
|
+
|
|
23
|
+
def add_component(self, component: "AgentComponent") -> None:
|
|
24
|
+
"""Add a unified component to this agent."""
|
|
25
|
+
if not component.name:
|
|
26
|
+
logger.error("Component must have a name to be added.")
|
|
27
|
+
return
|
|
28
|
+
|
|
29
|
+
# Check if component with same name already exists
|
|
30
|
+
existing = self.get_component(component.name)
|
|
31
|
+
if existing:
|
|
32
|
+
logger.warning(f"Overwriting existing component: {component.name}")
|
|
33
|
+
self.agent.components.remove(existing)
|
|
34
|
+
|
|
35
|
+
self.agent.components.append(component)
|
|
36
|
+
logger.debug(f"Added component '{component.name}' to agent '{self.agent.name}'")
|
|
37
|
+
|
|
38
|
+
def remove_component(self, component_name: str) -> None:
|
|
39
|
+
"""Remove a component from this agent."""
|
|
40
|
+
component = self.get_component(component_name)
|
|
41
|
+
if component:
|
|
42
|
+
self.agent.components.remove(component)
|
|
43
|
+
logger.debug(f"Removed component '{component_name}' from agent '{self.agent.name}'")
|
|
44
|
+
else:
|
|
45
|
+
logger.warning(f"Component '{component_name}' not found on agent '{self.agent.name}'")
|
|
46
|
+
|
|
47
|
+
def get_component(self, component_name: str) -> "AgentComponent | None":
|
|
48
|
+
"""Get a component by name."""
|
|
49
|
+
for component in self.agent.components:
|
|
50
|
+
if component.name == component_name:
|
|
51
|
+
return component
|
|
52
|
+
return None
|
|
53
|
+
|
|
54
|
+
def get_enabled_components(self) -> list["AgentComponent"]:
|
|
55
|
+
"""Get a list of currently enabled components attached to this agent."""
|
|
56
|
+
return [c for c in self.agent.components if c.config.enabled]
|
|
57
|
+
|
|
58
|
+
def get_components_by_type(self, component_type: type) -> list["AgentComponent"]:
|
|
59
|
+
"""Get all components of a specific type."""
|
|
60
|
+
return [c for c in self.agent.components if isinstance(c, component_type)]
|
|
61
|
+
|
|
62
|
+
def get_evaluation_components(self) -> list[EvaluationComponentBase]:
|
|
63
|
+
"""Get all evaluation components."""
|
|
64
|
+
return self.get_components_by_type(EvaluationComponentBase)
|
|
65
|
+
|
|
66
|
+
def get_routing_components(self) -> list[RoutingComponentBase]:
|
|
67
|
+
"""Get all routing components."""
|
|
68
|
+
return self.get_components_by_type(RoutingComponentBase)
|
|
69
|
+
|
|
70
|
+
def get_utility_components(self) -> list[UtilityComponentBase]:
|
|
71
|
+
"""Get all utility components."""
|
|
72
|
+
return self.get_components_by_type(UtilityComponentBase)
|
|
73
|
+
|
|
74
|
+
def get_primary_evaluator(self) -> EvaluationComponentBase | None:
|
|
75
|
+
"""Get the primary evaluation component (first one found)."""
|
|
76
|
+
evaluators = self.get_evaluation_components()
|
|
77
|
+
return evaluators[0] if evaluators else None
|
|
78
|
+
|
|
79
|
+
def get_primary_router(self) -> RoutingComponentBase | None:
|
|
80
|
+
"""Get the primary routing component (first one found)."""
|
|
81
|
+
routers = self.get_routing_components()
|
|
82
|
+
return routers[0] if routers else None
|
|
83
|
+
|
|
84
|
+
# Legacy compatibility methods (delegate to new unified approach)
|
|
85
|
+
def add_module(self, module: Any) -> None:
|
|
86
|
+
"""DEPRECATED: Use add_component() instead."""
|
|
87
|
+
logger.warning("add_module is deprecated - use add_component() instead")
|
|
88
|
+
if hasattr(module, 'name'):
|
|
89
|
+
self.add_component(module)
|
|
90
|
+
|
|
91
|
+
def get_module(self, module_name: str) -> Any | None:
|
|
92
|
+
"""DEPRECATED: Use get_component() instead."""
|
|
93
|
+
logger.warning("get_module is deprecated - use get_component() instead")
|
|
94
|
+
return self.get_component(module_name)
|
|
95
|
+
|
|
96
|
+
def get_evaluator(self) -> Any | None:
|
|
97
|
+
"""DEPRECATED: Use get_primary_evaluator() instead."""
|
|
98
|
+
logger.warning("get_evaluator is deprecated - use get_primary_evaluator() instead")
|
|
99
|
+
return self.get_primary_evaluator()
|
|
100
|
+
|
|
101
|
+
def get_router(self) -> Any | None:
|
|
102
|
+
"""DEPRECATED: Use get_primary_router() instead."""
|
|
103
|
+
logger.warning("get_router is deprecated - use get_primary_router() instead")
|
|
104
|
+
return self.get_primary_router()
|