samlax12 commited on
Commit
84d2f90
·
verified ·
1 Parent(s): e6f7ed0

Create app.py

Browse files
Files changed (1) hide show
  1. app.py +245 -0
app.py ADDED
@@ -0,0 +1,245 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # AIPPT 服务替换方案:修订版
2
+
3
+ 根据您提供的 PPT 结构示例和 AIPPT 基本原理文档,我发现之前提供的提示词确实需要调整,以确保生成的数据结构与 PPTist 期望的格式完全匹配。以下是修订后的 Flask 实现方案,重点调整了提示词部分:
4
+
5
+ ## 修订后的 Flask 实现代码
6
+
7
+ ```python
8
+ from flask import Flask, request, Response, stream_with_context
9
+ import requests
10
+ import os
11
+ import json
12
+
13
+ app = Flask(__name__)
14
+
15
+ # 从环境变量获取配置
16
+ OPENAI_API_KEY = os.environ.get('OPENAI_API_KEY')
17
+ OPENAI_API_URL = os.environ.get('OPENAI_API_URL', 'https://api.openai.com/v1/chat/completions')
18
+
19
+ # PPT 大纲生成接口
20
+ @app.route('/tools/aippt_outline', methods=['POST'])
21
+ def aippt_outline():
22
+ data = request.json
23
+ content = data.get('content')
24
+ language = data.get('language', 'zh')
25
+
26
+ # 构建发送给 OpenAI 的提示
27
+ messages = [
28
+ {
29
+ "role": "system",
30
+ "content": f"""您是一位专业的PPT大纲生成器。请为主题'{content}'创建一个详细的演示文稿大纲,使用{language}语言。
31
+
32
+ 大纲应当遵循以下结构:
33
+ # [主标题]
34
+ ## [一级章节标题]
35
+ ### [二级标题]
36
+ - [要点1]
37
+ - [要点2]
38
+ ...
39
+
40
+ 请确保大纲层次分明,内容全面且有深度。大纲应当包含3-6个一级章节,每个章节下有2-4个二级标题,每个二级标题包含2-4个要点。"""
41
+ }
42
+ ]
43
+
44
+ def generate():
45
+ headers = {
46
+ 'Content-Type': 'application/json',
47
+ 'Authorization': f'Bearer {OPENAI_API_KEY}'
48
+ }
49
+ payload = {
50
+ 'model': 'gpt-4o',
51
+ 'messages': messages,
52
+ 'stream': True
53
+ }
54
+
55
+ response = requests.post(
56
+ OPENAI_API_URL,
57
+ headers=headers,
58
+ json=payload,
59
+ stream=True
60
+ )
61
+
62
+ for line in response.iter_lines():
63
+ if line:
64
+ line = line.decode('utf-8')
65
+ if line.startswith('data: ') and line != 'data: [DONE]':
66
+ try:
67
+ data = json.loads(line[6:])
68
+ if data['choices'][0]['delta'].get('content'):
69
+ content = data['choices'][0]['delta']['content']
70
+ yield content
71
+ except Exception as e:
72
+ print(f"Error parsing OpenAI response: {e}")
73
+
74
+ return Response(stream_with_context(generate()), mimetype='text/event-stream')
75
+
76
+ # PPT 内容生成接口
77
+ @app.route('/tools/aippt', methods=['POST'])
78
+ def aippt():
79
+ data = request.json
80
+ content = data.get('content')
81
+ language = data.get('language', 'zh')
82
+
83
+ # 构建发送给 OpenAI 的提示,包含 PPTist 所需数据结构的详细说明
84
+ messages = [
85
+ {
86
+ "role": "system",
87
+ "content": f"""您是一位专业的PPT内容生成器。根据提供的大纲,生成符合AIPPT结构的JSON对象。每个幻灯片必须严格遵循以下格式,根据其类型包含适当的字段。
88
+
89
+ 以下是幻灯片类型及其必要字段格式:
90
+
91
+ 1. 封面页 (cover):
92
+ ```
93
+ {{
94
+ "type": "cover",
95
+ "data": {{
96
+ "title": "标题文本",
97
+ "text": "副标题或描述文本"
98
+ }}
99
+ }}
100
+ ```
101
+
102
+ 2. 目录页 (contents):
103
+ ```
104
+ {{
105
+ "type": "contents",
106
+ "data": {{
107
+ "items": ["目录项1", "目录项2", "目录项3", ...]
108
+ }}
109
+ }}
110
+ ```
111
+
112
+ 3. 过渡页 (transition):
113
+ ```
114
+ {{
115
+ "type": "transition",
116
+ "data": {{
117
+ "title": "章节标题",
118
+ "text": "章节描述文本"
119
+ }}
120
+ }}
121
+ ```
122
+
123
+ 4. 内容页 (content):
124
+ ```
125
+ {{
126
+ "type": "content",
127
+ "data": {{
128
+ "title": "页面标题",
129
+ "items": [
130
+ {{
131
+ "title": "内容项标题1",
132
+ "text": "内容项文本1"
133
+ }},
134
+ {{
135
+ "title": "内容项标题2",
136
+ "text": "内容项文本2"
137
+ }},
138
+ ...
139
+ ]
140
+ }}
141
+ }}
142
+ ```
143
+
144
+ 5. 结束页 (end):
145
+ ```
146
+ {{
147
+ "type": "end"
148
+ }}
149
+ ```
150
+
151
+ 请根据提供的大纲,按顺序生成以下幻灯片:
152
+ 1. 一个封面页,包含主题标题和简短描述
153
+ 2. 一个目录页,列出大纲中的所有一级章节
154
+ 3. 每个一级章节前添加一个过渡页
155
+ 4. 每个二级标题创建一个内容页,标题为二级标题名称,内容项为该标题下的要点
156
+ 5. 最后添加一个结束页
157
+
158
+ 每个幻灯片必须是一个完整有效的JSON对象,遵循上述格式。每个内容页应有2-4个内容项。
159
+
160
+ 请逐个输出每张幻灯片的JSON对象,每个对象单独成行,不要添加任何额外的文本说明。"""
161
+ },
162
+ {
163
+ "role": "user",
164
+ "content": f"根据这个大纲生成PPT内容:\n{content}"
165
+ }
166
+ ]
167
+
168
+ def generate():
169
+ headers = {
170
+ 'Content-Type': 'application/json',
171
+ 'Authorization': f'Bearer {OPENAI_API_KEY}'
172
+ }
173
+ payload = {
174
+ 'model': 'gpt-4o',
175
+ 'messages': messages,
176
+ 'stream': True
177
+ }
178
+
179
+ response = requests.post(
180
+ OPENAI_API_URL,
181
+ headers=headers,
182
+ json=payload,
183
+ stream=True
184
+ )
185
+
186
+ buffer = ""
187
+ for line in response.iter_lines():
188
+ if line:
189
+ line = line.decode('utf-8')
190
+ if line.startswith('data: ') and line != 'data: [DONE]':
191
+ try:
192
+ data = json.loads(line[6:])
193
+ if data['choices'][0]['delta'].get('content'):
194
+ buffer += data['choices'][0]['delta']['content']
195
+
196
+ # 尝试从缓冲区中提取完整的 JSON 对象
197
+ while True:
198
+ # 寻找可能的 JSON 对象开始和结束
199
+ json_start = buffer.find('{')
200
+ if json_start == -1:
201
+ break
202
+
203
+ # 寻找匹配的结束括号(处理嵌套JSON)
204
+ bracket_count = 0
205
+ json_end = -1
206
+
207
+ for i in range(json_start, len(buffer)):
208
+ if buffer[i] == '{':
209
+ bracket_count += 1
210
+ elif buffer[i] == '}':
211
+ bracket_count -= 1
212
+ if bracket_count == 0:
213
+ json_end = i
214
+ break
215
+
216
+ if json_end == -1:
217
+ # 没有找到匹配的结束括号,等待更多数据
218
+ break
219
+
220
+ json_str = buffer[json_start:json_end+1]
221
+
222
+ try:
223
+ # 验证是否为有效的 JSON
224
+ slide_data = json.loads(json_str)
225
+
226
+ # 如果是有效的JSON且包含type和data字段(为了安全起见)
227
+ if isinstance(slide_data, dict) and ('type' in slide_data or slide_data.get('type') == 'end'):
228
+ # 发送到客户端
229
+ yield json.dumps(slide_data) + '\n'
230
+ # 从缓冲区中移除已处理的部分
231
+ buffer = buffer[json_end+1:]
232
+ else:
233
+ # JSON格式不符合期望,从缓冲区删除并继续
234
+ buffer = buffer[json_end+1:]
235
+ except Exception as e:
236
+ # 解析错误,可能不是完整的JSON,继续等待
237
+ print(f"Error parsing JSON: {e}")
238
+ break
239
+ except Exception as e:
240
+ print(f"Error parsing OpenAI response: {e}")
241
+
242
+ return Response(stream_with_context(generate()), mimetype='text/event-stream')
243
+
244
+ if __name__ == '__main__':
245
+ app.run(debug=True, host='0.0.0.0', port=7860)