Skip to content

Commit

Permalink
feat: Integrate moonshot models to camel (#1526)
Browse files Browse the repository at this point in the history
  • Loading branch information
GitHoobar authored Feb 4, 2025
1 parent 03c3f22 commit 70ff695
Show file tree
Hide file tree
Showing 14 changed files with 350 additions and 0 deletions.
3 changes: 3 additions & 0 deletions .env
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,9 @@
# InternLM API (https://internlm.intern-ai.org.cn/api/tokens)
# INTERNLM_API_KEY="Fill your API key here"

# Moonshot API (https://platform.moonshot.cn/)
# MOONSHOT_API_KEY="Fill your API key here"

# JINA API (https://jina.ai/)
# JINA_API_KEY="Fill your API key here"

Expand Down
1 change: 1 addition & 0 deletions .github/workflows/build_package.yml
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ jobs:
DISCORD_BOT_TOKEN: "${{ secrets.DISCORD_BOT_TOKEN }}"
INTERNLM_API_KEY: "${{ secrets.INTERNLM_API_KEY }}"
JINA_API_KEY: "${{ secrets.JINA_API_KEY }}"
MOONSHOT_API_KEY: "${{ secrets.MOONSHOT_API_KEY }}"
run: |
source venv/bin/activate
pytest --fast-test-mode ./test
Expand Down
2 changes: 2 additions & 0 deletions .github/workflows/pytest_apps.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ jobs:
SEARCH_ENGINE_ID: "${{ secrets.SEARCH_ENGINE_ID }}"
COHERE_API_KEY: "${{ secrets.COHERE_API_KEY }}"
INTERNLM_API_KEY: "${{ secrets.INTERNLM_API_KEY }}"
MOONSHOT_API_KEY: "${{ secrets.MOONSHOT_API_KEY }}"
run: poetry run pytest -v apps/

pytest_examples:
Expand All @@ -49,4 +50,5 @@ jobs:
SEARCH_ENGINE_ID: "${{ secrets.SEARCH_ENGINE_ID }}"
COHERE_API_KEY: "${{ secrets.COHERE_API_KEY }}"
INTERNLM_API_KEY: "${{ secrets.INTERNLM_API_KEY }}"
MOONSHOT_API_KEY: "${{ secrets.MOONSHOT_API_KEY }}"
run: poetry run pytest -v examples/
3 changes: 3 additions & 0 deletions .github/workflows/pytest_package.yml
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ jobs:
DISCORD_BOT_TOKEN: "${{ secrets.DISCORD_BOT_TOKEN }}"
INTERNLM_API_KEY: "${{ secrets.INTERNLM_API_KEY }}"
JINA_API_KEY: "${{ secrets.JINA_API_KEY }}"
MOONSHOT_API_KEY: "${{ secrets.MOONSHOT_API_KEY }}"
run: poetry run pytest --fast-test-mode test/

pytest_package_llm_test:
Expand Down Expand Up @@ -107,6 +108,7 @@ jobs:
DISCORD_BOT_TOKEN: "${{ secrets.DISCORD_BOT_TOKEN }}"
INTERNLM_API_KEY: "${{ secrets.INTERNLM_API_KEY }}"
JINA_API_KEY: "${{ secrets.JINA_API_KEY }}"
MOONSHOT_API_KEY: "${{ secrets.MOONSHOT_API_KEY }}"
run: poetry run pytest --llm-test-only test/

pytest_package_very_slow_test:
Expand Down Expand Up @@ -155,4 +157,5 @@ jobs:
DISCORD_BOT_TOKEN: "${{ secrets.DISCORD_BOT_TOKEN }}"
INTERNLM_API_KEY: "${{ secrets.INTERNLM_API_KEY }}"
JINA_API_KEY: "${{ secrets.JINA_API_KEY }}"
MOONSHOT_API_KEY: "${{ secrets.MOONSHOT_API_KEY }}"
run: poetry run pytest --very-slow-test-only test/
3 changes: 3 additions & 0 deletions camel/configs/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
from .internlm_config import INTERNLM_API_PARAMS, InternLMConfig
from .litellm_config import LITELLM_API_PARAMS, LiteLLMConfig
from .mistral_config import MISTRAL_API_PARAMS, MistralConfig
from .moonshot_config import MOONSHOT_API_PARAMS, MoonshotConfig
from .nvidia_config import NVIDIA_API_PARAMS, NvidiaConfig
from .ollama_config import OLLAMA_API_PARAMS, OllamaConfig
from .openai_config import OPENAI_API_PARAMS, ChatGPTConfig
Expand Down Expand Up @@ -79,4 +80,6 @@
'DEEPSEEK_API_PARAMS',
'InternLMConfig',
'INTERNLM_API_PARAMS',
'MoonshotConfig',
"MOONSHOT_API_PARAMS",
]
63 changes: 63 additions & 0 deletions camel/configs/moonshot_config.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
# ========= Copyright 2023-2024 @ CAMEL-AI.org. All Rights Reserved. =========
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# ========= Copyright 2023-2024 @ CAMEL-AI.org. All Rights Reserved. =========

