2025-08-22 23:42:34 +02:00
|
|
|
|
2025-08-27 04:27:18 +02:00
|
|
|
from sqlalchemy import text
|
2026-05-06 06:13:31 +02:00
|
|
|
from sqlalchemy import create_engine
|
|
|
|
|
from sqlalchemy.orm import DeclarativeBase, sessionmaker
|
2026-06-15 03:51:19 +02:00
|
|
|
import uuid
|
2026-05-06 06:13:31 +02:00
|
|
|
|
|
|
|
|
from .paths import database_path, sqlite_database_url
|
2025-08-27 04:27:18 +02:00
|
|
|
|
|
|
|
|
"""
|
2026-04-16 21:27:43 +02:00
|
|
|
Database utilities and configuration. This module defines the SQLAlchemy
|
|
|
|
|
engine, session factory and base class for models. It also contains a
|
|
|
|
|
lightweight migration helper used to evolve the schema over time. The
|
|
|
|
|
`ensure_sources_column` helper adds the JSON-backed columns used by chat
|
|
|
|
|
messages when they do not already exist.
|
2025-08-27 04:27:18 +02:00
|
|
|
|
|
|
|
|
The migration uses SQLite's `ALTER TABLE` syntax and therefore should
|
2026-04-16 21:27:43 +02:00
|
|
|
only run once on startup. It is safe to call repeatedly: when a column
|
|
|
|
|
already exists, the function will simply no-op.
|
2025-08-27 04:27:18 +02:00
|
|
|
"""
|
2025-08-22 23:42:34 +02:00
|
|
|
|
2026-05-06 06:13:31 +02:00
|
|
|
DATABASE_PATH = database_path()
|
|
|
|
|
DATABASE_URL = sqlite_database_url(DATABASE_PATH)
|
2025-08-22 23:42:34 +02:00
|
|
|
|
|
|
|
|
engine = create_engine(
|
|
|
|
|
DATABASE_URL, connect_args={"check_same_thread": False}
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
|
|
|
|
|
|
|
|
|
|
class Base(DeclarativeBase):
|
|
|
|
|
pass
|
2025-08-27 04:27:18 +02:00
|
|
|
|
|
|
|
|
|
|
|
|
|
def ensure_sources_column(engine):
|
|
|
|
|
try:
|
2026-06-15 03:51:19 +02:00
|
|
|
with engine.begin() as conn:
|
2025-08-27 04:27:18 +02:00
|
|
|
cols = [row[1] for row in conn.execute(text("PRAGMA table_info(chat_messages)"))]
|
2026-06-15 03:51:19 +02:00
|
|
|
if "message_id" not in cols:
|
|
|
|
|
conn.execute(text("ALTER TABLE chat_messages ADD COLUMN message_id TEXT"))
|
2025-08-27 04:27:18 +02:00
|
|
|
if "sources_json" not in cols:
|
|
|
|
|
conn.execute(text("ALTER TABLE chat_messages ADD COLUMN sources_json TEXT DEFAULT '[]'"))
|
2026-04-16 21:27:43 +02:00
|
|
|
if "attachments_json" not in cols:
|
|
|
|
|
conn.execute(text("ALTER TABLE chat_messages ADD COLUMN attachments_json TEXT DEFAULT '[]'"))
|
2026-06-15 14:58:08 +02:00
|
|
|
for column_name, definition in (
|
|
|
|
|
("workflow_id", "TEXT"),
|
|
|
|
|
("workflow_revision_id", "TEXT"),
|
|
|
|
|
("workflow_run_id", "TEXT"),
|
|
|
|
|
("agent_summary_json", "TEXT DEFAULT '{}'"),
|
|
|
|
|
("usage_json", "TEXT DEFAULT '{}'"),
|
|
|
|
|
):
|
|
|
|
|
if column_name not in cols:
|
|
|
|
|
conn.execute(text(f"ALTER TABLE chat_messages ADD COLUMN {column_name} {definition}"))
|
2026-06-15 03:51:19 +02:00
|
|
|
missing_ids = conn.execute(
|
|
|
|
|
text("SELECT id FROM chat_messages WHERE message_id IS NULL OR message_id = ''")
|
|
|
|
|
).fetchall()
|
|
|
|
|
for row in missing_ids:
|
|
|
|
|
conn.execute(
|
|
|
|
|
text("UPDATE chat_messages SET message_id = :message_id WHERE id = :id"),
|
|
|
|
|
{"message_id": str(uuid.uuid4()), "id": row[0]},
|
|
|
|
|
)
|
|
|
|
|
conn.execute(text(
|
|
|
|
|
"CREATE UNIQUE INDEX IF NOT EXISTS ix_chat_messages_message_id "
|
|
|
|
|
"ON chat_messages (message_id)"
|
|
|
|
|
))
|
2026-06-15 14:58:08 +02:00
|
|
|
conn.execute(text(
|
|
|
|
|
"CREATE INDEX IF NOT EXISTS ix_chat_messages_workflow_run_id "
|
|
|
|
|
"ON chat_messages (workflow_run_id)"
|
|
|
|
|
))
|
2025-08-27 04:27:18 +02:00
|
|
|
except Exception as e:
|
|
|
|
|
print("[db] ensure_sources_column error:", e)
|