{"id":17603460,"url":"https://github.com/lebrunel/omni","last_synced_at":"2025-10-12T23:41:16.049Z","repository":{"id":244981703,"uuid":"816900802","full_name":"lebrunel/omni","owner":"lebrunel","description":"One client for all LLMs. Universal Elixir chat completion API client.","archived":false,"fork":false,"pushed_at":"2025-04-11T18:37:10.000Z","size":174,"stargazers_count":3,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-09-21T09:04:38.230Z","etag":null,"topics":["anthropic","api-client","chatgpt","elixir","llms","ollama"],"latest_commit_sha":null,"homepage":"https://hexdocs.pm/omni","language":"Elixir","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/lebrunel.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2024-06-18T16:01:16.000Z","updated_at":"2025-03-24T10:00:27.000Z","dependencies_parsed_at":"2024-06-18T20:42:51.525Z","dependency_job_id":"78b0aa5a-bd0d-4ecf-9e47-2e44297eb3a9","html_url":"https://github.com/lebrunel/omni","commit_stats":null,"previous_names":["lebrunel/omni"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/lebrunel/omni","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lebrunel%2Fomni","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lebrunel%2Fomni/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lebrunel%2Fomni/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lebrunel%2Fomni/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/lebrunel","download_url":"https://codeload.github.com/lebrunel/omni/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lebrunel%2Fomni/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":279013412,"owners_count":26085274,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-10-12T02:00:06.719Z","response_time":53,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["anthropic","api-client","chatgpt","elixir","llms","ollama"],"created_at":"2024-10-22T13:48:48.372Z","updated_at":"2025-10-12T23:41:16.017Z","avatar_url":"https://github.com/lebrunel.png","language":"Elixir","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Omni\n\n![Omni](https://raw.githubusercontent.com/lebrunel/omni/main/media/poster.png)\n\n![Hex.pm](https://img.shields.io/hexpm/v/omni?color=informational)\n![License](https://img.shields.io/github/license/lebrunel/omni?color=informational)\n![Build Status](https://img.shields.io/github/actions/workflow/status/lebrunel/omni/elixir.yml?branch=main)\n\nOmni focusses on one thing only - being a chat interface to *any* LLM provider. If you want a full featured client for a specific provider, supporting all available API endpoints, this is probably not it. If you want a single client to generate chat completions with literally any LLM backend, Omni is for you.\n\n- 🧩 `Omni.Provider` behaviour to create integrations with any LLM provider. Built-in providers for:\n  - [`Anthropic`](`Omni.Providers.Anthropic`) - chat with any of of the Claude models.\n  - [`Google`](`Omni.Providers.Google`) - chat with any of of the Gemini models.\n  - [`Ollama`](`Omni.Providers.Ollama`) - use Ollama to chat with any local model.\n  - [`OpenAI`](`Omni.Providers.OpenAI`) - chat with ChatGPT or **any** other OpenAI compatible API.\n- 🛜 Streaming API requests\n  - Stream to an Enumerable\n  - Or stream messages to any Elixir process\n- 💫 Simple to use and easily customisable\n\n## Installation\n\nThe package can be installed by adding `omni` to your list of dependencies in `mix.exs`.\n\n```elixir\ndef deps do\n  [\n    {:omni, \"~\u003e 0.1\"}\n  ]\nend\n```\n\n## Quickstart\n\nTo chat with an LLM, initialize a `t:provider/0` with `init/2`, and then send a `t:request/0`, using one of `generate/2`, `async/2` or `stream/2`. Refer to the schema documentation for each provider to ensure you construct a valid request.\n\n```elixir\niex\u003e provider = Omni.init(:openai)\niex\u003e Omni.generate(provider, model: \"gpt-4o\", messages: [\n...\u003e   %{role: \"user\", content: \"Write a haiku about the Greek Gods\"}\n...\u003e ])\n{:ok, %{\"object\" =\u003e \"chat.completion\", \"choices\" =\u003e [...]}}\n```\n\n## Streaming\n\nOmni supports streaming request through `async/2` or `stream/2`.\n\nCalling `async/2` returns a `t:Task.t/0`, which asynchronously sends text delta messages to the calling process. Using the `:stream_to` request option allows you to control the receiving process.\n\nThe example below demonstrates making a streaming request in a LiveView event, and sends each of the streaming messages back to the same LiveView process.\n\n```elixir\ndefmodule MyApp.ChatLive do\n  use Phoenix.LiveView\n\n  # When the client invokes the \"prompt\" event, create a streaming request and\n  # asynchronously send messages back to self.\n  def handle_event(\"prompt\", %{\"message\" =\u003e prompt}, socket) do\n    {:ok, task} = Omni.async(Omni.init(:openai), [\n      model: \"gpt-4o\",\n      messages: [\n        %{role: \"user\", content: \"Write a haiku about the Greek Gods\"}\n      ]\n    ])\n\n    {:noreply, assign(socket, current_request: task)}\n  end\n\n  # The streaming request sends messages back to the LiveView process.\n  def handle_info({_request_pid, {:data, _data}} = message, socket) do\n    pid = socket.assigns.current_request.pid\n    case message do\n      {:omni, ^pid, {:chunk, %{\"choices\" =\u003e choices, \"finish_reason\" =\u003e nil}}} -\u003e\n        # handle each streaming chunk\n\n      {:omni, ^pid, {:chunk, %{\"choices\" =\u003e choices}}} -\u003e\n        # handle the final streaming chunk\n    end\n  end\n\n  # Tidy up when the request is finished\n  def handle_info({ref, {:ok, _response}}, socket) do\n    Process.demonitor(ref, [:flush])\n    {:noreply, assign(socket, current_request: nil)}\n  end\nend\n```\n\nAlternatively, use `stream/2` to collect the streaming responses into an `t:Enumerable.t/0` that can be used with Elixir's `Stream` functions.\n\n```elixir\niex\u003e provider = Omni.init(:openai)\niex\u003e {:ok, stream} = Omni.stream(provider, model: \"gpt-4o\", messages: [\n...\u003e   %{role: \"user\", content: \"Write a haiku about the Greek Gods\"}\n...\u003e ])\n\niex\u003e stream\n...\u003e |\u003e Stream.each(\u0026IO.inspect/1)\n...\u003e |\u003e Stream.run()\n```\n\nBecause this function builds the `t:Enumerable.t/0` by calling `receive/1`, take care using `stream/2` inside `GenServer` callbacks as it may cause the GenServer to misbehave.\n\n## License\n\nThis package is open source and released under the [Apache-2 License](https://github.com/lebrunel/omni/blob/master/LICENSE).\n\n© Copyright 2024 [Push Code Ltd](https://www.pushcode.com/).","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flebrunel%2Fomni","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Flebrunel%2Fomni","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flebrunel%2Fomni/lists"}