Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
193 changes: 193 additions & 0 deletions packages/runtime/src/__tests__/provider-conformance.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1111,6 +1111,199 @@ describe('models.dev provider conformance', () => {
assert.equal(probedPath, '/v1/responses');
});

test('a root-mounted Responses relay keeps discovery and probes at the configured root', async () => {
const requests: Array<{ method: string; url: string }> = [];
const server = await startJsonServer((request, response) => {
requests.push({ method: request.method ?? '', url: request.url ?? '' });
if (request.method === 'GET' && request.url === '/models') {
respondJson(response, 200, { data: [{ id: 'relay-reasoner' }] });
return;
}
if (request.method === 'POST' && request.url === '/responses') {
respondJson(response, 200, {});
return;
}
respondJson(response, 404, { error: { message: 'not found' } });
});
const connection: LlmConnection = {
slug: 'responses-relay',
name: 'Responses Relay',
providerType: 'openai-responses-compatible',
baseUrl: server.url,
defaultModel: 'relay-reasoner',
enabled: true,
createdAt: 1,
updatedAt: 1,
};

assert.deepEqual(
(await fetchProviderModels(connection, 'relay-key')).map(({ id }) => id),
['relay-reasoner'],
);
assert.equal((await testConnection(connection, 'relay-key')).ok, true);
assert.deepEqual(requests, [
{ method: 'GET', url: '/models' },
{ method: 'POST', url: '/responses' },
]);
});

test('a Responses relay falls back to /v1/models when its root model route is absent', async () => {
const requests: Array<{ method: string; url: string }> = [];
const server = await startJsonServer((request, response) => {
requests.push({ method: request.method ?? '', url: request.url ?? '' });
if (request.method === 'GET' && request.url === '/models') {
respondJson(response, 404, { error: { message: 'not found' } });
return;
}
if (request.method === 'GET' && request.url === '/v1/models') {
respondJson(response, 200, { data: [{ id: 'relay-reasoner' }] });
return;
}
if (request.method === 'POST' && request.url === '/responses') {
respondJson(response, 200, {});
return;
}
respondJson(response, 404, { error: { message: 'not found' } });
});
const connection: LlmConnection = {
slug: 'responses-relay',
name: 'Responses Relay',
providerType: 'openai-responses-compatible',
baseUrl: server.url,
defaultModel: 'relay-reasoner',
enabled: true,
createdAt: 1,
updatedAt: 1,
};

assert.deepEqual(
(await fetchProviderModels(connection, 'relay-key')).map(({ id }) => id),
['relay-reasoner'],
);
assert.equal((await testConnection(connection, 'relay-key')).ok, true);
assert.deepEqual(requests, [
{ method: 'GET', url: '/models' },
{ method: 'GET', url: '/v1/models' },
{ method: 'POST', url: '/responses' },
]);
});

test('a plain OpenAI-compatible relay never retries discovery at /v1/models', async () => {
// The 404 fallback is scoped to the Responses relay by provider type; both
// OpenAI-shaped adapters share this discovery branch.
const requestedPaths: string[] = [];
const server = await startJsonServer((request, response) => {
requestedPaths.push(request.url ?? '');
respondJson(response, 404, { error: { message: 'not found' } });
});
const connection: LlmConnection = {
slug: 'chat-relay',
name: 'Chat Relay',
providerType: 'openai-compatible',
baseUrl: server.url,
defaultModel: 'relay-model',
enabled: true,
createdAt: 1,
updatedAt: 1,
};

await assert.rejects(fetchProviderModels(connection, 'relay-key'));
assert.deepEqual(requestedPaths, ['/models']);
});

test('a path-mounted Responses relay never appends /v1 to its own prefix', async () => {
const requestedPaths: string[] = [];
const server = await startJsonServer((request, response) => {
requestedPaths.push(request.url ?? '');
respondJson(response, 404, { error: { message: 'not found' } });
});
const connection: LlmConnection = {
slug: 'responses-relay',
name: 'Responses Relay',
providerType: 'openai-responses-compatible',
baseUrl: `${server.url}/gateway`,
defaultModel: 'relay-reasoner',
enabled: true,
createdAt: 1,
updatedAt: 1,
};

await assert.rejects(fetchProviderModels(connection, 'relay-key'));
assert.deepEqual(requestedPaths, ['/gateway/models']);
});

