Current section
7 Versions
Jump to
Current section
7 Versions
Compare versions
7
files changed
+298
additions
-171
deletions
| @@ -1,9 +1,14 @@ | |
| 1 1 | # Changelog |
| 2 2 | |
| 3 | - ## v0.1.0 |
| 3 | + ## v0.2.0 (2024-01-16) |
| 4 | + |
| 5 | + - Add `chat/completions` endpoint. |
| 6 | + - Improve ExDocs for developer. |
| 7 | + |
| 8 | + ## v0.1.0 (2024-01-15) |
| 4 9 | |
| 5 10 | - Add CHANGELOG.md |
| 6 11 | - Add `models` endpoint |
| 7 12 | - Add `Config` overwrite feature |
| 8 13 | - Add ex_docs |
| 9 | - - Add `Dependabot` and `c.yml` |
| \ No newline at end of file | |
| 14 | + - Add `Dependabot` and `ci.yml` |
| \ No newline at end of file |
| @@ -12,7 +12,7 @@ by adding `hipcall_openai` to your list of dependencies in `mix.exs`: | |
| 12 12 | ```elixir |
| 13 13 | def deps do |
| 14 14 | [ |
| 15 | - {:hipcall_openai, "~> 0.1.0"} |
| 15 | + {:hipcall_openai, "~> 0.2.0"} |
| 16 16 | ] |
| 17 17 | end |
| 18 18 | ``` |
| @@ -28,78 +28,23 @@ struct as last argument of the function you need to use. For instance if you nee | |
| 28 28 | api_key you can simply do: |
| 29 29 | |
| 30 30 | ```elixir |
| 31 | - config_override = %HipcallOpenai.Config{api_key: "mTRwVrbZ4aoHTyjMepleT3BlbkFJ7zZYazuN7F16XuY3WErl"} |
| 31 | + config_override = %HipcallOpenai.Config{ |
| 32 | + api_key: "mTRwVrbZ4aoHTyjMepleT3BlbkFJ7zZYazuN7F16XuY3WErl", |
| 33 | + api_organization: "org-blalba" |
| 34 | + } |
| 32 35 | # pass the overriden configuration as last argument of the function |
| 33 36 | HipcallOpenai.models(config_override) |
| 34 37 | ``` |
| 35 38 | |
| 36 39 | ## Use |
| 37 40 | |
| 38 | - ### Models |
| 39 | - |
| 40 | - #### List |
| 41 | - |
| 42 | - https://platform.openai.com/docs/api-reference/models |
| 43 | - |
| 44 | - ```elixir |
| 45 | - iex(1)> HipcallOpenai.models(config_override) |
| 46 | - {:ok, |
| 47 | - %{ |
| 48 | - "data" => [ |
| 49 | - %{ |
| 50 | - "created" => 1686588896, |
| 51 | - "id" => "gpt-4-0613", |
| 52 | - "object" => "model", |
| 53 | - "owned_by" => "openai" |
| 54 | - }, |
| 55 | - %{ |
| 56 | - "created" => 1651172509, |
| 57 | - "id" => "curie-search-query", |
| 58 | - "object" => "model", |
| 59 | - "owned_by" => "openai-dev" |
| 60 | - }, |
| 61 | - %{ |
| 62 | - "created" => 1687882411, |
| 63 | - "id" => "gpt-4", |
| 64 | - "object" => "model", |
| 65 | - "owned_by" => "openai" |
| 66 | - }, |
| 67 | - %{ |
| 68 | - "created" => 1651172509, |
| 69 | - "id" => "babbage-search-query", |
| 70 | - "object" => "model", |
| 71 | - "owned_by" => "openai-dev" |
| 72 | - }, |
| 73 | - %{ |
| 74 | - "created" => 1698785189, |
| 75 | - "id" => "dall-e-3", |
| 76 | - "object" => "model", |
| 77 | - "owned_by" => "system" |
| 78 | - } |
| 79 | - ], |
| 80 | - "object" => "list" |
| 81 | - }} |
| 82 | - ``` |
| 83 | - |
| 84 | - #### Retrieve |
| 85 | - |
| 86 | - https://platform.openai.com/docs/api-reference/models/retrieve |
| 87 | - |
| 88 | - ```elixir |
| 89 | - iex(1)> HipcallOpenai.model("gpt-3.5-turbo-instruct", config_override) |
| 90 | - {:ok, |
| 91 | - %{ |
| 92 | - "created" => 1692901427, |
| 93 | - "id" => "gpt-3.5-turbo-instruct", |
| 94 | - "object" => "model", |
| 95 | - "owned_by" => "system" |
| 96 | - }} |
| 97 | - ``` |
| 41 | + Documentation for using, please check the `HipcallOpenai` module. |
| 98 42 | |
| 99 43 | ## Roadmap |
| 100 44 | |
| 45 | + - [x] Add `Models` endpoint |
| 46 | + - [x] Add `Chat` endpoint |
| 101 47 | - [ ] Add `Audio` endpoints |
| 102 | - - [ ] Add `Chat` endpoint |
| 103 48 | - [ ] Add `Embeddings` endpoint |
| 104 49 | - [ ] Add `Fine-tuning` endpoint |
| 105 50 | - [ ] Add `Files` endpoint |
| @@ -2,7 +2,7 @@ | |
| 2 2 | [{<<"GitHub">>,<<"https://github.com/hipcall/hipcall_openai">>}, |
| 3 3 | {<<"Website">>,<<"https://www.hipcall.com/en-gb/">>}]}. |
| 4 4 | {<<"name">>,<<"hipcall_openai">>}. |
| 5 | - {<<"version">>,<<"0.1.0">>}. |
| 5 | + {<<"version">>,<<"0.2.0">>}. |
| 6 6 | {<<"description">>,<<"Unofficial OpenAI API Wrapper written in Elixir.">>}. |
| 7 7 | {<<"elixir">>,<<"~> 1.14">>}. |
| 8 8 | {<<"app">>,<<"hipcall_openai">>}. |
| @@ -26,6 +26,7 @@ | |
| 26 26 | {<<"files">>, |
| 27 27 | [<<"lib">>,<<"lib/hipcall_openai">>,<<"lib/hipcall_openai/config.ex">>, |
| 28 28 | <<"lib/hipcall_openai/models.ex">>,<<"lib/hipcall_openai/application.ex">>, |
| 29 | - <<"lib/hipcall_openai.ex">>,<<".formatter.exs">>,<<"mix.exs">>, |
| 30 | - <<"README.md">>,<<"LICENSE.md">>,<<"CHANGELOG.md">>]}. |
| 29 | + <<"lib/hipcall_openai/chat.ex">>,<<"lib/hipcall_openai.ex">>, |
| 30 | + <<".formatter.exs">>,<<"mix.exs">>,<<"README.md">>,<<"LICENSE.md">>, |
| 31 | + <<"CHANGELOG.md">>]}. |
| 31 32 | {<<"build_tools">>,[<<"mix">>]}. |
| @@ -2,12 +2,244 @@ defmodule HipcallOpenai do | |
| 2 2 | @moduledoc """ |
| 3 3 | Documentation for `HipcallOpenai`. |
| 4 4 | """ |
| 5 | + alias HipcallOpenai.Chat |
| 5 6 | alias HipcallOpenai.Config |
| 6 7 | alias HipcallOpenai.Models |
| 7 8 | |
| 8 | - # https://platform.openai.com/docs/api-reference/models/ |
| 9 | - def models(config \\ %Config{}), do: Models.list(config) |
| 9 | + @chat_completions_schema [ |
| 10 | + model: [ |
| 11 | + type: :string, |
| 12 | + doc: """ |
| 13 | + ID of the model to use. See the model endpoint compatibility table for |
| 14 | + details on which models work with the Chat API. |
| 15 | + """, |
| 16 | + default: "gpt-3.5-turbo" |
| 17 | + ], |
| 18 | + messages: [ |
| 19 | + type: {:list, :map}, |
| 20 | + doc: """ |
| 21 | + A list of messages comprising the conversation so far. |
| 22 | + """, |
| 23 | + default: [ |
| 24 | + %{role: "system", content: "You are a helpful assistant."}, |
| 25 | + %{role: "user", content: "Hello!"} |
| 26 | + ] |
| 27 | + ], |
| 28 | + frequency_penalty: [ |
| 29 | + type: :float, |
| 30 | + doc: """ |
| 31 | + Number between -2.0 and 2.0. Positive values penalize new tokens based on |
| 32 | + their existing frequency in the text so far, decreasing the model's |
| 33 | + likelihood to repeat the same line verbatim. |
| 34 | + """ |
| 35 | + ], |
| 36 | + max_tokens: [ |
| 37 | + type: :pos_integer, |
| 38 | + doc: """ |
| 39 | + The maximum number of tokens that can be generated in the chat completion. |
| 40 | + The total length of input tokens and generated tokens is limited by the |
| 41 | + model's context length. |
| 42 | + """ |
| 43 | + ], |
| 44 | + stream: [ |
| 45 | + type: :boolean, |
| 46 | + doc: """ |
| 47 | + If set, partial message deltas will be sent, like in ChatGPT. Tokens will |
| 48 | + be sent as data-only server-sent events as they become available, with |
| 49 | + the stream terminated by a data: [DONE] message. |
| 50 | + """ |
| 51 | + ], |
| 52 | + temperature: [ |
| 53 | + type: :float, |
| 54 | + doc: """ |
| 55 | + What sampling temperature to use, between 0 and 2. Higher values like 0.8 |
| 56 | + will make the output more random, while lower values like 0.2 will make |
| 57 | + it more focused and deterministic. |
| 58 | + """ |
| 59 | + ], |
| 60 | + user: [ |
| 61 | + type: :string, |
| 62 | + doc: """ |
| 63 | + A unique identifier representing your end-user, which can help OpenAI |
| 64 | + to monitor and detect abuse. |
| 65 | + """ |
| 66 | + ] |
| 67 | + ] |
| 10 68 | |
| 11 | - # https://platform.openai.com/docs/api-reference/models/retrieve |
| 12 | - def model(model, config \\ %Config{}), do: Models.retrieve(model, config) |
| 69 | + @doc """ |
| 70 | + List models |
| 71 | + |
| 72 | + For more information https://platform.openai.com/docs/api-reference/models |
| 73 | + |
| 74 | + ## Examples |
| 75 | + |
| 76 | + iex> iex(1)> config_override = %Config{api_key: "asdf_api"} |
| 77 | + iex> iex(2)> HipcallOpenai.models(config_override) |
| 78 | + ...> {:ok, |
| 79 | + ...> %{ |
| 80 | + ...> "data" => [ |
| 81 | + ...> %{ |
| 82 | + ...> "created" => 1686588896, |
| 83 | + ...> "id" => "gpt-4-0613", |
| 84 | + ...> "object" => "model", |
| 85 | + ...> "owned_by" => "openai" |
| 86 | + ...> }, |
| 87 | + ...> %{ |
| 88 | + ...> "created" => 1651172509, |
| 89 | + ...> "id" => "curie-search-query", |
| 90 | + ...> "object" => "model", |
| 91 | + ...> "owned_by" => "openai-dev" |
| 92 | + ...> }, |
| 93 | + ...> %{ |
| 94 | + ...> "created" => 1687882411, |
| 95 | + ...> "id" => "gpt-4", |
| 96 | + ...> "object" => "model", |
| 97 | + ...> "owned_by" => "openai" |
| 98 | + ...> }, |
| 99 | + ...> %{ |
| 100 | + ...> "created" => 1651172509, |
| 101 | + ...> "id" => "babbage-search-query", |
| 102 | + ...> "object" => "model", |
| 103 | + ...> "owned_by" => "openai-dev" |
| 104 | + ...> }, |
| 105 | + ...> %{ |
| 106 | + ...> "created" => 1698785189, |
| 107 | + ...> "id" => "dall-e-3", |
| 108 | + ...> "object" => "model", |
| 109 | + ...> "owned_by" => "system" |
| 110 | + ...> } |
| 111 | + ...> ], |
| 112 | + ...> "object" => "list" |
| 113 | + ...> }} |
| 114 | + |
| 115 | + ## Arguments |
| 116 | + |
| 117 | + - `config` |
| 118 | + |
| 119 | + ## Raises |
| 120 | + |
| 121 | + - There is no exception. |
| 122 | + |
| 123 | + ## Returns |
| 124 | + |
| 125 | + - `{:ok, Finch.Response.t()}` |
| 126 | + - `{:error, Exception.t()}` |
| 127 | + |
| 128 | + """ |
| 129 | + @spec models(config :: struct()) :: {:ok, map()} | {:error, map()} | {:error, any()} |
| 130 | + def models(config \\ %Config{}) when is_struct(config) do |
| 131 | + Models.list(config) |
| 132 | + end |
| 133 | + |
| 134 | + @doc """ |
| 135 | + Retrieve model |
| 136 | + |
| 137 | + For more information https://platform.openai.com/docs/api-reference/models/retrieve |
| 138 | + |
| 139 | + ## Examples |
| 140 | + |
| 141 | + iex> iex(1)> iex(1)> HipcallOpenai.model("gpt-3.5-turbo-instruct") |
| 142 | + ...> {:ok, |
| 143 | + ...> %{ |
| 144 | + ...> "created" => 1692901427, |
| 145 | + ...> "id" => "gpt-3.5-turbo-instruct", |
| 146 | + ...> "object" => "model", |
| 147 | + ...> "owned_by" => "system" |
| 148 | + ...> }} |
| 149 | + |
| 150 | + ## Arguments |
| 151 | + |
| 152 | + - `model` |
| 153 | + |
| 154 | + ## Raises |
| 155 | + |
| 156 | + There is no exceptions. |
| 157 | + |
| 158 | + ## Returns |
| 159 | + |
| 160 | + - `{:ok, map()}` |
| 161 | + - `{:error, Exception.t()}` |
| 162 | + |
| 163 | + """ |
| 164 | + @spec model(model :: String.t()) :: {:ok, map()} | {:error, map()} | {:error, any()} |
| 165 | + def model(model) when is_bitstring(model) do |
| 166 | + model(model, %Config{}) |
| 167 | + end |
| 168 | + |
| 169 | + @doc """ |
| 170 | + The same with `model/1` just overwrite the config. |
| 171 | + """ |
| 172 | + @spec model(model :: String.t(), config :: struct()) :: |
| 173 | + {:ok, map()} | {:error, map()} | {:error, any()} |
| 174 | + def model(model, %Config{} = config), do: Models.retrieve(model, config) |
| 175 | + |
| 176 | + @doc """ |
| 177 | + Create chat completion |
| 178 | + |
| 179 | + For more information https://platform.openai.com/docs/api-reference/chat/create |
| 180 | + |
| 181 | + ## Examples |
| 182 | + |
| 183 | + iex> params = [ |
| 184 | + iex> model: "gpt-4-1106-preview", |
| 185 | + iex> messages: [ |
| 186 | + iex> %{role: "system", content: "Sen yardımcı olan bir asistansın."}, |
| 187 | + iex> %{role: "user", content: "Merhaba!"} |
| 188 | + iex> ] |
| 189 | + iex> ] |
| 190 | + iex> HipcallOpenai.chat_completions(params) |
| 191 | + ...> {:ok, |
| 192 | + ...> %{ |
| 193 | + ...> "choices" => [ |
| 194 | + ...> %{ |
| 195 | + ...> "finish_reason" => "stop", |
| 196 | + ...> "index" => 0, |
| 197 | + ...> "logprobs" => nil, |
| 198 | + ...> "message" => %{ |
| 199 | + ...> "content" => "Merhaba! Size nasıl yardımcı olabilirim?", |
| 200 | + ...> "role" => "assistant" |
| 201 | + ...> } |
| 202 | + ...> } |
| 203 | + ...> ], |
| 204 | + ...> "created" => 1705330002, |
| 205 | + ...> "id" => "chatcmpl-8hIWYa9L3HBatp1Wyp5fJfneaBMUi", |
| 206 | + ...> "model" => "gpt-4-1106-preview", |
| 207 | + ...> "object" => "chat.completion", |
| 208 | + ...> "system_fingerprint" => "fp_168383a679", |
| 209 | + ...> "usage" => %{ |
| 210 | + ...> "completion_tokens" => 15, |
| 211 | + ...> "prompt_tokens" => 27, |
| 212 | + ...> "total_tokens" => 42 |
| 213 | + ...> } |
| 214 | + ...> }} |
| 215 | + |
| 216 | + ## Params |
| 217 | + |
| 218 | + #{NimbleOptions.docs(@chat_completions_schema)} |
| 219 | + |
| 220 | + ## Raises |
| 221 | + |
| 222 | + - Raise `NimbleOptions.ValidationError` if params are not valid. |
| 223 | + |
| 224 | + ## Returns |
| 225 | + |
| 226 | + - `{:ok, Finch.Response.t()}` |
| 227 | + - `{:error, Exception.t()}` |
| 228 | + |
| 229 | + """ |
| 230 | + @spec chat_completions(params :: keyword()) :: {:ok, map()} | {:error, map()} | {:error, any()} |
| 231 | + def chat_completions(params) do |
| 232 | + NimbleOptions.validate!(params, @chat_completions_schema) |
| 233 | + Chat.completions(params, %Config{}) |
| 234 | + end |
| 235 | + |
| 236 | + @doc """ |
| 237 | + The same `chat_completions/1` just overwrite config. |
| 238 | + """ |
| 239 | + @spec chat_completions(params :: keyword(), config :: struct()) :: |
| 240 | + {:ok, map()} | {:error, map()} | {:error, any()} |
| 241 | + def chat_completions(params, %Config{} = config) when is_struct(config) do |
| 242 | + NimbleOptions.validate!(params, @chat_completions_schema) |
| 243 | + Chat.completions(params, config) |
| 244 | + end |
| 13 245 | end |
| @@ -0,0 +1,42 @@ | |
| 1 | + defmodule HipcallOpenai.Chat do |
| 2 | + @moduledoc false |
| 3 | + |
| 4 | + alias HipcallOpenai.Config |
| 5 | + |
| 6 | + @endpoint_url "chat" |
| 7 | + |
| 8 | + def completions(params, config \\ %Config{}) do |
| 9 | + Finch.build( |
| 10 | + :post, |
| 11 | + "#{Config.api_url()}#{@endpoint_url}/completions", |
| 12 | + header(config), |
| 13 | + body(params) |
| 14 | + ) |
| 15 | + |> Finch.request(HipcallOpenaiFinch, receive_timeout: 600_000) |
| 16 | + |> case do |
| 17 | + {:ok, %Finch.Response{status: 200, body: body}} -> |
| 18 | + {:ok, body |> Jason.decode!()} |
| 19 | + |
| 20 | + {:ok, %Finch.Response{status: status, body: body, headers: headers}} -> |
| 21 | + {:error, %{status: status, body: body |> Jason.decode!(), headers: headers}} |
| 22 | + |
| 23 | + {:error, reason} -> |
| 24 | + {:error, reason} |
| 25 | + end |
| 26 | + end |
| 27 | + |
| 28 | + defp body(params) do |
| 29 | + params |> Enum.into(%{}) |> Jason.encode!() |
| 30 | + end |
| 31 | + |
| 32 | + defp header(config) do |
| 33 | + api_key = config.api_key || Config.api_key() |
| 34 | + api_organization = config.api_organization || Config.api_organization() |
| 35 | + |
| 36 | + [ |
| 37 | + {"Authorization", "Bearer #{api_key}"}, |
| 38 | + {"OpenAI-Organization", "#{api_organization}"}, |
| 39 | + {"content-type", "application/json"} |
| 40 | + ] |
| 41 | + end |
| 42 | + end |
Loading more files…