diff --git a/backend/.env.example b/backend/.env.example
index 9f381eea..91a7d540 100644
--- a/backend/.env.example
+++ b/backend/.env.example
@@ -2,8 +2,8 @@
# PORT=8000
# CORS_ORIGINS=http://localhost:3000
-# SUPABASE_URL=
-# SUPABASE_SERVICE_ROLE_KEY=
+SUPABASE_URL=
+SUPABASE_SERVICE_ROLE_KEY=
DISCORD_BOT_TOKEN=
# ENABLE_DISCORD_BOT=true
@@ -12,6 +12,8 @@ DISCORD_BOT_TOKEN=
# EMBEDDING_MAX_BATCH_SIZE=32
# EMBEDDING_DEVICE=cpu
+BACKEND_URL=
+
GEMINI_API_KEY=
TAVILY_API_KEY=
diff --git a/backend/app/api/v1/auth.py b/backend/app/api/v1/auth.py
new file mode 100644
index 00000000..1e29acf8
--- /dev/null
+++ b/backend/app/api/v1/auth.py
@@ -0,0 +1,274 @@
+from fastapi import APIRouter, Request, HTTPException, Query
+from fastapi.responses import HTMLResponse
+from app.db.supabase.supabase_client import get_supabase_client
+from app.db.supabase.users_service import find_user_by_session_and_verify, get_verification_session_info
+from typing import Optional
+import logging
+
+logger = logging.getLogger(__name__)
+router = APIRouter()
+
+@router.get("/callback", response_class=HTMLResponse)
+async def auth_callback(request: Request, code: Optional[str] = Query(None), session: Optional[str] = Query(None)):
+ """
+ Handles the OAuth callback from Supabase after a user authorizes on GitHub.
+ """
+ logger.info(
+ f"OAuth callback received with code: {'[PRESENT]' if code else '[MISSING]'}, session: {'[PRESENT]' if session else '[MISSING]'}")
+
+ if not code:
+ logger.error("Missing authorization code in callback")
+ return _error_response("Missing authorization code. Please try the verification process again.")
+
+ if not session:
+ logger.error("Missing session ID in callback")
+ return _error_response("Missing session ID. Please try the !verify_github command again.")
+
+ # Check if session is valid and not expired
+ session_info = await get_verification_session_info(session)
+ if not session_info:
+ logger.error(f"Invalid or expired session ID: {session}")
+ return _error_response("Your verification session has expired. Please run the !verify_github command again.")
+
+ supabase = get_supabase_client()
+ try:
+ # Exchange code for session
+ logger.info("Exchanging authorization code for session")
+ session_response = await supabase.auth.exchange_code_for_session({
+ "auth_code": code,
+ })
+
+ if not session_response or not session_response.user:
+ logger.error("Failed to exchange code for session")
+ return _error_response("Authentication failed. Could not retrieve user session.")
+
+ user = session_response.user
+ logger.info(f"Successfully got user session for user: {user.id}")
+
+ # Extract GitHub info from user metadata
+ github_id = user.user_metadata.get("provider_id")
+ github_username = user.user_metadata.get("user_name")
+ email = user.email
+
+ if not github_id or not github_username:
+ logger.error(f"Missing GitHub details - ID: {github_id}, Username: {github_username}")
+ return _error_response("Could not retrieve GitHub details from user session.")
+
+ # Verify user using session ID
+ logger.info(f"Verifying user with session ID: {session}")
+ verified_user = await find_user_by_session_and_verify(
+ session_id=session,
+ github_id=str(github_id),
+ github_username=github_username,
+ email=email
+ )
+
+ if not verified_user:
+ logger.error("User verification failed - no pending verification found")
+ return _error_response("No pending verification found or verification has expired. Please try the !verify_github command again.")
+
+ logger.info(f"Successfully verified user: {verified_user.id}")
+ return _success_response(github_username)
+
+ except Exception as e:
+ logger.error(f"Unexpected error in OAuth callback: {str(e)}", exc_info=True)
+
+ # Handle specific error cases
+ if "already linked" in str(e):
+ return _error_response(f"Error: {str(e)}")
+
+ return _error_response("An unexpected error occurred during verification. Please try again.")
+
+@router.get("/session/{session_id}")
+async def get_session_status(session_id: str):
+ """Get the status of a verification session"""
+ session_info = await get_verification_session_info(session_id)
+ if not session_info:
+ raise HTTPException(status_code=404, detail="Session not found or expired")
+
+ return {
+ "valid": True,
+ "discord_id": session_info["discord_id"],
+ "expiry_time": session_info["expiry_time"],
+ "time_remaining": session_info["time_remaining"]
+ }
+
+def _success_response(github_username: str) -> str:
+ """Generate success HTML response"""
+ return f"""
+
+
+
+ Verification Successful!
+
+
+
+
+
+
+
✅
+
Verification Successful!
+
+
Your Discord account has been successfully linked!
+
GitHub User: {github_username}
+
+
You can now access all features that require GitHub authentication.
+
+
This window will close automatically in 5 seconds.
+
+
+
+
+ """
+
+def _error_response(error_message: str) -> str:
+ """Generate error HTML response"""
+ return f"""
+
+
+
+ Verification Failed
+
+
+
+
+
+
+
❌
+
Verification Failed
+
+
Please return to Discord and try the !verify_github command again.
+
+
If you continue to experience issues, please contact support.
+
+
+
+ """
diff --git a/backend/app/core/config.py b/backend/app/core/config.py
index 5d9ca18d..d3a531cb 100644
--- a/backend/app/core/config.py
+++ b/backend/app/core/config.py
@@ -32,6 +32,9 @@ class Settings(BaseSettings):
agent_timeout: int = 30
max_retries: int = 3
+ # Backend URL
+ backend_url: str = ""
+
@field_validator("supabase_url", "supabase_key", mode="before")
@classmethod
def _not_empty(cls, v, field):
diff --git a/backend/app/db/supabase/auth.py b/backend/app/db/supabase/auth.py
index b5d212c6..6e31f807 100644
--- a/backend/app/db/supabase/auth.py
+++ b/backend/app/db/supabase/auth.py
@@ -1,30 +1,49 @@
-import asyncio
-from app.db.supabase.supabase_client import supabase_client
+from typing import Optional
+from app.db.supabase.supabase_client import get_supabase_client
+import logging
+logger = logging.getLogger(__name__)
-async def login_with_oauth(provider: str):
+async def login_with_oauth(provider: str, redirect_to: Optional[str] = None, state: Optional[str] = None):
+ """
+ Generates an asynchronous OAuth sign-in URL.
+ """
+ supabase = get_supabase_client()
try:
- result = await asyncio.to_thread(
- supabase_client.auth.sign_in_with_oauth,
- {"provider": provider}
- )
+ options = {}
+ if redirect_to:
+ options['redirect_to'] = redirect_to
+ if state:
+ options['queryParams'] = {'state': state}
+
+ result = await supabase.auth.sign_in_with_oauth({
+ "provider": provider,
+ "options": options
+ })
return {"url": result.url}
except Exception as e:
- raise Exception(f"OAuth login failed for {provider}") from e
+ logger.error(f"OAuth login failed for provider {provider}: {e}", exc_info=True)
+ raise
-async def login_with_github():
- return await login_with_oauth("github")
+async def login_with_github(redirect_to: Optional[str] = None, state: Optional[str] = None):
+ """Generates a GitHub OAuth login URL."""
+ return await login_with_oauth("github", redirect_to=redirect_to, state=state)
-async def login_with_discord():
- return await login_with_oauth("discord")
+async def login_with_discord(redirect_to: Optional[str] = None):
+ """Generates a Discord OAuth login URL."""
+ return await login_with_oauth("discord", redirect_to=redirect_to)
-async def login_with_slack():
- return await login_with_oauth("slack")
+async def login_with_slack(redirect_to: Optional[str] = None):
+ """Generates a Slack OAuth login URL."""
+ return await login_with_oauth("slack", redirect_to=redirect_to)
async def logout(access_token: str):
+ """Logs out a user by revoking their session."""
+ supabase = get_supabase_client()
try:
- supabase_client.auth.set_session(access_token, refresh_token="")
- supabase_client.auth.sign_out()
+ await supabase.auth.set_session(access_token, refresh_token="")
+ await supabase.auth.sign_out()
return {"message": "User logged out successfully"}
except Exception as e:
- raise Exception(f"Logout failed: {str(e)}")
+ logger.error(f"Logout failed: {e}", exc_info=True)
+ raise
diff --git a/backend/app/db/supabase/supabase_client.py b/backend/app/db/supabase/supabase_client.py
index a0ab208a..b0f5fa7c 100644
--- a/backend/app/db/supabase/supabase_client.py
+++ b/backend/app/db/supabase/supabase_client.py
@@ -1,11 +1,13 @@
from app.core.config import settings
-from supabase import create_client
+from supabase._async.client import AsyncClient
-SUPABASE_URL = settings.supabase_url
-SUPABASE_KEY = settings.supabase_key
+supabase_client: AsyncClient = AsyncClient(
+ settings.supabase_url,
+ settings.supabase_key
+)
-supabase_client = create_client(SUPABASE_URL, SUPABASE_KEY)
-
-
-def get_supabase_client():
+def get_supabase_client() -> AsyncClient:
+ """
+ Returns a shared asynchronous Supabase client instance.
+ """
return supabase_client
diff --git a/backend/app/db/supabase/users_service.py b/backend/app/db/supabase/users_service.py
new file mode 100644
index 00000000..7ef98a56
--- /dev/null
+++ b/backend/app/db/supabase/users_service.py
@@ -0,0 +1,203 @@
+import uuid
+from datetime import datetime, timedelta
+from typing import Optional, Dict, Tuple
+from app.db.supabase.supabase_client import get_supabase_client
+from app.model.supabase.models import User
+import logging
+
+logger = logging.getLogger(__name__)
+
+# session_id -> (discord_id, expiry_time)
+_verification_sessions: Dict[str, Tuple[str, datetime]] = {}
+
+SESSION_EXPIRY_MINUTES = 5
+
+async def get_or_create_user_by_discord(
+ discord_id: str, display_name: str, discord_username: str, avatar_url: Optional[str]
+) -> User:
+ """
+ Get or create a user by Discord ID.
+ """
+ supabase = get_supabase_client()
+ existing_user_res = await supabase.table("users").select("*").eq("discord_id", discord_id).limit(1).execute()
+
+ if existing_user_res.data:
+ logger.info(f"Found existing user for Discord ID: {discord_id}")
+ return User(**existing_user_res.data[0])
+ logger.info(f"No user found for Discord ID: {discord_id}. Creating new user.")
+ new_user_data = {
+ "id": str(uuid.uuid4()),
+ "discord_id": discord_id,
+ "display_name": display_name,
+ "discord_username": discord_username,
+ "avatar_url": avatar_url,
+ "preferred_languages": [],
+ "created_at": datetime.now().isoformat(),
+ "updated_at": datetime.now().isoformat()
+ }
+ insert_res = await supabase.table("users").insert(new_user_data).execute()
+ if not insert_res.data:
+ raise Exception("Failed to create new user in database.")
+ return User(**insert_res.data[0])
+
+def _cleanup_expired_sessions():
+ """
+ Remove expired verification sessions.
+ """
+ current_time = datetime.now()
+ expired_sessions = [
+ session_id for session_id, (discord_id, expiry_time) in _verification_sessions.items()
+ if current_time > expiry_time
+ ]
+
+ for session_id in expired_sessions:
+ discord_id, _ = _verification_sessions[session_id]
+ del _verification_sessions[session_id]
+ logger.info(f"Cleaned up expired verification session {session_id} for Discord user {discord_id}")
+
+ if expired_sessions:
+ logger.info(f"Cleaned up {len(expired_sessions)} expired verification sessions")
+
+async def create_verification_session(discord_id: str) -> Optional[str]:
+ """
+ Create a verification session with expiry and return session ID.
+ """
+ supabase = get_supabase_client()
+
+ _cleanup_expired_sessions()
+
+ token = str(uuid.uuid4())
+ session_id = str(uuid.uuid4())
+ expiry_time = datetime.now() + timedelta(minutes=SESSION_EXPIRY_MINUTES)
+
+ try:
+ update_res = await supabase.table("users").update({
+ "verification_token": token,
+ "verification_token_expires_at": expiry_time.isoformat(),
+ "updated_at": datetime.now().isoformat()
+ }).eq("discord_id", discord_id).execute()
+
+ if update_res.data:
+ _verification_sessions[session_id] = (discord_id, expiry_time)
+ logger.info(
+ f"Created verification session {session_id} for Discord user {discord_id}, expires at {expiry_time}")
+ return session_id
+ logger.error(f"Failed to set verification token for Discord ID: {discord_id}. User not found.")
+ return None
+ except Exception as e:
+ logger.error(f"Error creating verification session for Discord ID {discord_id}: {str(e)}")
+ return None
+
+async def find_user_by_session_and_verify(
+ session_id: str, github_id: str, github_username: str, email: Optional[str]
+) -> Optional[User]:
+ """
+ Find and verify user using session ID with expiry validation.
+ """
+ supabase = get_supabase_client()
+
+ _cleanup_expired_sessions()
+
+ try:
+ session_data = _verification_sessions.get(session_id)
+ if not session_data:
+ logger.warning(f"No verification session found for session ID: {session_id}")
+ return None
+
+ discord_id, expiry_time = session_data
+
+ current_time = datetime.now().isoformat()
+ user_res = await supabase.table("users").select("*").eq(
+ "discord_id", discord_id
+ ).neq(
+ "verification_token", None
+ ).gt(
+ "verification_token_expires_at", current_time
+ ).limit(1).execute()
+
+ if not user_res.data:
+ logger.warning(f"No valid pending verification found for Discord ID: {discord_id} (token may have expired)")
+ del _verification_sessions[session_id]
+ return None
+
+ # Delete the session after successful validation
+ del _verification_sessions[session_id]
+
+ user_to_verify = user_res.data[0]
+
+ existing_github_user = await supabase.table("users").select("*").eq(
+ "github_id", github_id
+ ).neq("id", user_to_verify['id']).limit(1).execute()
+ if existing_github_user.data:
+ logger.warning(f"GitHub account {github_username} is already linked to another user")
+ await supabase.table("users").update({
+ "verification_token": None,
+ "verification_token_expires_at": None,
+ "updated_at": datetime.now().isoformat()
+ }).eq("id", user_to_verify['id']).execute()
+ raise Exception(f"GitHub account {github_username} is already linked to another Discord user")
+
+ update_data = {
+ "github_id": github_id,
+ "github_username": github_username,
+ "email": user_to_verify.get('email') or email,
+ "is_verified": True,
+ "verified_at": datetime.now().isoformat(),
+ "verification_token": None,
+ "verification_token_expires_at": None,
+ "updated_at": datetime.now().isoformat()
+ }
+
+ await supabase.table("users").update(update_data).eq("id", user_to_verify['id']).execute()
+
+ updated_user_res = await supabase.table("users").select("*").eq("id", user_to_verify['id']).limit(1).execute()
+
+ if not updated_user_res.data:
+ raise Exception(f"Failed to fetch updated user with ID: {user_to_verify['id']}")
+
+ logger.info(f"Successfully verified user {user_to_verify['id']} and linked GitHub account {github_username}.")
+ return User(**updated_user_res.data[0])
+ except Exception as e:
+ logger.error(f"Database error in find_user_by_session_and_verify: {e}", exc_info=True)
+ raise
+
+async def cleanup_expired_tokens():
+ """
+ Clean up expired verification tokens from database.
+ """
+ supabase = get_supabase_client()
+ current_time = datetime.now().isoformat()
+
+ try:
+ cleanup_res = await supabase.table("users").update({
+ "verification_token": None,
+ "verification_token_expires_at": None,
+ "updated_at": current_time
+ }).lt("verification_token_expires_at", current_time).neq("verification_token", None).execute()
+
+ if cleanup_res.data:
+ logger.info(f"Cleaned up {len(cleanup_res.data)} expired verification tokens from database")
+ except Exception as e:
+ logger.error(f"Error cleaning up expired tokens: {e}")
+
+async def get_verification_session_info(session_id: str) -> Optional[Dict[str, str]]:
+ """
+ Get information about a verification session.
+ """
+ _cleanup_expired_sessions()
+
+ session_data = _verification_sessions.get(session_id)
+ if not session_data:
+ return None
+
+ discord_id, expiry_time = session_data
+
+ if datetime.now() > expiry_time:
+ del _verification_sessions[session_id]
+ return None
+
+ return {
+ "discord_id": discord_id,
+ "expiry_time": expiry_time.isoformat(),
+ "time_remaining": str(expiry_time - datetime.now())
+ }
diff --git a/backend/app/model/supabase/models.py b/backend/app/model/supabase/models.py
index f87a354d..5634061c 100644
--- a/backend/app/model/supabase/models.py
+++ b/backend/app/model/supabase/models.py
@@ -12,6 +12,7 @@ class User(BaseModel):
id (UUID): Unique identifier for the user.
created_at (datetime): Timestamp when the user was created.
updated_at (datetime): Timestamp when the user was last updated.
+ email (Optional[str]): Email address of the user.
discord_id (Optional[str]): Discord user ID, if linked.
discord_username (Optional[str]): Discord username, if linked.
github_id (Optional[str]): GitHub user ID, if linked.
@@ -19,47 +20,54 @@ class User(BaseModel):
slack_id (Optional[str]): Slack user ID, if linked.
slack_username (Optional[str]): Slack username, if linked.
display_name (str): Display name of the user.
- email (str): Email address of the user.
avatar_url (Optional[str]): URL to the user's avatar image.
bio (Optional[str]): Short biography or description of the user.
location (Optional[str]): User's location.
is_verified (bool): Indicates if the user is verified.
- verification_token (Optional[str]): Token used for verifying the user.
+ verification_token (Optional[str]): Verification token for email/GitHub verification.
+ verification_token_expires_at (Optional[datetime]): Expiry time for verification token.
verified_at (Optional[datetime]): Timestamp when the user was verified.
- skills (Optional[List[str]]): List of user's skills.
- github_stats (Optional[dict]): GitHub statistics for the user.
- last_active_discord (Optional[datetime]): Last active time on Discord.
- last_active_github (Optional[datetime]): Last active time on GitHub.
- last_active_slack (Optional[datetime]): Last active time on Slack.
- total_interactions_count (int): Total number of user interactions.
- preferred_languages (List[str]): List of user's preferred programming languages.
- weaviate_user_id (Optional[str]): Associated Weaviate user ID, if any.
+ skills (Optional[dict]): Skills of the user.
+ github_stats (Optional[dict]): GitHub statistics of the user.
+ last_active_discord (Optional[datetime]): Timestamp when the user was last active on Discord.
+ last_active_github (Optional[datetime]): Timestamp when the user was last active on GitHub.
+ last_active_slack (Optional[datetime]): Timestamp when the user was last active on Slack.
+ total_interactions_count (int): Total number of interactions.
+ preferred_languages (List[str]): List of preferred programming languages.
"""
id: UUID
created_at: datetime
updated_at: datetime
+
+ email: Optional[str] = None
+
discord_id: Optional[str] = None
discord_username: Optional[str] = None
github_id: Optional[str] = None
github_username: Optional[str] = None
slack_id: Optional[str] = None
slack_username: Optional[str] = None
+
display_name: str
- email: str
avatar_url: Optional[str] = None
bio: Optional[str] = None
location: Optional[str] = None
+
is_verified: bool = False
verification_token: Optional[str] = None
+ verification_token_expires_at: Optional[datetime] = None
verified_at: Optional[datetime] = None
- skills: Optional[List[str]] = None
+
+ skills: Optional[dict] = None
github_stats: Optional[dict] = None
+
last_active_discord: Optional[datetime] = None
last_active_github: Optional[datetime] = None
last_active_slack: Optional[datetime] = None
+
total_interactions_count: int = 0
preferred_languages: List[str] = Field(default_factory=list)
- weaviate_user_id: Optional[str] = None
+
class Repository(BaseModel):
"""
@@ -69,7 +77,7 @@ class Repository(BaseModel):
id (UUID): Unique identifier for the repository.
created_at (datetime): Timestamp when the repository record was created.
updated_at (datetime): Timestamp when the repository record was last updated.
- github_id (Optional[int]): GitHub's unique identifier for the repository.
+ github_id (int): GitHub's unique identifier for the repository.
full_name (str): Full name of the repository (e.g., "owner/name").
name (str): Name of the repository.
owner (str): Owner of the repository.
@@ -77,71 +85,35 @@ class Repository(BaseModel):
stars_count (int): Number of stars the repository has received.
forks_count (int): Number of times the repository has been forked.
open_issues_count (int): Number of open issues in the repository.
- language (Optional[str]): Primary programming language used in the repository.
+ languages_used (List[str]): List of programming languages used in the repository.
topics (List[str]): List of topics/tags associated with the repository.
is_indexed (bool): Indicates if the repository has been indexed.
indexed_at (Optional[datetime]): Timestamp when the repository was indexed.
indexing_status (Optional[str]): Current status of the indexing process.
- total_chunks_count (int): Total number of chunks generated during indexing.
last_commit_hash (Optional[str]): Hash of the last commit indexed.
- indexing_progress (Optional[dict]): Progress details of the indexing process.
- weaviate_repo_id (Optional[str]): Identifier for the repository in Weaviate.
"""
id: UUID
created_at: datetime
updated_at: datetime
- github_id: Optional[int] = None
+
+ github_id: int
full_name: str
name: str
owner: str
description: Optional[str] = None
+
stars_count: int = 0
forks_count: int = 0
open_issues_count: int = 0
- language: Optional[str] = None
+
+ languages_used: List[str] = Field(default_factory=list)
topics: List[str] = Field(default_factory=list)
+
is_indexed: bool = False
indexed_at: Optional[datetime] = None
indexing_status: Optional[str] = None
- total_chunks_count: int = 0
last_commit_hash: Optional[str] = None
- indexing_progress: Optional[dict] = None
- weaviate_repo_id: Optional[str] = None
-class CodeChunk(BaseModel):
- """
- Represents a chunk of code extracted from a file within a repository.
-
- Attributes:
- id (UUID): Unique identifier for the code chunk.
- repository_id (UUID): Identifier of the repository this chunk belongs to.
- created_at (datetime): Timestamp when the chunk was created.
- file_path (str): Path to the file containing the code chunk.
- file_name (str): Name of the file containing the code chunk.
- file_extension (Optional[str]): Extension of the file (e.g., '.py', '.js').
- chunk_index (int): Index of the chunk within the file.
- content (str): The actual code content of the chunk.
- chunk_type (Optional[str]): Type of the chunk (e.g., 'function', 'class', 'block').
- language (Optional[str]): Programming language of the code chunk.
- lines_start (Optional[int]): Starting line number of the chunk in the file.
- lines_end (Optional[int]): Ending line number of the chunk in the file.
- code_metadata (Optional[dict]): Additional metadata related to the code chunk.
- weaviate_chunk_id (Optional[str]): Identifier for the chunk in Weaviate vector database.
- """
- id: UUID
- repository_id: UUID
- created_at: datetime
- file_path: str
- file_name: str
- file_extension: Optional[str] = None
- chunk_index: int
- content: str
- chunk_type: Optional[str] = None
- language: Optional[str] = None
- lines_start: Optional[int] = None
- lines_end: Optional[int] = None
- code_metadata: Optional[dict] = None
- weaviate_chunk_id: Optional[str] = None
class Interaction(BaseModel):
"""
@@ -150,9 +122,8 @@ class Interaction(BaseModel):
Attributes:
id (UUID): Unique identifier for the interaction.
created_at (datetime): Timestamp when the interaction was created.
- updated_at (datetime): Timestamp when the interaction was last updated.
user_id (UUID): Unique identifier of the user who performed the interaction.
- repository_id (UUID): Unique identifier of the repository associated with the interaction.
+ repository_id (Optional[UUID]): Unique identifier of the repository associated with the interaction.
platform (str): Name of the platform where the interaction occurred (e.g., GitHub, Slack).
platform_specific_id (str): Platform-specific identifier for the interaction.
channel_id (Optional[str]): Identifier for the channel where the interaction took place, if applicable.
@@ -163,21 +134,55 @@ class Interaction(BaseModel):
intent_classification (Optional[str]): Classification of the user's intent in the interaction.
topics_discussed (List[str]): List of topics discussed in the interaction.
metadata (Optional[dict]): Additional metadata related to the interaction.
- weaviate_interaction_id (Optional[str]): Identifier for the interaction in the Weaviate vector database.
"""
id: UUID
created_at: datetime
- updated_at: datetime
+
user_id: UUID
- repository_id: UUID
+ repository_id: Optional[UUID] = None
+
platform: str
platform_specific_id: str
channel_id: Optional[str] = None
thread_id: Optional[str] = None
+
content: str
interaction_type: str
+
sentiment_score: Optional[float] = None
intent_classification: Optional[str] = None
topics_discussed: List[str] = Field(default_factory=list)
+
metadata: Optional[dict] = None
- weaviate_interaction_id: Optional[str] = None
+
+
+class ConversationContext(BaseModel):
+ """
+ Represents the user's previous interactions with the agents in a concise format as summary.
+
+ Attributes:
+ id (UUID): Unique identifier for the conversation context.
+ user_id (UUID): Unique identifier of the user.
+ platform (str): Platform where the conversation occurred.
+ memory_thread_id (str): Unique identifier for the memory thread.
+ conversation_summary (str): Summary of the conversation.
+ key_topics (List[str]): List of key topics discussed in the conversation.
+ total_interactions (int): Total number of interactions in the conversation.
+ session_start_time (datetime): Timestamp when the conversation session started.
+ session_end_time (Optional[datetime]): Timestamp when the conversation session ended.
+ created_at (datetime): Timestamp when the conversation context was created.
+ """
+ id: UUID
+ user_id: UUID
+
+ platform: str
+ memory_thread_id: str
+
+ conversation_summary: str
+ key_topics: List[str] = Field(default_factory=list)
+
+ total_interactions: int
+ session_start_time: datetime
+ session_end_time: Optional[datetime] = None
+
+ created_at: datetime = Field(default_factory=datetime.now)
diff --git a/backend/app/scripts/supabase/create_db.sql b/backend/app/scripts/supabase/create_db.sql
new file mode 100644
index 00000000..8ccd2df3
--- /dev/null
+++ b/backend/app/scripts/supabase/create_db.sql
@@ -0,0 +1,107 @@
+-- Drop existing tables if they exist
+DROP TABLE IF EXISTS conversation_context;
+DROP TABLE IF EXISTS interactions;
+DROP TABLE IF EXISTS repositories;
+DROP TABLE IF EXISTS users;
+
+-- Table: users
+CREATE TABLE users (
+ id UUID PRIMARY KEY NOT NULL,
+ created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
+ updated_at TIMESTAMPTZ NOT NULL DEFAULT now(),
+
+ -- The email is optional to allow social-only sign-ups, but must be unique if provided.
+ email TEXT UNIQUE,
+
+ -- Social IDs
+ discord_id TEXT UNIQUE,
+ discord_username TEXT,
+ github_id TEXT UNIQUE,
+ github_username TEXT,
+ slack_id TEXT UNIQUE,
+ slack_username TEXT,
+
+ display_name TEXT NOT NULL,
+ avatar_url TEXT,
+ bio TEXT,
+ location TEXT,
+
+ -- Verification fields to manage the GitHub linking flow.
+ is_verified BOOLEAN NOT NULL DEFAULT false,
+ verification_token TEXT UNIQUE,
+ verification_token_expires_at TIMESTAMPTZ,
+ verified_at TIMESTAMPTZ,
+
+ skills JSONB,
+ github_stats JSONB,
+
+ last_active_discord TIMESTAMPTZ,
+ last_active_github TIMESTAMPTZ,
+ last_active_slack TIMESTAMPTZ,
+
+ total_interactions_count INTEGER NOT NULL DEFAULT 0,
+ preferred_languages TEXT[]
+);
+
+-- Create index for efficient cleanup queries
+CREATE INDEX IF NOT EXISTS idx_users_verification_token_expires_at
+ON users(verification_token_expires_at)
+WHERE verification_token_expires_at IS NOT NULL;
+
+-- Create index for efficient verification queries
+CREATE INDEX IF NOT EXISTS idx_users_discord_verification
+ON users(discord_id, verification_token)
+WHERE verification_token IS NOT NULL;
+
+-- Table: repositories
+CREATE TABLE repositories (
+ id UUID PRIMARY KEY NOT NULL,
+ created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
+ updated_at TIMESTAMPTZ NOT NULL DEFAULT now(),
+ github_id BIGINT UNIQUE NOT NULL,
+ full_name TEXT NOT NULL,
+ name TEXT NOT NULL,
+ owner TEXT NOT NULL,
+ description TEXT,
+ stars_count INTEGER NOT NULL DEFAULT 0,
+ forks_count INTEGER NOT NULL DEFAULT 0,
+ open_issues_count INTEGER NOT NULL DEFAULT 0,
+ languages_used TEXT[],
+ topics TEXT[],
+ is_indexed BOOLEAN NOT NULL DEFAULT false,
+ indexed_at TIMESTAMPTZ,
+ indexing_status TEXT,
+ last_commit_hash TEXT
+);
+
+-- Table: interactions
+CREATE TABLE interactions (
+ id UUID PRIMARY KEY NOT NULL,
+ created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
+ user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
+ repository_id UUID REFERENCES repositories(id) ON DELETE SET NULL,
+ platform TEXT NOT NULL,
+ platform_specific_id TEXT NOT NULL,
+ channel_id TEXT,
+ thread_id TEXT,
+ content TEXT,
+ interaction_type TEXT,
+ sentiment_score FLOAT,
+ intent_classification TEXT,
+ topics_discussed TEXT[],
+ metadata JSONB
+);
+
+-- Table: conversation_contexts
+CREATE TABLE conversation_context (
+ id UUID PRIMARY KEY NOT NULL,
+ user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
+ platform TEXT NOT NULL,
+ memory_thread_id TEXT NOT NULL UNIQUE,
+ conversation_summary TEXT,
+ key_topics TEXT[],
+ total_interactions INTEGER,
+ session_start_time TIMESTAMPTZ,
+ session_end_time TIMESTAMPTZ,
+ created_at TIMESTAMPTZ NOT NULL DEFAULT now()
+);
diff --git a/backend/app/scripts/supabase/populate_db.sql b/backend/app/scripts/supabase/populate_db.sql
index f627795b..a3d3ee5d 100644
--- a/backend/app/scripts/supabase/populate_db.sql
+++ b/backend/app/scripts/supabase/populate_db.sql
@@ -1,1057 +1,127 @@
--- Table: users
--- Stores user profile and authentication information, including social platform identities and activity metadata.
-CREATE TABLE users (
- id UUID PRIMARY KEY NOT NULL,
- created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
- updated_at TIMESTAMPTZ NOT NULL DEFAULT now(),
-
- discord_id TEXT UNIQUE,
- discord_username TEXT,
-
- github_id TEXT UNIQUE,
- github_username TEXT,
-
- slack_id TEXT UNIQUE,
- slack_username TEXT,
-
- display_name TEXT NOT NULL,
- email TEXT NOT NULL,
- avatar_url TEXT,
- bio TEXT,
- location TEXT,
-
- is_verified BOOLEAN NOT NULL DEFAULT false,
- verification_token TEXT,
- verified_at TIMESTAMPTZ,
-
- skills JSONB, -- Array or object of user skills
- github_stats JSONB, -- GitHub statistics (e.g., contributions)
-
- last_active_discord TIMESTAMPTZ,
- last_active_github TIMESTAMPTZ,
- last_active_slack TIMESTAMPTZ,
-
- total_interactions_count INTEGER NOT NULL DEFAULT 0,
- preferred_languages TEXT[], -- List of programming languages
-
- weaviate_user_id TEXT UNIQUE -- External vector DB reference
-);
-
--- Table: repositories
--- Stores metadata for code repositories, including indexing and statistics.
-CREATE TABLE repositories (
- id UUID PRIMARY KEY NOT NULL,
- created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
- updated_at TIMESTAMPTZ NOT NULL DEFAULT now(),
-
- github_id BIGINT UNIQUE, -- Unique GitHub repository identifier
- full_name TEXT NOT NULL, -- Format: owner/repo
- name TEXT NOT NULL, -- Repository name
- owner TEXT NOT NULL, -- Repository owner
- description TEXT,
-
- stars_count INTEGER NOT NULL DEFAULT 0,
- forks_count INTEGER NOT NULL DEFAULT 0,
- open_issues_count INTEGER NOT NULL DEFAULT 0,
-
- language TEXT, -- Primary language
- topics TEXT[], -- List of repository topics
-
- is_indexed BOOLEAN NOT NULL DEFAULT false,
- indexed_at TIMESTAMPTZ, -- When repository was indexed
-
- indexing_status TEXT, -- Status: pending, processing, completed, failed
- total_chunks_count INTEGER NOT NULL DEFAULT 0,
-
- last_commit_hash TEXT, -- Last commit hash
- indexing_progress JSONB, -- Progress details
-
- weaviate_repo_id TEXT UNIQUE -- External vector DB reference
-);
-
--- Table: code_chunks
--- Stores segmented code blocks from repositories for analysis and retrieval.
-CREATE TABLE code_chunks (
- id UUID PRIMARY KEY NOT NULL,
- repository_id UUID NOT NULL REFERENCES repositories(id) ON DELETE CASCADE,
-
- created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
-
- file_path TEXT NOT NULL, -- Full path to the file
- file_name TEXT NOT NULL, -- File name
- file_extension TEXT, -- File extension (e.g., .py, .js)
-
- chunk_index INTEGER NOT NULL, -- Order of chunk in file
- content TEXT, -- Code content
-
- chunk_type TEXT, -- Type: function, class, module, comment, import
- language TEXT, -- Programming language
-
- lines_start INTEGER, -- Start line number
- lines_end INTEGER, -- End line number
-
- code_metadata JSONB, -- Additional analysis data
-
- weaviate_chunk_id TEXT UNIQUE -- External vector DB reference
-);
-
--- Table: interactions
--- Stores user interactions across platforms, including messages, issues, and comments.
-CREATE TABLE interactions (
- id UUID PRIMARY KEY NOT NULL,
- created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
- updated_at TIMESTAMPTZ NOT NULL DEFAULT now(),
-
- user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
- repository_id UUID REFERENCES repositories(id) ON DELETE SET NULL,
-
- platform TEXT NOT NULL, -- Platform: discord, github, slack
- platform_specific_id TEXT NOT NULL, -- Platform-specific identifier
- channel_id TEXT, -- Channel or repository reference
- thread_id TEXT, -- Thread or conversation reference
-
- content TEXT, -- Content of the interaction
- interaction_type TEXT, -- Type: message, issue, pr, comment, reaction
-
- sentiment_score FLOAT, -- Sentiment score (-1 to 1)
- intent_classification TEXT, -- Classified intent (e.g., help_request)
-
- topics_discussed TEXT[], -- Topics extracted from content
- metadata JSONB, -- Additional platform-specific data
-
- weaviate_interaction_id TEXT UNIQUE -- External vector DB reference
-);
-
+-- Users
insert into
users (
- id,
- created_at,
- updated_at,
- discord_id,
- discord_username,
- github_id,
- github_username,
- slack_id,
- slack_username,
- display_name,
- email,
- avatar_url,
- bio,
- location,
- is_verified,
- verification_token,
- verified_at,
- skills,
- github_stats,
- last_active_discord,
- last_active_github,
- last_active_slack,
- total_interactions_count,
- preferred_languages,
- weaviate_user_id
+ id, created_at, updated_at, email, discord_id, discord_username,
+ github_id, github_username, slack_id, slack_username, display_name,
+ avatar_url, bio, location, is_verified, verification_token,
+ verification_token_expires_at, verified_at, skills, github_stats,
+ last_active_discord, last_active_github, last_active_slack,
+ total_interactions_count, preferred_languages
)
values
(
- '6afc59e3-18b7-4182-b42c-8210d1152b07',
- '2025-05-05 03:56:41',
- '2025-01-22 14:50:25',
- '3eb13b90-4668-4257-bdd6-40fb06671ad1',
- 'donaldgarcia',
- '16419f82-8b9d-4434-a465-e150bd9c66b3',
- 'fjohnson',
- '9a1de644-815e-46d1-bb8f-aa1837f8a88b',
- 'hoffmanjennifer',
- 'Jennifer Cole',
- 'blakeerik@yahoo.com',
- 'https://dummyimage.com/696x569',
- 'Bill here grow gas enough analysis. Movie win her need stop peace technology.',
- 'East Steven',
- true,
- 'a3d70628-ece6-4fa2-bd51-66e6451b4cf3',
- '2025-05-14 15:04:01',
- '{"skills": ["Python", "C++", "Java"]}'::jsonb,
- '{"commits": 300}'::jsonb,
- '2025-04-19 03:34:26',
- '2025-02-12 15:28:51',
- '2025-05-13 22:32:01',
- 28,
- array['JavaScript', 'C++'],
- 'c6a7ee39-c4b0-42cc-97c5-24a55304317f'
- ),
- (
- '6f990423-0d57-4c64-b191-17e53f39c799',
- '2025-01-11 20:41:23',
- '2025-02-14 11:26:28',
- '50c187fc-ce17-4b4e-8837-b8a3d261a7ab',
- 'nadams',
- 'e059a0ee-9132-463e-b162-87e4e9c349e0',
- 'jason76',
- '757750a9-a491-40b2-aa1f-ca65e27a984d',
- 'josephwright',
- 'Deborah Richards',
- 'jeffrey28@yahoo.com',
- 'https://www.lorempixel.com/186/96',
- 'Civil quite others his other life edge network. Quite boy those.',
- 'Kathrynside',
- true,
- '87c5421e-ec24-43c5-8754-108ff4188f3f',
- '2025-01-01 02:39:54',
- '{"skills": ["C++", "TypeScript", "Rust"]}'::jsonb,
- '{"commits": 139}'::jsonb,
- '2025-04-27 07:17:02',
- '2025-03-04 22:40:36',
- '2025-04-05 21:04:03',
- 75,
- array['Go', 'Python'],
- '5cec4eb5-edd9-4831-9ca3-5cfb04fc6d82'
- ),
- (
- '2aefee92-c7da-4d6e-90c1-d6c3bb82c0e1',
- '2025-03-01 17:07:10',
- '2025-02-16 11:55:43',
- '913e4de2-e0c5-4cb8-bda9-c2a90ed42f1a',
- 'millertodd',
- '885f6e66-c2b6-42c5-ba5d-310011b7e948',
- 'davidalvarez',
- '8715a103-43da-4043-aa45-c2ab8cbfedb0',
- 'ibrandt',
- 'Melissa Marquez',
- 'samuel87@gmail.com',
- 'https://www.lorempixel.com/507/460',
- 'Open discover detail. Remain arrive attack all. Audience draw protect Democrat car very.',
- 'Stevenland',
- true,
- 'db20a56e-dc81-4fe7-8eda-8bbb71710434',
- '2025-04-17 20:42:06',
- '{"skills": ["Python", "JavaScript", "C++"]}'::jsonb,
- '{"commits": 567}'::jsonb,
- '2025-01-20 00:17:15',
- '2025-01-10 19:45:31',
- '2025-05-07 15:12:55',
- 77,
- array['Python', 'Rust'],
- '03c72ba8-d605-4770-8a63-f881ffd0f9d5'
- ),
- (
- '9b56cac8-504a-4dd8-b7ba-0a5bfce7abf7',
- '2025-01-19 02:38:09',
- '2025-05-27 07:52:11',
- '680ac07a-2a93-4d62-bc83-5dc0d9441fa5',
- 'zolson',
- '610461e3-2a25-4888-8f02-bad0e7067ef4',
- 'gallowayjoseph',
- '490617f2-747b-4dba-88fe-3ccdc8b8d9c6',
- 'uhorton',
- 'Kristi Higgins MD',
- 'tanyariley@hotmail.com',
- 'https://www.lorempixel.com/124/642',
- 'Live try most arm meet surface attention attack.',
- 'Stewartland',
- true,
- 'ff9ab5c2-9f04-4aed-b552-332702627f73',
- '2025-04-20 07:44:25',
- '{"skills": ["C++", "TypeScript", "Rust"]}'::jsonb,
- '{"commits": 479}'::jsonb,
- '2025-01-20 09:56:47',
- '2025-01-15 05:25:10',
- '2025-04-15 09:33:28',
- 28,
- array['Go', 'Rust'],
- '21e8ac68-43e4-4caf-8181-a8cc369147eb'
- ),
- (
- '5a64824b-afcd-4586-9a25-16af29e673a3',
- '2025-03-09 18:29:56',
- '2025-01-14 08:35:26',
- '48f4ef12-5e99-43d2-be89-6c64e117dac3',
- 'samueldaniels',
- 'fcbb4e59-fbdd-4f7c-9c96-e9ec4d71c366',
- 'contrerasangela',
- 'f05db76e-1a84-451a-a9d3-d7c7ee87905e',
- 'josephpreston',
- 'Brittney Campbell',
- 'james48@king-odonnell.com',
- 'https://placekitten.com/189/867',
- 'Four capital woman.
-Necessary into act away third tough. Along hard need involve among half value.',
- 'East Michelle',
- false,
- '2d534dd0-cf8e-4c5a-8cc5-6569f9e8a369',
- '2025-03-22 01:25:52',
- '{"skills": ["TypeScript", "Python", "JavaScript"]}'::jsonb,
- '{"commits": 764}'::jsonb,
- '2025-01-05 19:30:46',
- '2025-02-04 19:52:52',
- '2025-05-24 00:38:21',
- 54,
- array['Java', 'TypeScript'],
- 'ee49f329-c84a-4b28-950a-1b46ecab3301'
- ),
- (
- '26283e71-c735-4a11-9831-afd279af4a4f',
- '2025-03-21 22:43:20',
- '2025-05-11 01:59:36',
- '3f87e362-cf8d-446a-bc2c-bb0ddd334cc7',
- 'erik16',
- '787f2425-dbcc-4477-89e9-db0adf465290',
- 'jamessellers',
- 'cb9bc326-d20e-4c17-8e20-fd1a598336e3',
- 'jeffreykeller',
- 'Donald Jones',
- 'darlene68@yahoo.com',
- 'https://placekitten.com/56/236',
- 'While enter board its rock finish paper memory. Tonight couple and job mind southern.',
- 'South Elizabeth',
- true,
- 'e71e43a6-bf85-4f0e-ad64-b56c610faa3f',
- '2025-02-03 19:01:34',
- '{"skills": ["JavaScript", "Java", "Python"]}'::jsonb,
- '{"commits": 144}'::jsonb,
- '2025-04-30 12:30:26',
- '2025-04-21 12:13:21',
- '2025-02-28 10:31:41',
- 48,
- array['Python', 'Java'],
- '001a9a8b-d56f-4350-8c45-9ce267f48ad5'
- ),
- (
- '010d518f-362b-435b-a4de-148914bcbdb9',
- '2025-03-01 00:09:35',
- '2025-02-25 17:06:53',
- '9479e1e6-c927-4d9b-ae0d-264835ce8841',
- 'steven73',
- 'e6b3c944-cb32-4e35-b922-bac282dc4c8e',
- 'donnacampbell',
- '55cee5db-9e87-404c-a208-6977a9f25336',
- 'cardenaskaren',
- 'Courtney Gonzalez',
- 'sanchezthomas@gibson.org',
- 'https://placeimg.com/398/786/any',
- 'Imagine my indeed deal information toward. Watch affect thing offer local wall fear hope.',
- 'West Nicholasborough',
- false,
- 'c2dff335-5666-4f9f-93ac-2ab974672cd9',
- '2025-03-15 14:51:22',
- '{"skills": ["Rust", "Java", "Python"]}'::jsonb,
- '{"commits": 797}'::jsonb,
- '2025-02-24 00:46:11',
- '2025-05-27 00:37:11',
- '2025-03-23 20:38:04',
- 58,
- array['Rust', 'Python'],
- '78660765-14f7-4e8d-95bc-b8d04094dded'
- ),
- (
- 'c28e727c-f6f1-4fb1-98c4-d7230cd1c855',
- '2025-01-04 18:19:57',
- '2025-05-26 10:42:21',
- 'f3b1025b-fff9-4585-8d55-7b618a175dfe',
- 'jeffrey10',
- '3f4df561-f319-4125-87f1-94f9c1156d6d',
- 'maryhowell',
- 'ab61a7b1-793b-4c32-a050-04943d114802',
- 'elliottjeffery',
- 'Pamela Jackson',
- 'tamirodriguez@hickman.biz',
- 'https://www.lorempixel.com/812/406',
- 'Right with modern executive beyond. Fast guess few remain call. Window network recently.',
- 'Christopherbury',
- false,
- '6a8a616f-c3b2-40d0-8edd-dfcd1e52d770',
- '2025-04-28 17:38:42',
- '{"skills": ["Python", "Rust", "Java"]}'::jsonb,
- '{"commits": 899}'::jsonb,
- '2025-04-26 18:09:08',
- '2025-05-01 02:02:18',
- '2025-02-13 19:46:41',
- 80,
- array['Rust', 'Java'],
- '7354ea6f-6160-4459-85c7-504bc693da11'
- ),
- (
- '7771182e-ed52-4f4e-a376-0750b9854324',
- '2025-03-27 23:13:45',
- '2025-02-27 17:45:57',
- '6dc7cac7-fd72-4050-96a9-954fdc33e1f9',
- 'ashley09',
- 'f295456e-1967-4f06-bd76-7e35f5c9b047',
- 'colleenbaker',
- '2c7f0b79-3d67-4de9-a834-e4c014c8b3b4',
- 'moorericky',
- 'Julie Johnson',
- 'jacqueline71@hotmail.com',
- 'https://dummyimage.com/478x541',
- 'Name positive training step. Arrive society organization station. Keep light fight I evening.',
- 'Rickymouth',
- true,
- '292bd156-db94-4570-9ac7-0ec0ab8ddeb4',
- '2025-03-06 00:59:47',
- '{"skills": ["C++", "Python", "TypeScript"]}'::jsonb,
- '{"commits": 727}'::jsonb,
- '2025-03-21 22:24:18',
- '2025-05-15 16:45:13',
- '2025-04-07 05:03:10',
- 29,
- array['TypeScript', 'Java'],
- '668409e3-f1f8-443e-a99f-131849c8a43f'
- ),
- (
- 'e4ca4ab9-de13-42ea-a394-db08a247abb7',
- '2025-04-17 19:38:09',
- '2025-01-08 03:01:15',
- '409d3602-5084-4242-968b-1625746f7891',
- 'darrell68',
- 'a85c6e4a-004b-4fab-bcf5-6188d32e6dcd',
- 'mgutierrez',
- 'c1a6423b-9f64-4eed-9c9d-927d84b871bb',
- 'wgarrett',
- 'Derek Anderson',
- 'fgilmore@gmail.com',
- 'https://dummyimage.com/59x490',
- 'Add impact different success box water positive. Marriage respond meeting event.',
- 'Grimesmouth',
- true,
- '551ac8ea-585a-4afa-bbfd-cc1289e06ab3',
- '2025-04-18 04:07:17',
- '{"skills": ["TypeScript", "JavaScript", "Python"]}'::jsonb,
- '{"commits": 439}'::jsonb,
- '2025-04-16 12:34:03',
- '2025-03-15 05:55:34',
- '2025-03-30 10:03:34',
- 35,
- array['Go', 'C++'],
- '304b8590-de9e-4757-9260-001eeecf67d2'
+ '6afc59e3-18b7-4182-b42c-8210d1152b07', '2025-05-05 03:56:41', '2025-01-22 14:50:25',
+ 'blakeerik@yahoo.com', '3eb13b9046684257', 'donaldgarcia', '16419f828b9d4434', 'fjohnson',
+ '9a1de644815e46d1', 'hoffmanjennifer', 'Jennifer Cole', 'https://dummyimage.com/696x569',
+ 'Bill here grow gas enough analysis. Movie win her need stop peace technology.', 'East Steven',
+ true, null, null, '2025-05-14 15:04:01',
+ '{"skills": ["Python", "C++", "Java"]}'::jsonb, '{"commits": 300}'::jsonb,
+ '2025-04-19 03:34:26', '2025-02-12 15:28:51', '2025-05-13 22:32:01', 28,
+ array['JavaScript', 'C++']
+ ),
+ (
+ '6f990423-0d57-4c64-b191-17e53f39c799', '2025-01-11 20:41:23', '2025-02-14 11:26:28',
+ 'jeffrey28@yahoo.com', '50c187fcce174b4e', 'nadams', 'e059a0ee9132463e', 'jason76',
+ '757750a9a49140b2', 'josephwright', 'Deborah Richards', 'https://www.lorempixel.com/186/96',
+ 'Civil quite others his other life edge network. Quite boy those.', 'Kathrynside',
+ true, null, null, '2025-01-01 02:39:54',
+ '{"skills": ["C++", "TypeScript", "Rust"]}'::jsonb, '{"commits": 139}'::jsonb,
+ '2025-04-27 07:17:02', '2025-03-04 22:40:36', '2025-04-05 21:04:03', 75,
+ array['Go', 'Python']
+ ),
+ (
+ '2aefee92-c7da-4d6e-90c1-d6c3bb82c0e1', '2025-03-01 17:07:10', '2025-02-16 11:55:43',
+ 'samuel87@gmail.com', '913e4de2e0c54cb8', 'millertodd', '885f6e66c2b642c5', 'davidalvarez',
+ '8715a10343da4043', 'ibrandt', 'Melissa Marquez', 'https://www.lorempixel.com/507/460',
+ 'Open discover detail. Remain arrive attack all. Audience draw protect Democrat car very.', 'Stevenland',
+ false, 'db20a56e-dc81-4fe7-8eda-8bbb71710434', '2025-06-21 12:00:00', null,
+ '{"skills": ["Python", "JavaScript", "C++"]}'::jsonb, '{"commits": 567}'::jsonb,
+ '2025-01-20 00:17:15', '2025-01-10 19:45:31', '2025-05-07 15:12:55', 77,
+ array['Python', 'Rust']
);
+-- Repositories
insert into
repositories (
- id,
- created_at,
- updated_at,
- github_id,
- full_name,
- name,
- owner,
- description,
- stars_count,
- forks_count,
- open_issues_count,
- language,
- topics,
- is_indexed,
- indexed_at,
- indexing_status,
- total_chunks_count,
- last_commit_hash,
- indexing_progress,
- weaviate_repo_id
+ id, created_at, updated_at, github_id, full_name, name, owner, description,
+ stars_count, forks_count, open_issues_count, languages_used, topics, is_indexed,
+ indexed_at, indexing_status, last_commit_hash
)
values
(
- 'f6b0bff9-074d-4062-86f5-0a853e521334',
- '2025-05-16 10:34:41',
- '2025-02-16 08:54:52',
- 3728882,
- 'jamessellers/repo_0',
- 'repo_0',
- 'jamessellers',
- 'Him task improve fish list tree high.',
- 3032,
- 363,
- 26,
- 'C++',
- array['Java', 'C++'],
- true,
- '2025-05-09 21:00:50',
- 'processing',
- 18,
- 'e270dbf424cff6864cc592f6611d8df90c895ec5',
- '{"progress": 93}'::jsonb,
- '7ecddbaf-26f0-4fcf-bb16-e5dba6eab79e'
+ 'f6b0bff9-074d-4062-86f5-0a853e521334', '2025-05-16 10:34:41', '2025-02-16 08:54:52', 3728882,
+ 'jamessellers/repo_0', 'repo_0', 'jamessellers', 'Him task improve fish list tree high.',
+ 3032, 363, 26, array['C++', 'Python'], array['Java', 'C++'], true, '2025-05-09 21:00:50',
+ 'completed', 'e270dbf424cff6864cc592f6611d8df90c895ec5'
),
(
- '0f08ecdb-53dd-4352-bb50-b1cfbf09da8b',
- '2025-01-08 04:31:26',
- '2025-01-25 12:21:00',
- 3741438,
- 'gallowayjoseph/repo_1',
- 'repo_1',
- 'gallowayjoseph',
- 'Whole forward beyond suddenly between treat address.',
- 3786,
- 388,
- 34,
- 'C++',
- array['C++', 'Rust'],
- true,
- '2025-01-28 23:48:46',
- 'completed',
- 2,
- 'c9f97db5d2fc4b809df59bc23dd7345dbe6d14d5',
- '{"progress": 29}'::jsonb,
- '1327f1bc-2784-478f-b84f-16b3a79fbfaf'
+ '0f08ecdb-53dd-4352-bb50-b1cfbf09da8b', '2025-01-08 04:31:26', '2025-01-25 12:21:00', 3741438,
+ 'gallowayjoseph/repo_1', 'repo_1', 'gallowayjoseph', 'Whole forward beyond suddenly between treat address.',
+ 3786, 388, 34, array['C++', 'Go'], array['C++', 'Rust'], true, '2025-01-28 23:48:46',
+ 'completed', 'c9f97db5d2fc4b809df59bc23dd7345dbe6d14d5'
),
(
- '08946f22-0d74-4499-b40d-0f60218d5152',
- '2025-04-02 03:59:05',
- '2025-02-21 11:05:44',
- 6292423,
- 'fjohnson/repo_2',
- 'repo_2',
- 'fjohnson',
- 'Perhaps however bag forget purpose move.',
- 3286,
- 274,
- 8,
- 'JavaScript',
- array['Rust', 'C++'],
- false,
- '2025-03-03 11:44:52',
- 'processing',
- 16,
- '5e3af4aafc18e025cea707fa7707a1d945e0ffef',
- '{"progress": 50}'::jsonb,
- 'df547e50-7cea-4045-8268-283ee32f2e63'
- ),
- (
- 'dda18eb8-8354-4897-8c7f-2c66afbc73e6',
- '2025-04-16 01:19:02',
- '2025-01-07 23:18:06',
- 3396987,
- 'maryhowell/repo_3',
- 'repo_3',
- 'maryhowell',
- 'Attention piece TV young section its better plant.',
- 2169,
- 142,
- 31,
- 'C++',
- array['Rust', 'TypeScript'],
- false,
- '2025-01-20 12:23:51',
- 'failed',
- 19,
- '22a9658e1dcda6fa5df48102f5882b204e39bc17',
- '{"progress": 51}'::jsonb,
- '0769165f-e746-4cb9-8ca9-cf07b1aa0f6a'
- ),
- (
- '7a9eab06-0656-43dd-8b8d-b17b9e5f396c',
- '2025-01-09 22:20:00',
- '2025-03-04 23:41:43',
- 4679591,
- 'jamessellers/repo_4',
- 'repo_4',
- 'jamessellers',
- 'Fall hear certainly most.',
- 1133,
- 521,
- 63,
- 'Python',
- array['TypeScript', 'Python'],
- true,
- '2025-04-20 21:24:57',
- 'processing',
- 6,
- 'effd50723baca7c5da884a171f8b5bbed8320a23',
- '{"progress": 87}'::jsonb,
- '14374509-2cd1-486a-ab84-0c672e183554'
- ),
- (
- 'ea879f36-d060-4f65-bf5d-9138a542f74a',
- '2025-04-29 08:26:15',
- '2025-03-16 06:47:08',
- 2065818,
- 'donnacampbell/repo_5',
- 'repo_5',
- 'donnacampbell',
- 'Raise marriage on discussion point least project together.',
- 3152,
- 390,
- 76,
- 'Go',
- array['Rust', 'Java'],
- true,
- '2025-04-01 08:14:14',
- 'pending',
- 18,
- 'a76a3a0bef5688fbee63da697c29fa6d719b37d9',
- '{"progress": 96}'::jsonb,
- '12e89d10-2871-4733-8bed-db12ad77e82f'
- ),
- (
- '07921dba-1ae8-422b-9f29-9c908080aa1b',
- '2025-03-27 18:36:35',
- '2025-03-09 02:27:09',
- 6707197,
- 'contrerasangela/repo_6',
- 'repo_6',
- 'contrerasangela',
- 'Federal while real lead few yourself table blood.',
- 913,
- 300,
- 55,
- 'JavaScript',
- array['Go', 'Python'],
- false,
- '2025-03-16 01:17:11',
- 'processing',
- 17,
- '3a65c2c24c52e4b1907c677fc07132e89ec719bc',
- '{"progress": 13}'::jsonb,
- 'b303f438-fe21-40d0-8bbe-4aff9326dffd'
- ),
- (
- 'd96ff094-7f9c-42d8-bb60-f83f19b07f77',
- '2025-02-27 07:52:30',
- '2025-01-05 06:14:45',
- 9517169,
- 'contrerasangela/repo_7',
- 'repo_7',
- 'contrerasangela',
- 'Ever not rate seat any paper.',
- 4988,
- 203,
- 19,
- 'Java',
- array['TypeScript', 'JavaScript'],
- true,
- '2025-04-07 10:55:00',
- 'completed',
- 16,
- '4b0c490400e9caf8027725c024538d6df508bd11',
- '{"progress": 2}'::jsonb,
- 'cc8218da-c696-45e6-8944-051be726be23'
- ),
- (
- '4882ce56-489d-4abc-bc29-bf3ad5c48930',
- '2025-02-14 16:25:45',
- '2025-04-28 21:07:40',
- 7089806,
- 'jason76/repo_8',
- 'repo_8',
- 'jason76',
- 'Despite couple economy sense should race.',
- 2519,
- 245,
- 7,
- 'JavaScript',
- array['Rust', 'Python'],
- true,
- '2025-01-27 13:26:14',
- 'failed',
- 3,
- '6ab21b990f1416846b362fdb26b90d80cbf249a9',
- '{"progress": 97}'::jsonb,
- '0a17991e-a576-4411-a0a1-1839e7457704'
- ),
- (
- '46d4cf41-4cd4-4043-a835-625a0bf349f2',
- '2025-03-01 23:43:53',
- '2025-03-27 14:23:05',
- 3109911,
- 'colleenbaker/repo_9',
- 'repo_9',
- 'colleenbaker',
- 'Often run bed.',
- 1051,
- 675,
- 60,
- 'Rust',
- array['JavaScript', 'Java'],
- false,
- '2025-02-27 07:18:51',
- 'processing',
- 18,
- '1f93145c08645e58c60f86341a4f5e572e111863',
- '{"progress": 96}'::jsonb,
- '21d53971-3367-49b5-acf6-bf756a5e6920'
+ '08946f22-0d74-4499-b40d-0f60218d5152', '2025-04-02 03:59:05', '2025-02-21 11:05:44', 6292423,
+ 'fjohnson/repo_2', 'repo_2', 'fjohnson', 'Perhaps however bag forget purpose move.',
+ 3286, 274, 8, array['JavaScript', 'HTML'], array['Rust', 'C++'], false, '2025-03-03 11:44:52',
+ 'pending', '5e3af4aafc18e025cea707fa7707a1d945e0ffef'
);
+-- Interactions
insert into
- code_chunks (
- id,
- repository_id,
- created_at,
- file_path,
- file_name,
- file_extension,
- chunk_index,
- content,
- chunk_type,
- language,
- lines_start,
- lines_end,
- code_metadata,
- weaviate_chunk_id
+ interactions (
+ id, created_at, user_id, repository_id, platform, platform_specific_id, channel_id,
+ thread_id, content, interaction_type, sentiment_score, intent_classification,
+ topics_discussed, metadata
)
values
(
- '095a5ff0-545a-48ff-83ad-2ea3566f5674',
- 'dda18eb8-8354-4897-8c7f-2c66afbc73e6',
- '2025-04-15 17:49:20',
- '/src/file_0.py',
- 'file_0.py',
- '.py',
- 0,
- 'Maybe evening clearly trial want whose far. Sound life away senior difficult put. Whose source hand so add Mr.',
- 'comment',
- 'C++',
- 92,
- 106,
- '{"length": 14}'::jsonb,
- 'f23e323d-0b9b-4934-a3c8-6d301dde7969'
- ),
- (
- 'b6bbdb5a-deb1-43c7-bf99-b9f88e4af1ed',
- 'ea879f36-d060-4f65-bf5d-9138a542f74a',
- '2025-01-08 05:25:15',
- '/src/file_1.py',
- 'file_1.py',
- '.py',
- 1,
- 'Break doctor Mr home he we recent. Industry score choice increase between majority impact.
-Real describe know. Talk between rate name within.',
- 'function',
- 'Go',
- 57,
- 76,
- '{"length": 19}'::jsonb,
- '00b9d4a3-9892-40ac-a689-33a9c9e48e8c'
- ),
- (
- '1f787967-316c-4232-b251-64bcf8e3251b',
- 'dda18eb8-8354-4897-8c7f-2c66afbc73e6',
- '2025-02-23 20:11:39',
- '/src/file_2.py',
- 'file_2.py',
- '.py',
- 2,
- 'Music sometimes body term. Address so draw food.
-Appear score moment second live. Message board mean war analysis situation.',
- 'module',
- 'C++',
- 29,
- 36,
- '{"length": 7}'::jsonb,
- '1963c26d-6e21-4b09-9afd-4015816bcb9f'
- ),
- (
- '233530b2-d89f-416d-a73c-40b4ebb33c50',
- 'f6b0bff9-074d-4062-86f5-0a853e521334',
- '2025-05-17 06:31:44',
- '/src/file_3.py',
- 'file_3.py',
- '.py',
- 3,
- 'Result Democrat later direction fund law indeed. Fine fine effort well.
-Before be it season. Speech news only no form business. Them wait institution trouble anything explain.',
- 'import',
- 'C++',
- 76,
- 88,
- '{"length": 12}'::jsonb,
- '8e867f3c-a487-4eab-accb-461a9d132363'
- ),
- (
- 'b3103899-d683-422a-9072-2ad26050d8f5',
- 'dda18eb8-8354-4897-8c7f-2c66afbc73e6',
- '2025-01-06 02:21:06',
- '/src/file_4.py',
- 'file_4.py',
- '.py',
- 4,
- 'Ahead event several TV go. Thank not husband center. Begin most heavy. Game have return since nothing be apply.',
- 'function',
- 'C++',
- 1,
- 8,
- '{"length": 7}'::jsonb,
- '0e0630cd-996d-4c50-bc04-a168652ffb49'
- ),
- (
- '28ea68b7-1f26-472c-b568-319e1d41732b',
- 'dda18eb8-8354-4897-8c7f-2c66afbc73e6',
- '2025-01-02 11:49:27',
- '/src/file_5.py',
- 'file_5.py',
- '.py',
- 5,
- 'War should share face build. Section compare herself region matter street south.
-Technology amount affect TV television office. Identify policy face if whom commercial way.',
- 'module',
- 'C++',
- 9,
- 15,
- '{"length": 6}'::jsonb,
- '2c6a6e9a-3280-47a1-8187-222b257d5e52'
- ),
- (
- '1cb8ccc0-db27-49c5-8dff-8d535d5a37d3',
- '0f08ecdb-53dd-4352-bb50-b1cfbf09da8b',
- '2025-04-27 23:22:57',
- '/src/file_6.py',
- 'file_6.py',
- '.py',
- 6,
- 'Concern significant management senior. Large under north play person ten physical character.
-Kind field ever argue medical financial later. Hard expert popular within.',
- 'module',
- 'C++',
- 66,
- 78,
- '{"length": 12}'::jsonb,
- 'fb7d9f1c-57eb-49b1-965e-59dde62d2d06'
- ),
- (
- '9edaae8a-3d6c-47c1-8777-ff0b0002b85a',
- 'd96ff094-7f9c-42d8-bb60-f83f19b07f77',
- '2025-05-19 16:57:06',
- '/src/file_7.py',
- 'file_7.py',
- '.py',
- 7,
- 'Position always remain yard model particular hair. Hold simple quickly appear piece.',
- 'import',
- 'Java',
- 28,
- 37,
- '{"length": 9}'::jsonb,
- '86c1b6cb-e996-40f7-af77-520eff4625af'
+ '7c59fe66-53b6-44b5-8ae1-ddc29b071097', '2025-03-10 12:14:30', '6afc59e3-18b7-4182-b42c-8210d1152b07',
+ 'f6b0bff9-074d-4062-86f5-0a853e521334', 'github', 'aa143cd82ff34de4',
+ 'f982f4e08603456a', '86abd4e7f4124360',
+ 'Skill medical after them analysis hit health. Ground attack drop. Billion old series card good full poor store.',
+ 'comment', -0.07, 'help_request', array['C++', 'TypeScript'], '{"info": "capital"}'::jsonb
),
(
- 'd1927881-d0e7-4df3-a97a-18521db08ff4',
- '46d4cf41-4cd4-4043-a835-625a0bf349f2',
- '2025-01-19 03:31:20',
- '/src/file_8.py',
- 'file_8.py',
- '.py',
- 8,
- 'Gun guy Congress degree way main difficult. Choice fast small medical. Strong this also from short capital heavy.
-Story side speak close. Analysis hair rest wide particular sell.',
- 'comment',
- 'Rust',
- 61,
- 73,
- '{"length": 12}'::jsonb,
- 'ef2ddcc4-8df6-41da-9f07-c1a5dfc620ce'
+ 'f0c80815-fde1-4644-94ca-cd8915f11e46', '2025-03-19 16:14:11', '6f990423-0d57-4c64-b191-17e53f39c799',
+ '0f08ecdb-53dd-4352-bb50-b1cfbf09da8b', 'github', '62fb26d7f4db4a07',
+ '7f072cb92fd340c0', 'ec9f9c545e0a42ab',
+ 'Song risk bad own state. Family bill foreign fast knowledge response coach. Goal amount thank good your ever.',
+ 'pr', 0.6, 'help_request', array['JavaScript', 'TypeScript'], '{"info": "already"}'::jsonb
),
(
- 'fdda052a-ca4f-40b5-ae99-a711e2161d85',
- '07921dba-1ae8-422b-9f29-9c908080aa1b',
- '2025-01-20 22:06:10',
- '/src/file_9.py',
- 'file_9.py',
- '.py',
- 9,
- 'Expect several evening town. Store begin treat stage. Us increase how hear history bank.
-Five between research. Social case expert stop receive catch.',
- 'function',
- 'JavaScript',
- 25,
- 33,
- '{"length": 8}'::jsonb,
- '9d642932-0066-453d-ade2-99a14a90cd0c'
+ 'ef139daa-fa4c-445a-8bf7-fdd725bdb82c', '2025-05-06 06:40:36', '2aefee92-c7da-4d6e-90c1-d6c3bb82c0e1',
+ '08946f22-0d74-4499-b40d-0f60218d5152', 'slack', '9136f1f8f31046dc',
+ 'add702c92747493c', '5f3c44dc5ef747b8',
+ 'Off morning huge power. Whether ago control military trial. Energy employee land you.',
+ 'message', -0.16, 'feature_request', array['Go', 'JavaScript'], '{"info": "security"}'::jsonb
);
+-- Conversation Context
insert into
- interactions (
- id,
- created_at,
- updated_at,
- user_id,
- repository_id,
- platform,
- platform_specific_id,
- channel_id,
- thread_id,
- content,
- interaction_type,
- sentiment_score,
- intent_classification,
- topics_discussed,
- metadata,
- weaviate_interaction_id
+ conversation_context (
+ id, user_id, platform, memory_thread_id, conversation_summary, key_topics,
+ total_interactions, session_start_time, session_end_time, created_at
)
values
(
- '7c59fe66-53b6-44b5-8ae1-ddc29b071097',
- '2025-03-10 12:14:30',
- '2025-02-16 17:06:38',
- '010d518f-362b-435b-a4de-148914bcbdb9',
- 'ea879f36-d060-4f65-bf5d-9138a542f74a',
- 'github',
- 'aa143cd8-2ff3-4de4-aaa4-2c9f92170475',
- 'f982f4e0-8603-456a-95ea-cbcfab1021ce',
- '86abd4e7-f412-4360-9153-6e995c508720',
- 'Skill medical after them analysis hit health. Ground attack drop. Billion old series card good full poor store.',
- 'comment',
- -0.07,
- 'help_request',
- array['C++', 'TypeScript'],
- '{"info": "capital"}'::jsonb,
- 'e3b56360-6fdc-4bad-9e36-8127cca1b45c'
- ),
- (
- 'f0c80815-fde1-4644-94ca-cd8915f11e46',
- '2025-03-19 16:14:11',
- '2025-05-25 08:03:53',
- '6f990423-0d57-4c64-b191-17e53f39c799',
- 'f6b0bff9-074d-4062-86f5-0a853e521334',
- 'github',
- '62fb26d7-f4db-4a07-a506-f6707092947d',
- '7f072cb9-2fd3-40c0-b945-f2fd56cb1ab0',
- 'ec9f9c54-5e0a-42ab-bf5d-b163b12b6680',
- 'Song risk bad own state. Family bill foreign fast knowledge response coach. Goal amount thank good your ever.',
- 'pr',
- 0.6,
- 'help_request',
- array['JavaScript', 'TypeScript'],
- '{"info": "already"}'::jsonb,
- 'c74cc890-3c6a-4174-9136-34a520509c62'
- ),
- (
- 'ef139daa-fa4c-445a-8bf7-fdd725bdb82c',
- '2025-05-06 06:40:36',
- '2025-03-13 03:12:51',
- '9b56cac8-504a-4dd8-b7ba-0a5bfce7abf7',
- '4882ce56-489d-4abc-bc29-bf3ad5c48930',
- 'github',
- '9136f1f8-f310-46dc-a202-bee65cb5e69c',
- 'add702c9-2747-493c-9ae7-7eab084a6780',
- '5f3c44dc-5ef7-47b8-b2e6-195f732e2016',
- 'Off morning huge power. Whether ago control military trial. Energy employee land you.',
- 'issue',
- -0.16,
- 'feature_request',
- array['Go', 'JavaScript'],
- '{"info": "security"}'::jsonb,
- '2c913a7c-a340-4f08-b341-91b8ed6522b4'
- ),
- (
- 'd5c02f3d-6d9a-49b9-8d71-33ba08c610a2',
- '2025-03-12 04:40:32',
- '2025-04-09 18:58:25',
- '6f990423-0d57-4c64-b191-17e53f39c799',
- 'd96ff094-7f9c-42d8-bb60-f83f19b07f77',
- 'slack',
- '3373730e-fc31-4597-9f11-9c0f3967e60a',
- 'ca55e38b-3c9a-4e10-a38d-6f44cac4d0eb',
- '8dd4595b-5c63-46f3-86ba-8cd37e7838c9',
- 'Level work candidate this assume huge. Moment shoulder statement available win politics last. General there sister policy consider whom item.',
- 'message',
- -0.9,
- 'help_request',
- array['Python', 'JavaScript'],
- '{"info": "prove"}'::jsonb,
- '200b2903-4642-4c45-8d03-f17af4d375c1'
- ),
- (
- '5696eff8-bba4-41a4-953f-f70eece14b2d',
- '2025-05-02 08:48:55',
- '2025-02-28 19:15:53',
- '2aefee92-c7da-4d6e-90c1-d6c3bb82c0e1',
- '07921dba-1ae8-422b-9f29-9c908080aa1b',
- 'github',
- '80a8a23d-17ea-4c83-8892-042f9d4b2bf9',
- 'f10d27c8-9780-4215-ab2b-b8e9a417c093',
- 'a8a2b7ad-2bd3-4dcd-a779-468594a53fde',
- 'Wish candidate have no five letter. Last cell anything war ten. Eat tend civil force officer fine.',
- 'comment',
- -0.57,
- 'general_discussion',
- array['Python', 'JavaScript'],
- '{"info": "ready"}'::jsonb,
- '896490ab-4926-4e5f-b878-6140ac2a4f71'
- ),
- (
- '2ea1d897-a515-40cd-a92a-01eada9542d8',
- '2025-01-02 14:06:34',
- '2025-01-21 20:58:21',
- '010d518f-362b-435b-a4de-148914bcbdb9',
- 'f6b0bff9-074d-4062-86f5-0a853e521334',
- 'github',
- '43b47ee5-e1e8-4e7e-a249-8f666e51484d',
- '673ba8bd-c38c-4dec-9da3-9a73ba3df7ff',
- '0d18ab95-668c-4477-8b95-017c5dae1201',
- 'Foreign party class wrong. Order medical meeting majority none. Staff happy purpose woman on someone rise.',
- 'pr',
- 0.85,
- 'general_discussion',
- array['Java', 'Go'],
- '{"info": "market"}'::jsonb,
- 'ba370623-bc5f-44dd-992f-3c1edf70fb2a'
- ),
- (
- 'dc1ad7fb-edca-4c34-b07d-7b51f7a92974',
- '2025-01-13 02:15:06',
- '2025-05-18 01:09:03',
- '7771182e-ed52-4f4e-a376-0750b9854324',
- 'd96ff094-7f9c-42d8-bb60-f83f19b07f77',
- 'discord',
- '8f964685-3514-4890-84c8-6c4623595fa4',
- '7a768555-a987-4218-bf84-faef5336723b',
- 'f4e95734-5052-4700-a077-96a38685abaa',
- 'Treatment garden great sign return poor really. Machine whatever everything fear walk word side relate.',
- 'issue',
- -0.41,
- 'help_request',
- array['Rust', 'C++'],
- '{"info": "defense"}'::jsonb,
- 'eedaa802-4568-4426-89e0-3e22d3f4a49b'
- ),
- (
- '30634a69-7c7b-4f11-8ec5-b83299015938',
- '2025-04-20 03:11:37',
- '2025-05-16 04:23:36',
- '7771182e-ed52-4f4e-a376-0750b9854324',
- 'f6b0bff9-074d-4062-86f5-0a853e521334',
- 'slack',
- '9d9d028e-1bf6-45f6-a324-52114588fc1b',
- '3e11bafe-3d41-46fe-963a-8617bdab07e7',
- '87f255d6-e7ba-46ac-ab7a-3d1c0cfef683',
- 'Appear including response beyond side. Who within citizen.',
- 'pr',
- -0.89,
- 'general_discussion',
- array['Rust', 'TypeScript'],
- '{"info": "cultural"}'::jsonb,
- 'df4e713e-f64e-4dfc-bfbe-ac7aefc59738'
+ 'c1b2c3d4-e5f6-a7b8-c9d0-e1f2a3b4c5d6', '6afc59e3-18b7-4182-b42c-8210d1152b07',
+ 'discord', '112233445566778899',
+ 'The user asked about getting started with the API and had questions about authentication. They were provided with a link to the documentation.',
+ array['onboarding', 'api_keys', 'authentication'], 8, '2025-06-20 10:00:00',
+ '2025-06-20 10:25:00', '2025-06-20 10:25:00'
),
(
- '87916dfb-a7ce-4315-80f3-a72be814f08c',
- '2025-02-26 05:00:49',
- '2025-01-08 06:36:18',
- '2aefee92-c7da-4d6e-90c1-d6c3bb82c0e1',
- 'f6b0bff9-074d-4062-86f5-0a853e521334',
- 'slack',
- '9a4ffc0c-9165-42ed-8c63-6e95025f5543',
- '55c551fc-fba5-4cc8-adaf-37661b780ede',
- 'a42d0cd7-fd35-4f6a-b450-388748d90846',
- 'According himself land environment form. Reveal activity president realize artist brother fill if. Type thousand show real police wait happen.',
- 'message',
- 0.7,
- 'help_request',
- array['Rust', 'Python'],
- '{"info": "store"}'::jsonb,
- '87365a84-725e-434d-8687-9aa914f573d0'
+ 'd2c3d4e5-f6a7-b8c9-d0e1-f2a3b4c5d6e7', '6f990423-0d57-4c64-b191-17e53f39c799',
+ 'slack', '998877665544332211',
+ 'User reported a potential bug related to the repository indexing service. They provided logs and a repository URL. The issue was acknowledged and a ticket was created.',
+ array['bug_report', 'indexing', 'repositories'], 12, '2025-06-21 09:00:00',
+ '2025-06-21 09:45:00', '2025-06-21 09:45:00'
),
(
- 'c29c38dc-10be-4da2-81b7-6f82b746a359',
- '2025-05-17 15:47:16',
- '2025-03-12 06:02:22',
- '9b56cac8-504a-4dd8-b7ba-0a5bfce7abf7',
- '07921dba-1ae8-422b-9f29-9c908080aa1b',
- 'discord',
- '1cb9b73a-906d-4c8c-aad0-8c8913fe8a29',
- 'e723ada3-8c32-4db2-942a-895e0fcf601f',
- '89628f6e-929c-43b3-b3c0-8bf18167999f',
- 'Foreign minute break day. Major together knowledge argue car indeed nor next.
-How staff second. Authority interest red must art thus worry line.',
- 'reaction',
- -0.51,
- 'help_request',
- array['Rust', 'Python'],
- '{"info": "cell"}'::jsonb,
- '1e5ebe54-5907-4299-9c3b-bdd8a74e02a9'
+ 'e3d4e5f6-a7b8-c9d0-e1f2-a3b4c5d6e7f8', '2aefee92-c7da-4d6e-90c1-d6c3bb82c0e1',
+ 'discord', '123451234512345123',
+ 'A general discussion about the future of Rust and its use in web development. The user shared an article and asked for opinions.',
+ array['Rust', 'web_development', 'discussion'], 5, '2025-06-19 14:30:00',
+ '2025-06-19 15:00:00', '2025-06-19 15:00:00'
);
diff --git a/backend/bots/discord/discord_bot.py b/backend/bots/discord/discord_bot.py
index 0dce79c2..59ebab0c 100644
--- a/backend/bots/discord/discord_bot.py
+++ b/backend/bots/discord/discord_bot.py
@@ -15,6 +15,7 @@ def __init__(self, queue_manager: AsyncQueueManager, **kwargs):
intents.message_content = True
intents.guilds = True
intents.members = True
+ intents.dm_messages = True
super().__init__(
command_prefix="!",
diff --git a/backend/bots/discord/discord_cogs.py b/backend/bots/discord/discord_cogs.py
index 2159f86a..0c78549b 100644
--- a/backend/bots/discord/discord_cogs.py
+++ b/backend/bots/discord/discord_cogs.py
@@ -1,10 +1,16 @@
import discord
-from discord.ext import commands
+from discord.ext import commands, tasks
import logging
from app.core.orchestration.queue_manager import AsyncQueueManager, QueuePriority
from app.db.supabase.auth import login_with_github
+from app.db.supabase.users_service import (
+ get_or_create_user_by_discord,
+ create_verification_session,
+ cleanup_expired_tokens
+)
from bots.discord.discord_bot import DiscordBot
from bots.discord.discord_views import OAuthView
+from app.core.config import settings
logger = logging.getLogger(__name__)
@@ -12,6 +18,24 @@ class DevRelCommands(commands.Cog):
def __init__(self, bot: DiscordBot, queue_manager: AsyncQueueManager):
self.bot = bot
self.queue = queue_manager
+ self.cleanup_expired_tokens.start()
+
+ def cog_unload(self):
+ """Clean up when cog is unloaded"""
+ self.cleanup_expired_tokens.cancel()
+
+ @tasks.loop(minutes=5)
+ async def cleanup_expired_tokens(self):
+ """Periodic cleanup of expired verification tokens"""
+ try:
+ await cleanup_expired_tokens()
+ except Exception as e:
+ logger.error(f"Error during token cleanup: {e}")
+
+ @cleanup_expired_tokens.before_loop
+ async def before_cleanup(self):
+ """Wait until the bot is ready before starting cleanup"""
+ await self.bot.wait_until_ready()
@commands.command(name="reset")
async def reset_thread(self, ctx: commands.Context):
@@ -32,7 +56,8 @@ async def help_devrel(self, ctx: commands.Context):
"""Show DevRel assistant help."""
embed = discord.Embed(
title="DevRel Assistant Help",
- description="I can help you with Devr.AI related questions!"
+ description="I can help you with Devr.AI related questions!",
+ color=discord.Color.blue()
)
embed.add_field(
name="Commands",
@@ -40,34 +65,204 @@ async def help_devrel(self, ctx: commands.Context):
"• `!reset` – Reset your DevRel thread and memory\n"
"• `!help_devrel` – Show this help message\n"
"• `!verify_github` – Link your GitHub account\n"
+ "• `!verification_status` – Check your verification status\n"
+ ),
+ inline=False
+ )
+ embed.add_field(
+ name="Features",
+ value=(
+ "• Technical support and troubleshooting\n"
+ "• Onboarding assistance\n"
+ "• Web search capabilities\n"
+ "• Community FAQ answers\n"
),
inline=False
)
await ctx.send(embed=embed)
+ @commands.command(name="verification_status")
+ async def verification_status(self, ctx: commands.Context):
+ """Check your GitHub verification status."""
+ try:
+ user_profile = await get_or_create_user_by_discord(
+ discord_id=str(ctx.author.id),
+ display_name=ctx.author.display_name,
+ discord_username=ctx.author.name,
+ avatar_url=str(ctx.author.avatar.url) if ctx.author.avatar else None,
+ )
+
+ if user_profile.is_verified and user_profile.github_id:
+ embed = discord.Embed(
+ title="✅ Verification Status",
+ color=discord.Color.green()
+ )
+ embed.add_field(
+ name="GitHub Account",
+ value=f"`{user_profile.github_username}`",
+ inline=True
+ )
+ embed.add_field(
+ name="Verified At",
+ value=f"" if user_profile.verified_at else "Unknown",
+ inline=True
+ )
+ embed.add_field(
+ name="Status",
+ value="✅ Verified",
+ inline=True
+ )
+ else:
+ embed = discord.Embed(
+ title="❌ Verification Status",
+ description="Your GitHub account is not linked.",
+ color=discord.Color.red()
+ )
+ embed.add_field(
+ name="Next Steps",
+ value="Use `!verify_github` to link your GitHub account.",
+ inline=False
+ )
+
+ await ctx.reply(embed=embed)
+
+ except Exception as e:
+ logger.error(f"Error checking verification status for user {ctx.author.id}: {e}")
+ await ctx.reply("❌ Error checking verification status. Please try again.")
+
@commands.command(name="verify_github")
async def verify_github(self, ctx: commands.Context):
- """Get GitHub verification link."""
+ """Initiates the GitHub account linking and verification process."""
try:
logger.info(f"User {ctx.author.name}({ctx.author.id}) has requested for GitHub verification")
- oauth_result = await login_with_github()
- logger.info(f"OAuth result: {oauth_result}")
- oauth_url = oauth_result["url"]
+ user_profile = await get_or_create_user_by_discord(
+ discord_id=str(ctx.author.id),
+ display_name=ctx.author.display_name,
+ discord_username=ctx.author.name,
+ avatar_url=str(ctx.author.avatar.url) if ctx.author.avatar else None,
+ )
+
+ if user_profile.is_verified and user_profile.github_id:
+ embed = discord.Embed(
+ title="✅ Already Verified",
+ description=f"Your GitHub account `{user_profile.github_username}` is already linked!",
+ color=discord.Color.green()
+ )
+ embed.add_field(
+ name="Verified At",
+ value=f"" if user_profile.verified_at else "Unknown",
+ inline=True
+ )
+ await ctx.reply(embed=embed)
+ return
+
+ if user_profile.verification_token:
+ embed = discord.Embed(
+ title="⏳ Verification Pending",
+ description="You already have a verification in progress.",
+ color=discord.Color.orange()
+ )
+ embed.add_field(
+ name="What to do",
+ value="Please complete the existing verification or wait for it to expire (5 minutes).",
+ inline=False
+ )
+ await ctx.reply(embed=embed)
+ return
+
+ session_id = await create_verification_session(str(ctx.author.id))
+ if not session_id:
+ raise Exception("Failed to create verification session.")
+
+ logger.info(f"Created verification session for user {ctx.author.id}: {session_id[:8]}...")
+
+ if not settings.backend_url:
+ raise Exception("Backend URL not configured. Please set BACKEND_URL environment variable.")
+
+ callback_url = f"{settings.backend_url}/v1/auth/callback?session={session_id}"
+ logger.info(f"Using callback URL: {callback_url}")
+
+ auth_url_data = await login_with_github(redirect_to=callback_url)
+ auth_url = auth_url_data.get("url")
+
+ if not auth_url:
+ raise Exception("Failed to generate OAuth URL.")
+
+ logger.info(f"Generated OAuth URL for user {ctx.author.id}")
embed = discord.Embed(
- title="🔗 Verify GitHub Account",
- description="Click the button below to link your GitHub account \nAfter authorization, you'll be redirected to your GitHub profile.",
+ title="🔗 Link Your GitHub Account",
+ description=(
+ "Click the button below to securely link your GitHub account.\n\n"
+ "**What happens next:**\n"
+ "1. You'll be redirected to GitHub\n"
+ "2. Authorize the Devr.AI application\n"
+ "3. You'll be redirected back with confirmation\n"
+ "4. Your accounts will be linked!"
+ ),
+ color=discord.Color.blue()
+ )
+ embed.add_field(
+ name="⏰ Expiry Time",
+ value="This verification link expires in **5 minutes** for security.",
+ inline=False
)
embed.add_field(
- name="ℹ️ Note:",
- value="The link expires in 5 minutes for security purposes.",
+ name="🔒 Security Notice",
+ value="We only request access to your public GitHub profile. No private data is accessed.",
inline=False
)
- view = OAuthView(oauth_url, "GitHub")
- await ctx.send(embed=embed, view=view)
+ view = OAuthView(auth_url, "GitHub")
+
+ try:
+ await ctx.author.send(embed=embed, view=view)
+
+ confirmation_embed = discord.Embed(
+ title="📨 Check Your DMs",
+ description="I've sent you a private message with the GitHub verification link.",
+ color=discord.Color.green()
+ )
+ confirmation_embed.add_field(
+ name="⏰ Time Limit",
+ value="Please complete the verification within 5 minutes.",
+ inline=False
+ )
+ await ctx.reply(embed=confirmation_embed)
+
+ except discord.Forbidden:
+ embed.add_field(
+ name="🔒 Privacy Notice",
+ value="**Please enable DMs for a more secure experience.**",
+ inline=False
+ )
+ await ctx.reply(embed=embed, view=view)
+
+ except discord.Forbidden:
+ error_embed = discord.Embed(
+ title="❌ DM Error",
+ description=(
+ "I couldn't send you a DM. Please:\n"
+ "1. Enable DMs from server members in your privacy settings\n"
+ "2. Try the command again"
+ ),
+ color=discord.Color.red()
+ )
+ await ctx.reply(embed=error_embed)
except Exception as e:
- logger.error(f"Error in verify_github: {str(e)}")
- await ctx.send("Error generating GitHub verification link. Please try again.")
+ logger.error(f"Error in !verify_github for user {ctx.author.id}: {e}", exc_info=True)
+
+ error_embed = discord.Embed(
+ title="❌ Verification Error",
+ description="An error occurred during verification setup. Please try again or contact support.",
+ color=discord.Color.red()
+ )
+ if "Backend URL not configured" in str(e):
+ error_embed.add_field(
+ name="Configuration Issue",
+ value="The bot is not properly configured. Please contact an administrator.",
+ inline=False
+ )
+ await ctx.reply(embed=error_embed)
diff --git a/backend/main.py b/backend/main.py
index 9d3dfc29..190e99bd 100644
--- a/backend/main.py
+++ b/backend/main.py
@@ -1,118 +1,124 @@
import asyncio
import logging
-import os
-import signal
import sys
+from contextlib import asynccontextmanager
+
+import uvicorn
+from fastapi import FastAPI, Response
+
+from app.api.v1.auth import router as auth_router
from app.core.config import settings
-from app.core.orchestration.queue_manager import AsyncQueueManager
from app.core.orchestration.agent_coordinator import AgentCoordinator
+from app.core.orchestration.queue_manager import AsyncQueueManager
+# from app.db.weaviate.weaviate_client import get_client
from bots.discord.discord_bot import DiscordBot
from bots.discord.discord_cogs import DevRelCommands
-from app.db.weaviate.weaviate_client import get_client
-# Configure logging
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
)
logger = logging.getLogger(__name__)
+
class DevRAIApplication:
- """Main application class"""
+ """
+ Manages the application's core components and background tasks.
+ """
def __init__(self):
- try:
- self.weaviate_client = get_client()
- logger.info(f"Weaviate client initialized: {self.weaviate_client.is_ready()}")
- except Exception as e:
- logger.error(f"Error initializing Weaviate client: {str(e)}")
- self.weaviate_client = None
+ """Initializes all services required by the application."""
+ # try:
+ # self.weaviate_client = get_client()
+ # logger.info(f"Weaviate client initialized: {self.weaviate_client.is_ready()}")
+ # except Exception as e:
+ # logger.error(f"Fatal: Error initializing Weaviate client: {e}", exc_info=True)
+ # self.weaviate_client = None
+ # sys.exit(1)
self.queue_manager = AsyncQueueManager()
self.agent_coordinator = AgentCoordinator(self.queue_manager)
self.discord_bot = DiscordBot(self.queue_manager)
self.discord_bot.add_cog(DevRelCommands(self.discord_bot, self.queue_manager))
- self.running = False
- async def start(self):
- """Start the application"""
+ async def start_background_tasks(self):
+ """Starts the Discord bot and queue workers in the background."""
try:
- logger.info("Starting Devr.AI Application...")
-
- # Start queue manager
+ logger.info("Starting background tasks (Discord Bot & Queue Manager)...")
await self.queue_manager.start(num_workers=3)
-
- # Start Discord bot
- discord_task = asyncio.create_task(
+ asyncio.create_task(
self.discord_bot.start(settings.discord_bot_token)
)
+ logger.info("Background tasks started successfully!")
+ except Exception as e:
+ logger.error(f"Error during background task startup: {e}", exc_info=True)
+ await self.stop_background_tasks()
- self.running = True
- logger.info("Devr.AI Application started successfully!")
-
- # Wait for the Discord bot
- await discord_task
+ async def stop_background_tasks(self):
+ """Stops all background tasks and connections gracefully."""
+ logger.info("Stopping background tasks and closing connections...")
+ try:
+ if not self.discord_bot.is_closed():
+ await self.discord_bot.close()
+ logger.info("Discord bot has been closed.")
except Exception as e:
- logger.error(f"Error starting application: {str(e)}")
- await self.stop()
-
- async def stop(self):
- """Stop the application"""
- logger.info("Stopping Devr.AI Application...")
+ logger.error(f"Error closing Discord bot: {e}", exc_info=True)
- self.running = False
- # Close Weaviate client
try:
- if hasattr(self, 'weaviate_client') and self.weaviate_client is not None:
- self.weaviate_client.close()
- logger.info("Weaviate client closed")
+ await self.queue_manager.stop()
+ logger.info("Queue manager has been stopped.")
except Exception as e:
- logger.error(f"Error closing Weaviate client: {str(e)}")
+ logger.error(f"Error stopping queue manager: {e}", exc_info=True)
- # Stop Discord bot
try:
- if not self.discord_bot.is_closed():
- await self.discord_bot.close()
+ if hasattr(self, 'weaviate_client') and self.weaviate_client is not None:
+ self.weaviate_client.close()
+ logger.info("Weaviate client connection closed.")
except Exception as e:
- logger.error(f"Error closing Discord bot: {str(e)}")
- # Stop queue manager
- await self.queue_manager.stop()
+ logger.error(f"Error closing Weaviate client: {e}", exc_info=True)
- logger.info("Devr.AI Application stopped")
+ logger.info("All background tasks and connections stopped.")
+# --- FASTAPI LIFESPAN AND APP INITIALIZATION ---
# Global application instance
-app = DevRAIApplication()
+app_instance = DevRAIApplication()
+
+
+@asynccontextmanager
+async def lifespan(app: FastAPI):
+ """
+ Lifespan manager for the FastAPI application. Handles startup and shutdown events.
+ """
+ await app_instance.start_background_tasks()
+ yield
+ await app_instance.stop_background_tasks()
+
-async def main():
- """Main entry point"""
- # Setup signal handlers for graceful shutdown
- loop = asyncio.get_running_loop()
+api = FastAPI(title="Devr.AI API", version="1.0", lifespan=lifespan)
- def signal_handler(signum):
- logger.info(f"Received signal {signum}")
- loop.create_task(app.stop())
+@api.get("/favicon.ico")
+async def favicon():
+ """Return empty favicon to prevent 404 logs"""
+ return Response(status_code=204)
- for sig in (signal.SIGINT, signal.SIGTERM):
- loop.add_signal_handler(sig, lambda s=sig: signal_handler(s), sig)
+api.include_router(auth_router, prefix="/v1/auth", tags=["Authentication"])
- try:
- await app.start()
- except KeyboardInterrupt:
- logger.info("Keyboard interrupt received")
- except Exception as e:
- logger.error(f"Application error: {str(e)}")
- finally:
- await app.stop()
if __name__ == "__main__":
- # Check required environment variables
- required_vars = ["DISCORD_BOT_TOKEN", "GEMINI_API_KEY", "TAVILY_API_KEY"]
- missing_vars = [var for var in required_vars if not os.getenv(var)]
+ required_vars = [
+ "DISCORD_BOT_TOKEN", "SUPABASE_URL", "SUPABASE_KEY",
+ "BACKEND_URL", "GEMINI_API_KEY", "TAVILY_API_KEY"
+ ]
+ missing_vars = [var for var in required_vars if not getattr(settings, var.lower(), None)]
if missing_vars:
- logger.error(f"Missing required environment variables: {missing_vars}")
+ logger.error(f"Missing required environment variables: {', '.join(missing_vars)}")
sys.exit(1)
- # Run the application
- asyncio.run(main())
+ uvicorn.run(
+ "__main__:api",
+ host="0.0.0.0",
+ port=8000,
+ reload=True
+ )