From f57fe132c6421539659f96f897004751cd11a1ee Mon Sep 17 00:00:00 2001 From: Andre Ratzenberger Date: Fri, 30 May 2025 09:18:26 +0200 Subject: [PATCH 1/7] feat: upate package versioning --- examples | 2 +- pyproject.toml | 20 ++++++++++++++------ uv.lock | 16 ++++++++-------- 3 files changed, 23 insertions(+), 15 deletions(-) diff --git a/examples b/examples index 8279319f5..028ef635e 160000 --- a/examples +++ b/examples @@ -1 +1 @@ -Subproject commit 8279319f5509a46ad04c053ffe5bd0c9fbd482d8 +Subproject commit 028ef635ede8afee1055d95cd0a431ebdd22f9fb diff --git a/pyproject.toml b/pyproject.toml index 285e3dfe7..0dde7fa33 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "flock-core" -version = "0.4.519" +version = "0.4.520" description = "Declarative LLM Orchestration at Scale" readme = "README.md" authors = [ @@ -45,7 +45,7 @@ dependencies = [ "thefuzz>=0.22.1", "tiktoken>=0.8.0", "toml>=0.10.2", - "tqdm>=4.67.1", + "tqdm>=4.60.1", "uvicorn>=0.34.0", "aiosqlite>=0.21.0", "markdown2>=2.5.3", @@ -54,15 +54,15 @@ dependencies = [ "opik>=1.7.26", "azure-data-tables>=12.7.0", "croniter>=6.0.0", - ] [project.optional-dependencies] basic-tools = [ - "docling>=2.18.0", + "docling>=2.34.0", "tavily-python>=0.5.0", "markdownify>=0.14.1", "duckduckgo-search>=7.3.2", + ] azure-tools = [ "azure-identity>=1.23.0", @@ -83,7 +83,7 @@ evaluation = [ "sentence-transformers>=3.4.1", ] all-tools = [ - "docling>=2.18.0", + "docling>=2.34.0", "tavily-python>=0.5.0", "markdownify>=0.14.1", "duckduckgo-search>=7.3.2", @@ -94,7 +94,7 @@ all-tools = [ "docker>=7.1.0", ] all = [ - "docling>=2.18.0", + "docling>=2.34.0", "tavily-python>=0.5.0", "markdownify>=0.14.1", "duckduckgo-search>=7.3.2", @@ -214,3 +214,11 @@ docs = ["_docs-build", "_docs-serve"] [tool.poe.tasks.clean] script = "poethepoet.scripts:rm('dist', 'htmlcov', 'logs','metrics','.mypy_cache', '.pytest_cache', './**/__pycache__')" + +[tool.uv.sources] +torch = { index = "pytorch" } + +[[tool.uv.index]] +name = "pytorch" +url = "https://download.pytorch.org/whl/cpu" +explicit = true \ No newline at end of file diff --git a/uv.lock b/uv.lock index d0b4d9c29..fc19fe94f 100644 --- a/uv.lock +++ b/uv.lock @@ -1005,7 +1005,7 @@ wheels = [ [[package]] name = "docling" -version = "2.33.0" +version = "2.34.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "beautifulsoup4" }, @@ -1035,9 +1035,9 @@ dependencies = [ { name = "tqdm" }, { name = "typer" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/36/d8/76740f7d40a28794365e76029e7379f7d1b5994e24aaac899c3bb280e426/docling-2.33.0.tar.gz", hash = "sha256:40c27df6a7f90b8fb4279a4094ac26c48f3dbd38ceda0d3208e6d63eda2e8660", size = 132120 } +sdist = { url = "https://files.pythonhosted.org/packages/9c/42/33334eeaa3d42a4b3406adc7bbef8c655d026c1c41ad0a8fa858bbb7ef1b/docling-2.34.0.tar.gz", hash = "sha256:52ad3c79b7e56e978fcdd8f2040fe739584e25e1c2fd5f9519fe3d51002de0d2", size = 135016 } wheels = [ - { url = "https://files.pythonhosted.org/packages/9c/23/6dddf454610c4dff5799f9becbbc752b8595169de57c02a1d39067f5f2b5/docling-2.33.0-py3-none-any.whl", hash = "sha256:6c0ee223c5da551adc5da15aae205aaebf78222780198cb5a21c85648da618ba", size = 169441 }, + { url = "https://files.pythonhosted.org/packages/be/b6/b855f19ab37a6f92b60a31a7db57160048f88a980aef4b97a5c260860ed4/docling-2.34.0-py3-none-any.whl", hash = "sha256:a69c368382cc824a5a5ad21882b8772f85117a25c81257a57d54582bf38b2810", size = 173153 }, ] [[package]] @@ -1282,7 +1282,7 @@ wheels = [ [[package]] name = "flock-core" -version = "0.4.518" +version = "0.4.519" source = { editable = "." } dependencies = [ { name = "aiosqlite" }, @@ -1426,9 +1426,9 @@ requires-dist = [ { name = "devtools", specifier = ">=0.12.2" }, { name = "docker", marker = "extra == 'all-tools'", specifier = ">=7.1.0" }, { name = "docker", marker = "extra == 'code-tools'", specifier = ">=7.1.0" }, - { name = "docling", marker = "extra == 'all'", specifier = ">=2.18.0" }, - { name = "docling", marker = "extra == 'all-tools'", specifier = ">=2.18.0" }, - { name = "docling", marker = "extra == 'basic-tools'", specifier = ">=2.18.0" }, + { name = "docling", marker = "extra == 'all'", specifier = ">=2.34.0" }, + { name = "docling", marker = "extra == 'all-tools'", specifier = ">=2.34.0" }, + { name = "docling", marker = "extra == 'basic-tools'", specifier = ">=2.34.0" }, { name = "dspy", specifier = "==2.6.23" }, { name = "duckduckgo-search", marker = "extra == 'all'", specifier = ">=7.3.2" }, { name = "duckduckgo-search", marker = "extra == 'all-tools'", specifier = ">=7.3.2" }, @@ -1481,7 +1481,7 @@ requires-dist = [ { name = "thefuzz", specifier = ">=0.22.1" }, { name = "tiktoken", specifier = ">=0.8.0" }, { name = "toml", specifier = ">=0.10.2" }, - { name = "tqdm", specifier = ">=4.67.1" }, + { name = "tqdm", specifier = ">=4.60.1" }, { name = "uvicorn", specifier = ">=0.34.0" }, { name = "wd-di", specifier = ">=0.2.14" }, { name = "websockets", specifier = ">=15.0.1" }, From c60c430536a85eca9418a930c7c5b0dd07431d37 Mon Sep 17 00:00:00 2001 From: Andre Ratzenberger Date: Sun, 1 Jun 2025 00:22:33 +0200 Subject: [PATCH 2/7] =?UTF-8?q?=F0=9F=91=94=20add=20include=5Freasoning=20?= =?UTF-8?q?in=20declarative=20evaluator=20and=20flock=5Ffactory?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- examples | 2 +- pyproject.toml | 2 +- src/flock/core/flock_factory.py | 2 ++ .../declarative/declarative_evaluator.py | 25 ++++++++++++++++++- 4 files changed, 28 insertions(+), 3 deletions(-) diff --git a/examples b/examples index 028ef635e..fc45300dd 160000 --- a/examples +++ b/examples @@ -1 +1 @@ -Subproject commit 028ef635ede8afee1055d95cd0a431ebdd22f9fb +Subproject commit fc45300ddadb4e3b8f2d142b41a540e3508441ad diff --git a/pyproject.toml b/pyproject.toml index 0dde7fa33..01ac3d44d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "flock-core" -version = "0.4.520" +version = "0.4.521" description = "Declarative LLM Orchestration at Scale" readme = "README.md" authors = [ diff --git a/src/flock/core/flock_factory.py b/src/flock/core/flock_factory.py index 43971bf6c..a3aa59547 100644 --- a/src/flock/core/flock_factory.py +++ b/src/flock/core/flock_factory.py @@ -413,6 +413,7 @@ def create_default_agent( write_to_file: bool = False, stream: bool = False, include_thought_process: bool = False, + include_reasoning: bool = False, temporal_activity_config: TemporalActivityConfig | None = None, ) -> FlockAgent: """Creates a default FlockAgent. @@ -433,6 +434,7 @@ def create_default_agent( max_retries=max_retries, stream=stream, include_thought_process=include_thought_process, + include_reasoning=include_reasoning, ) evaluator = DeclarativeEvaluator(name="default", config=eval_config) diff --git a/src/flock/evaluators/declarative/declarative_evaluator.py b/src/flock/evaluators/declarative/declarative_evaluator.py index 1e89dd21c..ef624eb65 100644 --- a/src/flock/evaluators/declarative/declarative_evaluator.py +++ b/src/flock/evaluators/declarative/declarative_evaluator.py @@ -36,6 +36,10 @@ class DeclarativeEvaluatorConfig(FlockEvaluatorConfig): default=False, description="Include the thought process in the output.", ) + include_reasoning: bool = Field( + default=False, + description="Include the reasoning in the output.", + ) kwargs: dict[str, Any] = Field(default_factory=dict) @@ -154,6 +158,9 @@ async def evaluate( self._lm_history = lm_history console.print("\n") + result_dict = self.filter_reasoning( + result_dict, self.config.include_reasoning + ) return self.filter_thought_process( result_dict, self.config.include_thought_process ) @@ -170,6 +177,9 @@ async def evaluate( ) self._cost = cost self._lm_history = lm_history + result_dict = self.filter_reasoning( + result_dict, self.config.include_reasoning + ) return self.filter_thought_process( result_dict, self.config.include_thought_process ) @@ -190,5 +200,18 @@ def filter_thought_process( return { k: v for k, v in result_dict.items() - if not (k.startswith("reasoning") or k.startswith("trajectory")) + if not (k.startswith("trajectory")) + } + + def filter_reasoning( + self, result_dict: dict[str, Any], include_reasoning: bool + ) -> dict[str, Any]: + """Filter out reasoning from the result dictionary.""" + if include_reasoning: + return result_dict + else: + return { + k: v + for k, v in result_dict.items() + if not (k.startswith("reasoning")) } From ec292770b6b5771d67af84f83572cd72996b1128 Mon Sep 17 00:00:00 2001 From: Andre Ratzenberger Date: Sun, 1 Jun 2025 01:33:01 +0200 Subject: [PATCH 3/7] =?UTF-8?q?=F0=9F=90=9B=20fix:=20webapp=20crashed=20we?= =?UTF-8?q?nn=20deserializing=20a=20pydantic=20model=20for=20visualization?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pyproject.toml | 2 +- src/flock/webapp/app/api/execution.py | 11 +++++++++-- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 01ac3d44d..b9d68121a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "flock-core" -version = "0.4.521" +version = "0.4.522" description = "Declarative LLM Orchestration at Scale" readme = "README.md" authors = [ diff --git a/src/flock/webapp/app/api/execution.py b/src/flock/webapp/app/api/execution.py index 7008b7978..97fadec70 100644 --- a/src/flock/webapp/app/api/execution.py +++ b/src/flock/webapp/app/api/execution.py @@ -12,6 +12,7 @@ ) from fastapi.responses import HTMLResponse from fastapi.templating import Jinja2Templates +from pydantic import BaseModel if TYPE_CHECKING: from flock.core.flock import Flock @@ -151,7 +152,10 @@ async def htmx_run_flock( return HTMLResponse(f"

Error processing inputs for {start_agent_name}: {e_parse}

") result_data = await run_current_flock_service(start_agent_name, inputs, request.app.state) - raw_json_for_template = json.dumps(result_data, indent=2) + if isinstance(result_data, BaseModel): + raw_json_for_template = result_data.model_dump_json(indent=2, ensure_ascii=False) + else: + raw_json_for_template = json.dumps(result_data, indent=2, ensure_ascii=False) # Unescape newlines for proper display in HTML
 tag
     result_data_raw_json_str = raw_json_for_template.replace('\\n', '\n')
     root_path = request.scope.get("root_path", "")
@@ -215,7 +219,10 @@ async def htmx_run_shared_flock(
 
         shared_logger.info(f"HTMX Run Shared: Executing agent '{start_agent_name}' in pre-loaded Flock '{temp_flock.name}'. Inputs: {list(inputs.keys())}")
         result_data = await temp_flock.run_async(start_agent=start_agent_name, input=inputs, box_result=False)
-        raw_json_for_template = json.dumps(result_data, indent=2)
+        if isinstance(result_data, BaseModel):
+            raw_json_for_template = result_data.model_dump_json(indent=2, ensure_ascii=False)
+        else:
+            raw_json_for_template = json.dumps(result_data, indent=2, ensure_ascii=False)
         # Unescape newlines for proper display in HTML 
 tag
         result_data_raw_json_str = raw_json_for_template.replace('\\n', '\n')
         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'}")

From 4dd9c9080554dca47079a9bde03d8cbbff9665d9 Mon Sep 17 00:00:00 2001
From: Andre Ratzenberger 
Date: Sun, 1 Jun 2025 01:50:22 +0200
Subject: [PATCH 4/7] =?UTF-8?q?=F0=9F=90=9B=20other=20json=20issue=20fixed?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 pyproject.toml                        |  2 +-
 src/flock/webapp/app/api/execution.py | 22 +++++++++++++---------
 uv.lock                               |  2 +-
 3 files changed, 15 insertions(+), 11 deletions(-)

diff --git a/pyproject.toml b/pyproject.toml
index b9d68121a..196af9353 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,6 +1,6 @@
 [project]
 name = "flock-core"
-version = "0.4.522"
+version = "0.4.523"
 description = "Declarative LLM Orchestration at Scale"
 readme = "README.md"
 authors = [
diff --git a/src/flock/webapp/app/api/execution.py b/src/flock/webapp/app/api/execution.py
index 97fadec70..2389d627c 100644
--- a/src/flock/webapp/app/api/execution.py
+++ b/src/flock/webapp/app/api/execution.py
@@ -10,9 +10,9 @@
     Form,
     Request,
 )
+from fastapi.encoders import jsonable_encoder
 from fastapi.responses import HTMLResponse
 from fastapi.templating import Jinja2Templates
-from pydantic import BaseModel
 
 if TYPE_CHECKING:
     from flock.core.flock import Flock
@@ -152,10 +152,13 @@ async def htmx_run_flock(
             return HTMLResponse(f"

Error processing inputs for {start_agent_name}: {e_parse}

") result_data = await run_current_flock_service(start_agent_name, inputs, request.app.state) - if isinstance(result_data, BaseModel): - raw_json_for_template = result_data.model_dump_json(indent=2, ensure_ascii=False) - else: - raw_json_for_template = json.dumps(result_data, indent=2, ensure_ascii=False) + + + raw_json_for_template = json.dumps( + jsonable_encoder(result_data), # ← converts every nested BaseModel, datetime, etc. + indent=2, + ensure_ascii=False + ) # Unescape newlines for proper display in HTML
 tag
     result_data_raw_json_str = raw_json_for_template.replace('\\n', '\n')
     root_path = request.scope.get("root_path", "")
@@ -219,10 +222,11 @@ async def htmx_run_shared_flock(
 
         shared_logger.info(f"HTMX Run Shared: Executing agent '{start_agent_name}' in pre-loaded Flock '{temp_flock.name}'. Inputs: {list(inputs.keys())}")
         result_data = await temp_flock.run_async(start_agent=start_agent_name, input=inputs, box_result=False)
-        if isinstance(result_data, BaseModel):
-            raw_json_for_template = result_data.model_dump_json(indent=2, ensure_ascii=False)
-        else:
-            raw_json_for_template = json.dumps(result_data, indent=2, ensure_ascii=False)
+        raw_json_for_template = json.dumps(
+            jsonable_encoder(result_data),   # ← converts every nested BaseModel, datetime, etc.
+            indent=2,
+            ensure_ascii=False
+        )
         # Unescape newlines for proper display in HTML 
 tag
         result_data_raw_json_str = raw_json_for_template.replace('\\n', '\n')
         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'}")
diff --git a/uv.lock b/uv.lock
index fc19fe94f..575f99f87 100644
--- a/uv.lock
+++ b/uv.lock
@@ -1282,7 +1282,7 @@ wheels = [
 
 [[package]]
 name = "flock-core"
-version = "0.4.519"
+version = "0.4.523"
 source = { editable = "." }
 dependencies = [
     { name = "aiosqlite" },

From 1731ad02425870b4b8d466e51cd5e88110a71774 Mon Sep 17 00:00:00 2001
From: Andre Ratzenberger 
Date: Sun, 1 Jun 2025 13:19:32 +0200
Subject: [PATCH 5/7] =?UTF-8?q?=F0=9F=90=9B=20fix=20a=20crash=20when=20gen?=
 =?UTF-8?q?erating=20sharable=20links?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 pyproject.toml                                |   2 +-
 .../webapp/app/services/sharing_store.py      | 227 ++++++++++--------
 2 files changed, 124 insertions(+), 105 deletions(-)

diff --git a/pyproject.toml b/pyproject.toml
index 196af9353..05c021c4b 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,6 +1,6 @@
 [project]
 name = "flock-core"
-version = "0.4.523"
+version = "0.4.524"
 description = "Declarative LLM Orchestration at Scale"
 readme = "README.md"
 authors = [
diff --git a/src/flock/webapp/app/services/sharing_store.py b/src/flock/webapp/app/services/sharing_store.py
index 412624979..68a49b79e 100644
--- a/src/flock/webapp/app/services/sharing_store.py
+++ b/src/flock/webapp/app/services/sharing_store.py
@@ -227,136 +227,155 @@ async def save_feedback(self, record: FeedbackRecord) -> FeedbackRecord:
         except sqlite3.Error as e:
             logger.error(f"SQLite error saving feedback {record.feedback_id}: {e}", exc_info=True)
             raise
+# flock/webapp/app/services/sharing_store.py  ← replace only this class
+
+# ---------------------------------------------------------------------------
+# Azure Table + Blob implementation
+# ---------------------------------------------------------------------------
+
+try:
+    from azure.storage.blob.aio import BlobServiceClient
+    AZURE_BLOB_AVAILABLE = True
+except ImportError:  # blob SDK not installed
+    AZURE_BLOB_AVAILABLE = False
+    BlobServiceClient = None
 
 class AzureTableSharedLinkStore(SharedLinkStoreInterface):
-    """Azure Table Storage implementation for storing and retrieving shared link configurations."""
+    """Store configs in Azure Table; store large flock YAML in Blob Storage."""
+
+    _TABLE_NAME        = "flocksharedlinks"
+    _FEEDBACK_TBL_NAME = "flockfeedback"
+    _CONTAINER_NAME    = "flocksharedlinkdefs"          # blobs live here
+    _PARTITION_KEY     = "shared_links"
 
     def __init__(self, connection_string: str):
-        """Initialize Azure Table Storage store with connection string."""
         if not AZURE_AVAILABLE:
-            raise ImportError("Azure Table Storage dependencies not available. Install with: pip install azure-data-tables")
+            raise ImportError("pip install azure-data-tables")
+        if not AZURE_BLOB_AVAILABLE:
+            raise ImportError("pip install azure-storage-blob")
 
         self.connection_string = connection_string
-        self.table_service_client = TableServiceClient.from_connection_string(connection_string)
-        self.shared_links_table_name = "flocksharedlinks"
-        self.feedback_table_name = "flockfeedback"
-        logger.info("AzureTableSharedLinkStore initialized")
+        self.table_svc = TableServiceClient.from_connection_string(connection_string)
+        self.blob_svc  = BlobServiceClient.from_connection_string(connection_string)
 
+    # ------------------------------------------------------------------ init
     async def initialize(self) -> None:
-        """Initializes the Azure Tables (creates them if they don't exist)."""
+        # 1. Azure Tables ----------------------------------------------------
         try:
-            # Create shared_links table
-            try:
-                await self.table_service_client.create_table(self.shared_links_table_name)
-                logger.info(f"Created Azure Table: {self.shared_links_table_name}")
-            except ResourceExistsError:
-                logger.debug(f"Azure Table already exists: {self.shared_links_table_name}")
-
-            # Create feedback table
-            try:
-                await self.table_service_client.create_table(self.feedback_table_name)
-                logger.info(f"Created Azure Table: {self.feedback_table_name}")
-            except ResourceExistsError:
-                logger.debug(f"Azure Table already exists: {self.feedback_table_name}")
-
-            logger.info("Azure Table Storage initialized successfully")
-        except Exception as e:
-            logger.error(f"Error initializing Azure Table Storage: {e}", exc_info=True)
-            raise
+            await self.table_svc.create_table(self._TABLE_NAME)
+            logger.info("Created Azure Table '%s'", self._TABLE_NAME)
+        except ResourceExistsError:
+            logger.debug("Azure Table '%s' already exists", self._TABLE_NAME)
 
-    async def save_config(self, config: SharedLinkConfig) -> SharedLinkConfig:
-        """Saves a shared link configuration to Azure Table Storage."""
         try:
-            table_client = self.table_service_client.get_table_client(self.shared_links_table_name)
-
-            entity = {
-                "PartitionKey": "shared_links",  # Use a fixed partition key for simplicity
-                "RowKey": config.share_id,
-                "share_id": config.share_id,
-                "agent_name": config.agent_name,
-                "flock_definition": config.flock_definition,
-                "created_at": config.created_at.isoformat(),
-                "share_type": config.share_type,
-                "chat_message_key": config.chat_message_key,
-                "chat_history_key": config.chat_history_key,
-                "chat_response_key": config.chat_response_key,
-            }
-
-            await table_client.upsert_entity(entity)
-            logger.info(f"Saved shared link config to Azure Table Storage for ID: {config.share_id} with type: {config.share_type}")
-            return config
-        except Exception as e:
-            logger.error(f"Error saving config to Azure Table Storage for ID {config.share_id}: {e}", exc_info=True)
-            raise
+            await self.table_svc.create_table(self._FEEDBACK_TBL_NAME)
+            logger.info("Created Azure Table '%s'", self._FEEDBACK_TBL_NAME)
+        except ResourceExistsError:
+            logger.debug("Azure Table '%s' already exists", self._FEEDBACK_TBL_NAME)
+
+        # 2. Blob container --------------------------------------------------
+        try:
+            await self.blob_svc.create_container(self._CONTAINER_NAME)
+            logger.info("Created Blob container '%s'", self._CONTAINER_NAME)
+        except ResourceExistsError:
+            logger.debug("Blob container '%s' already exists", self._CONTAINER_NAME)
 
+    # ------------------------------------------------------------- save_config
+    async def save_config(self, config: SharedLinkConfig) -> SharedLinkConfig:
+        """Upload YAML to Blob, then upsert table row containing the blob name."""
+        blob_name   = f"{config.share_id}.yaml"
+        blob_client = self.blob_svc.get_blob_client(self._CONTAINER_NAME, blob_name)
+
+        # 1. Upload flock_definition (overwrite in case of retry)
+        await blob_client.upload_blob(config.flock_definition,
+                                      overwrite=True,
+                                      content_type="text/yaml")
+        logger.debug("Uploaded blob '%s' (%d bytes)",
+                     blob_name, len(config.flock_definition.encode()))
+
+        # 2. Persist lightweight record in the table
+        tbl_client = self.table_svc.get_table_client(self._TABLE_NAME)
+        entity = {
+            "PartitionKey": self._PARTITION_KEY,
+            "RowKey":       config.share_id,
+            "agent_name":   config.agent_name,
+            "created_at":   config.created_at.isoformat(),
+            "share_type":   config.share_type,
+            "chat_message_key":  config.chat_message_key,
+            "chat_history_key":  config.chat_history_key,
+            "chat_response_key": config.chat_response_key,
+            # NEW – just a few bytes, well under 64 KiB
+            "flock_blob_name": blob_name,
+        }
+        await tbl_client.upsert_entity(entity)
+        logger.info("Saved shared link %s → blob '%s'", config.share_id, blob_name)
+        return config
+
+    # -------------------------------------------------------------- get_config
     async def get_config(self, share_id: str) -> SharedLinkConfig | None:
-        """Retrieves a shared link configuration from Azure Table Storage by its ID."""
+        tbl_client = self.table_svc.get_table_client(self._TABLE_NAME)
         try:
-            table_client = self.table_service_client.get_table_client(self.shared_links_table_name)
-
-            entity = await table_client.get_entity(partition_key="shared_links", row_key=share_id)
-
-            logger.debug(f"Retrieved shared link config from Azure Table Storage for ID: {share_id}")
-            return SharedLinkConfig(
-                share_id=entity["share_id"],
-                agent_name=entity["agent_name"],
-                created_at=entity["created_at"],  # Pydantic will parse from ISO format
-                flock_definition=entity["flock_definition"],
-                share_type=entity.get("share_type", "agent_run"),
-                chat_message_key=entity.get("chat_message_key"),
-                chat_history_key=entity.get("chat_history_key"),
-                chat_response_key=entity.get("chat_response_key"),
-            )
+            entity = await tbl_client.get_entity(self._PARTITION_KEY, share_id)
         except ResourceNotFoundError:
-            logger.debug(f"No shared link config found in Azure Table Storage for ID: {share_id}")
+            logger.debug("No config entity for id '%s'", share_id)
             return None
+
+        blob_name   = entity["flock_blob_name"]
+        blob_client = self.blob_svc.get_blob_client(self._CONTAINER_NAME, blob_name)
+        try:
+            blob_bytes = await (await blob_client.download_blob()).readall()
+            flock_yaml = blob_bytes.decode()
         except Exception as e:
-            logger.error(f"Error retrieving config from Azure Table Storage for ID {share_id}: {e}", exc_info=True)
-            return None
+            logger.error("Cannot download blob '%s' for share_id=%s: %s",
+                         blob_name, share_id, e, exc_info=True)
+            raise
 
+        return SharedLinkConfig(
+            share_id          = share_id,
+            agent_name        = entity["agent_name"],
+            created_at        = entity["created_at"],
+            flock_definition  = flock_yaml,
+            share_type        = entity.get("share_type", "agent_run"),
+            chat_message_key  = entity.get("chat_message_key"),
+            chat_history_key  = entity.get("chat_history_key"),
+            chat_response_key = entity.get("chat_response_key"),
+        )
+
+    # ----------------------------------------------------------- delete_config
     async def delete_config(self, share_id: str) -> bool:
-        """Deletes a shared link configuration from Azure Table Storage by its ID."""
+        tbl_client = self.table_svc.get_table_client(self._TABLE_NAME)
         try:
-            table_client = self.table_service_client.get_table_client(self.shared_links_table_name)
-
-            await table_client.delete_entity(partition_key="shared_links", row_key=share_id)
-            logger.info(f"Deleted shared link config from Azure Table Storage for ID: {share_id}")
-            return True
+            entity = await tbl_client.get_entity(self._PARTITION_KEY, share_id)
         except ResourceNotFoundError:
-            logger.info(f"Attempted to delete non-existent shared link config from Azure Table Storage for ID: {share_id}")
-            return False
-        except Exception as e:
-            logger.error(f"Error deleting config from Azure Table Storage for ID {share_id}: {e}", exc_info=True)
+            logger.info("Delete: entity %s not found", share_id)
             return False
 
-    # ----------------------- Feedback methods -----------------------
+        # 1. Remove blob (ignore missing blob)
+        blob_name   = entity["flock_blob_name"]
+        blob_client = self.blob_svc.get_blob_client(self._CONTAINER_NAME, blob_name)
+        try:
+            await blob_client.delete_blob(delete_snapshots="include")
+            logger.debug("Deleted blob '%s'", blob_name)
+        except ResourceNotFoundError:
+            logger.warning("Blob '%s' already gone", blob_name)
+
+        # 2. Remove table row
+        await tbl_client.delete_entity(self._PARTITION_KEY, share_id)
+        logger.info("Deleted shared link %s and its blob", share_id)
+        return True
 
+    # -------------------------------------------------------- save_feedback --
     async def save_feedback(self, record: FeedbackRecord) -> FeedbackRecord:
-        """Persist a feedback record to Azure Table Storage."""
-        try:
-            table_client = self.table_service_client.get_table_client(self.feedback_table_name)
-
-            entity = {
-                "PartitionKey": "feedback",  # Use a fixed partition key for simplicity
-                "RowKey": record.feedback_id,
-                "feedback_id": record.feedback_id,
-                "share_id": record.share_id,
-                "context_type": record.context_type,
-                "reason": record.reason,
-                "expected_response": record.expected_response,
-                "actual_response": record.actual_response,
-                "flock_name": record.flock_name,
-                "agent_name": record.agent_name,
-                "flock_definition": record.flock_definition,
-                "created_at": record.created_at.isoformat(),
-            }
-
-            await table_client.upsert_entity(entity)
-            logger.info(f"Saved feedback to Azure Table Storage: {record.feedback_id} (share={record.share_id})")
-            return record
-        except Exception as e:
-            logger.error(f"Error saving feedback to Azure Table Storage {record.feedback_id}: {e}", exc_info=True)
-            raise
+        tbl_client = self.table_svc.get_table_client(self._FEEDBACK_TBL_NAME)
+        entity = {
+            "PartitionKey": "feedback",
+            "RowKey":       record.feedback_id,
+            **record.model_dump(exclude={"feedback_id"})  # all other fields
+        }
+        await tbl_client.upsert_entity(entity)
+        logger.info("Saved feedback %s", record.feedback_id)
+        return record
+
 
 
 # ----------------------- Factory Function -----------------------

From 3376cb6a415fa0e0d6c016a23bedec4a85bb93a5 Mon Sep 17 00:00:00 2001
From: Andre Ratzenberger 
Date: Fri, 4 Jul 2025 00:45:46 +0200
Subject: [PATCH 6/7] fix: if feedback for agent run provided the definition of
 flock will get saved into a blob like with shared links instead of getting
 saved in the table storage -> no >64kb exception anymore

---
 examples                                      |  2 +-
 pyproject.toml                                |  2 +-
 .../webapp/app/services/sharing_store.py      | 36 ++++++++++++++++---
 uv.lock                                       |  2 +-
 4 files changed, 35 insertions(+), 7 deletions(-)

diff --git a/examples b/examples
index fc45300dd..1656232ed 160000
--- a/examples
+++ b/examples
@@ -1 +1 @@
-Subproject commit fc45300ddadb4e3b8f2d142b41a540e3508441ad
+Subproject commit 1656232ed7face05ed8916cb14f1bed78ffcc70b
diff --git a/pyproject.toml b/pyproject.toml
index 05c021c4b..3f468de95 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,6 +1,6 @@
 [project]
 name = "flock-core"
-version = "0.4.524"
+version = "0.4.525"
 description = "Declarative LLM Orchestration at Scale"
 readme = "README.md"
 authors = [
diff --git a/src/flock/webapp/app/services/sharing_store.py b/src/flock/webapp/app/services/sharing_store.py
index 68a49b79e..f9d311c3d 100644
--- a/src/flock/webapp/app/services/sharing_store.py
+++ b/src/flock/webapp/app/services/sharing_store.py
@@ -4,6 +4,7 @@
 import sqlite3
 from abc import ABC, abstractmethod
 from pathlib import Path
+from typing import Any
 
 import aiosqlite
 
@@ -227,7 +228,6 @@ async def save_feedback(self, record: FeedbackRecord) -> FeedbackRecord:
         except sqlite3.Error as e:
             logger.error(f"SQLite error saving feedback {record.feedback_id}: {e}", exc_info=True)
             raise
-# flock/webapp/app/services/sharing_store.py  ← replace only this class
 
 # ---------------------------------------------------------------------------
 # Azure Table + Blob implementation
@@ -366,14 +366,42 @@ async def delete_config(self, share_id: str) -> bool:
 
     # -------------------------------------------------------- save_feedback --
     async def save_feedback(self, record: FeedbackRecord) -> FeedbackRecord:
+        """Persist a feedback record. If a flock_definition is present, upload it as a blob and
+        store only a reference in the table row to avoid oversized entities (64 KiB limit).
+        """
         tbl_client = self.table_svc.get_table_client(self._FEEDBACK_TBL_NAME)
-        entity = {
+
+        # Core entity fields (avoid dumping the full Pydantic model – too many columns / large value)
+        entity: dict[str, Any] = {
             "PartitionKey": "feedback",
             "RowKey":       record.feedback_id,
-            **record.model_dump(exclude={"feedback_id"})  # all other fields
+            "share_id":     record.share_id,
+            "context_type": record.context_type,
+            "reason":       record.reason,
+            "expected_response": record.expected_response,
+            "actual_response":   record.actual_response,
+            "created_at":   record.created_at.isoformat(),
         }
+        if record.flock_name is not None:
+            entity["flock_name"] = record.flock_name
+        if record.agent_name is not None:
+            entity["agent_name"] = record.agent_name
+
+        # ------------------------------------------------------------------ YAML → Blob
+        if record.flock_definition:
+            blob_name   = f"{record.feedback_id}.yaml"
+            blob_client = self.blob_svc.get_blob_client(self._CONTAINER_NAME, blob_name)
+            # Overwrite=true so repeated feedback_id uploads (shouldn't happen) won't error
+            await blob_client.upload_blob(record.flock_definition,
+                                          overwrite=True,
+                                          content_type="text/yaml")
+            entity["flock_blob_name"] = blob_name  # lightweight reference only
+
+        # ------------------------------------------------------------------ Table upsert
         await tbl_client.upsert_entity(entity)
-        logger.info("Saved feedback %s", record.feedback_id)
+        logger.info("Saved feedback %s%s",
+                    record.feedback_id,
+                    f" → blob '{entity['flock_blob_name']}'" if "flock_blob_name" in entity else "")
         return record
 
 
diff --git a/uv.lock b/uv.lock
index 575f99f87..7dd3736f5 100644
--- a/uv.lock
+++ b/uv.lock
@@ -1282,7 +1282,7 @@ wheels = [
 
 [[package]]
 name = "flock-core"
-version = "0.4.523"
+version = "0.4.524"
 source = { editable = "." }
 dependencies = [
     { name = "aiosqlite" },

From c4e3e37aff6761cb7e36d8c3ab54ef345d852160 Mon Sep 17 00:00:00 2001
From: Cursor Agent 
Date: Wed, 16 Jul 2025 14:38:46 +0000
Subject: [PATCH 7/7] Add feedback_on_failure option to ConditionalRouterConfig

Co-authored-by: andre.ratzenberger 
---
 src/flock/routers/conditional/conditional_router.py | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/src/flock/routers/conditional/conditional_router.py b/src/flock/routers/conditional/conditional_router.py
index a07a85545..bf70c9c90 100644
--- a/src/flock/routers/conditional/conditional_router.py
+++ b/src/flock/routers/conditional/conditional_router.py
@@ -115,6 +115,10 @@ class ConditionalRouterConfig(FlockRouterConfig):
         default="flock.assertion_feedback",  # Useful if paired with AssertionCheckerModule
         description="Optional context key containing feedback message to potentially include when retrying.",
     )
+    feedback_on_failure: str | None = Field(
+        default=None,
+        description="Default feedback message to use when condition evaluation fails.",
+    )
     retry_count_context_key_prefix: str = Field(
         default="flock.conditional_retry_count_",
         description="Internal prefix for context key storing retry attempts per agent.",