mirror of
https://github.com/rcourtman/Pulse.git
synced 2026-09-11 22:12:23 +00:00
Keep Assistant route labels provider-explicit
This commit is contained in:
@@ -1471,11 +1471,14 @@ timers when the stream already has a newer workflow/tool state.
|
||||
route. Assistant transcript rows must carry and render the effective model
|
||||
route that produced each live or restored response so route recovery and
|
||||
mixed-provider sessions remain auditable. Direct provider models must not
|
||||
gain a gateway label. Gateway route strings must still render as readable
|
||||
provider/model labels before catalog hydration; this mirrors OpenCode's
|
||||
source pattern of retaining structured `providerID`/`modelID` metadata and
|
||||
presenting catalog names with ID fallbacks rather than leaking raw route
|
||||
storage IDs into primary chat chrome. Assistant model selector actions must
|
||||
gain a gateway label, but explicit direct route strings such as
|
||||
`deepseek:deepseek-v4-pro` must still render with provider identity rather
|
||||
than collapsing to a bare-looking model payload. Gateway route strings must
|
||||
still render as readable provider/model labels before catalog hydration; this
|
||||
mirrors OpenCode's source pattern of retaining structured
|
||||
`providerID`/`modelID` metadata and presenting catalog names with ID
|
||||
fallbacks rather than leaking raw route storage IDs into primary chat chrome.
|
||||
Assistant model selector actions must
|
||||
remain route-distinct: if the configured chat override resolves to the same
|
||||
route as the effective default or the already selected session model, the
|
||||
drawer must not render a duplicate override action.
|
||||
|
||||
@@ -4921,6 +4921,29 @@ describe('AIChat', () => {
|
||||
expect(mockChat.setModel).toHaveBeenCalledWith('openrouter:qwen/qwen3.7-plus');
|
||||
});
|
||||
|
||||
it('labels direct provider recent routes with provider identity', () => {
|
||||
localStorage.setItem(
|
||||
'pulse:ai_chat_recent_models',
|
||||
JSON.stringify(['deepseek:deepseek-v4-pro']),
|
||||
);
|
||||
mockChat.model.mockReturnValue('openrouter:qwen/qwen3.7-plus');
|
||||
|
||||
renderChat();
|
||||
|
||||
fireEvent.click(
|
||||
screen.getByRole('button', {
|
||||
name: /Cycle recent Assistant model: DeepSeek: DeepSeek V4 Pro/,
|
||||
}),
|
||||
);
|
||||
|
||||
expect(
|
||||
screen.queryByRole('button', {
|
||||
name: 'Cycle recent Assistant model: deepseek-v4-pro',
|
||||
}),
|
||||
).not.toBeInTheDocument();
|
||||
expect(mockChat.setModel).toHaveBeenCalledWith('deepseek:deepseek-v4-pro');
|
||||
});
|
||||
|
||||
it('disables recent model cycling when the active route is the only recent route', () => {
|
||||
localStorage.setItem(
|
||||
'pulse:ai_chat_recent_models',
|
||||
|
||||
@@ -42,6 +42,10 @@ describe('aiProviderPresentation', () => {
|
||||
provider: 'deepseek',
|
||||
}),
|
||||
).toBe('DeepSeek: DeepSeek V4 Pro');
|
||||
|
||||
expect(formatAIModelRouteLabel('deepseek:deepseek-v4-pro')).toBe(
|
||||
'DeepSeek: DeepSeek V4 Pro',
|
||||
);
|
||||
});
|
||||
|
||||
it('does not duplicate an existing OpenRouter route label', () => {
|
||||
|
||||
@@ -191,12 +191,26 @@ const gatewayFallbackLabel = (model: AIModelRouteLabelInput): string => {
|
||||
return `${upstreamProvider}: ${modelLabel}`;
|
||||
};
|
||||
|
||||
const directProviderRouteFallbackLabel = (model: AIModelRouteLabelInput): string => {
|
||||
if (explicitModelNameForLabel(model)) return '';
|
||||
const id = modelIdForLabel(model);
|
||||
if (isPulseOwnedLocalModelRoute(id)) return '';
|
||||
const separator = id.indexOf(':');
|
||||
if (separator <= 0 || separator === id.length - 1) return '';
|
||||
const provider = id.slice(0, separator);
|
||||
if (GATEWAY_MODEL_PROVIDERS.has(provider)) return '';
|
||||
const providerName = getAIProviderDisplayName(provider);
|
||||
const modelLabel = titleizeModelRouteId(id.slice(separator + 1));
|
||||
if (!providerName || !modelLabel) return '';
|
||||
return `${providerName}: ${modelLabel}`;
|
||||
};
|
||||
|
||||
export const formatAIModelRouteLabel = (model: AIModelRouteLabelInput): string => {
|
||||
const provider = transportProviderForLabel(model);
|
||||
const label =
|
||||
provider && GATEWAY_MODEL_PROVIDERS.has(provider)
|
||||
? gatewayFallbackLabel(model) || baseModelLabel(model)
|
||||
: baseModelLabel(model);
|
||||
: directProviderRouteFallbackLabel(model) || baseModelLabel(model);
|
||||
if (!provider || !GATEWAY_MODEL_PROVIDERS.has(provider)) {
|
||||
return label;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user