Changeset 0.28.0.9 (#181)
This commit is contained in:
@@ -54,15 +54,14 @@ describe('GET /admin/users response contract', () => {
|
||||
});
|
||||
|
||||
// ── /models/enabled response ─────────────────
|
||||
// Bug: 500 from ambiguous SQL columns in JOIN
|
||||
// After fix: returns {models: [...]}
|
||||
// Returns {data: [...], default_model: "..."}
|
||||
|
||||
describe('GET /models/enabled response contract', () => {
|
||||
// ACTUAL backend UserModel struct sends BOTH provider_config_id AND config_id.
|
||||
// Mock uses provider_config_id as primary (Go struct canonical name)
|
||||
// and config_id as the alias (populated by resolver for frontend compat).
|
||||
const backendResponse = {
|
||||
models: [
|
||||
data: [
|
||||
{
|
||||
id: 'cfg1:gpt-4o',
|
||||
model_id: 'gpt-4o',
|
||||
@@ -77,21 +76,22 @@ describe('GET /models/enabled response contract', () => {
|
||||
pricing: {},
|
||||
},
|
||||
],
|
||||
default_model: '',
|
||||
};
|
||||
|
||||
it('response has "models" array', () => {
|
||||
assert.ok(Array.isArray(backendResponse.models));
|
||||
it('response has "data" array', () => {
|
||||
assert.ok(Array.isArray(backendResponse.data));
|
||||
});
|
||||
|
||||
it('each model has required fields for selector', () => {
|
||||
for (const m of backendResponse.models) {
|
||||
for (const m of backendResponse.data) {
|
||||
assert.ok(m.model_id || m.id, 'must have model_id or id');
|
||||
assert.ok(m.display_name || m.model_id, 'must have display_name or model_id');
|
||||
}
|
||||
});
|
||||
|
||||
it('catalog models have provider_config_id (backend canonical)', () => {
|
||||
const catalogModels = backendResponse.models.filter(m => m.source === 'catalog');
|
||||
const catalogModels = backendResponse.data.filter(m => m.source === 'catalog');
|
||||
for (const m of catalogModels) {
|
||||
assert.ok(m.provider_config_id,
|
||||
'catalog model must have provider_config_id (Go struct canonical name)');
|
||||
@@ -99,7 +99,7 @@ describe('GET /models/enabled response contract', () => {
|
||||
});
|
||||
|
||||
it('catalog models have config_id alias matching provider_config_id', () => {
|
||||
const catalogModels = backendResponse.models.filter(m => m.source === 'catalog');
|
||||
const catalogModels = backendResponse.data.filter(m => m.source === 'catalog');
|
||||
for (const m of catalogModels) {
|
||||
assert.ok(m.config_id,
|
||||
'catalog model must have config_id (frontend alias)');
|
||||
@@ -109,7 +109,7 @@ describe('GET /models/enabled response contract', () => {
|
||||
});
|
||||
|
||||
it('capabilities is an object (not null)', () => {
|
||||
for (const m of backendResponse.models) {
|
||||
for (const m of backendResponse.data) {
|
||||
assert.equal(typeof m.capabilities, 'object');
|
||||
assert.notEqual(m.capabilities, null);
|
||||
}
|
||||
@@ -421,9 +421,9 @@ describe('Response shape consistency', () => {
|
||||
assert.ok('data' in shape, 'teams/members uses "data" key');
|
||||
});
|
||||
|
||||
it('models/enabled uses {models:[]}', () => {
|
||||
const shape = { models: [] };
|
||||
assert.ok('models' in shape);
|
||||
it('models/enabled uses {data:[]}', () => {
|
||||
const shape = { data: [] };
|
||||
assert.ok('data' in shape);
|
||||
});
|
||||
|
||||
it('admin/models uses {models:[]}', () => {
|
||||
|
||||
@@ -128,7 +128,7 @@ function loadAppModules(overrides = {}) {
|
||||
* v0.22.8: unified persona naming — no more preset aliases.
|
||||
*/
|
||||
function processModelsResponse(data, hiddenModels = new Set()) {
|
||||
return (data.models || []).map(m => {
|
||||
return (data.data || data.models || []).map(m => {
|
||||
const isPersona = !!m.is_persona;
|
||||
const baseModelId = m.model_id || m.id;
|
||||
const cfgId = m.config_id || m.provider_config_id || '';
|
||||
|
||||
@@ -81,7 +81,7 @@ async function fetchModels() {
|
||||
try {
|
||||
const prefData = await API.getModelPreferences();
|
||||
App.hiddenModels = new Set(
|
||||
(prefData.preferences || []).filter(p => p.hidden).map(p =>
|
||||
(prefData.data || prefData.preferences || []).filter(p => p.hidden).map(p =>
|
||||
(p.provider_config_id || '') + ':' + p.model_id
|
||||
)
|
||||
);
|
||||
@@ -89,7 +89,7 @@ async function fetchModels() {
|
||||
if (!App.hiddenModels) App.hiddenModels = new Set();
|
||||
}
|
||||
|
||||
App.models = (data.models || []).map(m => {
|
||||
App.models = (data.data || data.models || []).map(m => {
|
||||
const isPersona = !!m.is_persona;
|
||||
const baseModelId = m.model_id || m.id;
|
||||
const cfgId = m.config_id || m.provider_config_id || '';
|
||||
|
||||
@@ -941,7 +941,7 @@ Object.assign(UI, {
|
||||
document.getElementById('adminGroupBudgetMonthly').value = g.token_budget_monthly != null ? g.token_budget_monthly : '';
|
||||
|
||||
// ── Model allowlist checkboxes ──
|
||||
const models = (modelsResp.models || []).filter(m => !m.is_persona);
|
||||
const models = (modelsResp.data || modelsResp.models || []).filter(m => !m.is_persona);
|
||||
const allowedSet = g.allowed_models ? new Set(g.allowed_models) : null;
|
||||
const modelEl = document.getElementById('adminGroupModels');
|
||||
const seen = new Set();
|
||||
|
||||
@@ -782,7 +782,7 @@ Object.assign(UI, {
|
||||
try {
|
||||
const prefData = await API.getModelPreferences();
|
||||
App.hiddenModels = new Set(
|
||||
(prefData.preferences || []).filter(p => p.hidden).map(p =>
|
||||
(prefData.data || prefData.preferences || []).filter(p => p.hidden).map(p =>
|
||||
(p.provider_config_id || '') + ':' + p.model_id
|
||||
)
|
||||
);
|
||||
@@ -792,7 +792,7 @@ Object.assign(UI, {
|
||||
}
|
||||
|
||||
const data = await API.listEnabledModels();
|
||||
const models = (data.models || []).filter(m => !m.is_persona);
|
||||
const models = (data.data || data.models || []).filter(m => !m.is_persona);
|
||||
if (!models.length) {
|
||||
el.innerHTML = '<div class="empty-hint">No models available</div>';
|
||||
return;
|
||||
|
||||
Reference in New Issue
Block a user