Current section

Files

Jump to
hyper_llm lib providers anthropic.openai_compatability.ex
Raw

lib/providers/anthropic.openai_compatability.ex

defmodule HyperLLM.Provider.Anthropic.OpenAICompatibility do
@moduledoc """
This module is used to convert OpenAI requests to Anthropic requests and vice versa.
"""
import HyperLLM.Provider
def attach(%Req.Request{} = request, _options \\ []) do
request
|> Req.Request.prepend_request_steps(openai_request: &from_openai_request/1)
|> Req.Request.append_response_steps(openai_response: &to_openai_response/1)
end
def from_openai_request(request) do
case request.url do
%URI{path: "/v1/messages"} ->
build_anthropic_request(request)
url ->
dbg(url)
request
end
end
defp build_anthropic_request(%{options: %{json: json}} = request) do
json =
json
|> cast(:stop_sequences)
|> cast(:metadata)
Map.update!(request, :options, fn options ->
%{options | json: json}
end)
end
defp build_anthropic_request(request), do: request
defp cast(data, :stop_sequences) do
rename_key(data, :stop, :stop_sequences, fn stop_sequences ->
if is_list(stop_sequences) do
stop_sequences
else
[stop_sequences]
end
end)
end
defp cast(%{user_id: user_id} = json, :metadata) when not is_nil(user_id) do
json
|> Map.put_new(:metadata, %{})
|> Map.put(:metadata, %{user_id: user_id})
|> Map.delete(:user_id)
end
defp cast(data, _), do: data
defp to_openai_response({%{url: %URI{path: "/v1/messages"}} = request, response}) do
dbg(request)
# when the headers of the requestion are "content-type" => ["text/event-stream; charset=utf-8"] use the streaming responses
if Map.get(response.headers, "content-type") == ["text/event-stream; charset=utf-8"] do
to_openai_chat_completion_streaming_response({request, response})
else
to_openai_chat_completion_response({request, response})
end
end
defp to_openai_response({request, response}) do
{request, response}
end
def to_openai_chat_completion_streaming_response({request, response}) do
{request, response}
end
def to_openai_chat_completion_response({request, response}) do
usage = Map.get(response.body, "usage", %{})
content = hd(response.body["content"])
response =
response
|> Map.put(:body, %{
"id" => response.body["id"],
"created" => DateTime.utc_now() |> DateTime.to_unix(),
"model" => request.options.json.model,
"object" => "chat.completion",
"service_tier" => "default",
"system_fingerprint" => nil,
"choices" => [
%{
"index" => 0,
"message" => %{
"role" => "assistant",
"content" => content["text"]
},
"logprobs" => nil,
"finish_reason" => response.body["stop_reason"]
}
],
"usage" => %{
"prompt_tokens" => Map.get(usage, "input_tokens", 0),
"completion_tokens" => Map.get(usage, "output_tokens", 0),
"total_tokens" => Map.get(usage, "input_tokens", 0) + Map.get(usage, "output_tokens", 0)
}
})
{request, response}
end
end