diff --git a/config/examples/tencent-hunyuan.yaml b/config/examples/tencent-hunyuan.yaml new file mode 100644 index 000000000..86473411f --- /dev/null +++ b/config/examples/tencent-hunyuan.yaml @@ -0,0 +1,6 @@ +llm: + api_type: "hunyuan" # 腾讯混元大模型 + model: "hunyuan-standard" # 可选 hunyuan-lite、hunyuan-standard 、hunyuan-standard-256K、hunyuan-pro、hunyuan-turbo、hunyuan-code + endpoint: "hunyuan.tencentcloudapi.com" #默认 hunyuan.tencentcloudapi.com + secret_id: "" #腾讯云账户 SecretId 和 SecretKey,请注意保密。申请链接:https://console.cloud.tencent.com/cam/capi + secret_key: "" diff --git a/metagpt/configs/llm_config.py b/metagpt/configs/llm_config.py index 60118303c..c39f9a53b 100644 --- a/metagpt/configs/llm_config.py +++ b/metagpt/configs/llm_config.py @@ -35,6 +35,7 @@ class LLMType(Enum): OPENROUTER = "openrouter" BEDROCK = "bedrock" ARK = "ark" # https://www.volcengine.com/docs/82379/1263482#python-sdk + HUNYUAN = "hunyuan" # Tencent Hunyuan def __missing__(self, key): return self.OPENAI @@ -60,6 +61,8 @@ class LLMConfig(YamlModel): secret_key: Optional[str] = None session_token: Optional[str] = None endpoint: Optional[str] = None # for self-deployed model on the cloud + # For Tencent Hunyuan + secret_id: Optional[str] = None # For Spark(Xunfei), maybe remove later app_id: Optional[str] = None diff --git a/metagpt/provider/__init__.py b/metagpt/provider/__init__.py index c90f5774a..e75991121 100644 --- a/metagpt/provider/__init__.py +++ b/metagpt/provider/__init__.py @@ -19,6 +19,7 @@ from metagpt.provider.anthropic_api import AnthropicLLM from metagpt.provider.bedrock_api import BedrockLLM from metagpt.provider.ark_api import ArkLLM +from metagpt.provider.hunyuan_api import HunYuanLLM __all__ = [ "GeminiLLM", @@ -34,4 +35,5 @@ "AnthropicLLM", "BedrockLLM", "ArkLLM", + "HunYuanLLM", ] diff --git a/metagpt/provider/hunyuan_api.py b/metagpt/provider/hunyuan_api.py new file mode 100644 index 000000000..61868d675 --- /dev/null +++ b/metagpt/provider/hunyuan_api.py @@ -0,0 +1,107 @@ +# -*- coding: utf-8 -*- +import json +import types + +from tencentcloud.common import credential +from tencentcloud.common.profile.client_profile import ClientProfile +from tencentcloud.common.profile.http_profile import HttpProfile +from tencentcloud.hunyuan.v20230901.hunyuan_client import HunyuanClient +from tencentcloud.hunyuan.v20230901.models import ( + ChatCompletionsRequest, + ChatCompletionsResponse, +) + +from metagpt.configs.llm_config import LLMConfig, LLMType +from metagpt.const import USE_CONFIG_TIMEOUT +from metagpt.logs import log_llm_stream +from metagpt.provider.base_llm import BaseLLM +from metagpt.provider.llm_provider_registry import register_provider +from metagpt.utils.cost_manager import CostManager +from metagpt.utils.token_counter import HUNYUAN_MODEL_TOKEN_COSTS + + +@register_provider(LLMType.HUNYUAN) +class HunYuanLLM(BaseLLM): + """参考资料 + 腾讯混元大模型产品概述:https://cloud.tencent.com/document/product/1729/104753 + 腾讯混元API接口说明:https://cloud.tencent.com/document/api/1729/105701 + 腾讯混元Python SDK源码:https://github.com/TencentCloud/tencentcloud-sdk-python/blob/master/tencentcloud/hunyuan/v20230901/models.py + 腾讯云控制台API密钥管理:https://console.cloud.tencent.com/cam/capi + """ + + def __init__(self, config: LLMConfig): + self.config = config + self.secret_id = self.config.secret_id + self.secret_key = self.config.secret_key + self.endpoint = self.config.endpoint + self.model = self.config.model + self.region = "" + self._init_client() + self.cost_manager = CostManager(token_costs=HUNYUAN_MODEL_TOKEN_COSTS) + + def _init_client(self): + """实例化一个认证客户端对象""" + cred = credential.Credential(self.secret_id, self.secret_key) + httpProfile = HttpProfile() + httpProfile.endpoint = self.endpoint + clientProfile = ClientProfile() + clientProfile.httpProfile = httpProfile + self.aclient: HunyuanClient = HunyuanClient(cred, self.region, clientProfile) + + def _format_messages(self, messages: list[dict]) -> list[dict]: + """将role和content转换为Role和Content""" + new_messages = [] + for message in messages: + new_messages.append({"Role": message["role"], "Content": message["content"]}) + return new_messages + + def _make_request( + self, + messages: list[dict], + stream=True, + ): + """构造请求参数对象""" + req = ChatCompletionsRequest() + params = { + "Model": self.model, + "Messages": self._format_messages(messages), + "Stream": stream, + } + req.from_json_string(json.dumps(params)) + return req + + async def _achat_completion(self, messages: list[dict], timeout=USE_CONFIG_TIMEOUT) -> ChatCompletionsResponse: + resp: ChatCompletionsResponse = self.aclient.ChatCompletions( + self._make_request(messages, timeout, stream=False) + ) + # 转换为字典格式 + usage = { + "prompt_tokens": resp.Usage.PromptTokens, + "completion_tokens": resp.Usage.CompletionTokens, + } + self._update_costs(usage) + return resp + + async def acompletion(self, messages: list[dict], timeout=USE_CONFIG_TIMEOUT) -> ChatCompletionsResponse: + return await self._achat_completion(messages, timeout) + + async def _achat_completion_stream(self, messages: list[dict], timeout: int = USE_CONFIG_TIMEOUT) -> str: + resp = self.aclient.ChatCompletions(self._make_request(messages, timeout, stream=True)) + full_reply_content = "" + usage = {} + if isinstance(resp, types.GeneratorType): # 流式响应 + for event in resp: + data = json.loads(event["data"]) + usage = data.get("Usage", {}) + for choice in data["Choices"]: + content = choice["Delta"]["Content"] + log_llm_stream(content) + full_reply_content += content + self._update_costs( + { + "prompt_tokens": usage.get("PromptTokens", 0), + "completion_tokens": usage.get("CompletionTokens", 0), + } + ) + log_llm_stream("\n") + return full_reply_content diff --git a/metagpt/utils/token_counter.py b/metagpt/utils/token_counter.py index c922f2cb4..bf9ab8d2e 100644 --- a/metagpt/utils/token_counter.py +++ b/metagpt/utils/token_counter.py @@ -95,6 +95,17 @@ "llama3-8b-llama3-8b-instruct": {"prompt": 0.0, "completion": 0.0}, } +""" +腾讯混元大模型价格说明:https://cloud.tencent.com/document/product/1729/97731 +""" +HUNYUAN_MODEL_TOKEN_COSTS = { + "hunyuan-turbo": {"prompt": 0.00211, "completion": 0.00703}, + "hunyuan-pro": {"prompt": 0.0042, "completion": 0.014}, + "hunyuan-standard": {"prompt": 0.00063, "completion": 0.0007}, + "hunyuan-standard-256k": {"prompt": 0.0021, "completion": 0.0084}, + "hunyuan-lite": {"prompt": 0.0, "completion": 0.0}, + "hunyuan-code": {"prompt": 0.00056, "completion": 0.00113}, +} """ QianFan Token Price https://cloud.baidu.com/doc/WENXINWORKSHOP/s/hlrk4akp7#tokens%E5%90%8E%E4%BB%98%E8%B4%B9 diff --git a/requirements.txt b/requirements.txt index 5344ffb8c..0081812ae 100644 --- a/requirements.txt +++ b/requirements.txt @@ -81,4 +81,5 @@ volcengine-python-sdk[ark]~=1.0.94 # Solution for installation error in Windows: gymnasium==0.29.1 boto3~=1.34.69 spark_ai_python~=0.3.30 -agentops \ No newline at end of file +tencentcloud-sdk-python-hunyuan==3.0.1163 # for tencent hunyuan +agentops