Skip to content

Custom OpenAI Responses relay does not append /v1 when fetching models #3320

Open

Description

@yingning2021

English

What happened

When a custom OpenAI Responses-compatible relay is configured with a root URL such as:

http://relay.example:3000

the connection test succeeds, but updating the model catalog fails.

Maka requests:

GET /models

However, the relay provides the OpenAI-compatible model list at:

GET /v1/models

Cherry Studio works with the same configuration because it automatically appends /v1 when the base URL does not contain an API version. OpenAI's standard endpoints are also versioned:

GET  /v1/models
POST /v1/responses

Expected behavior

We should clarify whether the configured URL is a host root or a complete API base URL, and use the same URL normalization rule for model discovery and Responses requests.

Environment

  • Maka: 0.1.11
  • Surface: Desktop
  • OS: Windows

中文

问题描述

配置自定义 OpenAI Responses 兼容中转站时,如果服务地址填写为根地址:

http://relay.example:3000

连接测试可以成功,但更新模型目录失败。

Maka 当前请求:

GET /models

而该中转站的 OpenAI 兼容模型接口为:

GET /v1/models

相同配置在 Cherry Studio 中可以使用,因为当地址不包含 API 版本时,它会自动补充 /v1。OpenAI 的标准接口也包含版本前缀:

GET  /v1/models
POST /v1/responses

期望行为

需要明确配置地址表示主机根地址还是完整 API Base URL,并让模型发现和 Responses 请求使用一致的 URL 处理规则。

环境

  • Maka:0.1.11
  • 使用端:Desktop
  • 系统:Windows

