Fixed RPG

This commit is contained in:
2026-06-01 07:44:38 +03:00
parent 600ad78f05
commit d4cd8f02f4
30 changed files with 1516 additions and 816 deletions
+48 -5
View File
@@ -2,7 +2,14 @@ from fastapi import APIRouter, File, Form, HTTPException, UploadFile
from pydantic import BaseModel
from typing import Optional
from services.character_card import list_characters, get_character, import_card_file, update_character, update_appearance_tags
from services.character_card import (
list_characters,
get_character,
import_card_file,
preview_card_file,
update_character,
update_appearance_tags,
)
router = APIRouter(prefix="/characters", tags=["characters"])
@@ -17,6 +24,7 @@ class CardPatch(BaseModel):
appearance_tags: Optional[str] = None
lora_name: Optional[str] = None
lora_weight: Optional[float] = None
alternate_greetings_json: Optional[str] = None
@router.get("/")
@@ -32,6 +40,15 @@ async def get_one(card_id: str):
return card
@router.post("/preview")
async def preview_card(file: UploadFile = File(...)):
content = await file.read()
try:
return await preview_card_file(content, file.filename or "card.json")
except Exception as e:
raise HTTPException(status_code=400, detail=str(e))
@router.patch("/{card_id}")
async def patch_card(card_id: str, body: CardPatch):
card = await get_character(card_id)
@@ -39,20 +56,26 @@ async def patch_card(card_id: str, body: CardPatch):
raise HTTPException(status_code=404, detail="Карточка не найдена")
fields = {k: v for k, v in body.model_dump().items() if v is not None}
await update_character(card_id, fields)
# sync appearance_tags and lora to persona
from services.personas import update_persona_appearance
if "appearance_tags" in fields:
await update_persona_appearance(f"card_{card_id}", fields["appearance_tags"])
if {"lora_name", "lora_weight"} & fields.keys():
from services.personas import update_persona_lora
await update_persona_lora(f"card_{card_id}", fields.get("lora_name"), fields.get("lora_weight"))
# rebuild system prompt if character fields changed
char_fields = {"name", "description", "personality", "scenario", "first_mes", "mes_example"}
if char_fields & fields.keys():
updated = await get_character(card_id)
from services.character_card import build_system_prompt
from services.personas import update_persona_prompt
await update_persona_prompt(f"card_{card_id}", build_system_prompt(updated))
if "first_mes" in fields or "alternate_greetings_json" in fields:
from services.personas import patch_persona
sync = {}
if "first_mes" in fields:
sync["first_mes"] = fields["first_mes"]
if "alternate_greetings_json" in fields:
sync["alternate_greetings_json"] = fields["alternate_greetings_json"]
await patch_persona(f"card_{card_id}", sync)
return await get_character(card_id)
@@ -67,7 +90,6 @@ async def upload_avatar(card_id: str, file: UploadFile = File(...)):
from services.character_card import _save_avatar_bytes
rel = _save_avatar_bytes(content, f"card_{card_id}")
await update_character(card_id, {"avatar_path": rel})
# sync persona
from services.personas import patch_persona
await patch_persona(f"card_{card_id}", {"avatar_path": rel})
return {"avatar_path": f"/static/{rel}"}
@@ -78,14 +100,35 @@ async def import_card(
file: UploadFile = File(...),
lora_name: str = Form(""),
lora_weight: float = Form(0.8),
card_id: str = Form(""),
name: str = Form(""),
description: str = Form(""),
personality: str = Form(""),
scenario: str = Form(""),
first_mes: str = Form(""),
mes_example: str = Form(""),
appearance_tags: str = Form(""),
alternate_greetings_json: str = Form("[]"),
):
content = await file.read()
overrides = {
"name": name or None,
"description": description or None,
"personality": personality or None,
"scenario": scenario or None,
"first_mes": first_mes or None,
"mes_example": mes_example or None,
"appearance_tags": appearance_tags or None,
"alternate_greetings_json": alternate_greetings_json or "[]",
}
try:
card = await import_card_file(
content,
file.filename or "card.json",
lora_name=lora_name,
lora_weight=lora_weight,
overrides=overrides,
card_id=card_id.strip() or None,
)
except Exception as e:
raise HTTPException(status_code=400, detail=str(e))
@@ -94,6 +137,7 @@ async def import_card(
"card_id": card["card_id"],
"persona_id": f"card_{card['card_id']}",
"name": card["name"],
"alternate_greetings": card.get("alternate_greetings", []),
}
@@ -104,4 +148,3 @@ async def remove_card(card_id: str):
if not await delete_persona(f"card_{card_id}"):
raise HTTPException(status_code=404, detail="Карточка не найдена")
return {"status": "deleted", "card_id": card_id}