Spaces:
Sleeping
λ°λͺ¨ API κ³μ½ β μΉ λ¦¬λ β μμ§ (M6)
μμΉ: μ¨μ μν(trust/doubt/μ μ½λ)λ μ΄λ€ μλ΅μλ μ£μ§ μλλ€ (보μ΄μ§ μλ κ²μ). μΈμ μμν: μμ¬ μμΉ + μ¨μ μνλ SQLite(
data/sessions.db)μ λ§€ λ³κ²½ μ¦μ μ μ₯. μλ² μ¬μμ νμλGET /api/session/{sid}λ‘ λ³΅μλλ€. ν¬μΌ(μ±ν ) μ€κ° μνλ λΉμμ β 볡μ μ ν¬μΌμ λ«ν μνλ‘ μμνκ³ , μ§ν μ€μ΄λ λνμ μ λ’°μΆ μ¦λΆμ μ μ€λλ€ (μΉμΈλ νΈλ μ΄λμ€ν). λ¨μΌ νλ‘μΈμ€ μ μ (μΈλ©λͺ¨λ¦¬ μΊμ + ADK μΈλ©λͺ¨λ¦¬ μΈμ )λ μ μ§.
κ³΅ν΅ νμ
// Step β λ¦¬λ© νλ©΄ ν μ₯
{
"card": {"id": "E01-01", "title": "μ΄λ μΌλ‘μ μ΄λ", "episode": "E01",
"episode_title": "μ 1ν: μ΄λ¦° λ°€μ μλ"},
"narration": [
{"type": "prose|dialogue_center|character_dialogue|stage_direction",
"text": "...", "speaker": "", // speakerλ character_dialogueλ§
"role": "lead_in|aftermath|null", // νλ©΄ λ°°μΉ μν β lead_in(λ°ν μ§μ μ§§μ λμ
)μ
// μ€ν
μ΄μ§ νλ©΄ μλ¨μ μΉνλ€ (μ λ: μ μ¬ μ κ·μΉ)
"portrait": "/assets/μΈλ¬Ό/μΉ΄λ₯΄λ°λΌ.png"} // νμ μ΄μ (router SPEAKER_PORTRAIT,
], // μμΌλ©΄ null β μ΄λ¦νλ§. μ μ Β·λ‘λΌλ νλ‘ νΈκ°
// 1μΈμΉ(μ€λ₯Έμͺ½ μ λ ¬)μΌλ‘ μ²λ¦¬
"choices": [{"label": "λμ λ¬λ€"}], // μμΌλ©΄ []
"can_chat": true, // κ°μ
ν¬μΌ(μμ μ±ν
) κ°λ₯ νμ΄μ§
"image": "/assets/νλμ΄ μ΄λ―Έμ§/4.png", // μ°μΆ μ»· (μμΌλ©΄ null). λ§€ν: router IMAGE_MAP
"background": "/assets/λ°°κ²½/μΉ¨μ€.png", // μ₯μ λ°°κ²½ (μΉ΄λ #μ₯μ/ νκ·Έ β router BACKGROUND_MAP,
// λ§€ν μλ μ₯μλ μ§μ λ°°κ²½ μ μ§ Β· μλ©μ null)
"progress": {"index": 5, "total": 44}, // μ½κΈ° μ§νλ (νμ΄μ§ λ¨μ)
// νμ΄μ§ = μ¬λ¬ μΉ΄λ λ³ν©(services/paging.py, κ²°μ μ
// νν°μ
Β· runλ³ κ· ν λΆν , λͺ©ν chapter.page_target_chars
// κΈ°λ³Έ 500μ). card.titleμ μ°Έκ³ μ© β ν΄λΌμ΄μΈνΈλ μΉ΄λ
// μμ λͺ©μ νμνμ§ μλλ€(μνΌμλ ν€λΒ·μ»· μΊ‘μ
λ§).
// card.id = νμ΄μ§ λ μΉ΄λ(μ νΒ·μ±ν
κΈ°μ€),
// title/episode = 첫 μΉ΄λ κΈ°μ€.
// μ°μΆ μ»·μ λ¨λ
νμ΄μ§ + νμ 2νμ΄μ§(μ»·+λ³Έλ¬Έ) β
// imageκ° μμΌλ©΄ μ»· νμ΄μ§ λ²νΈ = index - 1
// (ν΄λΌμ΄μΈνΈλ μ»·μ λ¨Όμ λ λν λ€ λ³Έλ¬Έ)
"ending": null // μ±ν° μ’
λ£ μμλ§ μλ Ending
}
// Ending
{"id": "ending_eternal", "label": "The Eternal β λν/μμ", "description": "..."}
// PocketTurn
{"reply": "β¦μ΄μν λ§μ νλꡬλ, λ‘λΌ.", "converged": false, "reason": null, "turn_no": 1}
μλν¬μΈνΈ
| λ©μλ/κ²½λ‘ | μμ² | μλ΅ | λΉκ³ |
|---|---|---|---|
POST /api/session |
{} |
{"session_id", "step": Step} |
μ νλ μ΄ μμ (E01-01) |
GET /api/session/{sid} |
β | {"step": Step} |
νμ¬ μμΉ μ¬μ‘°ν = 볡μ μ§μ μ (μλ‘κ³ μΉ¨Β·μλ² μ¬μμ). 404λ©΄ μ μΈμ μ λ§λ€ κ² |
POST /api/session/{sid}/advance |
{} |
{"step": Step} |
μ νμ§ μλ μΉ΄λμμ λ€μ μ₯ |
POST /api/session/{sid}/choose |
{"index": 0} |
{"picked": {"label","result"}, "step": Step} |
μ ν λ°μ(μΆμ μλ² λ΄λΆ) |
POST /api/session/{sid}/pocket/open |
{} |
{"opened": true} |
νμ¬ μΉ΄λμμ μ±ν μμ. can_chat=falseλ©΄ 409 |
POST /api/session/{sid}/pocket/turn |
{"text": "..."} |
PocketTurn |
converged=trueλ©΄ μλ close(컀λ°) μλ£ μν |
POST /api/session/{sid}/pocket/close |
{} |
{"closed": true} |
μ μ κ° λ¨Όμ λνλ₯Ό μ μ λ (μ»€λ° ν¬ν¨) |
GET /api/meta |
β | {"cover_image","title","subtitle","intro_images"} |
νμ§Β·μΈνΈλ‘ μνμ€(μν/μΈλ¬Ό μκ°Β·1μ₯ μμ β μ μμμλ§ νμ, μ΄μ΄ μ½κΈ°λ 건λλ) |
GET /assets/β¦ |
β | μ΄λ―Έμ§ νμΌ | μ°μΆ μμ (λνλ μ°μΆλ¬Ό, μ½κΈ° μ μ©) |
μ€λ₯: μ‘΄μ¬νμ§ μλ μΈμ
λλ μ½ν
μΈ κ°±μ μΌλ‘ 무ν¨νλ μΈμ
404 Β·
μνμ μ λ§λ νΈμΆ(μ νμ§ μΉ΄λμμ advance λ±) 409 Β· {"detail": "..."} (FastAPI κΈ°λ³Έ ν¬λ§·).
- ν΄λΌμ΄μΈνΈλ
session_idλ₯ΌlocalStorage(carmilla_sid)μ 보κ΄νκ³ , κΈ°λ μ GETμΌλ‘ 볡μμ μλνλ€. 404λ©΄ ν€λ₯Ό λ²λ¦¬κ³ μ μΈμ . 볡μ λ²μλ "νμ¬ νμ΄μ§λΆν°" β λ€λ‘κ°κΈ° νμ€ν 리λ μΈμ λ΄ νμ - μ½ν μΈ (λΉνΈμΉ΄λ) μ¬λ³νμΌλ‘ μΉ΄λ ID/μμκ° λ°λλ©΄ κΈ°μ‘΄ μΈμ μ μλ²κ° 무ν¨ννλ€ (λ²μ μ€ν¬ν)
νλ¦
POST /api/session ββ step
ββ choices=[] ββββ POST advance βββ step (λ°λ³΅)
ββ choices=[...] β POST choose βββ picked.result νμ β step
β ββ (μ ν μ ) POST pocket/open β pocket/turn* β converged β μ νμ§λ‘ 볡κ·
ββ ending βββββββ μλ© νλ©΄
- ν¬μΌ μλ ΄/ν΄ μν(6ν΄)Β·μ΅μ 체λ₯(2ν΄)λ μλ² μ½λκ° κ°μ . ν΄λΌμ΄μΈνΈλ convergedλ§ λ°λ₯Έλ€
- ν΄ λ‘κ·Έλ μλ²κ°
logs/playlog.jsonlμ κΈ°λ‘ (μ°κ΅¬ λ°μ΄ν°)