test('an endpoint-form Responses relay discovers through its own API base', async () => {
const requestedPaths: string[] = [];
const server = await startJsonServer((request, response) => {
requestedPaths.push(request.url ?? '');
if (request.url === '/v1/models') {
respondJson(response, 200, { data: [{ id: 'relay-reasoner' }] });
return;
}
respondJson(response, 404, { error: { message: 'not found' } });
});
const connection: LlmConnection = {
slug: 'responses-relay',
name: 'Responses Relay',
providerType: 'openai-responses-compatible',
baseUrl: `${server.url}/v1/responses`,
defaultModel: 'relay-reasoner',
enabled: true,
createdAt: 1,
updatedAt: 1,
};

assert.deepEqual(
(await fetchProviderModels(connection, 'relay-key')).map(({ id }) => id),
['relay-reasoner'],
);
assert.deepEqual(requestedPaths, ['/v1/models']);
});

test('a Responses relay does not mask root authorization failures with a /v1 fallback', async () => {
const requestedPaths: string[] = [];
const server = await startJsonServer((request, response) => {
requestedPaths.push(request.url ?? '');
respondJson(response, 401, { error: { message: 'unauthorized' } });
});
const connection: LlmConnection = {
slug: 'responses-relay',
name: 'Responses Relay',
providerType: 'openai-responses-compatible',
baseUrl: server.url,
defaultModel: 'relay-reasoner',
enabled: true,
createdAt: 1,
updatedAt: 1,
};

await assert.rejects(fetchProviderModels(connection, 'bad-key'));
assert.deepEqual(requestedPaths, ['/models']);
});

test('a DeepSeek root base keeps serving Responses at the root it publishes', async () => {
let probedPath: string | undefined;
const server = await startJsonServer((request, response) => {
probedPath = request.url;
respondJson(response, 200, {});
});
const connection: LlmConnection = {
slug: 'deepseek',
name: 'DeepSeek',
providerType: 'deepseek',
baseUrl: server.url,
defaultModel: 'deepseek-v4-pro',
enabled: true,
createdAt: 1,
updatedAt: 1,
};

// The compatibility fallback is scoped to custom relay discovery; a
// built-in root probe must remain unversioned.
assert.equal((await testConnection(connection, 'deepseek-token')).ok, true);
assert.equal(probedPath, '/responses');
});

