Two goals: 1. Centralize system_instruction vs context system message resolution into the LLM adapters. This eliminates duplication between in-pipeline and out-of-band (run_inference) code paths across ~16 locations in service llm.py files. 2. Add support for "developer" role messages in conversation context, which is facilitated by the above centralization. Shared helpers on BaseLLMAdapter: - _extract_initial_system_or_developer: extracts/converts messages[0] based on role and whether system_instruction is provided - _resolve_system_instruction: warns on conflicts between system_instruction and context system messages, returns the effective instruction Developer message handling (new): - Non-OpenAI adapters: an initial "developer" message is promoted to the system instruction when no system_instruction is provided; otherwise it is converted to "user". Subsequent "developer" messages are always converted to "user". No conflict warning is emitted for developer messages (unlike "system" messages). - OpenAI adapter: "developer" messages pass through in conversation history without triggering conflict warnings. - OpenAI Responses adapter: "developer" messages are kept as "developer" role (same as "system", which is also converted to "developer" for the Responses API). Other behavior changes: - Gemini: "initial" system message detection now checks messages[0] only (previously searched anywhere in the list) - Bedrock: a lone system message is now converted to "user" instead of being extracted to an empty message list (matches existing Anthropic behavior)
118 lines
4.1 KiB
Python
118 lines
4.1 KiB
Python
#
|
|
# Copyright (c) 2024-2026, Daily
|
|
#
|
|
# SPDX-License-Identifier: BSD 2-Clause License
|
|
#
|
|
|
|
"""Cerebras LLM service implementation using OpenAI-compatible interface."""
|
|
|
|
from dataclasses import dataclass
|
|
from typing import Optional
|
|
|
|
from loguru import logger
|
|
|
|
from pipecat.adapters.services.open_ai_adapter import OpenAILLMInvocationParams
|
|
from pipecat.services.openai.base_llm import BaseOpenAILLMService
|
|
from pipecat.services.openai.llm import OpenAILLMService
|
|
|
|
|
|
@dataclass
|
|
class CerebrasLLMSettings(BaseOpenAILLMService.Settings):
|
|
"""Settings for CerebrasLLMService."""
|
|
|
|
pass
|
|
|
|
|
|
class CerebrasLLMService(OpenAILLMService):
|
|
"""A service for interacting with Cerebras's API using the OpenAI-compatible interface.
|
|
|
|
This service extends OpenAILLMService to connect to Cerebras's API endpoint while
|
|
maintaining full compatibility with OpenAI's interface and functionality.
|
|
"""
|
|
|
|
Settings = CerebrasLLMSettings
|
|
_settings: Settings
|
|
|
|
def __init__(
|
|
self,
|
|
*,
|
|
api_key: str,
|
|
base_url: str = "https://api.cerebras.ai/v1",
|
|
model: Optional[str] = None,
|
|
settings: Optional[Settings] = None,
|
|
**kwargs,
|
|
):
|
|
"""Initialize the Cerebras LLM service.
|
|
|
|
Args:
|
|
api_key: The API key for accessing Cerebras's API.
|
|
base_url: The base URL for Cerebras API. Defaults to "https://api.cerebras.ai/v1".
|
|
model: The model identifier to use. Defaults to "gpt-oss-120b".
|
|
|
|
.. deprecated:: 0.0.105
|
|
Use ``settings=CerebrasLLMService.Settings(model=...)`` instead.
|
|
|
|
settings: Runtime-updatable settings. When provided alongside deprecated
|
|
parameters, ``settings`` values take precedence.
|
|
**kwargs: Additional keyword arguments passed to OpenAILLMService.
|
|
"""
|
|
# 1. Initialize default_settings with hardcoded defaults
|
|
default_settings = self.Settings(model="gpt-oss-120b")
|
|
|
|
# 2. Apply direct init arg overrides (deprecated)
|
|
if model is not None:
|
|
self._warn_init_param_moved_to_settings("model", "model")
|
|
default_settings.model = model
|
|
|
|
# 3. (No step 3, as there's no params object to apply)
|
|
|
|
# 4. Apply settings delta (canonical API, always wins)
|
|
if settings is not None:
|
|
default_settings.apply_update(settings)
|
|
|
|
super().__init__(api_key=api_key, base_url=base_url, settings=default_settings, **kwargs)
|
|
|
|
def create_client(self, api_key=None, base_url=None, **kwargs):
|
|
"""Create OpenAI-compatible client for Cerebras API endpoint.
|
|
|
|
Args:
|
|
api_key: The API key for authentication. If None, uses instance key.
|
|
base_url: The base URL for the API. If None, uses instance URL.
|
|
**kwargs: Additional arguments passed to the client constructor.
|
|
|
|
Returns:
|
|
An OpenAI-compatible client configured for Cerebras API.
|
|
"""
|
|
logger.debug(f"Creating Cerebras client with api {base_url}")
|
|
return super().create_client(api_key, base_url, **kwargs)
|
|
|
|
def build_chat_completion_params(self, params_from_context: OpenAILLMInvocationParams) -> dict:
|
|
"""Build parameters for Cerebras chat completion request.
|
|
|
|
Cerebras supports a subset of OpenAI parameters, focusing on core
|
|
completion settings without advanced features like frequency/presence penalties.
|
|
|
|
Args:
|
|
params_from_context: Parameters, derived from the LLM context, to
|
|
use for the chat completion. Contains messages, tools, and tool
|
|
choice.
|
|
|
|
Returns:
|
|
Dictionary of parameters for the chat completion request.
|
|
"""
|
|
params = {
|
|
"model": self._settings.model,
|
|
"stream": True,
|
|
"seed": self._settings.seed,
|
|
"temperature": self._settings.temperature,
|
|
"top_p": self._settings.top_p,
|
|
"max_completion_tokens": self._settings.max_completion_tokens,
|
|
}
|
|
|
|
# Messages, tools, tool_choice
|
|
params.update(params_from_context)
|
|
|
|
params.update(self._settings.extra)
|
|
|
|
return params
|