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
flock/core/util/hydrator.py
DELETED
|
@@ -1,309 +0,0 @@
|
|
|
1
|
-
# src/flock/core/util/hydrator.py (Revised - Simpler)
|
|
2
|
-
|
|
3
|
-
import asyncio
|
|
4
|
-
import json
|
|
5
|
-
from typing import (
|
|
6
|
-
Any,
|
|
7
|
-
TypeVar,
|
|
8
|
-
get_type_hints,
|
|
9
|
-
)
|
|
10
|
-
|
|
11
|
-
from pydantic import BaseModel
|
|
12
|
-
|
|
13
|
-
# Import necessary Flock components
|
|
14
|
-
from flock.core import Flock, FlockFactory
|
|
15
|
-
from flock.core.logging.logging import get_logger
|
|
16
|
-
|
|
17
|
-
# Import helper to format type hints back to strings
|
|
18
|
-
from flock.core.serialization.serialization_utils import _format_type_to_string
|
|
19
|
-
|
|
20
|
-
logger = get_logger("hydrator")
|
|
21
|
-
T = TypeVar("T", bound=BaseModel)
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
def flockclass(model: str = "openai/gpt-4o", agent_description: str | None = None):
|
|
25
|
-
"""Decorator to add a .hydrate() method to a Pydantic class.
|
|
26
|
-
Leverages a dynamic Flock agent to fill missing (None) fields.
|
|
27
|
-
|
|
28
|
-
Args:
|
|
29
|
-
model: The default LLM model identifier to use for hydration.
|
|
30
|
-
agent_description: An optional description for the dynamically created agent.
|
|
31
|
-
"""
|
|
32
|
-
|
|
33
|
-
def decorator(cls: type[T]) -> type[T]:
|
|
34
|
-
if not issubclass(cls, BaseModel):
|
|
35
|
-
raise TypeError(
|
|
36
|
-
"@flockclass can only decorate Pydantic BaseModel subclasses."
|
|
37
|
-
)
|
|
38
|
-
|
|
39
|
-
# Store metadata on the class
|
|
40
|
-
setattr(cls, "__flock_model__", model)
|
|
41
|
-
setattr(cls, "__flock_agent_description__", agent_description)
|
|
42
|
-
|
|
43
|
-
# --- Attach the async hydrate method directly ---
|
|
44
|
-
async def hydrate_async(self) -> T:
|
|
45
|
-
"""Hydrates the object by filling None fields using a dynamic Flock agent.
|
|
46
|
-
Uses existing non-None fields as input context.
|
|
47
|
-
Returns the hydrated object (self).
|
|
48
|
-
"""
|
|
49
|
-
class_name = self.__class__.__name__
|
|
50
|
-
logger.info(f"Starting hydration for instance of {class_name}")
|
|
51
|
-
|
|
52
|
-
# Get field information
|
|
53
|
-
all_fields, type_hints = _get_model_fields(self, class_name)
|
|
54
|
-
if all_fields is None or type_hints is None:
|
|
55
|
-
return self # Return early if field introspection failed
|
|
56
|
-
|
|
57
|
-
# Identify existing and missing fields
|
|
58
|
-
existing_data, missing_fields = _identify_fields(self, all_fields)
|
|
59
|
-
|
|
60
|
-
if not missing_fields:
|
|
61
|
-
logger.info(f"No fields to hydrate for {class_name} instance.")
|
|
62
|
-
return self
|
|
63
|
-
|
|
64
|
-
logger.debug(f"{class_name}: Fields to hydrate: {missing_fields}")
|
|
65
|
-
logger.debug(
|
|
66
|
-
f"{class_name}: Existing data for context: {json.dumps(existing_data, default=str)}"
|
|
67
|
-
)
|
|
68
|
-
|
|
69
|
-
# Create agent signatures
|
|
70
|
-
input_str, output_str, input_parts = _build_agent_signatures(
|
|
71
|
-
existing_data,
|
|
72
|
-
missing_fields,
|
|
73
|
-
type_hints,
|
|
74
|
-
all_fields,
|
|
75
|
-
class_name,
|
|
76
|
-
)
|
|
77
|
-
|
|
78
|
-
# Create and run agent
|
|
79
|
-
result = await _run_hydration_agent(
|
|
80
|
-
self,
|
|
81
|
-
input_str,
|
|
82
|
-
output_str,
|
|
83
|
-
input_parts,
|
|
84
|
-
existing_data,
|
|
85
|
-
class_name,
|
|
86
|
-
)
|
|
87
|
-
if result is None:
|
|
88
|
-
return self # Return early if agent run failed
|
|
89
|
-
|
|
90
|
-
# Update object fields with results
|
|
91
|
-
_update_fields_with_results(self, result, missing_fields, class_name)
|
|
92
|
-
|
|
93
|
-
return self
|
|
94
|
-
|
|
95
|
-
# --- Attach the sync hydrate method directly ---
|
|
96
|
-
def hydrate(self) -> T:
|
|
97
|
-
"""Synchronous wrapper for the async hydrate method."""
|
|
98
|
-
try:
|
|
99
|
-
# Try to get the current running loop
|
|
100
|
-
loop = asyncio.get_running_loop()
|
|
101
|
-
|
|
102
|
-
# If we reach here, there is a running loop
|
|
103
|
-
if loop.is_running():
|
|
104
|
-
# This runs the coroutine in the existing loop from a different thread
|
|
105
|
-
import concurrent.futures
|
|
106
|
-
|
|
107
|
-
with concurrent.futures.ThreadPoolExecutor() as executor:
|
|
108
|
-
future = executor.submit(asyncio.run, hydrate_async(self))
|
|
109
|
-
return future.result()
|
|
110
|
-
else:
|
|
111
|
-
# There's a loop but it's not running
|
|
112
|
-
return loop.run_until_complete(hydrate_async(self))
|
|
113
|
-
|
|
114
|
-
except RuntimeError: # No running loop
|
|
115
|
-
# If no loop is running, create a new one and run our coroutine
|
|
116
|
-
return asyncio.run(hydrate_async(self))
|
|
117
|
-
|
|
118
|
-
# Attach the methods to the class
|
|
119
|
-
setattr(cls, "hydrate_async", hydrate_async)
|
|
120
|
-
setattr(cls, "hydrate", hydrate)
|
|
121
|
-
setattr(cls, "hydrate_sync", hydrate) # Alias for backward compatibility
|
|
122
|
-
|
|
123
|
-
logger.debug(f"Attached hydrate methods to class {cls.__name__}")
|
|
124
|
-
return cls
|
|
125
|
-
|
|
126
|
-
return decorator
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
def _get_model_fields(
|
|
130
|
-
obj: BaseModel, class_name: str
|
|
131
|
-
) -> tuple[dict | None, dict | None]:
|
|
132
|
-
"""Extracts field information from a Pydantic model, handling v1/v2 compatibility."""
|
|
133
|
-
try:
|
|
134
|
-
if hasattr(obj, "model_fields"): # Pydantic v2
|
|
135
|
-
all_fields = obj.model_fields
|
|
136
|
-
type_hints = {name: field.annotation for name, field in all_fields.items()}
|
|
137
|
-
else: # Pydantic v1 fallback
|
|
138
|
-
type_hints = get_type_hints(obj.__class__)
|
|
139
|
-
all_fields = getattr(obj, "__fields__", {name: None for name in type_hints})
|
|
140
|
-
return all_fields, type_hints
|
|
141
|
-
except Exception as e:
|
|
142
|
-
logger.error(
|
|
143
|
-
f"Could not get fields/type hints for {class_name}: {e}",
|
|
144
|
-
exc_info=True,
|
|
145
|
-
)
|
|
146
|
-
return None, None
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
def _identify_fields(
|
|
150
|
-
obj: BaseModel, all_fields: dict
|
|
151
|
-
) -> tuple[dict[str, Any], list[str]]:
|
|
152
|
-
"""Identifies existing (non-None) and missing fields in the object."""
|
|
153
|
-
existing_data: dict[str, Any] = {}
|
|
154
|
-
missing_fields: list[str] = []
|
|
155
|
-
|
|
156
|
-
for field_name in all_fields:
|
|
157
|
-
if hasattr(obj, field_name): # Check if attribute exists
|
|
158
|
-
value = getattr(obj, field_name)
|
|
159
|
-
if value is not None:
|
|
160
|
-
existing_data[field_name] = value
|
|
161
|
-
else:
|
|
162
|
-
missing_fields.append(field_name)
|
|
163
|
-
|
|
164
|
-
return existing_data, missing_fields
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
def _build_agent_signatures(
|
|
168
|
-
existing_data: dict[str, Any],
|
|
169
|
-
missing_fields: list[str],
|
|
170
|
-
type_hints: dict,
|
|
171
|
-
all_fields: dict,
|
|
172
|
-
class_name: str,
|
|
173
|
-
) -> tuple[str, str, list]:
|
|
174
|
-
"""Builds input and output signatures for the dynamic agent."""
|
|
175
|
-
# Input signature based on existing data
|
|
176
|
-
input_parts = []
|
|
177
|
-
for name in existing_data:
|
|
178
|
-
field_type = type_hints.get(name, Any)
|
|
179
|
-
type_str = _format_type_to_string(field_type)
|
|
180
|
-
field_info = all_fields.get(name)
|
|
181
|
-
field_desc = getattr(field_info, "description", "")
|
|
182
|
-
if field_desc:
|
|
183
|
-
input_parts.append(f"{name}: {type_str} | {field_desc}")
|
|
184
|
-
else:
|
|
185
|
-
input_parts.append(f"{name}: {type_str}")
|
|
186
|
-
|
|
187
|
-
input_str = (
|
|
188
|
-
", ".join(input_parts)
|
|
189
|
-
if input_parts
|
|
190
|
-
else "context_info: dict | Optional context if no fields have values"
|
|
191
|
-
)
|
|
192
|
-
|
|
193
|
-
# Output signature based on missing fields
|
|
194
|
-
output_parts = []
|
|
195
|
-
for name in missing_fields:
|
|
196
|
-
field_type = type_hints.get(name, Any)
|
|
197
|
-
type_str = _format_type_to_string(field_type)
|
|
198
|
-
field_info = all_fields.get(name)
|
|
199
|
-
field_desc = getattr(field_info, "description", "")
|
|
200
|
-
if field_desc:
|
|
201
|
-
output_parts.append(f"{name}: {type_str} | {field_desc}")
|
|
202
|
-
else:
|
|
203
|
-
output_parts.append(f"{name}: {type_str}")
|
|
204
|
-
|
|
205
|
-
output_str = ", ".join(output_parts)
|
|
206
|
-
|
|
207
|
-
return input_str, output_str, input_parts
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
async def _run_hydration_agent(
|
|
211
|
-
obj: BaseModel,
|
|
212
|
-
input_str: str,
|
|
213
|
-
output_str: str,
|
|
214
|
-
input_parts: list,
|
|
215
|
-
existing_data: dict[str, Any],
|
|
216
|
-
class_name: str,
|
|
217
|
-
) -> dict[str, Any] | None:
|
|
218
|
-
"""Creates and runs a dynamic Flock agent to hydrate the object."""
|
|
219
|
-
# Agent configuration
|
|
220
|
-
agent_name = f"hydrator_{class_name}_{id(obj)}"
|
|
221
|
-
description = (
|
|
222
|
-
getattr(obj, "__flock_agent_description__", None)
|
|
223
|
-
or f"Agent that completes missing data for a {class_name} object."
|
|
224
|
-
)
|
|
225
|
-
hydration_model = getattr(obj, "__flock_model__", "openai/gpt-4o")
|
|
226
|
-
|
|
227
|
-
logger.debug(f"Creating dynamic agent '{agent_name}' for {class_name}")
|
|
228
|
-
logger.debug(f" Input Schema: {input_str}")
|
|
229
|
-
logger.debug(f" Output Schema: {output_str}")
|
|
230
|
-
|
|
231
|
-
try:
|
|
232
|
-
# Create agent
|
|
233
|
-
dynamic_agent = FlockFactory.create_default_agent(
|
|
234
|
-
name=agent_name,
|
|
235
|
-
description=description,
|
|
236
|
-
input=input_str,
|
|
237
|
-
output=output_str,
|
|
238
|
-
model=hydration_model,
|
|
239
|
-
no_output=True,
|
|
240
|
-
use_cache=False,
|
|
241
|
-
)
|
|
242
|
-
|
|
243
|
-
# Create temporary Flock
|
|
244
|
-
temp_flock = Flock(
|
|
245
|
-
name=f"temp_hydrator_flock_{agent_name}",
|
|
246
|
-
model=hydration_model,
|
|
247
|
-
show_flock_banner=False,
|
|
248
|
-
)
|
|
249
|
-
temp_flock.add_agent(dynamic_agent)
|
|
250
|
-
|
|
251
|
-
# Prepare input data
|
|
252
|
-
agent_input_data = (
|
|
253
|
-
existing_data
|
|
254
|
-
if input_parts
|
|
255
|
-
else {"context_info": {"object_type": class_name}}
|
|
256
|
-
)
|
|
257
|
-
|
|
258
|
-
logger.info(f"Running hydration agent '{agent_name}' for {class_name}...")
|
|
259
|
-
|
|
260
|
-
# Run agent
|
|
261
|
-
result = await temp_flock.run_async(
|
|
262
|
-
agent=agent_name,
|
|
263
|
-
input=agent_input_data,
|
|
264
|
-
box_result=False,
|
|
265
|
-
)
|
|
266
|
-
logger.info(f"Hydration agent returned for {class_name}: {list(result.keys())}")
|
|
267
|
-
|
|
268
|
-
return result
|
|
269
|
-
|
|
270
|
-
except Exception as e:
|
|
271
|
-
logger.error(
|
|
272
|
-
f"Hydration agent creation or run failed for {class_name}: {e}",
|
|
273
|
-
exc_info=True,
|
|
274
|
-
)
|
|
275
|
-
return None
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
def _update_fields_with_results(
|
|
279
|
-
obj: BaseModel,
|
|
280
|
-
result: dict[str, Any],
|
|
281
|
-
missing_fields: list[str],
|
|
282
|
-
class_name: str,
|
|
283
|
-
) -> None:
|
|
284
|
-
"""Updates object fields with results from the hydration agent."""
|
|
285
|
-
updated_count = 0
|
|
286
|
-
for field_name in missing_fields:
|
|
287
|
-
if field_name in result:
|
|
288
|
-
try:
|
|
289
|
-
setattr(obj, field_name, result[field_name])
|
|
290
|
-
logger.debug(
|
|
291
|
-
f"Hydrated field '{field_name}' in {class_name} with value: {getattr(obj, field_name)}"
|
|
292
|
-
)
|
|
293
|
-
updated_count += 1
|
|
294
|
-
except Exception as e:
|
|
295
|
-
logger.warning(
|
|
296
|
-
f"Failed to set hydrated value for '{field_name}' in {class_name}: {e}. Value received: {result[field_name]}"
|
|
297
|
-
)
|
|
298
|
-
else:
|
|
299
|
-
logger.warning(
|
|
300
|
-
f"Hydration result missing expected field for {class_name}: '{field_name}'"
|
|
301
|
-
)
|
|
302
|
-
|
|
303
|
-
logger.info(
|
|
304
|
-
f"Hydration complete for {class_name}. Updated {updated_count}/{len(missing_fields)} fields."
|
|
305
|
-
)
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
# Ensure helper functions are available
|
|
309
|
-
# from flock.core.serialization.serialization_utils import _format_type_to_string
|
|
@@ -1,164 +0,0 @@
|
|
|
1
|
-
"""Utility functions for resolving input keys to their corresponding values."""
|
|
2
|
-
|
|
3
|
-
from flock.core.context.context import FlockContext
|
|
4
|
-
from flock.core.util.splitter import split_top_level
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
def get_callable_members(obj):
|
|
8
|
-
"""Extract all callable (methods/functions) members from a module or class.
|
|
9
|
-
Returns a list of callable objects.
|
|
10
|
-
"""
|
|
11
|
-
import inspect
|
|
12
|
-
|
|
13
|
-
# Get all members of the object
|
|
14
|
-
members = inspect.getmembers(obj)
|
|
15
|
-
|
|
16
|
-
# Filter for callable members that don't start with underscore (to exclude private/special methods)
|
|
17
|
-
callables = [
|
|
18
|
-
member[1]
|
|
19
|
-
for member in members
|
|
20
|
-
if inspect.isroutine(member[1]) and not member[0].startswith("_")
|
|
21
|
-
]
|
|
22
|
-
|
|
23
|
-
return callables
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
def _parse_keys(keys: list[str]) -> list[str]:
|
|
27
|
-
"""Split a comma‐separated string and strip any type annotations.
|
|
28
|
-
|
|
29
|
-
For example, "a, b: list[str]" becomes ["a", "b"].
|
|
30
|
-
"""
|
|
31
|
-
res_keys = []
|
|
32
|
-
for key in keys:
|
|
33
|
-
if "|" in key:
|
|
34
|
-
key = key.split("|")[0].strip()
|
|
35
|
-
if ":" in key:
|
|
36
|
-
key = key.split(":")[0].strip()
|
|
37
|
-
res_keys.append(key)
|
|
38
|
-
return res_keys
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
def top_level_to_keys(s: str) -> list[str]:
|
|
42
|
-
"""Convert a top-level comma-separated string to a list of keys."""
|
|
43
|
-
top_level_split = split_top_level(s)
|
|
44
|
-
return _parse_keys(top_level_split)
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
def resolve_inputs(
|
|
48
|
-
input_spec: str,
|
|
49
|
-
context: FlockContext,
|
|
50
|
-
previous_agent_name: str,
|
|
51
|
-
previous_agent_output:str,
|
|
52
|
-
previous_agent_handoff_strategy:str,
|
|
53
|
-
previous_agent_handoff_map:dict[str, str]
|
|
54
|
-
) -> dict:
|
|
55
|
-
"""Build a dictionary of inputs based on the input specification string and the provided context.
|
|
56
|
-
|
|
57
|
-
The lookup rules are:
|
|
58
|
-
- "context" (case-insensitive): returns the entire context.
|
|
59
|
-
- "context.property": returns an attribute from the context.
|
|
60
|
-
- "def.agent_name": returns the agent definition for the given agent.
|
|
61
|
-
- "agent_name": returns the most up2date record from the given agent's history.
|
|
62
|
-
- "agent_name.property": returns the value of a property from the state variable keyed by "agent_name.property".
|
|
63
|
-
- "property": searches the history for the most recent value of a property.
|
|
64
|
-
- Otherwise, if no matching value is found, fallback to the FLOCK_INITIAL_INPUT.
|
|
65
|
-
|
|
66
|
-
-> Recommendations:
|
|
67
|
-
- prefix your agent variables with the agent name or a short handle to avoid conflicts.
|
|
68
|
-
eg. agent name: "idea_agent", variable: "ia_idea" (ia = idea agent)
|
|
69
|
-
- or set hand off mode to strict to avoid conflicts.
|
|
70
|
-
with strict mode, the agent will only accept inputs from the previous agent.
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
Strategy for passing data to the next agent.
|
|
74
|
-
|
|
75
|
-
Example:
|
|
76
|
-
ReviewAgent.next_agent = SummaryAgent
|
|
77
|
-
ReviewAgent(output = "text:str, keywords:list[str], rating:int")
|
|
78
|
-
SummaryAgent(input = "text:str, title:str")
|
|
79
|
-
|
|
80
|
-
'append' means the difference in signature is appended to the next agent's input signature.
|
|
81
|
-
SummaryAgent(input = "text:str, title:str, keywords:list[str], rating:int")
|
|
82
|
-
|
|
83
|
-
'override' means the target agent's signature is getting overriden.
|
|
84
|
-
SummaryAgent(input = "text:str, keywords:list[str], rating:int")
|
|
85
|
-
|
|
86
|
-
'static' means the the target agent's signature is not changed at all.
|
|
87
|
-
If source agent has no output fields that match the target agent's input,
|
|
88
|
-
there will be no data passed to the next agent.
|
|
89
|
-
SummaryAgent(input = "text:str, title:str")
|
|
90
|
-
|
|
91
|
-
'map' means the source agent's output is mapped to the target agent's input
|
|
92
|
-
based on 'handoff_map' configuration.
|
|
93
|
-
|
|
94
|
-
Args:
|
|
95
|
-
input_spec: Comma-separated input keys (e.g., "query" or "agent_name.property").
|
|
96
|
-
context: A FlockContext instance.
|
|
97
|
-
|
|
98
|
-
Returns:
|
|
99
|
-
A dictionary mapping each input key to its resolved value.
|
|
100
|
-
"""
|
|
101
|
-
split_input = split_top_level(input_spec)
|
|
102
|
-
keys = _parse_keys(split_input)
|
|
103
|
-
inputs = {}
|
|
104
|
-
|
|
105
|
-
def _normalize_empty_string(val):
|
|
106
|
-
"""Treat empty string inputs as None to match None semantics.
|
|
107
|
-
|
|
108
|
-
This aligns behavior so passing "" behaves like passing None
|
|
109
|
-
for agent input properties.
|
|
110
|
-
"""
|
|
111
|
-
if isinstance(val, str) and val == "":
|
|
112
|
-
return None
|
|
113
|
-
return val
|
|
114
|
-
|
|
115
|
-
for key in keys:
|
|
116
|
-
split_key = key.split(".")
|
|
117
|
-
|
|
118
|
-
# Case 1: A single key
|
|
119
|
-
if len(split_key) == 1:
|
|
120
|
-
# Special keyword: "context"
|
|
121
|
-
if key.lower() == "context":
|
|
122
|
-
inputs[key] = context
|
|
123
|
-
continue
|
|
124
|
-
|
|
125
|
-
# Try to get a historic record for an agent (if any)
|
|
126
|
-
historic_records = context.get_agent_history(key)
|
|
127
|
-
if historic_records:
|
|
128
|
-
# You may choose to pass the entire record or just its data.
|
|
129
|
-
inputs[key] = historic_records[0].data
|
|
130
|
-
continue
|
|
131
|
-
|
|
132
|
-
# Fallback to the most recent value in the state
|
|
133
|
-
historic_value = context.get_most_recent_value(key)
|
|
134
|
-
if historic_value is not None:
|
|
135
|
-
inputs[key] = _normalize_empty_string(historic_value)
|
|
136
|
-
continue
|
|
137
|
-
|
|
138
|
-
# Fallback to the initial input
|
|
139
|
-
var_value = context.get_variable(key)
|
|
140
|
-
if var_value is not None:
|
|
141
|
-
inputs[key] = _normalize_empty_string(var_value)
|
|
142
|
-
continue
|
|
143
|
-
|
|
144
|
-
inputs[key] = _normalize_empty_string(context.get_variable("flock." + key))
|
|
145
|
-
|
|
146
|
-
# Case 2: A compound key (e.g., "agent_name.property" or "context.property")
|
|
147
|
-
elif len(split_key) == 2:
|
|
148
|
-
entity_name, property_name = split_key
|
|
149
|
-
|
|
150
|
-
if entity_name.lower() == "context":
|
|
151
|
-
# Try to fetch the attribute from the context
|
|
152
|
-
inputs[key] = _normalize_empty_string(getattr(context, property_name, None))
|
|
153
|
-
continue
|
|
154
|
-
|
|
155
|
-
if entity_name.lower() == "def":
|
|
156
|
-
# Return the agent definition for the given property name
|
|
157
|
-
inputs[key] = _normalize_empty_string(context.get_agent_definition(property_name))
|
|
158
|
-
continue
|
|
159
|
-
|
|
160
|
-
# Otherwise, attempt to look up a state variable with the key "entity_name.property_name"
|
|
161
|
-
inputs[key] = _normalize_empty_string(context.get_variable(f"{entity_name}.{property_name}"))
|
|
162
|
-
continue
|
|
163
|
-
|
|
164
|
-
return inputs
|
flock/core/util/loader.py
DELETED
|
@@ -1,59 +0,0 @@
|
|
|
1
|
-
# src/flock/core/loader.py
|
|
2
|
-
"""Provides functionality to load Flock instances from files."""
|
|
3
|
-
|
|
4
|
-
from pathlib import Path
|
|
5
|
-
from typing import TYPE_CHECKING
|
|
6
|
-
|
|
7
|
-
# Use TYPE_CHECKING to avoid runtime circular import if Flock imports this module indirectly
|
|
8
|
-
if TYPE_CHECKING:
|
|
9
|
-
from flock.core.flock import Flock
|
|
10
|
-
|
|
11
|
-
# Import locally within the function to ensure Serializable methods are available
|
|
12
|
-
# from .serialization.serializable import Serializable # Serializable defines the file methods
|
|
13
|
-
|
|
14
|
-
# Cloudpickle check needs to be top-level
|
|
15
|
-
try:
|
|
16
|
-
import cloudpickle
|
|
17
|
-
|
|
18
|
-
PICKLE_AVAILABLE = True
|
|
19
|
-
except ImportError:
|
|
20
|
-
PICKLE_AVAILABLE = False
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
def load_flock_from_file(file_path: str) -> "Flock":
|
|
24
|
-
"""Load a Flock instance from various file formats (detects type)."""
|
|
25
|
-
# Import Flock locally within the function to avoid circular dependency at module level
|
|
26
|
-
from flock.core.flock import Flock
|
|
27
|
-
|
|
28
|
-
p = Path(file_path)
|
|
29
|
-
if not p.exists():
|
|
30
|
-
raise FileNotFoundError(f"Flock file not found: {file_path}")
|
|
31
|
-
|
|
32
|
-
try:
|
|
33
|
-
if p.suffix.lower() in [".yaml", ".yml"]:
|
|
34
|
-
return Flock.from_yaml_file(p)
|
|
35
|
-
elif p.suffix.lower() == ".json":
|
|
36
|
-
# Assuming from_json is available via Serializable or directly on Flock
|
|
37
|
-
return Flock.from_json(p.read_text())
|
|
38
|
-
elif p.suffix.lower() == ".msgpack":
|
|
39
|
-
# Assuming from_msgpack_file is available via Serializable or directly on Flock
|
|
40
|
-
return Flock.from_msgpack_file(p)
|
|
41
|
-
elif p.suffix.lower() == ".pkl":
|
|
42
|
-
if PICKLE_AVAILABLE:
|
|
43
|
-
# Assuming from_pickle_file is available via Serializable or directly on Flock
|
|
44
|
-
return Flock.from_pickle_file(p)
|
|
45
|
-
else:
|
|
46
|
-
raise RuntimeError(
|
|
47
|
-
"Cannot load Pickle file: cloudpickle not installed."
|
|
48
|
-
)
|
|
49
|
-
else:
|
|
50
|
-
raise ValueError(f"Unsupported file extension: {p.suffix}")
|
|
51
|
-
except Exception as e:
|
|
52
|
-
# Add specific error logging if helpful
|
|
53
|
-
from flock.core.logging.logging import get_logger
|
|
54
|
-
|
|
55
|
-
logger = get_logger("loader")
|
|
56
|
-
logger.error(
|
|
57
|
-
f"Error loading Flock from {file_path}: {e}", exc_info=True
|
|
58
|
-
)
|
|
59
|
-
raise # Re-raise the original exception
|