| import gradio as gr |
| import requests |
| import os |
| import base64 |
| OPENROUTER_API_KEY = os.environ["OPENROUTER_API_KEY"] |
|
|
|
|
| |
|
|
|
|
| |
| def load_knowledge(): |
| texts = [] |
| for fname in ["data/theory.txt", "data/notes.txt"]: |
| try: |
| with open(fname, "r", encoding="utf-8") as f: |
| texts.append(f.read()) |
| except: |
| pass |
| return "\n\n".join(texts) |
|
|
| SYSTEM_PROMPT = f""" |
| 你是一个站无产阶级立场,用马克思主义世界观和方法论,且基于以下材料进行回答的 AI,必须以这些材料为核心。 |
| |
| 注意: |
| - 笼统问题不超过 1000 字 |
| - 具体问题不超过 1500 字 |
| |
| 【参考资料】 |
| {load_knowledge()} |
| |
| 【回答风格要求】 |
| - 冷静、分析性、偏理论 |
| - 不自我免责声明 |
| - 依据马克思、列宁、毛泽东的理论或思想 |
| - 运用辩证唯物主义和历史唯物主义 |
| - 坚持政治先于经济,反对经济主义、唯生产力论 |
| |
| 【基本认识背景】 |
| - 1976 年后中国共产党彻底被官僚资产阶级掌权 |
| - 1978 年改革开放是资本主义复辟 |
| - 不存在"社会主义市场经济" |
| - 当代中国只能通过暴力革命和人民战争重新夺取政权 |
| |
| 【应用理论到现实】 |
| - 批判当代左翼的机会主义、小资产阶级化、审美化倾向 |
| """ |
|
|
| |
| def image_to_base64(image_path): |
| """将图片文件转换为base64编码""" |
| try: |
| with open(image_path, "rb") as img_file: |
| return base64.b64encode(img_file.read()).decode('utf-8') |
| except FileNotFoundError: |
| return None |
|
|
| |
| def chat(user_message): |
| headers = { |
| "Authorization": f"Bearer {OPENROUTER_API_KEY}", |
| "Content-Type": "application/json", |
| "HTTP-Referer": "https://huggingface.co", |
| "X-Title": "rotai" |
| } |
| |
| data = { |
| "model": "arcee-ai/trinity-large-preview:free", |
| "messages": [ |
| {"role": "system", "content": SYSTEM_PROMPT}, |
| {"role": "user", "content": user_message} |
| ] |
| } |
| |
| r = requests.post( |
| "https://openrouter.ai/api/v1/chat/completions", |
| headers=headers, |
| json=data, |
| timeout=60 |
| ) |
| |
| if r.status_code != 200: |
| return f"❌ OpenRouter 错误: {r.status_code}\n{r.text}" |
| |
| return r.json()["choices"][0]["message"]["content"] |
|
|
| |
| with gr.Blocks(css=""" |
| .avatar-wrapper { |
| position: relative; |
| width: 220px; |
| height: 220px; |
| margin: 0 auto 16px auto; |
| animation: float 4s ease-in-out infinite; |
| overflow: hidden; |
| } |
| |
| .avatar-wrapper img { |
| width: 100%; |
| height: 100%; |
| position: absolute; |
| top: 0; |
| left: 0; |
| border-radius: 16px; |
| } |
| |
| .blink { |
| animation: blink 5s infinite; |
| } |
| |
| @keyframes blink { |
| 0%, 93%, 100% { opacity: 0; } |
| 94%, 97% { opacity: 1; } |
| } |
| |
| @keyframes float { |
| 0% { transform: translateY(0px); } |
| 50% { transform: translateY(-6px); } |
| 100% { transform: translateY(0px); } |
| } |
| """) as demo: |
| |
| |
| avatar_open_b64 = image_to_base64("avatar_open.png") |
| avatar_close_b64 = image_to_base64("avatar_close.png") |
| |
| if avatar_open_b64 and avatar_close_b64: |
| gr.HTML(f""" |
| <div class="avatar-wrapper"> |
| <img src="data:image/png;base64,{avatar_open_b64}"> |
| <img src="data:image/png;base64,{avatar_close_b64}" class="blink"> |
| </div> |
| """) |
| else: |
| gr.HTML("<p style='text-align: center;'>⚠️ 图片文件未找到,请确保 avatar_open.png 和 avatar_close.png 在同一目录下</p>") |
| |
| |
| user_input = gr.Textbox( |
| label="请说,我会尽量帮助你的", |
| lines=3 |
| ) |
| |
| submit_btn = gr.Button("提交") |
| |
| output = gr.Textbox( |
| label="回复栏", |
| lines=12 |
| ) |
| |
| submit_btn.click( |
| fn=chat, |
| inputs=user_input, |
| outputs=output |
| ) |
|
|
| demo.launch() |
|
|
|
|
|
|