Skip to content

Add configurable OpenAI-compatible providers (config, CLI, and settings panel) - #1

Open
xmmanuellx wants to merge 7 commits into
konradk:mainfrom
xmmanuellx:main
Open

Add configurable OpenAI-compatible providers (config, CLI, and settings panel)#1
xmmanuellx wants to merge 7 commits into
konradk:mainfrom
xmmanuellx:main

Conversation

@xmmanuellx

@xmmanuellx xmmanuellx commented Aug 14, 2026

Copy link
Copy Markdown

Summary

Adds user-defined, OpenAI-compatible providers so Hark can talk to any Chat Completions endpoint (Ollama, LM Studio, vLLM, local gateways, or hosted proxies) — configurable via config.lua, the CLI, or the settings panel.

Fixes the missing ability to change the API base URL.

What changed

Provider definitions

  • internal/config: new top-level providers table in config.lua. Each entry is { id, label, base_url }; ids must be unique, must not collide with the built-ins (openai/openrouter/xai), and base_url must be an absolute http/https URL.
  • internal/ai/openai_compatible: new client that streams Chat Completions from a configurable base URL. It deliberately omits OpenRouter-specific behavior (HTTP-Referer/X-Title headers and the web search plugin).
  • internal/secrets: accepts custom provider ids and resolves <ID>_API_KEY environment variables for them.

Panel + daemon management

  • internal/history: migration v5 adds providers/models tables plus CRUD.
  • cmd/harkd: providers_list/add/remove IPC methods; live config snapshots and an atomic reload that rebuilds the provider map without a restart. Panel-managed providers merge with config.lua providers (config wins on ID collisions).
  • cmd/harkctl: provider list/add/remove commands.
  • QML: a Providers section in Settings with an Add provider form (name, base URL, model, API key) and per-provider removal; custom providers now count toward hasConfiguredProvider.

Example

-- config.lua
return {
  providers = {
    { id = "local", label = "Local vLLM", base_url = "http://localhost:8000/v1" },
  },
  provider = {
    default_model = "llama-3",
    models = {
      { id = "llama-3", label = "Llama 3", provider = "local", reasoning_efforts = { "auto", "low", "medium", "high" } },
    },
  },
}
# or via the settings panel / CLI
harkctl provider add --json --id local --label "Local vLLM" --base-url http://localhost:8000/v1 --model llama-3
harkctl secret set local

Tests

  • internal/config: parsing/validation of providers.
  • internal/ai/openai_compatible: SSE streaming, reasoning-effort forwarding, missing-key/base-url errors.
  • internal/secrets: custom-provider env fallback + name validation.
  • cmd/harkd: provider add/remove handlers, collision rejection, merge/rebuild.
  • QML: existing qmltestrunner suite (51 tests) still passes; qmllint clean.

go test ./... is green.

Notes / follow-ups

  • A panel-managed provider currently carries one model (the one entered in the Add form). Adding multiple models per provider or editing the base URL in place is a possible follow-up; today it's remove + re-add.
  • The settings panel treats a custom provider as configured if it exists, so the send action is enabled even before its API key is set (a missing key surfaces as a provider error on send).

Allow users to define custom providers with a base_url in config.lua, so
Hark can talk to any OpenAI Chat Completions endpoint (Ollama, LM Studio,
vLLM, local gateways, or hosted proxies) and switch between them.

- config: new top-level 'providers' table ({ id, label, base_url }), validated
  for unique ids, reserved-name collisions, and absolute http(s) base URLs
- ai/openai_compatible: new client streaming Chat Completions from a custom
  base URL (no OpenRouter-specific headers or web plugin)
- cmd/harkd: register each custom provider so model entries can route to it
- secrets: accept arbitrary provider ids for the keyring and <ID>_API_KEY env
  fallback so custom providers can store their own key
- docs: README section and config example
Let users add and remove OpenAI-compatible providers (base URL + model +
API key) from Hark Settings, persisted by the daemon and merged with the
config.lua providers at runtime.

- history: migration v5 adds providers/models tables plus CRUD
- cmd/harkd: providers_list/add/remove IPC methods, live config snapshot,
  and atomic reload that rebuilds the provider map without a restart
- cmd/harkctl: provider list/add/remove commands
- QML: a Providers section in Settings with an Add form and per-provider
  removal; custom providers now count toward hasConfiguredProvider
- docs: settings-panel and CLI usage
@xmmanuellx xmmanuellx changed the title Add configurable OpenAI-compatible providers (custom base_url) Add configurable OpenAI-compatible providers (config, CLI, and settings panel) Aug 14, 2026
- daemon: providers_add is now an upsert (edit in place); models_add and
  models_remove manage models independently of the provider; new
  providers_fetch_models lists an endpoint's models via GET {base_url}/models
- harkctl: model add/remove and provider fetch-models commands
- QML: ProviderRow with edit, remove, per-provider model chips, a Fetch
  action, and an Add model field; the provider form no longer requires a model
- model labels default to their endpoint id, so nothing extra needs naming
Drop the model-fetch flow; the panel now just lets you type a model id and
add it, with any number of models per provider. Model names are their id.

- remove providers_fetch_models IPC, harkctl provider fetch-models, and the
  Fetch UI; ProviderRow keeps Edit/Remove + model chips + an Add model field
The model id input now lives inside the provider form as a tag input: type an
id, press Add (or Enter), it becomes a removable chip, and the field stays
available for more ids. Saving writes the provider and its models atomically.

- daemon: providers_save upserts the provider and reconciles its model set
  (replaces providers_add/models_add/models_remove)
- harkctl: provider save with repeated --model flags
- QML: ProviderRow is read-only (Edit/Remove + model chips); the form collects
  the model ids as tags
ListModel does not round-trip a nested JS array role, so the models were
lost when opening the edit form. Store the model ids as a JSON string role
and parse it in the delegate; the edit form now preloads the existing ids
as removable tags.
@rotsen93

Copy link
Copy Markdown

This is what this plugin needed, i was going to ask to the main developer to do this, but i just tried, and is awesome, i can even have my local models running on this!!! Perfect

Bar widgets read a per-widget 'hidden' setting from their shell.json entry,
so BarWidget.qml collapses to zero size when hidden, and the settings panel
gets a 'Show Hark icon in the bar' toggle that drives 'omarchy bar set hark
hidden true/false'. Keeps the overlay, service, and shortcut untouched.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants