Initial commit: VoIdeaAI - voice-first AI idea assistant
This commit is contained in:
@@ -0,0 +1,15 @@
|
||||
# models Module - VoIdea
|
||||
|
||||
## Overview
|
||||
|
||||
[Auto-generated documentation]
|
||||
|
||||
## Files
|
||||
|
||||
| File | Purpose |
|
||||
|------|---------|
|
||||
| `agent.py` | Module file |
|
||||
| `backlog.py` | Module file |
|
||||
| `idea.py` | Module file |
|
||||
| `log.py` | Module file |
|
||||
| `user.py` | Module file |
|
||||
@@ -0,0 +1,12 @@
|
||||
from app.models.user import User
|
||||
from app.models.idea import Idea
|
||||
from app.models.session import Session
|
||||
from app.models.voice_command import VoiceCommand
|
||||
from app.models.agent import AgentConfig
|
||||
from app.models.backlog import BacklogTask
|
||||
from app.models.feedback import Feedback
|
||||
from app.models.tariff import TariffPlan, UserSubscription
|
||||
from app.models.log import LogEntry
|
||||
from app.models.conductor import ConductorInteraction
|
||||
from app.models.pipeline import PipelineStats
|
||||
from app.models.bot_command import BotCommand
|
||||
@@ -0,0 +1,35 @@
|
||||
"""Agent configuration model for VoIdea."""
|
||||
|
||||
from datetime import datetime
|
||||
from typing import Optional
|
||||
|
||||
from sqlalchemy import Boolean, DateTime, String, Text
|
||||
from sqlalchemy.orm import Mapped, mapped_column
|
||||
|
||||
from app.core.base import SQLBase, TimestampMixin, UUIDMixin
|
||||
|
||||
|
||||
class AgentConfig(SQLBase, UUIDMixin, TimestampMixin):
|
||||
__tablename__ = "agent_configs"
|
||||
|
||||
agent_name: Mapped[str] = mapped_column(
|
||||
String(100), unique=True, nullable=False, index=True
|
||||
)
|
||||
description: Mapped[Optional[str]] = mapped_column(
|
||||
Text, nullable=True, default=""
|
||||
)
|
||||
is_enabled: Mapped[bool] = mapped_column(
|
||||
Boolean, default=True, nullable=False
|
||||
)
|
||||
version: Mapped[str] = mapped_column(
|
||||
String(20), default="1.0.0", nullable=False
|
||||
)
|
||||
checksum: Mapped[Optional[str]] = mapped_column(
|
||||
String(64), nullable=True
|
||||
)
|
||||
config: Mapped[Optional[str]] = mapped_column(
|
||||
Text, nullable=True
|
||||
)
|
||||
last_run_at: Mapped[Optional[datetime]] = mapped_column(
|
||||
DateTime(timezone=True), nullable=True
|
||||
)
|
||||
@@ -0,0 +1,31 @@
|
||||
"""Backlog task model for VoIdea."""
|
||||
|
||||
from typing import Optional
|
||||
|
||||
from sqlalchemy import String, Text
|
||||
from sqlalchemy.orm import Mapped, mapped_column
|
||||
|
||||
from app.core.base import SQLBase, TimestampMixin, UUIDMixin
|
||||
|
||||
|
||||
class BacklogTask(SQLBase, UUIDMixin, TimestampMixin):
|
||||
__tablename__ = "backlog_tasks"
|
||||
|
||||
title: Mapped[str] = mapped_column(
|
||||
String(255), nullable=False
|
||||
)
|
||||
description: Mapped[Optional[str]] = mapped_column(
|
||||
Text, nullable=True
|
||||
)
|
||||
priority: Mapped[str] = mapped_column(
|
||||
String(20), default="medium", nullable=False, index=True
|
||||
)
|
||||
status: Mapped[str] = mapped_column(
|
||||
String(20), default="pending", nullable=False, index=True
|
||||
)
|
||||
source_agent: Mapped[Optional[str]] = mapped_column(
|
||||
String(100), nullable=True
|
||||
)
|
||||
category: Mapped[str] = mapped_column(
|
||||
String(50), default="general", nullable=False, index=True
|
||||
)
|
||||
@@ -0,0 +1,22 @@
|
||||
"""BotCommand model — Telegram bot commands with enable/disable toggle."""
|
||||
|
||||
from sqlalchemy import Boolean, String
|
||||
from sqlalchemy.orm import Mapped, mapped_column
|
||||
from app.core.base import SQLBase, TimestampMixin, UUIDMixin
|
||||
|
||||
|
||||
class BotCommand(SQLBase, UUIDMixin, TimestampMixin):
|
||||
__tablename__ = "bot_commands"
|
||||
|
||||
name: Mapped[str] = mapped_column(
|
||||
String(50), unique=True, nullable=False, index=True
|
||||
)
|
||||
description: Mapped[str] = mapped_column(
|
||||
String(255), nullable=False
|
||||
)
|
||||
enabled: Mapped[bool] = mapped_column(
|
||||
Boolean, default=True, nullable=False
|
||||
)
|
||||
requires_auth: Mapped[bool] = mapped_column(
|
||||
Boolean, default=False, nullable=False
|
||||
)
|
||||
@@ -0,0 +1,45 @@
|
||||
"""Conductor interaction model for VoIdeaAI.
|
||||
|
||||
Logs all Дирижёр interactions for self-learning and analytics.
|
||||
"""
|
||||
|
||||
from typing import Optional
|
||||
|
||||
from sqlalchemy import Float, ForeignKey, Integer, String, Text
|
||||
from sqlalchemy.dialects.postgresql import UUID
|
||||
from sqlalchemy.orm import Mapped, mapped_column
|
||||
|
||||
from app.core.base import SQLBase, TimestampMixin, UUIDMixin
|
||||
|
||||
|
||||
class ConductorInteraction(SQLBase, UUIDMixin, TimestampMixin):
|
||||
__tablename__ = "conductor_interactions"
|
||||
|
||||
user_id: Mapped[Optional[UUID]] = mapped_column(
|
||||
UUID(as_uuid=True),
|
||||
ForeignKey("users.id", ondelete="SET NULL"),
|
||||
nullable=True,
|
||||
index=True,
|
||||
)
|
||||
session_id: Mapped[Optional[UUID]] = mapped_column(
|
||||
UUID(as_uuid=True),
|
||||
ForeignKey("sessions.id", ondelete="SET NULL"),
|
||||
nullable=True,
|
||||
index=True,
|
||||
)
|
||||
input_text: Mapped[str] = mapped_column(Text, nullable=False)
|
||||
detected_intent: Mapped[str] = mapped_column(String(100), nullable=False)
|
||||
selected_agent: Mapped[str] = mapped_column(String(100), nullable=False)
|
||||
response_text: Mapped[str] = mapped_column(Text, nullable=False)
|
||||
user_rating: Mapped[Optional[int]] = mapped_column(
|
||||
Integer, nullable=True
|
||||
)
|
||||
confidence_score: Mapped[int] = mapped_column(
|
||||
Integer, default=80, nullable=False
|
||||
)
|
||||
verification_status: Mapped[str] = mapped_column(
|
||||
String(20), default="verified", nullable=False
|
||||
)
|
||||
was_auto_routed: Mapped[bool] = mapped_column(default=True)
|
||||
processing_time_ms: Mapped[float] = mapped_column(Float, default=0.0)
|
||||
context: Mapped[Optional[str]] = mapped_column(Text, nullable=True)
|
||||
@@ -0,0 +1,29 @@
|
||||
"""Feedback model for VoIdea."""
|
||||
|
||||
from typing import Optional
|
||||
|
||||
from sqlalchemy import ForeignKey, String, Text
|
||||
from sqlalchemy.dialects.postgresql import UUID
|
||||
from sqlalchemy.orm import Mapped, mapped_column
|
||||
|
||||
from app.core.base import SQLBase, TimestampMixin, UUIDMixin
|
||||
|
||||
|
||||
class Feedback(SQLBase, UUIDMixin, TimestampMixin):
|
||||
__tablename__ = "feedback"
|
||||
|
||||
user_id: Mapped[UUID] = mapped_column(
|
||||
UUID(as_uuid=True),
|
||||
ForeignKey("users.id", ondelete="SET NULL"),
|
||||
nullable=True,
|
||||
index=True,
|
||||
)
|
||||
text: Mapped[str] = mapped_column(
|
||||
Text, nullable=False
|
||||
)
|
||||
page_url: Mapped[Optional[str]] = mapped_column(
|
||||
String(512), nullable=True
|
||||
)
|
||||
status: Mapped[str] = mapped_column(
|
||||
String(20), default="new", nullable=False, index=True
|
||||
)
|
||||
@@ -0,0 +1,40 @@
|
||||
"""Idea model for VoIdea."""
|
||||
|
||||
from typing import Optional
|
||||
|
||||
from sqlalchemy import Boolean, ForeignKey, String, Text
|
||||
from sqlalchemy.dialects.postgresql import ARRAY, UUID
|
||||
from sqlalchemy.orm import Mapped, mapped_column, relationship
|
||||
|
||||
from app.core.base import SQLBase, TimestampMixin, UUIDMixin
|
||||
|
||||
|
||||
class Idea(SQLBase, UUIDMixin, TimestampMixin):
|
||||
__tablename__ = "ideas"
|
||||
|
||||
user_id: Mapped[UUID] = mapped_column(
|
||||
UUID(as_uuid=True),
|
||||
ForeignKey("users.id", ondelete="CASCADE"),
|
||||
nullable=False,
|
||||
index=True,
|
||||
)
|
||||
title: Mapped[str] = mapped_column(
|
||||
String(255), nullable=False, index=True
|
||||
)
|
||||
content: Mapped[str] = mapped_column(
|
||||
Text, nullable=False
|
||||
)
|
||||
status: Mapped[str] = mapped_column(
|
||||
String(20), default="draft", nullable=False, index=True
|
||||
)
|
||||
tags: Mapped[Optional[list[str]]] = mapped_column(
|
||||
ARRAY(String(50)), nullable=True
|
||||
)
|
||||
is_public: Mapped[bool] = mapped_column(
|
||||
Boolean, default=False, nullable=False
|
||||
)
|
||||
public_slug: Mapped[Optional[str]] = mapped_column(
|
||||
String(64), unique=True, nullable=True, index=True
|
||||
)
|
||||
|
||||
user = relationship("User", back_populates="ideas", lazy="selectin")
|
||||
@@ -0,0 +1,32 @@
|
||||
"""Log entry model for VoIdea."""
|
||||
|
||||
from typing import Optional
|
||||
|
||||
from sqlalchemy import ForeignKey, String, Text
|
||||
from sqlalchemy.dialects.postgresql import UUID
|
||||
from sqlalchemy.orm import Mapped, mapped_column
|
||||
|
||||
from app.core.base import SQLBase, TimestampMixin, UUIDMixin
|
||||
|
||||
|
||||
class LogEntry(SQLBase, UUIDMixin, TimestampMixin):
|
||||
__tablename__ = "log_entries"
|
||||
|
||||
level: Mapped[str] = mapped_column(
|
||||
String(20), nullable=False, index=True
|
||||
)
|
||||
source: Mapped[str] = mapped_column(
|
||||
String(100), nullable=False, index=True
|
||||
)
|
||||
message: Mapped[str] = mapped_column(
|
||||
Text, nullable=False
|
||||
)
|
||||
details: Mapped[Optional[str]] = mapped_column(
|
||||
Text, nullable=True
|
||||
)
|
||||
user_id: Mapped[Optional[UUID]] = mapped_column(
|
||||
UUID(as_uuid=True),
|
||||
ForeignKey("users.id", ondelete="SET NULL"),
|
||||
nullable=True,
|
||||
index=True,
|
||||
)
|
||||
@@ -0,0 +1,38 @@
|
||||
"""Pipeline models for VoIdea - voice processing pipeline stages."""
|
||||
|
||||
from datetime import datetime
|
||||
from typing import Optional
|
||||
|
||||
from sqlalchemy import Boolean, Float, ForeignKey, Integer, String
|
||||
from sqlalchemy.dialects.postgresql import JSONB, UUID
|
||||
from sqlalchemy.orm import Mapped, mapped_column
|
||||
|
||||
from app.core.base import SQLBase, TimestampMixin, UUIDMixin
|
||||
|
||||
|
||||
class PipelineStats(SQLBase, TimestampMixin):
|
||||
__tablename__ = "pipeline_stats"
|
||||
|
||||
id: Mapped[str] = mapped_column(
|
||||
String(36), primary_key=True, default=lambda: str(__import__("uuid").uuid4())
|
||||
)
|
||||
user_id: Mapped[Optional[str]] = mapped_column(
|
||||
String(36), ForeignKey("users.id", ondelete="CASCADE"),
|
||||
nullable=True, index=True
|
||||
)
|
||||
stage: Mapped[str] = mapped_column(
|
||||
String(50), nullable=False, index=True
|
||||
)
|
||||
passed: Mapped[bool] = mapped_column(
|
||||
Boolean, nullable=False
|
||||
)
|
||||
reason: Mapped[Optional[str]] = mapped_column(
|
||||
String(255), nullable=True
|
||||
)
|
||||
duration_ms: Mapped[Optional[int]] = mapped_column(
|
||||
Integer, nullable=True
|
||||
)
|
||||
created_at: Mapped[datetime] = mapped_column(
|
||||
nullable=False,
|
||||
default=lambda: datetime.now(__import__("datetime").timezone.utc),
|
||||
)
|
||||
@@ -0,0 +1,31 @@
|
||||
"""Session model — one chat session = one idea discussion."""
|
||||
|
||||
from typing import Optional
|
||||
|
||||
from sqlalchemy import ForeignKey, String, Text
|
||||
from sqlalchemy.dialects.postgresql import UUID
|
||||
from sqlalchemy.orm import Mapped, mapped_column
|
||||
|
||||
from app.core.base import SQLBase, TimestampMixin, UUIDMixin
|
||||
|
||||
|
||||
class Session(SQLBase, UUIDMixin, TimestampMixin):
|
||||
__tablename__ = "sessions"
|
||||
|
||||
user_id: Mapped[UUID] = mapped_column(
|
||||
UUID(as_uuid=True),
|
||||
ForeignKey("users.id", ondelete="CASCADE"),
|
||||
nullable=False,
|
||||
index=True,
|
||||
)
|
||||
title: Mapped[str] = mapped_column(
|
||||
String(255), nullable=False, default="Новое обсуждение"
|
||||
)
|
||||
status: Mapped[str] = mapped_column(
|
||||
String(20), nullable=False, default="active", index=True
|
||||
)
|
||||
idea_id: Mapped[Optional[UUID]] = mapped_column(
|
||||
UUID(as_uuid=True),
|
||||
ForeignKey("ideas.id", ondelete="SET NULL"),
|
||||
nullable=True,
|
||||
)
|
||||
@@ -0,0 +1,69 @@
|
||||
"""Tariff models for VoIdea."""
|
||||
|
||||
from datetime import datetime
|
||||
from decimal import Decimal
|
||||
from typing import Any, Optional
|
||||
|
||||
from sqlalchemy import Boolean, ForeignKey, Numeric, String, Text
|
||||
from sqlalchemy.dialects.postgresql import JSONB, UUID
|
||||
from sqlalchemy.orm import Mapped, mapped_column
|
||||
|
||||
from app.core.base import SQLBase, TimestampMixin, UUIDMixin
|
||||
|
||||
|
||||
class TariffPlan(SQLBase, UUIDMixin, TimestampMixin):
|
||||
__tablename__ = "tariff_plans"
|
||||
|
||||
name: Mapped[str] = mapped_column(
|
||||
String(100), nullable=False
|
||||
)
|
||||
code: Mapped[str] = mapped_column(
|
||||
String(50), unique=True, nullable=False, index=True
|
||||
)
|
||||
description: Mapped[Optional[str]] = mapped_column(
|
||||
Text, nullable=True
|
||||
)
|
||||
price_monthly: Mapped[Decimal] = mapped_column(
|
||||
Numeric(10, 2), default=0, nullable=False
|
||||
)
|
||||
price_yearly: Mapped[Optional[Decimal]] = mapped_column(
|
||||
Numeric(10, 2), nullable=True
|
||||
)
|
||||
features: Mapped[Optional[dict[str, Any]]] = mapped_column(
|
||||
JSONB, nullable=True, default=None
|
||||
)
|
||||
is_active: Mapped[bool] = mapped_column(
|
||||
Boolean, default=True, nullable=False
|
||||
)
|
||||
sort_order: Mapped[int] = mapped_column(
|
||||
default=0, nullable=False
|
||||
)
|
||||
|
||||
|
||||
class UserSubscription(SQLBase, UUIDMixin, TimestampMixin):
|
||||
__tablename__ = "user_subscriptions"
|
||||
|
||||
user_id: Mapped[UUID] = mapped_column(
|
||||
UUID(as_uuid=True),
|
||||
ForeignKey("users.id", ondelete="CASCADE"),
|
||||
unique=True,
|
||||
nullable=False,
|
||||
index=True,
|
||||
)
|
||||
plan_id: Mapped[UUID] = mapped_column(
|
||||
UUID(as_uuid=True),
|
||||
ForeignKey("tariff_plans.id", ondelete="RESTRICT"),
|
||||
nullable=False,
|
||||
)
|
||||
status: Mapped[str] = mapped_column(
|
||||
String(20), default="active", nullable=False, index=True
|
||||
)
|
||||
current_period_start: Mapped[Optional[datetime]] = mapped_column(
|
||||
nullable=True
|
||||
)
|
||||
current_period_end: Mapped[Optional[datetime]] = mapped_column(
|
||||
nullable=True
|
||||
)
|
||||
canceled_at: Mapped[Optional[datetime]] = mapped_column(
|
||||
nullable=True
|
||||
)
|
||||
@@ -0,0 +1,63 @@
|
||||
"""User model for VoIdea."""
|
||||
|
||||
from datetime import datetime
|
||||
from typing import Any, Optional
|
||||
|
||||
from sqlalchemy import Boolean, DateTime, String
|
||||
from sqlalchemy.dialects.postgresql import JSONB, UUID
|
||||
from sqlalchemy.orm import Mapped, mapped_column, relationship
|
||||
|
||||
from app.core.base import SQLBase, TimestampMixin, UUIDMixin
|
||||
|
||||
|
||||
class User(SQLBase, UUIDMixin, TimestampMixin):
|
||||
__tablename__ = "users"
|
||||
|
||||
email: Mapped[str] = mapped_column(
|
||||
String(255), unique=True, index=True, nullable=False
|
||||
)
|
||||
password_hash: Mapped[Optional[str]] = mapped_column(
|
||||
String(255), nullable=True
|
||||
)
|
||||
display_name: Mapped[str] = mapped_column(
|
||||
String(255), nullable=False
|
||||
)
|
||||
avatar_url: Mapped[Optional[str]] = mapped_column(
|
||||
String(512), nullable=True
|
||||
)
|
||||
is_active: Mapped[bool] = mapped_column(
|
||||
Boolean, default=True, nullable=False
|
||||
)
|
||||
is_superuser: Mapped[bool] = mapped_column(
|
||||
Boolean, default=False, nullable=False
|
||||
)
|
||||
|
||||
# v2.0 role system
|
||||
role: Mapped[str] = mapped_column(
|
||||
String(20), default="user", nullable=False, index=True
|
||||
)
|
||||
is_owner: Mapped[bool] = mapped_column(
|
||||
Boolean, default=False, nullable=False
|
||||
)
|
||||
permissions: Mapped[Optional[dict[str, Any]]] = mapped_column(
|
||||
JSONB, nullable=True, default=None
|
||||
)
|
||||
accepted_terms_at: Mapped[Optional[datetime]] = mapped_column(
|
||||
nullable=True
|
||||
)
|
||||
accepted_terms_version: Mapped[Optional[str]] = mapped_column(
|
||||
String(20), nullable=True
|
||||
)
|
||||
|
||||
pipeline_tuning: Mapped[Optional[dict[str, Any]]] = mapped_column(
|
||||
JSONB, nullable=True, default=None
|
||||
)
|
||||
|
||||
oauth_provider: Mapped[Optional[str]] = mapped_column(
|
||||
String(50), nullable=True
|
||||
)
|
||||
oauth_id: Mapped[Optional[str]] = mapped_column(
|
||||
String(255), nullable=True
|
||||
)
|
||||
|
||||
ideas = relationship("Idea", back_populates="user", lazy="selectin")
|
||||
@@ -0,0 +1,35 @@
|
||||
"""Voice command model — user-specific dynamic commands for self-learning."""
|
||||
|
||||
from typing import Optional
|
||||
|
||||
from sqlalchemy import Boolean, ForeignKey, Integer, String
|
||||
from sqlalchemy.dialects.postgresql import UUID
|
||||
from sqlalchemy.orm import Mapped, mapped_column
|
||||
|
||||
from app.core.base import SQLBase, TimestampMixin, UUIDMixin
|
||||
|
||||
|
||||
class VoiceCommand(SQLBase, UUIDMixin, TimestampMixin):
|
||||
__tablename__ = "voice_commands"
|
||||
|
||||
user_id: Mapped[UUID] = mapped_column(
|
||||
UUID(as_uuid=True),
|
||||
ForeignKey("users.id", ondelete="CASCADE"),
|
||||
nullable=False,
|
||||
index=True,
|
||||
)
|
||||
phrase: Mapped[str] = mapped_column(
|
||||
String(255), nullable=False
|
||||
)
|
||||
action: Mapped[str] = mapped_column(
|
||||
String(50), nullable=False
|
||||
)
|
||||
agent_name: Mapped[Optional[str]] = mapped_column(
|
||||
String(100), nullable=True
|
||||
)
|
||||
count: Mapped[int] = mapped_column(
|
||||
Integer, default=0, nullable=False
|
||||
)
|
||||
is_active: Mapped[bool] = mapped_column(
|
||||
Boolean, default=True, nullable=False
|
||||
)
|
||||
Reference in New Issue
Block a user