@qzhike/agent-search 1.0.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/LICENSE +21 -0
- package/README.md +123 -0
- package/dist/adapters/brave-config.js +46 -0
- package/dist/adapters/brave.js +83 -0
- package/dist/adapters/deeproute-mirror.js +31 -0
- package/dist/adapters/exa-config.js +35 -0
- package/dist/adapters/exa.js +91 -0
- package/dist/adapters/jina-config.js +102 -0
- package/dist/adapters/jina-reader.js +83 -0
- package/dist/adapters/jina.js +90 -0
- package/dist/adapters/kimi.js +196 -0
- package/dist/adapters/metaso-config.js +35 -0
- package/dist/adapters/metaso.js +102 -0
- package/dist/adapters/minimax-config.js +54 -0
- package/dist/adapters/minimax.js +85 -0
- package/dist/adapters/provider-auth.js +18 -0
- package/dist/adapters/ragflow-config.js +63 -0
- package/dist/adapters/ragflow.js +170 -0
- package/dist/adapters/searxng-config.js +40 -0
- package/dist/adapters/searxng.js +86 -0
- package/dist/adapters/serper-config.js +35 -0
- package/dist/adapters/serper.js +80 -0
- package/dist/adapters/tavily-config.js +46 -0
- package/dist/adapters/tavily.js +98 -0
- package/dist/adapters/zai-config.js +39 -0
- package/dist/adapters/zai.js +91 -0
- package/dist/config-schema.js +221 -0
- package/dist/http.js +49 -0
- package/dist/index.js +84 -0
- package/dist/plugin-meta.js +11 -0
- package/dist/read-url.js +38 -0
- package/dist/result.js +326 -0
- package/dist/run-provider.js +42 -0
- package/dist/search-concurrent.js +23 -0
- package/dist/search-fallback.js +14 -0
- package/dist/search.js +43 -0
- package/dist/types.js +2 -0
- package/openclaw.plugin.json +547 -0
- package/package.json +25 -0
- package/skills/qzhike-agent-search/SKILL.md +90 -0
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { configString, fetchJson } from "../http.js";
|
|
2
|
+
import { formatResultsAsContent, resolveTimeoutSeconds } from "../result.js";
|
|
3
|
+
import { buildJinaSearchRequestHeaders, resolveJinaAuthMode, resolveJinaSearchBaseUrl, resolveJinaSearchUrl, } from "./jina-config.js";
|
|
4
|
+
export async function searchJina(config, query, count, signal) {
|
|
5
|
+
const provider = "jina";
|
|
6
|
+
const cfg = config.providers?.jina;
|
|
7
|
+
const auth = resolveJinaAuthMode(cfg?.auth);
|
|
8
|
+
if (!auth) {
|
|
9
|
+
return {
|
|
10
|
+
ok: false,
|
|
11
|
+
provider,
|
|
12
|
+
error: "invalid_auth",
|
|
13
|
+
message: 'Invalid providers.jina.auth. Use "direct" (s.jina.ai) or "deeproute" (Console /api/mirror/jina).',
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
const apiKey = configString(cfg?.apiKey);
|
|
17
|
+
if (auth === "deeproute" && !apiKey) {
|
|
18
|
+
return {
|
|
19
|
+
ok: false,
|
|
20
|
+
provider,
|
|
21
|
+
error: "missing_api_key",
|
|
22
|
+
message: "deeproute: set providers.jina.apiKey to your DeepRoute user sk (Bearer).",
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
const baseUrl = resolveJinaSearchBaseUrl(cfg, auth);
|
|
26
|
+
if (!baseUrl) {
|
|
27
|
+
return {
|
|
28
|
+
ok: false,
|
|
29
|
+
provider,
|
|
30
|
+
error: "missing_base_url",
|
|
31
|
+
message: "deeproute: set providers.jina.baseUrl to Console origin (e.g. http://host:20316).",
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
const url = resolveJinaSearchUrl(baseUrl, auth);
|
|
35
|
+
const startedAt = Date.now();
|
|
36
|
+
try {
|
|
37
|
+
const { data } = await fetchJson(url, {
|
|
38
|
+
method: "POST",
|
|
39
|
+
headers: buildJinaSearchRequestHeaders(auth, apiKey),
|
|
40
|
+
body: { q: query, num: count },
|
|
41
|
+
timeoutSeconds: resolveTimeoutSeconds(config),
|
|
42
|
+
signal,
|
|
43
|
+
});
|
|
44
|
+
const raw = Array.isArray(data.data)
|
|
45
|
+
? data.data
|
|
46
|
+
: Array.isArray(data.results)
|
|
47
|
+
? data.results
|
|
48
|
+
: [];
|
|
49
|
+
const results = raw.slice(0, count).map((entry) => ({
|
|
50
|
+
title: entry.title ?? "",
|
|
51
|
+
url: entry.url ?? "",
|
|
52
|
+
snippet: entry.description ?? entry.content ?? "",
|
|
53
|
+
siteName: entry.url ? safeHostname(entry.url) : undefined,
|
|
54
|
+
}));
|
|
55
|
+
const citations = [...new Set(results.map((r) => r.url).filter(Boolean))];
|
|
56
|
+
return {
|
|
57
|
+
ok: true,
|
|
58
|
+
provider,
|
|
59
|
+
query,
|
|
60
|
+
tookMs: Date.now() - startedAt,
|
|
61
|
+
results,
|
|
62
|
+
citations,
|
|
63
|
+
content: formatResultsAsContent(results),
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
catch (err) {
|
|
67
|
+
const raw = err instanceof Error ? err.message : String(err);
|
|
68
|
+
const needsKeyHint = raw.includes("401") || raw.includes("AuthenticationRequired");
|
|
69
|
+
const hint = needsKeyHint
|
|
70
|
+
? auth === "deeproute"
|
|
71
|
+
? " Jina Search requires a valid Jina API Key in DeepRoute Console → 扩展服务 (secret jina)."
|
|
72
|
+
: " Jina Search requires a valid Jina API Key in providers.jina.apiKey."
|
|
73
|
+
: "";
|
|
74
|
+
return {
|
|
75
|
+
ok: false,
|
|
76
|
+
provider,
|
|
77
|
+
error: "jina_request_failed",
|
|
78
|
+
message: raw + hint,
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
function safeHostname(url) {
|
|
83
|
+
try {
|
|
84
|
+
return new URL(url).hostname;
|
|
85
|
+
}
|
|
86
|
+
catch {
|
|
87
|
+
return undefined;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
//# sourceMappingURL=jina.js.map
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
import { configString, fetchJson, trimTrailingSlashes } from "../http.js";
|
|
2
|
+
import { pluginConfigPath } from "../plugin-meta.js";
|
|
3
|
+
import { resolveTimeoutSeconds } from "../result.js";
|
|
4
|
+
const DEFAULT_MODEL = "kimi-k2.6";
|
|
5
|
+
const KIMI_THINKING_MODELS = new Set(["kimi-k2.6", "kimi-k2.5"]);
|
|
6
|
+
const WEB_SEARCH_TOOL = {
|
|
7
|
+
type: "builtin_function",
|
|
8
|
+
function: { name: "$web_search" },
|
|
9
|
+
};
|
|
10
|
+
function normalizeOptionalString(value) {
|
|
11
|
+
return typeof value === "string" && value.trim() ? value.trim() : undefined;
|
|
12
|
+
}
|
|
13
|
+
function extractMessageText(message) {
|
|
14
|
+
const content = message?.content?.trim();
|
|
15
|
+
if (content) {
|
|
16
|
+
return content;
|
|
17
|
+
}
|
|
18
|
+
return message?.reasoning_content?.trim() || undefined;
|
|
19
|
+
}
|
|
20
|
+
function extractCitations(data) {
|
|
21
|
+
const citations = (data.search_results ?? [])
|
|
22
|
+
.map((entry) => entry.url?.trim())
|
|
23
|
+
.filter((url) => Boolean(url));
|
|
24
|
+
for (const toolCall of data.choices?.[0]?.message?.tool_calls ?? []) {
|
|
25
|
+
const raw = toolCall.function?.arguments;
|
|
26
|
+
if (!raw) {
|
|
27
|
+
continue;
|
|
28
|
+
}
|
|
29
|
+
try {
|
|
30
|
+
const parsed = JSON.parse(raw);
|
|
31
|
+
const parsedUrl = normalizeOptionalString(parsed.url);
|
|
32
|
+
if (parsedUrl) {
|
|
33
|
+
citations.push(parsedUrl);
|
|
34
|
+
}
|
|
35
|
+
for (const result of parsed.search_results ?? []) {
|
|
36
|
+
const resultUrl = normalizeOptionalString(result.url);
|
|
37
|
+
if (resultUrl) {
|
|
38
|
+
citations.push(resultUrl);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
catch {
|
|
43
|
+
// ignore malformed tool arguments
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
return [...new Set(citations)];
|
|
47
|
+
}
|
|
48
|
+
function hasGroundingEvidence(data) {
|
|
49
|
+
if ((data.search_results ?? []).some((e) => e.url || e.title || e.content)) {
|
|
50
|
+
return true;
|
|
51
|
+
}
|
|
52
|
+
return extractCitations(data).length > 0;
|
|
53
|
+
}
|
|
54
|
+
export async function searchKimi(config, query, _count, signal) {
|
|
55
|
+
const provider = "kimi";
|
|
56
|
+
const cfg = config.providers?.kimi;
|
|
57
|
+
const baseUrl = trimTrailingSlashes(configString(cfg?.baseUrl) ?? "");
|
|
58
|
+
if (!baseUrl) {
|
|
59
|
+
return {
|
|
60
|
+
ok: false,
|
|
61
|
+
provider,
|
|
62
|
+
error: "missing_base_url",
|
|
63
|
+
message: `OpenAI-compatible baseUrl missing. Set ${pluginConfigPath("providers.kimi.baseUrl")}.`,
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
const apiKey = configString(cfg?.apiKey);
|
|
67
|
+
if (!apiKey) {
|
|
68
|
+
return {
|
|
69
|
+
ok: false,
|
|
70
|
+
provider,
|
|
71
|
+
error: "missing_api_key",
|
|
72
|
+
message: `Bearer apiKey missing. Set ${pluginConfigPath("providers.kimi.apiKey")}.`,
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
const model = cfg?.model?.trim() || DEFAULT_MODEL;
|
|
76
|
+
const endpoint = `${baseUrl}/chat/completions`;
|
|
77
|
+
const timeoutSeconds = resolveTimeoutSeconds(config);
|
|
78
|
+
const startedAt = Date.now();
|
|
79
|
+
const messages = [
|
|
80
|
+
{ role: "user", content: query },
|
|
81
|
+
];
|
|
82
|
+
const collectedCitations = new Set();
|
|
83
|
+
let grounded = false;
|
|
84
|
+
try {
|
|
85
|
+
for (let round = 0; round < 3; round += 1) {
|
|
86
|
+
const body = {
|
|
87
|
+
model,
|
|
88
|
+
messages,
|
|
89
|
+
tools: [WEB_SEARCH_TOOL],
|
|
90
|
+
};
|
|
91
|
+
if (KIMI_THINKING_MODELS.has(model)) {
|
|
92
|
+
body.thinking = { type: "disabled" };
|
|
93
|
+
}
|
|
94
|
+
const { data } = await fetchJson(endpoint, {
|
|
95
|
+
method: "POST",
|
|
96
|
+
headers: {
|
|
97
|
+
"Content-Type": "application/json",
|
|
98
|
+
Authorization: `Bearer ${apiKey}`,
|
|
99
|
+
},
|
|
100
|
+
body,
|
|
101
|
+
timeoutSeconds,
|
|
102
|
+
signal,
|
|
103
|
+
});
|
|
104
|
+
if (hasGroundingEvidence(data)) {
|
|
105
|
+
grounded = true;
|
|
106
|
+
}
|
|
107
|
+
for (const citation of extractCitations(data)) {
|
|
108
|
+
collectedCitations.add(citation);
|
|
109
|
+
}
|
|
110
|
+
const choice = data.choices?.[0];
|
|
111
|
+
const message = choice?.message;
|
|
112
|
+
const text = extractMessageText(message);
|
|
113
|
+
const toolCalls = message?.tool_calls ?? [];
|
|
114
|
+
if (choice?.finish_reason !== "tool_calls" || toolCalls.length === 0) {
|
|
115
|
+
const content = text ?? "No response from Kimi.";
|
|
116
|
+
const citations = [...collectedCitations];
|
|
117
|
+
const results = citations.map((url, index) => ({
|
|
118
|
+
title: `Source ${index + 1}`,
|
|
119
|
+
url,
|
|
120
|
+
snippet: "",
|
|
121
|
+
}));
|
|
122
|
+
if (!grounded) {
|
|
123
|
+
return {
|
|
124
|
+
ok: false,
|
|
125
|
+
provider,
|
|
126
|
+
error: "kimi_ungrounded",
|
|
127
|
+
message: "Kimi returned a completion without web-search grounding. Try another provider in fallback.",
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
return {
|
|
131
|
+
ok: true,
|
|
132
|
+
provider,
|
|
133
|
+
query,
|
|
134
|
+
tookMs: Date.now() - startedAt,
|
|
135
|
+
results,
|
|
136
|
+
citations,
|
|
137
|
+
content,
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
messages.push({
|
|
141
|
+
role: "assistant",
|
|
142
|
+
content: message?.content ?? "",
|
|
143
|
+
...(message?.reasoning_content
|
|
144
|
+
? { reasoning_content: message.reasoning_content }
|
|
145
|
+
: {}),
|
|
146
|
+
tool_calls: toolCalls,
|
|
147
|
+
});
|
|
148
|
+
let pushed = false;
|
|
149
|
+
for (const toolCall of toolCalls) {
|
|
150
|
+
const toolCallId = toolCall.id?.trim();
|
|
151
|
+
const toolCallName = toolCall.function?.name?.trim();
|
|
152
|
+
const toolContent = toolCall.function?.arguments?.trim();
|
|
153
|
+
if (!toolCallId || !toolCallName || !toolContent) {
|
|
154
|
+
continue;
|
|
155
|
+
}
|
|
156
|
+
if (toolCallName === WEB_SEARCH_TOOL.function.name) {
|
|
157
|
+
grounded = true;
|
|
158
|
+
}
|
|
159
|
+
pushed = true;
|
|
160
|
+
messages.push({
|
|
161
|
+
role: "tool",
|
|
162
|
+
tool_call_id: toolCallId,
|
|
163
|
+
name: toolCallName,
|
|
164
|
+
content: toolContent,
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
if (!pushed) {
|
|
168
|
+
const content = text ?? "No response from Kimi.";
|
|
169
|
+
return {
|
|
170
|
+
ok: true,
|
|
171
|
+
provider,
|
|
172
|
+
query,
|
|
173
|
+
tookMs: Date.now() - startedAt,
|
|
174
|
+
results: [],
|
|
175
|
+
citations: [...collectedCitations],
|
|
176
|
+
content,
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
return {
|
|
181
|
+
ok: false,
|
|
182
|
+
provider,
|
|
183
|
+
error: "kimi_incomplete",
|
|
184
|
+
message: "Kimi search exceeded maximum tool rounds without a final answer.",
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
catch (err) {
|
|
188
|
+
return {
|
|
189
|
+
ok: false,
|
|
190
|
+
provider,
|
|
191
|
+
error: "kimi_request_failed",
|
|
192
|
+
message: err instanceof Error ? err.message : String(err),
|
|
193
|
+
};
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
//# sourceMappingURL=kimi.js.map
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { configString, trimTrailingSlashes } from "../http.js";
|
|
2
|
+
import { joinMirrorPath, resolveDeeprouteMirrorRoot, } from "./deeproute-mirror.js";
|
|
3
|
+
import { buildDeeprouteBearerHeaders, resolveProviderAuthMode, } from "./provider-auth.js";
|
|
4
|
+
export const DEFAULT_METASO_DIRECT_BASE = "https://metaso.cn";
|
|
5
|
+
export const METASO_SEARCH_PATH = "/api/v1/search";
|
|
6
|
+
export function resolveMetasoAuthMode(auth) {
|
|
7
|
+
return resolveProviderAuthMode(auth);
|
|
8
|
+
}
|
|
9
|
+
export function resolveMetasoSearchUrl(cfg, auth) {
|
|
10
|
+
const configured = configString(cfg?.baseUrl);
|
|
11
|
+
if (auth === "deeproute") {
|
|
12
|
+
if (!configured)
|
|
13
|
+
return undefined;
|
|
14
|
+
const root = resolveDeeprouteMirrorRoot(configured, "metaso");
|
|
15
|
+
return joinMirrorPath(root, METASO_SEARCH_PATH);
|
|
16
|
+
}
|
|
17
|
+
const base = configured
|
|
18
|
+
? trimTrailingSlashes(configured)
|
|
19
|
+
: DEFAULT_METASO_DIRECT_BASE;
|
|
20
|
+
return joinMirrorPath(base, METASO_SEARCH_PATH);
|
|
21
|
+
}
|
|
22
|
+
export function buildMetasoRequestHeaders(auth, apiKey) {
|
|
23
|
+
const headers = {
|
|
24
|
+
Accept: "application/json",
|
|
25
|
+
"Content-Type": "application/json",
|
|
26
|
+
};
|
|
27
|
+
if (auth === "deeproute") {
|
|
28
|
+
Object.assign(headers, buildDeeprouteBearerHeaders(apiKey));
|
|
29
|
+
}
|
|
30
|
+
else {
|
|
31
|
+
headers.Authorization = `Bearer ${apiKey}`;
|
|
32
|
+
}
|
|
33
|
+
return headers;
|
|
34
|
+
}
|
|
35
|
+
//# sourceMappingURL=metaso-config.js.map
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import { configString, fetchJson } from "../http.js";
|
|
2
|
+
import { formatResultsAsContent, resolveTimeoutSeconds } from "../result.js";
|
|
3
|
+
import { buildMetasoRequestHeaders, resolveMetasoAuthMode, resolveMetasoSearchUrl, } from "./metaso-config.js";
|
|
4
|
+
export async function searchMetaso(config, query, count, signal) {
|
|
5
|
+
const provider = "metaso";
|
|
6
|
+
const cfg = config.providers?.metaso;
|
|
7
|
+
const auth = resolveMetasoAuthMode(cfg?.auth);
|
|
8
|
+
if (!auth) {
|
|
9
|
+
return {
|
|
10
|
+
ok: false,
|
|
11
|
+
provider,
|
|
12
|
+
error: "invalid_auth",
|
|
13
|
+
message: 'Invalid providers.metaso.auth. Use "direct" or "deeproute".',
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
const apiKey = configString(cfg?.apiKey);
|
|
17
|
+
if (!apiKey) {
|
|
18
|
+
return {
|
|
19
|
+
ok: false,
|
|
20
|
+
provider,
|
|
21
|
+
error: "missing_api_key",
|
|
22
|
+
message: auth === "deeproute"
|
|
23
|
+
? "deeproute: set providers.metaso.apiKey to your DeepRoute user sk."
|
|
24
|
+
: "direct: set providers.metaso.apiKey to your Metaso API key.",
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
const url = resolveMetasoSearchUrl(cfg, auth);
|
|
28
|
+
if (!url) {
|
|
29
|
+
return {
|
|
30
|
+
ok: false,
|
|
31
|
+
provider,
|
|
32
|
+
error: "missing_base_url",
|
|
33
|
+
message: "deeproute: set providers.metaso.baseUrl to Console origin (e.g. http://host:20316).",
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
const startedAt = Date.now();
|
|
37
|
+
try {
|
|
38
|
+
const { data } = await fetchJson(url, {
|
|
39
|
+
method: "POST",
|
|
40
|
+
headers: buildMetasoRequestHeaders(auth, apiKey),
|
|
41
|
+
body: {
|
|
42
|
+
q: query,
|
|
43
|
+
scope: "webpage",
|
|
44
|
+
page: 1,
|
|
45
|
+
includeSummary: true,
|
|
46
|
+
includeRawContent: false,
|
|
47
|
+
conciseSnippet: true,
|
|
48
|
+
},
|
|
49
|
+
timeoutSeconds: resolveTimeoutSeconds(config),
|
|
50
|
+
signal,
|
|
51
|
+
});
|
|
52
|
+
if (data.errCode !== undefined && data.errCode !== 0) {
|
|
53
|
+
return {
|
|
54
|
+
ok: false,
|
|
55
|
+
provider,
|
|
56
|
+
error: "metaso_api_error",
|
|
57
|
+
message: data.errMsg ?? `Metaso errCode ${data.errCode}`,
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
const nested = data.data ?? data;
|
|
61
|
+
const raw = Array.isArray(nested.webpages)
|
|
62
|
+
? nested.webpages
|
|
63
|
+
: Array.isArray(nested.results)
|
|
64
|
+
? nested.results
|
|
65
|
+
: Array.isArray(nested.items)
|
|
66
|
+
? nested.items
|
|
67
|
+
: [];
|
|
68
|
+
const results = raw.slice(0, count).map((entry) => ({
|
|
69
|
+
title: entry.title ?? "",
|
|
70
|
+
url: entry.link ?? "",
|
|
71
|
+
snippet: entry.snippet ?? entry.summary ?? "",
|
|
72
|
+
siteName: entry.link ? safeHostname(entry.link) : undefined,
|
|
73
|
+
}));
|
|
74
|
+
const citations = [...new Set(results.map((r) => r.url).filter(Boolean))];
|
|
75
|
+
return {
|
|
76
|
+
ok: true,
|
|
77
|
+
provider,
|
|
78
|
+
query,
|
|
79
|
+
tookMs: Date.now() - startedAt,
|
|
80
|
+
results,
|
|
81
|
+
citations,
|
|
82
|
+
content: formatResultsAsContent(results),
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
catch (err) {
|
|
86
|
+
return {
|
|
87
|
+
ok: false,
|
|
88
|
+
provider,
|
|
89
|
+
error: "metaso_request_failed",
|
|
90
|
+
message: err instanceof Error ? err.message : String(err),
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
function safeHostname(url) {
|
|
95
|
+
try {
|
|
96
|
+
return new URL(url).hostname;
|
|
97
|
+
}
|
|
98
|
+
catch {
|
|
99
|
+
return undefined;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
//# sourceMappingURL=metaso.js.map
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { configString, trimTrailingSlashes } from "../http.js";
|
|
2
|
+
import { joinMirrorPath, resolveDeeprouteMirrorRoot, } from "./deeproute-mirror.js";
|
|
3
|
+
import { buildDeeprouteBearerHeaders, resolveProviderAuthMode, } from "./provider-auth.js";
|
|
4
|
+
export const DEFAULT_MINIMAX_GLOBAL_BASE = "https://api.minimax.io";
|
|
5
|
+
export const DEFAULT_MINIMAX_CN_BASE = "https://api.minimaxi.com";
|
|
6
|
+
export const MINIMAX_SEARCH_PATH = "/v1/coding_plan/search";
|
|
7
|
+
export function resolveMinimaxAuthMode(auth) {
|
|
8
|
+
return resolveProviderAuthMode(auth);
|
|
9
|
+
}
|
|
10
|
+
export function resolveMinimaxBaseUrl(cfg, auth) {
|
|
11
|
+
const configured = configString(cfg?.baseUrl);
|
|
12
|
+
if (configured) {
|
|
13
|
+
if (auth === "deeproute") {
|
|
14
|
+
return resolveDeeprouteMirrorRoot(configured, "minimax");
|
|
15
|
+
}
|
|
16
|
+
return trimTrailingSlashes(configured);
|
|
17
|
+
}
|
|
18
|
+
if (auth === "direct") {
|
|
19
|
+
const region = (cfg?.region ?? "global").trim().toLowerCase();
|
|
20
|
+
if (region === "cn" || region === "china") {
|
|
21
|
+
return DEFAULT_MINIMAX_CN_BASE;
|
|
22
|
+
}
|
|
23
|
+
return DEFAULT_MINIMAX_GLOBAL_BASE;
|
|
24
|
+
}
|
|
25
|
+
return undefined;
|
|
26
|
+
}
|
|
27
|
+
export function resolveMinimaxSearchUrl(baseUrl, auth) {
|
|
28
|
+
if (auth === "deeproute") {
|
|
29
|
+
return joinMirrorPath(baseUrl, MINIMAX_SEARCH_PATH);
|
|
30
|
+
}
|
|
31
|
+
const trimmed = trimTrailingSlashes(baseUrl || DEFAULT_MINIMAX_GLOBAL_BASE);
|
|
32
|
+
try {
|
|
33
|
+
const url = new URL(trimmed);
|
|
34
|
+
url.pathname = `${url.pathname.replace(/\/$/, "")}${MINIMAX_SEARCH_PATH}`;
|
|
35
|
+
return url.toString();
|
|
36
|
+
}
|
|
37
|
+
catch {
|
|
38
|
+
return `${DEFAULT_MINIMAX_GLOBAL_BASE}${MINIMAX_SEARCH_PATH}`;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
export function buildMinimaxRequestHeaders(auth, apiKey) {
|
|
42
|
+
const headers = {
|
|
43
|
+
Accept: "application/json",
|
|
44
|
+
"Content-Type": "application/json",
|
|
45
|
+
};
|
|
46
|
+
if (auth === "deeproute") {
|
|
47
|
+
Object.assign(headers, buildDeeprouteBearerHeaders(apiKey));
|
|
48
|
+
}
|
|
49
|
+
else {
|
|
50
|
+
headers.Authorization = `Bearer ${apiKey}`;
|
|
51
|
+
}
|
|
52
|
+
return headers;
|
|
53
|
+
}
|
|
54
|
+
//# sourceMappingURL=minimax-config.js.map
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { configString, fetchJson } from "../http.js";
|
|
2
|
+
import { formatResultsAsContent, resolveTimeoutSeconds } from "../result.js";
|
|
3
|
+
import { buildMinimaxRequestHeaders, resolveMinimaxAuthMode, resolveMinimaxBaseUrl, resolveMinimaxSearchUrl, } from "./minimax-config.js";
|
|
4
|
+
export async function searchMinimax(config, query, count, signal) {
|
|
5
|
+
const provider = "minimax";
|
|
6
|
+
const cfg = config.providers?.minimax;
|
|
7
|
+
const auth = resolveMinimaxAuthMode(cfg?.auth);
|
|
8
|
+
if (!auth) {
|
|
9
|
+
return {
|
|
10
|
+
ok: false,
|
|
11
|
+
provider,
|
|
12
|
+
error: "invalid_auth",
|
|
13
|
+
message: 'Invalid providers.minimax.auth. Use "direct" (Token Plan key) or "deeproute" (Console mirror + user sk).',
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
const apiKey = configString(cfg?.apiKey);
|
|
17
|
+
if (!apiKey) {
|
|
18
|
+
return {
|
|
19
|
+
ok: false,
|
|
20
|
+
provider,
|
|
21
|
+
error: "missing_api_key",
|
|
22
|
+
message: auth === "deeproute"
|
|
23
|
+
? "deeproute: set providers.minimax.apiKey to your DeepRoute user sk (Bearer)."
|
|
24
|
+
: "direct: set providers.minimax.apiKey to your MiniMax Token Plan key.",
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
const baseUrl = resolveMinimaxBaseUrl(cfg, auth);
|
|
28
|
+
if (!baseUrl) {
|
|
29
|
+
return {
|
|
30
|
+
ok: false,
|
|
31
|
+
provider,
|
|
32
|
+
error: "missing_base_url",
|
|
33
|
+
message: "deeproute: set providers.minimax.baseUrl (e.g. http://host:20316/api/mirror/minimax).",
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
const url = resolveMinimaxSearchUrl(baseUrl, auth);
|
|
37
|
+
const startedAt = Date.now();
|
|
38
|
+
try {
|
|
39
|
+
const { data } = await fetchJson(url, {
|
|
40
|
+
method: "POST",
|
|
41
|
+
headers: buildMinimaxRequestHeaders(auth, apiKey),
|
|
42
|
+
body: { query, max_results: count },
|
|
43
|
+
timeoutSeconds: resolveTimeoutSeconds(config),
|
|
44
|
+
signal,
|
|
45
|
+
});
|
|
46
|
+
const raw = Array.isArray(data.results)
|
|
47
|
+
? data.results
|
|
48
|
+
: Array.isArray(data.data?.results)
|
|
49
|
+
? data.data.results
|
|
50
|
+
: [];
|
|
51
|
+
const results = raw.slice(0, count).map((entry) => ({
|
|
52
|
+
title: entry.title ?? "",
|
|
53
|
+
url: entry.url ?? "",
|
|
54
|
+
snippet: entry.snippet ?? entry.description ?? entry.content ?? "",
|
|
55
|
+
siteName: entry.url ? safeHostname(entry.url) : undefined,
|
|
56
|
+
}));
|
|
57
|
+
const citations = [...new Set(results.map((r) => r.url).filter(Boolean))];
|
|
58
|
+
return {
|
|
59
|
+
ok: true,
|
|
60
|
+
provider,
|
|
61
|
+
query,
|
|
62
|
+
tookMs: Date.now() - startedAt,
|
|
63
|
+
results,
|
|
64
|
+
citations,
|
|
65
|
+
content: formatResultsAsContent(results),
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
catch (err) {
|
|
69
|
+
return {
|
|
70
|
+
ok: false,
|
|
71
|
+
provider,
|
|
72
|
+
error: "minimax_request_failed",
|
|
73
|
+
message: err instanceof Error ? err.message : String(err),
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
function safeHostname(url) {
|
|
78
|
+
try {
|
|
79
|
+
return new URL(url).hostname;
|
|
80
|
+
}
|
|
81
|
+
catch {
|
|
82
|
+
return undefined;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
//# sourceMappingURL=minimax.js.map
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { configString } from "../http.js";
|
|
2
|
+
export function resolveProviderAuthMode(auth) {
|
|
3
|
+
const value = configString(auth)?.toLowerCase();
|
|
4
|
+
if (!value || value === "direct") {
|
|
5
|
+
return "direct";
|
|
6
|
+
}
|
|
7
|
+
if (value === "deeproute") {
|
|
8
|
+
return "deeproute";
|
|
9
|
+
}
|
|
10
|
+
return undefined;
|
|
11
|
+
}
|
|
12
|
+
export function buildDeeprouteBearerHeaders(apiKey) {
|
|
13
|
+
return {
|
|
14
|
+
Authorization: `Bearer ${apiKey}`,
|
|
15
|
+
Accept: "application/json",
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
//# sourceMappingURL=provider-auth.js.map
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { configString, trimTrailingSlashes } from "../http.js";
|
|
2
|
+
import { joinMirrorPath, resolveDeeprouteMirrorRoot, } from "./deeproute-mirror.js";
|
|
3
|
+
import { buildDeeprouteBearerHeaders, resolveProviderAuthMode, } from "./provider-auth.js";
|
|
4
|
+
export const RAGFLOW_RETRIEVAL_PATH = "/api/v1/retrieval";
|
|
5
|
+
export const DEFAULT_RAGFLOW_TIMEOUT_SECONDS = 120;
|
|
6
|
+
export function resolveRagflowAuthMode(auth) {
|
|
7
|
+
return resolveProviderAuthMode(auth);
|
|
8
|
+
}
|
|
9
|
+
export function parseRagflowDatasetIds(raw) {
|
|
10
|
+
const trimmed = configString(raw);
|
|
11
|
+
if (!trimmed) {
|
|
12
|
+
return [];
|
|
13
|
+
}
|
|
14
|
+
return trimmed
|
|
15
|
+
.split(",")
|
|
16
|
+
.map((part) => part.trim())
|
|
17
|
+
.filter(Boolean);
|
|
18
|
+
}
|
|
19
|
+
export function resolveRagflowBaseUrl(cfg, auth) {
|
|
20
|
+
const configured = configString(cfg?.baseUrl);
|
|
21
|
+
if (!configured) {
|
|
22
|
+
return undefined;
|
|
23
|
+
}
|
|
24
|
+
if (auth === "deeproute") {
|
|
25
|
+
return resolveDeeprouteMirrorRoot(configured, "ragflow");
|
|
26
|
+
}
|
|
27
|
+
return trimTrailingSlashes(configured);
|
|
28
|
+
}
|
|
29
|
+
export function resolveRagflowRetrievalUrl(baseUrl, auth) {
|
|
30
|
+
if (auth === "deeproute") {
|
|
31
|
+
return joinMirrorPath(baseUrl, RAGFLOW_RETRIEVAL_PATH);
|
|
32
|
+
}
|
|
33
|
+
const trimmed = trimTrailingSlashes(baseUrl);
|
|
34
|
+
try {
|
|
35
|
+
const url = new URL(trimmed);
|
|
36
|
+
url.pathname = `${url.pathname.replace(/\/$/, "")}${RAGFLOW_RETRIEVAL_PATH}`;
|
|
37
|
+
return url.toString();
|
|
38
|
+
}
|
|
39
|
+
catch {
|
|
40
|
+
return `${trimmed}${RAGFLOW_RETRIEVAL_PATH}`;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
export function buildRagflowRequestHeaders(auth, apiKey) {
|
|
44
|
+
const headers = {
|
|
45
|
+
Accept: "application/json",
|
|
46
|
+
"Content-Type": "application/json",
|
|
47
|
+
};
|
|
48
|
+
if (auth === "deeproute") {
|
|
49
|
+
Object.assign(headers, buildDeeprouteBearerHeaders(apiKey));
|
|
50
|
+
}
|
|
51
|
+
else {
|
|
52
|
+
headers.Authorization = `Bearer ${apiKey}`;
|
|
53
|
+
}
|
|
54
|
+
return headers;
|
|
55
|
+
}
|
|
56
|
+
export function resolveRagflowTimeoutSeconds(cfg) {
|
|
57
|
+
const value = cfg?.timeoutSeconds;
|
|
58
|
+
if (typeof value === "number" && value > 0) {
|
|
59
|
+
return Math.min(120, Math.floor(value));
|
|
60
|
+
}
|
|
61
|
+
return DEFAULT_RAGFLOW_TIMEOUT_SECONDS;
|
|
62
|
+
}
|
|
63
|
+
//# sourceMappingURL=ragflow-config.js.map
|