File size: 6,157 Bytes
a102e2e
ac901a7
a102e2e
ac901a7
 
a102e2e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
ac901a7
 
a102e2e
 
ac901a7
 
 
a102e2e
ac901a7
a102e2e
 
ac901a7
 
 
 
 
a102e2e
ac901a7
a102e2e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
409fb45
ac901a7
 
 
 
 
 
a102e2e
ac901a7
 
92a7b89
 
 
 
 
 
 
 
ac901a7
 
a102e2e
 
 
 
 
ac901a7
a102e2e
 
 
a3e232f
a102e2e
 
 
ac901a7
9f5b735
ac901a7
a102e2e
 
ac901a7
 
 
a102e2e
ac901a7
 
a102e2e
ac901a7
 
9f5b735
ac901a7
 
 
 
9f5b735
a102e2e
ac901a7
 
 
 
a102e2e
 
ac901a7
 
 
 
 
 
 
9f5b735
ac901a7
9f5b735
ac901a7
 
 
 
 
 
 
 
 
 
 
 
 
9f5b735
 
ac901a7
 
 
 
 
9f5b735
ac901a7
 
 
 
9f5b735
ac901a7
 
9f5b735
ac901a7
 
 
 
 
 
 
 
f20bc0e
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
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
import os
import streamlit as st
import tempfile
import requests
import json
# Entfernte Imports: PIL.Image, io, base64, pandas, zipfile, PyPDF2
# Da kein File Manager mehr benötigt wird.

# ----------------------------------------------------
# 🚨 KRITISCHE FIXES FÜR DEN PERMISSION ERROR
# Zwingt Streamlit, seine temporären/Konfigurationsdateien
# in den beschreibbaren /tmp-Bereich zu schreiben.
# ----------------------------------------------------

# 1. Temporären, beschreibbaren Pfad erstellen
TEMP_STREAMLIT_HOME = os.path.join(tempfile.gettempdir(), "st_config_workaround")
os.makedirs(TEMP_STREAMLIT_HOME, exist_ok=True)

# 2. Umgebungsvariablen setzen
os.environ["STREAMLIT_HOME"] = TEMP_STREAMLIT_HOME
os.environ["STREAMLIT_GATHER_USAGE_STATS"] = "false"

# 3. Minimale config.toml erstellen, um Schreibversuche zu unterbinden
CONFIG_PATH = os.path.join(TEMP_STREAMLIT_HOME, "config.toml")
CONFIG_CONTENT = """
[browser]
gatherUsageStats = false
"""

if not os.path.exists(CONFIG_PATH):
    try:
        with open(CONFIG_PATH, "w") as f:
            f.write(CONFIG_CONTENT)
    except Exception as e:
        print(f"WARNUNG: Konnte config.toml nicht schreiben: {e}")

# ----------------------------------------------------
# Ende der Workarounds
# ----------------------------------------------------

# --- Konfiguration ---
# Hinweis: Layout-Einstellung 'wide' bleibt erhalten
st.set_page_config(page_title="OpenRouter Minimal Chat UI", layout="wide")
OPENROUTER_API_BASE = "https://openrouter.ai/api/v1"

# --- Page Title ---
st.title("💸 OpenRouter Minimal Chat Interface")
st.markdown("""
**Willkommen im OpenRouter Minimal Chat Interface!**
Chatte mit **kostenlosen (Free-Tier)** Modellen über die OpenRouter API. Nur Text-Chat.
""")

# --- Session State Management ---
if "messages" not in st.session_state:
    st.session_state.messages = []
# st.session_state.uploaded_content wurde entfernt.

# --- Context-Length Fetch (Wird beibehalten, da für den Slider wichtig) ---
def fetch_model_contexts(api_key):
    """Lädt alle Modelle + deren context_length."""
    if not api_key:
        return {}
    headers = {"Authorization": f"Bearer {api_key}"}
    try:
        res = requests.get(f"{OPENROUTER_API_BASE}/models", headers=headers, timeout=10)
        contexts = {}
        if res.status_code == 200:
            for m in res.json().get("data", []):
                mid = m.get("id")
                ctx = m.get("context_length", 4096)
                contexts[mid] = ctx
        return contexts
    except Exception as e:
        return {}


