Files
EveFlow/tests/hermesModels.test.ts
T
Claude d5f19a9b3c feat: choix du modèle Hermes parmi les fournisseurs configurés et niveau d'effort complet (v2.5.1)
- Menu déroulant des modèles alimenté par GET /api/model/options (tous les
  fournisseurs authentifiés et leurs modèles, modèle courant du serveur marqué),
  bouton d'actualisation (?refresh=1), saisie libre en repli quand l'endpoint
  n'existe pas. /v1/models ne renvoie que l'alias « hermes-agent ».
- Le fournisseur choisi et l'effort de raisonnement (none, minimal, low, medium,
  high, xhigh, max, ultra) sont envoyés à chaque requête sur les trois transports
  (runs, sessions, chat completions) ; l'effort n'était transmis qu'en chat
  completions.
- Modèle « mission » sélectionné de la même façon (modèle + fournisseur).
- Tests des helpers du sélecteur ; README et feuille de route.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MMpgFriwxiBgurUVb21oCE
2026-09-04 15:47:35 +00:00

46 lines
2.4 KiB
TypeScript

import { describe, expect, it } from 'vitest';
import { flattenModelOptions, groupModelChoices, isReasoningEffort, modelChoiceValue, parseModelChoice } from '../src/lib/hermesModels';
const payload = {
model: 'claude-fable-5-1',
provider: 'anthropic',
providers: [
{ slug: 'anthropic', name: 'Anthropic', is_current: true, models: ['claude-opus-5', 'claude-fable-5-1', 'claude-opus-5'], authenticated: true },
{ slug: 'openrouter', name: 'OpenRouter', models: ['deepseek/deepseek-v4', ' '], authenticated: true },
{ slug: 'nous', name: 'Nous Portal', models: [], authenticated: false, warning: 'paste NOUS_API_KEY to activate' },
{ slug: 'custom', name: 'Custom endpoint', models: ['qwen3'], authenticated: true }
]
};
describe('flattenModelOptions', () => {
it('lists authenticated providers, current model first in its row, without duplicates or blanks', () => {
const choices = flattenModelOptions(payload);
expect(choices.map((c) => `${c.provider}/${c.model}`)).toEqual(['anthropic/claude-fable-5-1', 'anthropic/claude-opus-5', 'openrouter/deepseek/deepseek-v4', 'custom/qwen3']);
expect(choices[0].current).toBe(true);
expect(choices[1].current).toBe(false);
});
it('tolerates an empty or malformed payload', () => {
expect(flattenModelOptions(null)).toEqual([]);
expect(flattenModelOptions({ providers: [{ slug: 'x', name: 'x', models: 'nope' } as unknown as { slug: string; name: string; models: string[] }] })).toEqual([]);
});
it('groups by provider in row order', () => {
const groups = groupModelChoices(flattenModelOptions(payload));
expect(groups.map((g) => g.providerName)).toEqual(['Anthropic', 'OpenRouter', 'Custom endpoint']);
expect(groups[0].models).toHaveLength(2);
});
});
describe('model choice value', () => {
it('round-trips provider and model, including model ids containing slashes and colons', () => {
const v = modelChoiceValue('openrouter', 'deepseek/deepseek-v4:free');
expect(parseModelChoice(v)).toEqual({ provider: 'openrouter', model: 'deepseek/deepseek-v4:free' });
expect(parseModelChoice('hermes-agent')).toEqual({ provider: '', model: 'hermes-agent' });
expect(parseModelChoice('::')).toEqual({ provider: '', model: '' });
});
it('validates reasoning effort levels', () => {
expect(isReasoningEffort('xhigh')).toBe(true);
expect(isReasoningEffort('')).toBe(true);
expect(isReasoningEffort('turbo')).toBe(false);
});
});