for (const [label, providerType] of [
['a plain OpenAI-compatible relay', 'openai-compatible'],
['local Ollama', 'ollama'],
Expand Down
34 changes: 34 additions & 0 deletions packages/runtime/src/__tests__/responses-wire-contract.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -301,6 +301,40 @@ describe('responses wire contract', () => {
assert.deepEqual(urls, ['https://relay.example/v1/responses']);
});

test('keeps a Responses relay host root on its configured API base', async () => {
assert.equal(
resolveModelRuntime(
{ providerType: 'openai-responses-compatible', baseUrl: 'http://relay.example:3000' },
'relay-model',
).baseUrl,
'http://relay.example:3000',
);

const urls: string[] = [];
const fetch = (async (url: string | URL | Request) => {
urls.push(String(url));
return Response.json({
id: 'r',
object: 'response',
status: 'completed',
output: [],
usage: { input_tokens: 1, output_tokens: 1 },
});
}) as typeof globalThis.fetch;
const model = getAIModel({
connection: { ...conn('openai-responses-compatible'), baseUrl: 'http://relay.example:3000' },
apiKey: '[redacted]',
modelId: 'relay-model',
fetch,
});

await model.doGenerate({
prompt: [{ role: 'user', content: [{ type: 'text', text: 'ping' }] }],
});

assert.deepEqual(urls, ['http://relay.example:3000/responses']);
});

test('resolves only supported Responses adapter and replay pairings', () => {
const deepseek = resolveModelRuntime({ providerType: 'deepseek' }, 'deepseek-v4-flash');
assert.deepEqual(deepseek.reasoningReplay, {
Expand Down
64 changes: 50 additions & 14 deletions packages/runtime/src/model-fetcher.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ import {
CONNECTION_MODEL_ID_MAX_LENGTH,
normalizeConnectionModelDiscoveryResult,
} from '@maka/core/runtime-policy';
import { anthropicV1Url, googleApiUrl } from './provider-urls.js';
import { anthropicV1Url, googleApiUrl, openAiResponsesBaseUrl } from './provider-urls.js';
import { openAiCodexHeaders } from './subscription-auth.js';
import {
GITHUB_COPILOT_API_VERSION,
Expand Down Expand Up @@ -182,7 +182,14 @@ async function fetchProviderModelsStrict(
apiKey: string,
fetchFn: ConnectionEffectFetch | undefined,
): Promise<ModelInfo[]> {
const baseUrl = effectiveBaseUrl(connection);
// Endpoint-form overrides are valid for Responses send and probe. Reduce
// them to the API base before deriving `/models`; bare roots stay
// authoritative and get only the route-not-found fallback below (#3320).
const configuredBaseUrl = effectiveBaseUrl(connection);
const baseUrl =
connection.providerType === 'openai-responses-compatible'
? openAiResponsesBaseUrl(configuredBaseUrl)
: configuredBaseUrl;
const definition = PROVIDER_REGISTRY[connection.providerType];
// Unknown providerType → no discovery path. Throw a clear error (caught and
// generalized by the caller) rather than crashing on `.modelDiscovery`.
Expand Down Expand Up @@ -245,21 +252,33 @@ async function fetchProviderModelsStrict(
}
case 'openai':
case 'openai-compatible': {
const r = await fetchForConnectionEffect(
const requestInit = {
headers: {
'content-type': 'application/json',
...(apiKey &&
(discovery.auth === 'oauth-bearer' ||
(discovery.auth !== 'none' && providerAuthSupportsApiKey(connection.providerType)))
? { authorization: `Bearer ${apiKey}` }
: {}),
},
timeoutMs: MODEL_FETCH_TIMEOUT_MS,
};
let r = await fetchForConnectionEffect(
fetchFn,
modelListUrl(baseUrl, discovery.path, discovery.query),
{
headers: {
'content-type': 'application/json',
...(apiKey &&
(discovery.auth === 'oauth-bearer' ||
(discovery.auth !== 'none' && providerAuthSupportsApiKey(connection.providerType)))
? { authorization: `Bearer ${apiKey}` }
: {}),
},
timeoutMs: MODEL_FETCH_TIMEOUT_MS,
},
requestInit,
);
if (!r.ok && r.status === 404 && connection.providerType === 'openai-responses-compatible') {
const fallbackBaseUrl = responsesRelayV1FallbackBaseUrl(baseUrl);
if (fallbackBaseUrl) {
await r.cancel();
r = await fetchForConnectionEffect(
fetchFn,
modelListUrl(fallbackBaseUrl, discovery.path, discovery.query),
requestInit,
);
}
}
if (!r.ok) {
await r.cancel();
if (connection.providerType === 'xai-oauth') {
Expand Down Expand Up @@ -669,6 +688,23 @@ function modelListUrl(
return search ? `${url}?${search}` : url;
}

/**
* A pathless Responses relay keeps its configured root as the primary
* contract. If that exact `/models` route is absent, discovery can safely
* retry the conventional versioned list once; send and probe are not replayed.
*/
function responsesRelayV1FallbackBaseUrl(baseUrl: string): string | undefined {
let url: URL;
try {
url = new URL(baseUrl);
} catch {
return undefined;
}
if (stripTrailing(url.pathname) !== '') return undefined;
url.pathname = '/v1';
return url.toString();
}

async function fetchFireworksModels(
baseUrl: string,
apiKey: string,
Expand Down