From 21702031ffe8abdcc404995a102a17aa7b9e474c Mon Sep 17 00:00:00 2001 From: Adam Smith <2105363+adamjs83@users.noreply.github.com> Date: Thu, 17 Sep 2026 21:43:23 -0400 Subject: [PATCH] docs: add README and brand icon; drop invalid hacs.json key The `hacs` validation job has failed on every push since the scaffold commit (6/9 checks). This fixes the four failures that live in the repo; the two that live in GitHub metadata (description, topics) are set via the API and need no commit. - hacs.json: remove `render_readme`. It is not in the HACS manifest schema (valid keys: name, content_in_root, zip_release, filename, hide_default_branch, country, homeassistant, hacs, persistent_directory), and HACS rejects the whole file on an unknown key -- which is what ` failed` was. Expecting the cascading `integration_manifest` failure ("expected a dictionary. Got None") to clear with it, since manifest.json itself already carries every key HACS requires. - README.md: the repo had none, so the `information` check failed and HACS had nothing to render. Documents the one thing this fork adds (configurable base URL) and, importantly, the compatibility ceiling: upstream calls the Responses API, so an endpoint that only serves /v1/chat/completions cannot back the conversation or AI Task subentries. Also records which endpoint each subentry needs, that upstream's recommended model names and unsupported-model lists are OpenAI-specific, the base-URL rules (http/https, no embedded credentials, include /v1), and why a blank API key sends the `not-required` placeholder. - brand/icon.png + icon@2x.png: `brands` fails unless the repo ships brand assets or the domain is in home-assistant/brands. Shipping assets is the option that does not depend on a third-party PR. Generated with stdlib zlib/struct (no PIL or ImageMagick in this environment) from supersampled SDFs; the generator is scratch, not committed. Deliberately neither OpenAI's nor Home Assistant's mark, and a placeholder -- replace it with real artwork before release. Not done here: this fork has no git tag or GitHub release yet. HACS treats releases as preferred-but-not-required and the action does not check for one, so it is not part of this failure. Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_01NxJYWgGJtCw13qUHZtG8ki --- README.md | 149 ++++++++++++++++++ .../openai_compatible/brand/icon.png | Bin 0 -> 6968 bytes .../openai_compatible/brand/icon@2x.png | Bin 0 -> 14930 bytes hacs.json | 3 +- 4 files changed, 150 insertions(+), 2 deletions(-) create mode 100644 README.md create mode 100644 custom_components/openai_compatible/brand/icon.png create mode 100644 custom_components/openai_compatible/brand/icon@2x.png diff --git a/README.md b/README.md new file mode 100644 index 0000000..49420e8 --- /dev/null +++ b/README.md @@ -0,0 +1,149 @@ +# OpenAI Compatible for Home Assistant + +[![test](https://github.com/adamjs83/ha-openai-compatible/actions/workflows/test.yaml/badge.svg)](https://github.com/adamjs83/ha-openai-compatible/actions/workflows/test.yaml) +[![validate](https://github.com/adamjs83/ha-openai-compatible/actions/workflows/validate.yaml/badge.svg)](https://github.com/adamjs83/ha-openai-compatible/actions/workflows/validate.yaml) +[![HACS: custom](https://img.shields.io/badge/HACS-custom-41BDF5.svg)](https://hacs.xyz) + +Home Assistant's built-in **OpenAI** integration, with one change that matters: the +**API base URL is configurable**. Point Home Assistant's conversation agent, AI Task, +speech-to-text and text-to-speech at any OpenAI-compatible endpoint — LiteLLM, vLLM, +OpenRouter, a gateway of your own — instead of only `api.openai.com`. + +Everything else is upstream Home Assistant code, vendored unmodified, so behavior and +options match the core integration you already know. + +## Why this exists + +Core's `openai_conversation` hardcodes OpenAI's endpoint. If you run a proxy for cost +control, routing, key management or privacy, or you self-host models, there is no way to +tell the core integration about it. This fork adds a **Base URL** field to the config +flow and threads it to every client it creates. + +## What you get + +Everything the core integration provides, as config **subentries** you can add more than +one of: + +- **Conversation agent** — an Assist conversation entity, with the LLM Assist API, + tool calling, and the full upstream option set (model, temperature, top-p, max tokens, + reasoning effort, verbosity, web search, code interpreter, service tier, …) +- **AI Task** — for `ai_task.generate_data` / image generation +- **Speech-to-text** — an STT entity +- **Text-to-speech** — a TTS entity, with speed control +- **Actions** — `openai_compatible.generate_content` and `openai_compatible.generate_image` + +## Requirements + +- Home Assistant **2026.9.0** or newer +- An OpenAI-compatible endpoint (see [Endpoint compatibility](#endpoint-compatibility)) + +## Installation + +### HACS (custom repository) + +1. HACS → ⋮ → **Custom repositories** +2. Add `https://github.com/adamjs83/ha-openai-compatible`, category **Integration** +3. Find **OpenAI Compatible** in HACS, download it, and restart Home Assistant +4. **Settings → Devices & services → Add integration → OpenAI Compatible** + +### Manual + +Copy `custom_components/openai_compatible/` into your Home Assistant +`config/custom_components/` directory and restart. + +## Configuration + +The initial dialog asks for two things: + +| Field | Notes | +| --- | --- | +| **Base URL** | Full URL including any version path, e.g. `https://api.openai.com/v1`. Passed to the OpenAI SDK as-is. | +| **API key** | Optional — **leave blank** for an endpoint that does not authenticate. | + +Then add one subentry per capability you want (conversation, AI Task, STT, TTS), each +with its own model and options. + +### Base URL rules + +- Only `http` and `https` are accepted; the URL must include a host. +- Whitespace and trailing slashes are stripped. +- **Include the version path** if your server expects one. Most do: `/v1`. +- Credentials embedded in the URL (`https://user:pass@host/v1`) are **rejected**. Put + the token in the API key field — it is stored as a secret and kept out of logs, while + a base URL is echoed back in error and log messages. +- Re-authenticating (rotating the API key) **keeps** your configured base URL. + +### Empty API keys + +Local endpoints often require no auth. Leave the API key blank and the integration sends +the placeholder `not-required` — the OpenAI SDK refuses to build a client with no key at +all, so something has to be sent, and servers that ignore auth ignore it. + +### Endpoint examples + +| Server | Typical base URL | +| --- | --- | +| OpenAI | `https://api.openai.com/v1` | +| LiteLLM | `http://litellm.local:4000/v1` | +| vLLM | `http://vllm.local:8000/v1` | +| OpenRouter | `https://openrouter.ai/api/v1` | +| Azure OpenAI (via a proxy) | your proxy's URL | + +## Endpoint compatibility + +This is the important caveat. Upstream uses OpenAI's **Responses API**, not Chat +Completions, so a server that only implements `/v1/chat/completions` **will not work** +for conversation or AI Task. + +| Feature | Endpoint the server must implement | +| --- | --- | +| Setup / connection test | `GET /v1/models` | +| Conversation, AI Task | `POST /v1/responses` | +| Text-to-speech | `POST /v1/audio/speech` | +| Speech-to-text | `POST /v1/audio/transcriptions` | +| `generate_image` action | `POST /v1/images/generations` | + +You only need the endpoints for the subentries you actually add. Check your server's +docs for Responses API support before filing a bug — LiteLLM and vLLM have it in recent +versions; several popular local servers still do not. + +### Model names + +The default and "recommended" model names come from upstream and are OpenAI's +(`gpt-4o-mini`, `gpt-4o-mini-tts`, `gpt-4o-mini-transcribe`, …). Set the model your +endpoint actually serves in each subentry's options. Upstream's lists of models that +don't support web search, image generation, and so on are also keyed to OpenAI names, so +they won't match a third-party model string — the endpoint's own error is what you'll +see if you ask for something it can't do. + +## Relationship to Home Assistant core + +`custom_components/openai_compatible/` is derived from +`homeassistant/components/openai_conversation/` at core tag **2026.9.2** +(commit recorded in [`UPSTREAM_COMMIT`](custom_components/openai_compatible/UPSTREAM_COMMIT)), +with the domain renamed to `openai_compatible` and the base URL made configurable. + +Base-URL handling lives in its own module, +[`url_util.py`](custom_components/openai_compatible/url_util.py), deliberately separate +from the vendored files so re-syncing with core stays a clean merge. + +Installing this alongside core's OpenAI integration is fine — different domain, separate +config entries. This is **not** an official Home Assistant integration and is not +affiliated with or endorsed by OpenAI. + +## Development + +Requires Python 3.14+ (Home Assistant 2026.9's floor). + +```bash +pip install -r requirements_test.txt +pytest tests/ -v +``` + +The suite is core's `openai_conversation` test suite, ported as a regression net, plus +tests for the base-URL behavior this fork adds. + +## License + +[Apache-2.0](LICENSE), inherited from Home Assistant core. See [NOTICE](NOTICE) for +attribution. diff --git a/custom_components/openai_compatible/brand/icon.png b/custom_components/openai_compatible/brand/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..d138c264e603758575dd4f8b85c9a6ea4d433536 GIT binary patch literal 6968 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 literal 0 HcmV?d00001 diff --git a/custom_components/openai_compatible/brand/icon@2x.png b/custom_components/openai_compatible/brand/icon@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..efa1570c56d348b57342109342f58ac1574aef86 GIT binary patch literal 14930 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 literal 0 HcmV?d00001 diff --git a/hacs.json b/hacs.json index 51bc147..5ea8d43 100644 --- a/hacs.json +++ b/hacs.json @@ -1,6 +1,5 @@ { "name": "OpenAI Compatible", "content_in_root": false, - "homeassistant": "2026.9.0", - "render_readme": true + "homeassistant": "2026.9.0" }