feat: add guided exploration and share user-started MCP backend
This commit is contained in:
1 parent
f86db1ced6
commit
ee256ad7ce
144 files changed
+8625
-2075
No files matched your search
@@ -84,6 +84,12 @@ interface Capabilities {
|
||||
.number-fields { display: grid; grid-template-columns: 1fr 1fr; gap: 12px 16px; }
|
||||
.number-fields label { display: block; padding: 0; color: #334155; }
|
||||
.number-fields input, .number-fields select { box-sizing: border-box; display: block; width: 100%; height: auto; margin-top: 8px; padding: 9px; border: 1px solid #cbd5e1; border-radius: 8px; font: inherit; }
|
||||
section {font-size:13px;border-color:#e2e8f0}h3 {font-size:15px;color:#334155}
|
||||
section > label {align-items:center;flex-direction:row-reverse;justify-content:space-between;border-bottom:1px solid #f1f5f9;gap:20px}section > label > span {flex:1}small {font-size:12px}
|
||||
input[role=switch] {appearance:none;position:relative;width:36px;height:22px;border:1px solid #cbd5e1;border-radius:20px;background:#e2e8f0;cursor:pointer;transition:background .15s}
|
||||
input[role=switch]::after {content:'';position:absolute;top:2px;left:2px;width:16px;height:16px;border-radius:50%;background:#fff;box-shadow:0 1px 3px #0f172a25;transition:transform .15s}
|
||||
input[role=switch]:checked {background:#6366f1;border-color:#6366f1}input[role=switch]:checked::after {transform:translateX(14px)}input[role=switch]:focus-visible {outline:2px solid #a5b4fc;outline-offset:3px}input:disabled {opacity:.5;cursor:default}
|
||||
button {background:#4f46e5;border-color:#4f46e5;border-radius:12px;font-weight:600}.execution-settings {border-color:#e2e8f0;border-radius:14px;padding:14px 16px}.number-fields input,.number-fields select {background:#fff;border-color:#dbe3ec}
|
||||
@media(max-width: 480px) { .number-fields { grid-template-columns: 1fr; } }`]
|
||||
})
|
||||
export class CapabilitySettingsComponent implements OnInit {
|
||||
|
||||
@@ -71,9 +71,9 @@ describe('持久化模型与功能设置', () => {
|
||||
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'));
|
||||
const reasoning = root.querySelector<HTMLInputElement>('input[aria-label="思考强度"]')!;
|
||||
reasoning.value = '7';
|
||||
reasoning.dispatchEvent(new Event('input'));
|
||||
fixture.detectChanges();
|
||||
await fixture.whenStable();
|
||||
expect(presets[0].getAttribute('aria-pressed')).toBe('false');
|
||||
@@ -90,7 +90,7 @@ describe('持久化模型与功能设置', () => {
|
||||
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');
|
||||
expect(reopened.nativeElement.querySelector('input[aria-label="思考强度"]').getAttribute('aria-valuetext')).toBe('最高 · max');
|
||||
});
|
||||
|
||||
it('高级稳定性设置可编辑并保存', async () => {
|
||||
|
||||
@@ -2,6 +2,7 @@ import { Component, OnInit, inject, signal, output } from '@angular/core';
|
||||
import { FormsModule } from '@angular/forms';
|
||||
import { HttpClient } from '@angular/common/http';
|
||||
import { finalize } from 'rxjs';
|
||||
import { SettingSliderComponent, REASONING_VALUES, REASONING_LABELS } from '../setting-slider/setting-slider.component';
|
||||
|
||||
interface ModelSettings {
|
||||
base_url: string;
|
||||
@@ -24,7 +25,7 @@ interface ModelSettings {
|
||||
@Component({
|
||||
selector: 'app-model-settings',
|
||||
standalone: true,
|
||||
imports: [FormsModule],
|
||||
imports: [FormsModule, SettingSliderComponent],
|
||||
template: `
|
||||
<form class="model-settings" (ngSubmit)="save()">
|
||||
<p>统一使用 OpenAI Responses。保存后新任务立即生效,重启后自动加载。</p>
|
||||
@@ -43,14 +44,7 @@ interface ModelSettings {
|
||||
[(ngModel)]="model" required placeholder="输入接口支持的模型名" aria-describedby="model-name-help"
|
||||
[disabled]="busy() || loading()" />
|
||||
<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="max">最高(max)</option>
|
||||
</select>
|
||||
<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"
|
||||
placeholder="输入新密钥" [disabled]="busy() || loading()" />
|
||||
@@ -69,13 +63,7 @@ interface ModelSettings {
|
||||
<label>兜底模型名称<input name="toolFallbackModel" type="text" required autocomplete="off"
|
||||
autocapitalize="off" spellcheck="false" [(ngModel)]="toolFallbackModel"
|
||||
[disabled]="busy() || loading()" /></label>
|
||||
<label>兜底思考强度<select name="toolFallbackReasoning" [(ngModel)]="toolFallbackReasoning"
|
||||
[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="max">最高(max)</option>
|
||||
</select></label>
|
||||
<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
|
||||
[(ngModel)]="toolFallbackRetries" [disabled]="busy() || loading()" /></label>
|
||||
@@ -119,9 +107,16 @@ interface ModelSettings {
|
||||
.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}
|
||||
`]
|
||||
})
|
||||
export class ModelSettingsComponent implements OnInit {
|
||||
readonly reasoningValues = REASONING_VALUES;
|
||||
readonly reasoningLabels = REASONING_LABELS;
|
||||
private http = inject(HttpClient);
|
||||
saved = output<void>();
|
||||
modelPresets: string[] = [];
|
||||
|
||||
Reference in new issue
Block a user