@lobehub/chat 1.90.3 → 1.91.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/.cursor/rules/backend-architecture.mdc +12 -9
- package/.cursor/rules/cursor-ux-optimize.mdc +1 -1
- package/.cursor/rules/define-database-model.mdc +1 -1
- package/.cursor/rules/drizzle-schema-style-guide.mdc +1 -1
- package/.cursor/rules/i18n/i18n.mdc +1 -1
- package/.cursor/rules/project-introduce.mdc +2 -1
- package/.cursor/rules/system-role.mdc +42 -0
- package/.cursor/rules/zustand-action-patterns.mdc +318 -0
- package/.cursor/rules/zustand-slice-organization.mdc +300 -0
- package/CHANGELOG.md +66 -0
- package/README.md +2 -2
- package/README.zh-CN.md +2 -2
- package/changelog/v1.json +24 -0
- package/docs/self-hosting/advanced/model-list.mdx +1 -1
- package/docs/self-hosting/advanced/model-list.zh-CN.mdx +1 -1
- package/docs/self-hosting/environment-variables/model-provider.mdx +2 -2
- package/docs/self-hosting/environment-variables/model-provider.zh-CN.mdx +2 -2
- package/package.json +44 -44
- package/packages/web-crawler/src/crawImpl/exa.ts +93 -0
- package/packages/web-crawler/src/crawImpl/firecrawl.ts +97 -0
- package/packages/web-crawler/src/crawImpl/index.ts +6 -0
- package/packages/web-crawler/src/crawImpl/tavily.ts +94 -0
- package/src/config/aiModels/modelscope.ts +3 -3
- package/src/config/modelProviders/modelscope.ts +3 -3
- package/src/database/client/migrations.json +10 -0
- package/src/database/migrations/0023_remove_param_and_doubao.sql +6 -0
- package/src/database/migrations/meta/0023_snapshot.json +5340 -0
- package/src/database/migrations/meta/_journal.json +7 -0
- package/src/features/PluginsUI/Render/utils/iframeOnReady.test.ts +1 -1
- package/src/features/PluginsUI/Render/utils/pluginSettings.test.ts +1 -1
- package/src/features/PluginsUI/Render/utils/pluginState.test.ts +1 -1
- package/src/libs/model-runtime/BaseAI.ts +3 -3
- package/src/libs/model-runtime/ModelRuntime.ts +2 -2
- package/src/libs/model-runtime/UniformRuntime/index.ts +2 -2
- package/src/libs/model-runtime/ai21/index.ts +2 -2
- package/src/libs/model-runtime/ai360/index.ts +2 -2
- package/src/libs/model-runtime/anthropic/index.ts +15 -11
- package/src/libs/model-runtime/azureOpenai/index.ts +2 -2
- package/src/libs/model-runtime/azureai/index.ts +4 -4
- package/src/libs/model-runtime/baichuan/index.ts +2 -2
- package/src/libs/model-runtime/bedrock/index.ts +4 -4
- package/src/libs/model-runtime/cloudflare/index.ts +2 -2
- package/src/libs/model-runtime/cohere/index.ts +2 -2
- package/src/libs/model-runtime/deepseek/index.ts +2 -2
- package/src/libs/model-runtime/fireworksai/index.ts +2 -2
- package/src/libs/model-runtime/giteeai/index.ts +2 -2
- package/src/libs/model-runtime/github/index.ts +2 -2
- package/src/libs/model-runtime/google/index.ts +7 -5
- package/src/libs/model-runtime/groq/index.ts +2 -2
- package/src/libs/model-runtime/higress/index.ts +2 -2
- package/src/libs/model-runtime/huggingface/index.ts +2 -2
- package/src/libs/model-runtime/hunyuan/index.ts +2 -2
- package/src/libs/model-runtime/index.ts +1 -1
- package/src/libs/model-runtime/infiniai/index.ts +2 -2
- package/src/libs/model-runtime/internlm/index.ts +7 -9
- package/src/libs/model-runtime/jina/index.ts +2 -2
- package/src/libs/model-runtime/lmstudio/index.ts +2 -2
- package/src/libs/model-runtime/minimax/index.ts +2 -2
- package/src/libs/model-runtime/mistral/index.ts +2 -2
- package/src/libs/model-runtime/modelscope/index.ts +2 -3
- package/src/libs/model-runtime/moonshot/index.ts +2 -2
- package/src/libs/model-runtime/novita/index.ts +2 -2
- package/src/libs/model-runtime/nvidia/index.ts +2 -2
- package/src/libs/model-runtime/ollama/index.ts +2 -2
- package/src/libs/model-runtime/openai/index.ts +3 -3
- package/src/libs/model-runtime/openrouter/index.ts +2 -2
- package/src/libs/model-runtime/perplexity/index.ts +2 -2
- package/src/libs/model-runtime/ppio/index.ts +2 -2
- package/src/libs/model-runtime/qiniu/index.ts +2 -2
- package/src/libs/model-runtime/qwen/index.ts +2 -2
- package/src/libs/model-runtime/sambanova/index.ts +2 -2
- package/src/libs/model-runtime/search1api/index.ts +2 -2
- package/src/libs/model-runtime/sensenova/index.ts +2 -2
- package/src/libs/model-runtime/siliconcloud/index.ts +2 -2
- package/src/libs/model-runtime/spark/index.ts +15 -13
- package/src/libs/model-runtime/stepfun/index.ts +2 -2
- package/src/libs/model-runtime/taichu/index.ts +2 -2
- package/src/libs/model-runtime/tencentcloud/index.ts +2 -2
- package/src/libs/model-runtime/togetherai/index.ts +2 -2
- package/src/libs/model-runtime/types/chat.ts +1 -1
- package/src/libs/model-runtime/upstage/index.ts +2 -2
- package/src/libs/model-runtime/utils/openaiCompatibleFactory/index.test.ts +7 -7
- package/src/libs/model-runtime/utils/openaiCompatibleFactory/index.ts +3 -3
- package/src/libs/model-runtime/vllm/index.ts +2 -2
- package/src/libs/model-runtime/volcengine/index.ts +2 -2
- package/src/libs/model-runtime/wenxin/index.ts +2 -2
- package/src/libs/model-runtime/xai/index.ts +6 -3
- package/src/libs/model-runtime/xinference/index.ts +2 -2
- package/src/libs/model-runtime/zeroone/index.ts +2 -2
- package/src/libs/model-runtime/zhipu/index.ts +2 -2
- package/src/middleware.ts +3 -1
- package/src/server/routers/tools/search.test.ts +2 -4
- package/src/server/services/search/impls/bocha/index.ts +124 -0
- package/src/server/services/search/impls/bocha/type.ts +47 -0
- package/src/server/services/search/impls/exa/index.ts +129 -0
- package/src/server/services/search/impls/exa/type.ts +39 -0
- package/src/server/services/search/impls/firecrawl/index.ts +128 -0
- package/src/server/services/search/impls/firecrawl/type.ts +35 -0
- package/src/server/services/search/impls/index.ts +31 -0
- package/src/server/services/search/impls/jina/index.ts +109 -0
- package/src/server/services/search/impls/jina/type.ts +26 -0
- package/src/server/services/search/impls/tavily/index.ts +124 -0
- package/src/server/services/search/impls/tavily/type.ts +36 -0
- package/src/store/chat/slices/aiChat/actions/__tests__/generateAIChat.test.ts +4 -2
- package/src/store/chat/slices/message/action.test.ts +2 -1
- package/src/store/chat/slices/topic/action.test.ts +3 -2
@@ -0,0 +1,26 @@
|
|
1
|
+
export interface JinaSearchParameters {
|
2
|
+
q: string;
|
3
|
+
}
|
4
|
+
|
5
|
+
interface JinaUsage {
|
6
|
+
tokens: number;
|
7
|
+
}
|
8
|
+
|
9
|
+
interface JinaMeta {
|
10
|
+
usage: JinaUsage;
|
11
|
+
}
|
12
|
+
|
13
|
+
interface JinaData {
|
14
|
+
content?: string;
|
15
|
+
description?: string;
|
16
|
+
title: string;
|
17
|
+
url: string;
|
18
|
+
usage?: JinaUsage;
|
19
|
+
}
|
20
|
+
|
21
|
+
export interface JinaResponse {
|
22
|
+
code?: number;
|
23
|
+
data: JinaData[];
|
24
|
+
meta?: JinaMeta;
|
25
|
+
status?: number;
|
26
|
+
}
|
@@ -0,0 +1,124 @@
|
|
1
|
+
import { TRPCError } from '@trpc/server';
|
2
|
+
import debug from 'debug';
|
3
|
+
import urlJoin from 'url-join';
|
4
|
+
|
5
|
+
import { SearchParams, UniformSearchResponse, UniformSearchResult } from '@/types/tool/search';
|
6
|
+
|
7
|
+
import { SearchServiceImpl } from '../type';
|
8
|
+
import { TavilySearchParameters, TavilyResponse } from './type';
|
9
|
+
|
10
|
+
const log = debug('lobe-search:Tavily');
|
11
|
+
|
12
|
+
/**
|
13
|
+
* Tavily implementation of the search service
|
14
|
+
* Primarily used for web crawling
|
15
|
+
*/
|
16
|
+
export class TavilyImpl implements SearchServiceImpl {
|
17
|
+
private get apiKey(): string | undefined {
|
18
|
+
return process.env.TAVILY_API_KEY;
|
19
|
+
}
|
20
|
+
|
21
|
+
private get baseUrl(): string {
|
22
|
+
// Assuming the base URL is consistent with the crawl endpoint
|
23
|
+
return 'https://api.tavily.com';
|
24
|
+
}
|
25
|
+
|
26
|
+
async query(query: string, params: SearchParams = {}): Promise<UniformSearchResponse> {
|
27
|
+
log('Starting Tavily query with query: "%s", params: %o', query, params);
|
28
|
+
const endpoint = urlJoin(this.baseUrl, '/search');
|
29
|
+
|
30
|
+
const defaultQueryParams: TavilySearchParameters = {
|
31
|
+
include_answer: false,
|
32
|
+
include_image_descriptions: true,
|
33
|
+
include_images: false,
|
34
|
+
include_raw_content: false,
|
35
|
+
max_results: 15,
|
36
|
+
query,
|
37
|
+
search_depth: process.env.TAVILY_SEARCH_DEPTH || 'basic' // basic or advanced
|
38
|
+
};
|
39
|
+
|
40
|
+
let body: TavilySearchParameters = {
|
41
|
+
...defaultQueryParams,
|
42
|
+
time_range:
|
43
|
+
params?.searchTimeRange && params.searchTimeRange !== 'anytime'
|
44
|
+
? params.searchTimeRange
|
45
|
+
: undefined,
|
46
|
+
topic:
|
47
|
+
// Tavily 只支持 news 和 general 两种类型
|
48
|
+
params?.searchCategories?.filter(cat => ['news', 'general'].includes(cat))?.[0],
|
49
|
+
};
|
50
|
+
|
51
|
+
log('Constructed request body: %o', body);
|
52
|
+
|
53
|
+
let response: Response;
|
54
|
+
const startAt = Date.now();
|
55
|
+
let costTime = 0;
|
56
|
+
try {
|
57
|
+
log('Sending request to endpoint: %s', endpoint);
|
58
|
+
response = await fetch(endpoint, {
|
59
|
+
body: JSON.stringify(body),
|
60
|
+
headers: {
|
61
|
+
'Authorization': this.apiKey ? `Bearer ${this.apiKey}` : '',
|
62
|
+
'Content-Type': 'application/json',
|
63
|
+
},
|
64
|
+
method: 'POST',
|
65
|
+
});
|
66
|
+
log('Received response with status: %d', response.status);
|
67
|
+
costTime = Date.now() - startAt;
|
68
|
+
} catch (error) {
|
69
|
+
log.extend('error')('Tavily fetch error: %o', error);
|
70
|
+
throw new TRPCError({
|
71
|
+
cause: error,
|
72
|
+
code: 'SERVICE_UNAVAILABLE',
|
73
|
+
message: 'Failed to connect to Tavily.',
|
74
|
+
});
|
75
|
+
}
|
76
|
+
|
77
|
+
if (!response.ok) {
|
78
|
+
const errorBody = await response.text();
|
79
|
+
log.extend('error')(
|
80
|
+
`Tavily request failed with status ${response.status}: %s`,
|
81
|
+
errorBody.length > 200 ? `${errorBody.slice(0, 200)}...` : errorBody,
|
82
|
+
);
|
83
|
+
throw new TRPCError({
|
84
|
+
cause: errorBody,
|
85
|
+
code: 'SERVICE_UNAVAILABLE',
|
86
|
+
message: `Tavily request failed: ${response.statusText}`,
|
87
|
+
});
|
88
|
+
}
|
89
|
+
|
90
|
+
try {
|
91
|
+
const tavilyResponse = (await response.json()) as TavilyResponse;
|
92
|
+
|
93
|
+
log('Parsed Tavily response: %o', tavilyResponse);
|
94
|
+
|
95
|
+
const mappedResults = (tavilyResponse.results || []).map(
|
96
|
+
(result): UniformSearchResult => ({
|
97
|
+
category: body.topic || 'general', // Default category
|
98
|
+
content: result.content || '', // Prioritize content, fallback to snippet
|
99
|
+
engines: ['tavily'], // Use 'tavily' as the engine name
|
100
|
+
parsedUrl: result.url ? new URL(result.url).hostname : '', // Basic URL parsing
|
101
|
+
score: result.score || 0, // Default score to 0 if undefined
|
102
|
+
title: result.title || '',
|
103
|
+
url: result.url,
|
104
|
+
}),
|
105
|
+
);
|
106
|
+
|
107
|
+
log('Mapped %d results to SearchResult format', mappedResults.length);
|
108
|
+
|
109
|
+
return {
|
110
|
+
costTime,
|
111
|
+
query: query,
|
112
|
+
resultNumbers: mappedResults.length,
|
113
|
+
results: mappedResults,
|
114
|
+
};
|
115
|
+
} catch (error) {
|
116
|
+
log.extend('error')('Error parsing Tavily response: %o', error);
|
117
|
+
throw new TRPCError({
|
118
|
+
cause: error,
|
119
|
+
code: 'INTERNAL_SERVER_ERROR',
|
120
|
+
message: 'Failed to parse Tavily response.',
|
121
|
+
});
|
122
|
+
}
|
123
|
+
}
|
124
|
+
}
|
@@ -0,0 +1,36 @@
|
|
1
|
+
export interface TavilySearchParameters {
|
2
|
+
chunks_per_source?: number;
|
3
|
+
days?: number;
|
4
|
+
exclude_domains?: string[];
|
5
|
+
include_answer?: boolean | string;
|
6
|
+
include_domains?: string[];
|
7
|
+
include_image_descriptions?: boolean;
|
8
|
+
include_images?: boolean;
|
9
|
+
include_raw_content?: boolean;
|
10
|
+
max_results?: number;
|
11
|
+
query: string;
|
12
|
+
search_depth?: string;
|
13
|
+
time_range?: string;
|
14
|
+
topic?: string;
|
15
|
+
}
|
16
|
+
|
17
|
+
interface TavilyImages {
|
18
|
+
description?: string;
|
19
|
+
url: string;
|
20
|
+
}
|
21
|
+
|
22
|
+
interface TavilyResults {
|
23
|
+
content?: string;
|
24
|
+
raw_content?: string | null;
|
25
|
+
score?: number;
|
26
|
+
title?: string;
|
27
|
+
url: string;
|
28
|
+
}
|
29
|
+
|
30
|
+
export interface TavilyResponse {
|
31
|
+
answer?: string;
|
32
|
+
images?: TavilyImages[];
|
33
|
+
query: string;
|
34
|
+
response_time: number;
|
35
|
+
results: TavilyResults[];
|
36
|
+
}
|
@@ -671,14 +671,16 @@ describe('chatMessage actions', () => {
|
|
671
671
|
(chatService.createAssistantMessage as Mock).mockResolvedValue(aiResponse);
|
672
672
|
const spy = vi.spyOn(chatService, 'createAssistantMessageStream');
|
673
673
|
// 模拟消息创建
|
674
|
-
|
674
|
+
const createMessageSpyOn = vi
|
675
|
+
.spyOn(messageService, 'createMessage')
|
676
|
+
.mockResolvedValue('assistant-message-id');
|
675
677
|
|
676
678
|
await act(async () => {
|
677
679
|
await result.current.internal_coreProcessMessage(messages, userMessage.id);
|
678
680
|
});
|
679
681
|
|
680
682
|
// 验证是否创建了代表 AI 响应的消息
|
681
|
-
expect(
|
683
|
+
expect(createMessageSpyOn).toHaveBeenCalledWith(
|
682
684
|
expect.objectContaining({
|
683
685
|
role: 'assistant',
|
684
686
|
content: LOADING_FLAT,
|
@@ -369,11 +369,12 @@ describe('chatMessage actions', () => {
|
|
369
369
|
const messageId = 'message-id';
|
370
370
|
const newContent = 'Updated content';
|
371
371
|
|
372
|
+
const spy = vi.spyOn(messageService, 'updateMessage');
|
372
373
|
await act(async () => {
|
373
374
|
await result.current.internal_updateMessageContent(messageId, newContent);
|
374
375
|
});
|
375
376
|
|
376
|
-
expect(
|
377
|
+
expect(spy).toHaveBeenCalledWith(messageId, { content: newContent });
|
377
378
|
});
|
378
379
|
|
379
380
|
it('should dispatch message update action', async () => {
|
@@ -264,7 +264,8 @@ describe('topic action', () => {
|
|
264
264
|
const topicId = 'topic-id';
|
265
265
|
const newTitle = 'Updated Topic Title';
|
266
266
|
// Mock the topicService.updateTitle to resolve immediately
|
267
|
-
|
267
|
+
|
268
|
+
const spyOn = vi.spyOn(topicService, 'updateTopic');
|
268
269
|
|
269
270
|
const { result } = renderHook(() => useChatStore());
|
270
271
|
|
@@ -276,7 +277,7 @@ describe('topic action', () => {
|
|
276
277
|
});
|
277
278
|
|
278
279
|
// Verify that the topicService.updateTitle was called with correct parameters
|
279
|
-
expect(
|
280
|
+
expect(spyOn).toHaveBeenCalledWith(topicId, {
|
280
281
|
title: 'Updated Topic Title',
|
281
282
|
});
|
282
283
|
|