flock-core 0.4.0b42__py3-none-any.whl → 0.4.0b44__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of flock-core might be problematic. Click here for more details.

Files changed (42) hide show
  1. flock/core/api/__init__.py +1 -2
  2. flock/core/api/endpoints.py +149 -217
  3. flock/core/api/main.py +134 -653
  4. flock/core/api/service.py +214 -0
  5. flock/core/flock.py +192 -133
  6. flock/webapp/app/api/agent_management.py +135 -164
  7. flock/webapp/app/api/execution.py +76 -85
  8. flock/webapp/app/api/flock_management.py +60 -33
  9. flock/webapp/app/chat.py +233 -0
  10. flock/webapp/app/config.py +6 -3
  11. flock/webapp/app/dependencies.py +95 -0
  12. flock/webapp/app/main.py +320 -906
  13. flock/webapp/app/services/flock_service.py +183 -161
  14. flock/webapp/run.py +178 -97
  15. flock/webapp/static/css/chat.css +227 -0
  16. flock/webapp/static/css/components.css +167 -0
  17. flock/webapp/static/css/header.css +39 -0
  18. flock/webapp/static/css/layout.css +46 -0
  19. flock/webapp/static/css/sidebar.css +127 -0
  20. flock/webapp/templates/base.html +6 -1
  21. flock/webapp/templates/chat.html +60 -0
  22. flock/webapp/templates/chat_settings.html +20 -0
  23. flock/webapp/templates/flock_editor.html +1 -1
  24. flock/webapp/templates/partials/_agent_detail_form.html +4 -4
  25. flock/webapp/templates/partials/_agent_list.html +2 -2
  26. flock/webapp/templates/partials/_agent_manager_view.html +3 -4
  27. flock/webapp/templates/partials/_chat_container.html +9 -0
  28. flock/webapp/templates/partials/_chat_messages.html +13 -0
  29. flock/webapp/templates/partials/_chat_settings_form.html +65 -0
  30. flock/webapp/templates/partials/_execution_form.html +2 -2
  31. flock/webapp/templates/partials/_execution_view_container.html +1 -1
  32. flock/webapp/templates/partials/_flock_properties_form.html +2 -2
  33. flock/webapp/templates/partials/_registry_viewer_content.html +3 -3
  34. flock/webapp/templates/partials/_sidebar.html +17 -1
  35. flock/webapp/templates/registry_viewer.html +3 -3
  36. {flock_core-0.4.0b42.dist-info → flock_core-0.4.0b44.dist-info}/METADATA +1 -1
  37. {flock_core-0.4.0b42.dist-info → flock_core-0.4.0b44.dist-info}/RECORD +40 -29
  38. flock/webapp/static/css/custom.css +0 -612
  39. flock/webapp/templates/partials/_agent_manager_view_old.html +0 -19
  40. {flock_core-0.4.0b42.dist-info → flock_core-0.4.0b44.dist-info}/WHEEL +0 -0
  41. {flock_core-0.4.0b42.dist-info → flock_core-0.4.0b44.dist-info}/entry_points.txt +0 -0
  42. {flock_core-0.4.0b42.dist-info → flock_core-0.4.0b44.dist-info}/licenses/LICENSE +0 -0
@@ -1,11 +1,10 @@
1
1
  # src/flock/core/api/__init__.py
2
2
  """Flock API Server components."""
3
3
 
4
- from .main import FlockAPI
4
+
5
5
  from .models import FlockAPIRequest, FlockAPIResponse
6
6
 
7
7
  __all__ = [
8
- "FlockAPI",
9
8
  "FlockAPIRequest",
10
9
  "FlockAPIResponse",
11
10
  ]
@@ -1,23 +1,26 @@
1
1
  # src/flock/core/api/endpoints.py
2
- """FastAPI endpoints for the Flock API."""
2
+ """FastAPI endpoints for the Flock API, using FastAPI's dependency injection."""
3
3
 
4
- import html # For escaping
5
4
  import uuid
6
- from typing import TYPE_CHECKING # Added Any for type hinting clarity
5
+ from typing import TYPE_CHECKING
7
6
 
8
7
  from fastapi import (
9
8
  APIRouter,
10
9
  BackgroundTasks,
10
+ Depends, # Crucial import for dependency injection
11
11
  HTTPException,
12
- Request as FastAPIRequest,
13
12
  )
