Zenithwang commited on
Commit
fe0c240
·
verified ·
1 Parent(s): 40bde0c

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +43 -66
app.py CHANGED
@@ -1,33 +1,29 @@
1
  import os
2
  import io
3
  import base64
4
- from typing import Optional
5
 
6
- import httpx
7
  import gradio as gr
8
  from PIL import Image
 
9
  from fastapi import FastAPI
10
 
11
- # --------- 基本配置 ---------
12
  STEPFUN_ENDPOINT = os.getenv("STEPFUN_ENDPOINT", "https://api.stepfun.com/v1")
13
  MODEL_NAME = os.getenv("MODEL_NAME", "step-3")
14
- TITLE = "StepFun · step-3 图片问答 Demo"
15
- DESC = "上传一张图片,问一个问题;后台通过 StepFun OpenAI 兼容接口完成图文对话。"
16
- FOOTER = "提示:在 HF Spaces 的 Settings -> Variables 里设置 OPENAI_API_KEY 或 STEPFUN_KEY"
17
- # ---------------------------
18
 
19
 
20
- def _get_api_key() -> Optional[str]:
21
  """
22
- 从环境变量里取 API Key。
23
- 优先 OPENAI_API_KEY(OpenAI 兼容接口的常用名),否则退回 STEPFUN_KEY。
24
  """
25
  return os.getenv("OPENAI_API_KEY") or os.getenv("STEPFUN_KEY")
26
 
27
 
28
  def _pil_to_data_url(img: Image.Image, fmt: str = "PNG") -> str:
29
  """
30
- PIL.Image -> data:image/...;base64,... (OpenAI兼容的 image_url 需要)
31
  """
32
  buf = io.BytesIO()
33
  img.save(buf, format=fmt)
@@ -36,19 +32,19 @@ def _pil_to_data_url(img: Image.Image, fmt: str = "PNG") -> str:
36
  return f"data:{mime};base64,{b64}"
37
 
38
 
39
- def _post_chat(messages: list, temperature: float = 0.7) -> str:
40
  """
41
- 直接用 httpx 调用 StepFun 的 /chat/completions 接口,返回文本。
42
  """
43
  key = _get_api_key()
44
  if not key:
45
- raise RuntimeError(
46
  "API Key 未设置。\n"
47
- "请在本地环境变量或 HF Spaces -> Settings -> Variables 里设置:\n"
48
- "OPENAI_API_KEY 或 STEPFUN_KEY(值为 StepFun API Key)。"
49
  )
50
 
51
- url = f"{STEPFUN_ENDPOINT.rstrip('/')}/chat/completions"
52
  headers = {
53
  "Authorization": f"Bearer {key}",
54
  "Content-Type": "application/json",
@@ -59,25 +55,27 @@ def _post_chat(messages: list, temperature: float = 0.7) -> str:
59
  "temperature": temperature,
60
  }
61
 
62
- # 简单超时与错误抛出
63
- resp = httpx.post(url, headers=headers, json=payload, timeout=60)
64
- resp.raise_for_status()
65
- data = resp.json()
66
- return data["choices"][0]["message"]["content"]
 
 
 
 
67
 
68
 
69
- def chat_with_step3(image: Optional[Image.Image], question: Optional[str]) -> str:
70
  """
71
- Gradio 回调函数:输入图片和问题,返回模型回答。
72
  """
73
  if image is None:
74
  return "请先上传图片。"
75
 
76
- q = (question or "").strip()
77
- if not q:
78
- q = "请描述这张图片的内容,并指出可能的菜品名称与做法要点。"
79
-
80
  data_url = _pil_to_data_url(image, fmt="PNG")
 
81
  messages = [
82
  {
83
  "role": "user",
@@ -87,49 +85,28 @@ def chat_with_step3(image: Optional[Image.Image], question: Optional[str]) -> st
87
  ],
88
  }
89
  ]
90
-
91
- try:
92
- return _post_chat(messages)
93
- except httpx.HTTPStatusError as e:
94
- # 返回后端具体错误信息,便于排障
95
- try:
96
- detail = e.response.json()
97
- except Exception:
98
- detail = e.response.text
99
- return f"后端返回错误:HTTP {e.response.status_code}\n{detail}"
100
- except Exception as e:
101
- return f"调用失败:{repr(e)}"
102
 
103
 
104
- # --------- 构建 Gradio 界面 ---------
105
- with gr.Blocks(title=TITLE, analytics_enabled=False) as demo:
106
- gr.Markdown(f"## {TITLE}")
107
- gr.Markdown(DESC)
108
-
109
  with gr.Row():
 
110
  with gr.Column():
111
- img_in = gr.Image(type="pil", label="上传图片")
112
- txt_in = gr.Textbox(
113
- label="问题(可留空)",
114
- placeholder="例如:这���什么菜?做法是怎样的?",
115
- )
116
  btn = gr.Button("提交")
