style(usage): show reasoning effort under the model name
Drop the separate reasoning-effort column from request details and render the raw effort, or auto, directly beneath the model name. Include it in the model tooltip, reset saved layouts that still use the old default column, and keep the field in CSV export.
This commit is contained in:
1 parent
56a88ccb6f
commit
6e34850780
22 files changed
+1130
-384
No files matched your search
@@ -0,0 +1,36 @@
|
||||
import { describe, expect, it } from 'bun:test';
|
||||
import { renderToStaticMarkup } from 'react-dom/server';
|
||||
import { CredentialAdvancedFields, CredentialHeadersEditor } from '../src/components/CredentialAdvancedFields';
|
||||
import { I18nProvider } from '../src/i18n';
|
||||
|
||||
const renderAdvanced = (name: string, advanced: Record<string, unknown>, provider = '') => renderToStaticMarkup(
|
||||
<I18nProvider>
|
||||
<CredentialAdvancedFields name={name} provider={provider} advanced={advanced} onChange={() => {}} />
|
||||
</I18nProvider>,
|
||||
);
|
||||
|
||||
describe('credential settings interaction', () => {
|
||||
it('lets a Codex credential edit aliases directly and hides Claude cloaking', () => {
|
||||
const html = renderAdvanced('codex-cbb7bd85-user-team.json', {});
|
||||
expect(html).toContain('Add alias');
|
||||
expect(html).toContain('Credential timezone');
|
||||
expect(html).toContain('Asia/Shanghai');
|
||||
expect(html).not.toContain('Request cloaking');
|
||||
expect(html).not.toContain('Inherit / not set');
|
||||
expect(html).not.toContain('Model aliases and Claude compatibility');
|
||||
});
|
||||
|
||||
it('shows Claude controls as normal fields, including an existing override on another provider', () => {
|
||||
expect(renderAdvanced('claude-user.json', {})).toContain('Request cloaking');
|
||||
const overridden = renderAdvanced('codex-user.json', { cloak_mode: 'always' });
|
||||
expect(overridden).toContain('Request cloaking');
|
||||
expect(overridden).toContain('value="always" selected');
|
||||
});
|
||||
|
||||
it('edits headers as rows instead of an empty JSON object', () => {
|
||||
const html = renderToStaticMarkup(<I18nProvider><CredentialHeadersEditor value="{}" onChange={() => {}} /></I18nProvider>);
|
||||
expect(html).toContain('No custom headers.');
|
||||
expect(html).toContain('Add header');
|
||||
expect(html).not.toContain('<textarea');
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,20 @@
|
||||
import { describe, expect, it } from 'bun:test';
|
||||
import { renderToStaticMarkup } from 'react-dom/server';
|
||||
import { CredentialAdvancedFields } from '../src/components/CredentialAdvancedFields';
|
||||
import { I18nProvider } from '../src/i18n';
|
||||
|
||||
const html = renderToStaticMarkup(
|
||||
<I18nProvider>
|
||||
<CredentialAdvancedFields name="codex-user.json" advanced={{ model_aliases: [{ name: 'gpt-5', alias: '' }] }}
|
||||
models={[{ name: 'gpt-5', displayName: 'GPT 5' }]} onChange={() => {}} />
|
||||
</I18nProvider>,
|
||||
);
|
||||
|
||||
describe('credential alias model picker', () => {
|
||||
it('uses the searchable model picker for both model fields', () => {
|
||||
expect(html.match(/role="combobox"/g)).toHaveLength(2);
|
||||
expect(html).toContain('value="gpt-5"');
|
||||
expect(html).toContain('Search or enter a model');
|
||||
expect(html).not.toContain('<textarea');
|
||||
});
|
||||
});
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
<!doctype html>
|
||||
<html lang="zh-CN">
|
||||
<head><meta charset="UTF-8" /><meta name="viewport" content="width=device-width, initial-scale=1.0" /><title>Credential settings fixture</title></head>
|
||||
<body><div id="root"></div><script type="module" src="./credential-settings.tsx"></script></body>
|
||||
</html>
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
import React from 'react';
|
||||
import { createRoot } from 'react-dom/client';
|
||||
import { mockIPC } from '@tauri-apps/api/mocks';
|
||||
import { I18nProvider } from '../../src/i18n';
|
||||
import { AuthFileSettingsDialog } from '../../src/components/AuthFileSettingsDialog';
|
||||
import '../../src/styles/index.css';
|
||||
|
||||
localStorage.setItem('easy-cli-proxy-api.locale', 'zh-CN');
|
||||
const models = Array.from({ length: 20 }, (_, index) => ({ id: `gpt-${index + 1}`, display_name: `GPT ${index + 1}` }));
|
||||
mockIPC(async (cmd, rawArgs) => {
|
||||
if (cmd === 'set_app_locale') return null;
|
||||
if (cmd !== 'management_request') return null;
|
||||
const request = (rawArgs as { request?: { path?: string } }).request;
|
||||
if (request?.path === '/credentials/download') {
|
||||
return { prefix: '', proxy_url: 'socks5://127.0.0.1:1080', priority: 0, weight: 1, excluded_models: ['gpt-example', 'claude-*'], headers: {}, note: '' };
|
||||
}
|
||||
if (request?.path === '/credentials/models') return { models };
|
||||
return { status: 'ok' };
|
||||
});
|
||||
createRoot(document.getElementById('root')!).render(
|
||||
<I18nProvider>
|
||||
<AuthFileSettingsDialog name="codex-cbb7bd85-lzt404rum@gmail.com-team.json" provider="codex" onClose={() => {}} onSaved={() => {}} />
|
||||
</I18nProvider>,
|
||||
);
|
||||
@@ -35,13 +35,13 @@ function componentFiles(directory: string): string[] {
|
||||
}
|
||||
|
||||
describe('UI localization boundaries', () => {
|
||||
it('preserves the raw reasoning effort in usage text and tooltips', () => {
|
||||
it('preserves the raw reasoning effort under the usage model name', () => {
|
||||
const file = join(sourceRoot, 'pages', 'UsageEventsView.tsx');
|
||||
const source = ts.createSourceFile(file, readFileSync(file, 'utf8'), ts.ScriptTarget.Latest, true, ts.ScriptKind.TSX);
|
||||
let effortCase: ts.CaseClause | undefined;
|
||||
let modelCase: ts.CaseClause | undefined;
|
||||
const visit = (node: ts.Node) => {
|
||||
if (ts.isCaseClause(node) && ts.isStringLiteral(node.expression) && node.expression.text === 'effort') {
|
||||
effortCase = node;
|
||||
if (ts.isCaseClause(node) && ts.isStringLiteral(node.expression) && node.expression.text === 'model') {
|
||||
modelCase = node;
|
||||
} else {
|
||||
ts.forEachChild(node, visit);
|
||||
}
|
||||
@@ -60,10 +60,12 @@ describe('UI localization boundaries', () => {
|
||||
}
|
||||
ts.forEachChild(node, inspectEffort);
|
||||
};
|
||||
expect(effortCase).toBeDefined();
|
||||
if (effortCase) inspectEffort(effortCase);
|
||||
expect(displayedExpressions).toContain("record.reasoning_effort || 'auto'");
|
||||
expect(titleExpressions).toContain("{record.reasoning_effort || 'auto'}");
|
||||
expect(modelCase).toBeDefined();
|
||||
if (modelCase) inspectEffort(modelCase);
|
||||
expect(displayedExpressions).toContain('effort');
|
||||
expect(titleExpressions).toContain('{effort}');
|
||||
expect(source.getText()).toContain("const effort = record.reasoning_effort || 'auto'");
|
||||
expect(source.getText()).not.toContain("case 'effort'");
|
||||
});
|
||||
|
||||
it('routes visible prose and accessibility labels through translations', () => {
|
||||
|
||||
Reference in new issue
Block a user