Skip to content

fix(runtime): resolve a Responses relay from one API base - #3797

Open
Sandu1213 wants to merge 1 commit into
apache:mainfrom
Sandu1213:fix/responses-relay-api-base-url
Open

fix(runtime): resolve a Responses relay from one API base#3797
Sandu1213 wants to merge 1 commit into
apache:mainfrom
Sandu1213:fix/responses-relay-api-base-url

Conversation

@Sandu1213

Copy link
Copy Markdown

Summary

A custom OpenAI Responses relay configured with its host root — http://relay.example:3000 — passes the connection probe and then fails every model refresh. The probe posts to <root>/responses, which relays answer, while discovery requests <root>/models instead of the /v1/models an OpenAI-compatible surface publishes. The connection reads as verified and its catalog never updates.

responsesRelayApiBaseUrl normalizes the configured relay URL once into the single API base that send, probe, and discovery all derive from:

configured API base
http://relay.example:3000 http://relay.example:3000/v1
http://relay.example:3000/ http://relay.example:3000/v1
https://relay.example/v1 https://relay.example/v1
https://relay.example/v1/responses https://relay.example/v1
https://relay.example/relay/v1 https://relay.example/relay/v1

resolveModelRuntime applies it to the send and probe addresses, fetchProviderModelsStrict to discovery, so the two can no longer disagree. Root self-healing follows anthropicV1BaseUrl and googleV1BetaBaseUrl; an authored path is preserved verbatim, so openResponsesUrl's deliberate "no assumed /v1" contract (#2972) is unchanged for every other provider.

Fixes #3320

Review focus

The scope is the product decision. Only openai-responses-compatible self-heals. DeepSeek ships https://api.deepseek.com and legitimately serves /responses and /models at that unversioned root, so a blanket root → /v1 rule would break a built-in; a DeepSeek root base keeps serving Responses at the root it publishes guards exactly that. The alternative raised on the issue — keep the configured base authoritative and warn about a bare host root in the add/edit form — leaves the two addresses resolving differently and still requires the user to know the version segment. Happy to move to it if that is the direction you want.

Coordination: #3320 is assigned to @Xinbeok. Its other half — a failed discovery dropping the manually configured fallback model — already merged as #3581, so the URL half is what remains. Close this if it duplicates work already in flight.

Verification

  • npx tsc -p packages/runtime/tsconfig.json — clean.
  • node --test "dist/**/*.test.js" in packages/runtime — 3031 tests, 0 failures, including the three new cases.
  • Fails without the fix: reverting only provider-urls.ts, model-runtime.ts, and model-fetcher.ts and rebuilding turns a Responses relay configured with its host root discovers and probes one /v1 base red — actual '/models', expected '/v1/models'.
  • npx biome lint . and npx biome format . — clean repo-wide.
  • npm --workspace @maka/core run test:dist — 656/656.

Pre-existing on main at 689a451 and unchanged by this branch (reproduced with the branch stashed):

  • npm run build stops in @maka/runtime-hostsrc/__tests__/execution-host-queue.test.ts still calls queryTurn/stopTurn/startTurn, which refactor(runtime-host): use typed request as sole operation API #3784 removed from RuntimeHostConnection. @maka/desktop and @maka/ui therefore could not be built or typechecked locally.
  • @maka/storage — 5 SqliteSessionMetadataStore steering/follow-up failures. This branch touches no storage code.

AI use

Select exactly one:

  • No generative tool made a substantive contribution
  • Generative tooling made a substantive contribution

Tool(s) and scope: Claude Opus 5 investigated the two call sites, wrote the normalizer, its three call-site wirings, and the tests; the commit carries a Generated-by: Claude Opus 5 trailer. A human reviewed the scoping decision, ran the checks above, and owns the submission.

Checklist

  • Tests cover the change and fail without it
  • Lint, format, typecheck and the affected suites pass locally

Does this PR entail a change in behavior?

  • Yes — described under Summary above
  • No

A custom OpenAI Responses relay configured with its host root passed the
connection probe and then failed every model refresh. The probe posts to
`<root>/responses`, which relays answer, while discovery requested
`<root>/models` instead of the `/v1/models` an OpenAI-compatible surface
publishes — so the connection looked verified and its catalog never updated.

Normalize the configured relay URL once into the API base that send, probe,
and discovery all derive from: a path-less root self-heals to `/v1`, as
`anthropicV1BaseUrl` and `googleV1BetaBaseUrl` already do for their versions;
an authored path (`/v1`, `/relay/v1`) is preserved verbatim; the endpoint form
reduces to its base. Scoped to `openai-responses-compatible`, because built-ins
such as DeepSeek legitimately serve `/responses` and `/models` at their own
unversioned root and must not be rewritten.

Refs apache#3320

Generated-by: Claude Opus 5

@Astro-Han Astro-Han left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I reviewed this head and found no blocking issues.

The relay base normalization now consistently heals bare roots to /v1 and strips trailing /responses at the endpoint, aligning probe, discovery, and send. Scoped only to openai-responses-compatible to avoid breaking built-in providers that serve both paths. Tests lock the five normalization cases.

No P0-P2. Checks on afb7b5d have no hosted report yet — not green.

简体中文该头未发现阻断。

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.

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

2 participants