# --- Sidebar ---
with st.sidebar:
    st.header("⚙️ API Settings")
    api_key = st.text_input("OpenRouter API Key", type="password")

    # --- Manuelle Modelle ---
    FREE_MODEL_LIST = [
        "cognitivecomputations/dolphin-mistral-24b-venice-edition:free",
        "deepseek/deepseek-chat-v3.1:free",
        "nvidia/nemotron-nano-9b-v2:free",
        "google/gemma-3-27b-it:free",
        "openai/gpt-oss-20b:free",
        "qwen/qwen3-coder:free",
        "qwen/qwen2.5-vl-72b-instruct:free",
        "nousresearch/deephermes-3-llama-3-8b-preview:free",

    ]
    model = st.selectbox("Wähle ein Modell", FREE_MODEL_LIST, index=0)

    # Kontextlänge holen (mit Fallback)
    model_contexts = fetch_model_contexts(api_key)
    default_ctx = model_contexts.get(model, 4096) 

    temperature = st.slider("Temperature", 0.0, 1.0, 0.7)
    max_tokens = st.slider(
        f"Max Output Tokens (max {default_ctx})", 
        1, 
        min(default_ctx, 128000), 
        min(512, default_ctx)
    )
    st.caption(f"🔢 Model Context Length (Fallback 4096): {default_ctx}")

    if st.button("🔄 Chat Reset"):
        st.session_state.messages = []
        st.success("Chat-Verlauf gelöscht.")
        st.experimental_rerun()

    st.markdown("""
    ---
    **Minimal UI:** Nur Text-Chat.
    """)

# --- Datei Upload Sektion komplett entfernt ---


# --- Chat Verlauf anzeigen ---
for msg in st.session_state.messages:
    with st.chat_message(msg["role"]):
        st.markdown(msg["content"])


# --- API Request Funktion (Unverändert) ---
def call_openrouter(model, messages, temp, max_tok, key):
    headers = {
        "Authorization": f"Bearer {key}",
        "Content-Type": "application/json",
        "Referer": "https://aicodecraft.io",
        "X-Title": "OpenRouter-Minimal-Interface",
    }
    payload = {
        "model": model,
        "messages": messages,
        "temperature": temp,
        "max_tokens": max_tok,
    }
    
    res = requests.post(f"{OPENROUTER_API_BASE}/chat/completions", headers=headers, data=json.dumps(payload))
    
    if res.status_code == 200:
        try:
            return res.json()["choices"][0]["message"]["content"]
        except (KeyError, IndexError):
            raise Exception("Fehlerhafte API-Antwort: Konnte Antworttext nicht extrahieren.")
    else:
        try:
            err = res.json()
            msg = err.get("error", {}).get("message", res.text)
        except:
            msg = res.text
        raise Exception(f"API Error {res.status_code}: {msg}")


# --- Chat Input ---
if prompt := st.chat_input("Deine Nachricht..."):
    if not api_key:
        st.warning("Bitte trage deinen OpenRouter API Key in der Sidebar ein.")
        st.stop()

    # Nachricht hinzufügen und anzeigen
    st.session_state.messages.append({"role": "user", "content": prompt})
    with st.chat_message("user"):
        st.markdown(prompt)

    # API Nachrichten vorbereiten (für Chatverlauf)
    messages = [{"role": m["role"], "content": m["content"]} for m in st.session_state.messages]

    # Antwort generieren
    with st.chat_message("assistant"):
        with st.spinner(f"Fragend {model}..."):
            try:
                reply = call_openrouter(model, messages, temperature, max_tokens, api_key)
                st.markdown(reply)
                st.session_state.messages.append({"role": "assistant", "content": reply})
            except Exception as e:
                st.error(str(e))
                st.session_state.messages.append({"role": "assistant", "content": f"❌ {str(e)}"})