117
- with gr.Column():
118
- out = gr.Textbox(label="回答", lines=12)
119
-
120
- btn.click(fn=chat_with_step3, inputs=[img_in, txt_in], outputs=out)
121
-
122
- gr.Markdown(f"<small>{FOOTER}</small>")
123
-
124
- # 让 HF Spaces 识别到 FastAPI/ASGI 应用
125
- app = FastAPI()
126
- # 不使用自定义路径参数,直接挂载到根路径
127
- app = gr.mount_gradio_app(app, demo, path="/")
128
 
 
 
 
129
 
130
- # --------- 本地调试专用(Spaces 环境不会执行)---------
131
- if __name__ == "__main__" and os.environ.get("SPACE_BUILD") is None:
132
- import uvicorn
133
 
134
- port = int(os.getenv("PORT", "7860"))
135
- uvicorn.run(app, host="0.0.0.0", port=port)
 
 
 
 
1
  import os
2
  import io
3
  import base64
4
+ from typing import List, Dict, Any
5
 
 
6
  import gradio as gr
7
  from PIL import Image
8
+ import httpx
9
  from fastapi import FastAPI
10
 
11
+ # ================== 配置 ==================
12
  STEPFUN_ENDPOINT = os.getenv("STEPFUN_ENDPOINT", "https://api.stepfun.com/v1")
13
  MODEL_NAME = os.getenv("MODEL_NAME", "step-3")
14
+ # =========================================
 
 
 
15
 
16
 
17
+ def _get_api_key() -> str | None:
18
  """
19
+ 优先读 OPENAI_API_KEY(兼容 OpenAI SDK 约定),其次读 STEPFUN_KEY
 
20
  """
21
  return os.getenv("OPENAI_API_KEY") or os.getenv("STEPFUN_KEY")
22
 
23
 
24
  def _pil_to_data_url(img: Image.Image, fmt: str = "PNG") -> str:
25
  """
26
+ PIL.Image -> data:image/...;base64,xxxxx
27
  """
28
  buf = io.BytesIO()
29
  img.save(buf, format=fmt)
 
32
  return f"data:{mime};base64,{b64}"
33
 
34
 
35
+ def _post_chat(messages: List[Dict[str, Any]], temperature: float = 0.7) -> str:
36
  """
37
+ 直连 StepFun 的 OpenAI 兼容接口:/chat/completions
38
  """
39
  key = _get_api_key()
40
  if not key:
41
+ return (
42
  "API Key 未设置。\n"
43
+ "请在 Space Settings Variables and secrets 中添加:\n"
44
+ "OPENAI_API_KEY=<你的 StepFun API Key>(或设置 STEPFUN_KEY 也可)"
45
  )
46
 
47
+ url = f"{STEPFUN_ENDPOINT}/chat/completions"
48
  headers = {
49
  "Authorization": f"Bearer {key}",
50
  "Content-Type": "application/json",
 
55
  "temperature": temperature,
56
  }
57
 
58
+ try:
59
+ resp = httpx.post(url, headers=headers, json=payload, timeout=60)
60
+ resp.raise_for_status()
61
+ data = resp.json()
62
+ return data["choices"][0]["message"]["content"]
63
+ except httpx.HTTPError as e:
64
+ return f"调用失败(HTTP):{e}"
65
+ except Exception as e:
66
+ return f"调用失败:{e}"
67
 
68
 
69
+ def chat_fn(image: Image.Image, question: str) -> str:
70
  """
71
+ Gradio 回调:上传图片 + 文本问题 → 模型答案
72
  """
73
  if image is None:
74
  return "请先上传图片。"
75
 
76
+ q = question.strip() or "请描述这张图片。"
 
 
 
77
  data_url = _pil_to_data_url(image, fmt="PNG")
78
+
79
  messages = [
80
  {
81
  "role": "user",
 
85
  ],
86
  }
87
  ]
88
+ return _post_chat(messages)
 
 
 
 
 
 
 
 
 
 
 
89
 
90
 
91
+ with gr.Blocks(title="Step-3 · 图片问答") as demo:
92
+ gr.Markdown("## Step-3 图片问答\n上传一张图,随便问。")
 
 
 
93
  with gr.Row():
94
+ img = gr.Image(type="pil", label="上传图片")
95
  with gr.Column():
96
+ q = gr.Textbox(label="问题", placeholder="比如:这是什么菜?怎么做?")
 
 
 
 
97
  btn = gr.Button("提交")
98
+ out = gr.Textbox(label="答案", lines=8)
 
 
 
 
 
 
 
 
 
 
99
 
100
+ btn.click(fn=chat_fn, inputs=[img, q], outputs=[out])
101
+ # 也支持回车触发
102
+ q.submit(fn=chat_fn, inputs=[img, q], outputs=[out])
103
 
104
+ # ---- FastAPI + 挂载 Gradio(给 HF Spaces 用)----
105
+ _fastapi = FastAPI()
106
+ app = gr.mount_gradio_app(_fastapi, demo, path="/")
107
 
108
+ # ---- 本地调试:只有在非 HF Spaces 才会启动 ----
109
+ if __name__ == "__main__" and os.environ.get("SYSTEM") != "spaces":
110
+ # 本地跑:python app.py
111
+ # 不要在 HF Spaces 起 uvicorn,否则会端口冲突
112
+ demo.queue(max_size=32).launch(server_name="0.0.0.0", server_port=int(os.getenv("PORT", "7860")))