555e282cc4
pi-agent-plugin checks / lint (push) Has been cancelled
pi-agent-plugin checks / test (20) (push) Has been cancelled
pi-agent-plugin checks / test (22) (push) Has been cancelled
pi-agent-plugin checks / build (push) Has been cancelled
TypeScript SDK CI / check_changes (push) Has been cancelled
TypeScript SDK CI / changelog_check (push) Has been cancelled
ci / changelog_check (push) Has been cancelled
ci / check_changes (push) Has been cancelled
ci / build_mem0 (3.10) (push) Has been cancelled
ci / build_mem0 (3.11) (push) Has been cancelled
ci / build_mem0 (3.12) (push) Has been cancelled
CLI Node CI / lint (push) Has been cancelled
CLI Node CI / test (20) (push) Has been cancelled
CLI Node CI / test (22) (push) Has been cancelled
CLI Node CI / build (push) Has been cancelled
CLI Python CI / lint (push) Has been cancelled
CLI Python CI / test (3.10) (push) Has been cancelled
CLI Python CI / test (3.11) (push) Has been cancelled
CLI Python CI / test (3.12) (push) Has been cancelled
CLI Python CI / build (push) Has been cancelled
openclaw checks / lint (push) Has been cancelled
openclaw checks / test (20) (push) Has been cancelled
openclaw checks / test (22) (push) Has been cancelled
openclaw checks / build (push) Has been cancelled
opencode-plugin checks / build (push) Has been cancelled
TypeScript SDK CI / build_ts_sdk (20) (push) Has been cancelled
TypeScript SDK CI / build_ts_sdk (22) (push) Has been cancelled
TypeScript SDK CI / integration_ts_sdk (20) (push) Has been cancelled
TypeScript SDK CI / integration_ts_sdk (22) (push) Has been cancelled
176 lines
6.3 KiB
Python
176 lines
6.3 KiB
Python
from abc import ABC, abstractmethod
|
|
from typing import Dict, List, Optional, Union
|
|
|
|
from mem0.configs.llms.base import BaseLlmConfig
|
|
|
|
|
|
class LLMBase(ABC):
|
|
"""
|
|
Base class for all LLM providers.
|
|
Handles common functionality and delegates provider-specific logic to subclasses.
|
|
"""
|
|
|
|
def __init__(self, config: Optional[Union[BaseLlmConfig, Dict]] = None):
|
|
"""Initialize a base LLM class
|
|
|
|
:param config: LLM configuration option class or dict, defaults to None
|
|
:type config: Optional[Union[BaseLlmConfig, Dict]], optional
|
|
"""
|
|
if config is None:
|
|
self.config = BaseLlmConfig()
|
|
elif isinstance(config, dict):
|
|
# Handle dict-based configuration (backward compatibility)
|
|
self.config = BaseLlmConfig(**config)
|
|
else:
|
|
self.config = config
|
|
|
|
# Validate configuration
|
|
self._validate_config()
|
|
|
|
def _validate_config(self):
|
|
"""
|
|
Validate the configuration.
|
|
Override in subclasses to add provider-specific validation.
|
|
"""
|
|
if not hasattr(self.config, "model"):
|
|
raise ValueError("Configuration must have a 'model' attribute")
|
|
|
|
if not hasattr(self.config, "api_key") and not hasattr(self.config, "api_key"):
|
|
# Check if API key is available via environment variable
|
|
# This will be handled by individual providers
|
|
pass
|
|
|
|
def _is_reasoning_model(self, model: str) -> bool:
|
|
"""
|
|
Check if the model is a reasoning model or GPT-5 series that doesn't support certain parameters.
|
|
|
|
An explicit ``is_reasoning_model`` on the config takes precedence over the
|
|
name-based heuristic. This lets deployments with custom/versioned model
|
|
names (e.g. Azure ``gpt-5.4-nano-2026-03-17``) opt in or out without
|
|
relying on string matching. When the config value is ``None`` (default),
|
|
classification falls back to the name-based heuristic below.
|
|
|
|
Args:
|
|
model: The model name to check
|
|
|
|
Returns:
|
|
bool: True if the model is a reasoning model or GPT-5 series
|
|
"""
|
|
explicit = getattr(self.config, "is_reasoning_model", None)
|
|
if explicit is not None:
|
|
return explicit
|
|
|
|
reasoning_models = {
|
|
"o1", "o1-preview", "o3-mini", "o3",
|
|
"gpt-5", "gpt-5o", "gpt-5o-mini", "gpt-5o-micro",
|
|
}
|
|
|
|
model_lower = model.lower()
|
|
# Strip provider prefixes (e.g. "openai/o3-mini" -> "o3-mini")
|
|
base_model = model_lower.rsplit("/", 1)[-1]
|
|
|
|
if base_model in reasoning_models:
|
|
return True
|
|
|
|
# Match o1/o3 family with prefixes (o1-2024-12-17, o3-2025-04-16)
|
|
# but NOT gpt-5.x variants (gpt-5.4-mini supports temperature)
|
|
if any(base_model.startswith(prefix) for prefix in ["o1-", "o1.", "o3-", "o3."]):
|
|
return True
|
|
|
|
return False
|
|
|
|
def _uses_max_completion_tokens(self, model: str) -> bool:
|
|
"""
|
|
Check if the model expects ``max_completion_tokens`` instead of ``max_tokens``.
|
|
|
|
The whole GPT-5 family (gpt-5.4-mini, gpt-5.4-nano, gpt-5.5, ...) rejects the
|
|
legacy ``max_tokens`` parameter on the Chat Completions API and requires
|
|
``max_completion_tokens``. Older models (gpt-4.x, gpt-3.5, etc.) still accept
|
|
``max_tokens``.
|
|
|
|
Args:
|
|
model: The model name to check
|
|
|
|
Returns:
|
|
bool: True if the model requires ``max_completion_tokens``
|
|
"""
|
|
# Strip provider prefixes (e.g. "openai/gpt-5.4-mini" -> "gpt-5.4-mini")
|
|
base_model = (model or "").lower().rsplit("/", 1)[-1]
|
|
return base_model.startswith("gpt-5")
|
|
|
|
def _get_supported_params(self, **kwargs) -> Dict:
|
|
"""
|
|
Get parameters that are supported by the current model.
|
|
Filters out unsupported parameters for reasoning models and GPT-5 series.
|
|
|
|
Args:
|
|
**kwargs: Additional parameters to include
|
|
|
|
Returns:
|
|
Dict: Filtered parameters dictionary
|
|
"""
|
|
model = getattr(self.config, 'model', '')
|
|
|
|
if self._is_reasoning_model(model):
|
|
supported_params = {}
|
|
|
|
if "messages" in kwargs:
|
|
supported_params["messages"] = kwargs["messages"]
|
|
if "response_format" in kwargs:
|
|
supported_params["response_format"] = kwargs["response_format"]
|
|
if "tools" in kwargs:
|
|
supported_params["tools"] = kwargs["tools"]
|
|
if "tool_choice" in kwargs:
|
|
supported_params["tool_choice"] = kwargs["tool_choice"]
|
|
|
|
# Add reasoning_effort if configured
|
|
reasoning_effort = getattr(self.config, 'reasoning_effort', None)
|
|
if reasoning_effort:
|
|
supported_params["reasoning_effort"] = reasoning_effort
|
|
|
|
return supported_params
|
|
else:
|
|
# For regular models, include all common parameters
|
|
return self._get_common_params(**kwargs)
|
|
|
|
@abstractmethod
|
|
def generate_response(
|
|
self, messages: List[Dict[str, str]], tools: Optional[List[Dict]] = None, tool_choice: str = "auto", **kwargs
|
|
):
|
|
"""
|
|
Generate a response based on the given messages.
|
|
|
|
Args:
|
|
messages (list): List of message dicts containing 'role' and 'content'.
|
|
tools (list, optional): List of tools that the model can call. Defaults to None.
|
|
tool_choice (str, optional): Tool choice method. Defaults to "auto".
|
|
**kwargs: Additional provider-specific parameters.
|
|
|
|
Returns:
|
|
str or dict: The generated response.
|
|
"""
|
|
pass
|
|
|
|
def _get_common_params(self, **kwargs) -> Dict:
|
|
"""
|
|
Get common parameters that most providers use.
|
|
|
|
Returns:
|
|
Dict: Common parameters dictionary.
|
|
"""
|
|
params = {
|
|
"temperature": self.config.temperature,
|
|
"top_p": self.config.top_p,
|
|
}
|
|
|
|
model = getattr(self.config, "model", "")
|
|
if self._uses_max_completion_tokens(model):
|
|
params["max_completion_tokens"] = self.config.max_tokens
|
|
else:
|
|
params["max_tokens"] = self.config.max_tokens
|
|
|
|
# Add provider-specific parameters from kwargs
|
|
params.update(kwargs)
|
|
|
|
return params
|