File size: 3,680 Bytes
a0fbba6
945956c
a0fbba6
 
945956c
 
 
5911f50
 
a0fbba6
5911f50
 
 
a0fbba6
 
5911f50
 
 
 
 
 
 
a0fbba6
 
 
5911f50
a0fbba6
 
5911f50
a0fbba6
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
16521b1
a0fbba6
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5911f50
 
 
a0fbba6
5911f50
 
 
 
 
 
 
a0fbba6
 
 
 
 
 
 
 
 
5911f50
 
 
a0fbba6
 
 
 
 
 
 
 
 
 
 
 
9635477
a0fbba6
 
 
 
 
5911f50
 
9635477
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
import json
import os
import threading
import time

os.environ["TRANSFORMERS_TRUST_REMOTE_CODE"] = "1"

import gradio as gr
import torch
from huggingface_hub import CommitOperationAdd, HfApi
from transformers import AutoModelForCausalLM, AutoTokenizer

MODEL_ID = "QDHShamiro/Kairo"
DATASET_ID = "QDHShamiro/kairo-conversations"
WRITE_TOKEN = os.environ.get("HF_WRITE_TOKEN")

tokenizer = AutoTokenizer.from_pretrained(MODEL_ID)
model = AutoModelForCausalLM.from_pretrained(MODEL_ID, trust_remote_code=True)
model.eval()

MAX_NEW_TOKENS = 200

_log_lock = threading.Lock()
_pending_turns = []
api = HfApi(token=WRITE_TOKEN) if WRITE_TOKEN else None


def _split_thought(raw: str) -> tuple[str, str]:
    if "Kairo:" in raw:
        thought, reply = raw.split("Kairo:", 1)
        return thought.replace("Gedanke:", "", 1).strip(), reply.strip()
    return "", raw.replace("Gedanke:", "", 1).strip()


def _log_turn(user_text: str, thought: str, reply: str):
    if not api:
        return
    with _log_lock:
        _pending_turns.append({
            "user": user_text, "thought": thought, "reply": reply,
            "ts": time.time(),
        })


def _flush_loop():
    while True:
        time.sleep(20)
        if not api:
            continue
        with _log_lock:
            if not _pending_turns:
                continue
            batch = _pending_turns[:]
            _pending_turns.clear()
        content = "\n".join(json.dumps(t, ensure_ascii=False) for t in batch) + "\n"
        try:
            api.create_commit(
                repo_id=DATASET_ID,
                repo_type="dataset",
                operations=[CommitOperationAdd(
                    path_in_repo=f"logs/{int(time.time())}.jsonl",
                    path_or_fileobj=content.encode("utf-8"),
                )],
                commit_message="Add conversation batch",
            )
        except Exception:
            with _log_lock:
                _pending_turns[:0] = batch


if api:
    threading.Thread(target=_flush_loop, daemon=True).start()


def respond(message, history):
    history_text = "".join(f"User: {u}\nKairo: {a}\n" for u, a in history)
    prompt = f"{history_text}User: {message}\nGedanke:"

    ids = tokenizer(prompt, return_tensors="pt").input_ids
    with torch.no_grad():
        out = model.generate(ids, max_new_tokens=MAX_NEW_TOKENS)
    raw = tokenizer.decode(out[0].tolist())[len(prompt):]
    raw = raw.split("User:")[0].strip()
    thought, reply = _split_thought(raw)
    reply = reply or "Hoppla, mein Gehirn stolpert gerade."

    _log_turn(message, thought, reply)

    if thought:
        return (
            f"<details><summary>💭 Gedankengang</summary>\n\n{thought}\n\n</details>\n\n{reply}"
        )
    return reply


CSS = """
.gradio-container { max-width: 780px !important; margin: auto; }
#chat-col { min-height: 70vh; }
details summary { cursor: pointer; color: var(--body-text-color-subdued); font-size: 0.9em; }
"""

with gr.Blocks(css=CSS, title="Kairo", theme=gr.themes.Soft()) as demo:
    gr.Markdown("## 🗣️ Kairo\nFrom-scratch GPT chat model — brain behind Kairo Voice.")
    with gr.Column(elem_id="chat-col"):
        gr.ChatInterface(
            respond,
            examples=["Hallo Kairo, wie geht's dir?", "Erzähl mir einen Witz.", "Was ist dein Lieblingsthema?"],
            cache_examples=False,
        )
    gr.Markdown(
        "*Antworten fließen anonymisiert ins Weitertraining "
        "([kairo-conversations](https://huggingface.co/datasets/QDHShamiro/kairo-conversations)).*"
    )

if __name__ == "__main__":
    demo.launch(server_name="0.0.0.0", server_port=int(os.environ.get("PORT", 7860)))