pycoze 0.1.423__py3-none-any.whl → 0.1.425__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- pycoze/bot/chat_base.py +83 -88
- {pycoze-0.1.423.dist-info → pycoze-0.1.425.dist-info}/METADATA +1 -1
- {pycoze-0.1.423.dist-info → pycoze-0.1.425.dist-info}/RECORD +6 -6
- {pycoze-0.1.423.dist-info → pycoze-0.1.425.dist-info}/LICENSE +0 -0
- {pycoze-0.1.423.dist-info → pycoze-0.1.425.dist-info}/WHEEL +0 -0
- {pycoze-0.1.423.dist-info → pycoze-0.1.425.dist-info}/top_level.txt +0 -0
pycoze/bot/chat_base.py
CHANGED
@@ -87,15 +87,86 @@ def dumps_markdown_json(data):
|
|
87
87
|
|
88
88
|
|
89
89
|
|
90
|
+
class BufferProcessor:
|
91
|
+
def __init__(self):
|
92
|
+
self.buffer = ""
|
93
|
+
self.in_json_block = False
|
94
|
+
self.json_block_content = ""
|
95
|
+
self.text_content = ""
|
96
|
+
|
97
|
+
def process_buffer(self):
|
98
|
+
"""
|
99
|
+
处理 buffer 中的内容,并返回生成的内容类型和内容。
|
100
|
+
返回格式为 (type, content),其中 type 可以是 "text" 或 "json"。
|
101
|
+
"""
|
102
|
+
results = []
|
103
|
+
while self.buffer:
|
104
|
+
if not self.in_json_block:
|
105
|
+
# 查找 JSON 代码块的起始标记
|
106
|
+
json_start_match = re.search(r"```json", self.buffer, re.IGNORECASE)
|
107
|
+
if json_start_match:
|
108
|
+
# 提取 JSON 代码块之前的文本
|
109
|
+
self.text_content += self.buffer[:json_start_match.start()]
|
110
|
+
# 如果 text_content 不为空,先 yield 文本
|
111
|
+
if self.text_content.strip():
|
112
|
+
results.append(("text", self.text_content.strip()))
|
113
|
+
self.text_content = ""
|
114
|
+
# 进入 JSON 代码块模式
|
115
|
+
self.in_json_block = True
|
116
|
+
self.buffer = self.buffer[json_start_match.end():]
|
117
|
+
else:
|
118
|
+
# 如果没有找到 JSON 代码块,继续累积到 text_content
|
119
|
+
self.text_content += self.buffer
|
120
|
+
self.buffer = ""
|
121
|
+
else:
|
122
|
+
# 查找 JSON 代码块的结束标记
|
123
|
+
json_end_match = re.search(r"```", self.buffer)
|
124
|
+
if json_end_match:
|
125
|
+
# 提取 JSON 代码块内容
|
126
|
+
self.json_block_content += self.buffer[:json_end_match.start()]
|
127
|
+
# yield JSON 代码块
|
128
|
+
results.append(("json", self.json_block_content.strip()))
|
129
|
+
self.json_block_content = ""
|
130
|
+
self.in_json_block = False
|
131
|
+
self.buffer = self.buffer[json_end_match.end():]
|
132
|
+
else:
|
133
|
+
# 如果没有找到结束标记,继续累积 JSON 内容
|
134
|
+
self.json_block_content += self.buffer
|
135
|
+
self.buffer = ""
|
136
|
+
return results
|
137
|
+
|
138
|
+
def finalize(self):
|
139
|
+
"""
|
140
|
+
处理流结束后的剩余内容。
|
141
|
+
返回格式为 (type, content),其中 type 可以是 "text" 或 "json"。
|
142
|
+
"""
|
143
|
+
results = []
|
144
|
+
if self.in_json_block:
|
145
|
+
# 如果仍在 JSON 代码块模式,处理剩余的 JSON 内容
|
146
|
+
if self.json_block_content.strip():
|
147
|
+
results.append(("json", self.json_block_content.strip()))
|
148
|
+
else:
|
149
|
+
# 如果不在 JSON 代码块模式,处理剩余的文本内容
|
150
|
+
if self.text_content.strip():
|
151
|
+
results.append(("text", self.text_content.strip()))
|
152
|
+
return results
|
153
|
+
|
154
|
+
def reset(self):
|
155
|
+
"""
|
156
|
+
重置 buffer 处理器的状态。
|
157
|
+
"""
|
158
|
+
self.buffer = ""
|
159
|
+
self.in_json_block = False
|
160
|
+
self.json_block_content = ""
|
161
|
+
self.text_content = ""
|
162
|
+
|
163
|
+
|
90
164
|
async def stream_openai_response(conversation_history, start_new_stream):
|
91
165
|
"""
|
92
166
|
异步流式传输 OpenAI 聊天完成响应并处理结构化输出
|
93
167
|
"""
|
94
168
|
stream = None
|
95
|
-
|
96
|
-
in_json_block = False
|
97
|
-
json_block_content = ""
|
98
|
-
text_content = ""
|
169
|
+
buffer_processor = BufferProcessor()
|
99
170
|
|
100
171
|
while True:
|
101
172
|
# 检查是否需要重新创建流
|
@@ -104,10 +175,7 @@ async def stream_openai_response(conversation_history, start_new_stream):
|
|
104
175
|
await stream.aclose() # 关闭之前的流
|
105
176
|
stream = chat_stream_async(conversation_history) # 获取新的异步生成器
|
106
177
|
start_new_stream["value"] = False # 重置标志
|
107
|
-
|
108
|
-
in_json_block = False
|
109
|
-
json_block_content = ""
|
110
|
-
text_content = ""
|
178
|
+
buffer_processor.reset()
|
111
179
|
|
112
180
|
# 使用 async for 迭代异步生成器
|
113
181
|
try:
|
@@ -117,44 +185,11 @@ async def stream_openai_response(conversation_history, start_new_stream):
|
|
117
185
|
break # 退出当前的 async for 循环,进入下一次 while 循环
|
118
186
|
|
119
187
|
info("assistant", chunk)
|
120
|
-
buffer += chunk
|
188
|
+
buffer_processor.buffer += chunk
|
121
189
|
|
122
|
-
if "\n"
|
123
|
-
|
124
|
-
|
125
|
-
while buffer:
|
126
|
-
if not in_json_block:
|
127
|
-
# 查找 JSON 代码块的起始标记
|
128
|
-
json_start_match = re.search(r"```json", buffer, re.IGNORECASE)
|
129
|
-
if json_start_match:
|
130
|
-
# 提取 JSON 代码块之前的文本
|
131
|
-
text_content += buffer[:json_start_match.start()]
|
132
|
-
# 如果 text_content 不为空,先 yield 文本
|
133
|
-
if text_content.strip():
|
134
|
-
yield ("text", text_content.strip())
|
135
|
-
text_content = ""
|
136
|
-
# 进入 JSON 代码块模式
|
137
|
-
in_json_block = True
|
138
|
-
buffer = buffer[json_start_match.end():]
|
139
|
-
else:
|
140
|
-
# 如果没有找到 JSON 代码块,继续累积到 text_content
|
141
|
-
text_content += buffer
|
142
|
-
buffer = ""
|
143
|
-
else:
|
144
|
-
# 查找 JSON 代码块的结束标记
|
145
|
-
json_end_match = re.search(r"```", buffer)
|
146
|
-
if json_end_match:
|
147
|
-
# 提取 JSON 代码块内容
|
148
|
-
json_block_content += buffer[:json_end_match.start()]
|
149
|
-
# yield JSON 代码块
|
150
|
-
yield ("json", json_block_content.strip())
|
151
|
-
json_block_content = ""
|
152
|
-
in_json_block = False
|
153
|
-
buffer = buffer[json_end_match.end():]
|
154
|
-
else:
|
155
|
-
# 如果没有找到结束标记,继续累积 JSON 内容
|
156
|
-
json_block_content += buffer
|
157
|
-
buffer = ""
|
190
|
+
if "\n" in chunk:
|
191
|
+
for result in buffer_processor.process_buffer():
|
192
|
+
yield result
|
158
193
|
|
159
194
|
# 如果流正常结束,退出 while 循环
|
160
195
|
break
|
@@ -163,50 +198,10 @@ async def stream_openai_response(conversation_history, start_new_stream):
|
|
163
198
|
# 捕获其他异常(如网络错误)
|
164
199
|
print(f"Error: {e}", style="bold red")
|
165
200
|
break
|
166
|
-
|
167
|
-
if buffer:
|
168
|
-
if not in_json_block:
|
169
|
-
# 查找 JSON 代码块的起始标记
|
170
|
-
json_start_match = re.search(r"```json", buffer, re.IGNORECASE)
|
171
|
-
if json_start_match:
|
172
|
-
# 提取 JSON 代码块之前的文本
|
173
|
-
text_content += buffer[:json_start_match.start()]
|
174
|
-
# 如果 text_content 不为空,先 yield 文本
|
175
|
-
if text_content.strip():
|
176
|
-
yield ("text", text_content.strip())
|
177
|
-
text_content = ""
|
178
|
-
# 进入 JSON 代码块模式
|
179
|
-
in_json_block = True
|
180
|
-
buffer = buffer[json_start_match.end():]
|
181
|
-
else:
|
182
|
-
# 如果没有找到 JSON 代码块,继续累积到 text_content
|
183
|
-
text_content += buffer
|
184
|
-
buffer = ""
|
185
|
-
else:
|
186
|
-
# 查找 JSON 代码块的结束标记
|
187
|
-
json_end_match = re.search(r"```", buffer)
|
188
|
-
if json_end_match:
|
189
|
-
# 提取 JSON 代码块内容
|
190
|
-
json_block_content += buffer[:json_end_match.start()]
|
191
|
-
# yield JSON 代码块
|
192
|
-
yield ("json", json_block_content.strip())
|
193
|
-
json_block_content = ""
|
194
|
-
in_json_block = False
|
195
|
-
buffer = buffer[json_end_match.end():]
|
196
|
-
else:
|
197
|
-
# 如果没有找到结束标记,继续累积 JSON 内容
|
198
|
-
json_block_content += buffer
|
199
|
-
buffer = ""
|
200
|
-
|
201
|
+
|
201
202
|
# 处理流结束后的剩余内容
|
202
|
-
|
203
|
-
|
204
|
-
if json_block_content.strip():
|
205
|
-
yield ("json", json_block_content.strip())
|
206
|
-
else:
|
207
|
-
# 如果不在 JSON 代码块模式,处理剩余的文本内容
|
208
|
-
if text_content.strip():
|
209
|
-
yield ("text", text_content.strip())
|
203
|
+
for result in buffer_processor.finalize():
|
204
|
+
yield result
|
210
205
|
|
211
206
|
|
212
207
|
|
@@ -11,7 +11,7 @@ pycoze/api/lib/web.py,sha256=GWgtiTJOolKOX2drXcwuyqTcbo5FQVxa1NuBGcNyjyc,223
|
|
11
11
|
pycoze/api/lib/window.py,sha256=dkzWfLwn5pE_L0DfQ38K8nx9tQyT5KO-GYyXi0rytFc,2073
|
12
12
|
pycoze/bot/__init__.py,sha256=rL3Q-ycczRpSFfKn84fg3QBl5k22WpyeIU5qOEjEby8,79
|
13
13
|
pycoze/bot/chat.py,sha256=qEuMxH0cVFU9QSU36FrOsjhRAxtsvOv7CQtuXvM3F6Y,6446
|
14
|
-
pycoze/bot/chat_base.py,sha256=
|
14
|
+
pycoze/bot/chat_base.py,sha256=_yAzwngBqEmgDmF8mV44gms6-MkDlu4HeboqGZole2w,12267
|
15
15
|
pycoze/bot/lib.py,sha256=_bQ52mKsWgFGAogFHnmRBJbvK_tPOwsAJ8NqJNMR5K4,7210
|
16
16
|
pycoze/bot/message.py,sha256=udnIi-h4QgGzkbr_5VcAsVGjoLp9wXJSfBCeuOz7_Bk,802
|
17
17
|
pycoze/bot/prompt.md,sha256=t7NQdiiNe-jCDVfeVbvTPfq5WK5nF8CxFUQUFMyXJlo,13880
|
@@ -31,8 +31,8 @@ pycoze/utils/arg.py,sha256=jop1tBfe5hYkHW1NSpCeaZBEznkgguBscj_7M2dWfrs,503
|
|
31
31
|
pycoze/utils/env.py,sha256=5pWlXfM1F5ZU9hhv1rHlDEanjEW5wf0nbyez9bNRqqA,559
|
32
32
|
pycoze/utils/socket.py,sha256=bZbFFRH4mfThzRqt55BAAGQ6eICx_ja4x8UGGrUdAm8,2428
|
33
33
|
pycoze/utils/text_or_file.py,sha256=gpxZVWt2DW6YiEg_MnMuwg36VNf3TX383QD_1oZNB0Y,551
|
34
|
-
pycoze-0.1.
|
35
|
-
pycoze-0.1.
|
36
|
-
pycoze-0.1.
|
37
|
-
pycoze-0.1.
|
38
|
-
pycoze-0.1.
|
34
|
+
pycoze-0.1.425.dist-info/LICENSE,sha256=QStd_Qsd0-kAam_-sOesCIp_uKrGWeoKwt9M49NVkNU,1090
|
35
|
+
pycoze-0.1.425.dist-info/METADATA,sha256=r4rZ7Vt1KvP4uPnYTtdMX2h8XU2GLNqLNUEzNivUMQI,854
|
36
|
+
pycoze-0.1.425.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
|
37
|
+
pycoze-0.1.425.dist-info/top_level.txt,sha256=76dPeDhKvOCleL3ZC5gl1-y4vdS1tT_U1hxWVAn7sFo,7
|
38
|
+
pycoze-0.1.425.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|