from typing import List, Optional, Union

from camel.configs.base_config import BaseConfig


class MoonshotConfig(BaseConfig):
r"""Defines the parameters for generating chat completions using the
Moonshot API. You can refer to the following link for more details:
https://platform.moonshot.cn/docs/api-reference
Args:
temperature (float, optional): Controls randomness in the response.
Lower values make the output more focused and deterministic.
(default: :obj:`0.3`)
max_tokens (int, optional): The maximum number of tokens to generate.
(default: :obj:`None`)
stream (bool, optional): Whether to stream the response.
(default: :obj:`False`)
tools (list, optional): List of tools that the model can use for
function calling. Each tool should be a dictionary containing
type, function name, description, and parameters.
(default: :obj:`None`)
top_p (float, optional): Controls diversity via nucleus sampling.
(default: :obj:`1.0`)
n (int, optional): How many chat completion choices to generate for
each input message. (default: :obj:`1`)
presence_penalty (float, optional): Penalty for new tokens based on
whether they appear in the text so far.
(default: :obj:`0.0`)
frequency_penalty (float, optional): Penalty for new tokens based on
their frequency in the text so far.
(default: :obj:`0.0`)
stop (Optional[Union[str, List[str]]], optional): Up to 4 sequences
where the API will stop generating further tokens.
(default: :obj:`None`)
"""

temperature: float = 0.3
max_tokens: Optional[int] = None
stream: bool = False
tools: Optional[list] = None
top_p: float = 1.0
n: int = 1
presence_penalty: float = 0.0
frequency_penalty: float = 0.0
stop: Optional[Union[str, List[str]]] = None


