Tongyu-Yan commited on
Commit
fe96f48
·
1 Parent(s): 42e6d80
Files changed (2) hide show
  1. app.py +121 -90
  2. requirements.txt +2 -0
app.py CHANGED
@@ -1,117 +1,148 @@
 
1
  import openai
 
 
2
  import requests
3
  import json
4
  import os
 
5
  import gradio as gr
 
 
6
 
7
- # Set your ModelScope API credentials and base URL
8
- openai.api_key = 'cf707b08-a58a-4683-aba5-5fa0f0bea8fe'
9
- openai.api_base = 'https://api-inference.modelscope.cn/v1/'
10
-
11
- def chat_with_model(conv_history, user_message, image_paths=None):
12
  """
13
- ModelScope Qwen2.5-VL-72B-Instruct 模型交互,处理用户消息和可选的图像。
14
-
15
- 参数:
16
- - conv_history: 对话历史记录的列表。
17
- - user_message: 用户的最新消息。
18
- - image_paths: 上传的图像文件路径列表(如果有)。
19
-
20
- 返回:
21
- - 更新后的对话显示内容(Markdown 格式)。
22
- - 空字符串(用于清空文本输入框)。
23
- - 更新后的对话历史记录。
24
- - 清空后的图像组件(空列表,用于删除网页上的图片)。
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
25
  """
 
 
 
 
26
 
27
- # 系统提示,指导助手的行为
28
  system_prompt = {
29
  'role': 'system',
30
  'content': (
31
- "你是一个乐于助人的助手,帮助辅导学生。老师上传了一张学生解答问题的图片。"
32
- "如果学生的解答有错误,请纠正。如果解答正确,请说'解答正确'。"
33
- "如果解答有误,请提供正确的解答,并仔细解释学生哪里出错了。"
 
 
 
34
  )
35
  }
36
-
37
- # 构建消息历史记录
38
- messages = [system_prompt]
39
- if conv_history:
40
- for user, assistant in conv_history:
41
- messages.append({'role': 'user', 'content': user})
42
- messages.append({'role': 'assistant', 'content': assistant})
43
- messages.append({'role': 'user', 'content': user_message})
44
-
45
- # 准备 API 请求的有效载荷
46
- payload = {
47
- 'model': 'Qwen/Qwen2.5-VL-72B-Instruct',
48
- 'messages': messages
49
  }
50
-
51
- # 如果提供了图像,处理图像上传(支持多个图像)
52
- files = None
53
- if image_paths:
54
- if isinstance(image_paths, list):
55
- files = []
56
- for path in image_paths:
57
- with open(path, "rb") as f:
58
- image_bytes = f.read()
59
- files.append(("file", (os.path.basename(path), image_bytes, "application/octet-stream")))
60
- else:
61
- with open(image_paths, "rb") as f:
62
- image_bytes = f.read()
63
- files = {
64
- "file": (os.path.basename(image_paths), image_bytes, "application/octet-stream")
65
- }
66
-
67
- # ModelScope API 发送请求
68
- response = requests.post(
69
- openai.api_base + 'chat/completions',
70
- headers={
71
- 'Authorization': f'Bearer {openai.api_key}',
72
- 'Content-Type': 'application/json'
73
- },
74
- json=payload,
75
- files=files
 
 
 
 
 
76
  )
77
 
78
- # 处理 API 响应
79
- if response.status_code != 200:
80
- assistant_response = f"**错误:** {response.status_code} - {response.text}"
81
- else:
82
- assistant_response = response.json().get("choices", [{}])[0].get("message", {}).get("content", "API 未返回输出。")
83
 
84
- # 更新对话历史记录
85
- conv_history.append((user_message, assistant_response))
 
86
 
87
- # 构建整个对话的 Markdown 显示内容
88
  conversation_display = ""
89
- for user, assistant in conv_history:
90
- conversation_display += f"**用户:** {user}\n\n**助手:**\n{assistant}\n\n---\n\n"
91
-
92
- # 返回对话显示、清空文本输入框、更新状态以及清空文件上传组件(图片)
93
- return conversation_display, "", conv_history, []
94
-
95
- # 定义 Gradio 界面
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
96
  with gr.Blocks() as demo:
97
- gr.Markdown("# Qwen2.5-VL-72B-Instruct 模型的多轮对话")
98
-
99
- # 显示对话的 Markdown 组件
100
- conversation_md = gr.Markdown(value="*对话内容将显示在这里...*")
101
- # 保存对话历史记录的状态
102
- state = gr.State([])
103
 
104
  with gr.Row():
105
- txt = gr.Textbox(label="您的消息", placeholder="输入您的消息...", lines=2)
106
- # 使用 gr.File 组件接受多个文件上传
107
- img = gr.File(label="上传图片(可选)", file_count="multiple", type="filepath")
108
-
109
- btn = gr.Button("发送")
110
- btn.click(
111
  chat_with_model,
112
- inputs=[state, txt, img],
113
- outputs=[conversation_md, txt, state, img]
114
  )
115
 
116
- # 启动 Gradio 界面
117
  demo.launch(share=True)
 
1
+ from openai import OpenAI
2
  import openai
3
+ import base64
4
+ import PIL.Image as Image
5
  import requests
6
  import json
7
  import os
8
+ import pillow_heif
9
  import gradio as gr
10
+ import tempfile
11
+ import shutil
12
 
