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,709 +0,0 @@
|
|
|
1
|
-
# src/flock/webapp/app/api/execution.py
|
|
2
|
-
import asyncio
|
|
3
|
-
import html
|
|
4
|
-
import json
|
|
5
|
-
import uuid
|
|
6
|
-
from pathlib import Path
|
|
7
|
-
from typing import TYPE_CHECKING, Any, Literal
|
|
8
|
-
|
|
9
|
-
import markdown2 # Import markdown2
|
|
10
|
-
from fastapi import ( # Ensure Form and HTTPException are imported
|
|
11
|
-
APIRouter,
|
|
12
|
-
Depends,
|
|
13
|
-
Form,
|
|
14
|
-
Request,
|
|
15
|
-
)
|
|
16
|
-
from fastapi.encoders import jsonable_encoder
|
|
17
|
-
from fastapi.responses import FileResponse, HTMLResponse, StreamingResponse
|
|
18
|
-
from fastapi.templating import Jinja2Templates
|
|
19
|
-
from werkzeug.utils import secure_filename
|
|
20
|
-
|
|
21
|
-
from flock.webapp.app.services.feedback_file_service import (
|
|
22
|
-
create_csv_feedback_file,
|
|
23
|
-
create_csv_feedback_file_for_agent,
|
|
24
|
-
create_xlsx_feedback_file,
|
|
25
|
-
create_xlsx_feedback_file_for_agent,
|
|
26
|
-
)
|
|
27
|
-
|
|
28
|
-
if TYPE_CHECKING:
|
|
29
|
-
from flock.core.flock import Flock
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
from flock.core.logging.logging import (
|
|
33
|
-
get_logger as get_flock_logger, # For logging within the new endpoint
|
|
34
|
-
)
|
|
35
|
-
from flock.core.util.splitter import parse_schema
|
|
36
|
-
|
|
37
|
-
# Import the dependency to get the current Flock instance
|
|
38
|
-
from flock.webapp.app.dependencies import (
|
|
39
|
-
get_flock_instance,
|
|
40
|
-
get_optional_flock_instance,
|
|
41
|
-
get_shared_link_store,
|
|
42
|
-
)
|
|
43
|
-
|
|
44
|
-
# Service function now takes app_state
|
|
45
|
-
from flock.webapp.app.services.sharing_store import SharedLinkStoreInterface
|
|
46
|
-
|
|
47
|
-
router = APIRouter()
|
|
48
|
-
BASE_DIR = Path(__file__).resolve().parent.parent.parent
|
|
49
|
-
templates = Jinja2Templates(directory=str(BASE_DIR / "templates"))
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
# Add markdown2 filter to Jinja2 environment for this router
|
|
53
|
-
def markdown_filter(text):
|
|
54
|
-
return markdown2.markdown(text, extras=["tables", "fenced-code-blocks"])
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
templates.env.filters["markdown"] = markdown_filter
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
class ExecutionStreamManager:
|
|
61
|
-
"""In-memory tracker for live streaming sessions."""
|
|
62
|
-
|
|
63
|
-
def __init__(self) -> None:
|
|
64
|
-
self._sessions: dict[str, asyncio.Queue] = {}
|
|
65
|
-
self._lock = asyncio.Lock()
|
|
66
|
-
|
|
67
|
-
async def create_session(self) -> tuple[str, asyncio.Queue]:
|
|
68
|
-
run_id = uuid.uuid4().hex
|
|
69
|
-
queue: asyncio.Queue = asyncio.Queue()
|
|
70
|
-
async with self._lock:
|
|
71
|
-
self._sessions[run_id] = queue
|
|
72
|
-
return run_id, queue
|
|
73
|
-
|
|
74
|
-
async def get_queue(self, run_id: str) -> asyncio.Queue | None:
|
|
75
|
-
async with self._lock:
|
|
76
|
-
return self._sessions.get(run_id)
|
|
77
|
-
|
|
78
|
-
async def remove_session(self, run_id: str) -> None:
|
|
79
|
-
async with self._lock:
|
|
80
|
-
self._sessions.pop(run_id, None)
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
execution_stream_manager = ExecutionStreamManager()
|
|
84
|
-
stream_logger = get_flock_logger("webapp.execution.stream")
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
async def _execute_agent_with_stream(
|
|
88
|
-
run_id: str,
|
|
89
|
-
queue: asyncio.Queue,
|
|
90
|
-
start_agent_name: str,
|
|
91
|
-
inputs: dict[str, Any],
|
|
92
|
-
app_state: Any,
|
|
93
|
-
template_context: dict[str, Any],
|
|
94
|
-
) -> None:
|
|
95
|
-
"""Run the requested agent while forwarding streaming chunks to the UI."""
|
|
96
|
-
|
|
97
|
-
completed = False
|
|
98
|
-
|
|
99
|
-
def emit(payload: dict[str, Any]) -> None:
|
|
100
|
-
try:
|
|
101
|
-
queue.put_nowait(payload)
|
|
102
|
-
except asyncio.QueueFull:
|
|
103
|
-
stream_logger.warning(
|
|
104
|
-
"Dropping streaming payload for run %s due to full queue", run_id
|
|
105
|
-
)
|
|
106
|
-
|
|
107
|
-
async def terminate_with_error(message: str) -> None:
|
|
108
|
-
emit({"type": "error", "message": message})
|
|
109
|
-
await finalize_stream()
|
|
110
|
-
|
|
111
|
-
async def finalize_stream() -> None:
|
|
112
|
-
nonlocal completed
|
|
113
|
-
if not completed:
|
|
114
|
-
emit({"type": "complete"})
|
|
115
|
-
completed = True
|
|
116
|
-
await queue.put(None)
|
|
117
|
-
|
|
118
|
-
current_flock: "Flock | None" = getattr(app_state, "flock_instance", None)
|
|
119
|
-
run_store: RunStore | None = getattr(app_state, "run_store", None)
|
|
120
|
-
|
|
121
|
-
if not current_flock:
|
|
122
|
-
stream_logger.error("Stream run aborted: no flock loaded in app state.")
|
|
123
|
-
await terminate_with_error("No Flock loaded in the application.")
|
|
124
|
-
return
|
|
125
|
-
|
|
126
|
-
agent = current_flock.agents.get(start_agent_name)
|
|
127
|
-
if not agent:
|
|
128
|
-
stream_logger.error(
|
|
129
|
-
"Stream run aborted: agent '%s' not found in flock '%s'.",
|
|
130
|
-
start_agent_name,
|
|
131
|
-
current_flock.name,
|
|
132
|
-
)
|
|
133
|
-
await terminate_with_error(
|
|
134
|
-
f"Agent '{html.escape(str(start_agent_name))}' not found."
|
|
135
|
-
)
|
|
136
|
-
return
|
|
137
|
-
|
|
138
|
-
evaluator = getattr(agent, "evaluator", None)
|
|
139
|
-
previous_callbacks: list[Any] | None = None
|
|
140
|
-
original_stream_setting: bool | None = None
|
|
141
|
-
|
|
142
|
-
if evaluator is not None:
|
|
143
|
-
previous_callbacks = list(evaluator.config.stream_callbacks or [])
|
|
144
|
-
original_stream_setting = getattr(evaluator.config, "stream", False)
|
|
145
|
-
|
|
146
|
-
def stream_callback(message: Any) -> None:
|
|
147
|
-
chunk = getattr(message, "chunk", None)
|
|
148
|
-
signature_field = getattr(message, "signature_field_name", None)
|
|
149
|
-
if chunk is None:
|
|
150
|
-
return
|
|
151
|
-
emit(
|
|
152
|
-
{
|
|
153
|
-
"type": "token",
|
|
154
|
-
"chunk": str(chunk),
|
|
155
|
-
"field": signature_field,
|
|
156
|
-
}
|
|
157
|
-
)
|
|
158
|
-
|
|
159
|
-
evaluator.config.stream_callbacks = [
|
|
160
|
-
*previous_callbacks,
|
|
161
|
-
stream_callback,
|
|
162
|
-
]
|
|
163
|
-
if not original_stream_setting:
|
|
164
|
-
evaluator.config.stream = True
|
|
165
|
-
else:
|
|
166
|
-
emit(
|
|
167
|
-
{
|
|
168
|
-
"type": "status",
|
|
169
|
-
"message": "Streaming not available for this agent; results will appear when the run completes.",
|
|
170
|
-
}
|
|
171
|
-
)
|
|
172
|
-
|
|
173
|
-
try:
|
|
174
|
-
emit(
|
|
175
|
-
{
|
|
176
|
-
"type": "status",
|
|
177
|
-
"message": f"Running agent '{start_agent_name}'...",
|
|
178
|
-
}
|
|
179
|
-
)
|
|
180
|
-
result_data = await current_flock.run_async(
|
|
181
|
-
agent=start_agent_name, input=inputs, box_result=False
|
|
182
|
-
)
|
|
183
|
-
|
|
184
|
-
if run_store and hasattr(run_store, "add_run_details"):
|
|
185
|
-
run_identifier = (
|
|
186
|
-
result_data.get("run_id", run_id)
|
|
187
|
-
if isinstance(result_data, dict)
|
|
188
|
-
else run_id
|
|
189
|
-
)
|
|
190
|
-
run_store.add_run_details(
|
|
191
|
-
run_id=run_identifier,
|
|
192
|
-
agent_name=start_agent_name,
|
|
193
|
-
inputs=inputs,
|
|
194
|
-
outputs=result_data,
|
|
195
|
-
)
|
|
196
|
-
|
|
197
|
-
encoded_result = jsonable_encoder(result_data)
|
|
198
|
-
raw_json = json.dumps(
|
|
199
|
-
encoded_result, indent=2, ensure_ascii=False
|
|
200
|
-
).replace("\\n", "\n")
|
|
201
|
-
|
|
202
|
-
template = templates.get_template("partials/_results_display.html")
|
|
203
|
-
final_html = template.render(
|
|
204
|
-
{
|
|
205
|
-
**template_context,
|
|
206
|
-
"result": result_data,
|
|
207
|
-
"result_raw_json": raw_json,
|
|
208
|
-
}
|
|
209
|
-
)
|
|
210
|
-
|
|
211
|
-
emit(
|
|
212
|
-
{
|
|
213
|
-
"type": "final",
|
|
214
|
-
"html": final_html,
|
|
215
|
-
"result": encoded_result,
|
|
216
|
-
"raw_json": raw_json,
|
|
217
|
-
}
|
|
218
|
-
)
|
|
219
|
-
except Exception as exc: # pragma: no cover - defensive logging
|
|
220
|
-
stream_logger.error(
|
|
221
|
-
"Streamed execution for agent '%s' failed: %s",
|
|
222
|
-
start_agent_name,
|
|
223
|
-
exc,
|
|
224
|
-
exc_info=True,
|
|
225
|
-
)
|
|
226
|
-
await terminate_with_error(f"An error occurred: {html.escape(str(exc))}")
|
|
227
|
-
return
|
|
228
|
-
finally:
|
|
229
|
-
if evaluator is not None:
|
|
230
|
-
if previous_callbacks is not None:
|
|
231
|
-
evaluator.config.stream_callbacks = previous_callbacks
|
|
232
|
-
if original_stream_setting is not None:
|
|
233
|
-
evaluator.config.stream = original_stream_setting
|
|
234
|
-
await finalize_stream()
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
@router.get("/htmx/execution-form-content", response_class=HTMLResponse)
|
|
238
|
-
async def htmx_get_execution_form_content(
|
|
239
|
-
request: Request,
|
|
240
|
-
current_flock: "Flock | None" = Depends(
|
|
241
|
-
get_optional_flock_instance
|
|
242
|
-
), # Use optional if form can show 'no flock'
|
|
243
|
-
):
|
|
244
|
-
# flock instance is injected
|
|
245
|
-
return templates.TemplateResponse(
|
|
246
|
-
"partials/_execution_form.html",
|
|
247
|
-
{
|
|
248
|
-
"request": request,
|
|
249
|
-
"flock": current_flock, # Pass the injected flock instance
|
|
250
|
-
"input_fields": [],
|
|
251
|
-
"selected_agent_name": None, # Form starts with no agent selected
|
|
252
|
-
},
|
|
253
|
-
)
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
@router.get("/htmx/agents/{agent_name}/input-form", response_class=HTMLResponse)
|
|
257
|
-
async def htmx_get_agent_input_form(
|
|
258
|
-
request: Request,
|
|
259
|
-
agent_name: str,
|
|
260
|
-
current_flock: "Flock" = Depends(
|
|
261
|
-
get_flock_instance
|
|
262
|
-
), # Expect flock to be loaded
|
|
263
|
-
):
|
|
264
|
-
# flock instance is injected
|
|
265
|
-
agent = current_flock.agents.get(agent_name)
|
|
266
|
-
if not agent:
|
|
267
|
-
return HTMLResponse(
|
|
268
|
-
f"<p class='error'>Agent '{agent_name}' not found in the current Flock.</p>"
|
|
269
|
-
)
|
|
270
|
-
|
|
271
|
-
input_fields = []
|
|
272
|
-
if agent.input and isinstance(agent.input, str):
|
|
273
|
-
try:
|
|
274
|
-
parsed_spec = parse_schema(agent.input)
|
|
275
|
-
for name, type_str, description in parsed_spec:
|
|
276
|
-
field_info = {
|
|
277
|
-
"name": name,
|
|
278
|
-
"type": type_str.lower(),
|
|
279
|
-
"description": description or "",
|
|
280
|
-
}
|
|
281
|
-
if "bool" in field_info["type"]:
|
|
282
|
-
field_info["html_type"] = "checkbox"
|
|
283
|
-
elif (
|
|
284
|
-
"int" in field_info["type"] or "float" in field_info["type"]
|
|
285
|
-
):
|
|
286
|
-
field_info["html_type"] = "number"
|
|
287
|
-
elif (
|
|
288
|
-
"list" in field_info["type"] or "dict" in field_info["type"]
|
|
289
|
-
):
|
|
290
|
-
field_info["html_type"] = "textarea"
|
|
291
|
-
field_info["placeholder"] = (
|
|
292
|
-
f"Enter JSON for {field_info['type']}"
|
|
293
|
-
)
|
|
294
|
-
else:
|
|
295
|
-
field_info["html_type"] = "text"
|
|
296
|
-
input_fields.append(field_info)
|
|
297
|
-
except Exception as e:
|
|
298
|
-
return HTMLResponse(
|
|
299
|
-
f"<p class='error'>Error parsing input signature for {agent_name}: {e}</p>"
|
|
300
|
-
)
|
|
301
|
-
return templates.TemplateResponse(
|
|
302
|
-
"partials/_dynamic_input_form_content.html",
|
|
303
|
-
{"request": request, "input_fields": input_fields},
|
|
304
|
-
)
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
@router.post("/htmx/run", response_class=HTMLResponse)
|
|
308
|
-
async def htmx_run_flock(
|
|
309
|
-
request: Request,
|
|
310
|
-
):
|
|
311
|
-
current_flock_from_state: Flock | None = getattr(
|
|
312
|
-
request.app.state, "flock_instance", None
|
|
313
|
-
)
|
|
314
|
-
logger = get_flock_logger("webapp.execution.regular_run")
|
|
315
|
-
|
|
316
|
-
if not current_flock_from_state:
|
|
317
|
-
logger.error("HTMX Run (Regular): No Flock loaded in app_state.")
|
|
318
|
-
return HTMLResponse("<p class='error'>No Flock loaded to run.</p>")
|
|
319
|
-
|
|
320
|
-
form_data = await request.form()
|
|
321
|
-
start_agent_name = form_data.get("start_agent_name")
|
|
322
|
-
|
|
323
|
-
if not start_agent_name:
|
|
324
|
-
logger.warning("HTMX Run (Regular): Starting agent not selected.")
|
|
325
|
-
return HTMLResponse("<p class='error'>Starting agent not selected.</p>")
|
|
326
|
-
|
|
327
|
-
agent = current_flock_from_state.agents.get(start_agent_name)
|
|
328
|
-
if not agent:
|
|
329
|
-
logger.error(
|
|
330
|
-
f"HTMX Run (Regular): Agent '{start_agent_name}' not found in Flock '{current_flock_from_state.name}'."
|
|
331
|
-
)
|
|
332
|
-
return HTMLResponse(
|
|
333
|
-
f"<p class='error'>Agent '{start_agent_name}' not found in the current Flock.</p>"
|
|
334
|
-
)
|
|
335
|
-
|
|
336
|
-
inputs = {}
|
|
337
|
-
if agent.input and isinstance(agent.input, str):
|
|
338
|
-
try:
|
|
339
|
-
parsed_spec = parse_schema(agent.input)
|
|
340
|
-
for name, type_str, _ in parsed_spec:
|
|
341
|
-
form_field_name = f"agent_input_{name}"
|
|
342
|
-
raw_value = form_data.get(form_field_name)
|
|
343
|
-
if raw_value is None and "bool" in type_str.lower():
|
|
344
|
-
inputs[name] = False
|
|
345
|
-
continue
|
|
346
|
-
if raw_value is None:
|
|
347
|
-
inputs[name] = None
|
|
348
|
-
continue
|
|
349
|
-
if "int" in type_str.lower():
|
|
350
|
-
inputs[name] = int(raw_value)
|
|
351
|
-
elif "float" in type_str.lower():
|
|
352
|
-
inputs[name] = float(raw_value)
|
|
353
|
-
elif "bool" in type_str.lower():
|
|
354
|
-
inputs[name] = raw_value.lower() in [
|
|
355
|
-
"true",
|
|
356
|
-
"on",
|
|
357
|
-
"1",
|
|
358
|
-
"yes",
|
|
359
|
-
]
|
|
360
|
-
elif "list" in type_str.lower() or "dict" in type_str.lower():
|
|
361
|
-
inputs[name] = json.loads(raw_value)
|
|
362
|
-
else:
|
|
363
|
-
inputs[name] = raw_value
|
|
364
|
-
except ValueError as ve:
|
|
365
|
-
logger.error(
|
|
366
|
-
f"HTMX Run (Regular): Input parsing error for agent '{start_agent_name}': {ve}",
|
|
367
|
-
exc_info=True,
|
|
368
|
-
)
|
|
369
|
-
return HTMLResponse(
|
|
370
|
-
"<p class='error'>Invalid input format. Please check your input and try again.</p>"
|
|
371
|
-
)
|
|
372
|
-
except Exception as e_parse:
|
|
373
|
-
logger.error(
|
|
374
|
-
f"HTMX Run (Regular): Error processing inputs for '{start_agent_name}': {e_parse}",
|
|
375
|
-
exc_info=True,
|
|
376
|
-
)
|
|
377
|
-
return HTMLResponse(
|
|
378
|
-
f"<p class='error'>Error processing inputs for {html.escape(str(start_agent_name))}: {html.escape(str(e_parse))}</p>"
|
|
379
|
-
)
|
|
380
|
-
|
|
381
|
-
run_id, queue = await execution_stream_manager.create_session()
|
|
382
|
-
stream_url = str(request.url_for("htmx_stream_run", run_id=run_id))
|
|
383
|
-
root_path = request.scope.get("root_path", "")
|
|
384
|
-
|
|
385
|
-
template_context = {
|
|
386
|
-
"request": request,
|
|
387
|
-
"feedback_endpoint": f"{root_path}/ui/api/flock/htmx/feedback",
|
|
388
|
-
"share_id": None,
|
|
389
|
-
"flock_name": current_flock_from_state.name,
|
|
390
|
-
"agent_name": start_agent_name,
|
|
391
|
-
"flock_definition": current_flock_from_state.to_yaml(),
|
|
392
|
-
}
|
|
393
|
-
|
|
394
|
-
asyncio.create_task(
|
|
395
|
-
_execute_agent_with_stream(
|
|
396
|
-
run_id=run_id,
|
|
397
|
-
queue=queue,
|
|
398
|
-
start_agent_name=start_agent_name,
|
|
399
|
-
inputs=inputs,
|
|
400
|
-
app_state=request.app.state,
|
|
401
|
-
template_context=template_context,
|
|
402
|
-
)
|
|
403
|
-
)
|
|
404
|
-
|
|
405
|
-
return templates.TemplateResponse(
|
|
406
|
-
"partials/_streaming_results_container.html",
|
|
407
|
-
{
|
|
408
|
-
"request": request,
|
|
409
|
-
"run_id": run_id,
|
|
410
|
-
"stream_url": stream_url,
|
|
411
|
-
"agent_name": start_agent_name,
|
|
412
|
-
"flock_name": current_flock_from_state.name,
|
|
413
|
-
},
|
|
414
|
-
)
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
@router.get("/htmx/run-stream/{run_id}")
|
|
418
|
-
async def htmx_stream_run(run_id: str):
|
|
419
|
-
"""Server-Sent Events endpoint streaming live agent output."""
|
|
420
|
-
|
|
421
|
-
queue = await execution_stream_manager.get_queue(run_id)
|
|
422
|
-
if queue is None:
|
|
423
|
-
return HTMLResponse(
|
|
424
|
-
"<p class='error'>Streaming session not found or already closed.</p>",
|
|
425
|
-
status_code=404,
|
|
426
|
-
)
|
|
427
|
-
|
|
428
|
-
async def event_generator():
|
|
429
|
-
try:
|
|
430
|
-
while True:
|
|
431
|
-
payload = await queue.get()
|
|
432
|
-
if payload is None:
|
|
433
|
-
yield "event: close\ndata: {}\n\n"
|
|
434
|
-
break
|
|
435
|
-
data = json.dumps(payload, ensure_ascii=False)
|
|
436
|
-
yield f"data: {data}\n\n"
|
|
437
|
-
finally:
|
|
438
|
-
await execution_stream_manager.remove_session(run_id)
|
|
439
|
-
|
|
440
|
-
return StreamingResponse(event_generator(), media_type="text/event-stream")
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
# --- NEW ENDPOINT FOR SHARED RUNS ---
|
|
444
|
-
@router.post("/htmx/run-shared", response_class=HTMLResponse)
|
|
445
|
-
async def htmx_run_shared_flock(
|
|
446
|
-
request: Request,
|
|
447
|
-
share_id: str = Form(...),
|
|
448
|
-
):
|
|
449
|
-
shared_logger = get_flock_logger("webapp.execution.shared_run_stateful")
|
|
450
|
-
form_data = await request.form()
|
|
451
|
-
start_agent_name = form_data.get("start_agent_name")
|
|
452
|
-
|
|
453
|
-
if not start_agent_name:
|
|
454
|
-
shared_logger.warning("HTMX Run Shared: Starting agent not selected.")
|
|
455
|
-
return HTMLResponse(
|
|
456
|
-
"<p class='error'>Starting agent not selected for shared run.</p>"
|
|
457
|
-
)
|
|
458
|
-
|
|
459
|
-
inputs: dict[str, Any] = {}
|
|
460
|
-
try:
|
|
461
|
-
shared_flocks_store = getattr(request.app.state, "shared_flocks", {})
|
|
462
|
-
temp_flock = shared_flocks_store.get(share_id)
|
|
463
|
-
|
|
464
|
-
if not temp_flock:
|
|
465
|
-
shared_logger.error(
|
|
466
|
-
f"HTMX Run Shared: Flock instance for share_id '{share_id}' not found in app.state."
|
|
467
|
-
)
|
|
468
|
-
return HTMLResponse(
|
|
469
|
-
f"<p class='error'>Shared session not found or expired. Please try accessing the shared link again.</p>"
|
|
470
|
-
)
|
|
471
|
-
|
|
472
|
-
shared_logger.info(
|
|
473
|
-
f"HTMX Run Shared: Successfully retrieved pre-loaded Flock '{temp_flock.name}' for agent '{start_agent_name}' (share_id: {share_id})."
|
|
474
|
-
)
|
|
475
|
-
|
|
476
|
-
agent = temp_flock.agents.get(start_agent_name)
|
|
477
|
-
if not agent:
|
|
478
|
-
shared_logger.error(
|
|
479
|
-
f"HTMX Run Shared: Agent '{start_agent_name}' not found in shared Flock '{temp_flock.name}'."
|
|
480
|
-
)
|
|
481
|
-
return HTMLResponse(
|
|
482
|
-
f"<p class='error'>Agent '{start_agent_name}' not found in the provided shared Flock definition.</p>"
|
|
483
|
-
)
|
|
484
|
-
|
|
485
|
-
if agent.input and isinstance(agent.input, str):
|
|
486
|
-
parsed_spec = parse_schema(agent.input)
|
|
487
|
-
for name, type_str, _ in parsed_spec:
|
|
488
|
-
form_field_name = f"agent_input_{name}"
|
|
489
|
-
raw_value = form_data.get(form_field_name)
|
|
490
|
-
if raw_value is None and "bool" in type_str.lower():
|
|
491
|
-
inputs[name] = False
|
|
492
|
-
continue
|
|
493
|
-
if raw_value is None:
|
|
494
|
-
inputs[name] = None
|
|
495
|
-
continue
|
|
496
|
-
if "int" in type_str.lower():
|
|
497
|
-
inputs[name] = int(raw_value)
|
|
498
|
-
elif "float" in type_str.lower():
|
|
499
|
-
inputs[name] = float(raw_value)
|
|
500
|
-
elif "bool" in type_str.lower():
|
|
501
|
-
inputs[name] = raw_value.lower() in [
|
|
502
|
-
"true",
|
|
503
|
-
"on",
|
|
504
|
-
"1",
|
|
505
|
-
"yes",
|
|
506
|
-
]
|
|
507
|
-
elif "list" in type_str.lower() or "dict" in type_str.lower():
|
|
508
|
-
inputs[name] = json.loads(raw_value)
|
|
509
|
-
else:
|
|
510
|
-
inputs[name] = raw_value
|
|
511
|
-
|
|
512
|
-
shared_logger.info(
|
|
513
|
-
f"HTMX Run Shared: Executing agent '{start_agent_name}' in pre-loaded Flock '{temp_flock.name}'. Inputs: {list(inputs.keys())}"
|
|
514
|
-
)
|
|
515
|
-
result_data = await temp_flock.run_async(
|
|
516
|
-
start_agent=start_agent_name, input=inputs, box_result=False
|
|
517
|
-
)
|
|
518
|
-
raw_json_for_template = json.dumps(
|
|
519
|
-
jsonable_encoder(
|
|
520
|
-
result_data
|
|
521
|
-
), # ← converts every nested BaseModel, datetime, etc.
|
|
522
|
-
indent=2,
|
|
523
|
-
ensure_ascii=False,
|
|
524
|
-
)
|
|
525
|
-
# Unescape newlines for proper display in HTML <pre> tag
|
|
526
|
-
result_data_raw_json_str = raw_json_for_template.replace("\\n", "\n")
|
|
527
|
-
shared_logger.info(
|
|
528
|
-
f"HTMX Run Shared: Agent '{start_agent_name}' executed. Result keys: {list(result_data.keys()) if isinstance(result_data, dict) else 'N/A'}"
|
|
529
|
-
)
|
|
530
|
-
|
|
531
|
-
except ValueError as ve:
|
|
532
|
-
shared_logger.error(
|
|
533
|
-
f"HTMX Run Shared: Input parsing error for '{start_agent_name}' (share_id: {share_id}): {ve}",
|
|
534
|
-
exc_info=True,
|
|
535
|
-
)
|
|
536
|
-
return HTMLResponse(
|
|
537
|
-
f"<p class='error'>Invalid input format: {ve!s}</p>"
|
|
538
|
-
)
|
|
539
|
-
except Exception as e:
|
|
540
|
-
shared_logger.error(
|
|
541
|
-
f"HTMX Run Shared: Error during execution for '{start_agent_name}' (share_id: {share_id}): {e}",
|
|
542
|
-
exc_info=True,
|
|
543
|
-
)
|
|
544
|
-
return HTMLResponse(
|
|
545
|
-
f"<p class='error'>An unexpected error occurred: {e!s}</p>"
|
|
546
|
-
)
|
|
547
|
-
root_path = request.scope.get("root_path", "")
|
|
548
|
-
|
|
549
|
-
return templates.TemplateResponse(
|
|
550
|
-
"partials/_results_display.html",
|
|
551
|
-
{
|
|
552
|
-
"request": request,
|
|
553
|
-
"result": result_data,
|
|
554
|
-
"result_raw_json": result_data_raw_json_str,
|
|
555
|
-
"feedback_endpoint": f"{root_path}/ui/api/flock/htmx/feedback-shared",
|
|
556
|
-
"share_id": share_id,
|
|
557
|
-
"flock_name": temp_flock.name,
|
|
558
|
-
"agent_name": start_agent_name,
|
|
559
|
-
"flock_definition": temp_flock.to_yaml(),
|
|
560
|
-
},
|
|
561
|
-
)
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
# --- Feedback endpoints ---
|
|
565
|
-
@router.post("/htmx/feedback", response_class=HTMLResponse)
|
|
566
|
-
async def htmx_submit_feedback(
|
|
567
|
-
request: Request,
|
|
568
|
-
reason: str = Form(...),
|
|
569
|
-
expected_response: str | None = Form(None),
|
|
570
|
-
actual_response: str | None = Form(None),
|
|
571
|
-
flock_name: str | None = Form(None),
|
|
572
|
-
agent_name: str | None = Form(None),
|
|
573
|
-
flock_definition: str | None = Form(None),
|
|
574
|
-
store: SharedLinkStoreInterface = Depends(get_shared_link_store),
|
|
575
|
-
):
|
|
576
|
-
from uuid import uuid4
|
|
577
|
-
|
|
578
|
-
from flock.webapp.app.services.sharing_models import FeedbackRecord
|
|
579
|
-
|
|
580
|
-
record = FeedbackRecord(
|
|
581
|
-
feedback_id=uuid4().hex,
|
|
582
|
-
share_id=None,
|
|
583
|
-
context_type="agent_run",
|
|
584
|
-
reason=reason,
|
|
585
|
-
expected_response=expected_response,
|
|
586
|
-
actual_response=actual_response,
|
|
587
|
-
flock_name=flock_name,
|
|
588
|
-
agent_name=agent_name,
|
|
589
|
-
flock_definition=flock_definition,
|
|
590
|
-
)
|
|
591
|
-
await store.save_feedback(record)
|
|
592
|
-
return HTMLResponse("<p>🙏 Feedback received – thank you!</p>")
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
@router.post("/htmx/feedback-shared", response_class=HTMLResponse)
|
|
596
|
-
async def htmx_submit_feedback_shared(
|
|
597
|
-
request: Request,
|
|
598
|
-
share_id: str = Form(...),
|
|
599
|
-
reason: str = Form(...),
|
|
600
|
-
expected_response: str | None = Form(None),
|
|
601
|
-
actual_response: str | None = Form(None),
|
|
602
|
-
flock_definition: str | None = Form(None),
|
|
603
|
-
flock_name: str | None = Form(None),
|
|
604
|
-
agent_name: str | None = Form(None),
|
|
605
|
-
store: SharedLinkStoreInterface = Depends(get_shared_link_store),
|
|
606
|
-
):
|
|
607
|
-
from uuid import uuid4
|
|
608
|
-
|
|
609
|
-
from flock.webapp.app.services.sharing_models import FeedbackRecord
|
|
610
|
-
|
|
611
|
-
record = FeedbackRecord(
|
|
612
|
-
feedback_id=uuid4().hex,
|
|
613
|
-
share_id=share_id,
|
|
614
|
-
context_type="agent_run",
|
|
615
|
-
reason=reason,
|
|
616
|
-
expected_response=expected_response,
|
|
617
|
-
actual_response=actual_response,
|
|
618
|
-
flock_definition=flock_definition,
|
|
619
|
-
agent_name=agent_name,
|
|
620
|
-
flock_name=flock_name,
|
|
621
|
-
)
|
|
622
|
-
await store.save_feedback(record)
|
|
623
|
-
return HTMLResponse(
|
|
624
|
-
"<p>🙏 Feedback received for shared run – thank you!</p>"
|
|
625
|
-
)
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
@router.get("/htmx/feedback-download/{format}", response_class=FileResponse)
|
|
629
|
-
async def chat_feedback_download_all(
|
|
630
|
-
request: Request,
|
|
631
|
-
format: Literal["csv", "xlsx"] = "csv",
|
|
632
|
-
store: SharedLinkStoreInterface = Depends(get_shared_link_store),
|
|
633
|
-
):
|
|
634
|
-
"""Download all feedback records for all agents in the current flock as a CSV file.
|
|
635
|
-
|
|
636
|
-
This function iterates through all agents in the currently loaded flock and collects
|
|
637
|
-
all feedback records for each agent, then exports them as a single CSV file.
|
|
638
|
-
|
|
639
|
-
Args:
|
|
640
|
-
request: The FastAPI request object
|
|
641
|
-
store: The shared link store interface dependency
|
|
642
|
-
|
|
643
|
-
Returns:
|
|
644
|
-
FileResponse: CSV/XLSX file containing all feedback records for all agents
|
|
645
|
-
|
|
646
|
-
Raises:
|
|
647
|
-
HTTPException: If no flock is loaded or no agents are found in the flock
|
|
648
|
-
"""
|
|
649
|
-
safe_format = secure_filename(format)
|
|
650
|
-
if safe_format == "csv":
|
|
651
|
-
return await create_csv_feedback_file(
|
|
652
|
-
request=request,
|
|
653
|
-
store=store,
|
|
654
|
-
separator=","
|
|
655
|
-
)
|
|
656
|
-
elif safe_format == "xlsx":
|
|
657
|
-
return await create_xlsx_feedback_file(
|
|
658
|
-
request=request,
|
|
659
|
-
store=store,
|
|
660
|
-
)
|
|
661
|
-
else:
|
|
662
|
-
from fastapi import HTTPException
|
|
663
|
-
|
|
664
|
-
raise HTTPException(
|
|
665
|
-
status_code=400,
|
|
666
|
-
detail="Invalid file-format specified. Valid formats are: 'csv', 'xlsx'"
|
|
667
|
-
)
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
@router.get("/htmx/feedback-download/{agent_name}/{format}", response_class=FileResponse)
|
|
671
|
-
async def chat_feedback_download(
|
|
672
|
-
request: Request,
|
|
673
|
-
agent_name: str,
|
|
674
|
-
format: Literal["csv", "xlsx"] = "csv",
|
|
675
|
-
store: SharedLinkStoreInterface = Depends(get_shared_link_store),
|
|
676
|
-
):
|
|
677
|
-
"""Download all feedback records for a specific agent as a file.
|
|
678
|
-
|
|
679
|
-
Args:
|
|
680
|
-
request: The FastAPI request object
|
|
681
|
-
agent_name: Name of the agent to download feedback for
|
|
682
|
-
store: The shared link store interface dependency
|
|
683
|
-
format: Either 'csv' or 'xlsx' the file format to use
|
|
684
|
-
|
|
685
|
-
Returns:
|
|
686
|
-
FileResponse: CSV/XLSX file containing all feedback records for the specified agent
|
|
687
|
-
"""
|
|
688
|
-
safe_format = secure_filename(format)
|
|
689
|
-
safe_agent_name = secure_filename(agent_name)
|
|
690
|
-
if safe_format == "csv":
|
|
691
|
-
return await create_csv_feedback_file_for_agent(
|
|
692
|
-
request=request,
|
|
693
|
-
store=store,
|
|
694
|
-
separator=",",
|
|
695
|
-
agent_name=safe_agent_name,
|
|
696
|
-
)
|
|
697
|
-
elif safe_format == "xlsx":
|
|
698
|
-
return await create_xlsx_feedback_file_for_agent(
|
|
699
|
-
request=request,
|
|
700
|
-
store=store,
|
|
701
|
-
agent_name=safe_agent_name,
|
|
702
|
-
)
|
|
703
|
-
else:
|
|
704
|
-
from fastapi import HTTPException
|
|
705
|
-
|
|
706
|
-
raise HTTPException(
|
|
707
|
-
status_code=400,
|
|
708
|
-
detail="Invalid file-format specified. Valid formats are: 'csv', 'xlsx'"
|
|
709
|
-
)
|