MOONSHOT_API_PARAMS = {param for param in MoonshotConfig.model_fields.keys()}
2 changes: 2 additions & 0 deletions camel/models/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
from .mistral_model import MistralModel
from .model_factory import ModelFactory
from .model_manager import ModelManager, ModelProcessingError
from .moonshot_model import MoonshotModel
from .nemotron_model import NemotronModel
from .nvidia_model import NvidiaModel
from .ollama_model import OllamaModel
Expand Down Expand Up @@ -70,4 +71,5 @@
'DeepSeekModel',
'FishAudioModel',
'InternLMModel',
'MoonshotModel',
]
3 changes: 3 additions & 0 deletions camel/models/model_factory.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
from camel.models.internlm_model import InternLMModel
from camel.models.litellm_model import LiteLLMModel
from camel.models.mistral_model import MistralModel
from camel.models.moonshot_model import MoonshotModel
from camel.models.nvidia_model import NvidiaModel
from camel.models.ollama_model import OllamaModel
from camel.models.openai_compatible_model import OpenAICompatibleModel
Expand Down Expand Up @@ -127,6 +128,8 @@ def create(
model_class = DeepSeekModel
elif model_platform.is_internlm and model_type.is_internlm:
model_class = InternLMModel
elif model_platform.is_moonshot and model_type.is_moonshot:
model_class = MoonshotModel
elif model_type == ModelType.STUB:
model_class = StubModel

Expand Down
138 changes: 138 additions & 0 deletions camel/models/moonshot_model.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
# ========= Copyright 2023-2024 @ CAMEL-AI.org. All Rights Reserved. =========
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# ========= Copyright 2023-2024 @ CAMEL-AI.org. All Rights Reserved. =========

import os
from typing import Any, Dict, List, Optional, Union

from openai import OpenAI, Stream

from camel.configs import MOONSHOT_API_PARAMS, MoonshotConfig
from camel.messages import OpenAIMessage
from camel.models import BaseModelBackend
from camel.types import (
ChatCompletion,
ChatCompletionChunk,
ModelType,
)
from camel.utils import (
BaseTokenCounter,
OpenAITokenCounter,
api_keys_required,
)


class MoonshotModel(BaseModelBackend):
r"""Moonshot API in a unified BaseModelBackend interface.
Args:
model_type (Union[ModelType, str]): Model for which a backend is
created, one of Moonshot series.
model_config_dict (Optional[Dict[str, Any]], optional): A dictionary
that will be fed into :obj:`openai.ChatCompletion.create()`. If
:obj:`None`, :obj:`MoonshotConfig().as_dict()` will be used.
(default: :obj:`None`)
api_key (Optional[str], optional): The API key for authenticating with
the Moonshot service. (default: :obj:`None`)
url (Optional[str], optional): The url to the Moonshot service.
(default: :obj:`https://api.moonshot.cn/v1`)
token_counter (Optional[BaseTokenCounter], optional): Token counter to
use for the model. If not provided, :obj:`OpenAITokenCounter(
ModelType.GPT_4)` will be used.
(default: :obj:`None`)
"""

@api_keys_required([("api_key", "MOONSHOT_API_KEY")])
def __init__(
self,
model_type: Union[ModelType, str],
model_config_dict: Optional[Dict[str, Any]] = None,
api_key: Optional[str] = None,
url: Optional[str] = None,
token_counter: Optional[BaseTokenCounter] = None,
) -> None:
if model_config_dict is None:
model_config_dict = MoonshotConfig().as_dict()
api_key = api_key or os.environ.get("MOONSHOT_API_KEY")
url = url or os.environ.get(
"MOONSHOT_API_BASE_URL",
"https://api.moonshot.cn/v1",
)
super().__init__(
model_type, model_config_dict, api_key, url, token_counter
)
self._client = OpenAI(
api_key=self._api_key,
timeout=180,
max_retries=3,
base_url=self._url,
)

def run(
self,
messages: List[OpenAIMessage],
) -> Union[ChatCompletion, Stream[ChatCompletionChunk]]:
r"""Runs inference of Moonshot chat completion.
Args:
messages (List[OpenAIMessage]): Message list with the chat history
in OpenAI API format.
Returns:
Union[ChatCompletion, Stream[ChatCompletionChunk]]:
`ChatCompletion` in the non-stream mode, or
`Stream[ChatCompletionChunk]` in the stream mode.
"""
response = self._client.chat.completions.create(
messages=messages,
model=self.model_type,
**self.model_config_dict,
)
return response

@property
def token_counter(self) -> BaseTokenCounter:
r"""Initialize the token counter for the model backend.
Returns:
OpenAITokenCounter: The token counter following the model's
tokenization style.
"""
if not self._token_counter:
self._token_counter = OpenAITokenCounter(ModelType.GPT_4O_MINI)
return self._token_counter

def check_model_config(self):
r"""Check whether the model configuration contains any
unexpected arguments to Moonshot API.
Raises:
ValueError: If the model configuration dictionary contains any
unexpected arguments to Moonshot API.
"""
for param in self.model_config_dict:
if param not in MOONSHOT_API_PARAMS:
raise ValueError(
f"Unexpected argument `{param}` is "
"input into Moonshot model backend."
)

@property
def stream(self) -> bool:
r"""Returns whether the model is in stream mode, which sends partial
results each time.
Returns:
bool: Whether the model is in stream mode.
"""
return self.model_config_dict.get('stream', False)
23 changes: 23 additions & 0 deletions camel/types/enums.py
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,11 @@ class ModelType(UnifiedModelType, Enum):
INTERNLM2_5_LATEST = "internlm2.5-latest"
INTERNLM2_PRO_CHAT = "internlm2-pro-chat"

# Moonshot models
MOONSHOT_V1_8K = "moonshot-v1-8k"
MOONSHOT_V1_32K = "moonshot-v1-32k"
MOONSHOT_V1_128K = "moonshot-v1-128k"

def __str__(self):
return self.value

Expand Down Expand Up @@ -201,6 +206,7 @@ def support_native_tool_calling(self) -> bool:
self.is_sambanova,
self.is_groq,
self.is_sglang,
self.is_moonshot,
]
)

