@lobehub/chat 1.94.16 → 1.95.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.
Files changed (33) hide show
  1. package/CHANGELOG.md +50 -0
  2. package/README.zh-CN.md +8 -8
  3. package/changelog/v1.json +18 -0
  4. package/docs/self-hosting/advanced/online-search.mdx +123 -5
  5. package/docs/self-hosting/advanced/online-search.zh-CN.mdx +123 -4
  6. package/locales/ar/models.json +2 -2
  7. package/locales/bg-BG/models.json +2 -2
  8. package/locales/de-DE/models.json +2 -2
  9. package/locales/en-US/models.json +2 -2
  10. package/locales/es-ES/models.json +2 -2
  11. package/locales/fa-IR/models.json +2 -2
  12. package/locales/fr-FR/models.json +2 -2
  13. package/locales/it-IT/models.json +2 -2
  14. package/locales/ja-JP/models.json +2 -2
  15. package/locales/ko-KR/models.json +2 -2
  16. package/locales/nl-NL/models.json +2 -2
  17. package/locales/pl-PL/models.json +2 -2
  18. package/locales/pt-BR/models.json +2 -2
  19. package/locales/ru-RU/models.json +2 -2
  20. package/locales/tr-TR/models.json +2 -2
  21. package/locales/vi-VN/models.json +2 -2
  22. package/locales/zh-CN/models.json +2 -2
  23. package/locales/zh-TW/models.json +2 -2
  24. package/package.json +1 -1
  25. package/src/server/services/search/impls/anspire/index.ts +132 -0
  26. package/src/server/services/search/impls/anspire/type.ts +21 -0
  27. package/src/server/services/search/impls/brave/index.ts +129 -0
  28. package/src/server/services/search/impls/brave/type.ts +58 -0
  29. package/src/server/services/search/impls/google/index.ts +129 -0
  30. package/src/server/services/search/impls/google/type.ts +53 -0
  31. package/src/server/services/search/impls/index.ts +24 -0
  32. package/src/server/services/search/impls/kagi/index.ts +111 -0
  33. package/src/server/services/search/impls/kagi/type.ts +24 -0
@@ -0,0 +1,111 @@
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 { KagiSearchParameters, KagiResponse } from './type';
9
+
10
+ const log = debug('lobe-search:Kagi');
11
+
12
+ /**
13
+ * Kagi implementation of the search service
14
+ * Primarily used for web crawling
15
+ */
16
+ export class KagiImpl implements SearchServiceImpl {
17
+ private get apiKey(): string | undefined {
18
+ return process.env.KAGI_API_KEY;
19
+ }
20
+
21
+ private get baseUrl(): string {
22
+ // Assuming the base URL is consistent with the crawl endpoint
23
+ return 'https://kagi.com/api/v0';
24
+ }
25
+
26
+ async query(query: string, params: SearchParams = {}): Promise<UniformSearchResponse> {
27
+ log('Starting Kagi query with query: "%s", params: %o', query, params);
28
+ const endpoint = urlJoin(this.baseUrl, '/search');
29
+
30
+ const body: KagiSearchParameters = {
31
+ limit: 15,
32
+ q: query,
33
+ };
34
+
35
+ log('Constructed request body: %o', body);
36
+
37
+ const searchParams = new URLSearchParams();
38
+ for (const [key, value] of Object.entries(body)) {
39
+ searchParams.append(key, String(value));
40
+ }
41
+
42
+ let response: Response;
43
+ const startAt = Date.now();
44
+ let costTime = 0;
45
+ try {
46
+ log('Sending request to endpoint: %s', endpoint);
47
+ response = await fetch(`${endpoint}?${searchParams.toString()}`, {
48
+ headers: {
49
+ 'Authorization': this.apiKey ? `Bot ${this.apiKey}` : '',
50
+ },
51
+ method: 'GET',
52
+ });
53
+ log('Received response with status: %d', response.status);
54
+ costTime = Date.now() - startAt;
55
+ } catch (error) {
56
+ log.extend('error')('Kagi fetch error: %o', error);
57
+ throw new TRPCError({
58
+ cause: error,
59
+ code: 'SERVICE_UNAVAILABLE',
60
+ message: 'Failed to connect to Kagi.',
61
+ });
62
+ }
63
+
64
+ if (!response.ok) {
65
+ const errorBody = await response.text();
66
+ log.extend('error')(
67
+ `Kagi request failed with status ${response.status}: %s`,
68
+ errorBody.length > 200 ? `${errorBody.slice(0, 200)}...` : errorBody,
69
+ );
70
+ throw new TRPCError({
71
+ cause: errorBody,
72
+ code: 'SERVICE_UNAVAILABLE',
73
+ message: `Kagi request failed: ${response.statusText}`,
74
+ });
75
+ }
76
+
77
+ try {
78
+ const kagiResponse = (await response.json()) as KagiResponse;
79
+
80
+ log('Parsed Kagi response: %o', kagiResponse);
81
+
82
+ const mappedResults = (kagiResponse.data || []).map(
83
+ (result): UniformSearchResult => ({
84
+ category: 'general', // Default category
85
+ content: result.snippet || '', // Prioritize content
86
+ engines: ['kagi'], // Use 'kagi' as the engine name
87
+ parsedUrl: result.url ? new URL(result.url).hostname : '', // Basic URL parsing
88
+ score: 1, // Default score to 1
89
+ title: result.title || '',
90
+ url: result.url,
91
+ }),
92
+ );
93
+
94
+ log('Mapped %d results to SearchResult format', mappedResults.length);
95
+
96
+ return {
97
+ costTime,
98
+ query: query,
99
+ resultNumbers: mappedResults.length,
100
+ results: mappedResults,
101
+ };
102
+ } catch (error) {
103
+ log.extend('error')('Error parsing Kagi response: %o', error);
104
+ throw new TRPCError({
105
+ cause: error,
106
+ code: 'INTERNAL_SERVER_ERROR',
107
+ message: 'Failed to parse Kagi response.',
108
+ });
109
+ }
110
+ }
111
+ }
@@ -0,0 +1,24 @@
1
+ export interface KagiSearchParameters {
2
+ limit?: number;
3
+ q: string;
4
+ }
5
+
6
+ interface KagiThumbnail {
7
+ height?: number | null;
8
+ url: string;
9
+ width?: number | null;
10
+ }
11
+
12
+ interface KagiData {
13
+ published?: number;
14
+ snippet?: string;
15
+ t: number;
16
+ thumbnail?: KagiThumbnail;
17
+ title: string;
18
+ url: string;
19
+ }
20
+
21
+ export interface KagiResponse {
22
+ data: KagiData[];
23
+ meta?: any;
24
+ }