13
+ def load_image(image_path):
 
 
 
 
14
  """
15
+ Process a local image: if it's in HEIC format, convert it to JPG,
16
+ then return a base64 encoded data URL string.
17
+ """
18
+ if image_path.lower().endswith(".heic"):
19
+ heif_file = pillow_heif.open_heif(image_path)
20
+ image = Image.frombytes(heif_file.mode, heif_file.size, heif_file.data)
21
+ jpg_path = image_path.rsplit(".", 1)[0] + ".jpg"
22
+ image.save(jpg_path, "JPEG")
23
+ image_path = jpg_path
24
+
25
+ with open(image_path, "rb") as image_file:
26
+ image_data = image_file.read()
27
+ base64_encoded = base64.b64encode(image_data).decode("utf-8")
28
+ mime_type = Image.open(image_path).get_format_mimetype()
29
+ return f"data:{mime_type};base64,{base64_encoded}"
30
+
31
+ def chat_with_model(conv_history, user_message, image_files):
32
+ """
33
+ Sends a conversation turn to the ModelScope Qwen2.5-VL-72B-Instruct model.
34
+ Supports multiple images by:
35
+ 1. Copying each uploaded file to a temporary directory.
36
+ 2. Processing it (converting HEIC to JPG if necessary).
37
+ 3. Deleting the temporary file after inference.
38
+
39
+ Returns:
40
+ - A Markdown-formatted conversation display.
41
+ - An empty string to clear the text input.
42
+ - An empty list to clear the file input.
43
+ - The updated conversation history.
44
  """
45
+ client = OpenAI(
46
+ base_url='https://api-inference.modelscope.cn/v1/',
47
+ api_key='cf707b08-a58a-4683-aba5-5fa0f0bea8fe'
48
+ )
49
 
 
50
  system_prompt = {
51
  'role': 'system',
52
  'content': (
53
+ "You are a helpful assistant helping a student with their homework. "
54
+ "The teacher has uploaded a picture of a student's answer to a question. "
55
+ "If the student's answer is incorrect, please correct it. "
56
+ "If the answer is correct, say 'correct answer'. "
57
+ "if the question is not answered, solve the question with explaining your step\
58
+ clearly and easy to understand."
59
  )
60
  }
61
+
62
+ # Build a new user message with text and attached images.
63
+ new_message = {
64
+ 'role': 'user',
65
+ 'content': [{'type': 'text', 'text': user_message}]
 
 
 
 
 
 
 
 
66
  }
67
+
68
+ temp_files = []
69
+ imgs = []
70
+ if image_files is not None:
71
+ for file_path in image_files:
72
+ # Copy the file to a temporary directory.
73
+ temp_dir = tempfile.gettempdir()
74
+ filename = os.path.basename(file_path)
75
+ temp_path = os.path.join(temp_dir, filename)
76
+ shutil.copy(file_path, temp_path)
77
+ temp_files.append(temp_path)
78
+ # Process the image (HEIC conversion if needed).
79
+ data_url = load_image(temp_path)
80
+ imgs.append(data_url)
81
+
82
+ # For each processed image, add a separate content block.
83
+ if imgs:
84
+ for img in imgs:
85
+ new_message['content'].append({
86
+ 'type': 'image_url',
87
+ 'image_url': img
88
+ })
89
+
90
+ # Combine conversation history with the new message.
91
+ # conv_history should be a list of prior messages (each a dict).
92
+ all_messages = [system_prompt] + conv_history + [new_message]
93
+
94
+ response = client.chat.completions.create(
95
+ model='Qwen/Qwen2.5-VL-72B-Instruct',
96
+ messages=all_messages,
97
+ stream=False
98
  )
99
 
100
+ assistant_response = response.choices[0].message.content
 
 
 
 
101
 
102
+ # Update the conversation history with the new turn.
103
+ conv_history.append(new_message)
104
+ conv_history.append({'role': 'assistant', 'content': assistant_response})
105
 
106
+ # Build Markdown conversation display.
107
  conversation_display = ""
108
+ for msg in conv_history:
109
+ if msg['role'] == 'user':
110
+ conversation_display += f"**User:** {msg['content'][0]['text']}\n"
111
+ # Indicate image uploads if any.
112
+ if len(msg['content']) > 1:
113
+ conversation_display += "**User Uploaded Images:** " + " ".join("[Image]" for _ in range(len(msg['content']) - 1)) + "\n"
114
+ elif msg['role'] == 'assistant':
115
+ conversation_display += f"**Assistant:** {msg['content']}\n"
116
+ conversation_display += "\n---\n\n"
117
+
118
+ # Delete all temporary files.
119
+ for path in temp_files:
120
+ if os.path.exists(path):
121
+ os.remove(path)
122
+
123
+ # Return outputs:
124
+ # - The conversation display.
125
+ # - An empty string to clear the text box.
126
+ # - An empty list to clear the file upload widget.
127
+ # - The updated conversation history.
128
+ return conversation_display, "", [], conv_history
129
+
130
+ # Build the Gradio UI.
131
  with gr.Blocks() as demo:
132
+ gr.Markdown("# Multi-turn, Multi-image Conversation with Qwen2.5-VL-72B-Instruct")
133
+ conversation_md = gr.Markdown(value="Conversation will be displayed here...")
134
+ state = gr.State([]) # This state stores conversation history.
 
 
 
135
 
136
  with gr.Row():
137
+ text_input = gr.Textbox(label="Your Message", placeholder="Enter your message...", lines=2)
138
+ # Use type 'filepath' for file uploads.
139
+ file_input = gr.File(label="Upload Images (Optional)", file_count="multiple", type="filepath")
140
+ send_button = gr.Button("Send")
141
+
142
+ send_button.click(
143
  chat_with_model,
144
+ inputs=[state, text_input, file_input],
145
+ outputs=[conversation_md, text_input, file_input, state]
146
  )
147
 
 
148
  demo.launch(share=True)
requirements.txt CHANGED
@@ -1,3 +1,5 @@
1
  gradio
2
  openai
3
  requests
 
 
 
1
  gradio
2
  openai
3
  requests
4
+ pillow
5
+ pillow-heif