Expand Down Expand Up @@ -422,6 +428,14 @@ def is_internlm(self) -> bool:
ModelType.INTERNLM2_PRO_CHAT,
}

@property
def is_moonshot(self) -> bool:
return self in {
ModelType.MOONSHOT_V1_8K,
ModelType.MOONSHOT_V1_32K,
ModelType.MOONSHOT_V1_128K,
}

@property
def is_sglang(self) -> bool:
return self in {
Expand Down Expand Up @@ -469,6 +483,7 @@ def token_limit(self) -> int:
ModelType.QWEN_VL_PLUS,
ModelType.NVIDIA_LLAMA3_70B,
ModelType.TOGETHER_MISTRAL_7B,
ModelType.MOONSHOT_V1_8K,
}:
return 8_192
elif self in {
Expand Down Expand Up @@ -502,6 +517,7 @@ def token_limit(self) -> int:
ModelType.INTERNLM2_PRO_CHAT,
ModelType.TOGETHER_MIXTRAL_8_7B,
ModelType.SGLANG_MISTRAL_7B,
ModelType.MOONSHOT_V1_32K,
}:
return 32_768
elif self in {
Expand Down Expand Up @@ -546,6 +562,7 @@ def token_limit(self) -> int:
ModelType.SGLANG_LLAMA_3_1_405B,
ModelType.SGLANG_LLAMA_3_2_1B,
ModelType.SGLANG_MIXTRAL_NEMO,
ModelType.MOONSHOT_V1_128K,
}:
return 128_000
elif self in {
Expand Down Expand Up @@ -767,6 +784,7 @@ class ModelPlatformType(Enum):
DEEPSEEK = "deepseek"
SGLANG = "sglang"
INTERNLM = "internlm"
MOONSHOT = "moonshot"

@property
def is_openai(self) -> bool:
Expand Down Expand Up @@ -874,6 +892,11 @@ def is_internlm(self) -> bool:
r"""Returns whether this platform is InternLM."""
return self is ModelPlatformType.INTERNLM

@property
def is_moonshot(self) -> bool:
r"""Returns whether this platform is Moonshot model."""
return self is ModelPlatformType.MOONSHOT


class AudioModelType(Enum):
TTS_1 = "tts-1"
Expand Down
5 changes: 5 additions & 0 deletions camel/types/unified_model_type.py
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,11 @@ def is_internlm(self) -> bool:
r"""Returns whether the model is a InternLM model."""
return True

@property
def is_moonshot(self) -> bool:
r"""Returns whether this platform is Moonshot model."""
return True

@property
def support_native_structured_output(self) -> bool:
r"""Returns whether the model supports native structured output."""
Expand Down
Loading

0 comments on commit 70ff695

Please sign in to comment.