import json
import requests
import streamlit as st
import pycafe
st.set_page_config(page_title="Gemini Chat", layout="centered")
st.title("Gemini Chat (py.cafe + REST)")
# --- API Key via py.cafe Vault ---
API_KEY = pycafe.get_secret("GEMINI_API_KEY")
if not API_KEY:
st.error("GEMINI_API_KEY fehlt im py.cafe Tresor.")
st.stop()
MODEL = "gemini-2.5-flash"
URL = f"https://generativelanguage.googleapis.com/v1beta/models/{MODEL}:generateContent"
# --- Session State (Chat History) ---
if "messages" not in st.session_state:
st.session_state.messages = []
if "last_incomplete" not in st.session_state:
st.session_state.last_incomplete = False
def looks_incomplete(text: str) -> bool:
t = (text or "").rstrip()
if len(t) < 40:
return False
if t.endswith((".", "!", "?", "…", "”", '"', "```")):
return False
return True
# --- Sidebar Controls ---
with st.sidebar:
st.header("Einstellungen")
temperature = st.slider("Temperature", 0.0, 1.0, 0.4, 0.05)
max_tokens = st.slider("Max Output Tokens", 128, 10000, 2048, 128)
request_timeout = st.slider("HTTP Timeout (s)", 20, 180, 90, 10)
system_prompt = st.text_area(
"System Prompt",
value="Antworte präzise auf Deutsch. Wenn du unsicher bist, sage das explizit.",
height=120,
)
if st.button("Chat leeren"):
st.session_state.messages = []
st.session_state.last_incomplete = False
st.rerun()
# --- Render Chat ---
for m in st.session_state.messages:
role = "user" if m["role"] == "user" else "assistant"
with st.chat_message(role):
st.markdown(m["text"])
def build_contents():
contents = []
if system_prompt.strip():
contents.append({
"role": "user",
"parts": [{"text": f"[System]\n{system_prompt.strip()}"}],
})
for m in st.session_state.messages:
role = "user" if m["role"] == "user" else "model"
contents.append({
"role": role,
"parts": [{"text": m["text"]}],
})
return contents
def gemini_generate(user_text: str) -> str:
contents = build_contents()
contents.append({"role": "user", "parts": [{"text": user_text}]})
payload = {
"contents": contents,
"generationConfig": {
"temperature": float(temperature),
"maxOutputTokens": int(max_tokens),
},
}
r = requests.post(
URL,
params={"key": API_KEY},
headers={"Content-Type": "application/json"},
data=json.dumps(payload),
timeout=int(request_timeout),
)
if r.status_code != 200:
st.session_state.last_incomplete = False
return f"API-Fehler {r.status_code}: {r.text}"
data = r.json()
try:
answer = data["candidates"][0]["content"]["parts"][0]["text"].strip()
st.session_state.last_incomplete = looks_incomplete(answer)
return answer
except Exception:
st.session_state.last_incomplete = False
return f"Unerwartetes Antwortformat: {data}"
# --- Input ---
prompt = st.chat_input("Nachricht an Gemini…")
if prompt:
st.session_state.messages.append({"role": "user", "text": prompt})
with st.chat_message("user"):
st.markdown(prompt)
with st.chat_message("assistant"):
with st.spinner("Gemini antwortet…"):
answer = gemini_generate(prompt)
st.markdown(answer)
st.session_state.messages.append({"role": "assistant", "text": answer})
# --- Continue UI if truncated ---
if st.session_state.get("last_incomplete"):
st.info("Antwort wirkt abgeschnitten. Klicke „Weiter“, um exakt fortzusetzen.")
if st.button("Weiter"):
st.session_state.messages.append({
"role": "user",
"text": "Fahre exakt dort fort, wo du aufgehört hast. Wiederhole nichts. Setze den Satz fort."
})
st.rerun()