Activity

  1. yingning2021 commented on Aug 20, 2026

    @yingning2021
    Author

    Additional related issue

    providerSupportsModelDiscovery() currently means that a provider can attempt model discovery (modelDiscovery.kind !== 'fallback'). It does not mean that a specific connection has successfully fetched an authoritative model catalog.

    For custom OpenAI Responses relays, a failed fetch leaves models: []. The catalog code then treats this defined empty array as an authoritative inventory, marks the manually configured fallback model as not_in_live_list, and removes it from the Chat model selector.

    Expected behavior: only a successfully fetched catalog, such as modelSource === 'fetched', should be authoritative. A failed or not-yet-attempted discovery should preserve the manually configured picker fallback.

    相关补充

    providerSupportsModelDiscovery() 当前表示 Provider 可以尝试拉取模型(modelDiscovery.kind !== 'fallback'),并不表示某个具体连接已经成功获得了权威模型目录。

    对于自定义 OpenAI Responses 中转站,拉取失败后会保留 models: []。模型目录代码随后将这个空数组视为权威目录,把手工配置的兜底模型标记为 not_in_live_list,并从聊天模型选择器中移除。

    期望行为:只有成功拉取的目录,例如 modelSource === 'fetched',才应被视为权威目录。拉取失败或尚未拉取时,应继续保留用户手工配置的模型选择器兜底。

  2. FenjuFu commented on Aug 21, 2026

    @FenjuFu

    English

    I traced this end to end — happy to send a PR once the direction is confirmed, since the two obvious fixes point in opposite directions.

    Root cause. Both the probe and model discovery append their endpoint to the base URL as-is:

    • Discovery: modelListUrl(baseUrl, …) → ${baseUrl}/models (packages/runtime/src/model-fetcher.ts).
    • Connection test: openResponsesUrl(baseUrl) → ${baseUrl}/responses (packages/runtime/src/test-connection.ts).

    For the built-in OpenAI provider the default base is https://api.openai.com/v1, so both resolve to /v1/... correctly. With a bare-root relay (http://relay.example:3000) the two paths diverge only because the relay happens to accept /responses at the root (so the probe passes) while its model list lives at /v1/models (so discovery 404s). Maka's two call sites are actually consistent with each other; the relay is the inconsistent side.

    Why it's not a one-liner. The anthropic/google helpers self-heal to their version (anthropicV1BaseUrl, googleV1BetaBaseUrl), but openResponsesUrl deliberately does not — its doc comment: "Normalize an Open Responses endpoint without assuming a /v1 prefix" (#2972). So:

    1. Auto-append /v1 when the base has no version segment (the Cherry Studio behavior). Fixes this report, but contradicts the intentional no-/v1 Responses design and would break relays that legitimately serve /models / /responses at the root.
    2. Treat the configured URL as the complete API base (status quo) and instead improve UX — e.g. detect a bare host root in the add/edit form and prompt for the versioned base, and/or make discovery reuse openResponsesUrl's exact normalization so the two are provably identical.

    I lean toward (2): keep the base URL authoritative (no magic version rewriting, consistent with openResponsesUrl) and surface a clear hint when the user enters a bare root. But this is a product call — I'll implement whichever you prefer.

    Related (from @yingning2021 above). Independent of the URL question, a failed discovery leaves models: [], which the catalog then treats as an authoritative empty inventory and drops the manually-configured fallback model from the picker (missingEntryUnavailableReason → not_in_live_list in packages/core/src/model-catalog.ts; the source === 'fetched' empty-is-authoritative branch in packages/storage/src/runtime-policy/connection-catalog-document.ts). Only a successfully fetched catalog should be authoritative; a failed/not-yet-attempted fetch should preserve the user's fallback. This is a safe, self-contained fix and arguably the more user-visible half of the bug — happy to take it in a separate PR if you'd like to split the two concerns.

    中文

    根因:探针与模型发现都是把端点直接拼到 base URL 后面——发现走 ${baseUrl}/models,连接测试走 openResponsesUrl 即 ${baseUrl}/responses。内置 OpenAI 的默认 base 是 https://api.openai.com/v1,两者都能正确落到 /v1/...;而根地址中转站因为 /responses 在根路径下也能应答(探针通过)、但模型列表在 /v1/models(发现 404),才显得不一致。其实 Maka 两处是一致的,不一致的是中转站本身。

    两种修法方向相反:①无版本时自动补 /v1(Cherry Studio 行为),但与 openResponsesUrl 刻意"不假设 /v1"(#2972)的设计冲突,还会误伤真正在根路径提供 /models 的中转站;②把配置地址视为完整 API base(现状),改为在表单里识别"裸主机根地址"时提示补全版本,并让发现复用 openResponsesUrl 完全相同的归一化逻辑。我倾向 ②。方向定了我来提 PR。

    另外 @yingning2021 提到的点是独立且安全的 bug:发现失败留下 models: [] 被当成权威空目录,把手工配置的兜底模型从选择器里删掉。只有"成功拉取"的目录才应权威,失败/未拉取应保留用户兜底。这块可以单独一个 PR。

  3. Xinbeok commented on Aug 22, 2026

    @Xinbeok

    Has this issue been resolved? I want to claim this issue.

  4. FenjuFu commented on Aug 23, 2026

    @FenjuFu

    This is still open, unassigned, and not resolved. I have not claimed or started an implementation; my comment above was root-cause analysis and an offer pending maintainer direction. Please coordinate with the maintainers before coding because the URL-normalization choice changes product behavior, while the failed-discovery fallback is a separate, self-contained bug that could be handled independently. Linking any proposed scope or PR here will help avoid duplicate work.

  5. Sandu1213 commented on Aug 25, 2026

    @Sandu1213

    Opened #3797 for the remaining URL half of this issue. The other half — a failed discovery dropping the manually configured fallback model — merged as #3581.

    Scope and the reason for it: only openai-responses-compatible normalizes its configured URL into one API base (host root self-heals to /v1, an authored path such as /v1 or /relay/v1 is preserved, the …/responses endpoint form reduces to its base). Discovery, the probe, and the send path all derive from that base, so they can no longer disagree. A blanket root → /v1 rule is not safe: DeepSeek ships https://api.deepseek.com and legitimately serves both /responses and /models at that unversioned root, and the PR carries a regression test for it. openResponsesUrl's deliberate no-assumed-/v1 contract (#2972) is untouched for every other provider.

    @Xinbeok you are the assignee here — if you already have this in flight, say so and I will close #3797. Maintainers: if you prefer the other direction (keep the configured base authoritative and warn about a bare host root in the add/edit form instead), I will rework it that way.

  6. nagarwal-godaddy commented on Aug 25, 2026

    @nagarwal-godaddy

    Working on the URL-normalization half (PR #3581 covers only the fallback-model half).

  7. added theissue type on Aug 29, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

bugSomething isn't working

Type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions