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