Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
56 commits
Select commit Hold shift + click to select a range
80baf01
feat: revamped ui
Saahi30 Jul 19, 2025
d3fe0ba
feat: added new tables
Saahi30 Jul 19, 2025
00c4703
feat: added fastapi models
Saahi30 Jul 19, 2025
0432220
feat: added pydantic schemas
Saahi30 Jul 19, 2025
ab357c6
feat: add api routes and minor security improvements
Saahi30 Jul 20, 2025
20e6bdd
feat: add smart features to searchbar
Saahi30 Jul 20, 2025
425c9ab
feat: added more endpoints for refinement
Saahi30 Jul 20, 2025
8c0f605
fix: add missing SponsorshipCreate import
Saahi30 Jul 20, 2025
635f252
fix: resolve CSS import order and TypeScript errors
Saahi30 Jul 20, 2025
d1b8b33
feat: connect frontend to backend with API integration and AI search
Saahi30 Jul 20, 2025
6f528ef
docs: add frontend-backend integration documentation
Saahi30 Jul 20, 2025
e62ba6b
fix: separate AI API service and fix 404 error for AI queries
Saahi30 Jul 20, 2025
d79c411
feat: improve AI response display with better formatting
Saahi30 Jul 20, 2025
34ede84
fix: remove the metric cards from brand dashboard
Saahi30 Jul 20, 2025
e36a966
fix: changed primary call model to kimi-k2
Saahi30 Jul 21, 2025
6658f61
fix: llm parsing inconsistency fix
Saahi30 Jul 27, 2025
6420e00
feat: add redis cloud connect
Saahi30 Jul 27, 2025
4a4382e
feat: add hybrid orchestration and session management
Saahi30 Jul 27, 2025
1cdee7c
fix: fix session management and response types
Saahi30 Jul 27, 2025
ce9e4d1
feat: chat component
Saahi30 Jul 27, 2025
7cc96af
feat: backend assistant integration
Saahi30 Jul 27, 2025
8c7cfad
feat: add dashboard route
Saahi30 Jul 29, 2025
0d833db
feat: add the brand overview dashboard
Saahi30 Jul 30, 2025
da4415b
feat: update dashboard overview for design
Saahi30 Aug 1, 2025
a11acd2
feat: Added contracts schemas
Saahi30 Aug 1, 2025
65fba45
feat: main Contracts dashboard
Saahi30 Aug 1, 2025
21e7896
feat: comprehensive contract details modal
Saahi30 Aug 1, 2025
cb8950a
built the remaining features
Saahi30 Aug 1, 2025
fd36d56
feat: add database queries for contracts
Saahi30 Aug 2, 2025
9823585
feat: create api system
Saahi30 Aug 2, 2025
80970cc
feat: register all endpoints and routing
Saahi30 Aug 2, 2025
3dc6902
fix: prevent startup failures by proper database connection
Saahi30 Aug 2, 2025
c58bb4f
fix: improved error handling
Saahi30 Aug 2, 2025
9cdc56a
feat: added social icons for use throughout the program
Saahi30 Aug 2, 2025
79ca174
feat: back to dashboard button
Saahi30 Aug 2, 2025
b5f3140
feat: core api build
Saahi30 Aug 2, 2025
8da532b
feat: management modals
Saahi30 Aug 2, 2025
738625f
feat: added genAI for smart contracts
Saahi30 Aug 2, 2025
d9dbdcb
feat: developed api routes for contracts
Saahi30 Aug 2, 2025
3cf4212
fix: resolve jsonb type casting in SQL
Saahi30 Aug 6, 2025
080e327
feat: add jurisdiction selection to contract generator
Saahi30 Aug 6, 2025
948eceb
feat: add jurisdiction support to contract generation API
Saahi30 Aug 6, 2025
02315f6
feat: add jurisdiction fields to edit modal
Saahi30 Aug 6, 2025
c101c62
fix: enhance contract update error handling
Saahi30 Aug 6, 2025
4527c1f
fix: improve API error handling for 5xx errors
Saahi30 Aug 6, 2025
af32739
feat: add pricing service and API routes
Saahi30 Aug 6, 2025
1d4f3b7
feat: add governing laws selector component
Saahi30 Aug 6, 2025
dd05553
feat: register pricing routes and update post API
Saahi30 Aug 6, 2025
4e28a7c
feat: integrate jurisdiction features in contract pages
Saahi30 Aug 6, 2025
efb958a
feat: update contract routes and templates with new generation logic
Saahi30 Aug 7, 2025
df6e2f2
feat: implement pricing service and test smart contract features
Saahi30 Aug 7, 2025
aed909c
feat: update contract modals with new UI components
Saahi30 Aug 7, 2025
1b826fc
feat: add backend exports functionality
Saahi30 Aug 7, 2025
167affd
chore(backend):services for data collection & ingestion
Saahi30 Oct 7, 2025
827aad6
feat(backend): ROI service & analytics endpoint
Saahi30 Oct 7, 2025
996562c
feat(frontend):visualize ROI analytics
Saahi30 Oct 7, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 7 additions & 2 deletions Backend/.env-example
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,13 @@ password=[YOUR-PASSWORD]
host=
port=5432
dbname=postgres
GROQ_API_KEY=
GROQ_API_KEY=your_groq_api_key_here
SUPABASE_URL=
SUPABASE_KEY=
GEMINI_API_KEY=
YOUTUBE_API_KEY=
YOUTUBE_API_KEY=

