diff --git a/api_llm_generate/api_llm_generate.py b/api_llm_generate/api_llm_generate.py index db7314a..89290b6 100644 --- a/api_llm_generate/api_llm_generate.py +++ b/api_llm_generate/api_llm_generate.py @@ -3,6 +3,7 @@ import time import httpx import json +import anthropic 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" """ if task_data is None: - print("[ERROR] task_data 为 None,无法调用 LLM") - return "failed" + error_msg = "[ERROR] task_data 为 None,无法调用 LLM" + print(error_msg) + return error_msg # 提取 task_data 中的字段 final_prompt = task_data.get('final_prompt', '') @@ -153,6 +155,124 @@ def call_llm_generate(task_data, max_retries=3): 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): """ 上传 LLM 生成结果到 Odoo @@ -168,36 +288,45 @@ def upload_llm_result(odoo_base_url, upload_data): Returns: bool: 上传成功返回 True,失败返回 False """ + # 构建上传结果的 API URL url = f"{odoo_base_url}/api/alpha-idea/result" + # 设置请求头,指定内容类型为 JSON headers = { "Content-Type": "application/json" } try: + # 打印上传信息日志 print(f"[INFO] 正在上传结果到 Odoo: {url}") print(f"[INFO] 上传数据: {json.dumps(upload_data, ensure_ascii=False)}") + # 发送 POST 请求,设置超时时间为 30 秒 response = httpx.post( url, headers=headers, json=upload_data, timeout=30 ) + # 如果 HTTP 状态码表示错误,则抛出异常 response.raise_for_status() + # 解析响应结果 result = response.json() print(f"[INFO] 上传结果接口返回: {json.dumps(result, ensure_ascii=False)}") - # 检查返回结果 + # 检查返回结果是否成功 if isinstance(result, dict): + # 判断返回状态是否为成功 if result.get('status') == 'success' or result.get('success') is True: print(f"[SUCCESS] 结果上传成功") return True + # 如果返回结果不符合预期,打印警告信息 print(f"[WARNING] 结果上传可能失败: {result}") return False + # 处理各种异常情况 except httpx.TimeoutException: print("[ERROR] 上传结果超时") return False @@ -218,6 +347,9 @@ if __name__ == "__main__": print("开始获取 LLM 生成任务...") + # 这里我手动控制就行 + llm_select = 0 + while True: # 获取任务数据 task_data = fetch_llm_generate_task(odoo_base_url=ODOO_BASE_URL) @@ -225,11 +357,20 @@ if __name__ == "__main__": if not task_data: 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 生成结果...") upload_data = { 'record_id': task_data['id'], @@ -242,7 +383,7 @@ if __name__ == "__main__": 'record_id': task_data['id'], 'status': 'failed', 'result': '', - 'error_message': 'generate failed' + 'error_message': llm_generate_result } # 上传结果到 Odoo @@ -252,5 +393,4 @@ if __name__ == "__main__": else: print("[FAILED] 结果上传失败") - time.sleep(60) - + time.sleep(60) \ No newline at end of file diff --git a/test/call_llm_with_anthropic.py b/test/call_llm_with_anthropic.py new file mode 100644 index 0000000..e980235 --- /dev/null +++ b/test/call_llm_with_anthropic.py @@ -0,0 +1,20 @@ +import anthropic + + +client = anthropic.Anthropic( + api_key="sk-cp-l_as8mjqPhsOIny9IFKZ8jzA92z1c0eRwchldhEf4KzQjs9cjVknV2o7VNCcvYUXsXFq7uF4aSgp2RxxmUHLXwPGKgIvzedM70_XUIXiBB3gu_UmLDQLfh4", + base_url="https://api.minimaxi.com/anthropic" +) + +message = client.messages.create( + model="MiniMax-M2.7", + max_tokens=1000, + system="You are a helpful assistant.", + messages=[{"role": "user", "content": [{"type": "text", "text": "Hi, how are you?"}]}] +) + +for block in message.content: + if block.type == "thinking": + print(f"Thinking:\n{block.thinking}\n") + elif block.type == "text": + print(f"Text:\n{block.text}\n")