flock-core 0.5.0b28__py3-none-any.whl → 0.5.56b0__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/__init__.py +12 -217
- flock/agent.py +678 -0
- flock/api/themes.py +71 -0
- flock/artifacts.py +79 -0
- flock/cli.py +75 -0
- flock/components.py +173 -0
- flock/dashboard/__init__.py +28 -0
- flock/dashboard/collector.py +283 -0
- flock/dashboard/events.py +182 -0
- flock/dashboard/launcher.py +230 -0
- flock/dashboard/service.py +537 -0
- flock/dashboard/websocket.py +235 -0
- flock/engines/__init__.py +6 -0
- flock/engines/dspy_engine.py +856 -0
- flock/examples.py +128 -0
- flock/{core/util → helper}/cli_helper.py +4 -3
- flock/{core/logging → logging}/__init__.py +2 -3
- flock/{core/logging → logging}/formatters/enum_builder.py +3 -4
- flock/{core/logging → logging}/formatters/theme_builder.py +19 -44
- flock/{core/logging → logging}/formatters/themed_formatter.py +69 -115
- flock/{core/logging → logging}/logging.py +77 -61
- flock/{core/logging → logging}/telemetry.py +20 -26
- flock/{core/logging → logging}/telemetry_exporter/base_exporter.py +2 -2
- flock/{core/logging → logging}/telemetry_exporter/file_exporter.py +6 -9
- flock/{core/logging → logging}/telemetry_exporter/sqlite_exporter.py +2 -3
- flock/{core/logging → logging}/trace_and_logged.py +20 -24
- flock/mcp/__init__.py +91 -0
- flock/{core/mcp/mcp_client.py → mcp/client.py} +103 -154
- flock/{core/mcp/mcp_config.py → mcp/config.py} +62 -117
- flock/mcp/manager.py +255 -0
- flock/mcp/servers/sse/__init__.py +1 -1
- flock/mcp/servers/sse/flock_sse_server.py +11 -53
- flock/mcp/servers/stdio/__init__.py +1 -1
- flock/mcp/servers/stdio/flock_stdio_server.py +8 -48
- flock/mcp/servers/streamable_http/flock_streamable_http_server.py +17 -62
- flock/mcp/servers/websockets/flock_websocket_server.py +7 -40
- flock/{core/mcp/flock_mcp_tool.py → mcp/tool.py} +16 -26
- flock/mcp/types/__init__.py +42 -0
- flock/{core/mcp → mcp}/types/callbacks.py +9 -15
- flock/{core/mcp → mcp}/types/factories.py +7 -6
- flock/{core/mcp → mcp}/types/handlers.py +13 -18
- flock/{core/mcp → mcp}/types/types.py +70 -74
- flock/{core/mcp → mcp}/util/helpers.py +1 -1
- flock/orchestrator.py +645 -0
- flock/registry.py +148 -0
- flock/runtime.py +262 -0
- flock/service.py +140 -0
- flock/store.py +69 -0
- flock/subscription.py +111 -0
- flock/themes/andromeda.toml +1 -1
- flock/themes/apple-system-colors.toml +1 -1
- flock/themes/arcoiris.toml +1 -1
- flock/themes/atomonelight.toml +1 -1
- flock/themes/ayu copy.toml +1 -1
- flock/themes/ayu-light.toml +1 -1
- flock/themes/belafonte-day.toml +1 -1
- flock/themes/belafonte-night.toml +1 -1
- flock/themes/blulocodark.toml +1 -1
- flock/themes/breeze.toml +1 -1
- flock/themes/broadcast.toml +1 -1
- flock/themes/brogrammer.toml +1 -1
- flock/themes/builtin-dark.toml +1 -1
- flock/themes/builtin-pastel-dark.toml +1 -1
- flock/themes/catppuccin-latte.toml +1 -1
- flock/themes/catppuccin-macchiato.toml +1 -1
- flock/themes/catppuccin-mocha.toml +1 -1
- flock/themes/cga.toml +1 -1
- flock/themes/chalk.toml +1 -1
- flock/themes/ciapre.toml +1 -1
- flock/themes/coffee-theme.toml +1 -1
- flock/themes/cyberpunkscarletprotocol.toml +1 -1
- flock/themes/dark+.toml +1 -1
- flock/themes/darkermatrix.toml +1 -1
- flock/themes/darkside.toml +1 -1
- flock/themes/desert.toml +1 -1
- flock/themes/django.toml +1 -1
- flock/themes/djangosmooth.toml +1 -1
- flock/themes/doomone.toml +1 -1
- flock/themes/dotgov.toml +1 -1
- flock/themes/dracula+.toml +1 -1
- flock/themes/duckbones.toml +1 -1
- flock/themes/encom.toml +1 -1
- flock/themes/espresso.toml +1 -1
- flock/themes/everblush.toml +1 -1
- flock/themes/fairyfloss.toml +1 -1
- flock/themes/fideloper.toml +1 -1
- flock/themes/fishtank.toml +1 -1
- flock/themes/flexoki-light.toml +1 -1
- flock/themes/floraverse.toml +1 -1
- flock/themes/framer.toml +1 -1
- flock/themes/galizur.toml +1 -1
- flock/themes/github.toml +1 -1
- flock/themes/grass.toml +1 -1
- flock/themes/grey-green.toml +1 -1
- flock/themes/gruvboxlight.toml +1 -1
- flock/themes/guezwhoz.toml +1 -1
- flock/themes/harper.toml +1 -1
- flock/themes/hax0r-blue.toml +1 -1
- flock/themes/hopscotch.256.toml +1 -1
- flock/themes/ic-green-ppl.toml +1 -1
- flock/themes/iceberg-dark.toml +1 -1
- flock/themes/japanesque.toml +1 -1
- flock/themes/jubi.toml +1 -1
- flock/themes/kibble.toml +1 -1
- flock/themes/kolorit.toml +1 -1
- flock/themes/kurokula.toml +1 -1
- flock/themes/materialdesigncolors.toml +1 -1
- flock/themes/matrix.toml +1 -1
- flock/themes/mellifluous.toml +1 -1
- flock/themes/midnight-in-mojave.toml +1 -1
- flock/themes/monokai-remastered.toml +1 -1
- flock/themes/monokai-soda.toml +1 -1
- flock/themes/neon.toml +1 -1
- flock/themes/neopolitan.toml +1 -1
- flock/themes/nord-light.toml +1 -1
- flock/themes/ocean.toml +1 -1
- flock/themes/onehalfdark.toml +1 -1
- flock/themes/onehalflight.toml +1 -1
- flock/themes/palenighthc.toml +1 -1
- flock/themes/paulmillr.toml +1 -1
- flock/themes/pencildark.toml +1 -1
- flock/themes/pnevma.toml +1 -1
- flock/themes/purple-rain.toml +1 -1
- flock/themes/purplepeter.toml +1 -1
- flock/themes/raycast-dark.toml +1 -1
- flock/themes/red-sands.toml +1 -1
- flock/themes/relaxed.toml +1 -1
- flock/themes/retro.toml +1 -1
- flock/themes/rose-pine.toml +1 -1
- flock/themes/royal.toml +1 -1
- flock/themes/ryuuko.toml +1 -1
- flock/themes/sakura.toml +1 -1
- flock/themes/scarlet-protocol.toml +1 -1
- flock/themes/seoulbones-dark.toml +1 -1
- flock/themes/shades-of-purple.toml +1 -1
- flock/themes/smyck.toml +1 -1
- flock/themes/softserver.toml +1 -1
- flock/themes/solarized-darcula.toml +1 -1
- flock/themes/square.toml +1 -1
- flock/themes/sugarplum.toml +1 -1
- flock/themes/thayer-bright.toml +1 -1
- flock/themes/tokyonight.toml +1 -1
- flock/themes/tomorrow.toml +1 -1
- flock/themes/ubuntu.toml +1 -1
- flock/themes/ultradark.toml +1 -1
- flock/themes/ultraviolent.toml +1 -1
- flock/themes/unikitty.toml +1 -1
- flock/themes/urple.toml +1 -1
- flock/themes/vesper.toml +1 -1
- flock/themes/vimbones.toml +1 -1
- flock/themes/wildcherry.toml +1 -1
- flock/themes/wilmersdorf.toml +1 -1
- flock/themes/wryan.toml +1 -1
- flock/themes/xcodedarkhc.toml +1 -1
- flock/themes/xcodelight.toml +1 -1
- flock/themes/zenbones-light.toml +1 -1
- flock/themes/zenwritten-dark.toml +1 -1
- flock/utilities.py +301 -0
- flock/{components/utility → utility}/output_utility_component.py +68 -53
- flock/visibility.py +107 -0
- flock_core-0.5.56b0.dist-info/METADATA +747 -0
- flock_core-0.5.56b0.dist-info/RECORD +398 -0
- flock_core-0.5.56b0.dist-info/entry_points.txt +2 -0
- {flock_core-0.5.0b28.dist-info → flock_core-0.5.56b0.dist-info}/licenses/LICENSE +1 -1
- flock/adapter/__init__.py +0 -14
- flock/adapter/azure_adapter.py +0 -68
- flock/adapter/chroma_adapter.py +0 -73
- flock/adapter/faiss_adapter.py +0 -97
- flock/adapter/pinecone_adapter.py +0 -51
- flock/adapter/vector_base.py +0 -47
- flock/cli/assets/release_notes.md +0 -140
- flock/cli/config.py +0 -8
- flock/cli/constants.py +0 -36
- flock/cli/create_agent.py +0 -1
- flock/cli/create_flock.py +0 -280
- flock/cli/execute_flock.py +0 -620
- flock/cli/load_agent.py +0 -1
- flock/cli/load_examples.py +0 -1
- flock/cli/load_flock.py +0 -192
- flock/cli/load_release_notes.py +0 -20
- flock/cli/loaded_flock_cli.py +0 -254
- flock/cli/manage_agents.py +0 -459
- flock/cli/registry_management.py +0 -889
- flock/cli/runner.py +0 -41
- flock/cli/settings.py +0 -857
- flock/cli/utils.py +0 -135
- flock/cli/view_results.py +0 -29
- flock/cli/yaml_editor.py +0 -396
- flock/components/__init__.py +0 -30
- flock/components/evaluation/__init__.py +0 -9
- flock/components/evaluation/declarative_evaluation_component.py +0 -606
- flock/components/routing/__init__.py +0 -15
- flock/components/routing/conditional_routing_component.py +0 -494
- flock/components/routing/default_routing_component.py +0 -103
- flock/components/routing/llm_routing_component.py +0 -206
- flock/components/utility/__init__.py +0 -22
- flock/components/utility/example_utility_component.py +0 -250
- flock/components/utility/feedback_utility_component.py +0 -206
- flock/components/utility/memory_utility_component.py +0 -550
- flock/components/utility/metrics_utility_component.py +0 -700
- flock/config.py +0 -61
- flock/core/__init__.py +0 -110
- flock/core/agent/__init__.py +0 -16
- flock/core/agent/default_agent.py +0 -216
- flock/core/agent/flock_agent_components.py +0 -104
- flock/core/agent/flock_agent_execution.py +0 -101
- flock/core/agent/flock_agent_integration.py +0 -260
- flock/core/agent/flock_agent_lifecycle.py +0 -186
- flock/core/agent/flock_agent_serialization.py +0 -381
- flock/core/api/__init__.py +0 -10
- flock/core/api/custom_endpoint.py +0 -45
- flock/core/api/endpoints.py +0 -254
- flock/core/api/main.py +0 -162
- flock/core/api/models.py +0 -97
- flock/core/api/run_store.py +0 -224
- flock/core/api/runner.py +0 -44
- flock/core/api/service.py +0 -214
- flock/core/component/__init__.py +0 -15
- flock/core/component/agent_component_base.py +0 -309
- flock/core/component/evaluation_component.py +0 -62
- flock/core/component/routing_component.py +0 -74
- flock/core/component/utility_component.py +0 -69
- flock/core/config/flock_agent_config.py +0 -58
- flock/core/config/scheduled_agent_config.py +0 -40
- flock/core/context/context.py +0 -213
- flock/core/context/context_manager.py +0 -37
- flock/core/context/context_vars.py +0 -10
- flock/core/evaluation/utils.py +0 -396
- flock/core/execution/batch_executor.py +0 -369
- flock/core/execution/evaluation_executor.py +0 -438
- flock/core/execution/local_executor.py +0 -31
- flock/core/execution/opik_executor.py +0 -103
- flock/core/execution/temporal_executor.py +0 -164
- flock/core/flock.py +0 -634
- flock/core/flock_agent.py +0 -336
- flock/core/flock_factory.py +0 -613
- flock/core/flock_scheduler.py +0 -166
- flock/core/flock_server_manager.py +0 -136
- flock/core/interpreter/python_interpreter.py +0 -689
- flock/core/mcp/__init__.py +0 -1
- flock/core/mcp/flock_mcp_server.py +0 -680
- flock/core/mcp/mcp_client_manager.py +0 -201
- flock/core/mcp/types/__init__.py +0 -1
- flock/core/mixin/dspy_integration.py +0 -403
- flock/core/mixin/prompt_parser.py +0 -125
- flock/core/orchestration/__init__.py +0 -15
- flock/core/orchestration/flock_batch_processor.py +0 -94
- flock/core/orchestration/flock_evaluator.py +0 -113
- flock/core/orchestration/flock_execution.py +0 -295
- flock/core/orchestration/flock_initialization.py +0 -149
- flock/core/orchestration/flock_server_manager.py +0 -67
- flock/core/orchestration/flock_web_server.py +0 -117
- flock/core/registry/__init__.py +0 -45
- flock/core/registry/agent_registry.py +0 -69
- flock/core/registry/callable_registry.py +0 -139
- flock/core/registry/component_discovery.py +0 -142
- flock/core/registry/component_registry.py +0 -64
- flock/core/registry/config_mapping.py +0 -64
- flock/core/registry/decorators.py +0 -137
- flock/core/registry/registry_hub.py +0 -205
- flock/core/registry/server_registry.py +0 -57
- flock/core/registry/type_registry.py +0 -86
- flock/core/serialization/__init__.py +0 -13
- flock/core/serialization/callable_registry.py +0 -52
- flock/core/serialization/flock_serializer.py +0 -832
- flock/core/serialization/json_encoder.py +0 -41
- flock/core/serialization/secure_serializer.py +0 -175
- flock/core/serialization/serializable.py +0 -342
- flock/core/serialization/serialization_utils.py +0 -412
- flock/core/util/file_path_utils.py +0 -223
- flock/core/util/hydrator.py +0 -309
- flock/core/util/input_resolver.py +0 -164
- flock/core/util/loader.py +0 -59
- flock/core/util/splitter.py +0 -219
- flock/di.py +0 -27
- flock/platform/docker_tools.py +0 -49
- flock/platform/jaeger_install.py +0 -86
- flock/webapp/__init__.py +0 -1
- flock/webapp/app/__init__.py +0 -0
- flock/webapp/app/api/__init__.py +0 -0
- flock/webapp/app/api/agent_management.py +0 -241
- flock/webapp/app/api/execution.py +0 -709
- flock/webapp/app/api/flock_management.py +0 -129
- flock/webapp/app/api/registry_viewer.py +0 -30
- flock/webapp/app/chat.py +0 -665
- flock/webapp/app/config.py +0 -104
- flock/webapp/app/dependencies.py +0 -117
- flock/webapp/app/main.py +0 -1070
- flock/webapp/app/middleware.py +0 -113
- flock/webapp/app/models_ui.py +0 -7
- flock/webapp/app/services/__init__.py +0 -0
- flock/webapp/app/services/feedback_file_service.py +0 -363
- flock/webapp/app/services/flock_service.py +0 -337
- flock/webapp/app/services/sharing_models.py +0 -81
- flock/webapp/app/services/sharing_store.py +0 -762
- flock/webapp/app/templates/theme_mapper.html +0 -326
- flock/webapp/app/theme_mapper.py +0 -812
- flock/webapp/app/utils.py +0 -85
- flock/webapp/run.py +0 -215
- flock/webapp/static/css/chat.css +0 -301
- flock/webapp/static/css/components.css +0 -167
- flock/webapp/static/css/header.css +0 -39
- flock/webapp/static/css/layout.css +0 -46
- flock/webapp/static/css/sidebar.css +0 -127
- flock/webapp/static/css/two-pane.css +0 -48
- flock/webapp/templates/base.html +0 -200
- flock/webapp/templates/chat.html +0 -152
- flock/webapp/templates/chat_settings.html +0 -19
- flock/webapp/templates/flock_editor.html +0 -16
- flock/webapp/templates/index.html +0 -12
- flock/webapp/templates/partials/_agent_detail_form.html +0 -93
- flock/webapp/templates/partials/_agent_list.html +0 -18
- flock/webapp/templates/partials/_agent_manager_view.html +0 -51
- flock/webapp/templates/partials/_agent_tools_checklist.html +0 -14
- flock/webapp/templates/partials/_chat_container.html +0 -15
- flock/webapp/templates/partials/_chat_messages.html +0 -57
- flock/webapp/templates/partials/_chat_settings_form.html +0 -85
- flock/webapp/templates/partials/_create_flock_form.html +0 -50
- flock/webapp/templates/partials/_dashboard_flock_detail.html +0 -17
- flock/webapp/templates/partials/_dashboard_flock_file_list.html +0 -16
- flock/webapp/templates/partials/_dashboard_flock_properties_preview.html +0 -28
- flock/webapp/templates/partials/_dashboard_upload_flock_form.html +0 -16
- flock/webapp/templates/partials/_dynamic_input_form_content.html +0 -22
- flock/webapp/templates/partials/_env_vars_table.html +0 -23
- flock/webapp/templates/partials/_execution_form.html +0 -118
- flock/webapp/templates/partials/_execution_view_container.html +0 -28
- flock/webapp/templates/partials/_flock_file_list.html +0 -23
- flock/webapp/templates/partials/_flock_properties_form.html +0 -52
- flock/webapp/templates/partials/_flock_upload_form.html +0 -16
- flock/webapp/templates/partials/_header_flock_status.html +0 -5
- flock/webapp/templates/partials/_load_manager_view.html +0 -49
- flock/webapp/templates/partials/_registry_table.html +0 -25
- flock/webapp/templates/partials/_registry_viewer_content.html +0 -70
- flock/webapp/templates/partials/_results_display.html +0 -78
- flock/webapp/templates/partials/_settings_env_content.html +0 -9
- flock/webapp/templates/partials/_settings_theme_content.html +0 -14
- flock/webapp/templates/partials/_settings_view.html +0 -36
- flock/webapp/templates/partials/_share_chat_link_snippet.html +0 -11
- flock/webapp/templates/partials/_share_link_snippet.html +0 -35
- flock/webapp/templates/partials/_sidebar.html +0 -74
- flock/webapp/templates/partials/_streaming_results_container.html +0 -195
- flock/webapp/templates/partials/_structured_data_view.html +0 -40
- flock/webapp/templates/partials/_theme_preview.html +0 -36
- flock/webapp/templates/registry_viewer.html +0 -84
- flock/webapp/templates/shared_run_page.html +0 -140
- flock/workflow/__init__.py +0 -0
- flock/workflow/activities.py +0 -196
- flock/workflow/agent_activities.py +0 -24
- flock/workflow/agent_execution_activity.py +0 -202
- flock/workflow/flock_workflow.py +0 -214
- flock/workflow/temporal_config.py +0 -96
- flock/workflow/temporal_setup.py +0 -68
- flock_core-0.5.0b28.dist-info/METADATA +0 -274
- flock_core-0.5.0b28.dist-info/RECORD +0 -561
- flock_core-0.5.0b28.dist-info/entry_points.txt +0 -2
- /flock/{core/logging → logging}/formatters/themes.py +0 -0
- /flock/{core/logging → logging}/span_middleware/baggage_span_processor.py +0 -0
- /flock/{core/mcp → mcp}/util/__init__.py +0 -0
- {flock_core-0.5.0b28.dist-info → flock_core-0.5.56b0.dist-info}/WHEEL +0 -0
|
@@ -1,369 +0,0 @@
|
|
|
1
|
-
import asyncio
|
|
2
|
-
import concurrent.futures # For real parallelism via threads
|
|
3
|
-
from pathlib import Path
|
|
4
|
-
from typing import TYPE_CHECKING, Any
|
|
5
|
-
|
|
6
|
-
from box import Box
|
|
7
|
-
from opentelemetry import trace
|
|
8
|
-
from pandas import DataFrame
|
|
9
|
-
from rich.progress import ( # Import Rich Progress
|
|
10
|
-
BarColumn,
|
|
11
|
-
Progress,
|
|
12
|
-
SpinnerColumn,
|
|
13
|
-
TextColumn,
|
|
14
|
-
TimeElapsedColumn,
|
|
15
|
-
)
|
|
16
|
-
|
|
17
|
-
from flock.config import TELEMETRY
|
|
18
|
-
from flock.core.context.context import FlockContext
|
|
19
|
-
from flock.core.context.context_vars import FLOCK_BATCH_SILENT_MODE
|
|
20
|
-
from flock.core.flock_agent import FlockAgent
|
|
21
|
-
from flock.core.logging.logging import get_logger
|
|
22
|
-
|
|
23
|
-
try:
|
|
24
|
-
import pandas as pd
|
|
25
|
-
|
|
26
|
-
PANDAS_AVAILABLE = True
|
|
27
|
-
except ImportError:
|
|
28
|
-
pd = None
|
|
29
|
-
PANDAS_AVAILABLE = False
|
|
30
|
-
|
|
31
|
-
if TYPE_CHECKING:
|
|
32
|
-
from flock.core.flock import Flock
|
|
33
|
-
|
|
34
|
-
logger = get_logger("flock")
|
|
35
|
-
TELEMETRY.setup_tracing() # Setup OpenTelemetry
|
|
36
|
-
tracer = trace.get_tracer(__name__)
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
class BatchProcessor:
|
|
40
|
-
def __init__(self, flock_instance: "Flock"):
|
|
41
|
-
self.flock = flock_instance
|
|
42
|
-
|
|
43
|
-
async def run_batch_async(
|
|
44
|
-
self,
|
|
45
|
-
start_agent: FlockAgent | str,
|
|
46
|
-
batch_inputs: list[dict[str, Any]] | DataFrame | str,
|
|
47
|
-
input_mapping: dict[str, str] | None = None,
|
|
48
|
-
static_inputs: dict[str, Any] | None = None,
|
|
49
|
-
parallel: bool = True,
|
|
50
|
-
max_workers: int = 5,
|
|
51
|
-
use_temporal: bool | None = None,
|
|
52
|
-
box_results: bool = True,
|
|
53
|
-
return_errors: bool = False,
|
|
54
|
-
silent_mode: bool = False,
|
|
55
|
-
write_to_csv: str | None = None,
|
|
56
|
-
hide_columns: list[str] | None = None,
|
|
57
|
-
delimiter: str = ",",
|
|
58
|
-
) -> list[Box | dict | None | Exception]:
|
|
59
|
-
"""Runs the specified agent/workflow for each item in a batch asynchronously.
|
|
60
|
-
|
|
61
|
-
Args:
|
|
62
|
-
start_agent: Agent instance or name to start each run.
|
|
63
|
-
batch_inputs: Input data in one of these forms:
|
|
64
|
-
- List of dictionaries, each representing inputs for one run
|
|
65
|
-
- Pandas DataFrame where each row is inputs for one run
|
|
66
|
-
- String path to a CSV file to load as DataFrame
|
|
67
|
-
input_mapping: Maps DataFrame/CSV column names to agent input keys (required for DataFrame/CSV).
|
|
68
|
-
static_inputs: Dictionary of inputs constant across all batch runs.
|
|
69
|
-
parallel: Whether to run local jobs in parallel (ignored if use_temporal=True).
|
|
70
|
-
max_workers: Max concurrent local workers (used if parallel=True and use_temporal=False).
|
|
71
|
-
use_temporal: Override Flock's 'enable_temporal' setting for this batch.
|
|
72
|
-
box_results: Wrap successful dictionary results in Box objects.
|
|
73
|
-
return_errors: If True, return Exception objects for failed runs instead of raising.
|
|
74
|
-
silent_mode: If True, suppress output and show progress bar instead.
|
|
75
|
-
write_to_csv: Path to save results as CSV file.
|
|
76
|
-
hide_columns: List of column names to hide from output.
|
|
77
|
-
|
|
78
|
-
Returns:
|
|
79
|
-
List containing results (Box/dict), None (if error and not return_errors),
|
|
80
|
-
or Exception objects (if error and return_errors). Order matches input.
|
|
81
|
-
|
|
82
|
-
Raises:
|
|
83
|
-
ValueError: For invalid input combinations.
|
|
84
|
-
ImportError: If DataFrame/CSV used without pandas.
|
|
85
|
-
Exception: First exception from a run if return_errors is False.
|
|
86
|
-
"""
|
|
87
|
-
effective_use_temporal = (
|
|
88
|
-
use_temporal
|
|
89
|
-
if use_temporal is not None
|
|
90
|
-
else self.flock.enable_temporal
|
|
91
|
-
)
|
|
92
|
-
exec_mode = (
|
|
93
|
-
"Temporal"
|
|
94
|
-
if effective_use_temporal
|
|
95
|
-
else ("Parallel Local" if parallel else "Sequential Local")
|
|
96
|
-
)
|
|
97
|
-
logger.info(
|
|
98
|
-
f"Starting batch run for agent '{start_agent}'. Execution: {exec_mode}, Silent: {silent_mode}"
|
|
99
|
-
)
|
|
100
|
-
|
|
101
|
-
# --- Input Preparation ---
|
|
102
|
-
prepared_batch_inputs: list[dict[str, Any]] = []
|
|
103
|
-
|
|
104
|
-
if input_mapping == {}:
|
|
105
|
-
input_mapping = None
|
|
106
|
-
if static_inputs == {}:
|
|
107
|
-
static_inputs = None
|
|
108
|
-
|
|
109
|
-
if isinstance(batch_inputs, str):
|
|
110
|
-
# Handle CSV file input
|
|
111
|
-
try:
|
|
112
|
-
df = pd.read_csv(batch_inputs)
|
|
113
|
-
logger.debug(
|
|
114
|
-
f"Loaded CSV file with {len(df)} rows: {batch_inputs}"
|
|
115
|
-
)
|
|
116
|
-
batch_inputs = df # Convert to DataFrame for unified handling
|
|
117
|
-
except Exception as e:
|
|
118
|
-
raise ValueError(
|
|
119
|
-
f"Failed to load CSV file '{batch_inputs}': {e}"
|
|
120
|
-
)
|
|
121
|
-
|
|
122
|
-
if isinstance(batch_inputs, DataFrame):
|
|
123
|
-
# Handle DataFrame input
|
|
124
|
-
logger.debug(
|
|
125
|
-
f"Converting DataFrame ({len(batch_inputs)} rows) to batch inputs."
|
|
126
|
-
)
|
|
127
|
-
for _, row in batch_inputs.iterrows():
|
|
128
|
-
if input_mapping:
|
|
129
|
-
item_input = {
|
|
130
|
-
agent_key: row[df_col]
|
|
131
|
-
for df_col, agent_key in input_mapping.items()
|
|
132
|
-
if df_col in row
|
|
133
|
-
}
|
|
134
|
-
else:
|
|
135
|
-
item_input = row.to_dict()
|
|
136
|
-
prepared_batch_inputs.append(item_input)
|
|
137
|
-
else:
|
|
138
|
-
# Handle list of dictionaries
|
|
139
|
-
if not isinstance(batch_inputs, list):
|
|
140
|
-
raise ValueError(
|
|
141
|
-
"batch_inputs must be a list of dictionaries, DataFrame, or CSV file path"
|
|
142
|
-
)
|
|
143
|
-
|
|
144
|
-
if input_mapping:
|
|
145
|
-
# Apply mapping to dictionary inputs
|
|
146
|
-
logger.debug("Applying input mapping to dictionary inputs")
|
|
147
|
-
for item in batch_inputs:
|
|
148
|
-
mapped_input = {}
|
|
149
|
-
for df_col, agent_key in input_mapping.items():
|
|
150
|
-
if df_col in item:
|
|
151
|
-
mapped_input[agent_key] = item[df_col]
|
|
152
|
-
else:
|
|
153
|
-
logger.warning(
|
|
154
|
-
f"Input mapping key '{df_col}' not found in input dictionary"
|
|
155
|
-
)
|
|
156
|
-
prepared_batch_inputs.append(mapped_input)
|
|
157
|
-
else:
|
|
158
|
-
# Use dictionaries as-is if no mapping provided
|
|
159
|
-
prepared_batch_inputs = batch_inputs
|
|
160
|
-
|
|
161
|
-
logger.debug(
|
|
162
|
-
f"Using provided list of {len(prepared_batch_inputs)} batch inputs."
|
|
163
|
-
)
|
|
164
|
-
|
|
165
|
-
if not prepared_batch_inputs:
|
|
166
|
-
return []
|
|
167
|
-
|
|
168
|
-
# --- Setup Progress Bar if Silent ---
|
|
169
|
-
progress_context = None
|
|
170
|
-
progress_task_id = None
|
|
171
|
-
if silent_mode:
|
|
172
|
-
progress = Progress(
|
|
173
|
-
SpinnerColumn(),
|
|
174
|
-
TextColumn("[progress.description]{task.description}"),
|
|
175
|
-
BarColumn(),
|
|
176
|
-
TextColumn("[progress.percentage]{task.percentage:>3.0f}%"),
|
|
177
|
-
TextColumn("({task.completed}/{task.total})"),
|
|
178
|
-
TimeElapsedColumn(),
|
|
179
|
-
# transient=True # Optionally remove progress bar when done
|
|
180
|
-
)
|
|
181
|
-
progress_context = progress # Use as context manager
|
|
182
|
-
progress_task_id = progress.add_task(
|
|
183
|
-
f"Processing Batch ({exec_mode})",
|
|
184
|
-
total=len(prepared_batch_inputs),
|
|
185
|
-
)
|
|
186
|
-
progress.start()
|
|
187
|
-
|
|
188
|
-
results = [None] * len(prepared_batch_inputs) # Pre-allocate results list
|
|
189
|
-
|
|
190
|
-
# --- Worker Definitions ---
|
|
191
|
-
# We implement two flavours:
|
|
192
|
-
# * async_worker: used for Temporal or sequential runs (keeps the original behaviour)
|
|
193
|
-
# * thread_worker: executes the run in a dedicated thread via ThreadPoolExecutor for true parallelism.
|
|
194
|
-
|
|
195
|
-
async def async_worker(index: int, item_inputs: dict[str, Any]):
|
|
196
|
-
"""Original coroutine worker used for non-threaded execution paths."""
|
|
197
|
-
full_input = {**(static_inputs or {}), **item_inputs}
|
|
198
|
-
context = FlockContext()
|
|
199
|
-
context.set_variable(FLOCK_BATCH_SILENT_MODE, silent_mode)
|
|
200
|
-
|
|
201
|
-
run_desc = f"Batch item {index + 1}"
|
|
202
|
-
logger.debug(f"{run_desc} started (async).")
|
|
203
|
-
try:
|
|
204
|
-
result = await self.flock.run_async(
|
|
205
|
-
start_agent,
|
|
206
|
-
full_input,
|
|
207
|
-
box_result=box_results,
|
|
208
|
-
context=context,
|
|
209
|
-
)
|
|
210
|
-
results[index] = result
|
|
211
|
-
logger.debug(f"{run_desc} finished successfully.")
|
|
212
|
-
except Exception as e:
|
|
213
|
-
logger.error(f"{run_desc} failed: {e}", exc_info=not return_errors)
|
|
214
|
-
if return_errors:
|
|
215
|
-
results[index] = e
|
|
216
|
-
else:
|
|
217
|
-
raise # Propagate to calling gather
|
|
218
|
-
finally:
|
|
219
|
-
if progress_context:
|
|
220
|
-
progress.update(progress_task_id, advance=1)
|
|
221
|
-
|
|
222
|
-
# ThreadPool worker for real parallelism (suitable for blocking I/O)
|
|
223
|
-
def _thread_worker(index: int, item_inputs: dict[str, Any]):
|
|
224
|
-
"""Synchronous helper executed inside a worker thread."""
|
|
225
|
-
full_input = {**(static_inputs or {}), **item_inputs}
|
|
226
|
-
run_desc = f"Batch item {index + 1}"
|
|
227
|
-
logger.debug(f"{run_desc} started (thread).")
|
|
228
|
-
try:
|
|
229
|
-
# Use the synchronous wrapper to avoid nested event-loop issues inside threads
|
|
230
|
-
result = self.flock.run(
|
|
231
|
-
agent=start_agent,
|
|
232
|
-
input=full_input,
|
|
233
|
-
box_result=box_results,
|
|
234
|
-
)
|
|
235
|
-
logger.debug(f"{run_desc} finished successfully.")
|
|
236
|
-
return index, result, None
|
|
237
|
-
except Exception as e:
|
|
238
|
-
logger.error(f"{run_desc} failed: {e}")
|
|
239
|
-
return index, None, e
|
|
240
|
-
|
|
241
|
-
async def thread_worker(executor, index: int, item_inputs: dict[str, Any]):
|
|
242
|
-
"""Coroutine wrapper that submits _thread_worker to the specified executor."""
|
|
243
|
-
loop = asyncio.get_running_loop()
|
|
244
|
-
idx, res, err = await loop.run_in_executor(
|
|
245
|
-
executor, _thread_worker, index, item_inputs
|
|
246
|
-
)
|
|
247
|
-
# Handle result / error on the asyncio side
|
|
248
|
-
if err:
|
|
249
|
-
if return_errors:
|
|
250
|
-
results[idx] = err
|
|
251
|
-
else:
|
|
252
|
-
raise err
|
|
253
|
-
else:
|
|
254
|
-
results[idx] = res
|
|
255
|
-
if progress_context:
|
|
256
|
-
progress.update(progress_task_id, advance=1)
|
|
257
|
-
|
|
258
|
-
tasks = []
|
|
259
|
-
try:
|
|
260
|
-
if effective_use_temporal:
|
|
261
|
-
# Temporal Batching (Simplified: sequential execution for this example)
|
|
262
|
-
# A real implementation might use start_workflow or signals
|
|
263
|
-
logger.info(
|
|
264
|
-
"Running batch using Temporal (executing sequentially for now)..."
|
|
265
|
-
)
|
|
266
|
-
for i, item_data in enumerate(prepared_batch_inputs):
|
|
267
|
-
await async_worker(i, item_data) # Run sequentially for demo
|
|
268
|
-
# TODO: Implement true parallel Temporal workflow execution if needed
|
|
269
|
-
|
|
270
|
-
elif parallel:
|
|
271
|
-
# --- Real parallelism using ThreadPoolExecutor ---
|
|
272
|
-
logger.info(
|
|
273
|
-
f"Running batch in parallel (threads) with max_workers={max_workers}..."
|
|
274
|
-
)
|
|
275
|
-
loop = asyncio.get_running_loop()
|
|
276
|
-
with concurrent.futures.ThreadPoolExecutor(
|
|
277
|
-
max_workers=max_workers, thread_name_prefix="flock-batch"
|
|
278
|
-
) as executor:
|
|
279
|
-
for i, item_data in enumerate(prepared_batch_inputs):
|
|
280
|
-
tasks.append(
|
|
281
|
-
asyncio.create_task(
|
|
282
|
-
thread_worker(executor, i, item_data)
|
|
283
|
-
)
|
|
284
|
-
)
|
|
285
|
-
|
|
286
|
-
# Wait for all tasks allowing exceptions to propagate as needed
|
|
287
|
-
await asyncio.gather(*tasks)
|
|
288
|
-
|
|
289
|
-
else: # Sequential Local
|
|
290
|
-
logger.info("Running batch sequentially...")
|
|
291
|
-
for i, item_data in enumerate(prepared_batch_inputs):
|
|
292
|
-
await async_worker(i, item_data) # Already handles errors internally based on return_errors
|
|
293
|
-
|
|
294
|
-
logger.info("Batch execution finished.")
|
|
295
|
-
|
|
296
|
-
except Exception as batch_error:
|
|
297
|
-
# This catch handles errors re-raised from workers when return_errors=False
|
|
298
|
-
logger.error(f"Batch execution stopped due to error: {batch_error}")
|
|
299
|
-
# No need to cancel tasks here as gather would have stopped
|
|
300
|
-
if not return_errors:
|
|
301
|
-
raise # Re-raise the first error encountered if not returning errors
|
|
302
|
-
finally:
|
|
303
|
-
if progress_context:
|
|
304
|
-
progress.stop()
|
|
305
|
-
|
|
306
|
-
if write_to_csv:
|
|
307
|
-
try:
|
|
308
|
-
df = pd.DataFrame(results)
|
|
309
|
-
if hide_columns:
|
|
310
|
-
df = df.drop(columns=hide_columns)
|
|
311
|
-
# create write_to_csv directory if it doesn't exist
|
|
312
|
-
Path(write_to_csv).parent.mkdir(parents=True, exist_ok=True)
|
|
313
|
-
df.to_csv(write_to_csv, index=False, sep=delimiter)
|
|
314
|
-
logger.info(f"Results written to CSV file: {write_to_csv}")
|
|
315
|
-
except Exception as e:
|
|
316
|
-
logger.error(f"Failed to write results to CSV: {e}")
|
|
317
|
-
|
|
318
|
-
return results
|
|
319
|
-
|
|
320
|
-
def run_batch( # Synchronous wrapper
|
|
321
|
-
self,
|
|
322
|
-
start_agent: FlockAgent | str,
|
|
323
|
-
batch_inputs: list[dict[str, Any]] | DataFrame | str,
|
|
324
|
-
input_mapping: dict[str, str] | None = None,
|
|
325
|
-
static_inputs: dict[str, Any] | None = None,
|
|
326
|
-
parallel: bool = True,
|
|
327
|
-
max_workers: int = 5,
|
|
328
|
-
use_temporal: bool | None = None,
|
|
329
|
-
box_results: bool = True,
|
|
330
|
-
return_errors: bool = False,
|
|
331
|
-
silent_mode: bool = False,
|
|
332
|
-
write_to_csv: str | None = None,
|
|
333
|
-
hide_columns: list[str] | None = None,
|
|
334
|
-
delimiter: str = ",",
|
|
335
|
-
) -> list[Box | dict | None | Exception]:
|
|
336
|
-
"""Synchronous wrapper for run_batch_async."""
|
|
337
|
-
# (Standard asyncio run wrapper - same as in previous suggestion)
|
|
338
|
-
try:
|
|
339
|
-
loop = asyncio.get_running_loop()
|
|
340
|
-
if loop.is_closed():
|
|
341
|
-
raise RuntimeError("Event loop is closed")
|
|
342
|
-
except RuntimeError:
|
|
343
|
-
loop = asyncio.new_event_loop()
|
|
344
|
-
asyncio.set_event_loop(loop)
|
|
345
|
-
|
|
346
|
-
coro = self.run_batch_async(
|
|
347
|
-
start_agent=start_agent,
|
|
348
|
-
batch_inputs=batch_inputs,
|
|
349
|
-
input_mapping=input_mapping,
|
|
350
|
-
static_inputs=static_inputs,
|
|
351
|
-
parallel=parallel,
|
|
352
|
-
max_workers=max_workers,
|
|
353
|
-
use_temporal=use_temporal,
|
|
354
|
-
box_results=box_results,
|
|
355
|
-
return_errors=return_errors,
|
|
356
|
-
silent_mode=silent_mode,
|
|
357
|
-
write_to_csv=write_to_csv,
|
|
358
|
-
hide_columns=hide_columns,
|
|
359
|
-
delimiter=delimiter,
|
|
360
|
-
)
|
|
361
|
-
|
|
362
|
-
if asyncio.get_event_loop() is loop and not loop.is_running():
|
|
363
|
-
results = loop.run_until_complete(coro)
|
|
364
|
-
# loop.close() # Avoid closing potentially shared loop
|
|
365
|
-
return results
|
|
366
|
-
else:
|
|
367
|
-
# Run within an existing loop
|
|
368
|
-
future = asyncio.ensure_future(coro)
|
|
369
|
-
return loop.run_until_complete(future)
|