from sqlalchemy import Column, Integer, String, Boolean, Float, ForeignKey, Text, DateTime, JSON from sqlalchemy.orm import relationship import uuid from datetime import datetime from .database import Base # Compatibility layer for SQLite and PostgreSQL without pgvector try: from sqlalchemy.dialects.postgresql import UUID, ARRAY import os if "sqlite" in os.getenv("DATABASE_URL", ""): raise ImportError("Using SQLite") # Try to import pgvector, if fails use JSON try: from pgvector.sqlalchemy import Vector except ImportError: # No pgvector, use JSON instead Vector = JSON except ImportError: from sqlalchemy.types import TypeDecorator, CHAR import json class UUID(TypeDecorator): impl = CHAR(36) def __init__(self, as_uuid=True): super().__init__() def process_bind_param(self, value, dialect): if value is None: return None return str(value) def process_result_value(self, value, dialect): if value is None: return None return uuid.UUID(value) class Vector(TypeDecorator): impl = Text def __init__(self, dim=None): super().__init__() def process_bind_param(self, value, dialect): if value is None: return None return json.dumps(value) def process_result_value(self, value, dialect): if value is None: return None return json.loads(value) ARRAY = JSON class ContextNode(Base): __tablename__ = "context_nodes" id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4) content = Column(Text, nullable=False) embedding = Column(Vector(768)) version = Column(Integer, default=1) status = Column(String(20), default='active') created_by = Column(UUID(as_uuid=True), nullable=True) # Placeholder for user link # New fields for AI Insights ai_insights = Column(JSON, nullable=True) # Stores 'moat', 'tech_specs', etc. class Experiment(Base): __tablename__ = "experiments" id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4) title = Column(String(255)) hypothesis = Column(Text) status = Column(String(50), default='draft') owner_id = Column(UUID(as_uuid=True), nullable=True) # Placeholder linked_context_nodes = Column(JSON, nullable=True) # Changed from ARRAY to JSON for compatibility target_metric = Column(String(50)) current_score = Column(Float, nullable=True) class Signal(Base): __tablename__ = "signals" id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4) source = Column(String(50)) content = Column(Text) impact_score = Column(Float) priority = Column(String(20), default='low') # critical, high, medium, low processed = Column(Boolean, default=False) # Store strategic analysis result strategic_analysis = Column(Text, nullable=True) # JSON string tasks_generated = Column(Text, nullable=True) # JSON string created_at = Column(DateTime, default=datetime.utcnow) class SignalFeedback(Base): """User feedback on AI strategic analysis of signals""" __tablename__ = "signal_feedbacks" id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4) signal_id = Column(UUID(as_uuid=True), ForeignKey('signals.id'), nullable=False) # User's correction of the AI's judgment was_strategic = Column(Boolean, nullable=False) # User's actual judgment reason = Column(Text, nullable=True) # Why they agree/disagree # Reference to original AI prediction for analysis ai_predicted_strategic = Column(Boolean, nullable=False) ai_confidence = Column(Float, nullable=True) # Optional: user info for tracking user_id = Column(String(100), nullable=True) # Simple identifier (email, name, etc.) created_at = Column(DateTime, default=datetime.utcnow) # Relationship to signal signal = relationship("Signal", backref="feedbacks")