Spaces:
Running
Running
File size: 4,336 Bytes
6a6db8a e1d60fa 65038dc 6a6db8a 8987e34 6a6db8a e1d60fa 6a6db8a 61119fe e1d60fa 8987e34 e1d60fa 8987e34 e1d60fa 79cceb8 e1d60fa 79cceb8 e1d60fa 65038dc e1d60fa 6a6db8a e1d60fa 6a6db8a 8987e34 d3eab6a 6a6db8a d3eab6a 6a6db8a d3eab6a e1d60fa 6a6db8a e1d60fa 6a6db8a e1d60fa 6a6db8a e1d60fa 8987e34 6a6db8a aeb0af0 e1d60fa aeb0af0 6a6db8a 8987e34 e1d60fa 61119fe e1d60fa 6a6db8a e1d60fa 6a6db8a |
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 |
import os
import io
import base64
from typing import Optional
import httpx
import gradio as gr
from PIL import Image
from fastapi import FastAPI
# --------- 基本配置 ---------
STEPFUN_ENDPOINT = os.getenv("STEPFUN_ENDPOINT", "https://api.stepfun.com/v1")
MODEL_NAME = os.getenv("MODEL_NAME", "step-3")
TITLE = "StepFun · step-3 图片问答 Demo"
DESC = "上传一张图片,问一个问题;后台通过 StepFun OpenAI 兼容接口完成图文对话。"
FOOTER = "提示:在 HF Spaces 的 Settings -> Variables 里设置 OPENAI_API_KEY 或 STEPFUN_KEY"
# ---------------------------
def _get_api_key() -> Optional[str]:
"""
从环境变量里取 API Key。
优先 OPENAI_API_KEY(OpenAI 兼容接口的常用名),否则退回 STEPFUN_KEY。
"""
return os.getenv("OPENAI_API_KEY") or os.getenv("STEPFUN_KEY")
def _pil_to_data_url(img: Image.Image, fmt: str = "PNG") -> str:
"""
PIL.Image -> data:image/...;base64,... (OpenAI兼容的 image_url 需要)
"""
buf = io.BytesIO()
img.save(buf, format=fmt)
b64 = base64.b64encode(buf.getvalue()).decode("utf-8")
mime = "image/png" if fmt.upper() == "PNG" else "image/jpeg"
return f"data:{mime};base64,{b64}"
def _post_chat(messages: list, temperature: float = 0.7) -> str:
"""
直接用 httpx 调用 StepFun 的 /chat/completions 接口,返回文本。
"""
key = _get_api_key()
if not key:
raise RuntimeError(
"API Key 未设置。\n"
"请在本地环境变量或 HF Spaces -> Settings -> Variables 里设置:\n"
"OPENAI_API_KEY 或 STEPFUN_KEY(值为 StepFun API Key)。"
)
url = f"{STEPFUN_ENDPOINT.rstrip('/')}/chat/completions"
headers = {
"Authorization": f"Bearer {key}",
"Content-Type": "application/json",
}
payload = {
"model": MODEL_NAME,
"messages": messages,
"temperature": temperature,
}
# 简单超时与错误抛出
resp = httpx.post(url, headers=headers, json=payload, timeout=60)
resp.raise_for_status()
data = resp.json()
return data["choices"][0]["message"]["content"]
def chat_with_step3(image: Optional[Image.Image], question: Optional[str]) -> str:
"""
Gradio 回调函数:输入图片和问题,返回模型回答。
"""
if image is None:
return "请先上传图片。"
q = (question or "").strip()
if not q:
q = "请描述这张图片的内容,并指出可能的菜品名称与做法要点。"
data_url = _pil_to_data_url(image, fmt="PNG")
messages = [
{
"role": "user",
"content": [
{"type": "image_url", "image_url": {"url": data_url}},
{"type": "text", "text": q},
],
}
]
try:
return _post_chat(messages)
except httpx.HTTPStatusError as e:
# 返回后端具体错误信息,便于排障
try:
detail = e.response.json()
except Exception:
detail = e.response.text
return f"后端返回错误:HTTP {e.response.status_code}\n{detail}"
except Exception as e:
return f"调用失败:{repr(e)}"
# --------- 构建 Gradio 界面 ---------
with gr.Blocks(title=TITLE, analytics_enabled=False) as demo:
gr.Markdown(f"## {TITLE}")
gr.Markdown(DESC)
with gr.Row():
with gr.Column():
img_in = gr.Image(type="pil", label="上传图片")
txt_in = gr.Textbox(
label="问题(可留空)",
placeholder="例如:这是什么菜?做法是怎样的?",
)
btn = gr.Button("提交")
with gr.Column():
out = gr.Textbox(label="回答", lines=12)
btn.click(fn=chat_with_step3, inputs=[img_in, txt_in], outputs=out)
gr.Markdown(f"<small>{FOOTER}</small>")
# 让 HF Spaces 识别到 FastAPI/ASGI 应用
app = FastAPI()
# 不使用自定义路径参数,直接挂载到根路径
app = gr.mount_gradio_app(app, demo, path="/")
# --------- 本地调试专用(Spaces 环境不会执行)---------
if __name__ == "__main__" and os.environ.get("SPACE_BUILD") is None:
import uvicorn
port = int(os.getenv("PORT", "7860"))
uvicorn.run(app, host="0.0.0.0", port=port)
|