CoolFace
Apppublic

skyleaping/structured-bloom-api

sourceHugging Faceupdated 4mo agoView on Hugging Face
0likes
main.py187 linesDownload Raw Back to root
1import json2import os3from fastapi import FastAPI, HTTPException4from fastapi.middleware.cors import CORSMiddleware5from pydantic import BaseModel6from openai import OpenAI7 8app = FastAPI(title="Structured Bloom API")9 10app.add_middleware(11    CORSMiddleware,12    allow_origins=[13        "https://s23.aiweb2026.site",14        "http://localhost:5173",15        "http://127.0.0.1:5173",16    ],17    allow_credentials=True,18    allow_methods=["*"],19    allow_headers=["*"],20)21 22api_key = os.getenv("OPENAI_API_KEY")23client = OpenAI(api_key=api_key) if api_key else None24 25 26class AnalyzeRequest(BaseModel):27    text: str28    available_time: str29 30 31def normalize_time(value: str) -> str:32    value = str(value)33    mapping = {34        "5min": "5분",35        "10min": "10분",36        "20min": "20분",37        "30min": "30분",38    }39    return mapping.get(value, value)40 41 42def fallback_result(text: str, available_time: str):43    time_label = normalize_time(available_time)44 45    return {46        "emotion": ["지침", "정리 필요"],47        "energy": "low",48        "situation": "생각과 감정이 조금 쌓여 있어, 부담이 낮은 회복 행동이 필요한 상태입니다.",49        "template": "soft_reset",50        "flower_theme": "tulip",51        "color_theme": "warm",52        "activity": {53            "title": "책상 위에서 가장 눈에 띄는 물건 3개만 정리하기",54            "time": time_label,55            "burden": "낮음",56            "steps": [57                "타이머를 맞추고 주변을 둘러봅니다.",58                "가장 눈에 띄는 물건 3개만 제자리로 옮깁니다.",59                "마지막에 물 한 모금을 마시고 잠깐 숨을 고릅니다.",60            ],61            "first_action": "지금 손이 닿는 물건 하나를 먼저 치우기",62        },63        "mood_message": "오늘은 크게 바꾸려 하기보다, 작은 정리 하나로 흐름을 다시 만들어도 충분합니다.",64        "drink": "따뜻한 물 또는 연한 차",65        "space": "책상 앞이나 창가처럼 바로 움직일 수 있는 곳",66        "clothes": "몸을 조이지 않는 편한 옷차림",67        "reason": "작은 정리는 생각의 과부하를 줄이고 다음 행동을 시작하기 쉽게 만들어줍니다.",68    }69 70 71@app.get("/")72def health_check():73    return {"message": "Structured Bloom API is running"}74 75 76@app.get("/health")77def health():78    return {79        "status": "ok",80        "has_openai_key": bool(api_key),81        "openai_enabled": bool(client),82    }83 84 85@app.post("/analyze")86def analyze(req: AnalyzeRequest):87    if not req.text.strip():88        raise HTTPException(status_code=400, detail="분석할 문장을 입력해주세요.")89 90    if not client:91        return fallback_result(req.text, req.available_time)92 93    time_label = normalize_time(req.available_time)94 95    prompt = """96너는 사용자의 현재 상태를 분석하고, 작고 실천 가능한 회복 활동을 추천하는 Structured Bloom API야.97 98사용자 입력:99{user_text}100 101사용 가능한 시간:102{time_label}103 104반드시 아래 JSON 형식만 반환해.105설명 문장, 코드블록, ```json 같은 표시는 절대 붙이지 마.106 107{{108  "emotion": ["감정1", "감정2"],109  "energy": "low",110  "situation": "현재 상태를 한 문장으로 요약",111  "template": "soft_reset",112  "flower_theme": "tulip",113  "color_theme": "warm",114  "activity": {{115    "title": "추천 활동 제목",116    "time": "{time_label}",117    "burden": "낮음",118    "steps": ["1단계", "2단계", "3단계"],119    "first_action": "가장 먼저 할 행동"120  }},121  "mood_message": "사용자에게 건네는 짧고 부드러운 메시지",122  "drink": "추천 음료",123  "space": "추천 공간",124  "clothes": "추천 옷차림",125  "reason": "왜 이 활동이 지금 상태에 맞는지 설명"126}}127 128선택 가능한 값:129- energy: low, medium, high 중 하나130- template: soft_reset, sensory_shift, gentle_focus, tiny_action 중 하나131- flower_theme: tulip, lavender, daisy, camellia 중 하나132- color_theme: warm, calm, fresh, neutral 중 하나133""".format(134        user_text=req.text,135        time_label=time_label,136    )137 138    try:139        response = client.responses.create(140            model="gpt-4.1-mini",141            input=prompt,142        )143 144        raw_text = response.output_text.strip()145 146        if raw_text.startswith("```"):147            raw_text = raw_text.replace("```json", "").replace("```", "").strip()148 149        data = json.loads(raw_text)150 151        required_keys = [152            "emotion",153            "energy",154            "situation",155            "template",156            "flower_theme",157            "color_theme",158            "activity",159            "mood_message",160            "drink",161            "space",162            "clothes",163            "reason",164        ]165 166        for key in required_keys:167            if key not in data:168                raise ValueError(f"missing key: {key}")169 170        activity_keys = ["title", "time", "burden", "steps", "first_action"]171        for key in activity_keys:172            if key not in data["activity"]:173                raise ValueError(f"missing activity key: {key}")174 175        return data176 177    except Exception:178        return fallback_result(req.text, req.available_time)179 180 181@app.post("/recommend")182def recommend(req: AnalyzeRequest):183    result = analyze(req)184    return {185        "recommendation": f"추천 활동: {result['activity']['title']}\n이유: {result['reason']}",186        "source": "openai_or_fallback",187    }