mirror of
https://github.com/Darkatse/TauriTavern.git
synced 2026-10-19 07:35:14 +00:00
fix(panel-runtime): normalize custom source variants at gate activation (#353)
* fix(panel-runtime): normalize custom source variants at gate activation activateApiConnectionsSubtreeGates applied the openai source gate with the raw select value. Custom source variants (custom_openai_responses, custom_claude_messages, custom_gemini_generate_content, custom_gemini_interactions) do not appear in any data-source list, so every gated host under #openai_api was parked at startup while SillyTavern's own toggle still showed the matching blocks. The Custom Endpoint form rendered as an empty shell: URL input, model id and model list were missing until the user manually re-selected a source. Normalize the value through normalizeChatCompletionSourceForGate like the change-event handlers already do. * test(panel-runtime): cover custom source variants at gate activation Activating the API connections subtree gates with a saved custom source variant must keep the data-source="custom" hosts (Custom Endpoint form, model id, model list) connected while the plain OpenAI form stays parked. Fails on unpatched activation that applies the raw variant value. * refactor(panel-runtime): unify API connection gate updates Route activation and selection changes through one nested gate function. Keep the custom-source regression in the existing parking tests and remove the duplicate test file. --------- Co-authored-by: Darkatse <39111369+Darkatse@users.noreply.github.com>
This commit is contained in:
parent
5c0b48c34b
commit
0a84236364
2 changed files with 39 additions and 57 deletions
|
|
@ -169,19 +169,25 @@ function currentSelectValue(id) {
|
|||
}
|
||||
|
||||
/**
|
||||
* @param {string} source
|
||||
* Applies the nested gate (OpenAI source / Textgen type) of the given main API
|
||||
* from the current selection.
|
||||
*
|
||||
* @param {string} mainApi
|
||||
*/
|
||||
function normalizeChatCompletionSourceForGate(source) {
|
||||
const value = String(source || '').trim();
|
||||
if (value.startsWith('custom_')) {
|
||||
return 'custom';
|
||||
function applyNestedGate(mainApi) {
|
||||
if (mainApi === 'openai' && openaiSourceGate) {
|
||||
// Custom API format variants (`custom_*`) share the `custom` data-source hosts.
|
||||
const source = currentSelectValue('chat_completion_source');
|
||||
openaiSourceGate.apply(source.startsWith('custom_') ? 'custom' : source || 'openai');
|
||||
}
|
||||
if (mainApi === 'textgenerationwebui' && textgenTypeGate) {
|
||||
textgenTypeGate.apply(currentSelectValue('textgen_type'));
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Hydrates the selected provider subtree and parks all others, then applies any
|
||||
* nested gates (OpenAI source / Textgen type) for the active provider.
|
||||
* Hydrates the selected provider subtree and parks all others, then applies the
|
||||
* nested gate of the active provider.
|
||||
*
|
||||
* Must run synchronously inside the `change` event so downstream handlers can
|
||||
* safely query their expected DOM.
|
||||
|
|
@ -198,46 +204,7 @@ function handleMainApiChange(mainApi) {
|
|||
manager.setVisible(slotId, api === next);
|
||||
}
|
||||
manager.reconcile();
|
||||
|
||||
if (next === 'openai' && openaiSourceGate) {
|
||||
const selection = currentSelectValue('chat_completion_source') || 'openai';
|
||||
openaiSourceGate.apply(normalizeChatCompletionSourceForGate(selection) || 'openai');
|
||||
}
|
||||
if (next === 'textgenerationwebui' && textgenTypeGate) {
|
||||
textgenTypeGate.apply(currentSelectValue('textgen_type') || '');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} source
|
||||
*/
|
||||
function handleChatCompletionSourceChange(source) {
|
||||
if (!openaiSourceGate) {
|
||||
return;
|
||||
}
|
||||
|
||||
const mainApi = currentSelectValue('main_api');
|
||||
if (mainApi !== 'openai') {
|
||||
return;
|
||||
}
|
||||
|
||||
openaiSourceGate.apply(normalizeChatCompletionSourceForGate(source) || 'openai');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} type
|
||||
*/
|
||||
function handleTextgenTypeChange(type) {
|
||||
if (!textgenTypeGate) {
|
||||
return;
|
||||
}
|
||||
|
||||
const mainApi = currentSelectValue('main_api');
|
||||
if (mainApi !== 'textgenerationwebui') {
|
||||
return;
|
||||
}
|
||||
|
||||
textgenTypeGate.apply(type || '');
|
||||
applyNestedGate(next);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -265,7 +232,7 @@ export function preinstallApiConnectionsSubtreeGates() {
|
|||
throw new Error('PanelRuntime: #chat_completion_source is not a <select>');
|
||||
}
|
||||
$(chatCompletionSource).on('change.ttPanelRuntimeGate', () => {
|
||||
handleChatCompletionSourceChange(String(chatCompletionSource.value || ''));
|
||||
applyNestedGate(currentSelectValue('main_api'));
|
||||
});
|
||||
|
||||
const textgenType = mustGetElementById('textgen_type');
|
||||
|
|
@ -273,7 +240,7 @@ export function preinstallApiConnectionsSubtreeGates() {
|
|||
throw new Error('PanelRuntime: #textgen_type is not a <select>');
|
||||
}
|
||||
$(textgenType).on('change.ttPanelRuntimeGate', () => {
|
||||
handleTextgenTypeChange(String(textgenType.value || ''));
|
||||
applyNestedGate(currentSelectValue('main_api'));
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -315,11 +282,5 @@ export function activateApiConnectionsSubtreeGates({ manager: nextManager }) {
|
|||
});
|
||||
|
||||
manager.reconcile();
|
||||
|
||||
if (selectedMainApi === 'openai') {
|
||||
openaiSourceGate.apply(currentSelectValue('chat_completion_source') || 'openai');
|
||||
}
|
||||
if (selectedMainApi === 'textgenerationwebui') {
|
||||
textgenTypeGate.apply(currentSelectValue('textgen_type') || '');
|
||||
}
|
||||
applyNestedGate(selectedMainApi);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -63,3 +63,24 @@ test('aggressive parking keeps the OpenAI bounds but drops the third-party ancho
|
|||
dom.cleanup();
|
||||
}
|
||||
});
|
||||
|
||||
test('API connections gates keep the custom form for a saved custom source variant', async () => {
|
||||
const dom = installFakeDom();
|
||||
try {
|
||||
renderIndexBody(dom.document);
|
||||
dom.document.getElementById('main_api').value = 'openai';
|
||||
dom.document.getElementById('chat_completion_source').value = 'custom_gemini_generate_content';
|
||||
|
||||
const { createEmbeddedRuntimeManager } = await import('../src/tauri/main/services/embedded-runtime/embedded-runtime-manager.js');
|
||||
const { resolvePanelRuntimeProfile } = await import('../src/tauri/main/services/panel-runtime/panel-runtime-profiles.js');
|
||||
const { activateApiConnectionsSubtreeGates } = await import('../src/tauri/main/adapters/panel-runtime/api-connections-subtree-gates.js');
|
||||
activateApiConnectionsSubtreeGates({ manager: createEmbeddedRuntimeManager({ profile: resolvePanelRuntimeProfile('compat') }) });
|
||||
|
||||
assert.ok(dom.document.getElementById('custom_api_url_text'));
|
||||
assert.ok(dom.document.getElementById('custom_model_id'));
|
||||
assert.ok(dom.document.getElementById('model_custom_select'));
|
||||
assert.equal(dom.document.getElementById('openai_form').children.length, 0);
|
||||
} finally {
|
||||
dom.cleanup();
|
||||
}
|
||||
});
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue