flock-core 0.4.0b42__py3-none-any.whl → 0.4.0b44__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/core/api/__init__.py +1 -2
- flock/core/api/endpoints.py +149 -217
- flock/core/api/main.py +134 -653
- flock/core/api/service.py +214 -0
- flock/core/flock.py +192 -133
- flock/webapp/app/api/agent_management.py +135 -164
- flock/webapp/app/api/execution.py +76 -85
- flock/webapp/app/api/flock_management.py +60 -33
- flock/webapp/app/chat.py +233 -0
- flock/webapp/app/config.py +6 -3
- flock/webapp/app/dependencies.py +95 -0
- flock/webapp/app/main.py +320 -906
- flock/webapp/app/services/flock_service.py +183 -161
- flock/webapp/run.py +178 -97
- flock/webapp/static/css/chat.css +227 -0
- flock/webapp/static/css/components.css +167 -0
- flock/webapp/static/css/header.css +39 -0
- flock/webapp/static/css/layout.css +46 -0
- flock/webapp/static/css/sidebar.css +127 -0
- flock/webapp/templates/base.html +6 -1
- flock/webapp/templates/chat.html +60 -0
- flock/webapp/templates/chat_settings.html +20 -0
- flock/webapp/templates/flock_editor.html +1 -1
- flock/webapp/templates/partials/_agent_detail_form.html +4 -4
- flock/webapp/templates/partials/_agent_list.html +2 -2
- flock/webapp/templates/partials/_agent_manager_view.html +3 -4
- flock/webapp/templates/partials/_chat_container.html +9 -0
- flock/webapp/templates/partials/_chat_messages.html +13 -0
- flock/webapp/templates/partials/_chat_settings_form.html +65 -0
- flock/webapp/templates/partials/_execution_form.html +2 -2
- flock/webapp/templates/partials/_execution_view_container.html +1 -1
- flock/webapp/templates/partials/_flock_properties_form.html +2 -2
- flock/webapp/templates/partials/_registry_viewer_content.html +3 -3
- flock/webapp/templates/partials/_sidebar.html +17 -1
- flock/webapp/templates/registry_viewer.html +3 -3
- {flock_core-0.4.0b42.dist-info → flock_core-0.4.0b44.dist-info}/METADATA +1 -1
- {flock_core-0.4.0b42.dist-info → flock_core-0.4.0b44.dist-info}/RECORD +40 -29
- flock/webapp/static/css/custom.css +0 -612
- flock/webapp/templates/partials/_agent_manager_view_old.html +0 -19
- {flock_core-0.4.0b42.dist-info → flock_core-0.4.0b44.dist-info}/WHEEL +0 -0
- {flock_core-0.4.0b42.dist-info → flock_core-0.4.0b44.dist-info}/entry_points.txt +0 -0
- {flock_core-0.4.0b42.dist-info → flock_core-0.4.0b44.dist-info}/licenses/LICENSE +0 -0
flock/core/flock.py
CHANGED
|
@@ -20,23 +20,28 @@ from box import Box
|
|
|
20
20
|
from temporalio import workflow
|
|
21
21
|
|
|
22
22
|
with workflow.unsafe.imports_passed_through():
|
|
23
|
-
from datasets import Dataset
|
|
23
|
+
from datasets import Dataset # type: ignore
|
|
24
24
|
|
|
25
|
+
# Assuming run_local_workflow is correctly placed and importable
|
|
25
26
|
from flock.core.execution.local_executor import (
|
|
26
27
|
run_local_workflow,
|
|
27
28
|
)
|
|
28
29
|
from opentelemetry import trace
|
|
29
30
|
from opentelemetry.baggage import get_baggage, set_baggage
|
|
30
|
-
from pandas import DataFrame
|
|
31
|
+
from pandas import DataFrame # type: ignore
|
|
31
32
|
from pydantic import BaseModel, Field
|
|
32
33
|
|
|
33
34
|
# Flock core components & utilities
|
|
34
35
|
from flock.config import DEFAULT_MODEL, TELEMETRY
|
|
35
|
-
from flock.core.api.custom_endpoint import
|
|
36
|
+
from flock.core.api.custom_endpoint import (
|
|
37
|
+
FlockEndpoint, # Keep for type hinting custom_endpoints
|
|
38
|
+
)
|
|
36
39
|
from flock.core.context.context import FlockContext
|
|
37
40
|
from flock.core.context.context_manager import initialize_context
|
|
41
|
+
|
|
42
|
+
# Assuming run_temporal_workflow is correctly placed and importable
|
|
38
43
|
from flock.core.execution.temporal_executor import run_temporal_workflow
|
|
39
|
-
from flock.core.flock_evaluator import FlockEvaluator
|
|
44
|
+
from flock.core.flock_evaluator import FlockEvaluator # For type hint
|
|
40
45
|
from flock.core.logging.logging import LOGGERS, get_logger, get_module_loggers
|
|
41
46
|
from flock.core.serialization.serializable import Serializable
|
|
42
47
|
from flock.core.util.cli_helper import init_console
|
|
@@ -52,14 +57,14 @@ if TYPE_CHECKING:
|
|
|
52
57
|
from flock.core.flock_registry import get_registry
|
|
53
58
|
|
|
54
59
|
try:
|
|
55
|
-
import pandas as pd
|
|
60
|
+
import pandas as pd # type: ignore
|
|
56
61
|
|
|
57
62
|
PANDAS_AVAILABLE = True
|
|
58
63
|
except ImportError:
|
|
59
|
-
pd = None
|
|
64
|
+
pd = None # type: ignore
|
|
60
65
|
PANDAS_AVAILABLE = False
|
|
61
66
|
|
|
62
|
-
logger = get_logger("flock")
|
|
67
|
+
logger = get_logger("flock.api")
|
|
63
68
|
TELEMETRY.setup_tracing() # Setup OpenTelemetry
|
|
64
69
|
tracer = trace.get_tracer(__name__)
|
|
65
70
|
FlockRegistry = get_registry() # Get the registry instance
|
|
@@ -67,10 +72,6 @@ FlockRegistry = get_registry() # Get the registry instance
|
|
|
67
72
|
# Define TypeVar for generic class methods like from_dict
|
|
68
73
|
T = TypeVar("T", bound="Flock")
|
|
69
74
|
|
|
70
|
-
# from rich.traceback import install
|
|
71
|
-
|
|
72
|
-
# install(show_locals=True)
|
|
73
|
-
|
|
74
75
|
|
|
75
76
|
class Flock(BaseModel, Serializable):
|
|
76
77
|
"""Orchestrator for managing and executing agent systems.
|
|
@@ -115,6 +116,7 @@ class Flock(BaseModel, Serializable):
|
|
|
115
116
|
description="If True (default) and enable_temporal=True, start a temporary in-process worker for development/testing convenience. Set to False when using dedicated workers.",
|
|
116
117
|
)
|
|
117
118
|
# Internal agent storage - not part of the Pydantic model for direct serialization
|
|
119
|
+
# Marked with underscore to indicate it's managed internally and accessed via property
|
|
118
120
|
_agents: dict[str, FlockAgent]
|
|
119
121
|
_start_agent_name: str | None = None # For potential pre-configuration
|
|
120
122
|
_start_input: dict = {} # Instance attribute overwritten in __init__; kept for typing clarity
|
|
@@ -122,6 +124,7 @@ class Flock(BaseModel, Serializable):
|
|
|
122
124
|
# Pydantic v2 model config
|
|
123
125
|
model_config = {
|
|
124
126
|
"arbitrary_types_allowed": True,
|
|
127
|
+
# Assuming FlockRegistry type might not be serializable by default
|
|
125
128
|
"ignored_types": (type(FlockRegistry),),
|
|
126
129
|
}
|
|
127
130
|
|
|
@@ -148,7 +151,7 @@ class Flock(BaseModel, Serializable):
|
|
|
148
151
|
model=model,
|
|
149
152
|
description=description,
|
|
150
153
|
enable_temporal=enable_temporal,
|
|
151
|
-
enable_logging=enable_logging,
|
|
154
|
+
enable_logging=bool(enable_logging), # Store as bool, specific loggers handled by _configure
|
|
152
155
|
show_flock_banner=show_flock_banner,
|
|
153
156
|
temporal_config=temporal_config,
|
|
154
157
|
temporal_start_in_process_worker=temporal_start_in_process_worker,
|
|
@@ -161,11 +164,13 @@ class Flock(BaseModel, Serializable):
|
|
|
161
164
|
self._start_input = {}
|
|
162
165
|
|
|
163
166
|
# Set up logging based on the enable_logging flag
|
|
164
|
-
self._configure_logging(enable_logging)
|
|
167
|
+
self._configure_logging(enable_logging) # Pass original value to _configure_logging
|
|
165
168
|
|
|
166
169
|
# Register passed agents
|
|
167
170
|
if agents:
|
|
168
|
-
from flock.core.flock_agent import
|
|
171
|
+
from flock.core.flock_agent import (
|
|
172
|
+
FlockAgent as ConcreteFlockAgent, # Local import
|
|
173
|
+
)
|
|
169
174
|
|
|
170
175
|
for agent in agents:
|
|
171
176
|
if isinstance(agent, ConcreteFlockAgent):
|
|
@@ -176,7 +181,8 @@ class Flock(BaseModel, Serializable):
|
|
|
176
181
|
)
|
|
177
182
|
|
|
178
183
|
# Initialize console if needed for banner
|
|
179
|
-
|
|
184
|
+
if self.show_flock_banner: # Check instance attribute
|
|
185
|
+
init_console(clear_screen=True, show_banner=self.show_flock_banner)
|
|
180
186
|
|
|
181
187
|
# Set Temporal debug environment variable
|
|
182
188
|
self._set_temporal_debug_flag()
|
|
@@ -191,33 +197,37 @@ class Flock(BaseModel, Serializable):
|
|
|
191
197
|
enable_temporal=self.enable_temporal,
|
|
192
198
|
)
|
|
193
199
|
|
|
194
|
-
def _configure_logging(self,
|
|
200
|
+
def _configure_logging(self, enable_logging_config: bool | list[str]):
|
|
195
201
|
"""Configure logging levels based on the enable_logging flag."""
|
|
196
202
|
is_enabled_globally = False
|
|
197
|
-
|
|
203
|
+
specific_loggers_to_enable = []
|
|
198
204
|
|
|
199
|
-
if isinstance(
|
|
200
|
-
is_enabled_globally =
|
|
201
|
-
elif isinstance(
|
|
202
|
-
is_enabled_globally = bool(
|
|
203
|
-
|
|
205
|
+
if isinstance(enable_logging_config, bool):
|
|
206
|
+
is_enabled_globally = enable_logging_config
|
|
207
|
+
elif isinstance(enable_logging_config, list):
|
|
208
|
+
is_enabled_globally = bool(enable_logging_config) # True if list is not empty
|
|
209
|
+
specific_loggers_to_enable = enable_logging_config
|
|
204
210
|
|
|
205
211
|
# Configure core loggers
|
|
206
|
-
for log_name in LOGGERS:
|
|
212
|
+
for log_name in LOGGERS: # Assuming LOGGERS is a list of known logger names
|
|
207
213
|
log_instance = get_logger(log_name)
|
|
208
|
-
if is_enabled_globally or log_name in
|
|
214
|
+
if is_enabled_globally or log_name in specific_loggers_to_enable:
|
|
209
215
|
log_instance.enable_logging = True
|
|
210
216
|
else:
|
|
211
217
|
log_instance.enable_logging = False
|
|
212
218
|
|
|
213
219
|
# Configure module loggers (existing ones)
|
|
214
|
-
module_loggers = get_module_loggers()
|
|
220
|
+
module_loggers = get_module_loggers() # Assuming this returns list of FlockLogger instances
|
|
215
221
|
for mod_log in module_loggers:
|
|
216
|
-
if is_enabled_globally or mod_log.name in
|
|
222
|
+
if is_enabled_globally or mod_log.name in specific_loggers_to_enable:
|
|
217
223
|
mod_log.enable_logging = True
|
|
218
224
|
else:
|
|
219
225
|
mod_log.enable_logging = False
|
|
220
226
|
|
|
227
|
+
# Update the instance's Pydantic field
|
|
228
|
+
self.enable_logging = is_enabled_globally or bool(specific_loggers_to_enable)
|
|
229
|
+
|
|
230
|
+
|
|
221
231
|
def _set_temporal_debug_flag(self):
|
|
222
232
|
"""Set or remove LOCAL_DEBUG env var based on enable_temporal."""
|
|
223
233
|
if not self.enable_temporal:
|
|
@@ -242,7 +252,9 @@ class Flock(BaseModel, Serializable):
|
|
|
242
252
|
|
|
243
253
|
def add_agent(self, agent: FlockAgent) -> FlockAgent:
|
|
244
254
|
"""Adds an agent instance to this Flock configuration and registry."""
|
|
245
|
-
from flock.core.flock_agent import
|
|
255
|
+
from flock.core.flock_agent import (
|
|
256
|
+
FlockAgent as ConcreteFlockAgent, # Local import
|
|
257
|
+
)
|
|
246
258
|
|
|
247
259
|
if not isinstance(agent, ConcreteFlockAgent):
|
|
248
260
|
raise TypeError("Provided object is not a FlockAgent instance.")
|
|
@@ -250,7 +262,13 @@ class Flock(BaseModel, Serializable):
|
|
|
250
262
|
raise ValueError("Agent must have a name.")
|
|
251
263
|
|
|
252
264
|
if agent.name in self._agents:
|
|
253
|
-
raise
|
|
265
|
+
# Allow re-adding the same instance, but raise error for different instance with same name
|
|
266
|
+
if self._agents[agent.name] is not agent:
|
|
267
|
+
raise ValueError(f"Agent with name '{agent.name}' already exists with a different instance.")
|
|
268
|
+
else:
|
|
269
|
+
logger.debug(f"Agent '{agent.name}' is already added. Skipping.")
|
|
270
|
+
return agent # Return existing agent
|
|
271
|
+
|
|
254
272
|
self._agents[agent.name] = agent
|
|
255
273
|
FlockRegistry.register_agent(agent) # Register globally
|
|
256
274
|
|
|
@@ -286,7 +304,6 @@ class Flock(BaseModel, Serializable):
|
|
|
286
304
|
"""Entry point for running an agent system synchronously."""
|
|
287
305
|
try:
|
|
288
306
|
loop = asyncio.get_running_loop()
|
|
289
|
-
# If loop exists, check if it's closed
|
|
290
307
|
if loop.is_closed():
|
|
291
308
|
raise RuntimeError("Event loop is closed")
|
|
292
309
|
except RuntimeError: # No running loop
|
|
@@ -307,6 +324,12 @@ class Flock(BaseModel, Serializable):
|
|
|
307
324
|
)
|
|
308
325
|
return result
|
|
309
326
|
else:
|
|
327
|
+
# If called from an already running loop (e.g. Jupyter, FastAPI endpoint)
|
|
328
|
+
# Create a future and run it to completion. This is tricky and
|
|
329
|
+
# ideally, one should use `await self.run_async` directly in async contexts.
|
|
330
|
+
# This simple run_until_complete on a future might block the existing loop.
|
|
331
|
+
# For truly non-blocking execution in an existing loop, one might need
|
|
332
|
+
# to schedule the coroutine differently or advise users to use `await`.
|
|
310
333
|
future = asyncio.ensure_future(
|
|
311
334
|
self.run_async(
|
|
312
335
|
start_agent=start_agent,
|
|
@@ -319,6 +342,7 @@ class Flock(BaseModel, Serializable):
|
|
|
319
342
|
)
|
|
320
343
|
return loop.run_until_complete(future)
|
|
321
344
|
|
|
345
|
+
|
|
322
346
|
async def run_async(
|
|
323
347
|
self,
|
|
324
348
|
start_agent: FlockAgent | str | None = None,
|
|
@@ -330,8 +354,9 @@ class Flock(BaseModel, Serializable):
|
|
|
330
354
|
memo: dict[str, Any] | None = None,
|
|
331
355
|
) -> Box | dict:
|
|
332
356
|
"""Entry point for running an agent system asynchronously."""
|
|
333
|
-
|
|
334
|
-
|
|
357
|
+
from flock.core.flock_agent import (
|
|
358
|
+
FlockAgent as ConcreteFlockAgent, # Local import
|
|
359
|
+
)
|
|
335
360
|
|
|
336
361
|
with tracer.start_as_current_span("flock.run_async") as span:
|
|
337
362
|
# Add passed agents first
|
|
@@ -348,11 +373,11 @@ class Flock(BaseModel, Serializable):
|
|
|
348
373
|
start_agent_name: str | None = None
|
|
349
374
|
if isinstance(start_agent, ConcreteFlockAgent):
|
|
350
375
|
start_agent_name = start_agent.name
|
|
351
|
-
if start_agent_name not in self._agents:
|
|
376
|
+
if start_agent_name not in self._agents: # Add if not already present
|
|
352
377
|
self.add_agent(start_agent)
|
|
353
378
|
elif isinstance(start_agent, str):
|
|
354
379
|
start_agent_name = start_agent
|
|
355
|
-
else:
|
|
380
|
+
else: # start_agent is None
|
|
356
381
|
start_agent_name = self._start_agent_name
|
|
357
382
|
|
|
358
383
|
# Default to first agent if only one exists and none specified
|
|
@@ -360,7 +385,7 @@ class Flock(BaseModel, Serializable):
|
|
|
360
385
|
start_agent_name = list(self._agents.keys())[0]
|
|
361
386
|
elif not start_agent_name:
|
|
362
387
|
raise ValueError(
|
|
363
|
-
"No start_agent specified and multiple/no agents exist."
|
|
388
|
+
"No start_agent specified and multiple/no agents exist in the Flock instance."
|
|
364
389
|
)
|
|
365
390
|
|
|
366
391
|
# Check if start_agent is in agents
|
|
@@ -390,41 +415,31 @@ class Flock(BaseModel, Serializable):
|
|
|
390
415
|
|
|
391
416
|
try:
|
|
392
417
|
resolved_start_agent = self._agents.get(start_agent_name)
|
|
393
|
-
if not resolved_start_agent:
|
|
394
|
-
|
|
395
|
-
start_agent_name
|
|
396
|
-
)
|
|
397
|
-
if not resolved_start_agent:
|
|
398
|
-
raise ValueError(
|
|
399
|
-
f"Start agent '{start_agent_name}' not found."
|
|
400
|
-
)
|
|
401
|
-
self.add_agent(resolved_start_agent)
|
|
418
|
+
if not resolved_start_agent: # Should have been handled by now
|
|
419
|
+
raise ValueError(f"Start agent '{start_agent_name}' not found after checks.")
|
|
402
420
|
|
|
403
421
|
run_context = context if context else FlockContext()
|
|
404
|
-
set_baggage("run_id", effective_run_id)
|
|
422
|
+
set_baggage("run_id", effective_run_id) # Set for OpenTelemetry
|
|
405
423
|
|
|
406
424
|
initialize_context(
|
|
407
425
|
run_context,
|
|
408
426
|
start_agent_name,
|
|
409
427
|
run_input,
|
|
410
428
|
effective_run_id,
|
|
411
|
-
not self.enable_temporal,
|
|
429
|
+
not self.enable_temporal, # local_debug is inverse of enable_temporal
|
|
412
430
|
self.model or resolved_start_agent.model or DEFAULT_MODEL,
|
|
413
431
|
)
|
|
414
432
|
# Add agent definitions to context for routing/serialization within workflow
|
|
415
|
-
for
|
|
416
|
-
# Agents
|
|
417
|
-
agent_dict_repr = agent_instance.to_dict()
|
|
433
|
+
for agent_name_iter, agent_instance_iter in self.agents.items():
|
|
434
|
+
agent_dict_repr = agent_instance_iter.to_dict() # Agents handle their own serialization
|
|
418
435
|
run_context.add_agent_definition(
|
|
419
|
-
agent_type=type(
|
|
420
|
-
agent_name=
|
|
421
|
-
agent_data=agent_dict_repr,
|
|
436
|
+
agent_type=type(agent_instance_iter),
|
|
437
|
+
agent_name=agent_name_iter,
|
|
438
|
+
agent_data=agent_dict_repr,
|
|
422
439
|
)
|
|
423
440
|
|
|
424
441
|
# Add temporal config to context if enabled
|
|
425
442
|
if self.enable_temporal and self.temporal_config:
|
|
426
|
-
# Store the workflow config dict for the executor/workflow to use
|
|
427
|
-
# Using a specific key to avoid potential clashes in state
|
|
428
443
|
run_context.set_variable(
|
|
429
444
|
"flock.temporal_workflow_config",
|
|
430
445
|
self.temporal_config.model_dump(mode="json"),
|
|
@@ -439,17 +454,13 @@ class Flock(BaseModel, Serializable):
|
|
|
439
454
|
# Execute workflow
|
|
440
455
|
if not self.enable_temporal:
|
|
441
456
|
result = await run_local_workflow(
|
|
442
|
-
run_context, box_result=False
|
|
457
|
+
run_context, box_result=False # Boxing handled below
|
|
443
458
|
)
|
|
444
459
|
else:
|
|
445
|
-
# Pass the Flock instance itself to the executor
|
|
446
|
-
# so it can access the temporal_config directly if needed
|
|
447
|
-
# This avoids putting potentially large/complex config objects
|
|
448
|
-
# directly into the context state that gets passed around.
|
|
449
460
|
result = await run_temporal_workflow(
|
|
450
|
-
self,
|
|
461
|
+
self, # Pass the Flock instance
|
|
451
462
|
run_context,
|
|
452
|
-
box_result=False,
|
|
463
|
+
box_result=False, # Boxing handled below
|
|
453
464
|
memo=memo,
|
|
454
465
|
)
|
|
455
466
|
|
|
@@ -465,9 +476,9 @@ class Flock(BaseModel, Serializable):
|
|
|
465
476
|
try:
|
|
466
477
|
logger.debug("Boxing final result.")
|
|
467
478
|
return Box(result)
|
|
468
|
-
except ImportError:
|
|
479
|
+
except ImportError: # Should not happen as Box is a direct dependency
|
|
469
480
|
logger.warning(
|
|
470
|
-
"Box library not installed, returning raw dict."
|
|
481
|
+
"Box library not installed (should be direct dependency), returning raw dict."
|
|
471
482
|
)
|
|
472
483
|
return result
|
|
473
484
|
else:
|
|
@@ -479,10 +490,15 @@ class Flock(BaseModel, Serializable):
|
|
|
479
490
|
)
|
|
480
491
|
span.record_exception(e)
|
|
481
492
|
span.set_status(trace.Status(trace.StatusCode.ERROR, str(e)))
|
|
482
|
-
|
|
493
|
+
# Return a consistent error structure
|
|
494
|
+
error_output = {
|
|
483
495
|
"error": str(e),
|
|
484
496
|
"details": f"Flock run '{self.name}' failed.",
|
|
497
|
+
"run_id": effective_run_id,
|
|
498
|
+
"start_agent": start_agent_name,
|
|
485
499
|
}
|
|
500
|
+
return Box(error_output) if box_result else error_output
|
|
501
|
+
|
|
486
502
|
|
|
487
503
|
# --- Batch Processing (Delegation) ---
|
|
488
504
|
async def run_batch_async(
|
|
@@ -539,7 +555,6 @@ class Flock(BaseModel, Serializable):
|
|
|
539
555
|
delimiter: str = ",",
|
|
540
556
|
) -> list[Box | dict | None | Exception]:
|
|
541
557
|
"""Synchronous wrapper for run_batch_async."""
|
|
542
|
-
# (Standard asyncio run wrapper logic)
|
|
543
558
|
try:
|
|
544
559
|
loop = asyncio.get_running_loop()
|
|
545
560
|
if loop.is_closed():
|
|
@@ -574,15 +589,15 @@ class Flock(BaseModel, Serializable):
|
|
|
574
589
|
# --- Evaluation (Delegation) ---
|
|
575
590
|
async def evaluate_async(
|
|
576
591
|
self,
|
|
577
|
-
dataset: str | Path | list[dict[str, Any]] | DataFrame | Dataset,
|
|
592
|
+
dataset: str | Path | list[dict[str, Any]] | DataFrame | Dataset, # type: ignore
|
|
578
593
|
start_agent: FlockAgent | str,
|
|
579
594
|
input_mapping: dict[str, str],
|
|
580
595
|
answer_mapping: dict[str, str],
|
|
581
596
|
metrics: list[
|
|
582
597
|
str
|
|
583
598
|
| Callable[[Any, Any], bool | float | dict[str, Any]]
|
|
584
|
-
| FlockAgent
|
|
585
|
-
| FlockEvaluator
|
|
599
|
+
| FlockAgent # Type hint only
|
|
600
|
+
| FlockEvaluator # Type hint only
|
|
586
601
|
],
|
|
587
602
|
metric_configs: dict[str, dict[str, Any]] | None = None,
|
|
588
603
|
static_inputs: dict[str, Any] | None = None,
|
|
@@ -594,7 +609,7 @@ class Flock(BaseModel, Serializable):
|
|
|
594
609
|
return_dataframe: bool = True,
|
|
595
610
|
silent_mode: bool = False,
|
|
596
611
|
metadata_columns: list[str] | None = None,
|
|
597
|
-
) -> DataFrame | list[dict[str, Any]]:
|
|
612
|
+
) -> DataFrame | list[dict[str, Any]]: # type: ignore
|
|
598
613
|
"""Evaluates the Flock's performance against a dataset (delegated)."""
|
|
599
614
|
# Import processor locally
|
|
600
615
|
from flock.core.execution.evaluation_executor import (
|
|
@@ -622,15 +637,15 @@ class Flock(BaseModel, Serializable):
|
|
|
622
637
|
|
|
623
638
|
def evaluate(
|
|
624
639
|
self,
|
|
625
|
-
dataset: str | Path | list[dict[str, Any]] | DataFrame | Dataset,
|
|
640
|
+
dataset: str | Path | list[dict[str, Any]] | DataFrame | Dataset, # type: ignore
|
|
626
641
|
start_agent: FlockAgent | str,
|
|
627
642
|
input_mapping: dict[str, str],
|
|
628
643
|
answer_mapping: dict[str, str],
|
|
629
644
|
metrics: list[
|
|
630
645
|
str
|
|
631
646
|
| Callable[[Any, Any], bool | float | dict[str, Any]]
|
|
632
|
-
| FlockAgent
|
|
633
|
-
| FlockEvaluator
|
|
647
|
+
| FlockAgent # Type hint only
|
|
648
|
+
| FlockEvaluator # Type hint only
|
|
634
649
|
],
|
|
635
650
|
metric_configs: dict[str, dict[str, Any]] | None = None,
|
|
636
651
|
static_inputs: dict[str, Any] | None = None,
|
|
@@ -642,9 +657,8 @@ class Flock(BaseModel, Serializable):
|
|
|
642
657
|
return_dataframe: bool = True,
|
|
643
658
|
silent_mode: bool = False,
|
|
644
659
|
metadata_columns: list[str] | None = None,
|
|
645
|
-
) -> DataFrame | list[dict[str, Any]]:
|
|
660
|
+
) -> DataFrame | list[dict[str, Any]]: # type: ignore
|
|
646
661
|
"""Synchronous wrapper for evaluate_async."""
|
|
647
|
-
# (Standard asyncio run wrapper logic)
|
|
648
662
|
try:
|
|
649
663
|
loop = asyncio.get_running_loop()
|
|
650
664
|
if loop.is_closed():
|
|
@@ -678,103 +692,148 @@ class Flock(BaseModel, Serializable):
|
|
|
678
692
|
future = asyncio.ensure_future(coro)
|
|
679
693
|
return loop.run_until_complete(future)
|
|
680
694
|
|
|
681
|
-
# ---
|
|
695
|
+
# --- Server & CLI Starters (Delegation) ---
|
|
682
696
|
def start_api(
|
|
683
697
|
self,
|
|
684
698
|
host: str = "127.0.0.1",
|
|
685
699
|
port: int = 8344,
|
|
686
|
-
server_name: str = "Flock
|
|
687
|
-
create_ui: bool =
|
|
700
|
+
server_name: str = "Flock Server",
|
|
701
|
+
create_ui: bool = True, # Default to True for the integrated experience
|
|
688
702
|
ui_theme: str | None = None,
|
|
689
703
|
custom_endpoints: Sequence[FlockEndpoint] | dict[tuple[str, list[str] | None], Callable[..., Any]] | None = None,
|
|
690
704
|
) -> None:
|
|
691
|
-
"""Starts a REST API server for this Flock instance.
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
705
|
+
"""Starts a unified REST API server and/or Web UI for this Flock instance."""
|
|
706
|
+
import warnings
|
|
707
|
+
warnings.warn(
|
|
708
|
+
"start_api() is deprecated and will be removed in a future release. "
|
|
709
|
+
"Use serve() instead.",
|
|
710
|
+
DeprecationWarning,
|
|
711
|
+
stacklevel=2,
|
|
712
|
+
)
|
|
713
|
+
# Delegate to the new serve() method (create_ui maps to ui)
|
|
714
|
+
return self.serve(
|
|
715
|
+
host=host,
|
|
716
|
+
port=port,
|
|
717
|
+
server_name=server_name,
|
|
718
|
+
ui=create_ui,
|
|
719
|
+
ui_theme=ui_theme,
|
|
720
|
+
custom_endpoints=custom_endpoints,
|
|
721
|
+
)
|
|
699
722
|
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
# We need a function similar to start_flock_api but for the integrated app
|
|
704
|
-
# Let's assume it exists in webapp.run for now, called start_integrated_server
|
|
705
|
-
from flock.webapp.run import start_integrated_server
|
|
706
|
-
|
|
707
|
-
start_integrated_server(
|
|
708
|
-
flock_instance=self,
|
|
709
|
-
host=host,
|
|
710
|
-
port=port,
|
|
711
|
-
server_name=server_name,
|
|
712
|
-
theme_name=ui_theme,
|
|
713
|
-
)
|
|
714
|
-
except ImportError:
|
|
715
|
-
# Log error - cannot start integrated UI
|
|
716
|
-
from flock.core.logging.logging import get_logger
|
|
723
|
+
# ------------------------------------------------------------------
|
|
724
|
+
# New preferred method name
|
|
725
|
+
# ------------------------------------------------------------------
|
|
717
726
|
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
727
|
+
def serve(
|
|
728
|
+
self,
|
|
729
|
+
host: str = "127.0.0.1",
|
|
730
|
+
port: int = 8344,
|
|
731
|
+
server_name: str = "Flock Server",
|
|
732
|
+
ui: bool = True,
|
|
733
|
+
chat: bool = False,
|
|
734
|
+
chat_agent: str | None = None, # Reserved for future real agent chat
|
|
735
|
+
chat_message_key: str = "message",
|
|
736
|
+
chat_history_key: str = "history",
|
|
737
|
+
chat_response_key: str = "response",
|
|
738
|
+
ui_theme: str | None = None,
|
|
739
|
+
custom_endpoints: Sequence[FlockEndpoint] | dict[tuple[str, list[str] | None], Callable[..., Any]] | None = None,
|
|
740
|
+
) -> None:
|
|
741
|
+
"""Launch an HTTP server that exposes the core REST API and, optionally, the
|
|
742
|
+
browser-based UI.
|
|
743
|
+
|
|
744
|
+
Args:
|
|
745
|
+
host: Bind address for the server (default "127.0.0.1").
|
|
746
|
+
port: TCP port to listen on (default 8344).
|
|
747
|
+
server_name: Title shown in the OpenAPI docs / logs.
|
|
748
|
+
ui: If True (default) the Pico/HTMX web UI routes are included. If False
|
|
749
|
+
only the JSON API groups (core & custom) are served.
|
|
750
|
+
chat: If True, enable chat routes.
|
|
751
|
+
chat_agent: Name of the agent to use for chat.
|
|
752
|
+
chat_message_key: Key for chat message in input.
|
|
753
|
+
chat_history_key: Key for chat history in input.
|
|
754
|
+
chat_response_key: Key for chat response in output.
|
|
755
|
+
ui_theme: Optional UI theme name or "random".
|
|
756
|
+
custom_endpoints: Additional API routes to add, either as a list of
|
|
757
|
+
FlockEndpoint objects or the legacy dict format.
|
|
758
|
+
"""
|
|
759
|
+
try:
|
|
760
|
+
from flock.webapp.run import start_unified_server
|
|
761
|
+
except ImportError:
|
|
762
|
+
logger.error(
|
|
763
|
+
"Web application components not found (flock.webapp.run). "
|
|
764
|
+
"Cannot start HTTP server. Ensure webapp dependencies are installed."
|
|
740
765
|
)
|
|
766
|
+
return
|
|
767
|
+
|
|
768
|
+
logger.info(
|
|
769
|
+
f"Attempting to start server for Flock '{self.name}' on {host}:{port}. UI enabled: {ui}"
|
|
770
|
+
)
|
|
771
|
+
|
|
772
|
+
start_unified_server(
|
|
773
|
+
flock_instance=self,
|
|
774
|
+
host=host,
|
|
775
|
+
port=port,
|
|
776
|
+
server_title=server_name,
|
|
777
|
+
enable_ui_routes=ui,
|
|
778
|
+
enable_chat_routes=chat,
|
|
779
|
+
ui_theme=ui_theme,
|
|
780
|
+
custom_endpoints=custom_endpoints,
|
|
781
|
+
)
|
|
741
782
|
|
|
742
|
-
# --- CLI Starter ---
|
|
743
783
|
def start_cli(
|
|
744
784
|
self,
|
|
785
|
+
start_agent: FlockAgent | str | None = None, # Added start_agent to match method signature in file_26
|
|
745
786
|
server_name: str = "Flock CLI",
|
|
746
787
|
show_results: bool = False,
|
|
747
788
|
edit_mode: bool = False,
|
|
748
789
|
) -> None:
|
|
749
790
|
"""Starts an interactive CLI for this Flock instance."""
|
|
750
791
|
# Import runner locally
|
|
751
|
-
|
|
792
|
+
try:
|
|
793
|
+
from flock.cli.runner import start_flock_cli
|
|
794
|
+
except ImportError:
|
|
795
|
+
logger.error(
|
|
796
|
+
"CLI components not found. Cannot start CLI. "
|
|
797
|
+
"Ensure CLI dependencies are installed."
|
|
798
|
+
)
|
|
799
|
+
return
|
|
800
|
+
|
|
801
|
+
# The start_flock_cli function in file_50 doesn't take start_agent
|
|
802
|
+
# but the original docs for start_cli did.
|
|
803
|
+
# For now, I'll pass it through, assuming start_flock_cli will be updated or ignore it.
|
|
804
|
+
# If start_agent is crucial here, start_flock_cli needs to handle it.
|
|
805
|
+
logger.info(f"Starting CLI for Flock '{self.name}'...")
|
|
806
|
+
start_flock_cli(
|
|
807
|
+
flock=self, # Pass the Flock instance
|
|
808
|
+
# start_agent=start_agent, # This argument is not in the definition of start_flock_cli in file_50
|
|
809
|
+
server_name=server_name,
|
|
810
|
+
show_results=show_results,
|
|
811
|
+
edit_mode=edit_mode
|
|
812
|
+
)
|
|
752
813
|
|
|
753
|
-
start_flock_cli(self, server_name, show_results, edit_mode)
|
|
754
814
|
|
|
755
815
|
# --- Serialization Delegation Methods ---
|
|
756
816
|
def to_dict(self, path_type: str = "relative") -> dict[str, Any]:
|
|
757
817
|
"""Serialize Flock instance to dictionary using FlockSerializer."""
|
|
758
|
-
# Import locally to prevent circular imports at module level if structure is complex
|
|
759
818
|
from flock.core.serialization.flock_serializer import FlockSerializer
|
|
760
819
|
|
|
761
|
-
# Assuming FlockSerializer handles the nested temporal_config serialization
|
|
762
820
|
return FlockSerializer.serialize(self, path_type=path_type)
|
|
763
821
|
|
|
764
822
|
@classmethod
|
|
765
823
|
def from_dict(cls: type[T], data: dict[str, Any]) -> T:
|
|
766
824
|
"""Deserialize Flock instance from dictionary using FlockSerializer."""
|
|
767
|
-
# Import locally
|
|
768
825
|
from flock.core.serialization.flock_serializer import FlockSerializer
|
|
769
826
|
|
|
770
|
-
# Assuming FlockSerializer handles the nested temporal_config deserialization
|
|
771
827
|
return FlockSerializer.deserialize(cls, data)
|
|
772
828
|
|
|
773
829
|
# --- Static Method Loader (Delegates to loader module) ---
|
|
774
830
|
@staticmethod
|
|
775
|
-
def load_from_file(file_path: str) -> Flock:
|
|
831
|
+
def load_from_file(file_path: str) -> Flock: # Ensure return type is Flock
|
|
776
832
|
"""Load a Flock instance from various file formats (delegates to loader)."""
|
|
777
|
-
# Import locally
|
|
778
833
|
from flock.core.util.loader import load_flock_from_file
|
|
779
834
|
|
|
780
|
-
|
|
835
|
+
loaded_flock = load_flock_from_file(file_path)
|
|
836
|
+
# Ensure the loaded object is indeed a Flock instance
|
|
837
|
+
if not isinstance(loaded_flock, Flock):
|
|
838
|
+
raise TypeError(f"Loaded object from {file_path} is not a Flock instance, but {type(loaded_flock)}")
|
|
839
|
+
return loaded_flock
|