g2api-test / src /core /grok_api_client.py
misonL's picture
Initial project commit with gitignore
df4585d
Raw
History Blame Contribute Delete
9.92 kB
# src/core/grok_api_client.py
import json
import time
import base64
import re
import requests
from curl_cffi import requests as curl_requests
from src.core.logger import logger # 从新的位置导入 logger
from src.core.utils import Utils # 从新的位置导入 Utils
from app import CONFIG, DEFAULT_HEADERS # 导入 CONFIG 和 DEFAULT_HEADERS
class GrokApiClient:
def __init__(self, model_id):
if model_id not in CONFIG["MODELS"]:
raise ValueError(f"不支持的模型: {model_id}")
self.model_id = CONFIG["MODELS"][model_id]
def process_message_content(self, content):
if isinstance(content, str):
return content
return None
def get_image_type(self, base64_string):
mime_type = 'image/jpeg'
if 'data:image' in base64_string:
import re
matches = re.search(r'data:([a-zA-Z0-9]+\/[a-zA-Z0-9-.+]+);base64,', base64_string)
if matches:
mime_type = matches.group(1)
extension = mime_type.split('/')[1]
file_name = f"image.{extension}"
return {
"mimeType": mime_type,
"fileName": file_name
}
def upload_base64_file(self, message, model):
try:
message_base64 = base64.b64encode(message.encode('utf-8')).decode('utf-8')
upload_data = {
"fileName": "message.txt",
"fileMimeType": "text/plain",
"content": message_base64
}
logger.info("发送文字文件请求", "Server")
cookie = f"{Utils.create_auth_headers(model, True)};{CONFIG['SERVER']['CF_CLEARANCE']}"
proxy_options = Utils.get_proxy_options()
response = curl_requests.post(
"https://grok.com/rest/app-chat/upload-file",
headers={
**DEFAULT_HEADERS,
"Cookie":cookie
},
json=upload_data,
impersonate="chrome133a",
verify=False,
**proxy_options
)
if response.status_code != 200:
logger.error(f"上传文件失败,状态码:{response.status_code}", "Server")
raise Exception(f"上传文件失败,状态码:{response.status_code}")
result = response.json()
logger.info(f"上传文件成功: {result}", "Server")
return result.get("fileMetadataId", "")
except Exception as error:
logger.error(str(error), "Server")
raise Exception(f"上传文件失败,状态码:{response.status_code}")
def upload_base64_image(self, base64_data, url):
try:
if 'data:image' in base64_data:
image_buffer = base64_data.split(',')[1]
else:
image_buffer = base64_data
image_info = self.get_image_type(base64_data)
mime_type = image_info["mimeType"]
file_name = image_info["fileName"]
upload_data = {
"rpc": "uploadFile",
"req": {
"fileName": file_name,
"fileMimeType": mime_type,
"content": image_buffer
}
}
logger.info("发送图片请求", "Server")
proxy_options = Utils.get_proxy_options()
response = curl_requests.post(
url,
headers={
**DEFAULT_HEADERS,
"Cookie":CONFIG["SERVER"]['COOKIE']
},
json=upload_data,
impersonate="chrome133a",
verify=False,
**proxy_options
)
if response.status_code != 200:
logger.error(f"上传图片失败,状态码:{response.status_code}", "Server")
return ''
result = response.json()
logger.info(f"上传图片成功: {result}", "Server")
return result.get("fileMetadataId", "")
except Exception as error:
logger.error(str(error), "Server")
return ''
def prepare_chat_request(self, request):
if ((request["model"] == 'grok-2-imageGen' or request["model"] == 'grok-3-imageGen') and not CONFIG["API"]["PICGO_KEY"] and not CONFIG["API"]["TUMY_KEY"] and request.get("stream", False)):
raise ValueError("该模型流式输出需要配置PICGO或者TUMY图床密钥!")
todo_messages = request["messages"]
if request["model"] in ['grok-2-imageGen', 'grok-3-imageGen', 'grok-3-deepsearch']:
last_message = todo_messages[-1]
if last_message["role"] != 'user':
raise ValueError('此模型最后一条消息必须是用户消息!')
todo_messages = [last_message]
file_attachments = []
messages = ''
last_role = None
last_content = ''
message_length = 0
convert_to_file = False
last_message_content = ''
search = request["model"] in ['grok-2-search', 'grok-3-search']
# 移除标签及其内容和base64图片
def remove_think_tags(text):
import re
text = re.sub(r'[\s\S]*?<\/think>', '', text).strip()
text = re.sub(r'!\[image\]\(data:.*?base64,.*?\)', '[图片]', text)
return text
def process_content(content):
if isinstance(content, list):
text_content = ''
for item in content:
if item["type"] == 'image_url':
text_content += ("[图片]" if not text_content else '\n[图片]')
elif item["type"] == 'text':
text_content += (remove_think_tags(item["text"]) if not text_content else '\n' + remove_think_tags(item["text"]))
return text_content
elif isinstance(content, dict) and content is not None:
if content["type"] == 'image_url':
return "[图片]"
elif content["type"] == 'text':
return remove_think_tags(content["text"])
return remove_think_tags(self.process_message_content(content))
for current in todo_messages:
role = 'assistant' if current["role"] == 'assistant' else 'user'
is_last_message = current == todo_messages[-1]
if is_last_message and "content" in current:
if isinstance(current["content"], list):
for item in current["content"]:
if item["type"] == 'image_url':
processed_image = self.upload_base64_image(
item["image_url"]["url"],
f"{CONFIG['API']['BASE_URL']}/api/rpc"
)
if processed_image:
file_attachments.append(processed_image)
elif isinstance(current["content"], dict) and current["content"].get("type") == 'image_url':
processed_image = self.upload_base64_image(
current["content"]["image_url"]["url"],
f"{CONFIG['API']['BASE_URL']}/api/rpc"
)
if processed_image:
file_attachments.append(processed_image)
text_content = process_content(current.get("content", ""))
if is_last_message and convert_to_file:
last_message_content = f"{role.upper()}: {text_content or '[图片]'}\n"
continue
if text_content or (is_last_message and file_attachments):
if role == last_role and text_content:
last_content += '\n' + text_content
messages = messages[:messages.rindex(f"{role.upper()}: ")] + f"{role.upper()}: {last_content}\n"
else:
messages += f"{role.upper()}: {text_content or '[图片]'}\n"
last_content = text_content
last_role = role
message_length += len(messages)
if message_length >= 40000:
convert_to_file = True
if convert_to_file:
file_id = self.upload_base64_file(messages, request["model"])
if file_id:
file_attachments.insert(0, file_id)
messages = last_message_content.strip()
if messages.strip() == '':
if convert_to_file:
messages = '基于txt文件内容进行回复:'
else:
raise ValueError('消息内容为空!')
return {
"temporary": CONFIG["API"].get("IS_TEMP_CONVERSATION", False),
"modelName": self.model_id,
"message": messages.strip(),
"fileAttachments": file_attachments[:4],
"imageAttachments": [],
"disableSearch": False,
"enableImageGeneration": True,
"returnImageBytes": False,
"enableImageStreaming": False,
"imageGenerationCount": 1,
"forceConcise": False,
"toolOverrides": {
"imageGen": request["model"] in ['grok-2-imageGen', 'grok-3-imageGen'],
"webSearch": search,
"xSearch": search,
"xMediaSearch": search,
"trendsSearch": search,
"xPostAnalyze": search
},
"enableSideBySide": True,
"isPreset": False,
"sendFinalMetadata": True,
"customInstructions": "",
"deepsearchPreset": "default" if request["model"] == 'grok-3-deepsearch' else "",
"isReasoning": request["model"] == 'grok-3-reasoning'
}