|
from fastapi import FastAPI, HTTPException |
|
from fastapi.middleware.cors import CORSMiddleware |
|
from fastapi.staticfiles import StaticFiles |
|
from pydantic import BaseModel |
|
from typing import List, Optional |
|
import os |
|
from dotenv import load_dotenv |
|
|
|
|
|
if os.getenv("DOCKER_ENV"): |
|
from server.game.game_logic import GameState, StoryGenerator, MAX_RADIATION |
|
else: |
|
from game.game_logic import GameState, StoryGenerator, MAX_RADIATION |
|
|
|
|
|
load_dotenv() |
|
|
|
|
|
API_HOST = os.getenv("API_HOST", "0.0.0.0") |
|
API_PORT = int(os.getenv("API_PORT", "8000")) |
|
STATIC_FILES_DIR = os.getenv("STATIC_FILES_DIR", "../client/dist") |
|
|
|
app = FastAPI(title="Echoes of Influence") |
|
|
|
|
|
app.add_middleware( |
|
CORSMiddleware, |
|
allow_origins=[ |
|
"http://localhost:5173", |
|
f"http://localhost:{API_PORT}", |
|
"https://huggingface.co", |
|
"https://*.hf.space", |
|
], |
|
allow_credentials=True, |
|
allow_methods=["*"], |
|
allow_headers=["*"], |
|
) |
|
|
|
|
|
game_state = GameState() |
|
story_generator = StoryGenerator(api_key=os.getenv("MISTRAL_API_KEY")) |
|
|
|
class Choice(BaseModel): |
|
id: int |
|
text: str |
|
|
|
class StoryResponse(BaseModel): |
|
story_text: str |
|
choices: List[Choice] |
|
is_death: bool = False |
|
radiation_level: int |
|
|
|
class ChatMessage(BaseModel): |
|
message: str |
|
choice_id: Optional[int] = None |
|
|
|
@app.get("/api/health") |
|
async def health_check(): |
|
"""Health check endpoint""" |
|
return { |
|
"status": "healthy", |
|
"game_state": { |
|
"story_beat": game_state.story_beat, |
|
"radiation_level": game_state.radiation_level |
|
} |
|
} |
|
|
|
@app.post("/api/chat", response_model=StoryResponse) |
|
async def chat_endpoint(chat_message: ChatMessage): |
|
try: |
|
|
|
if chat_message.message.lower() == "restart": |
|
game_state.reset() |
|
previous_choice = "none" |
|
else: |
|
previous_choice = f"Choice {chat_message.choice_id}" if chat_message.choice_id else "none" |
|
|
|
|
|
story_segment = story_generator.generate_story_segment(game_state, previous_choice) |
|
|
|
|
|
game_state.radiation_level += story_segment.radiation_increase |
|
|
|
|
|
if game_state.radiation_level >= MAX_RADIATION: |
|
story_segment = story_generator.process_radiation_death(story_segment) |
|
|
|
|
|
if not story_segment.is_death: |
|
game_state.story_beat += 1 |
|
|
|
|
|
choices = [] if story_segment.is_death else [ |
|
Choice(id=i, text=choice.strip()) |
|
for i, choice in enumerate(story_segment.choices, 1) |
|
] |
|
|
|
return StoryResponse( |
|
story_text=story_segment.story_text, |
|
choices=choices, |
|
is_death=story_segment.is_death, |
|
radiation_level=game_state.radiation_level |
|
) |
|
|
|
except Exception as e: |
|
print(f"Error: {str(e)}") |
|
raise HTTPException(status_code=500, detail=str(e)) |
|
|
|
|
|
app.mount("/", StaticFiles(directory=STATIC_FILES_DIR, html=True), name="static") |
|
|
|
if __name__ == "__main__": |
|
import uvicorn |
|
uvicorn.run("server.server:app", host=API_HOST, port=API_PORT, reload=True) |