@lobehub/chat 1.90.4 → 1.91.1
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 +58 -0
- package/changelog/v1.json +21 -0
- package/docs/changelog/2023-09-09-plugin-system.mdx +2 -3
- package/docs/changelog/2023-11-14-gpt4-vision.mdx +4 -6
- package/docs/changelog/2023-11-19-tts-stt.mdx +2 -3
- package/docs/changelog/2023-12-22-dalle-3.mdx +2 -5
- package/docs/changelog/2023-12-22-dalle-3.zh-CN.mdx +2 -2
- package/docs/changelog/2024-02-08-sso-oauth.mdx +2 -2
- package/docs/changelog/2024-06-19-lobe-chat-v1.mdx +2 -3
- package/docs/changelog/2024-06-19-lobe-chat-v1.zh-CN.mdx +2 -2
- package/docs/changelog/2024-07-19-gpt-4o-mini.mdx +2 -3
- package/docs/changelog/2024-07-19-gpt-4o-mini.zh-CN.mdx +2 -2
- package/docs/changelog/2024-08-02-lobe-chat-database-docker.mdx +2 -3
- package/docs/changelog/2024-08-21-file-upload-and-knowledge-base.mdx +4 -5
- package/docs/changelog/2024-09-13-openai-o1-models.mdx +2 -2
- package/docs/changelog/2024-09-20-artifacts.mdx +2 -3
- package/docs/changelog/2024-09-20-artifacts.zh-CN.mdx +2 -2
- package/docs/changelog/2024-10-27-pin-assistant.mdx +2 -3
- package/docs/changelog/2024-11-06-share-text-json.mdx +2 -4
- package/docs/changelog/2024-11-06-share-text-json.zh-CN.mdx +2 -2
- package/docs/changelog/2024-11-25-november-providers.mdx +2 -2
- package/docs/changelog/2024-11-27-forkable-chat.mdx +2 -2
- package/docs/changelog/2025-01-03-user-profile.mdx +2 -2
- package/docs/changelog/2025-01-22-new-ai-provider.mdx +2 -2
- package/docs/changelog/2025-02-02-deepseek-r1.mdx +4 -4
- package/docs/development/basic/add-new-authentication-providers.zh-CN.mdx +1 -2
- package/docs/development/basic/chat-api.mdx +2 -4
- package/docs/development/basic/chat-api.zh-CN.mdx +2 -4
- package/docs/development/internationalization/internationalization-implementation.mdx +10 -10
- package/docs/development/internationalization/internationalization-implementation.zh-CN.mdx +10 -10
- package/docs/self-hosting/advanced/analytics.mdx +2 -2
- package/docs/self-hosting/advanced/auth/clerk.mdx +2 -2
- package/docs/self-hosting/advanced/auth/next-auth/auth0.mdx +2 -3
- package/docs/self-hosting/advanced/auth/next-auth/authelia.mdx +2 -3
- package/docs/self-hosting/advanced/auth/next-auth/authentik.mdx +2 -3
- package/docs/self-hosting/advanced/auth/next-auth/casdoor.mdx +4 -7
- package/docs/self-hosting/advanced/auth/next-auth/casdoor.zh-CN.mdx +0 -3
- package/docs/self-hosting/advanced/auth/next-auth/cloudflare-zero-trust.mdx +2 -3
- package/docs/self-hosting/advanced/auth/next-auth/cloudflare-zero-trust.zh-CN.mdx +2 -2
- package/docs/self-hosting/advanced/auth/next-auth/github.mdx +2 -3
- package/docs/self-hosting/advanced/auth/next-auth/keycloak.mdx +12 -6
- package/docs/self-hosting/advanced/auth/next-auth/keycloak.zh-CN.mdx +5 -1
- package/docs/self-hosting/advanced/auth/next-auth/logto.mdx +8 -14
- package/docs/self-hosting/advanced/auth/next-auth/logto.zh-CN.mdx +6 -12
- package/docs/self-hosting/advanced/auth/next-auth/microsoft-entra-id.mdx +2 -3
- package/docs/self-hosting/advanced/auth/next-auth/wechat.mdx +2 -2
- package/docs/self-hosting/advanced/auth/next-auth/zitadel.mdx +2 -3
- package/docs/self-hosting/advanced/auth.mdx +2 -3
- package/docs/self-hosting/advanced/desktop.mdx +2 -1
- package/docs/self-hosting/advanced/desktop.zh-CN.mdx +1 -3
- package/docs/self-hosting/advanced/feature-flags.mdx +2 -3
- package/docs/self-hosting/advanced/knowledge-base.mdx +4 -3
- package/docs/self-hosting/advanced/model-list.mdx +11 -10
- package/docs/self-hosting/advanced/model-list.zh-CN.mdx +10 -9
- package/docs/self-hosting/advanced/observability/langfuse.mdx +2 -3
- package/docs/self-hosting/advanced/online-search.mdx +11 -10
- package/docs/self-hosting/advanced/online-search.zh-CN.mdx +7 -7
- package/docs/self-hosting/advanced/s3/tencent-cloud.mdx +2 -2
- package/docs/self-hosting/advanced/settings-url-share.mdx +2 -3
- package/docs/self-hosting/advanced/upstream-sync.mdx +2 -3
- package/docs/self-hosting/advanced/webrtc.mdx +2 -2
- package/docs/self-hosting/environment-variables/analytics.mdx +2 -3
- package/docs/self-hosting/environment-variables/auth.mdx +2 -3
- package/docs/self-hosting/environment-variables/basic.mdx +4 -5
- package/docs/self-hosting/environment-variables/basic.zh-CN.mdx +2 -2
- package/docs/self-hosting/environment-variables/model-provider.mdx +2 -3
- package/docs/self-hosting/environment-variables/s3.mdx +3 -5
- package/docs/self-hosting/environment-variables.mdx +2 -2
- package/docs/self-hosting/examples/azure-openai.mdx +2 -2
- package/docs/self-hosting/examples/ollama.mdx +2 -3
- package/docs/self-hosting/faq/no-v1-suffix.mdx +4 -4
- package/docs/self-hosting/faq/proxy-with-unable-to-verify-leaf-signature.mdx +2 -3
- package/docs/self-hosting/platform/alibaba-cloud.mdx +2 -3
- package/docs/self-hosting/platform/btpanel.mdx +5 -5
- package/docs/self-hosting/platform/btpanel.zh-CN.mdx +4 -3
- package/docs/self-hosting/platform/docker-compose.mdx +2 -3
- package/docs/self-hosting/platform/docker-compose.zh-CN.mdx +0 -2
- package/docs/self-hosting/platform/docker.mdx +2 -2
- package/docs/self-hosting/platform/netlify.mdx +2 -4
- package/docs/self-hosting/platform/netlify.zh-CN.mdx +2 -2
- package/docs/self-hosting/platform/railway.mdx +2 -3
- package/docs/self-hosting/platform/repocloud.mdx +2 -3
- package/docs/self-hosting/platform/sealos.mdx +2 -2
- package/docs/self-hosting/platform/tencentcloud-lighthouse.mdx +2 -3
- package/docs/self-hosting/platform/vercel.mdx +2 -3
- package/docs/self-hosting/platform/zeabur.mdx +2 -2
- package/docs/self-hosting/server-database/docker-compose.mdx +65 -44
- package/docs/self-hosting/server-database/docker-compose.zh-CN.mdx +48 -55
- package/docs/self-hosting/server-database/docker.mdx +2 -2
- package/docs/self-hosting/server-database/docker.zh-CN.mdx +2 -2
- package/docs/self-hosting/server-database/dokploy.mdx +4 -5
- package/docs/self-hosting/server-database/dokploy.zh-CN.mdx +137 -138
- package/docs/self-hosting/server-database/netlify.mdx +2 -2
- package/docs/self-hosting/server-database/netlify.zh-CN.mdx +2 -2
- package/docs/self-hosting/server-database/railway.mdx +2 -2
- package/docs/self-hosting/server-database/repocloud.mdx +2 -2
- package/docs/self-hosting/server-database/sealos.mdx +4 -5
- package/docs/self-hosting/server-database/sealos.zh-CN.mdx +18 -20
- package/docs/self-hosting/server-database/vercel.mdx +5 -3
- package/docs/self-hosting/server-database/vercel.zh-CN.mdx +2 -2
- package/docs/self-hosting/server-database/zeabur.mdx +2 -2
- package/docs/self-hosting/server-database.mdx +1 -1
- package/docs/self-hosting/server-database.zh-CN.mdx +2 -1
- package/docs/self-hosting/start.mdx +2 -2
- package/docs/self-hosting/start.zh-CN.mdx +2 -2
- package/docs/usage/agents/agent-organization.mdx +2 -2
- package/docs/usage/agents/concepts.mdx +4 -5
- package/docs/usage/agents/concepts.zh-CN.mdx +2 -2
- package/docs/usage/agents/custom-agent.mdx +3 -4
- package/docs/usage/agents/custom-agent.zh-CN.mdx +1 -1
- package/docs/usage/agents/model.mdx +5 -5
- package/docs/usage/agents/model.zh-CN.mdx +3 -5
- package/docs/usage/agents/prompt.mdx +4 -5
- package/docs/usage/agents/topics.mdx +3 -4
- package/docs/usage/agents/topics.zh-CN.mdx +1 -1
- package/docs/usage/features/agent-market.mdx +3 -11
- package/docs/usage/features/agent-market.zh-CN.mdx +2 -7
- package/docs/usage/features/artifacts.mdx +2 -2
- package/docs/usage/features/auth.mdx +2 -3
- package/docs/usage/features/cot.mdx +2 -2
- package/docs/usage/features/database.mdx +2 -2
- package/docs/usage/features/knowledge-base.mdx +4 -3
- package/docs/usage/features/knowledge-base.zh-CN.mdx +2 -1
- package/docs/usage/features/local-llm.mdx +2 -3
- package/docs/usage/features/mobile.mdx +2 -2
- package/docs/usage/features/more.mdx +2 -3
- package/docs/usage/features/multi-ai-providers.mdx +2 -3
- package/docs/usage/features/plugin-system.mdx +3 -11
- package/docs/usage/features/plugin-system.zh-CN.mdx +1 -8
- package/docs/usage/features/pwa.mdx +4 -4
- package/docs/usage/features/pwa.zh-CN.mdx +2 -1
- package/docs/usage/features/text-to-image.mdx +3 -11
- package/docs/usage/features/text-to-image.zh-CN.mdx +3 -10
- package/docs/usage/features/theme.mdx +2 -3
- package/docs/usage/features/tts.mdx +3 -11
- package/docs/usage/features/tts.zh-CN.mdx +1 -8
- package/docs/usage/features/vision.mdx +3 -11
- package/docs/usage/features/vision.zh-CN.mdx +1 -8
- package/docs/usage/foundation/basic.mdx +2 -3
- package/docs/usage/foundation/share.mdx +2 -3
- package/docs/usage/foundation/text2image.mdx +2 -2
- package/docs/usage/foundation/translate.mdx +2 -2
- package/docs/usage/foundation/tts-stt.mdx +2 -2
- package/docs/usage/foundation/vision.mdx +2 -3
- package/docs/usage/plugins/basic-usage.mdx +2 -3
- package/docs/usage/plugins/custom-plugin.mdx +2 -2
- package/docs/usage/plugins/development.mdx +2 -4
- package/docs/usage/plugins/store.mdx +2 -2
- package/docs/usage/providers/ai21.mdx +2 -2
- package/docs/usage/providers/anthropic.mdx +2 -3
- package/docs/usage/providers/anthropic.zh-CN.mdx +2 -2
- package/docs/usage/providers/azure.mdx +2 -3
- package/docs/usage/providers/azureai.mdx +4 -2
- package/docs/usage/providers/azureai.zh-CN.mdx +2 -1
- package/docs/usage/providers/baichuan.mdx +2 -3
- package/docs/usage/providers/bedrock.mdx +2 -3
- package/docs/usage/providers/cloudflare.mdx +3 -2
- package/docs/usage/providers/deepseek.mdx +2 -2
- package/docs/usage/providers/fireworksai.mdx +2 -2
- package/docs/usage/providers/giteeai.mdx +2 -2
- package/docs/usage/providers/github.mdx +1 -1
- package/docs/usage/providers/github.zh-CN.mdx +1 -1
- package/docs/usage/providers/google.mdx +2 -3
- package/docs/usage/providers/groq.mdx +2 -2
- package/docs/usage/providers/hunyuan.mdx +2 -2
- package/docs/usage/providers/infiniai.zh-CN.mdx +3 -1
- package/docs/usage/providers/internlm.mdx +2 -2
- package/docs/usage/providers/jina.mdx +4 -3
- package/docs/usage/providers/jina.zh-CN.mdx +2 -2
- package/docs/usage/providers/lmstudio.mdx +2 -2
- package/docs/usage/providers/lmstudio.zh-CN.mdx +2 -4
- package/docs/usage/providers/minimax.mdx +2 -3
- package/docs/usage/providers/minimax.zh-CN.mdx +2 -2
- package/docs/usage/providers/mistral.mdx +2 -3
- package/docs/usage/providers/modelscope.mdx +4 -0
- package/docs/usage/providers/modelscope.zh-CN.mdx +4 -0
- package/docs/usage/providers/moonshot.mdx +2 -3
- package/docs/usage/providers/novita.mdx +2 -3
- package/docs/usage/providers/novita.zh-CN.mdx +2 -2
- package/docs/usage/providers/nvidia.mdx +3 -2
- package/docs/usage/providers/ollama/gemma.mdx +2 -3
- package/docs/usage/providers/ollama/gemma.zh-CN.mdx +2 -2
- package/docs/usage/providers/ollama.mdx +2 -2
- package/docs/usage/providers/openai.mdx +5 -5
- package/docs/usage/providers/openai.zh-CN.mdx +3 -3
- package/docs/usage/providers/openrouter.mdx +2 -3
- package/docs/usage/providers/perplexity.mdx +2 -2
- package/docs/usage/providers/ppio.mdx +5 -6
- package/docs/usage/providers/ppio.zh-CN.mdx +6 -6
- package/docs/usage/providers/qiniu.mdx +6 -6
- package/docs/usage/providers/qiniu.zh-CN.mdx +2 -1
- package/docs/usage/providers/qwen.mdx +2 -4
- package/docs/usage/providers/sambanova.mdx +2 -1
- package/docs/usage/providers/sensenova.mdx +2 -2
- package/docs/usage/providers/siliconcloud.mdx +2 -2
- package/docs/usage/providers/stepfun.mdx +2 -3
- package/docs/usage/providers/taichu.mdx +2 -3
- package/docs/usage/providers/togetherai.mdx +2 -2
- package/docs/usage/providers/vllm.mdx +15 -12
- package/docs/usage/providers/vllm.zh-CN.mdx +9 -7
- package/docs/usage/providers/volcengine.mdx +16 -14
- package/docs/usage/providers/wenxin.mdx +2 -2
- package/docs/usage/providers/xai.mdx +2 -2
- package/docs/usage/providers/zeroone.mdx +2 -3
- package/docs/usage/providers/zeroone.zh-CN.mdx +2 -2
- package/docs/usage/providers/zhipu.mdx +2 -3
- package/docs/usage/providers/zhipu.zh-CN.mdx +1 -1
- package/docs/usage/providers.mdx +2 -3
- package/docs/usage/start.mdx +2 -3
- package/docs/usage/tools-calling/anthropic.mdx +2 -2
- package/docs/usage/tools-calling/anthropic.zh-CN.mdx +2 -2
- package/docs/usage/tools-calling/google.mdx +2 -2
- package/docs/usage/tools-calling/google.zh-CN.mdx +4 -4
- package/docs/usage/tools-calling/groq.zh-CN.mdx +2 -2
- package/docs/usage/tools-calling/openai.mdx +2 -2
- package/docs/usage/tools-calling/openai.zh-CN.mdx +2 -2
- package/package.json +1 -1
- 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/app/(backend)/webapi/user/avatar/[id]/[image]/route.ts +1 -1
- package/src/app/[variants]/(main)/repos/[id]/_layout/Mobile.tsx +7 -7
- package/src/config/aiModels/deepseek.ts +1 -0
- package/src/config/aiModels/hunyuan.ts +1 -0
- package/src/config/aiModels/mistral.ts +1 -2
- package/src/config/aiModels/modelscope.ts +3 -3
- package/src/config/aiModels/novita.ts +23 -22
- package/src/config/aiModels/openrouter.ts +1 -0
- package/src/config/aiModels/qwen.ts +11 -11
- package/src/config/aiModels/siliconcloud.ts +7 -6
- package/src/config/aiModels/vertexai.ts +2 -2
- package/src/config/aiModels/wenxin.ts +1 -2
- package/src/config/modelProviders/baichuan.ts +3 -0
- package/src/config/modelProviders/fireworksai.ts +3 -0
- package/src/config/modelProviders/giteeai.ts +3 -0
- package/src/config/modelProviders/github.ts +1 -2
- package/src/config/modelProviders/groq.ts +0 -3
- package/src/config/modelProviders/hunyuan.ts +3 -0
- package/src/config/modelProviders/infiniai.ts +0 -3
- package/src/config/modelProviders/internlm.ts +3 -0
- package/src/config/modelProviders/minimax.ts +3 -4
- package/src/config/modelProviders/modelscope.ts +3 -3
- package/src/config/modelProviders/moonshot.ts +0 -7
- package/src/config/modelProviders/novita.ts +3 -0
- package/src/config/modelProviders/openrouter.ts +0 -4
- package/src/config/modelProviders/perplexity.ts +0 -3
- package/src/config/modelProviders/qiniu.ts +0 -3
- package/src/config/modelProviders/qwen.ts +0 -3
- package/src/config/modelProviders/sensenova.ts +3 -0
- package/src/config/modelProviders/siliconcloud.ts +0 -3
- package/src/config/modelProviders/spark.ts +0 -5
- package/src/config/modelProviders/stepfun.ts +3 -6
- package/src/config/modelProviders/taichu.ts +3 -0
- package/src/config/modelProviders/tencentcloud.ts +3 -0
- package/src/config/modelProviders/togetherai.ts +3 -0
- package/src/config/modelProviders/upstage.ts +3 -0
- package/src/config/modelProviders/wenxin.ts +3 -4
- package/src/config/modelProviders/xai.ts +0 -3
- package/src/config/modelProviders/zhipu.ts +3 -0
- 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/0014_snapshot.json +182 -539
- package/src/database/migrations/meta/0016_snapshot.json +182 -539
- package/src/database/migrations/meta/0023_snapshot.json +5340 -0
- package/src/database/migrations/meta/_journal.json +7 -0
- package/src/database/repositories/dataImporter/__tests__/fixtures/with-client-id.json +13 -58
- package/src/features/ChatInput/ActionBar/Model/ControlsForm.tsx +1 -8
- package/src/features/ChatInput/ActionBar/Model/index.tsx +8 -16
- package/src/features/ChatInput/ActionBar/Search/Controls.tsx +4 -12
- package/src/features/ChatInput/ActionBar/Search/FCSearchModel.tsx +1 -7
- package/src/features/ChatInput/ActionBar/Search/index.tsx +2 -4
- package/src/features/ModelSwitchPanel/index.tsx +1 -4
- package/src/libs/model-runtime/anthropic/index.test.ts +4 -2
- package/src/libs/model-runtime/utils/streams/anthropic.ts +12 -11
- package/src/libs/model-runtime/utils/streams/openai.ts +6 -4
- package/src/libs/model-runtime/utils/streams/protocol.ts +1 -1
- package/src/libs/model-runtime/utils/streams/spark.test.ts +1 -1
- package/src/libs/model-runtime/utils/streams/spark.ts +1 -2
- package/src/middleware.ts +1 -1
- 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/server/services/user/index.ts +3 -4
- package/src/services/__tests__/assistant.test.ts +4 -6
- package/src/services/__tests__/tool.test.ts +3 -1
- package/src/store/user/slices/auth/selectors.ts +1 -1
- package/src/store/user/slices/common/action.test.ts +1 -1
- package/src/tools/web-browsing/index.ts +1 -7
@@ -0,0 +1,129 @@
|
|
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 { ExaSearchParameters, ExaResponse } from './type';
|
9
|
+
|
10
|
+
const log = debug('lobe-search:Exa');
|
11
|
+
|
12
|
+
/**
|
13
|
+
* Exa implementation of the search service
|
14
|
+
* Primarily used for web crawling
|
15
|
+
*/
|
16
|
+
export class ExaImpl implements SearchServiceImpl {
|
17
|
+
private get apiKey(): string | undefined {
|
18
|
+
return process.env.EXA_API_KEY;
|
19
|
+
}
|
20
|
+
|
21
|
+
private get baseUrl(): string {
|
22
|
+
// Assuming the base URL is consistent with the crawl endpoint
|
23
|
+
return 'https://api.exa.ai';
|
24
|
+
}
|
25
|
+
|
26
|
+
async query(query: string, params: SearchParams = {}): Promise<UniformSearchResponse> {
|
27
|
+
log('Starting Exa query with query: "%s", params: %o', query, params);
|
28
|
+
const endpoint = urlJoin(this.baseUrl, '/search');
|
29
|
+
|
30
|
+
const defaultQueryParams: ExaSearchParameters = {
|
31
|
+
numResults: 15,
|
32
|
+
query,
|
33
|
+
type: 'auto',
|
34
|
+
};
|
35
|
+
|
36
|
+
let body: ExaSearchParameters = {
|
37
|
+
...defaultQueryParams,
|
38
|
+
...(params?.searchTimeRange && params.searchTimeRange !== 'anytime'
|
39
|
+
? (() => {
|
40
|
+
const now = Date.now();
|
41
|
+
const days = { day: 1, month: 30, week: 7, year: 365 }[params.searchTimeRange!];
|
42
|
+
|
43
|
+
if (days === undefined) return {};
|
44
|
+
|
45
|
+
return {
|
46
|
+
endPublishedDate: new Date(now).toISOString(),
|
47
|
+
startPublishedDate: new Date(now - days * 86_400 * 1000).toISOString(),
|
48
|
+
};
|
49
|
+
})()
|
50
|
+
: {}),
|
51
|
+
category:
|
52
|
+
// Exa 只支持 news 类型
|
53
|
+
params?.searchCategories?.filter(cat => ['news'].includes(cat))?.[0],
|
54
|
+
};
|
55
|
+
|
56
|
+
log('Constructed request body: %o', body);
|
57
|
+
|
58
|
+
let response: Response;
|
59
|
+
const startAt = Date.now();
|
60
|
+
let costTime = 0;
|
61
|
+
try {
|
62
|
+
log('Sending request to endpoint: %s', endpoint);
|
63
|
+
response = await fetch(endpoint, {
|
64
|
+
body: JSON.stringify(body),
|
65
|
+
headers: {
|
66
|
+
'Content-Type': 'application/json',
|
67
|
+
'x-api-key': this.apiKey ? this.apiKey : '',
|
68
|
+
},
|
69
|
+
method: 'POST',
|
70
|
+
});
|
71
|
+
log('Received response with status: %d', response.status);
|
72
|
+
costTime = Date.now() - startAt;
|
73
|
+
} catch (error) {
|
74
|
+
log.extend('error')('Exa fetch error: %o', error);
|
75
|
+
throw new TRPCError({
|
76
|
+
cause: error,
|
77
|
+
code: 'SERVICE_UNAVAILABLE',
|
78
|
+
message: 'Failed to connect to Exa.',
|
79
|
+
});
|
80
|
+
}
|
81
|
+
|
82
|
+
if (!response.ok) {
|
83
|
+
const errorBody = await response.text();
|
84
|
+
log.extend('error')(
|
85
|
+
`Exa request failed with status ${response.status}: %s`,
|
86
|
+
errorBody.length > 200 ? `${errorBody.slice(0, 200)}...` : errorBody,
|
87
|
+
);
|
88
|
+
throw new TRPCError({
|
89
|
+
cause: errorBody,
|
90
|
+
code: 'SERVICE_UNAVAILABLE',
|
91
|
+
message: `Exa request failed: ${response.statusText}`,
|
92
|
+
});
|
93
|
+
}
|
94
|
+
|
95
|
+
try {
|
96
|
+
const exaResponse = (await response.json()) as ExaResponse;
|
97
|
+
|
98
|
+
log('Parsed Exa response: %o', exaResponse);
|
99
|
+
|
100
|
+
const mappedResults = (exaResponse.results || []).map(
|
101
|
+
(result): UniformSearchResult => ({
|
102
|
+
category: body.category || 'general', // Default category
|
103
|
+
content: result.text || '', // Prioritize content, fallback to snippet
|
104
|
+
engines: ['exa'], // Use 'exa' as the engine name
|
105
|
+
parsedUrl: result.url ? new URL(result.url).hostname : '', // Basic URL parsing
|
106
|
+
score: result.score || 0, // Default score to 0 if undefined
|
107
|
+
title: result.title || '',
|
108
|
+
url: result.url,
|
109
|
+
}),
|
110
|
+
);
|
111
|
+
|
112
|
+
log('Mapped %d results to SearchResult format', mappedResults.length);
|
113
|
+
|
114
|
+
return {
|
115
|
+
costTime,
|
116
|
+
query: query,
|
117
|
+
resultNumbers: mappedResults.length,
|
118
|
+
results: mappedResults,
|
119
|
+
};
|
120
|
+
} catch (error) {
|
121
|
+
log.extend('error')('Error parsing Exa response: %o', error);
|
122
|
+
throw new TRPCError({
|
123
|
+
cause: error,
|
124
|
+
code: 'INTERNAL_SERVER_ERROR',
|
125
|
+
message: 'Failed to parse Exa response.',
|
126
|
+
});
|
127
|
+
}
|
128
|
+
}
|
129
|
+
}
|
@@ -0,0 +1,39 @@
|
|
1
|
+
export interface ExaSearchParameters {
|
2
|
+
category?: string;
|
3
|
+
endCrawlDate?: string;
|
4
|
+
endPublishedDate?: string;
|
5
|
+
excludeDomains?: string[];
|
6
|
+
excludeText?: string[];
|
7
|
+
includeDomains?: string[];
|
8
|
+
includeText?: string[];
|
9
|
+
numResults?: number;
|
10
|
+
query: string;
|
11
|
+
startCrawlDate?: string;
|
12
|
+
startPublishedDate?: string;
|
13
|
+
type?: string;
|
14
|
+
}
|
15
|
+
|
16
|
+
interface ExaCostDollars {
|
17
|
+
total: number;
|
18
|
+
}
|
19
|
+
|
20
|
+
interface ExaResults {
|
21
|
+
author?: string | null;
|
22
|
+
favicon?: string;
|
23
|
+
id?: string;
|
24
|
+
image?: string;
|
25
|
+
publishedDate?: string | null;
|
26
|
+
score?: number | null;
|
27
|
+
summery?: string;
|
28
|
+
text: string;
|
29
|
+
title: string;
|
30
|
+
url: string;
|
31
|
+
}
|
32
|
+
|
33
|
+
export interface ExaResponse {
|
34
|
+
costDollars?: ExaCostDollars;
|
35
|
+
requestId?: string;
|
36
|
+
resolvedSearchType?: string;
|
37
|
+
results: ExaResults[];
|
38
|
+
searchType?: string;
|
39
|
+
}
|
@@ -0,0 +1,128 @@
|
|
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 { FirecrawlSearchParameters, FirecrawlResponse } from './type';
|
9
|
+
|
10
|
+
const log = debug('lobe-search:Firecrawl');
|
11
|
+
|
12
|
+
const timeRangeMapping = {
|
13
|
+
day: 'qdr:d',
|
14
|
+
month: 'qdr:m',
|
15
|
+
week: 'qdr:w',
|
16
|
+
year: 'qdr:y',
|
17
|
+
};
|
18
|
+
|
19
|
+
/**
|
20
|
+
* Firecrawl implementation of the search service
|
21
|
+
* Primarily used for web crawling
|
22
|
+
*/
|
23
|
+
export class FirecrawlImpl implements SearchServiceImpl {
|
24
|
+
private get apiKey(): string | undefined {
|
25
|
+
return process.env.FIRECRAWL_API_KEY;
|
26
|
+
}
|
27
|
+
|
28
|
+
private get baseUrl(): string {
|
29
|
+
// Assuming the base URL is consistent with the crawl endpoint
|
30
|
+
return process.env.FIRECRAWL_URL || 'https://api.firecrawl.dev/v1';
|
31
|
+
}
|
32
|
+
|
33
|
+
async query(query: string, params: SearchParams = {}): Promise<UniformSearchResponse> {
|
34
|
+
log('Starting Firecrawl query with query: "%s", params: %o', query, params);
|
35
|
+
const endpoint = urlJoin(this.baseUrl, '/search');
|
36
|
+
|
37
|
+
const defaultQueryParams: FirecrawlSearchParameters = {
|
38
|
+
limit: 15,
|
39
|
+
query,
|
40
|
+
/*
|
41
|
+
scrapeOptions: {
|
42
|
+
formats: ["markdown"]
|
43
|
+
},
|
44
|
+
*/
|
45
|
+
};
|
46
|
+
|
47
|
+
let body: FirecrawlSearchParameters = {
|
48
|
+
...defaultQueryParams,
|
49
|
+
tbs:
|
50
|
+
params?.searchTimeRange && params.searchTimeRange !== 'anytime'
|
51
|
+
? timeRangeMapping[params.searchTimeRange as keyof typeof timeRangeMapping] ?? undefined
|
52
|
+
: undefined,
|
53
|
+
};
|
54
|
+
|
55
|
+
log('Constructed request body: %o', body);
|
56
|
+
|
57
|
+
let response: Response;
|
58
|
+
const startAt = Date.now();
|
59
|
+
let costTime = 0;
|
60
|
+
try {
|
61
|
+
log('Sending request to endpoint: %s', endpoint);
|
62
|
+
response = await fetch(endpoint, {
|
63
|
+
body: JSON.stringify(body),
|
64
|
+
headers: {
|
65
|
+
'Authorization': this.apiKey ? `Bearer ${this.apiKey}` : '',
|
66
|
+
'Content-Type': 'application/json',
|
67
|
+
},
|
68
|
+
method: 'POST',
|
69
|
+
});
|
70
|
+
log('Received response with status: %d', response.status);
|
71
|
+
costTime = Date.now() - startAt;
|
72
|
+
} catch (error) {
|
73
|
+
log.extend('error')('Firecrawl fetch error: %o', error);
|
74
|
+
throw new TRPCError({
|
75
|
+
cause: error,
|
76
|
+
code: 'SERVICE_UNAVAILABLE',
|
77
|
+
message: 'Failed to connect to Firecrawl.',
|
78
|
+
});
|
79
|
+
}
|
80
|
+
|
81
|
+
if (!response.ok) {
|
82
|
+
const errorBody = await response.text();
|
83
|
+
log.extend('error')(
|
84
|
+
`Firecrawl request failed with status ${response.status}: %s`,
|
85
|
+
errorBody.length > 200 ? `${errorBody.slice(0, 200)}...` : errorBody,
|
86
|
+
);
|
87
|
+
throw new TRPCError({
|
88
|
+
cause: errorBody,
|
89
|
+
code: 'SERVICE_UNAVAILABLE',
|
90
|
+
message: `Firecrawl request failed: ${response.statusText}`,
|
91
|
+
});
|
92
|
+
}
|
93
|
+
|
94
|
+
try {
|
95
|
+
const firecrawlResponse = (await response.json()) as FirecrawlResponse;
|
96
|
+
|
97
|
+
log('Parsed Firecrawl response: %o', firecrawlResponse);
|
98
|
+
|
99
|
+
const mappedResults = (firecrawlResponse.data || []).map(
|
100
|
+
(result): UniformSearchResult => ({
|
101
|
+
category: 'general', // Default category
|
102
|
+
content: result.description || '', // Prioritize content, fallback to snippet
|
103
|
+
engines: ['firecrawl'], // Use 'firecrawl' as the engine name
|
104
|
+
parsedUrl: result.url ? new URL(result.url).hostname : '', // Basic URL parsing
|
105
|
+
score: 1, // Default score to 1
|
106
|
+
title: result.title || '',
|
107
|
+
url: result.url,
|
108
|
+
}),
|
109
|
+
);
|
110
|
+
|
111
|
+
log('Mapped %d results to SearchResult format', mappedResults.length);
|
112
|
+
|
113
|
+
return {
|
114
|
+
costTime,
|
115
|
+
query: query,
|
116
|
+
resultNumbers: mappedResults.length,
|
117
|
+
results: mappedResults,
|
118
|
+
};
|
119
|
+
} catch (error) {
|
120
|
+
log.extend('error')('Error parsing Firecrawl response: %o', error);
|
121
|
+
throw new TRPCError({
|
122
|
+
cause: error,
|
123
|
+
code: 'INTERNAL_SERVER_ERROR',
|
124
|
+
message: 'Failed to parse Firecrawl response.',
|
125
|
+
});
|
126
|
+
}
|
127
|
+
}
|
128
|
+
}
|
@@ -0,0 +1,35 @@
|
|
1
|
+
interface FirecrawlScrapeOptions {
|
2
|
+
formats: string[];
|
3
|
+
}
|
4
|
+
|
5
|
+
export interface FirecrawlSearchParameters {
|
6
|
+
country?: string;
|
7
|
+
lang?: string;
|
8
|
+
limit?: number;
|
9
|
+
query: string;
|
10
|
+
scrapeOptions?: FirecrawlScrapeOptions;
|
11
|
+
tbs?: string;
|
12
|
+
timeout?: number;
|
13
|
+
}
|
14
|
+
|
15
|
+
interface FirecrawlMetadata {
|
16
|
+
description?: string;
|
17
|
+
sourceURL?: string;
|
18
|
+
statusCode?: number;
|
19
|
+
title: string;
|
20
|
+
}
|
21
|
+
|
22
|
+
interface FirecrawlData {
|
23
|
+
description?: string;
|
24
|
+
html?: string;
|
25
|
+
links?: string[];
|
26
|
+
markdown?: string;
|
27
|
+
metadata?: FirecrawlMetadata;
|
28
|
+
title?: string;
|
29
|
+
url: string;
|
30
|
+
}
|
31
|
+
|
32
|
+
export interface FirecrawlResponse {
|
33
|
+
data: FirecrawlData[];
|
34
|
+
success?: boolean;
|
35
|
+
}
|
@@ -1,13 +1,24 @@
|
|
1
|
+
import { BochaImpl } from './bocha';
|
2
|
+
import { ExaImpl } from './exa';
|
3
|
+
import { FirecrawlImpl } from './firecrawl';
|
4
|
+
import { JinaImpl } from './jina';
|
1
5
|
import { Search1APIImpl } from './search1api';
|
2
6
|
import { SearXNGImpl } from './searxng';
|
7
|
+
import { TavilyImpl } from './tavily';
|
8
|
+
|
3
9
|
import { SearchServiceImpl } from './type';
|
4
10
|
|
5
11
|
/**
|
6
12
|
* Available search service implementations
|
7
13
|
*/
|
8
14
|
export enum SearchImplType {
|
15
|
+
Bocha = 'bocha',
|
16
|
+
Exa = 'exa',
|
17
|
+
Firecrawl = 'firecrawl',
|
18
|
+
Jina = 'jina',
|
9
19
|
SearXNG = 'searxng',
|
10
20
|
Search1API = 'search1api',
|
21
|
+
Tavily = 'tavily',
|
11
22
|
}
|
12
23
|
|
13
24
|
/**
|
@@ -17,10 +28,30 @@ export const createSearchServiceImpl = (
|
|
17
28
|
type: SearchImplType = SearchImplType.SearXNG,
|
18
29
|
): SearchServiceImpl => {
|
19
30
|
switch (type) {
|
31
|
+
case SearchImplType.Bocha: {
|
32
|
+
return new BochaImpl();
|
33
|
+
}
|
34
|
+
|
35
|
+
case SearchImplType.Exa: {
|
36
|
+
return new ExaImpl();
|
37
|
+
}
|
38
|
+
|
39
|
+
case SearchImplType.Firecrawl: {
|
40
|
+
return new FirecrawlImpl();
|
41
|
+
}
|
42
|
+
|
43
|
+
case SearchImplType.Jina: {
|
44
|
+
return new JinaImpl();
|
45
|
+
}
|
46
|
+
|
20
47
|
case SearchImplType.SearXNG: {
|
21
48
|
return new SearXNGImpl();
|
22
49
|
}
|
23
50
|
|
51
|
+
case SearchImplType.Tavily: {
|
52
|
+
return new TavilyImpl();
|
53
|
+
}
|
54
|
+
|
24
55
|
default: {
|
25
56
|
return new Search1APIImpl();
|
26
57
|
}
|
@@ -0,0 +1,109 @@
|
|
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 { JinaSearchParameters, JinaResponse } from './type';
|
9
|
+
|
10
|
+
const log = debug('lobe-search:Jina');
|
11
|
+
|
12
|
+
/**
|
13
|
+
* Jina implementation of the search service
|
14
|
+
* Primarily used for web crawling
|
15
|
+
*/
|
16
|
+
export class JinaImpl implements SearchServiceImpl {
|
17
|
+
private get apiKey(): string | undefined {
|
18
|
+
return process.env.JINA_READER_API_KEY || process.env.JINA_API_KEY;
|
19
|
+
}
|
20
|
+
|
21
|
+
private get baseUrl(): string {
|
22
|
+
// Assuming the base URL is consistent with the crawl endpoint
|
23
|
+
return 'https://s.jina.ai';
|
24
|
+
}
|
25
|
+
|
26
|
+
async query(query: string, params: SearchParams = {}): Promise<UniformSearchResponse> {
|
27
|
+
log('Starting Jina query with query: "%s", params: %o', query, params);
|
28
|
+
const endpoint = urlJoin(this.baseUrl, '/');
|
29
|
+
|
30
|
+
let body: JinaSearchParameters = {
|
31
|
+
q: query,
|
32
|
+
};
|
33
|
+
|
34
|
+
log('Constructed request body: %o', body);
|
35
|
+
|
36
|
+
let response: Response;
|
37
|
+
const startAt = Date.now();
|
38
|
+
let costTime = 0;
|
39
|
+
try {
|
40
|
+
log('Sending request to endpoint: %s', endpoint);
|
41
|
+
response = await fetch(endpoint, {
|
42
|
+
body: JSON.stringify(body),
|
43
|
+
headers: {
|
44
|
+
'Accept': 'application/json',
|
45
|
+
'Authorization': this.apiKey ? `Bearer ${this.apiKey}` : '',
|
46
|
+
'Content-Type': 'application/json',
|
47
|
+
'X-Respond-With': 'no-content',
|
48
|
+
},
|
49
|
+
method: 'POST',
|
50
|
+
});
|
51
|
+
log('Received response with status: %d', response.status);
|
52
|
+
costTime = Date.now() - startAt;
|
53
|
+
} catch (error) {
|
54
|
+
log.extend('error')('Jina fetch error: %o', error);
|
55
|
+
throw new TRPCError({
|
56
|
+
cause: error,
|
57
|
+
code: 'SERVICE_UNAVAILABLE',
|
58
|
+
message: 'Failed to connect to Jina.',
|
59
|
+
});
|
60
|
+
}
|
61
|
+
|
62
|
+
if (!response.ok) {
|
63
|
+
const errorBody = await response.text();
|
64
|
+
log.extend('error')(
|
65
|
+
`Jina request failed with status ${response.status}: %s`,
|
66
|
+
errorBody.length > 200 ? `${errorBody.slice(0, 200)}...` : errorBody,
|
67
|
+
);
|
68
|
+
throw new TRPCError({
|
69
|
+
cause: errorBody,
|
70
|
+
code: 'SERVICE_UNAVAILABLE',
|
71
|
+
message: `Jina request failed: ${response.statusText}`,
|
72
|
+
});
|
73
|
+
}
|
74
|
+
|
75
|
+
try {
|
76
|
+
const jinaResponse = (await response.json()) as JinaResponse;
|
77
|
+
|
78
|
+
log('Parsed Jina response: %o', jinaResponse);
|
79
|
+
|
80
|
+
const mappedResults = (jinaResponse.data || []).map(
|
81
|
+
(result): UniformSearchResult => ({
|
82
|
+
category: 'general', // Default category
|
83
|
+
content: result.description || '', // Prioritize content, fallback to snippet
|
84
|
+
engines: ['jina'], // Use 'jina' as the engine name
|
85
|
+
parsedUrl: result.url ? new URL(result.url).hostname : '', // Basic URL parsing
|
86
|
+
score: 1, // Default score to 1
|
87
|
+
title: result.title || '',
|
88
|
+
url: result.url,
|
89
|
+
}),
|
90
|
+
);
|
91
|
+
|
92
|
+
log('Mapped %d results to SearchResult format', mappedResults.length);
|
93
|
+
|
94
|
+
return {
|
95
|
+
costTime,
|
96
|
+
query: query,
|
97
|
+
resultNumbers: mappedResults.length,
|
98
|
+
results: mappedResults,
|
99
|
+
};
|
100
|
+
} catch (error) {
|
101
|
+
log.extend('error')('Error parsing Jina response: %o', error);
|
102
|
+
throw new TRPCError({
|
103
|
+
cause: error,
|
104
|
+
code: 'INTERNAL_SERVER_ERROR',
|
105
|
+
message: 'Failed to parse Jina response.',
|
106
|
+
});
|
107
|
+
}
|
108
|
+
}
|
109
|
+
}
|
@@ -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
|
+
}
|