* chore(release): stop publishing legacy Windows update packages * feat(agents): support OpenCode Desktop across platforms Detect CLI and desktop installations on Windows, macOS, and Linux, and add separate launch and restart controls. Closes #156 * fix(codex): align model display names and show total session count - preserve runtime model identity in generated catalogs - count distinct sessions across database and rollout sources - update session count labels in all supported languages * fix(codex): keep config and auth files in sync - generate auth.json for API key configuration - preserve and validate OAuth credentials - include auth.json in transactional updates and rollback - retain compatibility with legacy Codex configuration states * fix(cpa-gui): update version to 0.2.59 in Cargo files * fix(codex): preserve OAuth mode after closing configuration * feat(config): add TLS management and protocol-aware local URLs - add TLS controls to advanced settings - persist certificate and private key paths - restart the core after TLS changes - switch WebUI, API and management URLs between HTTP and HTTPS - synchronize agent configurations and detection with the active protocol * refactor(config): reorganize network, routing, and TLS settings - split network settings and session routing into separate tabs - move TLS configuration into network settings - preserve TLS restart and HTTPS URL synchronization behavior * feat(config): add independent network settings and custom listen IP * fix(navigation): allow access to usage records while core is stopped * refactor(home): show a single API URL based on the configured listen IP * feat(config): add file pickers for TLS certificate and private key * fix(usage): distinguish client cancellations from request failures - persist usage failure status and response details - classify HTTP 499 and canceled contexts separately - exclude canceled requests from failure statistics - add canceled filtering and failure details to the usage UI - migrate and backfill existing usage records * feat(ui): revamp usage records and improve app interactions - redesign usage records dashboard, filters, charts, and pricing views - add resizable centered columns with persisted widths - expose OpenCode desktop launch and restart actions - suppress the API access enabled success notice * feat(agents): persist applied configurations across app restarts - preserve applied agent configs and managed Claude aliases on exit - keep explicit close actions restoring original configurations - add Codex restart persistence regression coverage - bump desktop version to 0.2.61 * fix(model-aliases): support provider-aware OAuth aliases - map OAuth aliases to their correct provider channels - support Antigravity aliases with force mapping - read and delete aliases by their exact OAuth channel - refresh the runtime model registry after alias changes - restrict Fast aliases to GPT models from OpenAI-compatible or Codex sources * docs(readme): add APIMart sponsorship sections - add English and Chinese sponsor descriptions - add localized APIMart banner images
116 lines
4.2 KiB
TypeScript
116 lines
4.2 KiB
TypeScript
import { describe, expect, test } from 'bun:test';
|
|
import {
|
|
combineModelAliasEntries,
|
|
combineModelAliasSources,
|
|
defaultModelAlias,
|
|
thinkingAliasSourceKindLabel,
|
|
uniqueModelAlias,
|
|
} from '../src/pages/ThinkingAliasesPage';
|
|
|
|
describe('模型别名默认名称', () => {
|
|
test('按思考强度和 Fast 选项生成可编辑的默认名称', () => {
|
|
expect(defaultModelAlias('gpt-5.6-sol', 'XHigh', false)).toBe('gpt-5.6-sol-xhigh');
|
|
expect(defaultModelAlias('gpt-5.6-sol', '', true)).toBe('gpt-5.6-sol-fast');
|
|
expect(defaultModelAlias('gpt-5.6-sol', 'xhigh', true)).toBe('gpt-5.6-sol-xhigh-fast');
|
|
expect(defaultModelAlias('gpt-5.6-sol', '', false)).toBe('gpt-5.6-sol-alias');
|
|
});
|
|
|
|
test('默认别名与现有模型重名时递增数字后缀', () => {
|
|
expect(uniqueModelAlias('gpt-5.6-sol-fast', ['gpt-5.6-sol-fast'])).toBe('gpt-5.6-sol-fast-2');
|
|
expect(uniqueModelAlias('gpt-5.6-sol-fast', [
|
|
'gpt-5.6-sol-fast',
|
|
'gpt-5.6-sol-fast-2',
|
|
])).toBe('gpt-5.6-sol-fast-3');
|
|
expect(uniqueModelAlias('gpt-5.6-sol-fast', ['GPT-5.6-SOL-FAST'])).toBe('gpt-5.6-sol-fast-2');
|
|
expect(uniqueModelAlias('gpt-5.6-sol-fast', ['gpt-5.6-sol'])).toBe('gpt-5.6-sol-fast');
|
|
});
|
|
});
|
|
|
|
describe('思考别名', () => {
|
|
test('区分同名模型的接入来源', () => {
|
|
expect(thinkingAliasSourceKindLabel('codex-oauth')).toBe('Codex OAuth');
|
|
expect(thinkingAliasSourceKindLabel('codex-api')).toBe('Codex API');
|
|
expect(thinkingAliasSourceKindLabel('openai-compatible')).toBe('OpenAI 兼容');
|
|
expect(thinkingAliasSourceKindLabel('custom')).toBe('其他来源');
|
|
});
|
|
});
|
|
|
|
describe('统一模型别名列表', () => {
|
|
test('同时显示思考别名和 Fast 速度别名', () => {
|
|
const entries = combineModelAliasEntries(
|
|
[{
|
|
sourceModel: 'gpt-thinking',
|
|
alias: 'gpt-high',
|
|
effort: 'high',
|
|
provider: 'Provider B',
|
|
kind: 'openai-compatible',
|
|
}],
|
|
[{
|
|
sourceModel: 'gpt-fast-source',
|
|
alias: 'gpt-fast',
|
|
serviceTier: 'priority',
|
|
provider: 'Provider A',
|
|
kind: 'codex-api',
|
|
}],
|
|
);
|
|
|
|
expect(entries.map((entry) => [entry.alias, entry.effort, entry.serviceTier])).toEqual([
|
|
['gpt-fast', null, 'priority'],
|
|
['gpt-high', 'high', null],
|
|
]);
|
|
});
|
|
|
|
test('同一个别名可同时设置思考强度和 Fast', () => {
|
|
const identity = {
|
|
sourceModel: 'gpt-5.6-sol',
|
|
alias: 'gpt-5.6-sol-xhigh-fast',
|
|
provider: 'Codex OAuth',
|
|
kind: 'codex-oauth',
|
|
};
|
|
const entries = combineModelAliasEntries(
|
|
[{ ...identity, effort: 'xhigh' }],
|
|
[{ ...identity, serviceTier: 'priority' }],
|
|
);
|
|
|
|
expect(entries).toHaveLength(1);
|
|
expect(entries[0]).toMatchObject({ effort: 'xhigh', serviceTier: 'priority' });
|
|
});
|
|
|
|
test('速度模型与思考模型合并后保留思考能力标记', () => {
|
|
const baseSource = {
|
|
model: 'gpt-5.6-sol',
|
|
displayName: 'GPT-5.6 Sol',
|
|
provider: 'Codex OAuth',
|
|
kind: 'codex-oauth',
|
|
protocol: 'codex',
|
|
};
|
|
const sources = combineModelAliasSources(
|
|
[
|
|
{ ...baseSource, id: 'reasoning-and-fast' },
|
|
{ ...baseSource, id: 'fast-only', model: 'gpt-speed-only' },
|
|
{
|
|
...baseSource,
|
|
id: 'plain-only',
|
|
model: 'gemini-pro-agent',
|
|
provider: 'Antigravity OAuth',
|
|
kind: 'antigravity-oauth',
|
|
protocol: 'antigravity',
|
|
},
|
|
],
|
|
[{ ...baseSource, id: 'reasoning-and-fast' }],
|
|
[
|
|
{ ...baseSource, id: 'reasoning-and-fast' },
|
|
{ ...baseSource, id: 'fast-only', model: 'gpt-speed-only' },
|
|
],
|
|
);
|
|
|
|
expect(sources).toHaveLength(3);
|
|
expect(sources.find((source) => source.id === 'reasoning-and-fast')?.supportsReasoning).toBe(true);
|
|
expect(sources.find((source) => source.id === 'reasoning-and-fast')?.supportsFast).toBe(true);
|
|
expect(sources.find((source) => source.id === 'fast-only')?.supportsReasoning).toBe(false);
|
|
expect(sources.find((source) => source.id === 'fast-only')?.supportsFast).toBe(true);
|
|
expect(sources.find((source) => source.id === 'plain-only')?.supportsReasoning).toBe(false);
|
|
expect(sources.find((source) => source.id === 'plain-only')?.supportsFast).toBe(false);
|
|
});
|
|
});
|