|
|
|
@ -3,6 +3,7 @@ import time |
|
|
|
|
|
|
|
|
|
|
|
import httpx |
|
|
|
import httpx |
|
|
|
import json |
|
|
|
import json |
|
|
|
|
|
|
|
import anthropic |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def fetch_llm_generate_task(odoo_base_url="http://localhost:8069"): |
|
|
|
def fetch_llm_generate_task(odoo_base_url="http://localhost:8069"): |
|
|
|
@ -80,8 +81,9 @@ def call_llm_generate(task_data, max_retries=3): |
|
|
|
str: 生成的内容,如果失败返回 "failed" |
|
|
|
str: 生成的内容,如果失败返回 "failed" |
|
|
|
""" |
|
|
|
""" |
|
|
|
if task_data is None: |
|
|
|
if task_data is None: |
|
|
|
print("[ERROR] task_data 为 None,无法调用 LLM") |
|
|
|
error_msg = "[ERROR] task_data 为 None,无法调用 LLM" |
|
|
|
return "failed" |
|
|
|
print(error_msg) |
|
|
|
|
|
|
|
return error_msg |
|
|
|
|
|
|
|
|
|
|
|
# 提取 task_data 中的字段 |
|
|
|
# 提取 task_data 中的字段 |
|
|
|
final_prompt = task_data.get('final_prompt', '') |
|
|
|
final_prompt = task_data.get('final_prompt', '') |
|
|
|
@ -153,6 +155,124 @@ def call_llm_generate(task_data, max_retries=3): |
|
|
|
return "failed" |
|
|
|
return "failed" |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def call_llm_generate_with_anthropic(task_data, max_retries=3): |
|
|
|
|
|
|
|
""" |
|
|
|
|
|
|
|
调用 MiniMax 的 Anthropic 兼容接口生成内容 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Args: |
|
|
|
|
|
|
|
task_data: 包含 final_prompt, system_prompt, user_prompt 的字典 |
|
|
|
|
|
|
|
max_retries: 最大重试次数,默认 3 次(针对非529错误) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Returns: |
|
|
|
|
|
|
|
str: 生成的内容,如果失败返回 "failed" |
|
|
|
|
|
|
|
""" |
|
|
|
|
|
|
|
if task_data is None: |
|
|
|
|
|
|
|
error_msg = "[ERROR] task_data 为 None,无法调用 LLM" |
|
|
|
|
|
|
|
print(error_msg) |
|
|
|
|
|
|
|
return error_msg |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# 提取 task_data 中的字段 |
|
|
|
|
|
|
|
final_prompt = task_data.get('final_prompt', '') |
|
|
|
|
|
|
|
system_prompt = task_data.get('system_prompt', '') |
|
|
|
|
|
|
|
user_prompt = task_data.get('user_prompt', '') |
|
|
|
|
|
|
|
record_id = task_data.get('id', 'unknown') |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
print(f"[INFO] 开始为 record_id={record_id} 调用 MiniMax Anthropic 接口") |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# 配置 Anthropic 客户端 |
|
|
|
|
|
|
|
ANTHROPIC_API_KEY = "sk-cp-l_as8mjqPhsOIny9IFKZ8jzA92z1c0eRwchldhEf4KzQjs9cjVknV2o7VNCcvYUXsXFq7uF4aSgp2RxxmUHLXwPGKgIvzedM70_XUIXiBB3gu_UmLDQLfh4" |
|
|
|
|
|
|
|
ANTHROPIC_BASE_URL = "https://api.minimaxi.com/anthropic" |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
client = anthropic.Anthropic( |
|
|
|
|
|
|
|
api_key=ANTHROPIC_API_KEY, |
|
|
|
|
|
|
|
base_url=ANTHROPIC_BASE_URL |
|
|
|
|
|
|
|
) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# 针对 529 错误的专门重试机制 |
|
|
|
|
|
|
|
MAX_RETRIES_529 = 30 # 529 错误最大重试次数 |
|
|
|
|
|
|
|
RETRY_DELAY_529 = 10 # 529 错误重试间隔(秒) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
total_retry = 1 |
|
|
|
|
|
|
|
for attempt_529 in range(1, MAX_RETRIES_529 + 1): |
|
|
|
|
|
|
|
# 普通错误的重试机制 |
|
|
|
|
|
|
|
for attempt in range(1, max_retries + 1): |
|
|
|
|
|
|
|
try: |
|
|
|
|
|
|
|
print(f"[INFO] 第 {total_retry} 次尝试调用 MiniMax...") |
|
|
|
|
|
|
|
total_retry += 1 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# 构建消息列表 |
|
|
|
|
|
|
|
messages = [] |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# 如果有 final_prompt 或 user_prompt,作为用户消息 |
|
|
|
|
|
|
|
user_content = final_prompt if final_prompt else user_prompt |
|
|
|
|
|
|
|
if user_content: |
|
|
|
|
|
|
|
messages.append({ |
|
|
|
|
|
|
|
"role": "user", |
|
|
|
|
|
|
|
"content": [ |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
"type": "text", |
|
|
|
|
|
|
|
"text": user_content |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
] |
|
|
|
|
|
|
|
}) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# 调用 API(使用流式传输,因为操作可能超过 10 分钟) |
|
|
|
|
|
|
|
generated_content = "" |
|
|
|
|
|
|
|
with client.messages.stream( |
|
|
|
|
|
|
|
model="MiniMax-M2.7", |
|
|
|
|
|
|
|
system=system_prompt if system_prompt else "You are a helpful assistant.", |
|
|
|
|
|
|
|
messages=messages, |
|
|
|
|
|
|
|
max_tokens=32768 |
|
|
|
|
|
|
|
) as stream: |
|
|
|
|
|
|
|
for text in stream.text_stream: |
|
|
|
|
|
|
|
generated_content += text |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# 检查是否有思考过程(流式模式下通常不会返回 thinking 块) |
|
|
|
|
|
|
|
if hasattr(stream, 'thinking') and stream.thinking: |
|
|
|
|
|
|
|
print(f"[DEBUG] 思考过程: {stream.thinking[:100]}...") |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if generated_content: |
|
|
|
|
|
|
|
print(f"[SUCCESS] 第 {attempt} 次尝试成功,生成长度: {len(generated_content)} 字符") |
|
|
|
|
|
|
|
return generated_content |
|
|
|
|
|
|
|
else: |
|
|
|
|
|
|
|
print(f"[WARNING] 第 {attempt} 次尝试返回空内容") |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
except Exception as e: |
|
|
|
|
|
|
|
# 检查是否是 529 错误 |
|
|
|
|
|
|
|
error_str = str(e) |
|
|
|
|
|
|
|
if "529" in error_str or "overloaded" in error_str.lower(): |
|
|
|
|
|
|
|
print(f"[WARNING] 遇到服务器拥挤 (529) 错误: {e}") |
|
|
|
|
|
|
|
# 跳出内层循环,进入外层 529 重试逻辑 |
|
|
|
|
|
|
|
break |
|
|
|
|
|
|
|
else: |
|
|
|
|
|
|
|
print(f"[ERROR] 第 {attempt} 次尝试发生错误: {e}") |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# 如果不是最后一次尝试,等待后继续(普通错误重试) |
|
|
|
|
|
|
|
if attempt < max_retries: |
|
|
|
|
|
|
|
print(f"[INFO] 等待 2 秒后重试...") |
|
|
|
|
|
|
|
time.sleep(2) |
|
|
|
|
|
|
|
else: |
|
|
|
|
|
|
|
# 内层循环正常结束(没有 break),说明没有遇到 529 错误且重试用完 |
|
|
|
|
|
|
|
# 所有重试都失败了 |
|
|
|
|
|
|
|
error_msg = f"[FAILED] 经过 {max_retries} 次尝试后仍然失败" |
|
|
|
|
|
|
|
print(error_msg) |
|
|
|
|
|
|
|
return error_msg |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# 执行到这里说明遇到了 529 错误,进行外层重试 |
|
|
|
|
|
|
|
if attempt_529 < MAX_RETRIES_529: |
|
|
|
|
|
|
|
print(f"[INFO] 遇到服务器拥挤 (529),第 {attempt_529}/{MAX_RETRIES_529} 次重试,等待 {RETRY_DELAY_529} 秒...") |
|
|
|
|
|
|
|
time.sleep(RETRY_DELAY_529) |
|
|
|
|
|
|
|
else: |
|
|
|
|
|
|
|
error_msg = f"[FAILED] 经过 {MAX_RETRIES_529} 次 529 重试后仍然失败" |
|
|
|
|
|
|
|
print(error_msg) |
|
|
|
|
|
|
|
return error_msg |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# 所有重试都失败了 |
|
|
|
|
|
|
|
error_msg = f"[FAILED] 经过 {MAX_RETRIES_529} 次 529 重试后仍然失败" |
|
|
|
|
|
|
|
print(error_msg) |
|
|
|
|
|
|
|
return error_msg |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def upload_llm_result(odoo_base_url, upload_data): |
|
|
|
def upload_llm_result(odoo_base_url, upload_data): |
|
|
|
""" |
|
|
|
""" |
|
|
|
上传 LLM 生成结果到 Odoo |
|
|
|
上传 LLM 生成结果到 Odoo |
|
|
|
@ -168,36 +288,45 @@ def upload_llm_result(odoo_base_url, upload_data): |
|
|
|
Returns: |
|
|
|
Returns: |
|
|
|
bool: 上传成功返回 True,失败返回 False |
|
|
|
bool: 上传成功返回 True,失败返回 False |
|
|
|
""" |
|
|
|
""" |
|
|
|
|
|
|
|
# 构建上传结果的 API URL |
|
|
|
url = f"{odoo_base_url}/api/alpha-idea/result" |
|
|
|
url = f"{odoo_base_url}/api/alpha-idea/result" |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# 设置请求头,指定内容类型为 JSON |
|
|
|
headers = { |
|
|
|
headers = { |
|
|
|
"Content-Type": "application/json" |
|
|
|
"Content-Type": "application/json" |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
try: |
|
|
|
try: |
|
|
|
|
|
|
|
# 打印上传信息日志 |
|
|
|
print(f"[INFO] 正在上传结果到 Odoo: {url}") |
|
|
|
print(f"[INFO] 正在上传结果到 Odoo: {url}") |
|
|
|
print(f"[INFO] 上传数据: {json.dumps(upload_data, ensure_ascii=False)}") |
|
|
|
print(f"[INFO] 上传数据: {json.dumps(upload_data, ensure_ascii=False)}") |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# 发送 POST 请求,设置超时时间为 30 秒 |
|
|
|
response = httpx.post( |
|
|
|
response = httpx.post( |
|
|
|
url, |
|
|
|
url, |
|
|
|
headers=headers, |
|
|
|
headers=headers, |
|
|
|
json=upload_data, |
|
|
|
json=upload_data, |
|
|
|
timeout=30 |
|
|
|
timeout=30 |
|
|
|
) |
|
|
|
) |
|
|
|
|
|
|
|
# 如果 HTTP 状态码表示错误,则抛出异常 |
|
|
|
response.raise_for_status() |
|
|
|
response.raise_for_status() |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# 解析响应结果 |
|
|
|
result = response.json() |
|
|
|
result = response.json() |
|
|
|
print(f"[INFO] 上传结果接口返回: {json.dumps(result, ensure_ascii=False)}") |
|
|
|
print(f"[INFO] 上传结果接口返回: {json.dumps(result, ensure_ascii=False)}") |
|
|
|
|
|
|
|
|
|
|
|
# 检查返回结果 |
|
|
|
# 检查返回结果是否成功 |
|
|
|
if isinstance(result, dict): |
|
|
|
if isinstance(result, dict): |
|
|
|
|
|
|
|
# 判断返回状态是否为成功 |
|
|
|
if result.get('status') == 'success' or result.get('success') is True: |
|
|
|
if result.get('status') == 'success' or result.get('success') is True: |
|
|
|
print(f"[SUCCESS] 结果上传成功") |
|
|
|
print(f"[SUCCESS] 结果上传成功") |
|
|
|
return True |
|
|
|
return True |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# 如果返回结果不符合预期,打印警告信息 |
|
|
|
print(f"[WARNING] 结果上传可能失败: {result}") |
|
|
|
print(f"[WARNING] 结果上传可能失败: {result}") |
|
|
|
return False |
|
|
|
return False |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# 处理各种异常情况 |
|
|
|
except httpx.TimeoutException: |
|
|
|
except httpx.TimeoutException: |
|
|
|
print("[ERROR] 上传结果超时") |
|
|
|
print("[ERROR] 上传结果超时") |
|
|
|
return False |
|
|
|
return False |
|
|
|
@ -218,6 +347,9 @@ if __name__ == "__main__": |
|
|
|
|
|
|
|
|
|
|
|
print("开始获取 LLM 生成任务...") |
|
|
|
print("开始获取 LLM 生成任务...") |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# 这里我手动控制就行 |
|
|
|
|
|
|
|
llm_select = 0 |
|
|
|
|
|
|
|
|
|
|
|
while True: |
|
|
|
while True: |
|
|
|
# 获取任务数据 |
|
|
|
# 获取任务数据 |
|
|
|
task_data = fetch_llm_generate_task(odoo_base_url=ODOO_BASE_URL) |
|
|
|
task_data = fetch_llm_generate_task(odoo_base_url=ODOO_BASE_URL) |
|
|
|
@ -225,11 +357,20 @@ if __name__ == "__main__": |
|
|
|
if not task_data: |
|
|
|
if not task_data: |
|
|
|
break |
|
|
|
break |
|
|
|
|
|
|
|
|
|
|
|
llm_generate_result = call_llm_generate(task_data) |
|
|
|
if llm_select == 1: |
|
|
|
|
|
|
|
llm_generate_result = call_llm_generate(task_data) |
|
|
|
|
|
|
|
else: |
|
|
|
|
|
|
|
llm_generate_result = call_llm_generate_with_anthropic(task_data) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if not llm_generate_result: |
|
|
|
|
|
|
|
continue |
|
|
|
|
|
|
|
|
|
|
|
print(llm_generate_result) |
|
|
|
print(llm_generate_result[:100]) |
|
|
|
|
|
|
|
|
|
|
|
if llm_generate_result != "failed": |
|
|
|
# 检查是否成功:如果结果以 [ERROR] 或 [FAILED] 开头,说明失败了 |
|
|
|
|
|
|
|
is_failed = llm_generate_result.startswith('[ERROR]') or llm_generate_result.startswith('[FAILED]') |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if not is_failed: |
|
|
|
print("开始上传 LLM 生成结果...") |
|
|
|
print("开始上传 LLM 生成结果...") |
|
|
|
upload_data = { |
|
|
|
upload_data = { |
|
|
|
'record_id': task_data['id'], |
|
|
|
'record_id': task_data['id'], |
|
|
|
@ -242,7 +383,7 @@ if __name__ == "__main__": |
|
|
|
'record_id': task_data['id'], |
|
|
|
'record_id': task_data['id'], |
|
|
|
'status': 'failed', |
|
|
|
'status': 'failed', |
|
|
|
'result': '', |
|
|
|
'result': '', |
|
|
|
'error_message': 'generate failed' |
|
|
|
'error_message': llm_generate_result |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
# 上传结果到 Odoo |
|
|
|
# 上传结果到 Odoo |
|
|
|
@ -253,4 +394,3 @@ if __name__ == "__main__": |
|
|
|
print("[FAILED] 结果上传失败") |
|
|
|
print("[FAILED] 结果上传失败") |
|
|
|
|
|
|
|
|
|
|
|
time.sleep(60) |
|
|
|
time.sleep(60) |
|
|
|
|
|
|
|
|
|
|
|
|