llama-index
232 строки · 7.5 Кб
1import logging2from importlib.metadata import version3from types import ModuleType4from typing import Any, Callable, Dict, List, Optional, Sequence, Tuple, Type5
6import openai7from packaging.version import parse8from tenacity import (9before_sleep_log,10retry,11retry_if_exception_type,12stop_after_attempt,13wait_exponential,14)
15
16from llama_index.legacy.bridge.pydantic import BaseModel17from llama_index.legacy.llms.generic_utils import get_from_param_or_env18from llama_index.legacy.llms.types import ChatMessage19
20DEFAULT_KONKO_API_TYPE = "open_ai"21DEFAULT_KONKO_API_BASE = "https://api.konko.ai/v1"22DEFAULT_KONKO_API_VERSION = ""23MISSING_API_KEY_ERROR_MESSAGE = """No Konko API key found for LLM.24E.g. to use konko Please set the KONKO_API_KEY environment variable or \
25konko.api_key prior to initialization.
26API keys can be found or created at \
27https://www.konko.ai/
28"""
29
30logger = logging.getLogger(__name__)31
32
33def import_konko() -> ModuleType:34try:35import konko36
37return konko38except ImportError:39raise ImportError(40"Could not import konko python package. "41"Please install it with `pip install konko`."42)43
44
45def is_openai_v1() -> bool:46try:47_version = parse(version("openai"))48major_version = _version.major49except AttributeError:50# Handle the case where version or major attribute is not present51return False52return bool(major_version >= 1)53
54
55def _create_retry_decorator(max_retries: int) -> Callable[[Any], Any]:56min_seconds = 457max_seconds = 1058# Wait 2^x * 1 second between each retry starting with59# 4 seconds, then up to 10 seconds, then 10 seconds afterwards60return retry(61reraise=True,62stop=stop_after_attempt(max_retries),63wait=wait_exponential(multiplier=1, min=min_seconds, max=max_seconds),64retry=(65retry_if_exception_type(openai.APITimeoutError)66| retry_if_exception_type(openai.APIError)67| retry_if_exception_type(openai.APIConnectionError)68| retry_if_exception_type(openai.RateLimitError)69| retry_if_exception_type(openai.APIStatusError)70),71before_sleep=before_sleep_log(logger, logging.WARNING),72)73
74
75def completion_with_retry(is_chat_model: bool, max_retries: int, **kwargs: Any) -> Any:76"""Use tenacity to retry the completion call."""77retry_decorator = _create_retry_decorator(max_retries=max_retries)78
79@retry_decorator80def _completion_with_retry(**kwargs: Any) -> Any:81client = get_completion_endpoint(is_chat_model)82return client.create(**kwargs)83
84return _completion_with_retry(**kwargs)85
86
87def get_completion_endpoint(is_chat_model: bool) -> Any:88"""89Get the appropriate completion endpoint based on the model type and API version.
90
91Args:
92- is_chat_model (bool): A flag indicating whether the model is a chat model.
93
94Returns:
95- The appropriate completion endpoint based on the model type and API version.
96
97Raises:
98- NotImplementedError: If the combination of is_chat_model and API version is not supported.
99"""
100konko = import_konko()101# For OpenAI version 1102if is_openai_v1():103return konko.chat.completions if is_chat_model else konko.completions104
105# For other versions106if not is_openai_v1():107return konko.ChatCompletion if is_chat_model else konko.Completion108
109# Raise error if the combination of is_chat_model and API version is not covered110raise NotImplementedError(111"The combination of model type and API version is not supported."112)113
114
115def to_openai_message_dict(message: ChatMessage) -> dict:116"""Convert generic message to OpenAI message dict."""117message_dict = {118"role": message.role,119"content": message.content,120}121message_dict.update(message.additional_kwargs)122
123return message_dict124
125
126def to_openai_message_dicts(messages: Sequence[ChatMessage]) -> List[dict]:127"""Convert generic messages to OpenAI message dicts."""128return [to_openai_message_dict(message) for message in messages]129
130
131def from_openai_message_dict(message_dict: Any) -> ChatMessage:132"""Convert openai message dict to generic message."""133if is_openai_v1():134# Handling for OpenAI version 1135role = message_dict.role136content = message_dict.content137additional_kwargs = {138attr: getattr(message_dict, attr)139for attr in dir(message_dict)140if not attr.startswith("_") and attr not in ["role", "content"]141}142else:143# Handling for OpenAI version 0144role = message_dict.get("role")145content = message_dict.get("content", None)146additional_kwargs = {147key: value148for key, value in message_dict.items()149if key not in ["role", "content"]150}151
152return ChatMessage(role=role, content=content, additional_kwargs=additional_kwargs)153
154
155def from_openai_message_dicts(message_dicts: Sequence[dict]) -> List[ChatMessage]:156"""Convert openai message dicts to generic messages."""157return [from_openai_message_dict(message_dict) for message_dict in message_dicts]158
159
160def to_openai_function(pydantic_class: Type[BaseModel]) -> Dict[str, Any]:161"""Convert pydantic class to OpenAI function."""162schema = pydantic_class.schema()163return {164"name": schema["title"],165"description": schema["description"],166"parameters": pydantic_class.schema(),167}168
169
170def resolve_konko_credentials(171konko_api_key: Optional[str] = None,172openai_api_key: Optional[str] = None,173api_type: Optional[str] = None,174api_base: Optional[str] = None,175api_version: Optional[str] = None,176) -> Tuple[str, str, str, str, str]:177""" "Resolve KonkoAI credentials.178
179The order of precedence is:
1801. param
1812. env
1823. konkoai module
1834. default
184"""
185konko = import_konko()186# resolve from param or env187konko_api_key = get_from_param_or_env(188"konko_api_key", konko_api_key, "KONKO_API_KEY", ""189)190openai_api_key = get_from_param_or_env(191"openai_api_key", openai_api_key, "OPENAI_API_KEY", ""192)193api_type = get_from_param_or_env("api_type", api_type, "KONKO_API_TYPE", "")194api_base = DEFAULT_KONKO_API_BASE195api_version = get_from_param_or_env(196"api_version", api_version, "KONKO_API_VERSION", ""197)198
199# resolve from konko module or default200konko_api_key = konko_api_key201openai_api_key = openai_api_key202api_type = api_type or DEFAULT_KONKO_API_TYPE203api_base = api_base or konko.api_base or DEFAULT_KONKO_API_BASE204api_version = api_version or DEFAULT_KONKO_API_VERSION205
206if not konko_api_key:207raise ValueError(MISSING_API_KEY_ERROR_MESSAGE)208
209return konko_api_key, openai_api_key, api_type, api_base, api_version210
211
212async def acompletion_with_retry(213is_chat_model: bool, max_retries: int, **kwargs: Any214) -> Any:215"""Use tenacity to retry the async completion call."""216konko = import_konko()217retry_decorator = _create_retry_decorator(max_retries=max_retries)218
219@retry_decorator220async def _completion_with_retry(**kwargs: Any) -> Any:221if is_chat_model:222if is_openai_v1():223return await konko.AsyncKonko().chat.completions.create(**kwargs)224else:225return await konko.ChatCompletion.acreate(**kwargs)226else:227if is_openai_v1():228return await konko.AsyncKonko().completions.create(**kwargs)229else:230return await konko.Completion.acreate(**kwargs)231
232return await _completion_with_retry(**kwargs)233