Skip to content

List models from the server for OpenAI-compatible providers - #41

Open
sachin-detrax wants to merge 2 commits into
AtomicBot-ai:mainfrom
sachin-detrax:feat/openai-compat-model-list
Open

List models from the server for OpenAI-compatible providers#41
sachin-detrax wants to merge 2 commits into
AtomicBot-ai:mainfrom
sachin-detrax:feat/openai-compat-model-list

Conversation

@sachin-detrax

Copy link
Copy Markdown
Contributor

Closes #31.

Problem

The OpenAI-compatible provider wizard asked the operator to type a chat model id by hand. Against a remote vLLM/SGLang deployment — the case in #31 — nobody knows the ids offhand, and providers rotate them without notice. The server already publishes them at GET {baseUrl}/v1/models.

Change

  • src/llm/provider/openai/fetch-openai-compat-models.ts (new) — GET {baseUrl}/v1/models with the bearer key, sorted ids, 10s timeout, cached per normalized base URL. Same fetch-then-read-sync shape the OpenRouter picker already uses, so the sync key handler can read the result.
  • Chat model step — now fetches on entry and renders the ids as a 12-row window around the cursor (↑/↓ move (21/341) · Enter select). Typing still works: any printable key is a deliberate override that hides the list, backspacing to empty brings it back. Arrows only for movement, so ids starting with j/k stay typable.
  • Failure is not fatal — a refused, empty or unreachable endpoint degrades to the old typed field with the reason in the hint (model list unavailable (http 401) — type the id).
  • Base URL normalization on save — a base pasted as https://host/v1/ was stored verbatim and produced https://host/v1//v1/chat/completions on every later request. It is now normalized once, and the same helper feeds the fetch, the cache key and the displayed URL.

The API key comes from the wizard buffer, falling back to resolveLlmProviderApiKey (OPENAI_COMPAT_API_KEY / OPENAI_API_KEY / ATOMIC_AGENT_OPENAI_API_KEY), so a key already in .env needs no retyping.

Verification

15 new tests (fetch/cache/normalize, picker key flow, typed override, windowed render, refusal fallback). Full suite: 3293 passing, unchanged pre-existing failures on main; tsc clean.

Also exercised against real servers, no mocks:

  • https://openrouter.ai/api — 341 ids listed and rendered; base pasted as .../api/v1/ hit the cache instead of re-requesting; arrows + Enter selected a model and built the entry with baseUrl: https://openrouter.ai/api.
  • A local vLLM-shaped HTTP server — server log showed exactly one GET /v1/models auth=Bearer <key>, and the cold-cache render produced the windowed picker.
  • https://api.openai.com with no key → http 401, nothing cached, hint shown; unreachable host → fetch failed hint.

Known limitation

Servers whose OpenAI base is a path prefix rather than a host root (e.g. DeepInfra's https://api.deepinfra.com/v1/openai) 404 on discovery, because the codebase appends /v1/... and the normalizer only strips a trailing /v1. That shape already cannot serve chat completions through this provider, so it is pre-existing rather than a regression; the wizard falls back to typed entry there.

Closes AtomicBot-ai#31. The OpenAI-compatible wizard asked operators to type a chat
model id by hand, which nobody can do for a remote vLLM deployment whose
model list changes without notice.

The chat model step now GETs `{baseUrl}/v1/models` (bearer key from the
wizard buffer or the resolved env key) and shows the ids as a windowed
picker. Typing still works — any keystroke is treated as a deliberate
override and hides the list; backspacing to empty brings it back. When
the server refuses or is unreachable the step degrades to the old typed
field with the error in the hint.

Base URLs are also normalised on save: a pasted `https://host/v1/` used
to be stored verbatim and produce `https://host/v1//v1/chat/completions`
on every request.
Live check against openrouter with a base URL pasted as `.../api/v1`
rendered `.../api/v1/v1/models` in the header while requesting the
correct URL. Normalize at the single source both the fetch and the
header read from.
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.

Model list for openai-compatible servers

1 participant