flock-core 0.4.522__py3-none-any.whl → 0.4.524__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/webapp/app/api/execution.py +13 -9
- flock/webapp/app/services/sharing_store.py +123 -104
- {flock_core-0.4.522.dist-info → flock_core-0.4.524.dist-info}/METADATA +1 -1
- {flock_core-0.4.522.dist-info → flock_core-0.4.524.dist-info}/RECORD +7 -7
- {flock_core-0.4.522.dist-info → flock_core-0.4.524.dist-info}/WHEEL +0 -0
- {flock_core-0.4.522.dist-info → flock_core-0.4.524.dist-info}/entry_points.txt +0 -0
- {flock_core-0.4.522.dist-info → flock_core-0.4.524.dist-info}/licenses/LICENSE +0 -0
|
@@ -10,9 +10,9 @@ from fastapi import ( # Ensure Form and HTTPException are imported
|
|
|
10
10
|
Form,
|
|
11
11
|
Request,
|
|
12
12
|
)
|
|
13
|
+
from fastapi.encoders import jsonable_encoder
|
|
13
14
|
from fastapi.responses import HTMLResponse
|
|
14
15
|
from fastapi.templating import Jinja2Templates
|
|
15
|
-
from pydantic import BaseModel
|
|
16
16
|
|
|
17
17
|
if TYPE_CHECKING:
|
|
18
18
|
from flock.core.flock import Flock
|
|
@@ -152,10 +152,13 @@ async def htmx_run_flock(
|
|
|
152
152
|
return HTMLResponse(f"<p class='error'>Error processing inputs for {start_agent_name}: {e_parse}</p>")
|
|
153
153
|
|
|
154
154
|
result_data = await run_current_flock_service(start_agent_name, inputs, request.app.state)
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
raw_json_for_template = json.dumps(
|
|
158
|
+
jsonable_encoder(result_data), # ← converts every nested BaseModel, datetime, etc.
|
|
159
|
+
indent=2,
|
|
160
|
+
ensure_ascii=False
|
|
161
|
+
)
|
|
159
162
|
# Unescape newlines for proper display in HTML <pre> tag
|
|
160
163
|
result_data_raw_json_str = raw_json_for_template.replace('\\n', '\n')
|
|
161
164
|
root_path = request.scope.get("root_path", "")
|
|
@@ -219,10 +222,11 @@ async def htmx_run_shared_flock(
|
|
|
219
222
|
|
|
220
223
|
shared_logger.info(f"HTMX Run Shared: Executing agent '{start_agent_name}' in pre-loaded Flock '{temp_flock.name}'. Inputs: {list(inputs.keys())}")
|
|
221
224
|
result_data = await temp_flock.run_async(start_agent=start_agent_name, input=inputs, box_result=False)
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
225
|
+
raw_json_for_template = json.dumps(
|
|
226
|
+
jsonable_encoder(result_data), # ← converts every nested BaseModel, datetime, etc.
|
|
227
|
+
indent=2,
|
|
228
|
+
ensure_ascii=False
|
|
229
|
+
)
|
|
226
230
|
# Unescape newlines for proper display in HTML <pre> tag
|
|
227
231
|
result_data_raw_json_str = raw_json_for_template.replace('\\n', '\n')
|
|
228
232
|
shared_logger.info(f"HTMX Run Shared: Agent '{start_agent_name}' executed. Result keys: {list(result_data.keys()) if isinstance(result_data, dict) else 'N/A'}")
|
|
@@ -227,136 +227,155 @@ class SQLiteSharedLinkStore(SharedLinkStoreInterface):
|
|
|
227
227
|
except sqlite3.Error as e:
|
|
228
228
|
logger.error(f"SQLite error saving feedback {record.feedback_id}: {e}", exc_info=True)
|
|
229
229
|
raise
|
|
230
|
+
# flock/webapp/app/services/sharing_store.py ← replace only this class
|
|
231
|
+
|
|
232
|
+
# ---------------------------------------------------------------------------
|
|
233
|
+
# Azure Table + Blob implementation
|
|
234
|
+
# ---------------------------------------------------------------------------
|
|
235
|
+
|
|
236
|
+
try:
|
|
237
|
+
from azure.storage.blob.aio import BlobServiceClient
|
|
238
|
+
AZURE_BLOB_AVAILABLE = True
|
|
239
|
+
except ImportError: # blob SDK not installed
|
|
240
|
+
AZURE_BLOB_AVAILABLE = False
|
|
241
|
+
BlobServiceClient = None
|
|
230
242
|
|
|
231
243
|
class AzureTableSharedLinkStore(SharedLinkStoreInterface):
|
|
232
|
-
"""Azure Table
|
|
244
|
+
"""Store configs in Azure Table; store large flock YAML in Blob Storage."""
|
|
245
|
+
|
|
246
|
+
_TABLE_NAME = "flocksharedlinks"
|
|
247
|
+
_FEEDBACK_TBL_NAME = "flockfeedback"
|
|
248
|
+
_CONTAINER_NAME = "flocksharedlinkdefs" # blobs live here
|
|
249
|
+
_PARTITION_KEY = "shared_links"
|
|
233
250
|
|
|
234
251
|
def __init__(self, connection_string: str):
|
|
235
|
-
"""Initialize Azure Table Storage store with connection string."""
|
|
236
252
|
if not AZURE_AVAILABLE:
|
|
237
|
-
raise ImportError("
|
|
253
|
+
raise ImportError("pip install azure-data-tables")
|
|
254
|
+
if not AZURE_BLOB_AVAILABLE:
|
|
255
|
+
raise ImportError("pip install azure-storage-blob")
|
|
238
256
|
|
|
239
257
|
self.connection_string = connection_string
|
|
240
|
-
self.
|
|
241
|
-
self.
|
|
242
|
-
self.feedback_table_name = "flockfeedback"
|
|
243
|
-
logger.info("AzureTableSharedLinkStore initialized")
|
|
258
|
+
self.table_svc = TableServiceClient.from_connection_string(connection_string)
|
|
259
|
+
self.blob_svc = BlobServiceClient.from_connection_string(connection_string)
|
|
244
260
|
|
|
261
|
+
# ------------------------------------------------------------------ init
|
|
245
262
|
async def initialize(self) -> None:
|
|
246
|
-
|
|
263
|
+
# 1. Azure Tables ----------------------------------------------------
|
|
247
264
|
try:
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
except ResourceExistsError:
|
|
253
|
-
logger.debug(f"Azure Table already exists: {self.shared_links_table_name}")
|
|
254
|
-
|
|
255
|
-
# Create feedback table
|
|
256
|
-
try:
|
|
257
|
-
await self.table_service_client.create_table(self.feedback_table_name)
|
|
258
|
-
logger.info(f"Created Azure Table: {self.feedback_table_name}")
|
|
259
|
-
except ResourceExistsError:
|
|
260
|
-
logger.debug(f"Azure Table already exists: {self.feedback_table_name}")
|
|
261
|
-
|
|
262
|
-
logger.info("Azure Table Storage initialized successfully")
|
|
263
|
-
except Exception as e:
|
|
264
|
-
logger.error(f"Error initializing Azure Table Storage: {e}", exc_info=True)
|
|
265
|
-
raise
|
|
265
|
+
await self.table_svc.create_table(self._TABLE_NAME)
|
|
266
|
+
logger.info("Created Azure Table '%s'", self._TABLE_NAME)
|
|
267
|
+
except ResourceExistsError:
|
|
268
|
+
logger.debug("Azure Table '%s' already exists", self._TABLE_NAME)
|
|
266
269
|
|
|
267
|
-
async def save_config(self, config: SharedLinkConfig) -> SharedLinkConfig:
|
|
268
|
-
"""Saves a shared link configuration to Azure Table Storage."""
|
|
269
270
|
try:
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
"chat_history_key": config.chat_history_key,
|
|
282
|
-
"chat_response_key": config.chat_response_key,
|
|
283
|
-
}
|
|
284
|
-
|
|
285
|
-
await table_client.upsert_entity(entity)
|
|
286
|
-
logger.info(f"Saved shared link config to Azure Table Storage for ID: {config.share_id} with type: {config.share_type}")
|
|
287
|
-
return config
|
|
288
|
-
except Exception as e:
|
|
289
|
-
logger.error(f"Error saving config to Azure Table Storage for ID {config.share_id}: {e}", exc_info=True)
|
|
290
|
-
raise
|
|
271
|
+
await self.table_svc.create_table(self._FEEDBACK_TBL_NAME)
|
|
272
|
+
logger.info("Created Azure Table '%s'", self._FEEDBACK_TBL_NAME)
|
|
273
|
+
except ResourceExistsError:
|
|
274
|
+
logger.debug("Azure Table '%s' already exists", self._FEEDBACK_TBL_NAME)
|
|
275
|
+
|
|
276
|
+
# 2. Blob container --------------------------------------------------
|
|
277
|
+
try:
|
|
278
|
+
await self.blob_svc.create_container(self._CONTAINER_NAME)
|
|
279
|
+
logger.info("Created Blob container '%s'", self._CONTAINER_NAME)
|
|
280
|
+
except ResourceExistsError:
|
|
281
|
+
logger.debug("Blob container '%s' already exists", self._CONTAINER_NAME)
|
|
291
282
|
|
|
283
|
+
# ------------------------------------------------------------- save_config
|
|
284
|
+
async def save_config(self, config: SharedLinkConfig) -> SharedLinkConfig:
|
|
285
|
+
"""Upload YAML to Blob, then upsert table row containing the blob name."""
|
|
286
|
+
blob_name = f"{config.share_id}.yaml"
|
|
287
|
+
blob_client = self.blob_svc.get_blob_client(self._CONTAINER_NAME, blob_name)
|
|
288
|
+
|
|
289
|
+
# 1. Upload flock_definition (overwrite in case of retry)
|
|
290
|
+
await blob_client.upload_blob(config.flock_definition,
|
|
291
|
+
overwrite=True,
|
|
292
|
+
content_type="text/yaml")
|
|
293
|
+
logger.debug("Uploaded blob '%s' (%d bytes)",
|
|
294
|
+
blob_name, len(config.flock_definition.encode()))
|
|
295
|
+
|
|
296
|
+
# 2. Persist lightweight record in the table
|
|
297
|
+
tbl_client = self.table_svc.get_table_client(self._TABLE_NAME)
|
|
298
|
+
entity = {
|
|
299
|
+
"PartitionKey": self._PARTITION_KEY,
|
|
300
|
+
"RowKey": config.share_id,
|
|
301
|
+
"agent_name": config.agent_name,
|
|
302
|
+
"created_at": config.created_at.isoformat(),
|
|
303
|
+
"share_type": config.share_type,
|
|
304
|
+
"chat_message_key": config.chat_message_key,
|
|
305
|
+
"chat_history_key": config.chat_history_key,
|
|
306
|
+
"chat_response_key": config.chat_response_key,
|
|
307
|
+
# NEW – just a few bytes, well under 64 KiB
|
|
308
|
+
"flock_blob_name": blob_name,
|
|
309
|
+
}
|
|
310
|
+
await tbl_client.upsert_entity(entity)
|
|
311
|
+
logger.info("Saved shared link %s → blob '%s'", config.share_id, blob_name)
|
|
312
|
+
return config
|
|
313
|
+
|
|
314
|
+
# -------------------------------------------------------------- get_config
|
|
292
315
|
async def get_config(self, share_id: str) -> SharedLinkConfig | None:
|
|
293
|
-
|
|
316
|
+
tbl_client = self.table_svc.get_table_client(self._TABLE_NAME)
|
|
294
317
|
try:
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
entity = await table_client.get_entity(partition_key="shared_links", row_key=share_id)
|
|
298
|
-
|
|
299
|
-
logger.debug(f"Retrieved shared link config from Azure Table Storage for ID: {share_id}")
|
|
300
|
-
return SharedLinkConfig(
|
|
301
|
-
share_id=entity["share_id"],
|
|
302
|
-
agent_name=entity["agent_name"],
|
|
303
|
-
created_at=entity["created_at"], # Pydantic will parse from ISO format
|
|
304
|
-
flock_definition=entity["flock_definition"],
|
|
305
|
-
share_type=entity.get("share_type", "agent_run"),
|
|
306
|
-
chat_message_key=entity.get("chat_message_key"),
|
|
307
|
-
chat_history_key=entity.get("chat_history_key"),
|
|
308
|
-
chat_response_key=entity.get("chat_response_key"),
|
|
309
|
-
)
|
|
318
|
+
entity = await tbl_client.get_entity(self._PARTITION_KEY, share_id)
|
|
310
319
|
except ResourceNotFoundError:
|
|
311
|
-
logger.debug(
|
|
320
|
+
logger.debug("No config entity for id '%s'", share_id)
|
|
312
321
|
return None
|
|
322
|
+
|
|
323
|
+
blob_name = entity["flock_blob_name"]
|
|
324
|
+
blob_client = self.blob_svc.get_blob_client(self._CONTAINER_NAME, blob_name)
|
|
325
|
+
try:
|
|
326
|
+
blob_bytes = await (await blob_client.download_blob()).readall()
|
|
327
|
+
flock_yaml = blob_bytes.decode()
|
|
313
328
|
except Exception as e:
|
|
314
|
-
logger.error(
|
|
315
|
-
|
|
329
|
+
logger.error("Cannot download blob '%s' for share_id=%s: %s",
|
|
330
|
+
blob_name, share_id, e, exc_info=True)
|
|
331
|
+
raise
|
|
316
332
|
|
|
333
|
+
return SharedLinkConfig(
|
|
334
|
+
share_id = share_id,
|
|
335
|
+
agent_name = entity["agent_name"],
|
|
336
|
+
created_at = entity["created_at"],
|
|
337
|
+
flock_definition = flock_yaml,
|
|
338
|
+
share_type = entity.get("share_type", "agent_run"),
|
|
339
|
+
chat_message_key = entity.get("chat_message_key"),
|
|
340
|
+
chat_history_key = entity.get("chat_history_key"),
|
|
341
|
+
chat_response_key = entity.get("chat_response_key"),
|
|
342
|
+
)
|
|
343
|
+
|
|
344
|
+
# ----------------------------------------------------------- delete_config
|
|
317
345
|
async def delete_config(self, share_id: str) -> bool:
|
|
318
|
-
|
|
346
|
+
tbl_client = self.table_svc.get_table_client(self._TABLE_NAME)
|
|
319
347
|
try:
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
await table_client.delete_entity(partition_key="shared_links", row_key=share_id)
|
|
323
|
-
logger.info(f"Deleted shared link config from Azure Table Storage for ID: {share_id}")
|
|
324
|
-
return True
|
|
348
|
+
entity = await tbl_client.get_entity(self._PARTITION_KEY, share_id)
|
|
325
349
|
except ResourceNotFoundError:
|
|
326
|
-
logger.info(
|
|
327
|
-
return False
|
|
328
|
-
except Exception as e:
|
|
329
|
-
logger.error(f"Error deleting config from Azure Table Storage for ID {share_id}: {e}", exc_info=True)
|
|
350
|
+
logger.info("Delete: entity %s not found", share_id)
|
|
330
351
|
return False
|
|
331
352
|
|
|
332
|
-
|
|
353
|
+
# 1. Remove blob (ignore missing blob)
|
|
354
|
+
blob_name = entity["flock_blob_name"]
|
|
355
|
+
blob_client = self.blob_svc.get_blob_client(self._CONTAINER_NAME, blob_name)
|
|
356
|
+
try:
|
|
357
|
+
await blob_client.delete_blob(delete_snapshots="include")
|
|
358
|
+
logger.debug("Deleted blob '%s'", blob_name)
|
|
359
|
+
except ResourceNotFoundError:
|
|
360
|
+
logger.warning("Blob '%s' already gone", blob_name)
|
|
361
|
+
|
|
362
|
+
# 2. Remove table row
|
|
363
|
+
await tbl_client.delete_entity(self._PARTITION_KEY, share_id)
|
|
364
|
+
logger.info("Deleted shared link %s and its blob", share_id)
|
|
365
|
+
return True
|
|
333
366
|
|
|
367
|
+
# -------------------------------------------------------- save_feedback --
|
|
334
368
|
async def save_feedback(self, record: FeedbackRecord) -> FeedbackRecord:
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
"reason": record.reason,
|
|
346
|
-
"expected_response": record.expected_response,
|
|
347
|
-
"actual_response": record.actual_response,
|
|
348
|
-
"flock_name": record.flock_name,
|
|
349
|
-
"agent_name": record.agent_name,
|
|
350
|
-
"flock_definition": record.flock_definition,
|
|
351
|
-
"created_at": record.created_at.isoformat(),
|
|
352
|
-
}
|
|
353
|
-
|
|
354
|
-
await table_client.upsert_entity(entity)
|
|
355
|
-
logger.info(f"Saved feedback to Azure Table Storage: {record.feedback_id} (share={record.share_id})")
|
|
356
|
-
return record
|
|
357
|
-
except Exception as e:
|
|
358
|
-
logger.error(f"Error saving feedback to Azure Table Storage {record.feedback_id}: {e}", exc_info=True)
|
|
359
|
-
raise
|
|
369
|
+
tbl_client = self.table_svc.get_table_client(self._FEEDBACK_TBL_NAME)
|
|
370
|
+
entity = {
|
|
371
|
+
"PartitionKey": "feedback",
|
|
372
|
+
"RowKey": record.feedback_id,
|
|
373
|
+
**record.model_dump(exclude={"feedback_id"}) # all other fields
|
|
374
|
+
}
|
|
375
|
+
await tbl_client.upsert_entity(entity)
|
|
376
|
+
logger.info("Saved feedback %s", record.feedback_id)
|
|
377
|
+
return record
|
|
378
|
+
|
|
360
379
|
|
|
361
380
|
|
|
362
381
|
# ----------------------- Factory Function -----------------------
|
|
@@ -502,13 +502,13 @@ flock/webapp/app/theme_mapper.py,sha256=QzWwLWpED78oYp3FjZ9zxv1KxCyj43m8MZ0fhfzz
|
|
|
502
502
|
flock/webapp/app/utils.py,sha256=RF8DMKKAj1XPmm4txUdo2OdswI1ATQ7cqUm6G9JFDzA,2942
|
|
503
503
|
flock/webapp/app/api/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
504
504
|
flock/webapp/app/api/agent_management.py,sha256=5xqO94QjjAYvxImyjKV9EGUQOvo4n3eqs7pGwGPSQJ4,10394
|
|
505
|
-
flock/webapp/app/api/execution.py,sha256=
|
|
505
|
+
flock/webapp/app/api/execution.py,sha256=EeOhpABZ1STcRKIa8pXRqWeqtD4KS8KAWTNYbVns2FQ,13432
|
|
506
506
|
flock/webapp/app/api/flock_management.py,sha256=1o-6-36kTnUjI3am_BqLpdrcz0aqFXrxE-hQHIFcCsg,4869
|
|
507
507
|
flock/webapp/app/api/registry_viewer.py,sha256=IoInxJiRR0yFlecG_l2_eRc6l35RQQyEDMG9BcBkipY,1020
|
|
508
508
|
flock/webapp/app/services/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
509
509
|
flock/webapp/app/services/flock_service.py,sha256=olU1My3YYkrTCVIOYPgRted-8YgAop-Yi7G4gbRHTrg,14941
|
|
510
510
|
flock/webapp/app/services/sharing_models.py,sha256=XeJk1akILV_1l-cIUaG8k_eYhjV3EWBCWZ2kpwbdImA,3609
|
|
511
|
-
flock/webapp/app/services/sharing_store.py,sha256=
|
|
511
|
+
flock/webapp/app/services/sharing_store.py,sha256=Ogc2MWFS1FDu8GQ89txMP32DvWZ5NcJjQAv4mwEroYs,18121
|
|
512
512
|
flock/webapp/app/templates/theme_mapper.html,sha256=z8ZY7nmk6PiUGzD_-px7wSXcEnuBM121rMq6u-2oaCo,14249
|
|
513
513
|
flock/webapp/static/css/chat.css,sha256=Njc9gXfQzbXMrqtFJH2Yda-IQlwNPd2z4apXxzfA0sY,8169
|
|
514
514
|
flock/webapp/static/css/components.css,sha256=WnicEHy3ptPzggKmyG9_oZp3X30EMJBUW3KEXaiUCUE,6018
|
|
@@ -562,8 +562,8 @@ flock/workflow/agent_execution_activity.py,sha256=Gy6FtuVAjf0NiUXmC3syS2eJpNQF4R
|
|
|
562
562
|
flock/workflow/flock_workflow.py,sha256=iSUF_soFvWar0ffpkzE4irkDZRx0p4HnwmEBi_Ne2sY,9666
|
|
563
563
|
flock/workflow/temporal_config.py,sha256=3_8O7SDEjMsSMXsWJBfnb6XTp0TFaz39uyzSlMTSF_I,3988
|
|
564
564
|
flock/workflow/temporal_setup.py,sha256=YIHnSBntzOchHfMSh8hoLeNXrz3B1UbR14YrR6soM7A,1606
|
|
565
|
-
flock_core-0.4.
|
|
566
|
-
flock_core-0.4.
|
|
567
|
-
flock_core-0.4.
|
|
568
|
-
flock_core-0.4.
|
|
569
|
-
flock_core-0.4.
|
|
565
|
+
flock_core-0.4.524.dist-info/METADATA,sha256=hD_opwGCMB2Y8gDQGmnS9w_XRNKQwzCF65_EeXmjUMU,22786
|
|
566
|
+
flock_core-0.4.524.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
567
|
+
flock_core-0.4.524.dist-info/entry_points.txt,sha256=rWaS5KSpkTmWySURGFZk6PhbJ87TmvcFQDi2uzjlagQ,37
|
|
568
|
+
flock_core-0.4.524.dist-info/licenses/LICENSE,sha256=iYEqWy0wjULzM9GAERaybP4LBiPeu7Z1NEliLUdJKSc,1072
|
|
569
|
+
flock_core-0.4.524.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|