feat: unify model selection and complete favorite settings
This commit is contained in:
1 parent
a4a5430922
commit
50638d4130
24 files changed
+347
-90
No files matched your search
@@ -4,6 +4,8 @@ import { HttpClient } from '@angular/common/http';
|
||||
import { finalize, timeout } from 'rxjs';
|
||||
import { UI_READ_TIMEOUT_MS } from '../../core/http-timeouts';
|
||||
import { SettingSliderComponent, REASONING_VALUES, REASONING_LABELS } from '../setting-slider/setting-slider.component';
|
||||
import { ModelSelectorComponent } from '../model-selector/model-selector.component';
|
||||
import { MODEL_PRESETS } from '../../core/model-presets';
|
||||
|
||||
interface ModelSettings {
|
||||
base_url: string;
|
||||
@@ -26,7 +28,7 @@ interface ModelSettings {
|
||||
@Component({
|
||||
selector: 'app-model-settings',
|
||||
standalone: true,
|
||||
imports: [FormsModule, SettingSliderComponent],
|
||||
imports: [FormsModule, SettingSliderComponent, ModelSelectorComponent],
|
||||
template: `
|
||||
<form class="model-settings" (ngSubmit)="save()">
|
||||
<p>统一使用 OpenAI Responses。保存后新任务立即生效,重启后自动加载。</p>
|
||||
@@ -34,17 +36,8 @@ interface ModelSettings {
|
||||
<input id="model-base-url" name="baseUrl" type="url" [(ngModel)]="baseUrl" required
|
||||
placeholder="https://api-n-cd.playones.com/v1" [disabled]="busy() || loading()" />
|
||||
<small>填写接口根地址,通常包含 /v1,无需添加 /responses。</small>
|
||||
<label for="model-name">模型名称</label>
|
||||
<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()" />
|
||||
<small id="model-name-help">点击预设填入,或直接输入自定义模型名称;保存后可继续使用。</small>
|
||||
<app-model-selector label="模型名称" [(value)]="model" [presets]="modelPresets" [disabled]="!!busy() || loading()" />
|
||||
<small>选择预设模型,或选择“自定义模型”填写名称。</small>
|
||||
<app-setting-slider label="思考强度" [(value)]="reasoning" [values]="reasoningValues" [labels]="reasoningLabels" [disabled]="!!busy() || loading()" hint="更高的强度适合复杂推理;需接口支持,也可选择模型默认。" />
|
||||
<label for="model-key">API Key <span>{{ keyConfigured() ? '(已配置,留空保留)' : '(尚未配置)' }}</span></label>
|
||||
<input id="model-key" name="apiKey" type="password" [(ngModel)]="apiKey" autocomplete="new-password"
|
||||
@@ -61,9 +54,7 @@ interface ModelSettings {
|
||||
<div class="recovery-content">
|
||||
<label>429 等待重试(秒)<input name="rateLimitRetry" type="number" min="0" max="3600" required
|
||||
[(ngModel)]="rateLimitRetry" [disabled]="busy() || loading()" /></label>
|
||||
<label>兜底模型名称<input name="toolFallbackModel" type="text" required autocomplete="off"
|
||||
autocapitalize="off" spellcheck="false" [(ngModel)]="toolFallbackModel"
|
||||
[disabled]="busy() || loading()" /></label>
|
||||
<app-model-selector label="兜底模型名称" [(value)]="toolFallbackModel" [presets]="modelPresets" [disabled]="!!busy() || loading()" />
|
||||
<app-setting-slider label="兜底思考强度" [(value)]="toolFallbackReasoning" [values]="reasoningValues" [labels]="reasoningLabels" [disabled]="!!busy() || loading()" />
|
||||
<div class="number-fields">
|
||||
<label>兜底请求重试次数<input name="toolFallbackRetries" type="number" min="0" max="10" required
|
||||
@@ -102,16 +93,11 @@ 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; } }
|
||||
.model-settings {gap:12px;font-size:13px} label {margin-top:6px}input,select {background:#fff;border-color:#dbe3ec;padding:11px 12px}
|
||||
input:focus,select:focus {outline:2px solid #a5b4fc;outline-offset:2px}
|
||||
.actions {border-top:1px solid #e2e8f0;padding-top:16px;align-items:center}.actions button {min-height:40px;background:#4f46e5;border-color:#4f46e5;font-weight:600;border-radius:12px}.actions button[type=button] {background:#fff;border-color:#dbe3ec;color:#475569}
|
||||
.model-presets button {border-radius:20px;font-size:12px}.model-presets button[aria-pressed=true] {background:#eef2ff;border-color:#a5b4fc;color:#4338ca}
|
||||
.recovery-settings {border-color:#e2e8f0;border-radius:14px;padding:14px 16px}.recovery-settings summary {font-size:13px}
|
||||
`]
|
||||
})
|
||||
@@ -120,7 +106,7 @@ export class ModelSettingsComponent implements OnInit {
|
||||
readonly reasoningLabels = REASONING_LABELS;
|
||||
private http = inject(HttpClient);
|
||||
saved = output<void>();
|
||||
modelPresets: string[] = [];
|
||||
modelPresets: string[] = MODEL_PRESETS;
|
||||
baseUrl = ''; model = ''; apiKey = ''; timeout = 300; retries = 5; retryInterval = 10; reasoning = 'high';
|
||||
rateLimitRetry = 60; toolFallbackModel = 'gpt-6-luna'; toolFallbackReasoning = 'high';
|
||||
toolFallbackRetries = 3; toolFallbackNoProgress = 3;
|
||||
|
||||
Reference in new issue
Block a user