Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 53 additions & 0 deletions example/llms/test_apis.py
Original file line number Diff line number Diff line change
@@ -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()
154 changes: 154 additions & 0 deletions llm4ad/tools/llm/llm_api_baiduqianfan.py
Original file line number Diff line number Diff line change
@@ -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."""
158 changes: 158 additions & 0 deletions llm4ad/tools/llm/llm_api_qwen.py
Original file line number Diff line number Diff line change
@@ -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."""
Loading