# Redis Cloud configuration
REDIS_HOST=your-redis-cloud-host
REDIS_PORT=12345
REDIS_PASSWORD=your-redis-cloud-password
12 changes: 12 additions & 0 deletions Backend/app/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,12 @@
from .routes.post import router as post_router
from .routes.chat import router as chat_router
from .routes.match import router as match_router
from .routes.brand_dashboard import router as brand_dashboard_router
from .routes.ai_query import router as ai_query_router
from .routes.contracts import router as contracts_router
from .routes.contracts_ai import router as contracts_ai_router
from .routes.contracts_generation import router as contracts_generation_router
from .routes.pricing import router as pricing_router
from sqlalchemy.exc import SQLAlchemyError
import logging
import os
Expand Down Expand Up @@ -54,6 +60,12 @@ async def lifespan(app: FastAPI):
app.include_router(post_router)
app.include_router(chat_router)
app.include_router(match_router)
app.include_router(brand_dashboard_router)
app.include_router(ai_query_router)
app.include_router(contracts_router)
app.include_router(contracts_ai_router)
app.include_router(contracts_generation_router)
app.include_router(pricing_router)
app.include_router(ai.router)
app.include_router(ai.youtube_router)

Expand Down
81 changes: 80 additions & 1 deletion Backend/app/models/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
TIMESTAMP,
)
from sqlalchemy.orm import relationship
from datetime import datetime
from datetime import datetime, timezone
from app.db.db import Base
import uuid

Expand Down Expand Up @@ -160,3 +160,82 @@ class SponsorshipPayment(Base):
brand = relationship(
"User", foreign_keys=[brand_id], back_populates="brand_payments"
)


# ============================================================================
# BRAND DASHBOARD MODELS
# ============================================================================

# Brand Profile Table (Extended brand information)
class BrandProfile(Base):
__tablename__ = "brand_profiles"

id = Column(String, primary_key=True, default=generate_uuid)
user_id = Column(String, ForeignKey("users.id"), nullable=False)
company_name = Column(String, nullable=True)
website = Column(String, nullable=True)
industry = Column(String, nullable=True)
contact_person = Column(String, nullable=True)
contact_email = Column(String, nullable=True)
created_at = Column(
DateTime(timezone=True), default=lambda: datetime.now(timezone.utc)
)

# Relationships
user = relationship("User", backref="brand_profile")


# Campaign Metrics Table (Performance tracking)
class CampaignMetrics(Base):
__tablename__ = "campaign_metrics"

id = Column(String, primary_key=True, default=generate_uuid)
campaign_id = Column(String, ForeignKey("sponsorships.id"), nullable=False)
impressions = Column(Integer, nullable=True)
clicks = Column(Integer, nullable=True)
conversions = Column(Integer, nullable=True)
revenue = Column(DECIMAL(10, 2), nullable=True)
engagement_rate = Column(Float, nullable=True)
recorded_at = Column(
DateTime(timezone=True), default=lambda: datetime.now(timezone.utc)
)

# Relationships
campaign = relationship("Sponsorship", backref="metrics")


# Contracts Table (Contract management)
class Contract(Base):
__tablename__ = "contracts"

id = Column(String, primary_key=True, default=generate_uuid)
sponsorship_id = Column(String, ForeignKey("sponsorships.id"), nullable=False)
creator_id = Column(String, ForeignKey("users.id"), nullable=False)
brand_id = Column(String, ForeignKey("users.id"), nullable=False)
contract_url = Column(String, nullable=True)
status = Column(String, default="draft") # draft, signed, completed, cancelled
created_at = Column(
DateTime(timezone=True), default=lambda: datetime.now(timezone.utc)
)

# Relationships
sponsorship = relationship("Sponsorship", backref="contracts")
creator = relationship("User", foreign_keys=[creator_id], backref="creator_contracts")
brand = relationship("User", foreign_keys=[brand_id], backref="brand_contracts")


# Creator Matches Table (AI-powered matching)
class CreatorMatch(Base):
__tablename__ = "creator_matches"