14
13
 
15
- # Import HTMLResponse for the UI form endpoint
16
- from fastapi.responses import HTMLResponse
17
-
18
14
  from flock.core.logging.logging import get_logger
19
15
 
20
- # Import models and UI utils
16
+ # Import the dependency provider functions
17
+ # This assumes you'll create 'src/flock/webapp/app/dependencies.py' (or similar)
18
+ # with get_flock_instance and get_run_store functions.
19
+ # For now, to make this file runnable in isolation for generation,
20
+ # we might need temporary stubs or to adjust the import path later.
21
+ # Let's assume a common location for dependencies.
22
+ from flock.webapp.app.dependencies import get_flock_instance, get_run_store
23
+
21
24
  from .models import (
22
25
  FlockAPIRequest,
23
26
  FlockAPIResponse,
@@ -25,148 +28,196 @@ from .models import (
25
28
  FlockBatchResponse,
26
29
  )
27
30
 
28
- # Import UI utils - assuming they are now in ui/utils.py
29
-
30
- # Use TYPE_CHECKING to avoid circular imports for type hints
31
31
  if TYPE_CHECKING:
32
32
  from flock.core.flock import Flock
33
33
 
34
- from .main import FlockAPI
35
34
  from .run_store import RunStore
35
+ # We also need the _run_flock, _run_batch, _type_convert_inputs methods.
36
+ # These will be part of the Flock instance itself or helper functions
37
+ # that take Flock/RunStore as arguments. For now, let's assume they
38
+ # are methods on the Flock instance or can be called statically with it.
36
39
 
37
40
  logger = get_logger("api.endpoints")
38
41
 
39
-
40
- # Factory function to create the router with dependencies
41
- def create_api_router(flock_api: "FlockAPI") -> APIRouter:
42
- """Creates the APIRouter and defines endpoints, injecting dependencies."""
42
+ # The create_api_router function no longer needs to be passed the FlockAPI instance
43
+ # as dependencies will be injected directly into the route handlers.
44
+ def create_api_router() -> APIRouter:
45
+ """Creates the APIRouter for core Flock API endpoints."""
43
46
  router = APIRouter()
44
- # Get dependencies from the main FlockAPI instance passed in
45
- run_store: RunStore = flock_api.run_store
46
- flock_instance: Flock = flock_api.flock
47
47
 
48
48
  # --- API Endpoints ---
49
- @router.post("/run/flock", response_model=FlockAPIResponse, tags=["API"])
50
- async def run_flock_json(
51
- request: FlockAPIRequest, background_tasks: BackgroundTasks
49
+ @router.post("/run/flock", response_model=FlockAPIResponse, tags=["Flock API Core"])
50
+ async def run_flock_workflow(
51
+ request_model: FlockAPIRequest, # Renamed from 'request' to avoid conflict with FastAPI's Request
52
+ background_tasks: BackgroundTasks,
53
+ flock_instance: "Flock" = Depends(get_flock_instance), # Injected
54
+ run_store: "RunStore" = Depends(get_run_store) # Injected
52
55
  ):
53
- """Run a flock workflow starting with the specified agent (expects JSON)."""
56
+ """Run a flock workflow starting with the specified agent."""
54
57
  run_id = None
55
58
  try:
56
59
  run_id = str(uuid.uuid4())
57
- run_store.create_run(run_id) # Use RunStore
58
- response = run_store.get_run(
59
- run_id
60
- ) # Get initial response from store
60
+ run_store.create_run(run_id)
61
+ response_data = run_store.get_run(run_id)
62
+
63
+ if not response_data:
64
+ logger.error(f"Failed to create run record for run_id: {run_id}")
65
+ raise HTTPException(status_code=500, detail="Failed to initialize run.")
61
66
 
62
- processed_inputs = request.inputs if request.inputs else {}
67
+ processed_inputs = request_model.inputs if request_model.inputs else {}
63
68
  logger.info(
64
- f"API request: run flock '{request.agent_name}' (run_id: {run_id})",
69
+ f"API request: run flock '{request_model.agent_name}' (run_id: {run_id})",
65
70
  inputs=processed_inputs,
66
71
  )
67
72
 
68
- if request.async_run:
73
+ # The _run_flock logic now needs to be called. It could be a method on
74
+ # flock_instance, or a static/helper function that takes flock_instance.
75
+ # Let's assume it's a method on flock_instance for now.
76
+ # To avoid direct calls to private-like methods from router,
77
+ # Flock class would expose a public method that handles this internal logic.
78
+ # For this refactoring step, let's assume a helper `_execute_flock_run` exists
79
+ # that we can call.
80
+
81
+ async def _execute_flock_run_task(run_id_task, agent_name_task, inputs_task):
82
+ # This is a simplified version of what was in FlockAPI._run_flock
83
+ try:
84
+ if agent_name_task not in flock_instance.agents:
85
+ raise ValueError(f"Starting agent '{agent_name_task}' not found")
86
+ # Type conversion would ideally be part of the Flock's run logic
87
+ # or a utility. For now, assume it's handled or simple.
88
+ # typed_inputs = flock_instance._type_convert_inputs(agent_name_task, inputs_task) # Example if kept on Flock
89
+ typed_inputs = inputs_task # Simplified for now
90
+
91
+ result = await flock_instance.run_async(
92
+ start_agent=agent_name_task, input=typed_inputs
93
+ )
94
+ run_store.update_run_result(run_id_task, result)
95
+ except Exception as e_task:
96
+ logger.error(f"Error in background flock run {run_id_task}: {e_task!s}", exc_info=True)
97
+ run_store.update_run_status(run_id_task, "failed", str(e_task))
98
+
99
+
100
+ if request_model.async_run:
69
101
  logger.debug(
70
- f"Running flock '{request.agent_name}' asynchronously (run_id: {run_id})"
102
+ f"Running flock '{request_model.agent_name}' asynchronously (run_id: {run_id})"
71
103
  )
72
- # Call the helper method on the passed FlockAPI instance
73
104
  background_tasks.add_task(
74
- flock_api._run_flock,
105
+ _execute_flock_run_task,
75
106
  run_id,
76
- request.agent_name,
107
+ request_model.agent_name,
77
108
  processed_inputs,
78
109
  )
79
110
  run_store.update_run_status(run_id, "running")
80
- response.status = "running" # Update local response copy too
111
+ response_data.status = "running"
81
112
  else:
82
113
  logger.debug(
83
- f"Running flock '{request.agent_name}' synchronously (run_id: {run_id})"
114
+ f"Running flock '{request_model.agent_name}' synchronously (run_id: {run_id})"
84
115
  )
85
- # Call the helper method on the passed FlockAPI instance
86
- await flock_api._run_flock(
87
- run_id, request.agent_name, processed_inputs
116
+ await _execute_flock_run_task(
117
+ run_id, request_model.agent_name, processed_inputs
88
118
  )
89
- response = run_store.get_run(
90
- run_id
91
- ) # Fetch updated status/result
119
+ response_data = run_store.get_run(run_id)
92
120
 
93
- return response
121
+ if not response_data:
122
+ logger.error(f"Run data lost for run_id: {run_id} after execution.")
123
+ raise HTTPException(status_code=500, detail="Run data lost after execution.")
124
+
125
+ return response_data
94
126
  except ValueError as ve:
95
- logger.error(f"Value error starting run: {ve}")
96
- if run_id:
97
- run_store.update_run_status(run_id, "failed", str(ve))
127
+ logger.error(f"Value error starting run for agent '{request_model.agent_name}': {ve}", exc_info=True)
128
+ if run_id: run_store.update_run_status(run_id, "failed", str(ve))
98
129
  raise HTTPException(status_code=400, detail=str(ve))
99
130
  except Exception as e:
100
- error_msg = f"Internal server error: {type(e).__name__}"
101
- logger.error(f"Error starting run: {e!s}", exc_info=True)
102
- if run_id:
103
- run_store.update_run_status(run_id, "failed", error_msg)
131
+ error_msg = f"Internal server error during flock run: {type(e).__name__}"
132
+ logger.error(f"Error starting flock run: {e!s}", exc_info=True)
133
+ if run_id: run_store.update_run_status(run_id, "failed", error_msg)
104
134
  raise HTTPException(status_code=500, detail=error_msg)
105
135
 
106
- @router.post("/run/batch", response_model=FlockBatchResponse, tags=["API"])
107
- async def run_batch_json(
108
- request: FlockBatchRequest, background_tasks: BackgroundTasks
136
+ @router.post("/run/batch", response_model=FlockBatchResponse, tags=["Flock API Core"])
137
+ async def run_batch_workflow(
138
+ request_model: FlockBatchRequest, # Renamed from 'request'
139
+ background_tasks: BackgroundTasks,
140
+ flock_instance: "Flock" = Depends(get_flock_instance),
141
+ run_store: "RunStore" = Depends(get_run_store)
109
142
  ):
110
- """Run a batch of inputs through the flock workflow (expects JSON)."""
143
+ """Run a batch of inputs through the flock workflow."""
111
144
  batch_id = None
112
145
  try:
113
- # Validate agent exists
114
- if request.agent_name not in flock_instance.agents:
115
- raise ValueError(f"Agent '{request.agent_name}' not found")
146
+ if request_model.agent_name not in flock_instance.agents:
147
+ raise ValueError(f"Agent '{request_model.agent_name}' not found in current Flock.")
116
148
 
117
- # Validate batch inputs
118
- if (
119
- isinstance(request.batch_inputs, list)
120
- and not request.batch_inputs
121
- ):
122
- raise ValueError("Batch inputs list cannot be empty")
149
+ if isinstance(request_model.batch_inputs, list) and not request_model.batch_inputs:
150
+ raise ValueError("Batch inputs list cannot be empty if provided as a list.")
123
151
 
124
152
  batch_id = str(uuid.uuid4())
125
- run_store.create_batch(batch_id) # Use RunStore
126
- response = run_store.get_batch(
127
- batch_id
128
- ) # Get initial response from store
153
+ run_store.create_batch(batch_id)
154
+ response_data = run_store.get_batch(batch_id)
155
+
156
+ if not response_data:
157
+ logger.error(f"Failed to create batch record for batch_id: {batch_id}")
158
+ raise HTTPException(status_code=500, detail="Failed to initialize batch run.")
129
159
 
130
- # Log batch size for monitoring
131
160
  batch_size = (
132
- len(request.batch_inputs)
133
- if isinstance(request.batch_inputs, list)
161
+ len(request_model.batch_inputs)
162
+ if isinstance(request_model.batch_inputs, list)
134
163
  else "CSV/DataFrame"
135
164
  )
136
165
  logger.info(
137
- f"API request: run batch with '{request.agent_name}' (batch_id: {batch_id})",
166
+ f"API request: run batch with '{request_model.agent_name}' (batch_id: {batch_id})",
138
167
  batch_size=batch_size,
139
168
  )
140
169
 
141
- # Always run batch processing asynchronously
170
+ async def _execute_flock_batch_task(batch_id_task, batch_request_task: FlockBatchRequest):
171
+ # This is a simplified version of what was in FlockAPI._run_batch
172
+ try:
173
+ # Directly use the flock_instance's run_batch_async method
174
+ results = await flock_instance.run_batch_async(
175
+ start_agent=batch_request_task.agent_name,
176
+ batch_inputs=batch_request_task.batch_inputs,
177
+ input_mapping=batch_request_task.input_mapping,
178
+ static_inputs=batch_request_task.static_inputs,
179
+ parallel=batch_request_task.parallel,
180
+ max_workers=batch_request_task.max_workers,
181
+ use_temporal=batch_request_task.use_temporal,
182
+ box_results=batch_request_task.box_results,
183
+ return_errors=batch_request_task.return_errors,
184
+ silent_mode=True, # API batch runs should be internally silent
185
+ write_to_csv=None # API handles CSV writing based on request if needed, not here
186
+ )
187
+ run_store.update_batch_result(batch_id_task, results)
188
+ logger.info(f"Batch run completed (batch_id: {batch_id_task})", num_results=len(results))
189
+ except Exception as e_task:
190
+ logger.error(f"Error in background flock batch {batch_id_task}: {e_task!s}", exc_info=True)
191
+ run_store.update_batch_status(batch_id_task, "failed", str(e_task))
192
+
142
193
  logger.debug(
143
- f"Running batch with '{request.agent_name}' asynchronously (batch_id: {batch_id})"
194
+ f"Running batch with '{request_model.agent_name}' asynchronously (batch_id: {batch_id})"
144
195
  )
145
- # Call the helper method on the passed FlockAPI instance
146
196
  background_tasks.add_task(
147
- flock_api._run_batch,
197
+ _execute_flock_batch_task,
148
198
  batch_id,
149
- request,
199
+ request_model, # Pass the Pydantic request model
150
200
  )
151
201
  run_store.update_batch_status(batch_id, "running")
152
- response.status = "running" # Update local response copy too
202
+ response_data.status = "running"
153
203
 
154
- return response
204
+ return response_data
155
205
  except ValueError as ve:
156
- error_msg = f"Value error starting batch: {ve}"
157
- logger.error(error_msg)
158
- if batch_id:
159
- run_store.update_batch_status(batch_id, "failed", str(ve))
206
+ error_msg = f"Value error starting batch for agent '{request_model.agent_name}': {ve}"
207
+ logger.error(error_msg, exc_info=True)
208
+ if batch_id: run_store.update_batch_status(batch_id, "failed", str(ve))
160
209
  raise HTTPException(status_code=400, detail=str(ve))
161
210
  except Exception as e:
162
- error_msg = f"Internal server error: {type(e).__name__}: {e!s}"
211
+ error_msg = f"Internal server error during batch run: {type(e).__name__}: {e!s}"
163
212
  logger.error(error_msg, exc_info=True)
164
- if batch_id:
165
- run_store.update_batch_status(batch_id, "failed", error_msg)
213
+ if batch_id: run_store.update_batch_status(batch_id, "failed", error_msg)
166
214
  raise HTTPException(status_code=500, detail=error_msg)
167
215
 
168
- @router.get("/run/{run_id}", response_model=FlockAPIResponse, tags=["API"])
169
- async def get_run_status(run_id: str):
216
+ @router.get("/run/{run_id}", response_model=FlockAPIResponse, tags=["Flock API Core"])
217
+ async def get_run_status(
218
+ run_id: str,
219
+ run_store: "RunStore" = Depends(get_run_store)
220
+ ):
170
221
  """Get the status of a specific run."""
171
222
  logger.debug(f"API request: get status for run_id: {run_id}")
172
223
  run_data = run_store.get_run(run_id)
@@ -175,148 +226,29 @@ def create_api_router(flock_api: "FlockAPI") -> APIRouter:
175
226
  raise HTTPException(status_code=404, detail="Run not found")
176
227
  return run_data
177
228
 
178
- @router.get(
179
- "/batch/{batch_id}", response_model=FlockBatchResponse, tags=["API"]
180
- )
181
- async def get_batch_status(batch_id: str):
182
- """Get the status of a specific batch run.
183
-
184
- Returns details including:
185
- - Total number of items in the batch
186
- - Number of completed items
187
- - Percentage of completion
188
- - Any partial results available (for running batches)
189
- - Complete results (for completed batches)
190
- """
229
+ @router.get("/batch/{batch_id}", response_model=FlockBatchResponse, tags=["Flock API Core"])
230
+ async def get_batch_status(
231
+ batch_id: str,
232
+ run_store: "RunStore" = Depends(get_run_store)
233
+ ):
234
+ """Get the status and results of a specific batch run."""
191
235
  logger.debug(f"API request: get status for batch_id: {batch_id}")
192
236
  batch_data = run_store.get_batch(batch_id)
193
237
  if not batch_data:
194
238
  logger.warning(f"Batch ID not found: {batch_id}")
195
239
  raise HTTPException(status_code=404, detail="Batch not found")
196
-
197
- # Add useful info for client display
198
- extra_info = {
199
- "status": batch_data.status,
200
- "completed_items": batch_data.completed_items,
201
- "total_items": batch_data.total_items,
202
- "progress_percentage": round(batch_data.progress_percentage, 1),
203
- "has_partial_results": len(batch_data.results) > 0
204
- and batch_data.status == "running",
205
- "has_error": batch_data.error is not None,
206
- }
207
- logger.debug(f"Returning batch status: {extra_info}")
208
-
209
240
  return batch_data
210
241
 
211
- @router.get("/agents", tags=["API"])
212
- async def list_agents():
213
- """List all available agents."""
242
+ @router.get("/agents", tags=["Flock API Core"])
243
+ async def list_agents(
244
+ flock_instance: "Flock" = Depends(get_flock_instance)
245
+ ):
246
+ """List all available agents in the currently loaded Flock."""
214
247
  logger.debug("API request: list agents")
215
- # Access flock instance via factory closure
216
248
  agents_list = [
217
249
  {"name": agent.name, "description": agent.description or agent.name}
218
250
  for agent in flock_instance.agents.values()
219
251
  ]
220
252
  return {"agents": agents_list}
221
253
 
222
- # --- UI Form Endpoint ---
223
- @router.post("/ui/run-agent-form", response_class=HTMLResponse, tags=["UI"])
224
- async def run_flock_form(fastapi_req: FastAPIRequest):
225
- """Endpoint to handle form submissions from the UI."""
226
- run_id = None
227
- try:
228
- form_data = await fastapi_req.form()
229
- agent_name = form_data.get("agent_name")
230
- if not agent_name:
231
- logger.warning("UI form submission missing agent_name")
232
- return HTMLResponse(
233
- '<div id="result-content" class="error-message">Error: Agent name not provided.</div>',
234
- status_code=400,
235
- )
236
-
237
- logger.info(f"UI Form submission for agent: {agent_name}")
238
- form_inputs = {}
239
- # Access flock instance via factory closure
240
- agent_def = flock_instance.agents.get(agent_name)
241
- # Use helper from flock_api instance for parsing
242
- defined_input_fields = (
243
- flock_api._parse_input_spec(agent_def.input or "")
244
- if agent_def
245
- else []
246
- )
247
-
248
- for key, value in form_data.items():
249
- if key.startswith("inputs."):
250
- form_inputs[key[len("inputs.") :]] = value
251
- for field in defined_input_fields: # Handle checkboxes
252
- if (
253
- field["html_type"] == "checkbox"
254
- and field["name"] not in form_inputs
255
- ):
256
- form_inputs[field["name"]] = False
257
- elif (
258
- field["html_type"] == "checkbox"
259
- and field["name"] in form_inputs
260
- ):
261
- form_inputs[field["name"]] = True
262
-
263
- logger.debug(f"Parsed form inputs for UI run: {form_inputs}")
264
- run_id = str(uuid.uuid4())
265
- run_store.create_run(run_id)
266
- logger.debug(
267
- f"Running flock '{agent_name}' synchronously from UI (run_id: {run_id})"
268
- )
269
-
270
- # Call helper method on flock_api instance
271
- await flock_api._run_flock(run_id, agent_name, form_inputs)
272
-
273
- final_status = run_store.get_run(run_id)
274
- if final_status and final_status.status == "completed":
275
- # Use helper from flock_api instance for formatting
276
- formatted_html = flock_api._format_result_to_html(
277
- final_status.result
278
- )
279
- logger.info(f"UI run completed successfully (run_id: {run_id})")
280
- return HTMLResponse(
281
- f"<div id='result-content'>{formatted_html}</div>"
282
- ) # Wrap in target div
283
- elif final_status and final_status.status == "failed":
284
- logger.error(
285
- f"UI run failed (run_id: {run_id}): {final_status.error}"
286
- )
287
- error_msg = html.escape(final_status.error or "Unknown error")
288
- return HTMLResponse(
289
- f"<div id='result-content' class='error-message'>Run Failed: {error_msg}</div>",
290
- status_code=500,
291
- )
292
- else:
293
- status_str = (
294
- final_status.status if final_status else "Not Found"
295
- )
296
- logger.warning(
297
- f"UI run {run_id} ended in unexpected state: {status_str}"
298
- )
299
- return HTMLResponse(
300
- f"<div id='result-content' class='error-message'>Run ended unexpectedly. Status: {status_str}</div>",
301
- status_code=500,
302
- )
303
-
304
- except ValueError as ve:
305
- logger.error(f"Value error processing UI form run: {ve}")
306
- if run_id:
307
- run_store.update_run_status(run_id, "failed", str(ve))
308
- return HTMLResponse(
309
- f"<div id='result-content' class='error-message'>Error: {html.escape(str(ve))}</div>",
310
- status_code=400,
311
- )
312
- except Exception as e:
313
- error_msg = f"Internal server error: {type(e).__name__}"
314
- logger.error(f"Error processing UI form run: {e!s}", exc_info=True)
315
- if run_id:
316
- run_store.update_run_status(run_id, "failed", error_msg)
317
- return HTMLResponse(
318
- f"<div id='result-content' class='error-message'>{html.escape(error_msg)}</div>",
319
- status_code=500,
320
- )
321
-
322
254
  return router