fix: support max reasoning and custom model selection for 1.0.0
This commit is contained in:
1 parent
e1185a5104
commit
2ec563c079
12 files changed
+119
-16
No files matched your search
@@ -22,9 +22,9 @@ class ModelSettingsUpdate(BaseModel):
|
||||
timeout_seconds: float = Field(default=300, gt=0, le=3600)
|
||||
max_retries: int = Field(default=5, ge=0, le=10)
|
||||
retry_interval_seconds: float = Field(default=10, ge=0, le=300)
|
||||
reasoning_effort: Literal["default", "none", "minimal", "low", "medium", "high", "xhigh"] = (
|
||||
"high"
|
||||
)
|
||||
reasoning_effort: Literal[
|
||||
"default", "none", "minimal", "low", "medium", "high", "xhigh", "max"
|
||||
] = "high"
|
||||
|
||||
@field_validator("base_url")
|
||||
@classmethod
|
||||
|
||||
@@ -36,6 +36,47 @@ describe('持久化模型与功能设置', () => {
|
||||
expect(component.message()).toContain('保存');
|
||||
});
|
||||
|
||||
it('预设按钮填入名称,自定义模型与 max 可以保存并重新加载', async () => {
|
||||
const fixture = TestBed.createComponent(ModelSettingsComponent);
|
||||
fixture.detectChanges();
|
||||
http.expectOne('/api/system/model-settings').flush(config);
|
||||
fixture.detectChanges();
|
||||
await fixture.whenStable();
|
||||
const root: HTMLElement = fixture.nativeElement;
|
||||
const presets = root.querySelectorAll<HTMLButtonElement>('.model-presets button');
|
||||
expect(presets.length).toBe(2);
|
||||
presets[0].click();
|
||||
fixture.detectChanges();
|
||||
await fixture.whenStable();
|
||||
const input = root.querySelector<HTMLInputElement>('#model-name')!;
|
||||
expect(input.value).toBe('gpt-5.6-luna');
|
||||
expect(presets[0].getAttribute('aria-pressed')).toBe('true');
|
||||
expect(input.getAttribute('list')).toBeNull();
|
||||
expect(input.autocomplete).toBe('off');
|
||||
input.value = 'custom/model-v2';
|
||||
input.dispatchEvent(new Event('input'));
|
||||
const reasoning = root.querySelector<HTMLSelectElement>('#model-reasoning')!;
|
||||
reasoning.value = 'max';
|
||||
reasoning.dispatchEvent(new Event('change'));
|
||||
fixture.detectChanges();
|
||||
await fixture.whenStable();
|
||||
expect(presets[0].getAttribute('aria-pressed')).toBe('false');
|
||||
root.querySelector<HTMLFormElement>('form')!.dispatchEvent(new Event('submit', { cancelable: true }));
|
||||
const request = http.expectOne('/api/system/model-settings');
|
||||
expect(request.request.body.model).toBe('custom/model-v2');
|
||||
expect(request.request.body.reasoning_effort).toBe('max');
|
||||
const saved = { ...config, model: 'custom/model-v2', reasoning_effort: 'max' };
|
||||
request.flush(saved);
|
||||
fixture.destroy();
|
||||
const reopened = TestBed.createComponent(ModelSettingsComponent);
|
||||
reopened.detectChanges();
|
||||
http.expectOne('/api/system/model-settings').flush(saved);
|
||||
reopened.detectChanges();
|
||||
await reopened.whenStable();
|
||||
expect(reopened.nativeElement.querySelector('#model-name').value).toBe('custom/model-v2');
|
||||
expect(reopened.nativeElement.querySelector('#model-reasoning').value).toBe('max');
|
||||
});
|
||||
|
||||
it('连接测试使用当前输入而不写入保存接口', () => {
|
||||
const fixture = TestBed.createComponent(ModelSettingsComponent);
|
||||
fixture.detectChanges();
|
||||
|
||||
@@ -27,14 +27,22 @@ interface ModelSettings {
|
||||
placeholder="https://api.openai.com/v1" [disabled]="busy() || loading()" />
|
||||
<small>填写接口根地址,通常包含 /v1,无需添加 /responses。</small>
|
||||
<label for="model-name">模型名称</label>
|
||||
<input id="model-name" name="model" list="model-presets" [(ngModel)]="model" required placeholder="输入接口支持的模型名"
|
||||
<div class="model-presets" role="group" aria-label="预设模型">
|
||||
@for (preset of modelPresets; track preset) {
|
||||
<button type="button" [attr.aria-pressed]="model === preset" (click)="model = preset"
|
||||
[disabled]="busy() || loading()">{{ preset }}</button>
|
||||
}
|
||||
</div>
|
||||
<input id="model-name" name="llmModelName" type="text" autocomplete="off" autocapitalize="off" spellcheck="false"
|
||||
[(ngModel)]="model" required placeholder="输入接口支持的模型名" aria-describedby="model-name-help"
|
||||
[disabled]="busy() || loading()" />
|
||||
<datalist id="model-presets"><option value="gpt-5.6-luna"></option><option value="mimo-v2.6-flash"></option></datalist>
|
||||
<small id="model-name-help">点击预设填入,或直接输入自定义模型名称;保存后可继续使用。</small>
|
||||
<label for="model-reasoning">思考强度</label>
|
||||
<select id="model-reasoning" name="reasoning" [(ngModel)]="reasoning" [disabled]="busy() || loading()">
|
||||
<option value="default">由模型默认</option><option value="none">关闭(none)</option>
|
||||
<option value="minimal">最低(minimal)</option><option value="low">低(low)</option>
|
||||
<option value="medium">中(medium)</option><option value="high">高(high)</option><option value="xhigh">最高(xhigh)</option>
|
||||
<option value="medium">中(medium)</option><option value="high">高(high)</option>
|
||||
<option value="xhigh">极高(xhigh)</option><option value="max">最高(max)</option>
|
||||
</select>
|
||||
<small>接口需支持所选强度;不支持时会明确报错,可手动选择“由模型默认”。</small>
|
||||
<label for="model-key">API Key <span>{{ keyConfigured() ? '(已配置,留空保留)' : '(尚未配置)' }}</span></label>
|
||||
@@ -71,12 +79,18 @@ interface ModelSettings {
|
||||
.actions { display: flex; gap: 12px; margin-top: 8px; }
|
||||
button { padding: 11px 20px; border-radius: 10px; border: 1px solid #38bdf877; background: #0284c7; color: white; cursor: pointer; font: inherit; }
|
||||
button:disabled { opacity: .5; cursor: default; } .success { color: #047857; } .error { color: #be123c; }
|
||||
.model-presets { display: flex; flex-wrap: wrap; gap: 8px; }
|
||||
.model-presets button { padding: 8px 12px; background: #f8fafc; color: #475569; border-color: #cbd5e1; font-size: 14px; }
|
||||
.model-presets button:hover:not(:disabled) { background: #e0f2fe; border-color: #38bdf8; }
|
||||
.model-presets button[aria-pressed="true"] { background: #e0f2fe; border-color: #0284c7; color: #075985; }
|
||||
button:focus-visible { outline: 2px solid #0284c7; outline-offset: 2px; }
|
||||
small { overflow-wrap: anywhere; } @media(max-width: 480px) { .number-fields { grid-template-columns: 1fr; } }
|
||||
`]
|
||||
})
|
||||
export class ModelSettingsComponent implements OnInit {
|
||||
private http = inject(HttpClient);
|
||||
saved = output<void>();
|
||||
readonly modelPresets = ['gpt-5.6-luna', 'mimo-v2.6-flash'];
|
||||
baseUrl = ''; model = ''; apiKey = ''; timeout = 300; retries = 5; retryInterval = 10; reasoning = 'high';
|
||||
busy = signal<'save' | 'test' | null>(null);
|
||||
loading = signal(true); keyConfigured = signal(false); envPath = signal('');
|
||||
|
||||
Reference in new issue
Block a user