Source code for sherpa_ai.models.sherpa_base_model
"""Base OpenAI model integration for Sherpa AI.
This module provides the base OpenAI model integration for the Sherpa AI system.
It defines the SherpaOpenAI class which extends OpenAI's chat model with
Sherpa-specific functionality like usage tracking.
"""
from typing import Any, List, Optional
from langchain_openai import ChatOpenAI
from langchain_core.callbacks import (
AsyncCallbackManagerForLLMRun,
CallbackManagerForLLMRun,
UsageMetadataCallbackHandler,
)
from langchain_core.language_models import BaseChatModel
from langchain_core.messages import BaseMessage
from langchain_core.outputs import ChatResult
from pydantic import BaseModel
from sherpa_ai.database.user_usage_tracker import UserUsageTracker
[docs]
class SherpaOpenAI(ChatOpenAI):
"""Enhanced OpenAI chat model with Sherpa-specific features.
This class extends the OpenAI chat model to add Sherpa-specific functionality,
particularly user-based token usage tracking. It maintains compatibility with
the base ChatOpenAI interface while adding user tracking capabilities.
Attributes:
user_id (Optional[str]): ID of the user making model requests.
Example:
>>> model = SherpaOpenAI(user_id="user123")
>>> response = model.generate("What is the weather?")
>>> print(response.generations[0].text)
'The weather is sunny.'
"""
user_id: Optional[str] = None
def _agenerate(
self,
messages: List[BaseMessage],
stop: Optional[List[str]] = None,
run_manager: Optional[AsyncCallbackManagerForLLMRun] = None,
**kwargs: Any,
):
"""Asynchronously generate chat completions.
Args:
messages (List[BaseMessage]): List of messages in the conversation.
stop (Optional[List[str]]): List of stop sequences.
run_manager (Optional[AsyncCallbackManagerForLLMRun]): Callback manager.
**kwargs (Any): Additional arguments for the model.
Note:
This is a placeholder method that needs to be implemented.
"""
pass
@property
def _llm_type(self):
"""Get the type of the language model.
Returns:
str: Type identifier for the language model.
Example:
>>> model = SherpaOpenAI()
>>> print(model._llm_type)
'openai'
"""
return super()._llm_type
def _generate(
self,
prompts: List[str],
stop: Optional[List[str]] = None,
run_manager: Optional[CallbackManagerForLLMRun] = None,
**kwargs: Any,
) -> ChatResult:
"""Generate chat completions and track token usage.
This method extends the base generation functionality to track token
usage per user in the database.
Args:
prompts (List[str]): List of prompts to generate from.
stop (Optional[List[str]]): List of stop sequences.
run_manager (Optional[CallbackManagerForLLMRun]): Callback manager.
**kwargs (Any): Additional arguments for the model.
Returns:
ChatResult: Generated chat completion result.
Example:
>>> model = SherpaOpenAI(user_id="user123")
>>> result = model._generate(["Hello!"])
>>> print(result.generations[0].text)
'Hi there!'
"""
# Create UsageMetadataCallbackHandler to track token usage
usage_callback = UsageMetadataCallbackHandler()
# Add the callback to the run manager if provided
if run_manager:
run_manager.add_handler(usage_callback)
# Generate response with usage tracking
response = super()._generate(prompts, stop, run_manager, **kwargs)
# Track usage if user_id is provided
if self.user_id:
user_db = UserUsageTracker()
# Extract usage metadata from callback
usage_metadata = usage_callback.usage_metadata
if usage_metadata:
# Use the new unified add_usage method
user_db.add_usage(
user_id=self.user_id,
usage_metadata=usage_metadata
)
else:
# Fallback to legacy tracking if no usage metadata
# Extract from response.llm_output as fallback
total_token = response.llm_output.get("token_usage", {}).get("total_tokens", 0)
user_db.add_usage(
user_id=self.user_id,
input_tokens=total_token // 2, # Rough split for legacy
output_tokens=total_token // 2
)
user_db.close_connection()
return response