id = Column(String, primary_key=True, default=generate_uuid)
brand_id = Column(String, ForeignKey("users.id"), nullable=False)
creator_id = Column(String, ForeignKey("users.id"), nullable=False)
match_score = Column(Float, nullable=True)
matched_at = Column(
DateTime(timezone=True), default=lambda: datetime.now(timezone.utc)
)

# Relationships
brand = relationship("User", foreign_keys=[brand_id], backref="creator_matches")
creator = relationship("User", foreign_keys=[creator_id], backref="brand_matches")
244 changes: 244 additions & 0 deletions Backend/app/routes/ai_query.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,244 @@
from fastapi import APIRouter, HTTPException, Query, Depends, Request
from typing import Dict, Any, Optional
from pydantic import BaseModel
import logging
from ..services.ai_router import ai_router
from ..services.redis_client import get_session_state, save_session_state
import uuid

# Setup logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)

# Define Router
router = APIRouter(prefix="/api/ai", tags=["AI Query"])

# Pydantic models for request/response
class AIQueryRequest(BaseModel):
query: str
brand_id: Optional[str] = None
context: Optional[Dict[str, Any]] = None

class AIQueryResponse(BaseModel):
intent: str
route: Optional[str] = None
parameters: Dict[str, Any] = {}
follow_up_needed: bool = False
follow_up_question: Optional[str] = None
explanation: str
original_query: str
timestamp: str

Comment on lines +22 to +31
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | πŸ”΄ Critical

Response model is dropping session_id/result/error. Include them in schema and avoid mutable default.

FastAPI filters unknown fields; your extras won’t reach clients. Also avoid {} as default.

-from pydantic import BaseModel
+from pydantic import BaseModel, Field
@@
 class AIQueryResponse(BaseModel):
     intent: str
     route: Optional[str] = None
-    parameters: Dict[str, Any] = {}
+    parameters: Dict[str, Any] = Field(default_factory=dict)
     follow_up_needed: bool = False
     follow_up_question: Optional[str] = None
     explanation: str
     original_query: str
     timestamp: str
+    session_id: str
+    result: Optional[Any] = None
+    error: Optional[str] = None
πŸ€– Prompt for AI Agents
In Backend/app/routes/ai_query.py around lines 22 to 31, the AIQueryResponse
Pydantic model is missing session_id, result, and error fields and uses a
mutable default for parameters; update the schema to include session_id:
Optional[str], result: Optional[Any] (or a more specific type), and error:
Optional[str] (or Optional[Dict[str, Any]] depending on needs), and change
parameters to use a non-mutable default (e.g., Optional[Dict[str, Any]] = None
or use Field(default_factory=dict)) so FastAPI returns those fields and you
avoid mutable default pitfalls.

@router.post("/query", response_model=AIQueryResponse)
async def process_ai_query(request: AIQueryRequest, http_request: Request):
"""
Process a natural language query through AI and return routing information
"""
try:
# Validate input
if not request.query or len(request.query.strip()) == 0:
raise HTTPException(status_code=400, detail="Query cannot be empty")

# Process query through AI router
result = await ai_router.process_query(
query=request.query.strip(),
brand_id=request.brand_id
)

# --- Hybrid Orchestration Logic ---
# Extended intent-to-parameter mapping for all available routes
intent_param_map = {
"dashboard_overview": {"required": ["brand_id"], "optional": []},
"brand_profile": {"required": ["user_id"], "optional": []},
"campaigns": {"required": ["brand_id"], "optional": ["campaign_id"]},
"creator_matches": {"required": ["brand_id"], "optional": []},
"creator_search": {"required": ["brand_id"], "optional": ["industry", "min_engagement", "location"]},
"creator_profile": {"required": ["creator_id", "brand_id"], "optional": []},
"analytics_performance": {"required": ["brand_id"], "optional": []},
"analytics_revenue": {"required": ["brand_id"], "optional": []},
"contracts": {"required": ["brand_id"], "optional": ["contract_id"]},
}
intent = result.get("route")
params = result.get("parameters", {})

# Debug: Log the parameters to understand the type issue
logger.info(f"Intent: {intent}")
logger.info(f"Params: {params}")
logger.info(f"Params type: {type(params)}")
for key, value in params.items():
logger.info(f" {key}: {value} (type: {type(value)})")

api_result = None
api_error = None
# Prepare arguments for API calls, including optional params if present
def get_api_args(intent, params):
args = {}
if intent in intent_param_map:
# Add required params
for param in intent_param_map[intent]["required"]:
if params.get(param) is not None:
args[param] = params[param]
# Add optional params if present
for param in intent_param_map[intent]["optional"]:
if params.get(param) is not None:
args[param] = params[param]
return args

# Check if all required params are present
all_params_present = True
missing_params = []
if intent in intent_param_map:
for param in intent_param_map[intent]["required"]:
if not params.get(param):
all_params_present = False
missing_params.append(param)

