pplx / app.py
samlax12's picture
Update app.py
e71a2c6 verified
from flask import Flask, request, Response, stream_with_context
import requests
import os
import json
import urllib3
# 禁用 SSL 警告
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
app = Flask(__name__)
# 从环境变量获取配置
OPENAI_API_KEY = os.environ.get('OPENAI_API_KEY')
OPENAI_API_URL = os.environ.get('OPENAI_API_URL', 'https://api.openai.com/v1/chat/completions')
# PPT 大纲生成接口
@app.route('/tools/aippt_outline', methods=['POST'])
def aippt_outline():
data = request.json
content = data.get('content')
language = data.get('language', 'zh')
model = data.get('model', 'gpt-4o')
# 构建发送给 OpenAI 的提示
messages = [
{
"role": "system",
"content": f"""您是一位专业的PPT大纲生成器。请为主题'{content}'创建一个详细的演示文稿大纲,使用{language}语言。
您的大纲必须严格遵循以下格式,这是非常重要的:
# [主题标题]
## [一级章节标题]
### [二级标题]
- [要点]
- [要点]
...
具体要求:
1. 大纲必须以"# [主题标题]"开始
2. 包含4-6个一级章节,每个章节以"##"开头
3. 每个一级章节下包含2-3个二级标题,每个二级标题以"###"开头
4. 每个二级标题下包含2-4个简短要点,每个要点以"-"开头
5. 要点应简洁明了,每个要点不超过15个字
6. 章节标题和二级标题应该是名词短语,不要使用问句或长句
请确保大纲层次分明,内容全面且有深度。章节安排应当符合逻辑顺序,便于读者理解。"""
}
]
def generate():
headers = {
'Content-Type': 'application/json',
'Authorization': f'Bearer {OPENAI_API_KEY}'
}
payload = {
'model': model,
'messages': messages,
'stream': True
}
response = requests.post(
OPENAI_API_URL,
headers=headers,
json=payload,
stream=True,
verify=False # 禁用 SSL 验证
)
for line in response.iter_lines():
if line:
line = line.decode('utf-8')
if line.startswith('data: ') and line != 'data: [DONE]':
try:
data = json.loads(line[6:])
if data['choices'][0]['delta'].get('content'):
content = data['choices'][0]['delta']['content']
yield content
except Exception as e:
print(f"Error parsing OpenAI response: {e}")
return Response(stream_with_context(generate()), mimetype='text/event-stream')
# PPT 内容生成接口
@app.route('/tools/aippt', methods=['POST'])
def aippt():
data = request.json
content = data.get('content')
language = data.get('language', 'zh')
model = data.get('model', 'gpt-4.1-mini')
# 构建发送给 OpenAI 的提示,包含 PPTist 所需数据结构的详细说明
messages = [
{
"role": "system",
"content": f"""您是一位专业的PPT内容生成器。根据提供的大纲,您需要生成一系列JSON对象,每个对象代表一张幻灯片。请严格按照以下规则和格式进行生成:
1. 您需要生成这些类型的幻灯片:封面页(cover)、目录页(contents)、过渡页(transition)、内容页(content)和结束页(end)
2. 必须严格遵循以下JSON格式,任何格式错误都会导致幻灯片无法显示:
封面页:
{{"type":"cover","data":{{"title":"主标题","text":"副标题或简短描述"}}}}
目录页:
{{"type":"contents","data":{{"items":["一级章节1","一级章节2","一级章节3","一级章节4","一级章节5"]}}}}
过渡页:
{{"type":"transition","data":{{"title":"章节标题","text":"章节简介"}}}}
内容页:
{{"type":"content","data":{{"title":"二级标题","items":[{{"title":"要点1标题","text":"要点1描述"}},{{"title":"要点2标题","text":"要点2描述"}},{{"title":"要点3标题","text":"要点3描述"}},{{"title":"要点4标题","text":"要点4描述"}}]}}}}
结束页:
{{"type":"end"}}
3. 转换规则:
- 大纲中的主标题("#"开头)转换为封面页
- 所有一级章节("##"开头)组成目录页
- 每个一级章节前生成一个过渡页
- 每个二级标题("###"开头)生成一个内容页
- 内容页中的items数组由该二级标题下的要点("-"开头)组成
- 最后添加一个结束页
4. 输出要求:
- 每个JSON对象必须单独成行
- 不要添加任何额外的注释或解释
- 确保每个JSON对象格式正确且完整
- 每个内容页必须包含该节点下所有的要点,通常为2-4个
示例输出:
{{"type":"cover","data":{{"title":"5G技术如何改变我们的生活","text":"探索5G技术对现代生活的全方位影响"}}}}
{{"type":"contents","data":{{"items":["5G技术概述","5G对通信领域的变革","5G与智能家居的融合","5G推动智能交通发展","5G在医疗领域的应用","5G助力工业互联网升级"]}}}}
{{"type":"transition","data":{{"title":"5G技术概述","text":"本章将介绍5G技术的定义、关键特性及发展历程。"}}}}"""
},
{
"role": "user",
"content": f"根据这个大纲生成PPT内容:\n{content}"
}
]
def generate():
headers = {
'Content-Type': 'application/json',
'Authorization': f'Bearer {OPENAI_API_KEY}'
}
payload = {
'model': model,
'messages': messages,
'stream': True
}
response = requests.post(
OPENAI_API_URL,
headers=headers,
json=payload,
stream=True,
verify=False # 禁用 SSL 验证
)
buffer = ""
for line in response.iter_lines():
if line:
line = line.decode('utf-8')
if line.startswith('data: ') and line != 'data: [DONE]':
try:
data = json.loads(line[6:])
if data['choices'][0]['delta'].get('content'):
buffer += data['choices'][0]['delta']['content']
# 尝试从缓冲区中提取完整的 JSON 对象
while True:
# 寻找可能的 JSON 对象开始和结束
json_start = buffer.find('{')
if json_start == -1:
break
# 寻找匹配的结束括号(处理嵌套JSON)
bracket_count = 0
json_end = -1
for i in range(json_start, len(buffer)):
if buffer[i] == '{':
bracket_count += 1
elif buffer[i] == '}':
bracket_count -= 1
if bracket_count == 0:
json_end = i
break
if json_end == -1:
# 没有找到匹配的结束括号,等待更多数据
break
json_str = buffer[json_start:json_end+1]
try:
# 验证是否为有效的 JSON
slide_data = json.loads(json_str)
# 验证是否包含必要的字段
if isinstance(slide_data, dict) and 'type' in slide_data:
# 发送到客户端
yield json.dumps(slide_data) + '\n'
# 从缓冲区中移除已处理的部分
buffer = buffer[json_end+1:]
else:
# 格式不符合要求,从缓冲区移除
buffer = buffer[json_end+1:]
except Exception as e:
# 解析错误,可能不是完整的JSON,等待更多数据
print(f"Error parsing JSON: {e}")
break
except Exception as e:
print(f"Error parsing OpenAI response: {e}")
return Response(stream_with_context(generate()), mimetype='text/event-stream')
if __name__ == '__main__':
app.run(debug=True, host='0.0.0.0', port=7860)