pi-provider-kimi-code 0.5.0 → 0.6.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/README.md +63 -40
- package/index.ts +60 -46
- package/package.json +25 -23
- package/src/config.ts +82 -37
- package/src/constants.ts +3 -4
- package/src/models.ts +23 -4
- package/src/oauth.ts +1 -0
- package/src/payload.ts +22 -6
- package/src/stream.ts +54 -8
- package/src/tools/common.ts +115 -0
- package/src/tools/datasource.ts +191 -0
- package/src/tools/moonshot.ts +43 -112
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
import { defineTool } from "@earendil-works/pi-coding-agent";
|
|
2
|
+
import { Type, type Static } from "@earendil-works/pi-ai";
|
|
3
|
+
|
|
4
|
+
import {
|
|
5
|
+
type BuildKimiToolOptions,
|
|
6
|
+
buildHeaders,
|
|
7
|
+
getKimiDatasourceUrl,
|
|
8
|
+
buildKimiToolDeps,
|
|
9
|
+
buildTimeoutSignal,
|
|
10
|
+
errorResult,
|
|
11
|
+
fetchWithAuthRetry,
|
|
12
|
+
firstText,
|
|
13
|
+
readErrorBody,
|
|
14
|
+
shouldCollapse,
|
|
15
|
+
textComponent,
|
|
16
|
+
} from "./common.ts";
|
|
17
|
+
|
|
18
|
+
const DATASOURCE_DEFINITIONS = [
|
|
19
|
+
{
|
|
20
|
+
name: "stock_finance_data",
|
|
21
|
+
description: "A/HK/US stocks, prices, financial reports, indicators, shareholders.",
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
name: "yahoo_finance",
|
|
25
|
+
description: "Global finance: quotes, analyst ratings, options, indexes, historical prices.",
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
name: "world_bank_open_data",
|
|
29
|
+
description: "World Bank macro data: GDP, inflation, population, country comparisons.",
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
name: "tianyancha",
|
|
33
|
+
description: "Chinese enterprise registry: shareholders, legal representative, risks, patents.",
|
|
34
|
+
},
|
|
35
|
+
{ name: "arxiv", description: "arXiv preprints: paper search, abstracts, arXiv IDs." },
|
|
36
|
+
{
|
|
37
|
+
name: "scholar",
|
|
38
|
+
description: "Google Scholar: academic papers, authors, citations, high-citation surveys.",
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
name: "yuandian_law",
|
|
42
|
+
description: "Chinese laws, regulations, judicial cases; labor contracts and legal clauses.",
|
|
43
|
+
},
|
|
44
|
+
] as const;
|
|
45
|
+
|
|
46
|
+
const DATASOURCE_NAMES = DATASOURCE_DEFINITIONS.map((source) => source.name);
|
|
47
|
+
|
|
48
|
+
export const kimiDatasourceSchema = Type.Object({
|
|
49
|
+
data_source_name: Type.Union(
|
|
50
|
+
DATASOURCE_NAMES.map((name) => Type.Literal(name)),
|
|
51
|
+
{
|
|
52
|
+
description:
|
|
53
|
+
"Source: stock_finance_data stocks/reports; yahoo_finance quotes/options; world_bank_open_data GDP/population; tianyancha Chinese company registry/legal rep; arxiv preprints; scholar citations; yuandian_law Chinese laws/cases/contracts.",
|
|
54
|
+
},
|
|
55
|
+
),
|
|
56
|
+
api_name: Type.Optional(
|
|
57
|
+
Type.String({
|
|
58
|
+
description: "API name from the datasource docs. Omit to get docs first.",
|
|
59
|
+
}),
|
|
60
|
+
),
|
|
61
|
+
params: Type.Optional(
|
|
62
|
+
Type.Record(Type.String(), Type.Unknown(), {
|
|
63
|
+
description: "Parameters matching the selected datasource API docs.",
|
|
64
|
+
}),
|
|
65
|
+
),
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
export type KimiDatasourceParams = Static<typeof kimiDatasourceSchema>;
|
|
69
|
+
|
|
70
|
+
interface DatasourceToolResponse {
|
|
71
|
+
is_success?: boolean;
|
|
72
|
+
error?: unknown;
|
|
73
|
+
result?: {
|
|
74
|
+
assistant?: unknown;
|
|
75
|
+
user?: unknown;
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function extractTextFromResult(result: unknown): string {
|
|
80
|
+
if (!Array.isArray(result)) return "";
|
|
81
|
+
const texts: string[] = [];
|
|
82
|
+
for (const item of result) {
|
|
83
|
+
if (typeof item === "string") {
|
|
84
|
+
texts.push(item);
|
|
85
|
+
} else if (typeof item === "object" && item !== null) {
|
|
86
|
+
const record = item as Record<string, unknown>;
|
|
87
|
+
if (typeof record.text === "string") {
|
|
88
|
+
texts.push(record.text);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
return texts.join("\n");
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function extractResponseText(data: DatasourceToolResponse): string {
|
|
96
|
+
if (data.result?.assistant) {
|
|
97
|
+
const text = extractTextFromResult(data.result.assistant);
|
|
98
|
+
if (text) return text;
|
|
99
|
+
}
|
|
100
|
+
if (data.result?.user) {
|
|
101
|
+
const text = extractTextFromResult(data.result.user);
|
|
102
|
+
if (text) return text;
|
|
103
|
+
}
|
|
104
|
+
return JSON.stringify(data, null, 2);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
export function buildKimiDatasourceTool(options: BuildKimiToolOptions = {}) {
|
|
108
|
+
const deps = buildKimiToolDeps(options);
|
|
109
|
+
const defaultCollapsed = options.defaultCollapsed ?? true;
|
|
110
|
+
|
|
111
|
+
return defineTool({
|
|
112
|
+
name: "kimi_datasource",
|
|
113
|
+
label: "Kimi Datasource",
|
|
114
|
+
description:
|
|
115
|
+
"Call Kimi datasource APIs. Covers stocks/finance, macro data, Chinese company registry, papers, and Chinese law/cases/contracts. First omit api_name to get docs, then call api_name with documented params.",
|
|
116
|
+
promptSnippet: "Query Kimi datasource APIs; get docs first, then call documented api_name",
|
|
117
|
+
parameters: kimiDatasourceSchema,
|
|
118
|
+
|
|
119
|
+
async execute(toolCallId, params, signal) {
|
|
120
|
+
const accessToken = deps.getAccessToken();
|
|
121
|
+
if (!accessToken) {
|
|
122
|
+
return errorResult<string>(
|
|
123
|
+
"Missing Kimi Code OAuth credentials. Run /login kimi-coding first.",
|
|
124
|
+
);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
const timeout = buildTimeoutSignal(signal);
|
|
128
|
+
try {
|
|
129
|
+
const apiName = params.api_name?.trim();
|
|
130
|
+
const body = apiName
|
|
131
|
+
? {
|
|
132
|
+
method: "call_data_source_tool",
|
|
133
|
+
params: {
|
|
134
|
+
data_source_name: params.data_source_name,
|
|
135
|
+
api_name: apiName,
|
|
136
|
+
params: params.params ?? {},
|
|
137
|
+
},
|
|
138
|
+
}
|
|
139
|
+
: {
|
|
140
|
+
method: "get_data_source_desc",
|
|
141
|
+
params: { data_source_name: params.data_source_name },
|
|
142
|
+
};
|
|
143
|
+
|
|
144
|
+
const response = await fetchWithAuthRetry(deps, accessToken, (token) =>
|
|
145
|
+
deps.fetch(getKimiDatasourceUrl(), {
|
|
146
|
+
method: "POST",
|
|
147
|
+
headers: buildHeaders(token, toolCallId),
|
|
148
|
+
body: JSON.stringify(body),
|
|
149
|
+
signal: timeout.signal,
|
|
150
|
+
}),
|
|
151
|
+
);
|
|
152
|
+
|
|
153
|
+
if (!response.ok) {
|
|
154
|
+
const bodyText = await readErrorBody(response);
|
|
155
|
+
return errorResult<string>(
|
|
156
|
+
`kimi_datasource failed: ${response.status}${bodyText ? ` ${bodyText}` : ""}`,
|
|
157
|
+
);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
const data = (await response.json()) as DatasourceToolResponse;
|
|
161
|
+
if (data.is_success === false) {
|
|
162
|
+
const errorText =
|
|
163
|
+
typeof data.error === "string"
|
|
164
|
+
? data.error
|
|
165
|
+
: JSON.stringify(data.error ?? data, null, 2);
|
|
166
|
+
return errorResult<string>(`kimi_datasource error: ${errorText}`);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
const text = extractResponseText(data);
|
|
170
|
+
return {
|
|
171
|
+
content: [{ type: "text", text }],
|
|
172
|
+
details: text,
|
|
173
|
+
};
|
|
174
|
+
} catch (error) {
|
|
175
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
176
|
+
return errorResult<string>(`kimi_datasource failed: ${message}`);
|
|
177
|
+
} finally {
|
|
178
|
+
timeout.cleanup();
|
|
179
|
+
}
|
|
180
|
+
},
|
|
181
|
+
|
|
182
|
+
renderResult(result, renderOptions) {
|
|
183
|
+
const text = firstText(result);
|
|
184
|
+
if (!shouldCollapse(defaultCollapsed, renderOptions.expanded)) {
|
|
185
|
+
return textComponent(text);
|
|
186
|
+
}
|
|
187
|
+
const preview = text.length > 200 ? `${text.slice(0, 200)}…` : text;
|
|
188
|
+
return textComponent(`kimi_datasource: ${text.length} chars\n${preview}`);
|
|
189
|
+
},
|
|
190
|
+
});
|
|
191
|
+
}
|
package/src/tools/moonshot.ts
CHANGED
|
@@ -1,11 +1,19 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { defineTool, type AgentToolResult } from "@earendil-works/pi-coding-agent";
|
|
2
2
|
import { Type, type Static } from "@earendil-works/pi-ai";
|
|
3
3
|
|
|
4
|
-
import {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
4
|
+
import {
|
|
5
|
+
type BuildKimiToolOptions,
|
|
6
|
+
buildHeaders,
|
|
7
|
+
getKimiBaseV1,
|
|
8
|
+
buildKimiToolDeps,
|
|
9
|
+
buildTimeoutSignal,
|
|
10
|
+
errorResult,
|
|
11
|
+
fetchWithAuthRetry,
|
|
12
|
+
firstText,
|
|
13
|
+
readErrorBody,
|
|
14
|
+
shouldCollapse,
|
|
15
|
+
textComponent,
|
|
16
|
+
} from "./common.ts";
|
|
9
17
|
|
|
10
18
|
export const moonshotSearchSchema = Type.Object({
|
|
11
19
|
query: Type.String({ description: "The query text to search for." }),
|
|
@@ -44,74 +52,13 @@ export interface MoonshotFetchResult {
|
|
|
44
52
|
content: string;
|
|
45
53
|
}
|
|
46
54
|
|
|
47
|
-
interface MoonshotToolDeps {
|
|
48
|
-
fetch: typeof fetch;
|
|
49
|
-
getAccessToken: () => string | null;
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
export interface BuildMoonshotToolOptions {
|
|
53
|
-
deps?: Partial<MoonshotToolDeps>;
|
|
54
|
-
defaultCollapsed?: boolean;
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
interface MoonshotSearchResponse {
|
|
58
|
-
search_results?: unknown;
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
function defaultGetAccessToken(): string | null {
|
|
62
|
-
const credential = AuthStorage.create().get(PROVIDER_ID);
|
|
63
|
-
if (!credential || credential.type !== "oauth") return null;
|
|
64
|
-
return credential.access;
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
function buildDeps(options: BuildMoonshotToolOptions = {}): MoonshotToolDeps {
|
|
68
|
-
return {
|
|
69
|
-
fetch: options.deps?.fetch ?? fetch,
|
|
70
|
-
getAccessToken: options.deps?.getAccessToken ?? defaultGetAccessToken,
|
|
71
|
-
};
|
|
72
|
-
}
|
|
73
|
-
|
|
74
55
|
function clampLimit(value: number | undefined): number {
|
|
75
56
|
if (typeof value !== "number" || !Number.isFinite(value)) return 5;
|
|
76
57
|
return Math.max(1, Math.min(20, Math.trunc(value)));
|
|
77
58
|
}
|
|
78
59
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
content: [{ type: "text", text: message }],
|
|
82
|
-
details: undefined as T,
|
|
83
|
-
};
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
function buildTimeoutSignal(signal: AbortSignal | undefined): {
|
|
87
|
-
signal: AbortSignal;
|
|
88
|
-
cleanup: () => void;
|
|
89
|
-
} {
|
|
90
|
-
const controller = new AbortController();
|
|
91
|
-
const timeout = setTimeout(() => controller.abort(), MOONSHOT_TIMEOUT_MS);
|
|
92
|
-
const abort = () => controller.abort();
|
|
93
|
-
if (signal?.aborted) {
|
|
94
|
-
controller.abort();
|
|
95
|
-
} else {
|
|
96
|
-
signal?.addEventListener("abort", abort, { once: true });
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
return {
|
|
100
|
-
signal: controller.signal,
|
|
101
|
-
cleanup: () => {
|
|
102
|
-
clearTimeout(timeout);
|
|
103
|
-
signal?.removeEventListener("abort", abort);
|
|
104
|
-
},
|
|
105
|
-
};
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
function buildHeaders(accessToken: string, toolCallId: string): Record<string, string> {
|
|
109
|
-
return {
|
|
110
|
-
...getCommonHeaders(),
|
|
111
|
-
Authorization: `Bearer ${accessToken}`,
|
|
112
|
-
"Content-Type": "application/json",
|
|
113
|
-
"X-Msh-Tool-Call-Id": toolCallId,
|
|
114
|
-
};
|
|
60
|
+
interface MoonshotSearchResponse {
|
|
61
|
+
search_results?: unknown;
|
|
115
62
|
}
|
|
116
63
|
|
|
117
64
|
function stringField(record: Record<string, unknown>, key: string): string {
|
|
@@ -141,22 +88,6 @@ function searchResultsText(results: MoonshotSearchResult[]): string {
|
|
|
141
88
|
return JSON.stringify(results, null, 2);
|
|
142
89
|
}
|
|
143
90
|
|
|
144
|
-
function textComponent(text: string) {
|
|
145
|
-
return {
|
|
146
|
-
render: () => text.split("\n"),
|
|
147
|
-
invalidate: () => {},
|
|
148
|
-
};
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
function firstText(result: AgentToolResult<unknown>): string {
|
|
152
|
-
const first = result.content[0];
|
|
153
|
-
return first?.type === "text" ? first.text : "";
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
function shouldCollapse(defaultCollapsed: boolean, expanded: boolean | undefined): boolean {
|
|
157
|
-
return defaultCollapsed && expanded !== true;
|
|
158
|
-
}
|
|
159
|
-
|
|
160
91
|
function renderSearchResult(
|
|
161
92
|
result: AgentToolResult<MoonshotSearchResult[]>,
|
|
162
93
|
expanded: boolean | undefined,
|
|
@@ -192,12 +123,8 @@ function renderFetchResult(
|
|
|
192
123
|
);
|
|
193
124
|
}
|
|
194
125
|
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
export function buildMoonshotSearchTool(options: BuildMoonshotToolOptions = {}) {
|
|
200
|
-
const deps = buildDeps(options);
|
|
126
|
+
export function buildMoonshotSearchTool(options: BuildKimiToolOptions = {}) {
|
|
127
|
+
const deps = buildKimiToolDeps(options);
|
|
201
128
|
const defaultCollapsed = options.defaultCollapsed ?? true;
|
|
202
129
|
|
|
203
130
|
return defineTool({
|
|
@@ -219,17 +146,19 @@ export function buildMoonshotSearchTool(options: BuildMoonshotToolOptions = {})
|
|
|
219
146
|
const includeContent = params.include_content === true;
|
|
220
147
|
const timeout = buildTimeoutSignal(signal);
|
|
221
148
|
try {
|
|
222
|
-
const response = await deps
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
149
|
+
const response = await fetchWithAuthRetry(deps, accessToken, (token) =>
|
|
150
|
+
deps.fetch(`${getKimiBaseV1()}/search`, {
|
|
151
|
+
method: "POST",
|
|
152
|
+
headers: buildHeaders(token, toolCallId),
|
|
153
|
+
body: JSON.stringify({
|
|
154
|
+
text_query: params.query,
|
|
155
|
+
limit,
|
|
156
|
+
enable_page_crawling: includeContent,
|
|
157
|
+
timeout_seconds: 30,
|
|
158
|
+
}),
|
|
159
|
+
signal: timeout.signal,
|
|
230
160
|
}),
|
|
231
|
-
|
|
232
|
-
});
|
|
161
|
+
);
|
|
233
162
|
|
|
234
163
|
if (!response.ok) {
|
|
235
164
|
const body = await readErrorBody(response);
|
|
@@ -258,8 +187,8 @@ export function buildMoonshotSearchTool(options: BuildMoonshotToolOptions = {})
|
|
|
258
187
|
});
|
|
259
188
|
}
|
|
260
189
|
|
|
261
|
-
export function buildMoonshotFetchTool(options:
|
|
262
|
-
const deps =
|
|
190
|
+
export function buildMoonshotFetchTool(options: BuildKimiToolOptions = {}) {
|
|
191
|
+
const deps = buildKimiToolDeps(options);
|
|
263
192
|
const defaultCollapsed = options.defaultCollapsed ?? true;
|
|
264
193
|
|
|
265
194
|
return defineTool({
|
|
@@ -279,15 +208,17 @@ export function buildMoonshotFetchTool(options: BuildMoonshotToolOptions = {}) {
|
|
|
279
208
|
|
|
280
209
|
const timeout = buildTimeoutSignal(signal);
|
|
281
210
|
try {
|
|
282
|
-
const response = await deps
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
211
|
+
const response = await fetchWithAuthRetry(deps, accessToken, (token) =>
|
|
212
|
+
deps.fetch(`${getKimiBaseV1()}/fetch`, {
|
|
213
|
+
method: "POST",
|
|
214
|
+
headers: {
|
|
215
|
+
...buildHeaders(token, toolCallId),
|
|
216
|
+
Accept: "text/markdown",
|
|
217
|
+
},
|
|
218
|
+
body: JSON.stringify({ url: params.url }),
|
|
219
|
+
signal: timeout.signal,
|
|
220
|
+
}),
|
|
221
|
+
);
|
|
291
222
|
|
|
292
223
|
if (!response.ok) {
|
|
293
224
|
const body = await readErrorBody(response);
|