@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.
- package/CHANGELOG.md +50 -0
- package/README.zh-CN.md +8 -8
- package/changelog/v1.json +18 -0
- package/docs/self-hosting/advanced/online-search.mdx +123 -5
- package/docs/self-hosting/advanced/online-search.zh-CN.mdx +123 -4
- package/locales/ar/models.json +2 -2
- package/locales/bg-BG/models.json +2 -2
- package/locales/de-DE/models.json +2 -2
- package/locales/en-US/models.json +2 -2
- package/locales/es-ES/models.json +2 -2
- package/locales/fa-IR/models.json +2 -2
- package/locales/fr-FR/models.json +2 -2
- package/locales/it-IT/models.json +2 -2
- package/locales/ja-JP/models.json +2 -2
- package/locales/ko-KR/models.json +2 -2
- package/locales/nl-NL/models.json +2 -2
- package/locales/pl-PL/models.json +2 -2
- package/locales/pt-BR/models.json +2 -2
- package/locales/ru-RU/models.json +2 -2
- package/locales/tr-TR/models.json +2 -2
- package/locales/vi-VN/models.json +2 -2
- package/locales/zh-CN/models.json +2 -2
- package/locales/zh-TW/models.json +2 -2
- package/package.json +1 -1
- package/src/server/services/search/impls/anspire/index.ts +132 -0
- package/src/server/services/search/impls/anspire/type.ts +21 -0
- package/src/server/services/search/impls/brave/index.ts +129 -0
- package/src/server/services/search/impls/brave/type.ts +58 -0
- package/src/server/services/search/impls/google/index.ts +129 -0
- package/src/server/services/search/impls/google/type.ts +53 -0
- package/src/server/services/search/impls/index.ts +24 -0
- package/src/server/services/search/impls/kagi/index.ts +111 -0
- 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
|
+
}
|