From b4e92015617f8dda5378bd3e1f5f550b0146cdda Mon Sep 17 00:00:00 2001
From: Przemyslaw Kukulski <przemyslaw@baserow.io>
Date: Thu, 13 Mar 2025 08:33:09 +0000
Subject: [PATCH] Data sync fails when syncing an AI field with a prompt that
 refers to a field in the original table

---
 .gitignore                                    |  5 +-
 .../baserow/contrib/database/rows/handler.py  |  2 +-
 ..._when_syncing_baserow_table_with_aifi.json |  8 +++
 .../data_sync/test_data_sync_ai_field.py      | 61 +++++++++++++++++++
 4 files changed, 74 insertions(+), 2 deletions(-)
 create mode 100644 changelog/entries/unreleased/bug/3403_fix_for_data_sync_fails_when_syncing_baserow_table_with_aifi.json
 create mode 100644 enterprise/backend/tests/baserow_enterprise_tests/data_sync/test_data_sync_ai_field.py

diff --git a/.gitignore b/.gitignore
index 3ef1fa69f..a648828b3 100644
--- a/.gitignore
+++ b/.gitignore
@@ -123,6 +123,9 @@ out/
 .cursor-config/
 !config/cursor/.cursor-config/
 
+# Pyright config file
+pyrightconfig.json
+
 # VIM's swap files
 *.swp
 
@@ -146,4 +149,4 @@ premium/web-frontend/package.json
 
 # Storybook
 web-frontend/.nuxt-storybook/
-web-frontend/storybook-static
\ No newline at end of file
+web-frontend/storybook-static
diff --git a/backend/src/baserow/contrib/database/rows/handler.py b/backend/src/baserow/contrib/database/rows/handler.py
index 1c91cb225..87786902b 100644
--- a/backend/src/baserow/contrib/database/rows/handler.py
+++ b/backend/src/baserow/contrib/database/rows/handler.py
@@ -1875,7 +1875,7 @@ class RowHandler(metaclass=baserow_trace_methods(tracer)):
             if (
                 not isinstance(model_field, ManyToManyField)
                 and field_id in updated_field_ids
-                and field_type.valid_for_bulk_update(model_field)
+                and field_type.valid_for_bulk_update(field_obj["field"])
             ):
                 bulk_update_fields.append(field_name)
 
diff --git a/changelog/entries/unreleased/bug/3403_fix_for_data_sync_fails_when_syncing_baserow_table_with_aifi.json b/changelog/entries/unreleased/bug/3403_fix_for_data_sync_fails_when_syncing_baserow_table_with_aifi.json
new file mode 100644
index 000000000..f4b27223c
--- /dev/null
+++ b/changelog/entries/unreleased/bug/3403_fix_for_data_sync_fails_when_syncing_baserow_table_with_aifi.json
@@ -0,0 +1,8 @@
+{
+    "type": "bug",
+    "message": "Fix for data sync failing when syncing baserow table with AIField",
+    "domain": "database",
+    "issue_number": 3403,
+    "bullet_points": [],
+    "created_at": "2025-03-12"
+}
\ No newline at end of file
diff --git a/enterprise/backend/tests/baserow_enterprise_tests/data_sync/test_data_sync_ai_field.py b/enterprise/backend/tests/baserow_enterprise_tests/data_sync/test_data_sync_ai_field.py
new file mode 100644
index 000000000..0df8a8bb7
--- /dev/null
+++ b/enterprise/backend/tests/baserow_enterprise_tests/data_sync/test_data_sync_ai_field.py
@@ -0,0 +1,61 @@
+from django.test.utils import override_settings
+
+import pytest
+
+from baserow.contrib.database.data_sync.handler import DataSyncHandler
+
+
+@pytest.mark.django_db
+@override_settings(DEBUG=True)
+def test_sync_table_with_ai_field(enterprise_data_fixture, premium_data_fixture):
+    enterprise_data_fixture.enable_enterprise()
+
+    user = enterprise_data_fixture.create_user()
+
+    source_table = enterprise_data_fixture.create_database_table(
+        user=user, name="Source"
+    )
+    field_1 = enterprise_data_fixture.create_text_field(
+        table=source_table, name="Text", primary=True
+    )
+    ai_field = premium_data_fixture.create_ai_field(
+        table=source_table,
+        name="ai_field",
+        ai_generative_ai_type="test_generative_ai",
+        ai_generative_ai_model="test_1",
+        ai_prompt="field('text_field')",
+    )
+    source_model = source_table.get_model()
+    source_row_1 = source_model.objects.create(
+        **{
+            f"field_{field_1.id}": "Text field",
+            f"field_{ai_field.id}": "AI Field value #1",
+        }
+    )
+
+    database = enterprise_data_fixture.create_database_application(user=user)
+    handler = DataSyncHandler()
+
+    data_sync = handler.create_data_sync_table(
+        user=user,
+        database=database,
+        table_name="Test",
+        type_name="local_baserow_table",
+        synced_properties=["id", f"field_{field_1.id}", f"field_{ai_field.id}"],
+        source_table_id=source_table.id,
+    )
+    handler.sync_data_sync_table(user=user, data_sync=data_sync)
+
+    source_row_1.refresh_from_db()
+
+    # We don't need AI calculations for this test. We just need value changed to
+    # trigger the sync.
+    setattr(source_row_1, f"field_{ai_field.id}", "AI Field value #2")
+    source_row_1.save()
+
+    handler.sync_data_sync_table(user=user, data_sync=data_sync)
+
+    source_row_1.refresh_from_db()
+
+    assert data_sync.last_sync is not None
+    assert data_sync.last_error is None