@nextclaw/ui 0.6.11 → 0.6.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +13 -0
- package/dist/assets/ChannelsList-CXLzowHj.js +1 -0
- package/dist/assets/ChatPage-CvtonrzM.js +36 -0
- package/dist/assets/DocBrowser-4NK6-Q_u.js +1 -0
- package/dist/assets/LogoBadge-NI7KQCLa.js +1 -0
- package/dist/assets/{MarketplacePage-BOzko5s9.js → MarketplacePage-n7y-pif2.js} +2 -2
- package/dist/assets/ModelConfig-DztCs0mA.js +1 -0
- package/dist/assets/ProvidersList-hSzfE0pG.js +1 -0
- package/dist/assets/{RuntimeConfig-Dt9pLB9P.js → RuntimeConfig-CKFGVus7.js} +1 -1
- package/dist/assets/SearchConfig-Cxs1744q.js +1 -0
- package/dist/assets/{SecretsConfig-C1PU0Yy8.js → SecretsConfig-C90UckNB.js} +2 -2
- package/dist/assets/SessionsConfig-CRor418P.js +2 -0
- package/dist/assets/{card-C7Gtw2Vs.js → card-BQiPUGaa.js} +1 -1
- package/dist/assets/config-layout-BHnOoweL.js +1 -0
- package/dist/assets/index-BCfS4UY1.css +1 -0
- package/dist/assets/index-CB5eJOGS.js +8 -0
- package/dist/assets/index-CkqvHQAt.js +1 -0
- package/dist/assets/{input-oBvxsnV9.js → input-DmFFMdAk.js} +1 -1
- package/dist/assets/{label-C7F8lMpQ.js → label-BHvlZoIz.js} +1 -1
- package/dist/assets/{page-layout-DO8BlScF.js → page-layout-COPE9JyG.js} +1 -1
- package/dist/assets/popover-gcypYeec.js +1 -0
- package/dist/assets/provider-models-D3B_xWXx.js +1 -0
- package/dist/assets/{session-run-status-Kg0FwAPn.js → session-run-status-BgNvd_-a.js} +1 -1
- package/dist/assets/{switch-C6a5GyZB.js → switch-BampMwqT.js} +1 -1
- package/dist/assets/{tabs-custom-BatFap5k.js → tabs-custom-DSQeYaKd.js} +1 -1
- package/dist/assets/useConfirmDialog-DZUn23Li.js +5 -0
- package/dist/assets/{vendor-TlME1INH.js → vendor-BKtTvQYU.js} +69 -64
- package/dist/index.html +3 -3
- package/package.json +1 -1
- package/src/App.tsx +2 -0
- package/src/api/config.ts +13 -0
- package/src/api/types.ts +61 -0
- package/src/components/chat/ChatPage.tsx +16 -0
- package/src/components/chat/chat-input/ChatInputBottomToolbar.tsx +12 -0
- package/src/components/chat/chat-input/components/ChatInputSlashPanelSection.tsx +3 -3
- package/src/components/chat/chat-input/components/bottom-toolbar/ChatInputThinkingSelector.tsx +74 -0
- package/src/components/chat/chat-input/useChatInputBarController.ts +11 -2
- package/src/components/chat/chat-input.types.ts +8 -0
- package/src/components/chat/chat-page-data.ts +40 -3
- package/src/components/chat/chat-stream/transport.ts +3 -0
- package/src/components/chat/chat-stream/types.ts +3 -1
- package/src/components/chat/managers/chat-input.manager.ts +51 -0
- package/src/components/chat/stores/chat-input.store.ts +5 -1
- package/src/components/common/SearchableModelInput.tsx +22 -5
- package/src/components/config/ModelConfig.tsx +13 -12
- package/src/components/config/ProviderForm.tsx +292 -19
- package/src/components/config/SearchConfig.tsx +297 -0
- package/src/components/layout/Sidebar.tsx +6 -1
- package/src/hooks/useConfig.ts +17 -0
- package/src/lib/i18n.ts +37 -0
- package/src/lib/provider-models.ts +91 -3
- package/dist/assets/ChannelsList-C49JQ-Zt.js +0 -1
- package/dist/assets/ChatPage-DIx05c6s.js +0 -36
- package/dist/assets/DocBrowser-CpOosDEI.js +0 -1
- package/dist/assets/LogoBadge-CL_8ZPXU.js +0 -1
- package/dist/assets/ModelConfig-BZ4ZfaQB.js +0 -1
- package/dist/assets/ProvidersList-fPpJ5gl6.js +0 -1
- package/dist/assets/SessionsConfig-EskBOofQ.js +0 -2
- package/dist/assets/index-Cn6_2To7.js +0 -8
- package/dist/assets/index-nEYGCJTC.css +0 -1
- package/dist/assets/provider-models-y4mUDcGF.js +0 -1
- package/dist/assets/useConfirmDialog-zJzVKMdu.js +0 -5
package/dist/index.html
CHANGED
|
@@ -6,9 +6,9 @@
|
|
|
6
6
|
<link rel="icon" type="image/svg+xml" href="/logo.svg" />
|
|
7
7
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
8
8
|
<title>NextClaw - 系统配置</title>
|
|
9
|
-
<script type="module" crossorigin src="/assets/index-
|
|
10
|
-
<link rel="modulepreload" crossorigin href="/assets/vendor-
|
|
11
|
-
<link rel="stylesheet" crossorigin href="/assets/index-
|
|
9
|
+
<script type="module" crossorigin src="/assets/index-CB5eJOGS.js"></script>
|
|
10
|
+
<link rel="modulepreload" crossorigin href="/assets/vendor-BKtTvQYU.js">
|
|
11
|
+
<link rel="stylesheet" crossorigin href="/assets/index-BCfS4UY1.css">
|
|
12
12
|
</head>
|
|
13
13
|
|
|
14
14
|
<body>
|
package/package.json
CHANGED
package/src/App.tsx
CHANGED
|
@@ -16,6 +16,7 @@ const queryClient = new QueryClient({
|
|
|
16
16
|
|
|
17
17
|
const ModelConfigPage = lazy(async () => ({ default: (await import('@/components/config/ModelConfig')).ModelConfig }));
|
|
18
18
|
const ChatPage = lazy(async () => ({ default: (await import('@/components/chat/ChatPage')).ChatPage }));
|
|
19
|
+
const SearchConfigPage = lazy(async () => ({ default: (await import('@/components/config/SearchConfig')).SearchConfig }));
|
|
19
20
|
const ProvidersListPage = lazy(async () => ({ default: (await import('@/components/config/ProvidersList')).ProvidersList }));
|
|
20
21
|
const ChannelsListPage = lazy(async () => ({ default: (await import('@/components/config/ChannelsList')).ChannelsList }));
|
|
21
22
|
const RuntimeConfigPage = lazy(async () => ({ default: (await import('@/components/config/RuntimeConfig')).RuntimeConfig }));
|
|
@@ -45,6 +46,7 @@ function AppContent() {
|
|
|
45
46
|
<Route path="/skills" element={<LazyRoute><ChatPage view="skills" /></LazyRoute>} />
|
|
46
47
|
<Route path="/cron" element={<LazyRoute><ChatPage view="cron" /></LazyRoute>} />
|
|
47
48
|
<Route path="/model" element={<LazyRoute><ModelConfigPage /></LazyRoute>} />
|
|
49
|
+
<Route path="/search" element={<LazyRoute><SearchConfigPage /></LazyRoute>} />
|
|
48
50
|
<Route path="/providers" element={<LazyRoute><ProvidersListPage /></LazyRoute>} />
|
|
49
51
|
<Route path="/channels" element={<LazyRoute><ChannelsListPage /></LazyRoute>} />
|
|
50
52
|
<Route path="/runtime" element={<LazyRoute><RuntimeConfigPage /></LazyRoute>} />
|
package/src/api/config.ts
CHANGED
|
@@ -14,6 +14,8 @@ import type {
|
|
|
14
14
|
ProviderAuthPollRequest,
|
|
15
15
|
ProviderAuthPollResult,
|
|
16
16
|
ProviderAuthImportResult,
|
|
17
|
+
SearchConfigUpdate,
|
|
18
|
+
SearchConfigView,
|
|
17
19
|
ProviderCreateRequest,
|
|
18
20
|
ProviderCreateResult,
|
|
19
21
|
ProviderDeleteResult,
|
|
@@ -91,6 +93,17 @@ export async function updateModel(data: {
|
|
|
91
93
|
return response.data;
|
|
92
94
|
}
|
|
93
95
|
|
|
96
|
+
// PUT /api/config/search
|
|
97
|
+
export async function updateSearch(
|
|
98
|
+
data: SearchConfigUpdate
|
|
99
|
+
): Promise<SearchConfigView> {
|
|
100
|
+
const response = await api.put<SearchConfigView>('/api/config/search', data);
|
|
101
|
+
if (!response.ok) {
|
|
102
|
+
throw new Error(response.error.message);
|
|
103
|
+
}
|
|
104
|
+
return response.data;
|
|
105
|
+
}
|
|
106
|
+
|
|
94
107
|
// PUT /api/config/providers/:provider
|
|
95
108
|
export async function updateProvider(
|
|
96
109
|
provider: string,
|
package/src/api/types.ts
CHANGED
|
@@ -14,6 +14,8 @@ export type AppMetaView = {
|
|
|
14
14
|
productVersion: string;
|
|
15
15
|
};
|
|
16
16
|
|
|
17
|
+
export type ThinkingLevel = "off" | "minimal" | "low" | "medium" | "high" | "adaptive" | "xhigh";
|
|
18
|
+
|
|
17
19
|
export type ProviderConfigView = {
|
|
18
20
|
displayName?: string;
|
|
19
21
|
apiKeySet: boolean;
|
|
@@ -22,6 +24,7 @@ export type ProviderConfigView = {
|
|
|
22
24
|
extraHeaders?: Record<string, string> | null;
|
|
23
25
|
wireApi?: "auto" | "chat" | "responses" | null;
|
|
24
26
|
models?: string[];
|
|
27
|
+
modelThinking?: Record<string, { supported: ThinkingLevel[]; default?: ThinkingLevel | null }>;
|
|
25
28
|
};
|
|
26
29
|
|
|
27
30
|
export type ProviderConfigUpdate = {
|
|
@@ -31,6 +34,7 @@ export type ProviderConfigUpdate = {
|
|
|
31
34
|
extraHeaders?: Record<string, string> | null;
|
|
32
35
|
wireApi?: "auto" | "chat" | "responses" | null;
|
|
33
36
|
models?: string[] | null;
|
|
37
|
+
modelThinking?: Record<string, { supported?: ThinkingLevel[]; default?: ThinkingLevel | null }> | null;
|
|
34
38
|
};
|
|
35
39
|
|
|
36
40
|
export type ProviderConnectionTestRequest = ProviderConfigUpdate & {
|
|
@@ -74,6 +78,52 @@ export type ProviderConnectionTestResult = {
|
|
|
74
78
|
hint?: string;
|
|
75
79
|
};
|
|
76
80
|
|
|
81
|
+
export type SearchProviderName = "bocha" | "brave";
|
|
82
|
+
export type BochaFreshnessValue = "noLimit" | "oneDay" | "oneWeek" | "oneMonth" | "oneYear" | string;
|
|
83
|
+
|
|
84
|
+
export type SearchProviderConfigView = {
|
|
85
|
+
enabled: boolean;
|
|
86
|
+
apiKeySet: boolean;
|
|
87
|
+
apiKeyMasked?: string;
|
|
88
|
+
baseUrl: string;
|
|
89
|
+
docsUrl?: string;
|
|
90
|
+
summary?: boolean;
|
|
91
|
+
freshness?: BochaFreshnessValue;
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
export type SearchConfigView = {
|
|
95
|
+
provider: SearchProviderName;
|
|
96
|
+
enabledProviders: SearchProviderName[];
|
|
97
|
+
defaults: {
|
|
98
|
+
maxResults: number;
|
|
99
|
+
};
|
|
100
|
+
providers: {
|
|
101
|
+
bocha: SearchProviderConfigView;
|
|
102
|
+
brave: SearchProviderConfigView;
|
|
103
|
+
};
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
export type SearchConfigUpdate = {
|
|
107
|
+
provider?: SearchProviderName;
|
|
108
|
+
enabledProviders?: SearchProviderName[];
|
|
109
|
+
defaults?: {
|
|
110
|
+
maxResults?: number;
|
|
111
|
+
};
|
|
112
|
+
providers?: {
|
|
113
|
+
bocha?: {
|
|
114
|
+
apiKey?: string | null;
|
|
115
|
+
baseUrl?: string | null;
|
|
116
|
+
docsUrl?: string | null;
|
|
117
|
+
summary?: boolean;
|
|
118
|
+
freshness?: BochaFreshnessValue | null;
|
|
119
|
+
};
|
|
120
|
+
brave?: {
|
|
121
|
+
apiKey?: string | null;
|
|
122
|
+
baseUrl?: string | null;
|
|
123
|
+
};
|
|
124
|
+
};
|
|
125
|
+
};
|
|
126
|
+
|
|
77
127
|
export type ProviderAuthStartResult = {
|
|
78
128
|
provider: string;
|
|
79
129
|
kind: "device_code";
|
|
@@ -445,6 +495,7 @@ export type ConfigView = {
|
|
|
445
495
|
};
|
|
446
496
|
};
|
|
447
497
|
providers: Record<string, ProviderConfigView>;
|
|
498
|
+
search: SearchConfigView;
|
|
448
499
|
channels: Record<string, Record<string, unknown>>;
|
|
449
500
|
bindings?: AgentBindingView[];
|
|
450
501
|
session?: SessionConfigView;
|
|
@@ -507,8 +558,18 @@ export type ChannelSpecView = {
|
|
|
507
558
|
};
|
|
508
559
|
};
|
|
509
560
|
|
|
561
|
+
export type SearchProviderSpecView = {
|
|
562
|
+
name: SearchProviderName;
|
|
563
|
+
displayName: string;
|
|
564
|
+
description: string;
|
|
565
|
+
docsUrl?: string;
|
|
566
|
+
isDefault?: boolean;
|
|
567
|
+
supportsSummary?: boolean;
|
|
568
|
+
};
|
|
569
|
+
|
|
510
570
|
export type ConfigMetaView = {
|
|
511
571
|
providers: ProviderSpecView[];
|
|
572
|
+
search: SearchProviderSpecView[];
|
|
512
573
|
channels: ChannelSpecView[];
|
|
513
574
|
};
|
|
514
575
|
|
|
@@ -122,6 +122,7 @@ export function ChatPage({ view }: ChatPageProps) {
|
|
|
122
122
|
const { sessionId: routeSessionIdParam } = useParams<{ sessionId?: string }>();
|
|
123
123
|
const threadRef = useRef<HTMLDivElement | null>(null);
|
|
124
124
|
const selectedSessionKeyRef = useRef<string | null>(selectedSessionKey);
|
|
125
|
+
const thinkingHydratedSessionKeyRef = useRef<string | null>(null);
|
|
125
126
|
const routeSessionKey = useMemo(
|
|
126
127
|
() => parseSessionKeyFromRoute(routeSessionIdParam),
|
|
127
128
|
[routeSessionIdParam]
|
|
@@ -137,6 +138,7 @@ export function ChatPage({ view }: ChatPageProps) {
|
|
|
137
138
|
skillRecords,
|
|
138
139
|
selectedSession,
|
|
139
140
|
historyMessages,
|
|
141
|
+
selectedSessionThinkingLevel,
|
|
140
142
|
sessionTypeOptions,
|
|
141
143
|
defaultSessionType,
|
|
142
144
|
selectedSessionType,
|
|
@@ -232,6 +234,12 @@ export function ChatPage({ view }: ChatPageProps) {
|
|
|
232
234
|
]);
|
|
233
235
|
|
|
234
236
|
useEffect(() => {
|
|
237
|
+
const shouldHydrateThinkingFromHistory =
|
|
238
|
+
!isSending &&
|
|
239
|
+
!isAwaitingAssistantOutput &&
|
|
240
|
+
!historyQuery.isLoading &&
|
|
241
|
+
selectedSessionKey !== thinkingHydratedSessionKeyRef.current;
|
|
242
|
+
|
|
235
243
|
presenter.chatInputManager.syncSnapshot({
|
|
236
244
|
isProviderStateResolved,
|
|
237
245
|
defaultSessionType,
|
|
@@ -244,11 +252,18 @@ export function ChatPage({ view }: ChatPageProps) {
|
|
|
244
252
|
modelOptions,
|
|
245
253
|
sessionTypeOptions,
|
|
246
254
|
selectedSessionType,
|
|
255
|
+
...(shouldHydrateThinkingFromHistory ? { selectedThinkingLevel: selectedSessionThinkingLevel } : {}),
|
|
247
256
|
canEditSessionType,
|
|
248
257
|
sessionTypeUnavailable,
|
|
249
258
|
skillRecords,
|
|
250
259
|
isSkillsLoading: installedSkillsQuery.isLoading
|
|
251
260
|
});
|
|
261
|
+
if (shouldHydrateThinkingFromHistory) {
|
|
262
|
+
thinkingHydratedSessionKeyRef.current = selectedSessionKey;
|
|
263
|
+
}
|
|
264
|
+
if (!selectedSessionKey) {
|
|
265
|
+
thinkingHydratedSessionKeyRef.current = null;
|
|
266
|
+
}
|
|
252
267
|
presenter.chatSessionListManager.syncSnapshot({
|
|
253
268
|
sessions,
|
|
254
269
|
query,
|
|
@@ -292,6 +307,7 @@ export function ChatPage({ view }: ChatPageProps) {
|
|
|
292
307
|
presenter,
|
|
293
308
|
query,
|
|
294
309
|
selectedSession,
|
|
310
|
+
selectedSessionThinkingLevel,
|
|
295
311
|
selectedSessionKey,
|
|
296
312
|
selectedAgentId,
|
|
297
313
|
selectedSessionType,
|
|
@@ -5,6 +5,7 @@ import { ChatInputAttachButton } from '@/components/chat/chat-input/components/b
|
|
|
5
5
|
import { ChatInputModelSelector } from '@/components/chat/chat-input/components/bottom-toolbar/ChatInputModelSelector';
|
|
6
6
|
import { ChatInputSendControls } from '@/components/chat/chat-input/components/bottom-toolbar/ChatInputSendControls';
|
|
7
7
|
import { ChatInputSessionTypeSelector } from '@/components/chat/chat-input/components/bottom-toolbar/ChatInputSessionTypeSelector';
|
|
8
|
+
import { ChatInputThinkingSelector } from '@/components/chat/chat-input/components/bottom-toolbar/ChatInputThinkingSelector';
|
|
8
9
|
import { t } from '@/lib/i18n';
|
|
9
10
|
|
|
10
11
|
export function ChatInputBottomToolbar() {
|
|
@@ -27,6 +28,9 @@ export function ChatInputBottomToolbar() {
|
|
|
27
28
|
snapshot.stopDisabledReason === '__preparing__'
|
|
28
29
|
? t('chatStopPreparing')
|
|
29
30
|
: snapshot.stopDisabledReason?.trim() || t('chatStopUnavailable');
|
|
31
|
+
const selectedModelThinkingCapability = selectedModelOption?.thinkingCapability;
|
|
32
|
+
const thinkingSupportedLevels = selectedModelThinkingCapability?.supported ?? [];
|
|
33
|
+
const shouldShowThinkingSelector = thinkingSupportedLevels.length > 0;
|
|
30
34
|
|
|
31
35
|
return (
|
|
32
36
|
<div className="flex items-center justify-between px-3 pb-3">
|
|
@@ -53,6 +57,14 @@ export function ChatInputBottomToolbar() {
|
|
|
53
57
|
isModelOptionsLoading={isModelOptionsLoading}
|
|
54
58
|
hasModelOptions={hasModelOptions}
|
|
55
59
|
/>
|
|
60
|
+
{shouldShowThinkingSelector ? (
|
|
61
|
+
<ChatInputThinkingSelector
|
|
62
|
+
supportedLevels={thinkingSupportedLevels}
|
|
63
|
+
selectedThinkingLevel={snapshot.selectedThinkingLevel}
|
|
64
|
+
defaultThinkingLevel={selectedModelThinkingCapability?.default ?? null}
|
|
65
|
+
onSelectedThinkingLevelChange={presenter.chatInputManager.selectThinkingLevel}
|
|
66
|
+
/>
|
|
67
|
+
) : null}
|
|
56
68
|
<ChatInputAttachButton />
|
|
57
69
|
</div>
|
|
58
70
|
<ChatInputSendControls
|
|
@@ -43,10 +43,10 @@ export function ChatInputSlashPanelSection({
|
|
|
43
43
|
onOpenAutoFocus={(event) => event.preventDefault()}
|
|
44
44
|
style={resolvedSlashPanelWidth ? { width: `${resolvedSlashPanelWidth}px` } : undefined}
|
|
45
45
|
>
|
|
46
|
-
<div className="grid min-h-[240px] grid-cols-[minmax(
|
|
46
|
+
<div className="grid min-h-[240px] grid-cols-[minmax(220px,300px)_minmax(0,1fr)]">
|
|
47
47
|
<div
|
|
48
48
|
ref={slashListRef}
|
|
49
|
-
className="max-h-[320px] overflow-y-auto border-r border-gray-200 p-
|
|
49
|
+
className="max-h-[320px] overflow-y-auto border-r border-gray-200 p-2.5 custom-scrollbar"
|
|
50
50
|
>
|
|
51
51
|
{isSlashPanelLoading ? (
|
|
52
52
|
<div className="p-2 text-xs text-gray-500">{t('chatSlashLoading')}</div>
|
|
@@ -82,7 +82,7 @@ export function ChatInputSlashPanelSection({
|
|
|
82
82
|
</>
|
|
83
83
|
)}
|
|
84
84
|
</div>
|
|
85
|
-
<div className="p-
|
|
85
|
+
<div className="max-w-[320px] p-3.5">
|
|
86
86
|
{activeSlashItem ? (
|
|
87
87
|
<div className="space-y-3">
|
|
88
88
|
<div className="flex items-center gap-2">
|
package/src/components/chat/chat-input/components/bottom-toolbar/ChatInputThinkingSelector.tsx
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import type { ThinkingLevel } from '@/api/types';
|
|
2
|
+
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select';
|
|
3
|
+
import { t } from '@/lib/i18n';
|
|
4
|
+
import { Brain } from 'lucide-react';
|
|
5
|
+
|
|
6
|
+
type ChatInputThinkingSelectorProps = {
|
|
7
|
+
supportedLevels: ThinkingLevel[];
|
|
8
|
+
selectedThinkingLevel: ThinkingLevel | null;
|
|
9
|
+
defaultThinkingLevel?: ThinkingLevel | null;
|
|
10
|
+
onSelectedThinkingLevelChange: (value: ThinkingLevel) => void;
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
function thinkingLabel(level: ThinkingLevel): string {
|
|
14
|
+
if (level === 'off') {
|
|
15
|
+
return t('chatThinkingLevelOff');
|
|
16
|
+
}
|
|
17
|
+
if (level === 'minimal') {
|
|
18
|
+
return t('chatThinkingLevelMinimal');
|
|
19
|
+
}
|
|
20
|
+
if (level === 'low') {
|
|
21
|
+
return t('chatThinkingLevelLow');
|
|
22
|
+
}
|
|
23
|
+
if (level === 'medium') {
|
|
24
|
+
return t('chatThinkingLevelMedium');
|
|
25
|
+
}
|
|
26
|
+
if (level === 'high') {
|
|
27
|
+
return t('chatThinkingLevelHigh');
|
|
28
|
+
}
|
|
29
|
+
if (level === 'adaptive') {
|
|
30
|
+
return t('chatThinkingLevelAdaptive');
|
|
31
|
+
}
|
|
32
|
+
return t('chatThinkingLevelXhigh');
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function normalizeLevels(levels: ThinkingLevel[]): ThinkingLevel[] {
|
|
36
|
+
const deduped: ThinkingLevel[] = [];
|
|
37
|
+
for (const level of ['off', ...levels] as ThinkingLevel[]) {
|
|
38
|
+
if (!deduped.includes(level)) {
|
|
39
|
+
deduped.push(level);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
return deduped;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export function ChatInputThinkingSelector(props: ChatInputThinkingSelectorProps) {
|
|
46
|
+
if (props.supportedLevels.length === 0) {
|
|
47
|
+
return null;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const options = normalizeLevels(props.supportedLevels);
|
|
51
|
+
const fallback = options.includes('off') ? 'off' : options[0];
|
|
52
|
+
const resolvedValue =
|
|
53
|
+
(props.selectedThinkingLevel && options.includes(props.selectedThinkingLevel) && props.selectedThinkingLevel) ||
|
|
54
|
+
(props.defaultThinkingLevel && options.includes(props.defaultThinkingLevel) && props.defaultThinkingLevel) ||
|
|
55
|
+
fallback;
|
|
56
|
+
|
|
57
|
+
return (
|
|
58
|
+
<Select value={resolvedValue} onValueChange={(value) => props.onSelectedThinkingLevelChange(value as ThinkingLevel)}>
|
|
59
|
+
<SelectTrigger className="h-8 w-auto min-w-[150px] rounded-lg border-0 bg-transparent px-3 text-xs font-medium text-gray-600 shadow-none hover:bg-gray-100 focus:ring-0">
|
|
60
|
+
<div className="flex min-w-0 items-center gap-2 text-left">
|
|
61
|
+
<Brain className="h-3.5 w-3.5 shrink-0 text-gray-500" />
|
|
62
|
+
<span className="truncate text-xs font-semibold text-gray-700">{thinkingLabel(resolvedValue)}</span>
|
|
63
|
+
</div>
|
|
64
|
+
</SelectTrigger>
|
|
65
|
+
<SelectContent className="w-[180px]">
|
|
66
|
+
{options.map((level) => (
|
|
67
|
+
<SelectItem key={level} value={level}>
|
|
68
|
+
{thinkingLabel(level)}
|
|
69
|
+
</SelectItem>
|
|
70
|
+
))}
|
|
71
|
+
</SelectContent>
|
|
72
|
+
</Select>
|
|
73
|
+
);
|
|
74
|
+
}
|
|
@@ -4,7 +4,9 @@ import type { MarketplaceInstalledRecord } from '@/api/types';
|
|
|
4
4
|
import { t } from '@/lib/i18n';
|
|
5
5
|
import type { ChatInputBarSlashItem } from '@/components/chat/chat-input.types';
|
|
6
6
|
|
|
7
|
-
const SLASH_PANEL_MAX_WIDTH =
|
|
7
|
+
const SLASH_PANEL_MAX_WIDTH = 680;
|
|
8
|
+
const SLASH_PANEL_DESKTOP_SHRINK_RATIO = 0.82;
|
|
9
|
+
const SLASH_PANEL_DESKTOP_MIN_WIDTH = 560;
|
|
8
10
|
|
|
9
11
|
type RankedSkill = {
|
|
10
12
|
record: MarketplaceInstalledRecord;
|
|
@@ -162,7 +164,14 @@ function useSlashPanelController(params: SlashPanelControllerParams) {
|
|
|
162
164
|
const isSlashPanelOpen = slashQuery !== null && !dismissedSlashPanel;
|
|
163
165
|
const activeSlashItem = slashItems[activeSlashIndex] ?? null;
|
|
164
166
|
const isSlashPanelLoading = params.isSkillsLoading;
|
|
165
|
-
const resolvedSlashPanelWidth = slashPanelWidth
|
|
167
|
+
const resolvedSlashPanelWidth = slashPanelWidth
|
|
168
|
+
? Math.min(
|
|
169
|
+
slashPanelWidth > SLASH_PANEL_DESKTOP_MIN_WIDTH
|
|
170
|
+
? slashPanelWidth * SLASH_PANEL_DESKTOP_SHRINK_RATIO
|
|
171
|
+
: slashPanelWidth,
|
|
172
|
+
SLASH_PANEL_MAX_WIDTH
|
|
173
|
+
)
|
|
174
|
+
: undefined;
|
|
166
175
|
|
|
167
176
|
useEffect(() => {
|
|
168
177
|
const anchor = slashAnchorRef.current;
|
|
@@ -1,7 +1,15 @@
|
|
|
1
|
+
import type { ThinkingLevel } from '@/api/types';
|
|
2
|
+
|
|
3
|
+
export type ChatModelThinkingCapability = {
|
|
4
|
+
supported: ThinkingLevel[];
|
|
5
|
+
default?: ThinkingLevel | null;
|
|
6
|
+
};
|
|
7
|
+
|
|
1
8
|
export type ChatModelOption = {
|
|
2
9
|
value: string;
|
|
3
10
|
modelLabel: string;
|
|
4
11
|
providerLabel: string;
|
|
12
|
+
thinkingCapability?: ChatModelThinkingCapability | null;
|
|
5
13
|
};
|
|
6
14
|
|
|
7
15
|
export type ChatInputBarSlashItem = {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { useMemo } from 'react';
|
|
2
2
|
import type { Dispatch, SetStateAction } from 'react';
|
|
3
|
-
import type { SessionEntryView } from '@/api/types';
|
|
3
|
+
import type { SessionEntryView, ThinkingLevel } from '@/api/types';
|
|
4
4
|
import type { ChatModelOption } from '@/components/chat/chat-input.types';
|
|
5
5
|
import { useChatSessionTypeState } from '@/components/chat/useChatSessionTypeState';
|
|
6
6
|
import { useSyncSelectedModel } from '@/components/chat/chat-page-runtime';
|
|
@@ -13,7 +13,7 @@ import {
|
|
|
13
13
|
useSessions,
|
|
14
14
|
} from '@/hooks/useConfig';
|
|
15
15
|
import { useMarketplaceInstalled } from '@/hooks/useMarketplace';
|
|
16
|
-
import { buildProviderModelCatalog, composeProviderModel } from '@/lib/provider-models';
|
|
16
|
+
import { buildProviderModelCatalog, composeProviderModel, resolveModelThinkingCapability } from '@/lib/provider-models';
|
|
17
17
|
|
|
18
18
|
type UseChatPageDataParams = {
|
|
19
19
|
query: string;
|
|
@@ -24,6 +24,19 @@ type UseChatPageDataParams = {
|
|
|
24
24
|
setSelectedModel: Dispatch<SetStateAction<string>>;
|
|
25
25
|
};
|
|
26
26
|
|
|
27
|
+
const THINKING_LEVEL_SET = new Set<string>(['off', 'minimal', 'low', 'medium', 'high', 'adaptive', 'xhigh']);
|
|
28
|
+
|
|
29
|
+
function parseThinkingLevel(value: unknown): ThinkingLevel | null {
|
|
30
|
+
if (typeof value !== 'string') {
|
|
31
|
+
return null;
|
|
32
|
+
}
|
|
33
|
+
const normalized = value.trim().toLowerCase();
|
|
34
|
+
if (!normalized) {
|
|
35
|
+
return null;
|
|
36
|
+
}
|
|
37
|
+
return THINKING_LEVEL_SET.has(normalized) ? (normalized as ThinkingLevel) : null;
|
|
38
|
+
}
|
|
39
|
+
|
|
27
40
|
export function useChatPageData(params: UseChatPageDataParams) {
|
|
28
41
|
const configQuery = useConfig();
|
|
29
42
|
const configMetaQuery = useConfigMeta();
|
|
@@ -57,7 +70,8 @@ export function useChatPageData(params: UseChatPageDataParams) {
|
|
|
57
70
|
options.push({
|
|
58
71
|
value,
|
|
59
72
|
modelLabel: localModel,
|
|
60
|
-
providerLabel: provider.displayName
|
|
73
|
+
providerLabel: provider.displayName,
|
|
74
|
+
thinkingCapability: resolveModelThinkingCapability(provider.modelThinking, localModel, provider.aliases)
|
|
61
75
|
});
|
|
62
76
|
}
|
|
63
77
|
}
|
|
@@ -93,6 +107,28 @@ export function useChatPageData(params: UseChatPageDataParams) {
|
|
|
93
107
|
});
|
|
94
108
|
|
|
95
109
|
const historyMessages = useMemo(() => historyQuery.data?.messages ?? [], [historyQuery.data?.messages]);
|
|
110
|
+
const selectedSessionThinkingLevel = useMemo(() => {
|
|
111
|
+
if (!params.selectedSessionKey) {
|
|
112
|
+
return null;
|
|
113
|
+
}
|
|
114
|
+
const metadata = historyQuery.data?.metadata;
|
|
115
|
+
if (!metadata || typeof metadata !== 'object') {
|
|
116
|
+
return null;
|
|
117
|
+
}
|
|
118
|
+
const candidates = [
|
|
119
|
+
metadata.preferred_thinking,
|
|
120
|
+
metadata.thinking,
|
|
121
|
+
metadata.thinking_level,
|
|
122
|
+
metadata.thinkingLevel
|
|
123
|
+
];
|
|
124
|
+
for (const value of candidates) {
|
|
125
|
+
const level = parseThinkingLevel(value);
|
|
126
|
+
if (level) {
|
|
127
|
+
return level;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
return null;
|
|
131
|
+
}, [historyQuery.data?.metadata, params.selectedSessionKey]);
|
|
96
132
|
|
|
97
133
|
return {
|
|
98
134
|
configQuery,
|
|
@@ -108,6 +144,7 @@ export function useChatPageData(params: UseChatPageDataParams) {
|
|
|
108
144
|
skillRecords,
|
|
109
145
|
selectedSession,
|
|
110
146
|
historyMessages,
|
|
147
|
+
selectedSessionThinkingLevel,
|
|
111
148
|
...sessionTypeState
|
|
112
149
|
};
|
|
113
150
|
}
|
|
@@ -6,6 +6,9 @@ function buildSendTurnPayload(item: SendMessageParams, requestedSkills: string[]
|
|
|
6
6
|
if (item.sessionType) {
|
|
7
7
|
metadata.session_type = item.sessionType;
|
|
8
8
|
}
|
|
9
|
+
if (item.thinkingLevel) {
|
|
10
|
+
metadata.thinking = item.thinkingLevel;
|
|
11
|
+
}
|
|
9
12
|
if (requestedSkills.length > 0) {
|
|
10
13
|
metadata.requested_skills = requestedSkills;
|
|
11
14
|
}
|
|
@@ -4,7 +4,8 @@ import type {
|
|
|
4
4
|
ChatTurnStreamDeltaEvent,
|
|
5
5
|
ChatTurnStreamReadyEvent,
|
|
6
6
|
ChatTurnStreamSessionEvent,
|
|
7
|
-
SessionMessageView
|
|
7
|
+
SessionMessageView,
|
|
8
|
+
ThinkingLevel
|
|
8
9
|
} from '@/api/types';
|
|
9
10
|
|
|
10
11
|
export type SendMessageParams = {
|
|
@@ -14,6 +15,7 @@ export type SendMessageParams = {
|
|
|
14
15
|
agentId: string;
|
|
15
16
|
sessionType?: string;
|
|
16
17
|
model?: string;
|
|
18
|
+
thinkingLevel?: ThinkingLevel;
|
|
17
19
|
requestedSkills?: string[];
|
|
18
20
|
stopSupported?: boolean;
|
|
19
21
|
stopReason?: string;
|
|
@@ -7,6 +7,8 @@ import { normalizeSessionType } from '@/components/chat/useChatSessionTypeState'
|
|
|
7
7
|
import type { ChatInputSnapshot } from '@/components/chat/stores/chat-input.store';
|
|
8
8
|
import type { SetStateAction } from 'react';
|
|
9
9
|
import type { ChatStreamActionsManager } from '@/components/chat/managers/chat-stream-actions.manager';
|
|
10
|
+
import type { ThinkingLevel } from '@/api/types';
|
|
11
|
+
import type { ChatModelOption } from '@/components/chat/chat-input.types';
|
|
10
12
|
|
|
11
13
|
export class ChatInputManager {
|
|
12
14
|
constructor(
|
|
@@ -36,6 +38,14 @@ export class ChatInputManager {
|
|
|
36
38
|
return;
|
|
37
39
|
}
|
|
38
40
|
useChatInputStore.getState().setSnapshot(patch);
|
|
41
|
+
if (
|
|
42
|
+
Object.prototype.hasOwnProperty.call(patch, 'modelOptions') ||
|
|
43
|
+
Object.prototype.hasOwnProperty.call(patch, 'selectedModel') ||
|
|
44
|
+
Object.prototype.hasOwnProperty.call(patch, 'selectedThinkingLevel')
|
|
45
|
+
) {
|
|
46
|
+
const snapshot = useChatInputStore.getState().snapshot;
|
|
47
|
+
this.reconcileThinkingForModel(snapshot.selectedModel);
|
|
48
|
+
}
|
|
39
49
|
};
|
|
40
50
|
|
|
41
51
|
setDraft = (next: SetStateAction<string>) => {
|
|
@@ -77,6 +87,7 @@ export class ChatInputManager {
|
|
|
77
87
|
agentId: sessionSnapshot.selectedAgentId,
|
|
78
88
|
sessionType: inputSnapshot.selectedSessionType,
|
|
79
89
|
model: inputSnapshot.selectedModel || undefined,
|
|
90
|
+
thinkingLevel: inputSnapshot.selectedThinkingLevel ?? undefined,
|
|
80
91
|
stopSupported: inputSnapshot.stopSupported,
|
|
81
92
|
stopReason: inputSnapshot.stopReason,
|
|
82
93
|
requestedSkills,
|
|
@@ -99,6 +110,16 @@ export class ChatInputManager {
|
|
|
99
110
|
return;
|
|
100
111
|
}
|
|
101
112
|
useChatInputStore.getState().setSnapshot({ selectedModel: value });
|
|
113
|
+
this.reconcileThinkingForModel(value);
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
setSelectedThinkingLevel = (next: SetStateAction<ThinkingLevel | null>) => {
|
|
117
|
+
const prev = useChatInputStore.getState().snapshot.selectedThinkingLevel;
|
|
118
|
+
const value = this.resolveUpdateValue(prev, next);
|
|
119
|
+
if (value === prev) {
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
useChatInputStore.getState().setSnapshot({ selectedThinkingLevel: value });
|
|
102
123
|
};
|
|
103
124
|
|
|
104
125
|
selectSessionType = (value: string) => {
|
|
@@ -120,10 +141,40 @@ export class ChatInputManager {
|
|
|
120
141
|
this.setSelectedModel(value);
|
|
121
142
|
};
|
|
122
143
|
|
|
144
|
+
selectThinkingLevel = (value: ThinkingLevel) => {
|
|
145
|
+
this.setSelectedThinkingLevel(value);
|
|
146
|
+
};
|
|
147
|
+
|
|
123
148
|
selectSkills = (next: string[]) => {
|
|
124
149
|
this.setSelectedSkills(next);
|
|
125
150
|
};
|
|
126
151
|
|
|
152
|
+
private resolveThinkingForModel(modelOption: ChatModelOption | undefined, current: ThinkingLevel | null): ThinkingLevel | null {
|
|
153
|
+
const capability = modelOption?.thinkingCapability;
|
|
154
|
+
if (!capability || capability.supported.length === 0) {
|
|
155
|
+
return null;
|
|
156
|
+
}
|
|
157
|
+
if (current === 'off') {
|
|
158
|
+
return 'off';
|
|
159
|
+
}
|
|
160
|
+
if (current && capability.supported.includes(current)) {
|
|
161
|
+
return current;
|
|
162
|
+
}
|
|
163
|
+
if (capability.default && capability.supported.includes(capability.default)) {
|
|
164
|
+
return capability.default;
|
|
165
|
+
}
|
|
166
|
+
return 'off';
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
private reconcileThinkingForModel(model: string): void {
|
|
170
|
+
const snapshot = useChatInputStore.getState().snapshot;
|
|
171
|
+
const modelOption = snapshot.modelOptions.find((option) => option.value === model);
|
|
172
|
+
const nextThinking = this.resolveThinkingForModel(modelOption, snapshot.selectedThinkingLevel);
|
|
173
|
+
if (nextThinking !== snapshot.selectedThinkingLevel) {
|
|
174
|
+
useChatInputStore.getState().setSnapshot({ selectedThinkingLevel: nextThinking });
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
127
178
|
private syncRemoteSessionType = async (normalizedType: string) => {
|
|
128
179
|
const sessionSnapshot = useChatSessionListStore.getState().snapshot;
|
|
129
180
|
const selectedSessionKey = sessionSnapshot.selectedSessionKey;
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { create } from 'zustand';
|
|
2
2
|
import type { MarketplaceInstalledRecord } from '@/api/types';
|
|
3
|
+
import type { ThinkingLevel } from '@/api/types';
|
|
4
|
+
import type { ChatModelOption } from '@/components/chat/chat-input.types';
|
|
3
5
|
|
|
4
6
|
export type ChatInputSnapshot = {
|
|
5
7
|
isProviderStateResolved: boolean;
|
|
@@ -10,8 +12,9 @@ export type ChatInputSnapshot = {
|
|
|
10
12
|
stopDisabledReason: string | null;
|
|
11
13
|
sendError: string | null;
|
|
12
14
|
isSending: boolean;
|
|
13
|
-
modelOptions:
|
|
15
|
+
modelOptions: ChatModelOption[];
|
|
14
16
|
selectedModel: string;
|
|
17
|
+
selectedThinkingLevel: ThinkingLevel | null;
|
|
15
18
|
sessionTypeOptions: Array<{ value: string; label: string }>;
|
|
16
19
|
selectedSessionType?: string;
|
|
17
20
|
stopSupported: boolean;
|
|
@@ -39,6 +42,7 @@ const initialSnapshot: ChatInputSnapshot = {
|
|
|
39
42
|
isSending: false,
|
|
40
43
|
modelOptions: [],
|
|
41
44
|
selectedModel: '',
|
|
45
|
+
selectedThinkingLevel: null,
|
|
42
46
|
sessionTypeOptions: [],
|
|
43
47
|
selectedSessionType: undefined,
|
|
44
48
|
stopSupported: false,
|