import os import base64 import requests from typing import Dict import gradio as gr from huggingface_hub import InferenceClient # ----------------------------- # GitHub Prompt Configuration # ----------------------------- GITHUB_OWNER = "Tony14100" GITHUB_REPO = "Prompt-Tonic" GITHUB_REF = "main" GITHUB_PATH = "OzonConsultant" GITHUB_API_URL = ( f"https://api.github.com/repos/{GITHUB_OWNER}/{GITHUB_REPO}/contents/{GITHUB_PATH}?ref={GITHUB_REF}" ) LOCAL_PROMPT_FILE = "prompt_cache.txt" LOCAL_SHA_FILE = "prompt_cache_sha.txt" # ----------------------------- # Load system prompt from GitHub # ----------------------------- def load_system_prompt(): github_token = os.getenv("GITHUB_TOKEN") if not github_token: return "Вы — полезный консультант по Ozon. Отвечайте четко и профессионально." try: headers = {"Authorization": f"token {github_token}", "User-Agent": "Gradio-App"} response = requests.get(GITHUB_API_URL, headers=headers, timeout=10) response.raise_for_status() data = response.json() current_sha = data["sha"] cached_sha = None if os.path.exists(LOCAL_SHA_FILE): with open(LOCAL_SHA_FILE, "r") as f: cached_sha = f.read().strip() if cached_sha == current_sha and os.path.exists(LOCAL_PROMPT_FILE): with open(LOCAL_PROMPT_FILE, "r", encoding="utf-8") as f: return f.read().strip() content_b64 = data["content"] prompt = base64.b64decode(content_b64).decode("utf-8").strip() with open(LOCAL_PROMPT_FILE, "w", encoding="utf-8") as f: f.write(prompt) with open(LOCAL_SHA_FILE, "w") as f: f.write(current_sha) return prompt except: return "Вы — полезный консультант по Ozon. Отвечайте четко и профессионально." SYSTEM_PROMPT = load_system_prompt() # ----------------------------- # HF Client # ----------------------------- MODEL_ID = "zai-org/GLM-4.5" MAX_TOKENS = 1024 def get_client() -> InferenceClient: token = os.getenv("HF_TOKEN") or os.getenv("HF_API_KEY") if not token: raise RuntimeError("HF_TOKEN не найден!") return InferenceClient(token=token) # ----------------------------- # State management # ----------------------------- def reset_state(): return {"messages": [], "system_used": False} def mock_predict(user_message: str): return f"[Mock reply] Ты написал: {user_message}" def on_user_message(user_message: str, state: Dict): if not user_message.strip(): return [], state, gr.update(value="") messages = state["messages"] if not state["system_used"]: messages.insert(0, {"role": "system", "content": SYSTEM_PROMPT}) state["system_used"] = True messages.append({"role": "user", "content": user_message}) try: client = get_client() response = client.chat_completion( model=MODEL_ID, messages=messages, max_tokens=MAX_TOKENS, temperature=0.5, stream=False, ) assistant_reply = response.choices[0].message["content"].strip() except: assistant_reply = mock_predict(user_message) messages.append({"role": "assistant", "content": assistant_reply}) state["messages"] = messages chat_history = [] for msg in messages: if msg["role"] != "system": html = ( f'
' f'' f'{msg["content"]}
' ) chat_history.append({"role": msg["role"], "content": html}) return chat_history, state, gr.update(value="") # ----------------------------- # Build UI # ----------------------------- def build_ui(): css = """ body {background-color:#000; color:#00aaff;} .chat-container {display:flex; flex-direction:row; gap:20px; height:600px;} .chat-box { flex:1; overflow-y:auto; padding:10px; border:1px solid #00aaff; border-radius:5px; background:#000; height:420px; } .ads-box { flex:1; overflow-y:auto; background:#111; border-radius:5px; padding:10px; border:1px solid #00aaff; display:flex; flex-direction:column; gap:15px; } .ad-item { background:#000; border:1px solid #00aaff; border-radius:6px; padding:10px; text-align:center; color:#00aaff; font-size:14px; } .ad-item img { width:100%; border-radius:6px; margin-bottom:5px; } .input-styled textarea { background:#000 !important; color:#00aaff !important; border:1px solid #00aaff !important; border-radius:5px !important; padding:10px !important; font-size:14px !important; font-family:Arial !important; } """ autoscroll_js = """ """ # ---- РЕКЛАМНЫЕ БЛОКИ ---- ad_html = """
Здесь может быть ваша реклама
Здесь может быть ваша реклама
Здесь может быть ваша реклама
Здесь может быть ваша реклама
""" initial_bot_message = ( "🔥 Я твой эксперт по продажам на Ozon! Готов разобрать карточку, проверить трафик и подсказать стратегию. С чего начинаем?" ) chat_history_initial = [ { "role": "assistant", "content": f'
{initial_bot_message}
', } ] with gr.Blocks() as app: gr.HTML(f"") gr.HTML(autoscroll_js) with gr.Row(elem_classes="chat-container"): # ----------- ЧАТ ----------- with gr.Column(scale=1): chat = gr.Chatbot(value=chat_history_initial, elem_classes="chat-box") input_box = gr.Textbox( placeholder="Введите сообщение…", label="Сообщение", elem_classes="input-styled", lines=1, ) state = gr.State(reset_state()) input_box.submit( on_user_message, inputs=[input_box, state], outputs=[chat, state, input_box], ) gr.Button("Очистить чат").click( lambda: (chat_history_initial, reset_state(), gr.update(value="")), None, [chat, state, input_box], ) # ---------- РЕКЛАМНЫЕ БЛОКИ ---------- with gr.Column(scale=1): gr.HTML(ad_html, elem_classes="ads-box") return app app = build_ui() if __name__ == "__main__": app.queue(max_size=5) app.launch(server_name="0.0.0.0", server_port=7860, debug=True, share=False)