cpschat / app.py
yogies's picture
Update app.py
c818a14 verified
raw
history blame
13.2 kB
import gradio as gr
import os
import json
from datetime import datetime
from openai import OpenAI
# ----------------------------------------------------------------------
# Helper to read secrets from the HF Space environment
# ----------------------------------------------------------------------
def _secret(key: str, fallback: str = None) -> str:
val = os.getenv(key)
if val is not None:
return val
if fallback is not None:
return fallback
raise RuntimeError(f"Secret '{key}' not found. Please add it to your Space secrets.")
# ----------------------------------------------------------------------
# User Management
# ----------------------------------------------------------------------
def load_users():
users = {}
users_json = _secret("CHAT_USERS", "{}")
try:
users_data = json.loads(users_json)
for username, password in users_data.items():
users[username] = password
except:
pass
return users
VALID_USERS = load_users()
def authenticate_user(username, password):
return username in VALID_USERS and VALID_USERS[username] == password
def gradio_auth(username, password):
return authenticate_user(username, password)
# ----------------------------------------------------------------------
# Configuration
# ----------------------------------------------------------------------
MODELS = {
"Gpt-oss-20b": {
"provider": "openrouter",
"model_name": "@preset/precise-chat-2",
"api_url": "https://openrouter.ai/api/v1",
"translate":"no"
},
"Gpt-oss-120b": {
"provider": "openrouter",
"model_name": "@preset/precise-chat",
"api_url": "https://openrouter.ai/api/v1",
"translate":"no"
},
}
MODEL_NAMES = list(MODELS.keys())
# ----------------------------------------------------------------------
# Core Chat Logic
# ----------------------------------------------------------------------
def generate_response(history, system_message, max_tokens, selected_model):
if not history:
return history
try:
model_config = MODELS[selected_model]
provider = model_config["provider"]
if provider == "huggingface":
api_key = _secret("HF_TOKEN")
elif provider == "openrouter":
api_key = _secret("OPENROUTER_KEY")
client = OpenAI(base_url=model_config["api_url"], api_key=api_key)
translator_client = OpenAI(base_url="https://openrouter.ai/api/v1", api_key=_secret("OPENROUTER_KEY"))
if model_config.get("translate") == "yes":
final_system_message = "**HIGHEST PRIORITY: YOU MUST ALWAYS THINK AND RESPOND IN ENGLISH...**\n" + system_message
else:
final_system_message = system_message
messages = [{"role": "system", "content": final_system_message}]
api_history = [{"role": m["role"], "content": m["content"]} for m in history]
messages.extend(api_history)
if ((provider == 'huggingface') | (provider == 'openrouter')) :
response = client.chat.completions.create(
model=model_config["model_name"],
messages=messages,
max_tokens=max_tokens,
reasoning_effort="high",
stream=False,
)
english_response = response.choices[0].message.content
if model_config.get("translate") == "yes":
try:
translation_messages = [
{"role": "system", "content": "Translate..."},
{"role": "user", "content": english_response}
]
translation_response = translator_client.chat.completions.create(
model="google/gemma-3n-e4b-it:floor",
messages=translation_messages,
max_tokens=max_tokens,
stream=False,
)
final_response = translation_response.choices[0].message.content.strip()
if not final_response or len(final_response) < 10:
final_response = english_response
except Exception as trans_error:
print(f"Translation error: {trans_error}")
final_response = english_response
else:
final_response = english_response
history.append({"role": "assistant", "content": final_response})
return history
except Exception as e:
history.append({"role": "assistant", "content": f"Error: {str(e)}"})
return history
def user_input(user_message, history):
if not user_message:
return history, ""
history.append({"role": "user", "content": user_message})
return history, ""
# ----------------------------------------------------------------------
# CSS / Visual System ("Alabaster Clean")
# ----------------------------------------------------------------------
current_date_display = datetime.now().strftime("%B %d, %Y")
css = """
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500&family=Space+Grotesk:wght@400;600&display=swap');
/* 1. FORCE LIGHT MODE / RESET DARK THEME LEAKS */
:root, .dark, body, .gradio-container {
--body-background-fill: #FAFAFA !important;
--body-text-color: #444444 !important;
--background-fill-primary: #FFFFFF !important;
--background-fill-secondary: #FAFAFA !important;
--block-background-fill: #FFFFFF !important;
--block-label-text-color: #999999 !important;
--input-background-fill: rgba(255, 255, 255, 0.7) !important;
--bg-core: #FAFAFA;
--text-primary: #111111;
--text-body: #444444;
--text-dim: #999999;
--border-glass: rgba(0, 0, 0, 0.08);
--font-head: 'Space Grotesk', sans-serif;
--font-body: 'Inter', sans-serif;
}
/* Force background animation on body */
body {
background: radial-gradient(circle at 50% 50%, #FFFFFF 0%, #F2F2F4 100%) !important;
background-size: 150% 150% !important;
animation: daylight 25s ease-in-out infinite alternate;
color: var(--text-body) !important;
font-family: var(--font-body) !important;
}
@keyframes daylight {
0% { background-position: 40% 40%; }
100% { background-position: 60% 60%; }
}
/* 2. LAYOUT CENTERING */
.main-column {
max-width: 900px !important;
margin: 0 auto !important;
padding-top: 2rem !important;
}
/* 3. HEADER */
.liquid-header {
text-align: center;
margin-bottom: 2rem;
border-bottom: 1px solid var(--border-glass);
padding-bottom: 1.5rem;
}
.liquid-header h1 {
font-family: var(--font-head);
font-weight: 600;
font-size: 2rem;
color: var(--text-primary);
margin: 0;
}
.liquid-header .meta {
font-family: var(--font-body);
font-size: 0.85rem;
color: var(--text-dim);
margin-top: 0.5rem;
}
/* 4. CHATBOT (Clean Paper Look) */
.gradio-container .chatbot {
background: transparent !important;
border: none !important;
box-shadow: none !important;
height: 600px !important;
overflow-y: auto;
}
.message {
border-radius: 8px !important;
border: 1px solid var(--border-glass) !important;
padding: 1.2rem !important;
margin-bottom: 1rem !important;
box-shadow: 0 2px 6px rgba(0,0,0,0.03) !important;
}
/* User Message */
.message.user {
background-color: #F4F4F5 !important; /* Light Grey */
border-left: none !important;
justify-content: flex-end;
}
/* Bot Message */
.message.bot {
background-color: #FFFFFF !important; /* Pure White */
border-left: none !important;
}
.message * {
font-family: var(--font-body);
color: var(--text-body) !important;
font-size: 1rem !important;
line-height: 1.6 !important;
}
/* 5. INPUT BAR */
.command-bar textarea {
background: rgba(255, 255, 255, 0.85) !important;
backdrop-filter: blur(12px) !important;
-webkit-backdrop-filter: blur(12px) !important;
border: 1px solid var(--border-glass) !important;
border-radius: 12px !important;
box-shadow: 0 4px 20px rgba(0,0,0,0.05) !important;
color: var(--text-primary) !important;
font-family: var(--font-body) !important;
padding: 1rem !important;
font-size: 1rem !important;
}
.command-bar textarea:focus {
border-color: var(--text-dim) !important;
background: #FFFFFF !important;
box-shadow: 0 8px 30px rgba(0,0,0,0.08) !important;
}
/* 6. SETTINGS (Simple HUD) */
.hud-accordion {
background: transparent !important;
border: none !important;
margin-top: 1rem !important;
}
.hud-accordion .label-wrap {
font-family: var(--font-body) !important;
font-size: 0.85rem !important;
color: var(--text-dim) !important;
}
.hud-panel {
border-top: 1px solid var(--border-glass);
padding-top: 1rem;
}
/* Force white backgrounds on inputs inside settings to kill dark mode leaks */
.hud-panel input, .hud-panel textarea, .hud-panel .single-select, .hud-panel .wrap {
background-color: #FFFFFF !important;
color: #000000 !important;
border: 1px solid var(--border-glass) !important;
}
.hud-panel span {
color: var(--text-dim) !important;
}
/* 7. EXAMPLES */
.examples-container button {
background: #FFFFFF !important;
border: 1px solid var(--border-glass) !important;
color: var(--text-body) !important;
border-radius: 6px !important;
font-size: 0.9rem !important;
padding: 0.5rem 1rem !important;
margin: 4px !important;
}
.examples-container button:hover {
background-color: #F9FAFB !important;
border-color: var(--text-dim) !important;
}
footer { display: none !important; }
"""
header_html = f"""
<div class="liquid-header">
<h1>AI Workspace</h1>
<div class="meta">
{current_date_display} • Secure Environment
</div>
</div>
"""
# ----------------------------------------------------------------------
# Layout Construction
# ----------------------------------------------------------------------
with gr.Blocks(
title="AI Workspace",
theme=gr.themes.Soft(primary_hue="neutral", radius_size="none"),
css=css
) as demo:
with gr.Column(elem_classes="main-column"):
# 1. Header
gr.HTML(header_html)
# 2. Chat Area
chatbot = gr.Chatbot(
type="messages",
show_label=False,
avatar_images=None,
render_markdown=True,
elem_id="main-chatbot"
)
# 3. Input Bar
with gr.Row():
msg_input = gr.Textbox(
placeholder="Type a message...",
label=None,
show_label=False,
lines=1,
max_lines=4,
elem_classes="command-bar",
autofocus=True
)
# 4. Settings (Accordion below input)
with gr.Accordion("Settings", open=False, elem_classes="hud-accordion"):
with gr.Column(elem_classes="hud-panel"):
with gr.Row():
selected_model = gr.Dropdown(
choices=MODEL_NAMES,
value=MODEL_NAMES[0],
label="Select Model",
interactive=True,
scale=2
)
max_tokens = gr.Slider(
minimum=1, maximum=30000, value=4000, step=100,
label="Token Limit",
scale=1
)
with gr.Row():
system_message = gr.Textbox(
value="Anda adalah asisten AI. Jawab dalam Bahasa Indonesia, di bawah 100 kata. Berikan label yang jelas antara fakta dan inferensi.",
label="System Prompt",
lines=2,
max_lines=3
)
# 5. Examples
with gr.Column(elem_classes="examples-container"):
gr.Examples(
examples=[
["Jelaskan penggunaan King's Safety Stock dalam inventory management."],
["Rancang strategi optimasi layout toko retail menggunakan Market Basket Analysis."],
["Analisis fenomena 'Quiet Quitting' di startup teknologi Indonesia."],
],
inputs=[msg_input]
)
# ------------------------------------------------------------------
# Event Wiring
# ------------------------------------------------------------------
msg_input.submit(
user_input,
inputs=[msg_input, chatbot],
outputs=[chatbot, msg_input]
).then(
generate_response,
inputs=[chatbot, system_message, max_tokens, selected_model],
outputs=[chatbot]
)
if __name__ == "__main__":
demo.launch(
auth=gradio_auth,
auth_message="Please login to access the chat interface",
server_name="0.0.0.0",
ssr_mode=False,
server_port=7860,
show_error=True
)