File size: 9,240 Bytes
84d2f90
 
 
 
e71a2c6
 
 
 
84d2f90
 
 
 
 
 
 
 
 
 
 
 
 
e71a2c6
84d2f90
 
 
 
 
 
68dceb7
 
 
 
84d2f90
 
68dceb7
 
84d2f90
 
68dceb7
 
 
 
 
 
 
 
 
84d2f90
 
 
 
 
 
 
 
 
e71a2c6
84d2f90
 
 
 
 
 
 
 
b6900a2
 
84d2f90
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
e71a2c6
84d2f90
 
 
 
 
68dceb7
 
 
 
 
 
 
e71a2c6
68dceb7
 
e71a2c6
68dceb7
 
e71a2c6
68dceb7
 
e71a2c6
68dceb7
 
e71a2c6
68dceb7
 
e71a2c6
 
68dceb7
e71a2c6
68dceb7
 
 
 
 
 
 
 
 
 
e71a2c6
 
 
84d2f90
 
 
 
 
 
 
 
 
 
 
 
 
e71a2c6
84d2f90
 
 
 
 
 
 
 
b6900a2
 
84d2f90
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
e71a2c6
 
84d2f90
 
 
 
 
e71a2c6
84d2f90
 
e71a2c6
84d2f90
 
 
 
 
 
 
 
 
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
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
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)