mirror of
https://github.com/BerriAI/litellm.git
synced 2025-04-27 11:43:54 +00:00
* fix(utils.py): initial commit to remove circular imports - moves llmproviders to utils.py * fix(router.py): fix 'litellm.EmbeddingResponse' import from router.py ' * refactor: fix litellm.ModelResponse import on pass through endpoints * refactor(litellm_logging.py): fix circular import for custom callbacks literal * fix(factory.py): fix circular imports inside prompt factory * fix(cost_calculator.py): fix circular import for 'litellm.Usage' * fix(proxy_server.py): fix potential circular import with `litellm.Router' * fix(proxy/utils.py): fix potential circular import in `litellm.Router` * fix: remove circular imports in 'auth_checks' and 'guardrails/' * fix(prompt_injection_detection.py): fix router impor t * fix(vertex_passthrough_logging_handler.py): fix potential circular imports in vertex pass through * fix(anthropic_pass_through_logging_handler.py): fix potential circular imports * fix(slack_alerting.py-+-ollama_chat.py): fix modelresponse import * fix(base.py): fix potential circular import * fix(handler.py): fix potential circular ref in codestral + cohere handler's * fix(azure.py): fix potential circular imports * fix(gpt_transformation.py): fix modelresponse import * fix(litellm_logging.py): add logging base class - simplify typing makes it easy for other files to type check the logging obj without introducing circular imports * fix(azure_ai/embed): fix potential circular import on handler.py * fix(databricks/): fix potential circular imports in databricks/ * fix(vertex_ai/): fix potential circular imports on vertex ai embeddings * fix(vertex_ai/image_gen): fix import * fix(watsonx-+-bedrock): cleanup imports * refactor(anthropic-pass-through-+-petals): cleanup imports * refactor(huggingface/): cleanup imports * fix(ollama-+-clarifai): cleanup circular imports * fix(openai_like/): fix impor t * fix(openai_like/): fix embedding handler cleanup imports * refactor(openai.py): cleanup imports * fix(sagemaker/transformation.py): fix import * ci(config.yml): add circular import test to ci/cd
197 lines
7.3 KiB
Python
197 lines
7.3 KiB
Python
# What is this?
|
|
## Translates OpenAI call to Anthropic `/v1/messages` format
|
|
import json
|
|
import os
|
|
import traceback
|
|
import uuid
|
|
from typing import Any, Literal, Optional
|
|
|
|
import dotenv
|
|
import httpx
|
|
from pydantic import BaseModel
|
|
|
|
import litellm
|
|
from litellm import ChatCompletionRequest, verbose_logger
|
|
from litellm.integrations.custom_logger import CustomLogger
|
|
from litellm.types.llms.anthropic import (
|
|
AnthropicMessagesRequest,
|
|
AnthropicResponse,
|
|
ContentBlockDelta,
|
|
)
|
|
from litellm.types.utils import AdapterCompletionStreamWrapper, ModelResponse
|
|
|
|
|
|
class AnthropicAdapter(CustomLogger):
|
|
def __init__(self) -> None:
|
|
super().__init__()
|
|
|
|
def translate_completion_input_params(
|
|
self, kwargs
|
|
) -> Optional[ChatCompletionRequest]:
|
|
"""
|
|
- translate params, where needed
|
|
- pass rest, as is
|
|
"""
|
|
request_body = AnthropicMessagesRequest(**kwargs) # type: ignore
|
|
|
|
translated_body = litellm.AnthropicExperimentalPassThroughConfig().translate_anthropic_to_openai(
|
|
anthropic_message_request=request_body
|
|
)
|
|
|
|
return translated_body
|
|
|
|
def translate_completion_output_params(
|
|
self, response: ModelResponse
|
|
) -> Optional[AnthropicResponse]:
|
|
|
|
return litellm.AnthropicExperimentalPassThroughConfig().translate_openai_response_to_anthropic(
|
|
response=response
|
|
)
|
|
|
|
def translate_completion_output_params_streaming(
|
|
self, completion_stream: Any
|
|
) -> AdapterCompletionStreamWrapper | None:
|
|
return AnthropicStreamWrapper(completion_stream=completion_stream)
|
|
|
|
|
|
anthropic_adapter = AnthropicAdapter()
|
|
|
|
|
|
class AnthropicStreamWrapper(AdapterCompletionStreamWrapper):
|
|
"""
|
|
- first chunk return 'message_start'
|
|
- content block must be started and stopped
|
|
- finish_reason must map exactly to anthropic reason, else anthropic client won't be able to parse it.
|
|
"""
|
|
|
|
sent_first_chunk: bool = False
|
|
sent_content_block_start: bool = False
|
|
sent_content_block_finish: bool = False
|
|
sent_last_message: bool = False
|
|
holding_chunk: Optional[Any] = None
|
|
|
|
def __next__(self):
|
|
try:
|
|
if self.sent_first_chunk is False:
|
|
self.sent_first_chunk = True
|
|
return {
|
|
"type": "message_start",
|
|
"message": {
|
|
"id": "msg_1nZdL29xx5MUA1yADyHTEsnR8uuvGzszyY",
|
|
"type": "message",
|
|
"role": "assistant",
|
|
"content": [],
|
|
"model": "claude-3-5-sonnet-20240620",
|
|
"stop_reason": None,
|
|
"stop_sequence": None,
|
|
"usage": {"input_tokens": 25, "output_tokens": 1},
|
|
},
|
|
}
|
|
if self.sent_content_block_start is False:
|
|
self.sent_content_block_start = True
|
|
return {
|
|
"type": "content_block_start",
|
|
"index": 0,
|
|
"content_block": {"type": "text", "text": ""},
|
|
}
|
|
|
|
for chunk in self.completion_stream:
|
|
if chunk == "None" or chunk is None:
|
|
raise Exception
|
|
|
|
processed_chunk = litellm.AnthropicExperimentalPassThroughConfig().translate_streaming_openai_response_to_anthropic(
|
|
response=chunk
|
|
)
|
|
if (
|
|
processed_chunk["type"] == "message_delta"
|
|
and self.sent_content_block_finish is False
|
|
):
|
|
self.holding_chunk = processed_chunk
|
|
self.sent_content_block_finish = True
|
|
return {
|
|
"type": "content_block_stop",
|
|
"index": 0,
|
|
}
|
|
elif self.holding_chunk is not None:
|
|
return_chunk = self.holding_chunk
|
|
self.holding_chunk = processed_chunk
|
|
return return_chunk
|
|
else:
|
|
return processed_chunk
|
|
if self.holding_chunk is not None:
|
|
return_chunk = self.holding_chunk
|
|
self.holding_chunk = None
|
|
return return_chunk
|
|
if self.sent_last_message is False:
|
|
self.sent_last_message = True
|
|
return {"type": "message_stop"}
|
|
raise StopIteration
|
|
except StopIteration:
|
|
if self.sent_last_message is False:
|
|
self.sent_last_message = True
|
|
return {"type": "message_stop"}
|
|
raise StopIteration
|
|
except Exception as e:
|
|
verbose_logger.error(
|
|
"Anthropic Adapter - {}\n{}".format(e, traceback.format_exc())
|
|
)
|
|
|
|
async def __anext__(self):
|
|
try:
|
|
if self.sent_first_chunk is False:
|
|
self.sent_first_chunk = True
|
|
return {
|
|
"type": "message_start",
|
|
"message": {
|
|
"id": "msg_1nZdL29xx5MUA1yADyHTEsnR8uuvGzszyY",
|
|
"type": "message",
|
|
"role": "assistant",
|
|
"content": [],
|
|
"model": "claude-3-5-sonnet-20240620",
|
|
"stop_reason": None,
|
|
"stop_sequence": None,
|
|
"usage": {"input_tokens": 25, "output_tokens": 1},
|
|
},
|
|
}
|
|
if self.sent_content_block_start is False:
|
|
self.sent_content_block_start = True
|
|
return {
|
|
"type": "content_block_start",
|
|
"index": 0,
|
|
"content_block": {"type": "text", "text": ""},
|
|
}
|
|
async for chunk in self.completion_stream:
|
|
if chunk == "None" or chunk is None:
|
|
raise Exception
|
|
processed_chunk = litellm.AnthropicExperimentalPassThroughConfig().translate_streaming_openai_response_to_anthropic(
|
|
response=chunk
|
|
)
|
|
if (
|
|
processed_chunk["type"] == "message_delta"
|
|
and self.sent_content_block_finish is False
|
|
):
|
|
self.holding_chunk = processed_chunk
|
|
self.sent_content_block_finish = True
|
|
return {
|
|
"type": "content_block_stop",
|
|
"index": 0,
|
|
}
|
|
elif self.holding_chunk is not None:
|
|
return_chunk = self.holding_chunk
|
|
self.holding_chunk = processed_chunk
|
|
return return_chunk
|
|
else:
|
|
return processed_chunk
|
|
if self.holding_chunk is not None:
|
|
return_chunk = self.holding_chunk
|
|
self.holding_chunk = None
|
|
return return_chunk
|
|
if self.sent_last_message is False:
|
|
self.sent_last_message = True
|
|
return {"type": "message_stop"}
|
|
raise StopIteration
|
|
except StopIteration:
|
|
if self.sent_last_message is False:
|
|
self.sent_last_message = True
|
|
return {"type": "message_stop"}
|
|
raise StopAsyncIteration
|