@lobehub/chat 1.69.5 → 1.70.0
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 +50 -0
- package/changelog/v1.json +18 -0
- package/docs/self-hosting/advanced/auth/clerk.zh-CN.mdx +1 -1
- package/docs/self-hosting/server-database/vercel.zh-CN.mdx +1 -1
- package/locales/ar/chat.json +7 -1
- package/locales/ar/components.json +2 -0
- package/locales/ar/models.json +3 -0
- package/locales/bg-BG/chat.json +7 -1
- package/locales/bg-BG/components.json +2 -0
- package/locales/bg-BG/models.json +3 -0
- package/locales/de-DE/chat.json +7 -1
- package/locales/de-DE/components.json +2 -0
- package/locales/de-DE/models.json +3 -0
- package/locales/en-US/chat.json +7 -1
- package/locales/en-US/components.json +2 -0
- package/locales/en-US/models.json +3 -0
- package/locales/es-ES/chat.json +7 -1
- package/locales/es-ES/components.json +2 -0
- package/locales/es-ES/models.json +3 -0
- package/locales/fa-IR/chat.json +7 -1
- package/locales/fa-IR/components.json +2 -0
- package/locales/fa-IR/models.json +3 -0
- package/locales/fr-FR/chat.json +7 -1
- package/locales/fr-FR/components.json +2 -0
- package/locales/fr-FR/models.json +3 -0
- package/locales/it-IT/chat.json +7 -1
- package/locales/it-IT/components.json +2 -0
- package/locales/it-IT/models.json +3 -0
- package/locales/ja-JP/chat.json +7 -1
- package/locales/ja-JP/components.json +2 -0
- package/locales/ja-JP/models.json +3 -0
- package/locales/ko-KR/chat.json +7 -1
- package/locales/ko-KR/components.json +2 -0
- package/locales/ko-KR/models.json +3 -0
- package/locales/nl-NL/chat.json +7 -1
- package/locales/nl-NL/components.json +2 -0
- package/locales/nl-NL/models.json +3 -0
- package/locales/pl-PL/chat.json +7 -1
- package/locales/pl-PL/components.json +2 -0
- package/locales/pl-PL/models.json +3 -0
- package/locales/pt-BR/chat.json +7 -1
- package/locales/pt-BR/components.json +2 -0
- package/locales/pt-BR/models.json +3 -0
- package/locales/ru-RU/chat.json +7 -1
- package/locales/ru-RU/components.json +2 -0
- package/locales/ru-RU/models.json +3 -0
- package/locales/tr-TR/chat.json +7 -1
- package/locales/tr-TR/components.json +2 -0
- package/locales/tr-TR/models.json +3 -0
- package/locales/vi-VN/chat.json +7 -1
- package/locales/vi-VN/components.json +2 -0
- package/locales/vi-VN/models.json +3 -0
- package/locales/zh-CN/chat.json +7 -1
- package/locales/zh-CN/components.json +3 -1
- package/locales/zh-CN/models.json +3 -0
- package/locales/zh-TW/chat.json +7 -1
- package/locales/zh-TW/components.json +2 -0
- package/locales/zh-TW/models.json +3 -0
- package/package.json +3 -3
- package/packages/web-crawler/package.json +1 -1
- package/packages/web-crawler/src/crawImpl/__tests__/browserless.test.ts +1 -1
- package/packages/web-crawler/src/crawImpl/browserless.ts +1 -1
- package/packages/web-crawler/src/crawImpl/naive.ts +1 -1
- package/packages/web-crawler/src/crawler.ts +11 -1
- package/packages/web-crawler/src/utils/__snapshots__/htmlToMarkdown.test.ts.snap +2 -382
- package/packages/web-crawler/src/utils/htmlToMarkdown.ts +32 -2
- package/src/app/(backend)/webapi/chat/[provider]/route.test.ts +4 -1
- package/src/app/(backend)/webapi/chat/[provider]/route.ts +5 -1
- package/src/app/[variants]/(main)/chat/(workspace)/@conversation/features/ChatList/WelcomeChatItem/InboxWelcome/AgentsSuggest.tsx +5 -1
- package/src/{features/Conversation/Messages/Assistant/Tool/Inspector/Loader.tsx → components/CircleLoader/index.tsx} +4 -3
- package/src/config/modelProviders/openai.ts +3 -0
- package/src/config/tools.ts +2 -0
- package/src/const/settings/agent.ts +6 -0
- package/src/database/client/db.ts +3 -3
- package/src/features/ChatInput/ActionBar/Search/FCSearchModel.tsx +56 -0
- package/src/features/ChatInput/ActionBar/Search/FunctionCallingModelSelect/index.tsx +85 -0
- package/src/features/ChatInput/ActionBar/Search/SwitchPanel.tsx +9 -23
- package/src/features/Conversation/Extras/Usage/UsageDetail/ModelCard.tsx +15 -23
- package/src/features/Conversation/Extras/Usage/UsageDetail/pricing.ts +26 -0
- package/src/features/Conversation/Extras/Usage/UsageDetail/tokens.test.ts +4 -4
- package/src/features/Conversation/Extras/Usage/UsageDetail/tokens.ts +15 -10
- package/src/features/Conversation/Messages/Assistant/IntentUnderstanding.tsx +25 -0
- package/src/features/Conversation/Messages/Assistant/Tool/Inspector/ToolTitle.tsx +1 -2
- package/src/features/Conversation/Messages/Assistant/index.tsx +18 -9
- package/src/features/Conversation/components/MarkdownElements/LobeArtifact/Render/index.tsx +51 -52
- package/src/features/ModelSwitchPanel/index.tsx +34 -8
- package/src/hooks/useAgentEnableSearch.ts +1 -9
- package/src/libs/agent-runtime/anthropic/index.ts +5 -2
- package/src/locales/default/chat.ts +7 -3
- package/src/locales/default/components.ts +3 -1
- package/src/server/routers/tools/search.ts +8 -1
- package/src/services/__tests__/chat.test.ts +124 -0
- package/src/services/chat.ts +82 -49
- package/src/services/session/type.ts +1 -1
- package/src/store/agent/slices/chat/selectors/__snapshots__/agent.test.ts.snap +4 -0
- package/src/store/agent/slices/chat/selectors/chatConfig.ts +5 -1
- package/src/store/chat/slices/aiChat/actions/__tests__/generateAIChat.test.ts +12 -4
- package/src/store/chat/slices/aiChat/actions/generateAIChat.ts +113 -13
- package/src/store/chat/slices/aiChat/initialState.ts +2 -0
- package/src/store/chat/slices/aiChat/selectors.ts +8 -1
- package/src/store/chat/slices/message/action.ts +9 -1
- package/src/store/chat/slices/plugin/action.ts +6 -4
- package/src/store/user/slices/settings/selectors/__snapshots__/settings.test.ts.snap +4 -0
- package/src/types/agent/chatConfig.ts +6 -0
- package/src/types/openai/chat.ts +0 -1
- package/src/utils/fetch/__tests__/fetchSSE.test.ts +3 -2
- package/src/utils/fetch/fetchSSE.ts +1 -1
@@ -0,0 +1,85 @@
|
|
1
|
+
import { Select, SelectProps } from 'antd';
|
2
|
+
import { createStyles } from 'antd-style';
|
3
|
+
import { memo, useMemo } from 'react';
|
4
|
+
|
5
|
+
import { ModelItemRender, ProviderItemRender } from '@/components/ModelSelect';
|
6
|
+
import { useEnabledChatModels } from '@/hooks/useEnabledChatModels';
|
7
|
+
import { WorkingModel } from '@/types/agent';
|
8
|
+
import { EnabledProviderWithModels } from '@/types/aiProvider';
|
9
|
+
|
10
|
+
const useStyles = createStyles(({ css, prefixCls }) => ({
|
11
|
+
select: css`
|
12
|
+
&.${prefixCls}-select-dropdown .${prefixCls}-select-item-option-grouped {
|
13
|
+
padding-inline-start: 12px;
|
14
|
+
}
|
15
|
+
`,
|
16
|
+
}));
|
17
|
+
|
18
|
+
interface ModelOption {
|
19
|
+
label: any;
|
20
|
+
provider: string;
|
21
|
+
value: string;
|
22
|
+
}
|
23
|
+
|
24
|
+
interface ModelSelectProps {
|
25
|
+
onChange?: (props: WorkingModel) => void;
|
26
|
+
showAbility?: boolean;
|
27
|
+
value?: WorkingModel;
|
28
|
+
}
|
29
|
+
|
30
|
+
const ModelSelect = memo<ModelSelectProps>(({ value, onChange }) => {
|
31
|
+
const enabledList = useEnabledChatModels();
|
32
|
+
|
33
|
+
const { styles } = useStyles();
|
34
|
+
|
35
|
+
const options = useMemo<SelectProps['options']>(() => {
|
36
|
+
const getChatModels = (provider: EnabledProviderWithModels) =>
|
37
|
+
provider.children
|
38
|
+
.filter((model) => !!model.abilities.functionCall)
|
39
|
+
.map((model) => ({
|
40
|
+
label: <ModelItemRender {...model} {...model.abilities} showInfoTag={false} />,
|
41
|
+
provider: provider.id,
|
42
|
+
value: `${provider.id}/${model.id}`,
|
43
|
+
}));
|
44
|
+
|
45
|
+
if (enabledList.length === 1) {
|
46
|
+
const provider = enabledList[0];
|
47
|
+
|
48
|
+
return getChatModels(provider);
|
49
|
+
}
|
50
|
+
|
51
|
+
return enabledList
|
52
|
+
.filter((p) => !!getChatModels(p).length)
|
53
|
+
.map((provider) => {
|
54
|
+
const options = getChatModels(provider);
|
55
|
+
|
56
|
+
return {
|
57
|
+
label: (
|
58
|
+
<ProviderItemRender
|
59
|
+
logo={provider.logo}
|
60
|
+
name={provider.name}
|
61
|
+
provider={provider.id}
|
62
|
+
source={provider.source}
|
63
|
+
/>
|
64
|
+
),
|
65
|
+
options,
|
66
|
+
};
|
67
|
+
});
|
68
|
+
}, [enabledList]);
|
69
|
+
|
70
|
+
return (
|
71
|
+
<Select
|
72
|
+
onChange={(value, option) => {
|
73
|
+
const model = value.split('/').slice(1).join('/');
|
74
|
+
onChange?.({ model, provider: (option as unknown as ModelOption).provider });
|
75
|
+
}}
|
76
|
+
options={options}
|
77
|
+
popupClassName={styles.select}
|
78
|
+
popupMatchSelectWidth={false}
|
79
|
+
value={`${value?.provider}/${value?.model}`}
|
80
|
+
variant={'filled'}
|
81
|
+
/>
|
82
|
+
);
|
83
|
+
});
|
84
|
+
|
85
|
+
export default ModelSelect;
|
@@ -12,6 +12,7 @@ import { agentChatConfigSelectors, agentSelectors } from '@/store/agent/slices/c
|
|
12
12
|
import { aiModelSelectors, useAiInfraStore } from '@/store/aiInfra';
|
13
13
|
import { SearchMode } from '@/types/search';
|
14
14
|
|
15
|
+
import FCSearchModel from './FCSearchModel';
|
15
16
|
import ModelBuiltinSearch from './ModelBuiltinSearch';
|
16
17
|
|
17
18
|
const { Text } = Typography;
|
@@ -38,10 +39,6 @@ const useStyles = createStyles(({ css, token }) => ({
|
|
38
39
|
font-size: 12px;
|
39
40
|
color: ${token.colorTextSecondary};
|
40
41
|
`,
|
41
|
-
disable: css`
|
42
|
-
cursor: not-allowed;
|
43
|
-
opacity: 0.45;
|
44
|
-
`,
|
45
42
|
iconWrapper: css`
|
46
43
|
display: flex;
|
47
44
|
flex-shrink: 0;
|
@@ -80,8 +77,7 @@ const useStyles = createStyles(({ css, token }) => ({
|
|
80
77
|
`,
|
81
78
|
}));
|
82
79
|
|
83
|
-
const Item = memo<NetworkOption>(({ value, description, icon, label
|
84
|
-
const { t } = useTranslation('chat');
|
80
|
+
const Item = memo<NetworkOption>(({ value, description, icon, label }) => {
|
85
81
|
const { styles } = useStyles();
|
86
82
|
const [mode, updateAgentChatConfig] = useAgentStore((s) => [
|
87
83
|
agentChatConfigSelectors.agentSearchMode(s),
|
@@ -96,12 +92,12 @@ const Item = memo<NetworkOption>(({ value, description, icon, label, disable })
|
|
96
92
|
key={value}
|
97
93
|
onClick={() => updateAgentChatConfig({ searchMode: value })}
|
98
94
|
>
|
99
|
-
<Flexbox
|
95
|
+
<Flexbox gap={8} horizontal>
|
100
96
|
<div className={styles.iconWrapper}>{icon}</div>
|
101
97
|
<div className={styles.content}>
|
102
98
|
<div className={styles.title}>{label}</div>
|
103
99
|
<Text className={styles.description} type="secondary">
|
104
|
-
{
|
100
|
+
{description}
|
105
101
|
</Text>
|
106
102
|
</div>
|
107
103
|
</Flexbox>
|
@@ -132,34 +128,24 @@ const AINetworkSettings = memo<AINetworkSettingsProps>(() => {
|
|
132
128
|
label: t('search.mode.off.title'),
|
133
129
|
value: 'off',
|
134
130
|
},
|
135
|
-
// 等应用层联网功能做好以后再开启
|
136
|
-
// {
|
137
|
-
// description: t('search.mode.on.desc'),
|
138
|
-
// icon: <WifiOutlined />,
|
139
|
-
// label: t('search.mode.on.title'),
|
140
|
-
// value: 'on',
|
141
|
-
// },
|
142
131
|
{
|
143
132
|
description: t('search.mode.auto.desc'),
|
144
|
-
disable: !supportFC,
|
145
133
|
icon: <Icon icon={SparklesIcon} />,
|
146
134
|
label: t('search.mode.auto.title'),
|
147
135
|
value: 'auto',
|
148
136
|
},
|
149
137
|
];
|
150
138
|
|
139
|
+
const showDivider = isModelHasBuiltinSearchConfig || !supportFC;
|
140
|
+
|
151
141
|
return (
|
152
142
|
<Flexbox gap={8}>
|
153
143
|
{options.map((option) => (
|
154
144
|
<Item {...option} key={option.value} />
|
155
145
|
))}
|
156
|
-
|
157
|
-
{isModelHasBuiltinSearchConfig &&
|
158
|
-
|
159
|
-
<Divider style={{ margin: 0, paddingInline: 12 }} />
|
160
|
-
<ModelBuiltinSearch />
|
161
|
-
</>
|
162
|
-
)}
|
146
|
+
{showDivider && <Divider style={{ margin: 0, paddingInline: 12 }} />}
|
147
|
+
{isModelHasBuiltinSearchConfig && <ModelBuiltinSearch />}
|
148
|
+
{!supportFC && <FCSearchModel />}
|
163
149
|
</Flexbox>
|
164
150
|
);
|
165
151
|
});
|
@@ -7,11 +7,10 @@ import { memo } from 'react';
|
|
7
7
|
import { useTranslation } from 'react-i18next';
|
8
8
|
import { Flexbox } from 'react-layout-kit';
|
9
9
|
|
10
|
+
import { getPrice } from '@/features/Conversation/Extras/Usage/UsageDetail/pricing';
|
10
11
|
import { useGlobalStore } from '@/store/global';
|
11
12
|
import { systemStatusSelectors } from '@/store/global/selectors';
|
12
13
|
import { LobeDefaultAiModelListItem } from '@/types/aiModel';
|
13
|
-
import { ModelPriceCurrency } from '@/types/llm';
|
14
|
-
import { formatPriceByCurrency } from '@/utils/format';
|
15
14
|
|
16
15
|
export const useStyles = createStyles(({ css, token }) => {
|
17
16
|
return {
|
@@ -40,19 +39,8 @@ const ModelCard = memo<ModelCardProps>(({ pricing, id, provider, displayName })
|
|
40
39
|
const isShowCredit = useGlobalStore(systemStatusSelectors.isShowCredit) && !!pricing;
|
41
40
|
const updateSystemStatus = useGlobalStore((s) => s.updateSystemStatus);
|
42
41
|
|
43
|
-
const
|
44
|
-
|
45
|
-
pricing?.cachedInput,
|
46
|
-
pricing?.currency as ModelPriceCurrency,
|
47
|
-
);
|
48
|
-
const writeCacheInputPrice = formatPriceByCurrency(
|
49
|
-
pricing?.writeCacheInput,
|
50
|
-
pricing?.currency as ModelPriceCurrency,
|
51
|
-
);
|
52
|
-
const outputPrice = formatPriceByCurrency(
|
53
|
-
pricing?.output,
|
54
|
-
pricing?.currency as ModelPriceCurrency,
|
55
|
-
);
|
42
|
+
const formatPrice = getPrice(pricing || {});
|
43
|
+
|
56
44
|
return (
|
57
45
|
<Flexbox gap={8}>
|
58
46
|
<Flexbox
|
@@ -103,37 +91,41 @@ const ModelCard = memo<ModelCardProps>(({ pricing, id, provider, displayName })
|
|
103
91
|
{pricing?.cachedInput && (
|
104
92
|
<Tooltip
|
105
93
|
title={t('messages.modelCard.pricing.inputCachedTokens', {
|
106
|
-
amount:
|
94
|
+
amount: formatPrice.cachedInput,
|
107
95
|
})}
|
108
96
|
>
|
109
97
|
<Flexbox gap={2} horizontal>
|
110
98
|
<Icon icon={CircleFadingArrowUp} />
|
111
|
-
{
|
99
|
+
{formatPrice.cachedInput}
|
112
100
|
</Flexbox>
|
113
101
|
</Tooltip>
|
114
102
|
)}
|
115
103
|
{pricing?.writeCacheInput && (
|
116
104
|
<Tooltip
|
117
105
|
title={t('messages.modelCard.pricing.writeCacheInputTokens', {
|
118
|
-
amount:
|
106
|
+
amount: formatPrice.writeCacheInput,
|
119
107
|
})}
|
120
108
|
>
|
121
109
|
<Flexbox gap={2} horizontal>
|
122
110
|
<Icon icon={BookUp2Icon} />
|
123
|
-
{
|
111
|
+
{formatPrice.writeCacheInput}
|
124
112
|
</Flexbox>
|
125
113
|
</Tooltip>
|
126
114
|
)}
|
127
|
-
<Tooltip
|
115
|
+
<Tooltip
|
116
|
+
title={t('messages.modelCard.pricing.inputTokens', { amount: formatPrice.input })}
|
117
|
+
>
|
128
118
|
<Flexbox gap={2} horizontal>
|
129
119
|
<Icon icon={ArrowUpFromDot} />
|
130
|
-
{
|
120
|
+
{formatPrice.input}
|
131
121
|
</Flexbox>
|
132
122
|
</Tooltip>
|
133
|
-
<Tooltip
|
123
|
+
<Tooltip
|
124
|
+
title={t('messages.modelCard.pricing.outputTokens', { amount: formatPrice.output })}
|
125
|
+
>
|
134
126
|
<Flexbox gap={2} horizontal>
|
135
127
|
<Icon icon={ArrowDownToDot} />
|
136
|
-
{
|
128
|
+
{formatPrice.output}
|
137
129
|
</Flexbox>
|
138
130
|
</Tooltip>
|
139
131
|
</Flexbox>
|
@@ -0,0 +1,26 @@
|
|
1
|
+
import { ChatModelPricing } from '@/types/aiModel';
|
2
|
+
import { ModelPriceCurrency } from '@/types/llm';
|
3
|
+
import { formatPriceByCurrency } from '@/utils/format';
|
4
|
+
|
5
|
+
export const getPrice = (pricing: ChatModelPricing) => {
|
6
|
+
const inputPrice = formatPriceByCurrency(pricing?.input, pricing?.currency as ModelPriceCurrency);
|
7
|
+
const cachedInputPrice = formatPriceByCurrency(
|
8
|
+
pricing?.cachedInput,
|
9
|
+
pricing?.currency as ModelPriceCurrency,
|
10
|
+
);
|
11
|
+
const writeCacheInputPrice = formatPriceByCurrency(
|
12
|
+
pricing?.writeCacheInput,
|
13
|
+
pricing?.currency as ModelPriceCurrency,
|
14
|
+
);
|
15
|
+
const outputPrice = formatPriceByCurrency(
|
16
|
+
pricing?.output,
|
17
|
+
pricing?.currency as ModelPriceCurrency,
|
18
|
+
);
|
19
|
+
|
20
|
+
return {
|
21
|
+
cachedInput: Number(cachedInputPrice),
|
22
|
+
input: Number(inputPrice),
|
23
|
+
output: Number(outputPrice),
|
24
|
+
writeCacheInput: Number(writeCacheInputPrice),
|
25
|
+
};
|
26
|
+
};
|
@@ -70,7 +70,7 @@ describe('getDetailsToken', () => {
|
|
70
70
|
const result = getDetailsToken(usage, mockModelCard);
|
71
71
|
|
72
72
|
expect(result.inputCached).toEqual({
|
73
|
-
credit:
|
73
|
+
credit: 1,
|
74
74
|
token: 50,
|
75
75
|
});
|
76
76
|
|
@@ -165,12 +165,12 @@ describe('getDetailsToken', () => {
|
|
165
165
|
const result = getDetailsToken(usage, mockModelCard);
|
166
166
|
|
167
167
|
// uncachedInput: (200 - 50) * 0.01 = 1.5 -> 2
|
168
|
-
// cachedInput: 50 * 0.005 = 0.25 ->
|
168
|
+
// cachedInput: 50 * 0.005 = 0.25 -> 1
|
169
169
|
// totalOutput: 300 * 0.02 = 6
|
170
|
-
// totalCredit = 2 +
|
170
|
+
// totalCredit = 2 + 1 + 6 = 9
|
171
171
|
|
172
172
|
expect(result.totalTokens).toEqual({
|
173
|
-
credit:
|
173
|
+
credit: 9,
|
174
174
|
token: 500,
|
175
175
|
});
|
176
176
|
});
|
@@ -1,6 +1,8 @@
|
|
1
|
-
import { LobeDefaultAiModelListItem } from '@/types/aiModel';
|
1
|
+
import { ChatModelPricing, LobeDefaultAiModelListItem } from '@/types/aiModel';
|
2
2
|
import { ModelTokensUsage } from '@/types/message';
|
3
3
|
|
4
|
+
import { getPrice } from './pricing';
|
5
|
+
|
4
6
|
const calcCredit = (token: number, pricing?: number) => {
|
5
7
|
if (!pricing) return '-';
|
6
8
|
|
@@ -29,23 +31,26 @@ export const getDetailsToken = (
|
|
29
31
|
? usage?.inputCacheMissTokens
|
30
32
|
: totalInputTokens - (inputCacheTokens || 0);
|
31
33
|
|
34
|
+
// Pricing
|
35
|
+
const formatPrice = getPrice(modelCard?.pricing as ChatModelPricing);
|
36
|
+
|
32
37
|
const inputCacheMissCredit = (
|
33
|
-
!!inputCacheMissTokens ? calcCredit(inputCacheMissTokens,
|
38
|
+
!!inputCacheMissTokens ? calcCredit(inputCacheMissTokens, formatPrice.input) : 0
|
34
39
|
) as number;
|
35
40
|
|
36
41
|
const inputCachedCredit = (
|
37
|
-
!!inputCacheTokens ? calcCredit(inputCacheTokens,
|
42
|
+
!!inputCacheTokens ? calcCredit(inputCacheTokens, formatPrice.cachedInput) : 0
|
38
43
|
) as number;
|
39
44
|
|
40
45
|
const inputWriteCachedCredit = !!inputWriteCacheTokens
|
41
|
-
? (calcCredit(inputWriteCacheTokens,
|
46
|
+
? (calcCredit(inputWriteCacheTokens, formatPrice.writeCacheInput) as number)
|
42
47
|
: 0;
|
43
48
|
|
44
49
|
const totalOutputCredit = (
|
45
|
-
!!totalOutputTokens ? calcCredit(totalOutputTokens,
|
50
|
+
!!totalOutputTokens ? calcCredit(totalOutputTokens, formatPrice.output) : 0
|
46
51
|
) as number;
|
47
52
|
const totalInputCredit = (
|
48
|
-
!!totalInputTokens ? calcCredit(totalInputTokens,
|
53
|
+
!!totalInputTokens ? calcCredit(totalInputTokens, formatPrice.output) : 0
|
49
54
|
) as number;
|
50
55
|
|
51
56
|
const totalCredit =
|
@@ -69,13 +74,13 @@ export const getDetailsToken = (
|
|
69
74
|
: undefined,
|
70
75
|
inputCitation: !!usage.inputCitationTokens
|
71
76
|
? {
|
72
|
-
credit: calcCredit(usage.inputCitationTokens,
|
77
|
+
credit: calcCredit(usage.inputCitationTokens, formatPrice.input),
|
73
78
|
token: usage.inputCitationTokens,
|
74
79
|
}
|
75
80
|
: undefined,
|
76
81
|
inputText: !!inputTextTokens
|
77
82
|
? {
|
78
|
-
credit: calcCredit(inputTextTokens,
|
83
|
+
credit: calcCredit(inputTextTokens, formatPrice.input),
|
79
84
|
token: inputTextTokens,
|
80
85
|
}
|
81
86
|
: undefined,
|
@@ -89,13 +94,13 @@ export const getDetailsToken = (
|
|
89
94
|
: undefined,
|
90
95
|
outputReasoning: !!outputReasoningTokens
|
91
96
|
? {
|
92
|
-
credit: calcCredit(outputReasoningTokens,
|
97
|
+
credit: calcCredit(outputReasoningTokens, formatPrice.output),
|
93
98
|
token: outputReasoningTokens,
|
94
99
|
}
|
95
100
|
: undefined,
|
96
101
|
outputText: !!outputTextTokens
|
97
102
|
? {
|
98
|
-
credit: calcCredit(outputTextTokens,
|
103
|
+
credit: calcCredit(outputTextTokens, formatPrice.output),
|
99
104
|
token: outputTextTokens,
|
100
105
|
}
|
101
106
|
: undefined,
|
@@ -0,0 +1,25 @@
|
|
1
|
+
import { createStyles } from 'antd-style';
|
2
|
+
import { useTranslation } from 'react-i18next';
|
3
|
+
import { Flexbox } from 'react-layout-kit';
|
4
|
+
|
5
|
+
import CircleLoader from '@/components/CircleLoader';
|
6
|
+
import { shinyTextStylish } from '@/styles/loading';
|
7
|
+
|
8
|
+
const useStyles = createStyles(({ token }) => ({
|
9
|
+
shinyText: shinyTextStylish(token),
|
10
|
+
}));
|
11
|
+
|
12
|
+
const IntentUnderstanding = () => {
|
13
|
+
const { styles } = useStyles();
|
14
|
+
const { t } = useTranslation('chat');
|
15
|
+
|
16
|
+
return (
|
17
|
+
<Flexbox align={'center'} gap={8} horizontal>
|
18
|
+
<CircleLoader />
|
19
|
+
<Flexbox className={styles.shinyText} horizontal>
|
20
|
+
{t('intentUnderstanding.title')}
|
21
|
+
</Flexbox>
|
22
|
+
</Flexbox>
|
23
|
+
);
|
24
|
+
};
|
25
|
+
export default IntentUnderstanding;
|
@@ -6,6 +6,7 @@ import { memo } from 'react';
|
|
6
6
|
import { useTranslation } from 'react-i18next';
|
7
7
|
import { Flexbox } from 'react-layout-kit';
|
8
8
|
|
9
|
+
import Loader from '@/components/CircleLoader';
|
9
10
|
import PluginAvatar from '@/features/PluginAvatar';
|
10
11
|
import { useChatStore } from '@/store/chat';
|
11
12
|
import { chatSelectors } from '@/store/chat/selectors';
|
@@ -14,8 +15,6 @@ import { toolSelectors } from '@/store/tool/selectors';
|
|
14
15
|
import { shinyTextStylish } from '@/styles/loading';
|
15
16
|
import { WebBrowsingManifest } from '@/tools/web-browsing';
|
16
17
|
|
17
|
-
import Loader from './Loader';
|
18
|
-
|
19
18
|
export const useStyles = createStyles(({ css, token }) => ({
|
20
19
|
apiName: css`
|
21
20
|
overflow: hidden;
|
@@ -8,6 +8,7 @@ import { ChatMessage } from '@/types/message';
|
|
8
8
|
|
9
9
|
import { DefaultMessage } from '../Default';
|
10
10
|
import FileChunks from './FileChunks';
|
11
|
+
import IntentUnderstanding from './IntentUnderstanding';
|
11
12
|
import Reasoning from './Reasoning';
|
12
13
|
import SearchGrounding from './SearchGrounding';
|
13
14
|
import Tool from './Tool';
|
@@ -24,6 +25,8 @@ export const AssistantMessage = memo<
|
|
24
25
|
|
25
26
|
const isReasoning = useChatStore(aiChatSelectors.isMessageInReasoning(id));
|
26
27
|
|
28
|
+
const isIntentUnderstanding = useChatStore(aiChatSelectors.isIntentUnderstanding(id));
|
29
|
+
|
27
30
|
const showSearch = !!search && !!search.citations?.length;
|
28
31
|
|
29
32
|
// remove \n to avoid empty content
|
@@ -32,6 +35,8 @@ export const AssistantMessage = memo<
|
|
32
35
|
(!!props.reasoning && props.reasoning.content?.trim() !== '') ||
|
33
36
|
(!props.reasoning && isReasoning);
|
34
37
|
|
38
|
+
const showFileChunks = !!chunksList && chunksList.length > 0;
|
39
|
+
|
35
40
|
return editing ? (
|
36
41
|
<DefaultMessage
|
37
42
|
content={content}
|
@@ -44,16 +49,20 @@ export const AssistantMessage = memo<
|
|
44
49
|
{showSearch && (
|
45
50
|
<SearchGrounding citations={search?.citations} searchQueries={search?.searchQueries} />
|
46
51
|
)}
|
47
|
-
{
|
52
|
+
{showFileChunks && <FileChunks data={chunksList} />}
|
48
53
|
{showReasoning && <Reasoning {...props.reasoning} id={id} />}
|
49
|
-
{
|
50
|
-
<
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
54
|
+
{isIntentUnderstanding ? (
|
55
|
+
<IntentUnderstanding />
|
56
|
+
) : (
|
57
|
+
content && (
|
58
|
+
<DefaultMessage
|
59
|
+
addIdOnDOM={false}
|
60
|
+
content={content}
|
61
|
+
id={id}
|
62
|
+
isToolCallGenerating={isToolCallGenerating}
|
63
|
+
{...props}
|
64
|
+
/>
|
65
|
+
)
|
57
66
|
)}
|
58
67
|
{tools && (
|
59
68
|
<Flexbox gap={8}>
|
@@ -64,16 +64,14 @@ const Render = memo<ArtifactProps>(({ identifier, title, type, language, childre
|
|
64
64
|
|
65
65
|
const inThread = useContext(InPortalThreadContext);
|
66
66
|
const { message } = App.useApp();
|
67
|
-
const [isGenerating, isArtifactTagClosed,
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
];
|
76
|
-
});
|
67
|
+
const [isGenerating, isArtifactTagClosed, openArtifact, closeArtifact] = useChatStore((s) => {
|
68
|
+
return [
|
69
|
+
chatSelectors.isMessageGenerating(id)(s),
|
70
|
+
chatPortalSelectors.isArtifactTagClosed(id)(s),
|
71
|
+
s.openArtifact,
|
72
|
+
s.closeArtifact,
|
73
|
+
];
|
74
|
+
});
|
77
75
|
|
78
76
|
const openArtifactUI = () => {
|
79
77
|
openArtifact({ id, identifier, language, title, type });
|
@@ -86,52 +84,53 @@ const Render = memo<ArtifactProps>(({ identifier, title, type, language, childre
|
|
86
84
|
}, [isGenerating, hasChildren, str, identifier, title, type, id, language]);
|
87
85
|
|
88
86
|
return (
|
89
|
-
<
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
87
|
+
<Flexbox
|
88
|
+
className={styles.container}
|
89
|
+
gap={16}
|
90
|
+
onClick={() => {
|
91
|
+
const currentArtifactMessageId = chatPortalSelectors.artifactMessageId(
|
92
|
+
useChatStore.getState(),
|
93
|
+
);
|
94
|
+
if (currentArtifactMessageId === id) {
|
95
|
+
closeArtifact();
|
96
|
+
} else {
|
97
|
+
if (inThread) {
|
98
|
+
message.info(t('artifact.inThread'));
|
99
|
+
return;
|
102
100
|
}
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
<
|
111
|
-
|
112
|
-
|
113
|
-
|
101
|
+
openArtifactUI();
|
102
|
+
}
|
103
|
+
}}
|
104
|
+
width={'100%'}
|
105
|
+
>
|
106
|
+
<Flexbox align={'center'} flex={1} horizontal>
|
107
|
+
<Center className={styles.avatar} height={64} horizontal width={64}>
|
108
|
+
<ArtifactIcon type={type} />
|
109
|
+
</Center>
|
110
|
+
<Flexbox gap={4} paddingBlock={8} paddingInline={12}>
|
111
|
+
{!title && isGenerating ? (
|
112
|
+
<Flexbox className={cx(dotLoading)} horizontal>
|
113
|
+
{t('artifact.generating')}
|
114
|
+
</Flexbox>
|
115
|
+
) : (
|
116
|
+
<Flexbox className={cx(styles.title)}>{title || t('artifact.unknownTitle')}</Flexbox>
|
117
|
+
)}
|
118
|
+
{hasChildren && (
|
119
|
+
<Flexbox className={styles.desc} horizontal>
|
120
|
+
{identifier} ·{' '}
|
121
|
+
<Flexbox gap={2} horizontal>
|
122
|
+
{!isArtifactTagClosed && (
|
123
|
+
<div>
|
124
|
+
<Icon icon={Loader2} spin />
|
125
|
+
</div>
|
126
|
+
)}
|
127
|
+
{str?.length}
|
114
128
|
</Flexbox>
|
115
|
-
|
116
|
-
|
117
|
-
)}
|
118
|
-
{hasChildren && (
|
119
|
-
<Flexbox className={styles.desc} horizontal>
|
120
|
-
{identifier} ·{' '}
|
121
|
-
<Flexbox gap={2} horizontal>
|
122
|
-
{!isArtifactTagClosed && (
|
123
|
-
<div>
|
124
|
-
<Icon icon={Loader2} spin />
|
125
|
-
</div>
|
126
|
-
)}
|
127
|
-
{str?.length}
|
128
|
-
</Flexbox>
|
129
|
-
</Flexbox>
|
130
|
-
)}
|
131
|
-
</Flexbox>
|
129
|
+
</Flexbox>
|
130
|
+
)}
|
132
131
|
</Flexbox>
|
133
132
|
</Flexbox>
|
134
|
-
</
|
133
|
+
</Flexbox>
|
135
134
|
);
|
136
135
|
});
|
137
136
|
|
@@ -1,8 +1,9 @@
|
|
1
|
-
import { Icon } from '@lobehub/ui';
|
1
|
+
import { ActionIcon, Icon } from '@lobehub/ui';
|
2
2
|
import { Dropdown } from 'antd';
|
3
3
|
import { createStyles } from 'antd-style';
|
4
4
|
import type { ItemType } from 'antd/es/menu/interface';
|
5
|
-
import { LucideArrowRight } from 'lucide-react';
|
5
|
+
import { LucideArrowRight, LucideBolt } from 'lucide-react';
|
6
|
+
import Link from 'next/link';
|
6
7
|
import { useRouter } from 'next/navigation';
|
7
8
|
import { PropsWithChildren, memo, useMemo } from 'react';
|
8
9
|
import { useTranslation } from 'react-i18next';
|
@@ -86,17 +87,42 @@ const ModelSwitchPanel = memo<PropsWithChildren>(({ children }) => {
|
|
86
87
|
return items;
|
87
88
|
};
|
88
89
|
|
90
|
+
if (enabledList.length === 0)
|
91
|
+
return [
|
92
|
+
{
|
93
|
+
key: `no-provider`,
|
94
|
+
label: (
|
95
|
+
<Flexbox gap={8} horizontal style={{ color: theme.colorTextTertiary }}>
|
96
|
+
{t('ModelSwitchPanel.emptyProvider')}
|
97
|
+
<Icon icon={LucideArrowRight} />
|
98
|
+
</Flexbox>
|
99
|
+
),
|
100
|
+
onClick: () => {
|
101
|
+
router.push(isDeprecatedEdition ? '/settings/llm' : `/settings/provider`);
|
102
|
+
},
|
103
|
+
},
|
104
|
+
];
|
105
|
+
|
89
106
|
// otherwise show with provider group
|
90
107
|
return enabledList.map((provider) => ({
|
91
108
|
children: getModelItems(provider),
|
92
109
|
key: provider.id,
|
93
110
|
label: (
|
94
|
-
<
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
111
|
+
<Flexbox horizontal justify="space-between">
|
112
|
+
<ProviderItemRender
|
113
|
+
logo={provider.logo}
|
114
|
+
name={provider.name}
|
115
|
+
provider={provider.id}
|
116
|
+
source={provider.source}
|
117
|
+
/>
|
118
|
+
<Link href={isDeprecatedEdition ? '/settings/llm' : `/settings/provider/${provider.id}`}>
|
119
|
+
<ActionIcon
|
120
|
+
icon={LucideBolt}
|
121
|
+
size={'small'}
|
122
|
+
title={t('ModelSwitchPanel.goToSettings')}
|
123
|
+
/>
|
124
|
+
</Link>
|
125
|
+
</Flexbox>
|
100
126
|
),
|
101
127
|
type: 'group',
|
102
128
|
}));
|