Spaces:
Paused
Paused
Upload 4 files
Browse files
app.py
CHANGED
|
@@ -83,27 +83,23 @@ safety_settings_g2 = [
|
|
| 83 |
]
|
| 84 |
@dataclass
|
| 85 |
class GeneratedText:
|
| 86 |
-
"""用于存储生成的文本片段"""
|
| 87 |
text: str
|
| 88 |
finish_reason: Optional[str] = None
|
| 89 |
|
| 90 |
|
| 91 |
class ResponseWrapper:
|
| 92 |
-
"""处理非流式响应的包装类"""
|
| 93 |
def __init__(self, data: Dict[Any, Any]):
|
| 94 |
self._data = data
|
| 95 |
self._text = self._extract_text()
|
| 96 |
self._finish_reason = self._extract_finish_reason()
|
| 97 |
|
| 98 |
def _extract_text(self) -> str:
|
| 99 |
-
"""从响应数据中提取文本"""
|
| 100 |
try:
|
| 101 |
return self._data['candidates'][0]['content']['parts'][0]['text']
|
| 102 |
except (KeyError, IndexError):
|
| 103 |
return ""
|
| 104 |
|
| 105 |
def _extract_finish_reason(self) -> Optional[str]:
|
| 106 |
-
"""提取完成原因"""
|
| 107 |
try:
|
| 108 |
return self._data['candidates'][0].get('finishReason')
|
| 109 |
except (KeyError, IndexError):
|
|
@@ -111,12 +107,10 @@ class ResponseWrapper:
|
|
| 111 |
|
| 112 |
@property
|
| 113 |
def text(self) -> str:
|
| 114 |
-
"""获取响应文本"""
|
| 115 |
return self._text
|
| 116 |
|
| 117 |
@property
|
| 118 |
def finish_reason(self) -> Optional[str]:
|
| 119 |
-
"""获取完成原因"""
|
| 120 |
return self._finish_reason
|
| 121 |
|
| 122 |
class APIKeyManager:
|
|
@@ -345,6 +339,7 @@ def chat_completions():
|
|
| 345 |
model = request_data.get('model', 'gemini-2.0-flash-exp')
|
| 346 |
temperature = request_data.get('temperature', 1)
|
| 347 |
max_tokens = request_data.get('max_tokens', 8192)
|
|
|
|
| 348 |
stream = request_data.get('stream', False)
|
| 349 |
hint = "流式" if stream else "非流"
|
| 350 |
logger.info(f"\n{model} [{hint}] → ...")
|
|
@@ -399,7 +394,7 @@ def chat_completions():
|
|
| 399 |
return handle_api_error(e, attempt)
|
| 400 |
|
| 401 |
def generate_stream(response):
|
| 402 |
-
buffer = b""
|
| 403 |
try:
|
| 404 |
for line in response.iter_lines():
|
| 405 |
if not line:
|
|
@@ -418,21 +413,25 @@ def chat_completions():
|
|
| 418 |
if 'content' in candidate:
|
| 419 |
content = candidate['content']
|
| 420 |
if 'parts' in content and content['parts']:
|
| 421 |
-
|
| 422 |
-
|
| 423 |
-
|
| 424 |
-
if
|
| 425 |
-
|
| 426 |
-
|
| 427 |
-
|
| 428 |
-
|
| 429 |
-
|
| 430 |
-
'
|
| 431 |
-
|
| 432 |
-
|
| 433 |
-
|
| 434 |
-
|
| 435 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 436 |
|
| 437 |
except json.JSONDecodeError:
|
| 438 |
logger.debug(f"JSONDecodeError, buffer now: {buffer}")
|
|
@@ -487,7 +486,7 @@ def chat_completions():
|
|
| 487 |
try:
|
| 488 |
text_content = response.text
|
| 489 |
except (AttributeError, IndexError, TypeError, ValueError) as e:
|
| 490 |
-
|
| 491 |
logger.error(f"用户输入被AI安全过滤器阻止")
|
| 492 |
return jsonify({
|
| 493 |
'error': {
|
|
@@ -496,13 +495,28 @@ def chat_completions():
|
|
| 496 |
'details': str(e)
|
| 497 |
}
|
| 498 |
}), 400
|
| 499 |
-
|
| 500 |
return jsonify({
|
| 501 |
'error': {
|
| 502 |
-
|
| 503 |
-
|
| 504 |
-
|
| 505 |
}), 500
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 506 |
|
| 507 |
response_data = {
|
| 508 |
'id': 'chatcmpl-xxxxxxxxxxxx',
|
|
|
|
| 83 |
]
|
| 84 |
@dataclass
|
| 85 |
class GeneratedText:
|
|
|
|
| 86 |
text: str
|
| 87 |
finish_reason: Optional[str] = None
|
| 88 |
|
| 89 |
|
| 90 |
class ResponseWrapper:
|
|
|
|
| 91 |
def __init__(self, data: Dict[Any, Any]):
|
| 92 |
self._data = data
|
| 93 |
self._text = self._extract_text()
|
| 94 |
self._finish_reason = self._extract_finish_reason()
|
| 95 |
|
| 96 |
def _extract_text(self) -> str:
|
|
|
|
| 97 |
try:
|
| 98 |
return self._data['candidates'][0]['content']['parts'][0]['text']
|
| 99 |
except (KeyError, IndexError):
|
| 100 |
return ""
|
| 101 |
|
| 102 |
def _extract_finish_reason(self) -> Optional[str]:
|
|
|
|
| 103 |
try:
|
| 104 |
return self._data['candidates'][0].get('finishReason')
|
| 105 |
except (KeyError, IndexError):
|
|
|
|
| 107 |
|
| 108 |
@property
|
| 109 |
def text(self) -> str:
|
|
|
|
| 110 |
return self._text
|
| 111 |
|
| 112 |
@property
|
| 113 |
def finish_reason(self) -> Optional[str]:
|
|
|
|
| 114 |
return self._finish_reason
|
| 115 |
|
| 116 |
class APIKeyManager:
|
|
|
|
| 339 |
model = request_data.get('model', 'gemini-2.0-flash-exp')
|
| 340 |
temperature = request_data.get('temperature', 1)
|
| 341 |
max_tokens = request_data.get('max_tokens', 8192)
|
| 342 |
+
show_thoughts = request_data.get('show_thoughts', False)
|
| 343 |
stream = request_data.get('stream', False)
|
| 344 |
hint = "流式" if stream else "非流"
|
| 345 |
logger.info(f"\n{model} [{hint}] → ...")
|
|
|
|
| 394 |
return handle_api_error(e, attempt)
|
| 395 |
|
| 396 |
def generate_stream(response):
|
| 397 |
+
buffer = b""
|
| 398 |
try:
|
| 399 |
for line in response.iter_lines():
|
| 400 |
if not line:
|
|
|
|
| 413 |
if 'content' in candidate:
|
| 414 |
content = candidate['content']
|
| 415 |
if 'parts' in content and content['parts']:
|
| 416 |
+
parts = content['parts']
|
| 417 |
+
if is_thinking and not show_thoughts:
|
| 418 |
+
parts = [part for part in parts if not part.get('thought')]
|
| 419 |
+
if parts:
|
| 420 |
+
text = parts[0].get('text', '')
|
| 421 |
+
finish_reason = candidate.get('finishReason')
|
| 422 |
+
|
| 423 |
+
if text:
|
| 424 |
+
data = {
|
| 425 |
+
'choices': [{
|
| 426 |
+
'delta': {
|
| 427 |
+
'content': text
|
| 428 |
+
},
|
| 429 |
+
'finish_reason': finish_reason,
|
| 430 |
+
'index': 0
|
| 431 |
+
}],
|
| 432 |
+
'object': 'chat.completion.chunk'
|
| 433 |
+
}
|
| 434 |
+
yield f"data: {json.dumps(data)}\n\n"
|
| 435 |
|
| 436 |
except json.JSONDecodeError:
|
| 437 |
logger.debug(f"JSONDecodeError, buffer now: {buffer}")
|
|
|
|
| 486 |
try:
|
| 487 |
text_content = response.text
|
| 488 |
except (AttributeError, IndexError, TypeError, ValueError) as e:
|
| 489 |
+
if "response.candidates" in str(e) or "response.text" in str(e):
|
| 490 |
logger.error(f"用户输入被AI安全过滤器阻止")
|
| 491 |
return jsonify({
|
| 492 |
'error': {
|
|
|
|
| 495 |
'details': str(e)
|
| 496 |
}
|
| 497 |
}), 400
|
| 498 |
+
else:
|
| 499 |
return jsonify({
|
| 500 |
'error': {
|
| 501 |
+
'message': 'AI响应处理失败',
|
| 502 |
+
'type': 'response_processing_error'
|
| 503 |
+
}
|
| 504 |
}), 500
|
| 505 |
+
try:
|
| 506 |
+
response_json = json.loads(response.text)
|
| 507 |
+
if 'candidates' in response_json and response_json['candidates']:
|
| 508 |
+
candidate = response_json['candidates'][0]
|
| 509 |
+
if 'content' in candidate:
|
| 510 |
+
content = candidate['content']
|
| 511 |
+
if 'parts' in content and content['parts']:
|
| 512 |
+
parts = content['parts']
|
| 513 |
+
if is_thinking and not show_thoughts:
|
| 514 |
+
parts = [part for part in parts if not part.get('thought')]
|
| 515 |
+
|
| 516 |
+
if parts:
|
| 517 |
+
text_content = "".join(part.get('text', '') for part in parts)
|
| 518 |
+
except json.JSONDecodeError:
|
| 519 |
+
pass
|
| 520 |
|
| 521 |
response_data = {
|
| 522 |
'id': 'chatcmpl-xxxxxxxxxxxx',
|