@lobehub/chat 1.69.6 → 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 +25 -0
- package/changelog/v1.json +9 -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/bg-BG/chat.json +7 -1
- package/locales/de-DE/chat.json +7 -1
- package/locales/en-US/chat.json +7 -1
- package/locales/es-ES/chat.json +7 -1
- package/locales/fa-IR/chat.json +7 -1
- package/locales/fr-FR/chat.json +7 -1
- package/locales/it-IT/chat.json +7 -1
- package/locales/ja-JP/chat.json +7 -1
- package/locales/ko-KR/chat.json +7 -1
- package/locales/nl-NL/chat.json +7 -1
- package/locales/pl-PL/chat.json +7 -1
- package/locales/pt-BR/chat.json +7 -1
- package/locales/ru-RU/chat.json +7 -1
- package/locales/tr-TR/chat.json +7 -1
- package/locales/vi-VN/chat.json +7 -1
- package/locales/zh-CN/chat.json +7 -1
- package/locales/zh-TW/chat.json +7 -1
- package/package.json +1 -1
- package/packages/web-crawler/src/crawler.ts +11 -1
- 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/{features/Conversation/Messages/Assistant/Tool/Inspector/Loader.tsx → components/CircleLoader/index.tsx} +4 -3
- package/src/config/tools.ts +2 -0
- package/src/const/settings/agent.ts +6 -0
- 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/ModelSwitchPanel/index.tsx +1 -4
- package/src/hooks/useAgentEnableSearch.ts +1 -9
- package/src/locales/default/chat.ts +7 -3
- package/src/server/routers/tools/search.ts +8 -1
- package/src/services/__tests__/chat.test.ts +1 -0
- package/src/services/chat.ts +63 -30
- 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
@@ -1,4 +1,5 @@
|
|
1
1
|
import { createStyles } from 'antd-style';
|
2
|
+
import { memo } from 'react';
|
2
3
|
|
3
4
|
export const useStyles = createStyles(({ css, token }, borderWidth: number = 2.5) => ({
|
4
5
|
background: css`
|
@@ -44,7 +45,7 @@ export const useStyles = createStyles(({ css, token }, borderWidth: number = 2.5
|
|
44
45
|
`,
|
45
46
|
}));
|
46
47
|
|
47
|
-
const
|
48
|
+
const CircleLoader = memo(() => {
|
48
49
|
const { styles } = useStyles();
|
49
50
|
|
50
51
|
return (
|
@@ -53,6 +54,6 @@ const Loader = () => {
|
|
53
54
|
<div className={styles.background} />
|
54
55
|
</div>
|
55
56
|
);
|
56
|
-
};
|
57
|
+
});
|
57
58
|
|
58
|
-
export default
|
59
|
+
export default CircleLoader;
|
package/src/config/tools.ts
CHANGED
@@ -4,10 +4,12 @@ import { z } from 'zod';
|
|
4
4
|
export const getToolsConfig = () => {
|
5
5
|
return createEnv({
|
6
6
|
runtimeEnv: {
|
7
|
+
CRAWLER_IMPLS: process.env.CRAWLER_IMPLS,
|
7
8
|
SEARXNG_URL: process.env.SEARXNG_URL,
|
8
9
|
},
|
9
10
|
|
10
11
|
server: {
|
12
|
+
CRAWLER_IMPLS: z.string().optional(),
|
11
13
|
SEARXNG_URL: z.string().url().optional(),
|
12
14
|
},
|
13
15
|
});
|
@@ -13,6 +13,11 @@ export const DEFAUTT_AGENT_TTS_CONFIG: LobeAgentTTSConfig = {
|
|
13
13
|
},
|
14
14
|
};
|
15
15
|
|
16
|
+
export const DEFAULT_AGENT_SEARCH_FC_MODEL = {
|
17
|
+
model: DEFAULT_MODEL,
|
18
|
+
provider: ModelProvider.OpenAI,
|
19
|
+
};
|
20
|
+
|
16
21
|
export const DEFAULT_AGENT_CHAT_CONFIG: LobeAgentChatConfig = {
|
17
22
|
autoCreateTopicThreshold: 2,
|
18
23
|
displayMode: 'chat',
|
@@ -22,6 +27,7 @@ export const DEFAULT_AGENT_CHAT_CONFIG: LobeAgentChatConfig = {
|
|
22
27
|
enableReasoning: false,
|
23
28
|
historyCount: 8,
|
24
29
|
reasoningBudgetToken: 1024,
|
30
|
+
searchFCModel: DEFAULT_AGENT_SEARCH_FC_MODEL,
|
25
31
|
searchMode: 'off',
|
26
32
|
};
|
27
33
|
|
@@ -0,0 +1,56 @@
|
|
1
|
+
import { createStyles } from 'antd-style';
|
2
|
+
import { useTranslation } from 'react-i18next';
|
3
|
+
import { Flexbox } from 'react-layout-kit';
|
4
|
+
|
5
|
+
import InfoTooltip from '@/components/InfoTooltip';
|
6
|
+
import { useAgentStore } from '@/store/agent';
|
7
|
+
import { agentChatConfigSelectors } from '@/store/agent/slices/chat';
|
8
|
+
|
9
|
+
import FunctionCallingModelSelect from './FunctionCallingModelSelect';
|
10
|
+
|
11
|
+
const useStyles = createStyles(({ css, token }) => ({
|
12
|
+
check: css`
|
13
|
+
margin-inline-start: 12px;
|
14
|
+
font-size: 16px;
|
15
|
+
color: ${token.colorPrimary};
|
16
|
+
`,
|
17
|
+
content: css`
|
18
|
+
flex: 1;
|
19
|
+
width: 230px;
|
20
|
+
`,
|
21
|
+
description: css`
|
22
|
+
width: 200px;
|
23
|
+
font-size: 12px;
|
24
|
+
color: ${token.colorTextSecondary};
|
25
|
+
`,
|
26
|
+
title: css`
|
27
|
+
font-size: 14px;
|
28
|
+
font-weight: 500;
|
29
|
+
color: ${token.colorText};
|
30
|
+
`,
|
31
|
+
}));
|
32
|
+
|
33
|
+
const FCSearchModel = () => {
|
34
|
+
const { t } = useTranslation('chat');
|
35
|
+
const { styles } = useStyles();
|
36
|
+
const [searchFCModel, updateAgentChatConfig] = useAgentStore((s) => [
|
37
|
+
agentChatConfigSelectors.searchFCModel(s),
|
38
|
+
s.updateAgentChatConfig,
|
39
|
+
]);
|
40
|
+
return (
|
41
|
+
<Flexbox distribution={'space-between'} gap={16} horizontal padding={8}>
|
42
|
+
<Flexbox align={'center'} gap={4} horizontal>
|
43
|
+
<Flexbox className={styles.title}>{t('search.searchModel.title')}</Flexbox>
|
44
|
+
<InfoTooltip title={t('search.searchModel.desc')} />
|
45
|
+
</Flexbox>
|
46
|
+
<FunctionCallingModelSelect
|
47
|
+
onChange={(value) => {
|
48
|
+
updateAgentChatConfig({ searchFCModel: value });
|
49
|
+
}}
|
50
|
+
value={searchFCModel}
|
51
|
+
/>
|
52
|
+
</Flexbox>
|
53
|
+
);
|
54
|
+
};
|
55
|
+
|
56
|
+
export default FCSearchModel;
|
@@ -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}>
|
@@ -115,10 +115,7 @@ const ModelSwitchPanel = memo<PropsWithChildren>(({ children }) => {
|
|
115
115
|
provider={provider.id}
|
116
116
|
source={provider.source}
|
117
117
|
/>
|
118
|
-
<Link
|
119
|
-
href={isDeprecatedEdition ? '/settings/llm' : `/settings/provider/${provider.id}`}
|
120
|
-
prefetch={false}
|
121
|
-
>
|
118
|
+
<Link href={isDeprecatedEdition ? '/settings/llm' : `/settings/provider/${provider.id}`}>
|
122
119
|
<ActionIcon
|
123
120
|
icon={LucideBolt}
|
124
121
|
size={'small'}
|
@@ -9,19 +9,11 @@ export const useAgentEnableSearch = () => {
|
|
9
9
|
agentChatConfigSelectors.agentSearchMode(s),
|
10
10
|
]);
|
11
11
|
|
12
|
-
const isModelSupportToolUse = useAiInfraStore(
|
13
|
-
aiModelSelectors.isModelSupportToolUse(model, provider),
|
14
|
-
);
|
15
12
|
const searchImpl = useAiInfraStore(aiModelSelectors.modelBuiltinSearchImpl(model, provider));
|
16
13
|
|
17
14
|
// 只要是内置的搜索实现,一定可以联网搜索
|
18
15
|
if (searchImpl === 'internal') return true;
|
19
16
|
|
20
17
|
// 如果是关闭状态,一定不能联网搜索
|
21
|
-
|
22
|
-
|
23
|
-
// 如果是智能模式,根据是否支持 Tool Calling 判断
|
24
|
-
if (agentSearchMode === 'auto') {
|
25
|
-
return isModelSupportToolUse;
|
26
|
-
}
|
18
|
+
return agentSearchMode !== 'off';
|
27
19
|
};
|
@@ -65,6 +65,9 @@ export default {
|
|
65
65
|
stop: '停止',
|
66
66
|
warp: '换行',
|
67
67
|
},
|
68
|
+
intentUnderstanding: {
|
69
|
+
title: '正在分析并理解意图您的意图...',
|
70
|
+
},
|
68
71
|
knowledgeBase: {
|
69
72
|
all: '所有内容',
|
70
73
|
allFiles: '所有文件',
|
@@ -142,13 +145,11 @@ export default {
|
|
142
145
|
searchQueries: '搜索关键词',
|
143
146
|
title: '已搜索到 {{count}} 个结果',
|
144
147
|
},
|
145
|
-
|
146
148
|
mode: {
|
147
149
|
auto: {
|
148
150
|
desc: '根据对话内容智能判断是否需要搜索',
|
149
151
|
title: '智能联网',
|
150
152
|
},
|
151
|
-
disable: '当前模型不支持函数调用,因此无法使用智能联网功能',
|
152
153
|
off: {
|
153
154
|
desc: '仅使用模型的基础知识,不进行网络搜索',
|
154
155
|
title: '关闭联网',
|
@@ -159,7 +160,10 @@ export default {
|
|
159
160
|
},
|
160
161
|
useModelBuiltin: '使用模型内置搜索引擎',
|
161
162
|
},
|
162
|
-
|
163
|
+
searchModel: {
|
164
|
+
desc: '当前模型不支持函数调用,因此需要搭配支持函数调用的模型才能联网搜索',
|
165
|
+
title: '搜索辅助模型',
|
166
|
+
},
|
163
167
|
title: '联网搜索',
|
164
168
|
},
|
165
169
|
searchAgentPlaceholder: '搜索助手...',
|