File size: 4,938 Bytes
4832b3b
 
 
 
 
 
3a0aba1
 
 
 
4832b3b
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
649fee6
 
 
 
 
 
 
 
4832b3b
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
649fee6
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
from fastapi import FastAPI, HTTPException
from fastapi.middleware.cors import CORSMiddleware
from fastapi.responses import FileResponse
from pydantic import BaseModel
from typing import List, Optional
import os
try:
    from . import chat, voice, database
except ImportError:
    import chat, voice, database
import traceback

app = FastAPI()

app.add_middleware(
    CORSMiddleware,
    allow_origins=["*"],
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
)

# Initialize database on startup
@app.on_event("startup")
async def startup_event():
    database.init_db()

class ChatRequest(BaseModel):
    message: str
    # history is now optional/deprecated as we use session_id, but keeping for backward compatibility if needed
    history: List[dict] = [] 

class SessionCreateRequest(BaseModel):
    name: str = "New Chat"
    user_id: str

class VoiceRequest(BaseModel):
    text: str
    voice: str

@app.get("/")
async def root():
    return {"message": "AI Girlfriend API"}

@app.get("/sessions/{user_id}")
async def get_sessions(user_id: str):
    return database.get_sessions(user_id)

@app.post("/sessions")
async def create_session(request: SessionCreateRequest):
    return database.create_session(request.user_id, request.name)

@app.delete("/sessions/{session_id}")
async def delete_session(session_id: str):
    database.delete_session(session_id)
    return {"message": "Session deleted"}

@app.get("/sessions/{session_id}/messages")
async def get_session_messages(session_id: str):
    return database.get_messages(session_id)

@app.post("/sessions/{session_id}/chat")
async def chat_session_endpoint(session_id: str, request: ChatRequest):
    try:
        # Get existing history from DB
        db_history = database.get_messages(session_id)
        
        # Convert to format expected by chat.py (list of dicts with role/content)
        history_for_model = [{"role": msg["role"], "content": msg["content"]} for msg in db_history]
        
        # Get response from AI
        response_text = chat.get_chat_response(request.message, history_for_model)
        
        # Save user message and AI response to DB
        database.add_message(session_id, "user", request.message)
        database.add_message(session_id, "assistant", response_text)
        
        # If this was the first message (history was empty before this turn), generate a summary
        if not db_history:
            try:
                summary = chat.generate_summary(request.message, response_text)
                database.update_session_name(session_id, summary)
            except Exception as e:
                print(f"Failed to generate summary: {e}")
        
        return {"response": response_text}
    except Exception as e:
        traceback.print_exc()
        raise HTTPException(status_code=500, detail=str(e))

# Legacy endpoint - keeping for now or can be removed if frontend is fully updated
@app.post("/chat")
async def chat_endpoint(request: ChatRequest):
    try:
        response = chat.get_chat_response(request.message, request.history)
        return {"response": response}
    except Exception as e:
        traceback.print_exc() # Print error to console
        raise HTTPException(status_code=500, detail=str(e))

@app.get("/voices")
async def voices_endpoint():
    return voice.get_voices()

@app.post("/speak")
async def speak_endpoint(request: VoiceRequest):
    try:
        audio_path = await voice.generate_audio(request.text, request.voice)
        return FileResponse(audio_path, media_type="audio/mpeg", filename="response.mp3")
    except Exception as e:
        raise HTTPException(status_code=500, detail=str(e))

from fastapi.staticfiles import StaticFiles
from fastapi.responses import FileResponse

# Mount static files if they exist (for production/served build)
frontend_dist = os.path.join(os.path.dirname(__file__), "../frontend/dist")
assets_path = os.path.join(frontend_dist, "assets")

if os.path.exists(assets_path):
    app.mount("/assets", StaticFiles(directory=assets_path), name="assets")

@app.get("/{full_path:path}")
async def serve_frontend(full_path: str):
    # Only serve if dist exists
    if os.path.exists(frontend_dist):
        # Serve index.html for any other path (SPA routing)
        # Check if file exists in dist, else serve index.html
        target_file = os.path.join(frontend_dist, full_path)
        if full_path and os.path.exists(target_file):
            return FileResponse(target_file)
        return FileResponse(os.path.join(frontend_dist, "index.html"))
    
    # If dist doesn't exist, just return a message or 404 for frontend routes
    # This allows backend to run even if frontend isn't built
    if full_path == "":
        return {"message": "Backend is running. Frontend build not found. Use 'npm run dev' for frontend development."}
    raise HTTPException(status_code=404, detail="Frontend build not found")