Download Game! Currently 97 players. Last logged in:FonkensembleUrsaStylusSilencer

Blitzer's Blog >> 72642

Back to blogs index
Posted: 21 Sep 2026 07:11 [ permalink ]
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel
import json
import os
import time
import requests

app = FastAPI(title="Valvomo LLM Proxy")

STATE_FILE = "valvomo_state.json"
# Todellinen LLM-backend (esim. paikallinen Ollama tai OpenAI)
REAL_LLM_URL = "http://localhost:11434/v1/chat/completions" 

def load_state():
    if not os.path.exists(STATE_FILE):
        return {"prompts": {}, "responses": {}}
    with open(STATE_FILE, "r", encoding="utf-8") as f:
        return json.load(f)

def save_state(state):
    with open(STATE_FILE, "w", encoding="utf-8") as f:
        json.dump(state, f, indent=4, ensure_ascii=False)

class ChatCompletionRequest(BaseModel):
    model: str = "default"
    messages: list
    temperature: float = 0.7

# ==========================================
# 1. CLIENT-RAJAPINTA (Matkii LLM API:ta)
# ==========================================
def intercept_prompt(req: ChatCompletionRequest):
    """
    Sovellukset lC$hettC$vC$t viestinsC$ tC$nne luullen tC$tC$ oikeaksi LLM
API:ksi.
    PyyntC6 pysC$ytetC$C$n saapuvien jonoon.
    """
    state = load_state()
    prompt_id = str(int(time.time() * 1000))
    
    prompt_data = {
        "id": prompt_id,
        "status": "pending_approval", # pending_approval, approved, rejected,
manual_answered
        "request": req.dict(),
        "created_at": time.time(),
        "manual_response": None
    }
    
    state["prompts"][prompt_id] = prompt_data
    save_state(state)
    
    # Palautetaan klientille simuloitu "odottaa" -tila tai pidetC$C$n yhteys
auki
    # Yksinkertaisuuden vuoksi palautetaan viesti, ettC$ pyyntC6 on jonossa
    return {
        "id": prompt_id,
        "object": "chat.completion",
        "choices": [{
            "message": {"role": "assistant", "content": "[VALVOMO] Prompti on
siirretty hyvC$ksyntC$jonoon odottamaan tarkastusta."},
            "finish_reason": "stop"
        }]
    }

# ==========================================
# 2. VALVOMON HALLINTARAJAPINTA (Promptit)
# ==========================================
def list_pending_prompts():
    """Listaa odottavat promptit."""
    state = load_state()
    return {pid: p for pid, p in state["prompts"].items() if p["status"] ==
"pending_approval"}

def edit_pending_prompt(prompt_id: str, new_messages: list):
    """Muokkaa odottavaa promptia ennen sen vapauttamista."""
    state = load_state()
    if prompt_id not in state["prompts"]:
        raise HTTPException(status_code=404, detail="Promptia ei lC6ydy")
    
    state["prompts"][prompt_id]["request"]["messages"] = new_messages
    save_state(state)
    return {"status": "success", "message": "Promptia muokattu"}

def reject_prompt(prompt_id: str):
    """HylkC$C$ odottava prompti."""
    state = load_state()
    if prompt_id not in state["prompts"]:
        raise HTTPException(status_code=404, detail="Promptia ei lC6ydy")
    
    state["prompts"][prompt_id]["status"] = "rejected"
    save_state(state)
    return {"status": "success", "message": "Prompti hylC$tty"}

def manual_answer_prompt(prompt_id: str, answer_content: str):
    """Antaa odottavaan promptiin suoraan oman vastauksen ohittaen LLM:n."""
    state = load_state()
    if prompt_id not in state["prompts"]:
        raise HTTPException(status_code=404, detail="Promptia ei lC6ydy")
    
    p = state["prompts"][prompt_id]
    p["status"] = "manual_answered"
    p["manual_response"] = answer_content
    
    # Luodaan suoraan vastausjonoon valmis vastaus
    state["responses"][prompt_id] = {
        "id": prompt_id,
        "status": "pending_response_approval",
        "content": answer_content,
        "source": "manual"
    }
    save_state(state)
    return {"status": "success", "message": "Manuaalinen vastaus tallennettu"}

def approve_and_send_to_llm(prompt_id: str):
    """HyvC$ksyy promptin ja lC$hettC$C$ sen eteenpC$in oikealle LLM:lle."""
    state = load_state()
    if prompt_id not in state["prompts"]:
        raise HTTPException(status_code=404, detail="Promptia ei lC6ydy")
    
    p = state["prompts"][prompt_id]
    p["status"] = "approved"
    
    # LC$hetetC$C$n pyyntC6 eteenpC$in oikealle LLM:lle (esim. Ollama /
OpenAI)
    try:
        llm_resp = requests.post(REAL_LLM_URL, json=p["request"], timeout=60)
        llm_data = llm_resp.json()
        llm_content = llm_data["choices"][0]["message"]["content"]
    except Exception as e:
        llm_content = f"[VIRHE LLM-KUTSUSSA]: {str(e)}"

    # Tallennetaan vastaus UUSIEN VASTAUSTEN JONOON (Outbound Queue)
    state["responses"][prompt_id] = {
        "id": prompt_id,
        "status": "pending_response_approval",
        "content": llm_content,
        "source": "llm"
    }
    
    save_state(state)
    return {"status": "success", "message": "Prompti ajettu LLM:ssC$, vastaus
siirretty vastausjonoon tarkastettavaksi."}


# ==========================================
# 3. VALVOMON HALLINTARAJAPINTA (Vastaukset)
# ==========================================
def list_pending_responses():
    """Listaa LLM:n palauttamat vastaukset, jotka odottavat kuratointia/hyvC$ks