# Allow queries with only optional params if API supports it (e.g., creator_search with filters)
only_optional_params = False
if intent in intent_param_map and not all_params_present:
# If at least one optional param is present and no required params are present
if (
len(intent_param_map[intent]["optional"]) > 0 and
all(params.get(p) is None for p in intent_param_map[intent]["required"]) and
any(params.get(p) is not None for p in intent_param_map[intent]["optional"])
):
only_optional_params = True

if (intent and all_params_present) or (intent and only_optional_params):
try:
api_args = get_api_args(intent, params)
# Use aliases for get_campaigns and get_contracts
if intent == "creator_search":
from ..routes.brand_dashboard import search_creators
api_result = await search_creators(**api_args)
elif intent == "dashboard_overview":
from ..routes.brand_dashboard import get_dashboard_overview
api_result = await get_dashboard_overview(**api_args)
elif intent == "creator_matches":
from ..routes.brand_dashboard import get_creator_matches
api_result = await get_creator_matches(**api_args)
elif intent == "brand_profile":
from ..routes.brand_dashboard import get_brand_profile
api_result = await get_brand_profile(**api_args)
elif intent == "campaigns":
from ..routes.brand_dashboard import get_brand_campaigns as get_campaigns
api_result = await get_campaigns(**api_args)
elif intent == "creator_profile":
from ..routes.brand_dashboard import get_creator_profile
api_result = await get_creator_profile(**api_args)
elif intent == "analytics_performance":
from ..routes.brand_dashboard import get_campaign_performance
api_result = await get_campaign_performance(**api_args)
elif intent == "analytics_revenue":
from ..routes.brand_dashboard import get_revenue_analytics
api_result = await get_revenue_analytics(**api_args)
elif intent == "contracts":
from ..routes.brand_dashboard import get_brand_contracts as get_contracts
api_result = await get_contracts(**api_args)
except Exception as api_exc:
logger.error(f"API call failed for intent '{intent}': {api_exc}")
api_error = str(api_exc)

# Convert to response model, add 'result' field for actual data
response = AIQueryResponse(
intent=result.get("intent", "unknown"),
route=result.get("route"),
parameters=params,
follow_up_needed=not all_params_present and not only_optional_params or api_error is not None,
follow_up_question=(result.get("follow_up_question") if not all_params_present and not only_optional_params else None),
explanation=(result.get("explanation", "") if not api_error else f"An error occurred while processing your request: {api_error}"),
original_query=result.get("original_query", request.query),
timestamp=result.get("timestamp", ""),
)
# Attach result if available
response_dict = response.dict()
# 1. Get or generate session_id
session_id = http_request.headers.get("X-Session-ID")
if not session_id and request.context:
session_id = request.context.get("session_id")
if not session_id:
session_id = str(uuid.uuid4())

# 2. Load previous state from Redis
state = await get_session_state(session_id)
prev_params = state.get("params", {})
prev_intent = state.get("intent")

# 3. Merge new params and intent
# Use new intent if present, else previous
intent = result.get("route") or prev_intent
params = {**prev_params, **result.get("parameters", {})}
state["params"] = params
state["intent"] = intent

# 4. Save updated state to Redis
await save_session_state(session_id, state)

response_dict["session_id"] = session_id
if api_result is not None:
response_dict["result"] = api_result
if api_error is not None:
response_dict["error"] = api_error
return response_dict
except HTTPException:
raise
except Exception as e:
logger.error(f"Error processing AI query: {e}")
raise HTTPException(status_code=500, detail="Failed to process AI query")

@router.get("/routes")
async def get_available_routes():
"""
Get list of available routes that the AI can route to
"""
try:
routes = ai_router.list_available_routes()
return {
"available_routes": routes,
"total_routes": len(routes)
}
except Exception as e:
logger.error(f"Error fetching available routes: {e}")
raise HTTPException(status_code=500, detail="Failed to fetch routes")

@router.get("/route/{route_name}")
async def get_route_info(route_name: str):
"""
Get detailed information about a specific route
"""
try:
route_info = ai_router.get_route_info(route_name)
if not route_info:
raise HTTPException(status_code=404, detail=f"Route '{route_name}' not found")

return {
"route_name": route_name,
"info": route_info
}
except HTTPException:
raise
except Exception as e:
logger.error(f"Error fetching route info: {e}")
raise HTTPException(status_code=500, detail="Failed to fetch route info")

@router.post("/test")
async def test_ai_query(query: str = Query(..., description="Test query")):
"""
Test endpoint for AI query processing (for development)
"""
try:
# Process test query
result = await ai_router.process_query(query=query)

return {
"test_query": query,
"result": result,
"status": "success"
}
except Exception as e:
logger.error(f"Error in test AI query: {e}")
return {
"test_query": query,
"error": str(e),
"status": "error"
}
Loading