diff --git a/example/llms/test_apis.py b/example/llms/test_apis.py new file mode 100644 index 00000000..8eaaaba9 --- /dev/null +++ b/example/llms/test_apis.py @@ -0,0 +1,53 @@ +import os +import sys + +sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))) + +from llm4ad.tools.llm.llm_api_qwen import QwenAPI +from llm4ad.tools.llm.llm_api_zhipu import ZhipuAPI +from llm4ad.tools.llm.llm_api_volcengine import VolcengineAPI +from llm4ad.tools.llm.llm_api_baiduqianfan import BaiduQianfanAPI +from llm4ad.tools.llm.llm_api_tencentcloud import TencentCloudAPI + + +def main(): + # Qwen + llm = QwenAPI( + key='your-api-key', + model='qwen-plus', + timeout=120 + ) + + # Zhipu AI + # llm = ZhipuAPI( + # key='your-api-key', + # model='GLM-5.2', + # timeout=120 + # ) + + # Volcengine Ark + # llm = VolcengineAPI( + # key='your-api-key', + # model='doubao-seed-character-260628', + # timeout=120 + # ) + + # Baidu Qianfan + # llm = BaiduQianfanAPI( + # key='your-api-key', + # model='ernie-5.1', + # timeout=120 + # ) + + # Tencent Cloud + # llm = TencentCloudAPI( + # key='your-api-key', + # model='hy3-preview', + # timeout=120 + # ) + + print(llm.draw_sample('hello')) + + +if __name__ == '__main__': + main() diff --git a/llm4ad/tools/llm/llm_api_baiduqianfan.py b/llm4ad/tools/llm/llm_api_baiduqianfan.py new file mode 100644 index 00000000..96c81f30 --- /dev/null +++ b/llm4ad/tools/llm/llm_api_baiduqianfan.py @@ -0,0 +1,154 @@ +# This file is part of the LLM4AD project (https://github.com/Optima-CityU/llm4ad). +# Last Revision: 2026/7/5 +# +# ------------------------------- Copyright -------------------------------- +# Copyright (c) 2025 Optima Group. +# +# Permission is granted to use the LLM4AD platform for research purposes. +# All publications, software, or other works that utilize this platform +# or any part of its codebase must acknowledge the use of "LLM4AD" and +# cite the following reference: +# +# Fei Liu, Rui Zhang, Zhuoliang Xie, Rui Sun, Kai Li, Xi Lin, Zhenkun Wang, +# Zhichao Lu, and Qingfu Zhang, "LLM4AD: A Platform for Algorithm Design +# with Large Language Model," arXiv preprint arXiv:2412.17287 (2024). +# +# For inquiries regarding commercial use or licensing, please contact +# http://www.llm4ad.com/contact.html +# -------------------------------------------------------------------------- + +from __future__ import annotations + +import http.client +import json +import time +from typing import Any +import traceback +from ...base import LLM + + +class BaiduQianfanAPI(LLM): + def __init__(self, host='qianfan.baidubce.com', key=None, model=None, + path='/v2/chat/completions', timeout=60, **kwargs): + """Baidu Qianfan API + Args: + host : host name. please note that the host name does not include 'https://' + key : API key. + model : LLM model name. + path : API path for chat completions. + timeout: API timeout. + """ + if key is None: + raise ValueError('BaiduQianfanAPI requires key.') + if model is None: + raise ValueError('BaiduQianfanAPI requires model.') + super().__init__(**kwargs) + self._host = host + self._path = path + self._key = key + self._model = model + self._timeout = timeout + self._kwargs = kwargs + self._cumulative_error = 0 + + def draw_sample(self, prompt: str | Any, *args, **kwargs) -> str: + """ + Sends a request to the LLM and retrieves the generated response. + + This method supports multiple input formats for backward compatibility: + 1. Explicit 'messages' list via kwargs. + 2. A message list passed directly as the 'prompt'. + 3. Multimodal inputs (text + base64 images). + 4. Simple string prompts. + + Args: + prompt: The text prompt or a list of message dictionaries. + **kwargs: Can include 'image64s' (list of base64 strings) or 'messages'. + + Returns: + The string content of the LLM's response. + """ + image64s = kwargs.get('image64s', None) # List[str] + messages_input = kwargs.get('messages', None) + + # --- 1. Priority: Explicit messages list --- + if messages_input is not None: + if isinstance(messages_input, dict): + messages = [messages_input] + else: + messages = messages_input + + # --- 2. Legacy Support: prompt passed as a pre-constructed list --- + elif not isinstance(prompt, str): + messages = prompt + + # --- 3. Construction from String + Optional Images --- + else: + text_content = prompt.strip() + + if image64s: + # Construct multimodal content structure + content = [{ + "type": "text", + "text": text_content + }] + for image in image64s: + content.append({ + "type": "image_url", + "image_url": { + "url": f"data:image/png;base64,{image}", + } + }) + messages = [{'role': 'user', 'content': content}] + + else: + # Construct standard text-only message + messages = [{'role': 'user', 'content': text_content}] + + # Retry loop for handling network or API transient errors + while True: + try: + conn = http.client.HTTPSConnection(self._host, timeout=self._timeout) + + # Prepare standard OpenAI-compatible payload + payload = json.dumps({ + 'max_tokens': self._kwargs.get('max_tokens', 8192), + 'top_p': self._kwargs.get('top_p', None), + 'temperature': self._kwargs.get('temperature', 1.0), + 'model': self._model, + 'messages': messages + }) + headers = { + 'Authorization': f'Bearer {self._key}', + 'User-Agent': 'Apifox/1.0.0 (https://apifox.com)', + 'Content-Type': 'application/json' + } + conn.request('POST', self._path, payload, headers) + res = conn.getresponse() + data = res.read().decode('utf-8') + data = json.loads(data) + + # Extract content from the standard response format + response = data['choices'][0]['message']['content'] + # Reset error counter on success + if self.debug_mode: + self._cumulative_error = 0 + return response + + except Exception as e: + self._cumulative_error += 1 + + # In debug mode, crash after consecutive failures to allow debugging + if self.debug_mode: + if self._cumulative_error == 10: + raise RuntimeError(f'{self.__class__.__name__} error: {traceback.format_exc()}.' + f'You may check your API host, path, API key, and model.') + else: + print(f'{self.__class__.__name__} error: {traceback.format_exc()}.' + f'You may check your API host, path, API key, and model.') + time.sleep(2) + continue + + +class QianfanAPI(BaiduQianfanAPI): + """Alias for BaiduQianfanAPI.""" diff --git a/llm4ad/tools/llm/llm_api_qwen.py b/llm4ad/tools/llm/llm_api_qwen.py new file mode 100644 index 00000000..f66903f6 --- /dev/null +++ b/llm4ad/tools/llm/llm_api_qwen.py @@ -0,0 +1,158 @@ +# This file is part of the LLM4AD project (https://github.com/Optima-CityU/llm4ad). +# Last Revision: 2026/7/5 +# +# ------------------------------- Copyright -------------------------------- +# Copyright (c) 2025 Optima Group. +# +# Permission is granted to use the LLM4AD platform for research purposes. +# All publications, software, or other works that utilize this platform +# or any part of its codebase must acknowledge the use of "LLM4AD" and +# cite the following reference: +# +# Fei Liu, Rui Zhang, Zhuoliang Xie, Rui Sun, Kai Li, Xi Lin, Zhenkun Wang, +# Zhichao Lu, and Qingfu Zhang, "LLM4AD: A Platform for Algorithm Design +# with Large Language Model," arXiv preprint arXiv:2412.17287 (2024). +# +# For inquiries regarding commercial use or licensing, please contact +# http://www.llm4ad.com/contact.html +# -------------------------------------------------------------------------- + +from __future__ import annotations + +import http.client +import json +import time +from typing import Any +import traceback +from ...base import LLM + + +class QwenAPI(LLM): + def __init__(self, host='dashscope.aliyuncs.com', key=None, model=None, + path='/compatible-mode/v1/chat/completions', timeout=60, **kwargs): + """Qwen API + Args: + host : host name. please note that the host name does not include 'https://' + key : API key. + model : LLM model name. + path : API path for chat completions. + timeout: API timeout. + """ + if key is None: + raise ValueError('QwenAPI requires key.') + if model is None: + raise ValueError('QwenAPI requires model.') + super().__init__(**kwargs) + self._host = host + self._path = path + self._key = key + self._model = model + self._timeout = timeout + self._kwargs = kwargs + self._cumulative_error = 0 + + def draw_sample(self, prompt: str | Any, *args, **kwargs) -> str: + """ + Sends a request to the LLM and retrieves the generated response. + + This method supports multiple input formats for backward compatibility: + 1. Explicit 'messages' list via kwargs. + 2. A message list passed directly as the 'prompt'. + 3. Multimodal inputs (text + base64 images). + 4. Simple string prompts. + + Args: + prompt: The text prompt or a list of message dictionaries. + **kwargs: Can include 'image64s' (list of base64 strings) or 'messages'. + + Returns: + The string content of the LLM's response. + """ + image64s = kwargs.get('image64s', None) # List[str] + messages_input = kwargs.get('messages', None) + + # --- 1. Priority: Explicit messages list --- + if messages_input is not None: + if isinstance(messages_input, dict): + messages = [messages_input] + else: + messages = messages_input + + # --- 2. Legacy Support: prompt passed as a pre-constructed list --- + elif not isinstance(prompt, str): + messages = prompt + + # --- 3. Construction from String + Optional Images --- + else: + text_content = prompt.strip() + + if image64s: + # Construct multimodal content structure + content = [{ + "type": "text", + "text": text_content + }] + for image in image64s: + content.append({ + "type": "image_url", + "image_url": { + "url": f"data:image/png;base64,{image}", + } + }) + messages = [{'role': 'user', 'content': content}] + + else: + # Construct standard text-only message + messages = [{'role': 'user', 'content': text_content}] + + # Retry loop for handling network or API transient errors + while True: + try: + conn = http.client.HTTPSConnection(self._host, timeout=self._timeout) + + # Prepare standard OpenAI-compatible payload + payload = json.dumps({ + 'max_tokens': self._kwargs.get('max_tokens', 8192), + 'top_p': self._kwargs.get('top_p', None), + 'temperature': self._kwargs.get('temperature', 1.0), + 'model': self._model, + 'messages': messages + }) + headers = { + 'Authorization': f'Bearer {self._key}', + 'User-Agent': 'Apifox/1.0.0 (https://apifox.com)', + 'Content-Type': 'application/json' + } + conn.request('POST', self._path, payload, headers) + res = conn.getresponse() + data = res.read().decode('utf-8') + data = json.loads(data) + + # Extract content from the standard response format + response = data['choices'][0]['message']['content'] + # Reset error counter on success + if self.debug_mode: + self._cumulative_error = 0 + return response + + except Exception as e: + self._cumulative_error += 1 + + # In debug mode, crash after consecutive failures to allow debugging + if self.debug_mode: + if self._cumulative_error == 10: + raise RuntimeError(f'{self.__class__.__name__} error: {traceback.format_exc()}.' + f'You may check your API host, path, API key, and model.') + else: + print(f'{self.__class__.__name__} error: {traceback.format_exc()}.' + f'You may check your API host, path, API key, and model.') + time.sleep(2) + continue + + +class DashScopeAPI(QwenAPI): + """Alias for QwenAPI.""" + + +class BailianAPI(QwenAPI): + """Alias for QwenAPI.""" diff --git a/llm4ad/tools/llm/llm_api_tencentcloud.py b/llm4ad/tools/llm/llm_api_tencentcloud.py new file mode 100644 index 00000000..a24a00c4 --- /dev/null +++ b/llm4ad/tools/llm/llm_api_tencentcloud.py @@ -0,0 +1,152 @@ +# This file is part of the LLM4AD project (https://github.com/Optima-CityU/llm4ad). +# Last Revision: 2026/7/5 +# +# ------------------------------- Copyright -------------------------------- +# Copyright (c) 2025 Optima Group. +# +# Permission is granted to use the LLM4AD platform for research purposes. +# All publications, software, or other works that utilize this platform +# or any part of its codebase must acknowledge the use of "LLM4AD" and +# cite the following reference: +# +# Fei Liu, Rui Zhang, Zhuoliang Xie, Rui Sun, Kai Li, Xi Lin, Zhenkun Wang, +# Zhichao Lu, and Qingfu Zhang, "LLM4AD: A Platform for Algorithm Design +# with Large Language Model," arXiv preprint arXiv:2412.17287 (2024). +# +# For inquiries regarding commercial use or licensing, please contact +# http://www.llm4ad.com/contact.html +# -------------------------------------------------------------------------- + +from __future__ import annotations + +import http.client +import json +import time +from typing import Any +import traceback +from ...base import LLM + + +class TencentCloudAPI(LLM): + def __init__(self, host='tokenhub.tencentmaas.com', key=None, model=None, + path='/v1/chat/completions', timeout=60, **kwargs): + """Tencent Cloud API + Args: + host : host name. please note that the host name does not include 'https://' + key : API key. + model : LLM model name. + path : API path for chat completions. + timeout: API timeout. + """ + if key is None: + raise ValueError('TencentCloudAPI requires key.') + if model is None: + raise ValueError('TencentCloudAPI requires model.') + super().__init__(**kwargs) + self._host = host + self._path = path + self._key = key + self._model = model + self._timeout = timeout + self._kwargs = kwargs + self._cumulative_error = 0 + + def draw_sample(self, prompt: str | Any, *args, **kwargs) -> str: + """ + Sends a request to the LLM and retrieves the generated response. + + This method supports multiple input formats for backward compatibility: + 1. Explicit 'messages' list via kwargs. + 2. A message list passed directly as the 'prompt'. + 3. Multimodal inputs (text + base64 images). + 4. Simple string prompts. + + Args: + prompt: The text prompt or a list of message dictionaries. + **kwargs: Can include 'image64s' (list of base64 strings) or 'messages'. + + Returns: + The string content of the LLM's response. + """ + image64s = kwargs.get('image64s', None) # List[str] + messages_input = kwargs.get('messages', None) + + # --- 1. Priority: Explicit messages list --- + if messages_input is not None: + if isinstance(messages_input, dict): + messages = [messages_input] + else: + messages = messages_input + + # --- 2. Legacy Support: prompt passed as a pre-constructed list --- + elif not isinstance(prompt, str): + messages = prompt + + # --- 3. Construction from String + Optional Images --- + else: + text_content = prompt.strip() + + if image64s: + # Construct multimodal content structure + content = [{ + "type": "text", + "text": text_content + }] + for image in image64s: + content.append({ + "type": "image_url", + "image_url": { + "url": f"data:image/png;base64,{image}", + } + }) + messages = [{'role': 'user', 'content': content}] + + else: + # Construct standard text-only message + messages = [{'role': 'user', 'content': text_content}] + + # Retry loop for handling network or API transient errors + while True: + try: + conn = http.client.HTTPSConnection(self._host, timeout=self._timeout) + + # Prepare standard OpenAI-compatible payload + payload = json.dumps({ + 'max_tokens': self._kwargs.get('max_tokens', 8192), + 'top_p': self._kwargs.get('top_p', None), + 'temperature': self._kwargs.get('temperature', 1.0), + 'model': self._model, + 'messages': messages + }) + headers = { + 'Authorization': f'Bearer {self._key}', + 'User-Agent': 'Apifox/1.0.0 (https://apifox.com)', + 'Content-Type': 'application/json' + } + conn.request('POST', self._path, payload, headers) + res = conn.getresponse() + data = res.read().decode('utf-8') + data = json.loads(data) + + # Extract content from the standard response format + response = data['choices'][0]['message']['content'] + # Reset error counter on success + if self.debug_mode: + self._cumulative_error = 0 + return response + + except Exception as e: + self._cumulative_error += 1 + + # In debug mode, crash after consecutive failures to allow debugging + if self.debug_mode: + if self._cumulative_error == 10: + raise RuntimeError(f'{self.__class__.__name__} error: {traceback.format_exc()}.' + f'You may check your API host, path, API key, and model.') + else: + print(f'{self.__class__.__name__} error: {traceback.format_exc()}.' + f'You may check your API host, path, API key, and model.') + time.sleep(2) + continue + + diff --git a/llm4ad/tools/llm/llm_api_volcengine.py b/llm4ad/tools/llm/llm_api_volcengine.py new file mode 100644 index 00000000..2fc42fcb --- /dev/null +++ b/llm4ad/tools/llm/llm_api_volcengine.py @@ -0,0 +1,155 @@ +# This file is part of the LLM4AD project (https://github.com/Optima-CityU/llm4ad). +# Last Revision: 2026/7/5 +# +# ------------------------------- Copyright -------------------------------- +# Copyright (c) 2025 Optima Group. +# +# Permission is granted to use the LLM4AD platform for research purposes. +# All publications, software, or other works that utilize this platform +# or any part of its codebase must acknowledge the use of "LLM4AD" and +# cite the following reference: +# +# Fei Liu, Rui Zhang, Zhuoliang Xie, Rui Sun, Kai Li, Xi Lin, Zhenkun Wang, +# Zhichao Lu, and Qingfu Zhang, "LLM4AD: A Platform for Algorithm Design +# with Large Language Model," arXiv preprint arXiv:2412.17287 (2024). +# +# For inquiries regarding commercial use or licensing, please contact +# http://www.llm4ad.com/contact.html +# -------------------------------------------------------------------------- + +from __future__ import annotations + +import http.client +import json +import time +from typing import Any +import traceback +from ...base import LLM + + +class VolcengineAPI(LLM): + def __init__(self, host='ark.cn-beijing.volces.com', key=None, model=None, + path='/api/v3/chat/completions', timeout=60, **kwargs): + """Volcengine API + Args: + host : host name. please note that the host name does not include 'https://' + key : API key. + model : LLM model name. + path : API path for chat completions. + timeout: API timeout. + """ + if key is None: + raise ValueError('VolcengineAPI requires key.') + if model is None: + raise ValueError('VolcengineAPI requires model.') + super().__init__(**kwargs) + self._host = host + self._path = path + self._key = key + self._model = model + self._timeout = timeout + self._kwargs = kwargs + self._cumulative_error = 0 + + def draw_sample(self, prompt: str | Any, *args, **kwargs) -> str: + """ + Sends a request to the LLM and retrieves the generated response. + + This method supports multiple input formats for backward compatibility: + 1. Explicit 'messages' list via kwargs. + 2. A message list passed directly as the 'prompt'. + 3. Multimodal inputs (text + base64 images). + 4. Simple string prompts. + + Args: + prompt: The text prompt or a list of message dictionaries. + **kwargs: Can include 'image64s' (list of base64 strings) or 'messages'. + + Returns: + The string content of the LLM's response. + """ + image64s = kwargs.get('image64s', None) # List[str] + messages_input = kwargs.get('messages', None) + + # --- 1. Priority: Explicit messages list --- + if messages_input is not None: + if isinstance(messages_input, dict): + messages = [messages_input] + else: + messages = messages_input + + # --- 2. Legacy Support: prompt passed as a pre-constructed list --- + elif not isinstance(prompt, str): + messages = prompt + + # --- 3. Construction from String + Optional Images --- + else: + text_content = prompt.strip() + + if image64s: + # Construct multimodal content structure + content = [{ + "type": "text", + "text": text_content + }] + for image in image64s: + content.append({ + "type": "image_url", + "image_url": { + "url": f"data:image/png;base64,{image}", + } + }) + messages = [{'role': 'user', 'content': content}] + + else: + # Construct standard text-only message + messages = [{'role': 'user', 'content': text_content}] + + # Retry loop for handling network or API transient errors + while True: + try: + conn = http.client.HTTPSConnection(self._host, timeout=self._timeout) + + # Prepare standard OpenAI-compatible payload + payload = json.dumps({ + 'max_tokens': self._kwargs.get('max_tokens', 8192), + 'top_p': self._kwargs.get('top_p', None), + 'temperature': self._kwargs.get('temperature', 1.0), + 'model': self._model, + 'messages': messages + }) + headers = { + 'Authorization': f'Bearer {self._key}', + 'User-Agent': 'Apifox/1.0.0 (https://apifox.com)', + 'Content-Type': 'application/json' + } + conn.request('POST', self._path, payload, headers) + res = conn.getresponse() + data = res.read().decode('utf-8') + data = json.loads(data) + + # Extract content from the standard response format + response = data['choices'][0]['message']['content'] + # Reset error counter on success + if self.debug_mode: + self._cumulative_error = 0 + return response + + except Exception as e: + self._cumulative_error += 1 + + # In debug mode, crash after consecutive failures to allow debugging + if self.debug_mode: + if self._cumulative_error == 10: + raise RuntimeError(f'{self.__class__.__name__} error: {traceback.format_exc()}.' + f'You may check your API host, path, API key, and model.') + else: + print(f'{self.__class__.__name__} error: {traceback.format_exc()}.' + f'You may check your API host, path, API key, and model.') + time.sleep(2) + continue + + +class DoubaoAPI(VolcengineAPI): + """Alias for VolcengineAPI.""" + diff --git a/llm4ad/tools/llm/llm_api_zhipu.py b/llm4ad/tools/llm/llm_api_zhipu.py new file mode 100644 index 00000000..bf17437a --- /dev/null +++ b/llm4ad/tools/llm/llm_api_zhipu.py @@ -0,0 +1,150 @@ +# This file is part of the LLM4AD project (https://github.com/Optima-CityU/llm4ad). +# Last Revision: 2026/7/5 +# +# ------------------------------- Copyright -------------------------------- +# Copyright (c) 2025 Optima Group. +# +# Permission is granted to use the LLM4AD platform for research purposes. +# All publications, software, or other works that utilize this platform +# or any part of its codebase must acknowledge the use of "LLM4AD" and +# cite the following reference: +# +# Fei Liu, Rui Zhang, Zhuoliang Xie, Rui Sun, Kai Li, Xi Lin, Zhenkun Wang, +# Zhichao Lu, and Qingfu Zhang, "LLM4AD: A Platform for Algorithm Design +# with Large Language Model," arXiv preprint arXiv:2412.17287 (2024). +# +# For inquiries regarding commercial use or licensing, please contact +# http://www.llm4ad.com/contact.html +# -------------------------------------------------------------------------- + +from __future__ import annotations + +import http.client +import json +import time +from typing import Any +import traceback +from ...base import LLM + + +class ZhipuAPI(LLM): + def __init__(self, host='open.bigmodel.cn', key=None, model=None, + path='/api/paas/v4/chat/completions', timeout=60, **kwargs): + """Zhipu AI API + Args: + host : host name. please note that the host name does not include 'https://' + key : API key. + model : LLM model name. + path : API path for chat completions. + timeout: API timeout. + """ + if key is None: + raise ValueError('ZhipuAPI requires key.') + if model is None: + raise ValueError('ZhipuAPI requires model.') + super().__init__(**kwargs) + self._host = host + self._path = path + self._key = key + self._model = model + self._timeout = timeout + self._kwargs = kwargs + self._cumulative_error = 0 + + def draw_sample(self, prompt: str | Any, *args, **kwargs) -> str: + """ + Sends a request to the LLM and retrieves the generated response. + + This method supports multiple input formats for backward compatibility: + 1. Explicit 'messages' list via kwargs. + 2. A message list passed directly as the 'prompt'. + 3. Multimodal inputs (text + base64 images). + 4. Simple string prompts. + + Args: + prompt: The text prompt or a list of message dictionaries. + **kwargs: Can include 'image64s' (list of base64 strings) or 'messages'. + + Returns: + The string content of the LLM's response. + """ + image64s = kwargs.get('image64s', None) # List[str] + messages_input = kwargs.get('messages', None) + + # --- 1. Priority: Explicit messages list --- + if messages_input is not None: + if isinstance(messages_input, dict): + messages = [messages_input] + else: + messages = messages_input + + # --- 2. Legacy Support: prompt passed as a pre-constructed list --- + elif not isinstance(prompt, str): + messages = prompt + + # --- 3. Construction from String + Optional Images --- + else: + text_content = prompt.strip() + + if image64s: + # Construct multimodal content structure + content = [{ + "type": "text", + "text": text_content + }] + for image in image64s: + content.append({ + "type": "image_url", + "image_url": { + "url": f"data:image/png;base64,{image}", + } + }) + messages = [{'role': 'user', 'content': content}] + + else: + # Construct standard text-only message + messages = [{'role': 'user', 'content': text_content}] + + # Retry loop for handling network or API transient errors + while True: + try: + conn = http.client.HTTPSConnection(self._host, timeout=self._timeout) + + # Prepare standard OpenAI-compatible payload + payload = json.dumps({ + 'max_tokens': self._kwargs.get('max_tokens', 8192), + 'top_p': self._kwargs.get('top_p', None), + 'temperature': self._kwargs.get('temperature', 1.0), + 'model': self._model, + 'messages': messages + }) + headers = { + 'Authorization': f'Bearer {self._key}', + 'User-Agent': 'Apifox/1.0.0 (https://apifox.com)', + 'Content-Type': 'application/json' + } + conn.request('POST', self._path, payload, headers) + res = conn.getresponse() + data = res.read().decode('utf-8') + data = json.loads(data) + + # Extract content from the standard response format + response = data['choices'][0]['message']['content'] + # Reset error counter on success + if self.debug_mode: + self._cumulative_error = 0 + return response + + except Exception as e: + self._cumulative_error += 1 + + # In debug mode, crash after consecutive failures to allow debugging + if self.debug_mode: + if self._cumulative_error == 10: + raise RuntimeError(f'{self.__class__.__name__} error: {traceback.format_exc()}.' + f'You may check your API host, path, API key, and model.') + else: + print(f'{self.__class__.__name__} error: {traceback.format_exc()}.' + f'You may check your API host, path, API key, and model.') + time.sleep(2) + continue