@kweaver-ai/kweaver-sdk 0.7.3 → 0.8.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/README.md +49 -0
- package/README.zh.md +44 -0
- package/bin/kweaver.js +12 -11
- package/dist/api/agent-observability.d.ts +51 -0
- package/dist/api/agent-observability.js +108 -0
- package/dist/api/bkn-backend.d.ts +1 -0
- package/dist/api/bkn-backend.js +1 -1
- package/dist/api/bkn-metrics.d.ts +59 -0
- package/dist/api/bkn-metrics.js +129 -0
- package/dist/api/conversations.d.ts +43 -2
- package/dist/api/conversations.js +77 -23
- package/dist/api/datasources.d.ts +2 -20
- package/dist/api/datasources.js +7 -86
- package/dist/api/model-invocation.d.ts +58 -0
- package/dist/api/model-invocation.js +203 -0
- package/dist/api/models.d.ts +79 -0
- package/dist/api/models.js +183 -0
- package/dist/api/ontology-query-metrics.d.ts +14 -0
- package/dist/api/ontology-query-metrics.js +30 -0
- package/dist/api/trace.d.ts +44 -0
- package/dist/api/trace.js +81 -0
- package/dist/api/vega.d.ts +53 -0
- package/dist/api/vega.js +144 -0
- package/dist/bundled-model-templates.d.ts +17 -0
- package/dist/bundled-model-templates.js +24 -0
- package/dist/cli.js +15 -0
- package/dist/client.d.ts +3 -0
- package/dist/client.js +5 -0
- package/dist/commands/agent.d.ts +7 -1
- package/dist/commands/agent.js +75 -21
- package/dist/commands/bkn-metric.d.ts +1 -0
- package/dist/commands/bkn-metric.js +406 -0
- package/dist/commands/bkn-ops.js +28 -16
- package/dist/commands/bkn-utils.d.ts +38 -0
- package/dist/commands/bkn-utils.js +54 -0
- package/dist/commands/bkn.js +4 -0
- package/dist/commands/ds.js +14 -3
- package/dist/commands/explore-chat.js +2 -2
- package/dist/commands/model.d.ts +72 -0
- package/dist/commands/model.js +1315 -0
- package/dist/commands/trace.d.ts +14 -0
- package/dist/commands/trace.js +168 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.js +5 -0
- package/dist/resources/datasources.js +2 -1
- package/dist/resources/models.d.ts +40 -0
- package/dist/resources/models.js +88 -0
- package/dist/templates/model/llm-basic.json +13 -0
- package/dist/templates/model/manifest.json +16 -0
- package/dist/templates/model/small-basic.json +6 -0
- package/dist/trace-core/diagnose/builtin-rules/excessive-tool-calls-per-turn.d.ts +2 -0
- package/dist/trace-core/diagnose/builtin-rules/excessive-tool-calls-per-turn.js +15 -0
- package/dist/trace-core/diagnose/builtin-rules/excessive-tool-calls-per-turn.yaml +16 -0
- package/dist/trace-core/diagnose/builtin-rules/llm-response-truncated-no-continue.d.ts +2 -0
- package/dist/trace-core/diagnose/builtin-rules/llm-response-truncated-no-continue.js +44 -0
- package/dist/trace-core/diagnose/builtin-rules/llm-response-truncated-no-continue.yaml +15 -0
- package/dist/trace-core/diagnose/builtin-rules/register.d.ts +1 -0
- package/dist/trace-core/diagnose/builtin-rules/register.js +11 -0
- package/dist/trace-core/diagnose/builtin-rules/retrieval-empty-no-fallback.d.ts +2 -0
- package/dist/trace-core/diagnose/builtin-rules/retrieval-empty-no-fallback.js +29 -0
- package/dist/trace-core/diagnose/builtin-rules/retrieval-empty-no-fallback.yaml +15 -0
- package/dist/trace-core/diagnose/builtin-rules/tool-error-swallowed.d.ts +2 -0
- package/dist/trace-core/diagnose/builtin-rules/tool-error-swallowed.js +45 -0
- package/dist/trace-core/diagnose/builtin-rules/tool-error-swallowed.yaml +15 -0
- package/dist/trace-core/diagnose/builtin-rules/tool-loop-no-state-change.d.ts +2 -0
- package/dist/trace-core/diagnose/builtin-rules/tool-loop-no-state-change.js +38 -0
- package/dist/trace-core/diagnose/builtin-rules/tool-loop-no-state-change.yaml +16 -0
- package/dist/trace-core/diagnose/index.d.ts +9 -0
- package/dist/trace-core/diagnose/index.js +104 -0
- package/dist/trace-core/diagnose/predicate-registry.d.ts +7 -0
- package/dist/trace-core/diagnose/predicate-registry.js +30 -0
- package/dist/trace-core/diagnose/report-assembler.d.ts +12 -0
- package/dist/trace-core/diagnose/report-assembler.js +90 -0
- package/dist/trace-core/diagnose/rule-loader.d.ts +11 -0
- package/dist/trace-core/diagnose/rule-loader.js +86 -0
- package/dist/trace-core/diagnose/schemas.d.ts +109 -0
- package/dist/trace-core/diagnose/schemas.js +94 -0
- package/dist/trace-core/diagnose/signal-probe.d.ts +5 -0
- package/dist/trace-core/diagnose/signal-probe.js +21 -0
- package/dist/trace-core/diagnose/synthesizer-template.d.ts +2 -0
- package/dist/trace-core/diagnose/synthesizer-template.js +49 -0
- package/dist/trace-core/diagnose/trace-shaper.d.ts +3 -0
- package/dist/trace-core/diagnose/trace-shaper.js +72 -0
- package/dist/trace-core/diagnose/types.d.ts +124 -0
- package/dist/trace-core/diagnose/types.js +1 -0
- package/dist/utils/trace-views.d.ts +44 -0
- package/dist/utils/trace-views.js +425 -0
- package/package.json +15 -5
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
import { buildHeaders } from "./headers.js";
|
|
2
|
+
import { HttpError } from "../utils/http.js";
|
|
3
|
+
export const MF_MODEL_MANAGER_PATH_PREFIX = "/api/mf-model-manager/v1";
|
|
4
|
+
function resolveManagerOrigin(options) {
|
|
5
|
+
const env = process.env.KWEAVER_MF_MODEL_MANAGER_URL;
|
|
6
|
+
const raw = options.mfManagerBaseUrl ?? (env && env.length > 0 ? env : undefined) ?? options.baseUrl;
|
|
7
|
+
return raw.replace(/\/+$/, "");
|
|
8
|
+
}
|
|
9
|
+
function managerEndpoint(options, relPath) {
|
|
10
|
+
const origin = resolveManagerOrigin(options);
|
|
11
|
+
const path = relPath.startsWith("/") ? relPath : `/${relPath}`;
|
|
12
|
+
return `${origin}${MF_MODEL_MANAGER_PATH_PREFIX}${path}`;
|
|
13
|
+
}
|
|
14
|
+
async function fetchJson(url, accessToken, businessDomain, init) {
|
|
15
|
+
const headers = {
|
|
16
|
+
...buildHeaders(accessToken, businessDomain),
|
|
17
|
+
...(init.headers ?? {}),
|
|
18
|
+
};
|
|
19
|
+
const response = await fetch(url, { ...init, headers });
|
|
20
|
+
const text = await response.text();
|
|
21
|
+
if (!response.ok) {
|
|
22
|
+
throw new HttpError(response.status, response.statusText, text);
|
|
23
|
+
}
|
|
24
|
+
if (!text.trim())
|
|
25
|
+
return null;
|
|
26
|
+
return JSON.parse(text);
|
|
27
|
+
}
|
|
28
|
+
// ── Client-side validation (mirrors mf-model-manager mutual exclusion) ─────
|
|
29
|
+
/**
|
|
30
|
+
* Ensure small-model request bodies do not combine direct `model_config` with adapter mode.
|
|
31
|
+
*/
|
|
32
|
+
export function assertSmallModelConfigAdapterExclusive(body) {
|
|
33
|
+
const cfg = body.model_config;
|
|
34
|
+
const hasConfig = cfg != null &&
|
|
35
|
+
typeof cfg === "object" &&
|
|
36
|
+
!Array.isArray(cfg) &&
|
|
37
|
+
Object.keys(cfg).length > 0;
|
|
38
|
+
const adapter = body.adapter === true;
|
|
39
|
+
const code = typeof body.adapter_code === "string" && body.adapter_code.length > 0;
|
|
40
|
+
if (hasConfig && (adapter
|
|
41
|
+
|| code)) {
|
|
42
|
+
throw new Error("model_config cannot be combined with adapter or adapter_code.");
|
|
43
|
+
}
|
|
44
|
+
if (!hasConfig && (!adapter
|
|
45
|
+
|| !code)) {
|
|
46
|
+
throw new Error("Either model_config (non-empty) or adapter=true with adapter_code is required.");
|
|
47
|
+
}
|
|
48
|
+
if (adapter && !code) {
|
|
49
|
+
throw new Error("adapter=true requires adapter_code.");
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
/** For edit: validate mutual exclusion only when config or adapter fields are present. */
|
|
53
|
+
export function assertSmallModelEditBody(body) {
|
|
54
|
+
const cfg = body.model_config;
|
|
55
|
+
const hasConfig = cfg != null &&
|
|
56
|
+
typeof cfg === "object" &&
|
|
57
|
+
!Array.isArray(cfg) &&
|
|
58
|
+
Object.keys(cfg).length > 0;
|
|
59
|
+
const adapter = body.adapter === true;
|
|
60
|
+
const code = typeof body.adapter_code === "string" && body.adapter_code.length > 0;
|
|
61
|
+
if (!hasConfig && !adapter && !code) {
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
assertSmallModelConfigAdapterExclusive(body);
|
|
65
|
+
}
|
|
66
|
+
export async function listLlmModels(options) {
|
|
67
|
+
const { accessToken, businessDomain = "bd_public", page, size, order = "desc", rule = "update_time", series = "all", name = "", apiModel = "", modelType = "", quota, } = options;
|
|
68
|
+
const params = new URLSearchParams({
|
|
69
|
+
page: String(page),
|
|
70
|
+
size: String(size),
|
|
71
|
+
order,
|
|
72
|
+
rule,
|
|
73
|
+
series,
|
|
74
|
+
name,
|
|
75
|
+
api_model: apiModel,
|
|
76
|
+
model_type: modelType,
|
|
77
|
+
});
|
|
78
|
+
if (quota !== undefined) {
|
|
79
|
+
params.set("quota", String(quota));
|
|
80
|
+
}
|
|
81
|
+
const url = `${managerEndpoint(options, "/llm/list")}?${params.toString()}`;
|
|
82
|
+
return fetchJson(url, accessToken, businessDomain, { method: "GET" });
|
|
83
|
+
}
|
|
84
|
+
export async function getLlmModel(options) {
|
|
85
|
+
const { accessToken, businessDomain = "bd_public", modelId } = options;
|
|
86
|
+
const params = new URLSearchParams({ model_id: modelId });
|
|
87
|
+
const url = `${managerEndpoint(options, "/llm/get")}?${params.toString()}`;
|
|
88
|
+
return fetchJson(url, accessToken, businessDomain, { method: "GET" });
|
|
89
|
+
}
|
|
90
|
+
export async function addLlmModel(options) {
|
|
91
|
+
const { accessToken, businessDomain = "bd_public", body } = options;
|
|
92
|
+
const url = managerEndpoint(options, "/llm/add");
|
|
93
|
+
return fetchJson(url, accessToken, businessDomain, {
|
|
94
|
+
method: "POST",
|
|
95
|
+
headers: { "content-type": "application/json" },
|
|
96
|
+
body: JSON.stringify(body),
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
export async function editLlmModel(options) {
|
|
100
|
+
const { accessToken, businessDomain = "bd_public", body } = options;
|
|
101
|
+
const url = managerEndpoint(options, "/llm/edit");
|
|
102
|
+
return fetchJson(url, accessToken, businessDomain, {
|
|
103
|
+
method: "POST",
|
|
104
|
+
headers: { "content-type": "application/json" },
|
|
105
|
+
body: JSON.stringify(body),
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
export async function deleteLlmModels(options) {
|
|
109
|
+
const { accessToken, businessDomain = "bd_public", modelIds } = options;
|
|
110
|
+
const url = managerEndpoint(options, "/llm/delete");
|
|
111
|
+
return fetchJson(url, accessToken, businessDomain, {
|
|
112
|
+
method: "POST",
|
|
113
|
+
headers: { "content-type": "application/json" },
|
|
114
|
+
body: JSON.stringify({ model_ids: modelIds }),
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
export async function testLlmModel(options) {
|
|
118
|
+
const { accessToken, businessDomain = "bd_public", body } = options;
|
|
119
|
+
const url = managerEndpoint(options, "/llm/test");
|
|
120
|
+
return fetchJson(url, accessToken, businessDomain, {
|
|
121
|
+
method: "POST",
|
|
122
|
+
headers: { "content-type": "application/json" },
|
|
123
|
+
body: JSON.stringify(body),
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
export async function listSmallModels(options) {
|
|
127
|
+
const { accessToken, businessDomain = "bd_public", page, size, order = "desc", rule = "update_time", modelName = "", modelType = "", modelSeries = "", } = options;
|
|
128
|
+
const params = new URLSearchParams({
|
|
129
|
+
order,
|
|
130
|
+
rule,
|
|
131
|
+
page: String(page),
|
|
132
|
+
size: String(size),
|
|
133
|
+
model_name: modelName,
|
|
134
|
+
model_type: modelType,
|
|
135
|
+
model_series: modelSeries,
|
|
136
|
+
});
|
|
137
|
+
const url = `${managerEndpoint(options, "/small-model/list")}?${params.toString()}`;
|
|
138
|
+
return fetchJson(url, accessToken, businessDomain, { method: "GET" });
|
|
139
|
+
}
|
|
140
|
+
export async function getSmallModel(options) {
|
|
141
|
+
const { accessToken, businessDomain = "bd_public", modelId } = options;
|
|
142
|
+
const params = new URLSearchParams({ model_id: modelId });
|
|
143
|
+
const url = `${managerEndpoint(options, "/small-model/get")}?${params.toString()}`;
|
|
144
|
+
return fetchJson(url, accessToken, businessDomain, { method: "GET" });
|
|
145
|
+
}
|
|
146
|
+
export async function addSmallModel(options) {
|
|
147
|
+
assertSmallModelConfigAdapterExclusive(options.body);
|
|
148
|
+
const { accessToken, businessDomain = "bd_public", body } = options;
|
|
149
|
+
const url = managerEndpoint(options, "/small-model/add");
|
|
150
|
+
return fetchJson(url, accessToken, businessDomain, {
|
|
151
|
+
method: "POST",
|
|
152
|
+
headers: { "content-type": "application/json" },
|
|
153
|
+
body: JSON.stringify(body),
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
export async function editSmallModel(options) {
|
|
157
|
+
assertSmallModelEditBody(options.body);
|
|
158
|
+
const { accessToken, businessDomain = "bd_public", body } = options;
|
|
159
|
+
const url = managerEndpoint(options, "/small-model/edit");
|
|
160
|
+
return fetchJson(url, accessToken, businessDomain, {
|
|
161
|
+
method: "POST",
|
|
162
|
+
headers: { "content-type": "application/json" },
|
|
163
|
+
body: JSON.stringify(body),
|
|
164
|
+
});
|
|
165
|
+
}
|
|
166
|
+
export async function deleteSmallModels(options) {
|
|
167
|
+
const { accessToken, businessDomain = "bd_public", modelIds } = options;
|
|
168
|
+
const url = managerEndpoint(options, "/small-model/delete");
|
|
169
|
+
return fetchJson(url, accessToken, businessDomain, {
|
|
170
|
+
method: "POST",
|
|
171
|
+
headers: { "content-type": "application/json" },
|
|
172
|
+
body: JSON.stringify({ model_ids: modelIds }),
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
export async function testSmallModel(options) {
|
|
176
|
+
const { accessToken, businessDomain = "bd_public", body } = options;
|
|
177
|
+
const url = managerEndpoint(options, "/small-model/test");
|
|
178
|
+
return fetchJson(url, accessToken, businessDomain, {
|
|
179
|
+
method: "POST",
|
|
180
|
+
headers: { "content-type": "application/json" },
|
|
181
|
+
body: JSON.stringify(body),
|
|
182
|
+
});
|
|
183
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { OntologyQueryBaseOptions } from "./ontology-query.js";
|
|
2
|
+
export interface MetricQueryDataOptions extends OntologyQueryBaseOptions {
|
|
3
|
+
metricId: string;
|
|
4
|
+
body: string;
|
|
5
|
+
branch?: string;
|
|
6
|
+
fillNull?: boolean;
|
|
7
|
+
}
|
|
8
|
+
export interface MetricDryRunOptions extends OntologyQueryBaseOptions {
|
|
9
|
+
body: string;
|
|
10
|
+
branch?: string;
|
|
11
|
+
fillNull?: boolean;
|
|
12
|
+
}
|
|
13
|
+
export declare function metricQueryData(options: MetricQueryDataOptions): Promise<string>;
|
|
14
|
+
export declare function metricDryRun(options: MetricDryRunOptions): Promise<string>;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { fetchWithRetry } from "./ontology-query.js";
|
|
2
|
+
import { buildHeaders } from "./headers.js";
|
|
3
|
+
function appendMetricQueryParams(url, branch, fillNull) {
|
|
4
|
+
if (branch !== undefined)
|
|
5
|
+
url.searchParams.set("branch", branch);
|
|
6
|
+
if (fillNull !== undefined)
|
|
7
|
+
url.searchParams.set("fill_null", String(fillNull));
|
|
8
|
+
}
|
|
9
|
+
export async function metricQueryData(options) {
|
|
10
|
+
const { baseUrl, accessToken, knId, metricId, body, businessDomain = "bd_public", branch, fillNull, } = options;
|
|
11
|
+
const base = baseUrl.replace(/\/+$/, "");
|
|
12
|
+
const url = new URL(`${base}/api/ontology-query/v1/knowledge-networks/${encodeURIComponent(knId)}/metrics/${encodeURIComponent(metricId)}/data`);
|
|
13
|
+
appendMetricQueryParams(url, branch, fillNull);
|
|
14
|
+
const headers = {
|
|
15
|
+
...buildHeaders(accessToken, businessDomain),
|
|
16
|
+
"content-type": "application/json",
|
|
17
|
+
};
|
|
18
|
+
return fetchWithRetry(url.toString(), { method: "POST", headers, body });
|
|
19
|
+
}
|
|
20
|
+
export async function metricDryRun(options) {
|
|
21
|
+
const { baseUrl, accessToken, knId, body, businessDomain = "bd_public", branch, fillNull, } = options;
|
|
22
|
+
const base = baseUrl.replace(/\/+$/, "");
|
|
23
|
+
const url = new URL(`${base}/api/ontology-query/v1/knowledge-networks/${encodeURIComponent(knId)}/metrics/dry-run`);
|
|
24
|
+
appendMetricQueryParams(url, branch, fillNull);
|
|
25
|
+
const headers = {
|
|
26
|
+
...buildHeaders(accessToken, businessDomain),
|
|
27
|
+
"content-type": "application/json",
|
|
28
|
+
};
|
|
29
|
+
return fetchWithRetry(url.toString(), { method: "POST", headers, body });
|
|
30
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `RawSpan`-flavored view of conversation trace data, for diagnose rule
|
|
3
|
+
* predicates. The HTTP / two-hop / auth concerns live in `./agent-observability`;
|
|
4
|
+
* this module only normalizes the raw `_source` documents into the minimal
|
|
5
|
+
* span shape rules read.
|
|
6
|
+
*/
|
|
7
|
+
export { TraceFetchError } from "./agent-observability.js";
|
|
8
|
+
export interface GetSpansByConversationIdOpts {
|
|
9
|
+
baseUrl: string;
|
|
10
|
+
token: string;
|
|
11
|
+
businessDomain: string;
|
|
12
|
+
conversationId: string;
|
|
13
|
+
/** Cap on `terms` aggregation bucket count. Default 100. */
|
|
14
|
+
maxTraceIds?: number;
|
|
15
|
+
/** Cap on spans returned by the second query. Default 2000. */
|
|
16
|
+
maxSpans?: number;
|
|
17
|
+
}
|
|
18
|
+
export interface RawSpan {
|
|
19
|
+
spanId: string;
|
|
20
|
+
parentSpanId: string | null;
|
|
21
|
+
name?: string;
|
|
22
|
+
startTimeUnixNano?: string;
|
|
23
|
+
endTimeUnixNano?: string;
|
|
24
|
+
status?: {
|
|
25
|
+
code?: string;
|
|
26
|
+
};
|
|
27
|
+
attributes?: Record<string, unknown>;
|
|
28
|
+
/** OTel traceId for the trace this span belongs to (when known). */
|
|
29
|
+
traceId?: string;
|
|
30
|
+
}
|
|
31
|
+
export interface GetSpansByConversationIdResult {
|
|
32
|
+
/** Distinct traceIds observed for this conversation. */
|
|
33
|
+
traceIds: string[];
|
|
34
|
+
/** All spans across all observed traceIds, mapped to `RawSpan` shape. */
|
|
35
|
+
spans: RawSpan[];
|
|
36
|
+
/** True if the agg saw `sum_other_doc_count > 0` (more traceIds than maxTraceIds). */
|
|
37
|
+
truncated: boolean;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* ISO timestamp → nanos-since-epoch string. Preserves up to 9 fractional digits.
|
|
41
|
+
* Falls back to ms precision when the input lacks a fractional component.
|
|
42
|
+
*/
|
|
43
|
+
export declare function isoToNanos(iso: string | undefined): string | undefined;
|
|
44
|
+
export declare function getSpansByConversationId(opts: GetSpansByConversationIdOpts): Promise<GetSpansByConversationIdResult>;
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `RawSpan`-flavored view of conversation trace data, for diagnose rule
|
|
3
|
+
* predicates. The HTTP / two-hop / auth concerns live in `./agent-observability`;
|
|
4
|
+
* this module only normalizes the raw `_source` documents into the minimal
|
|
5
|
+
* span shape rules read.
|
|
6
|
+
*/
|
|
7
|
+
import { fetchRawSpansByConversation } from "./agent-observability.js";
|
|
8
|
+
export { TraceFetchError } from "./agent-observability.js";
|
|
9
|
+
/**
|
|
10
|
+
* ISO timestamp → nanos-since-epoch string. Preserves up to 9 fractional digits.
|
|
11
|
+
* Falls back to ms precision when the input lacks a fractional component.
|
|
12
|
+
*/
|
|
13
|
+
export function isoToNanos(iso) {
|
|
14
|
+
if (!iso)
|
|
15
|
+
return undefined;
|
|
16
|
+
// "YYYY-MM-DDTHH:MM:SS.fffffffffZ" or "...+08:00"
|
|
17
|
+
const m = iso.match(/^(.+?)\.(\d{1,9})(Z|[+-]\d{2}:?\d{2})$/);
|
|
18
|
+
if (!m) {
|
|
19
|
+
const ms = Date.parse(iso);
|
|
20
|
+
if (Number.isNaN(ms))
|
|
21
|
+
return undefined;
|
|
22
|
+
return (BigInt(ms) * 1000000n).toString();
|
|
23
|
+
}
|
|
24
|
+
const ms = Date.parse(m[1] + m[3]);
|
|
25
|
+
if (Number.isNaN(ms))
|
|
26
|
+
return undefined;
|
|
27
|
+
const frac = m[2].padEnd(9, "0").slice(0, 9);
|
|
28
|
+
const seconds = BigInt(Math.floor(ms / 1000));
|
|
29
|
+
return (seconds * 1000000000n + BigInt(frac)).toString();
|
|
30
|
+
}
|
|
31
|
+
function normalizeToRawSpan(source) {
|
|
32
|
+
const spanIdRaw = source.spanId ?? source.span_id;
|
|
33
|
+
const spanId = typeof spanIdRaw === "string" ? spanIdRaw : "";
|
|
34
|
+
if (!spanId)
|
|
35
|
+
return null;
|
|
36
|
+
const parentRaw = source.parentSpanId ?? source.parent_span_id ?? source.parentSpanID;
|
|
37
|
+
const parentSpanId = typeof parentRaw === "string" && parentRaw !== "" && parentRaw !== "0" ? parentRaw : null;
|
|
38
|
+
// Prefer pre-normalized nanos (synthetic fixtures); else derive from ISO.
|
|
39
|
+
let startTimeUnixNano;
|
|
40
|
+
let endTimeUnixNano;
|
|
41
|
+
if (typeof source.startTimeUnixNano === "string")
|
|
42
|
+
startTimeUnixNano = source.startTimeUnixNano;
|
|
43
|
+
else if (typeof source.startTime === "string")
|
|
44
|
+
startTimeUnixNano = isoToNanos(source.startTime);
|
|
45
|
+
if (typeof source.endTimeUnixNano === "string")
|
|
46
|
+
endTimeUnixNano = source.endTimeUnixNano;
|
|
47
|
+
else if (typeof source.endTime === "string")
|
|
48
|
+
endTimeUnixNano = isoToNanos(source.endTime);
|
|
49
|
+
const status = source.status;
|
|
50
|
+
const attributes = source.attributes;
|
|
51
|
+
const name = typeof source.name === "string" ? source.name : undefined;
|
|
52
|
+
const traceIdRaw = source.traceId ?? source.trace_id;
|
|
53
|
+
const traceId = typeof traceIdRaw === "string" ? traceIdRaw : undefined;
|
|
54
|
+
return {
|
|
55
|
+
spanId,
|
|
56
|
+
parentSpanId,
|
|
57
|
+
name,
|
|
58
|
+
startTimeUnixNano,
|
|
59
|
+
endTimeUnixNano,
|
|
60
|
+
status,
|
|
61
|
+
attributes,
|
|
62
|
+
traceId,
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
export async function getSpansByConversationId(opts) {
|
|
66
|
+
const fetched = await fetchRawSpansByConversation({
|
|
67
|
+
baseUrl: opts.baseUrl,
|
|
68
|
+
accessToken: opts.token,
|
|
69
|
+
businessDomain: opts.businessDomain,
|
|
70
|
+
conversationId: opts.conversationId,
|
|
71
|
+
maxTraceIds: opts.maxTraceIds,
|
|
72
|
+
maxSpans: opts.maxSpans,
|
|
73
|
+
});
|
|
74
|
+
const spans = [];
|
|
75
|
+
for (const src of fetched.rawSources) {
|
|
76
|
+
const span = normalizeToRawSpan(src);
|
|
77
|
+
if (span)
|
|
78
|
+
spans.push(span);
|
|
79
|
+
}
|
|
80
|
+
return { traceIds: fetched.traceIds, spans, truncated: fetched.truncated };
|
|
81
|
+
}
|
package/dist/api/vega.d.ts
CHANGED
|
@@ -238,3 +238,56 @@ export interface ListAllVegaResourcesOptions {
|
|
|
238
238
|
/** List all Vega resources (no catalog filter). Uses GET /resources — not /resources/list, which
|
|
239
239
|
* conflicts with GET /resources/{id} on some gateways (path segment "list" is treated as an id). */
|
|
240
240
|
export declare function listAllVegaResources(options: ListAllVegaResourcesOptions): Promise<string>;
|
|
241
|
+
export interface ListTablesWithColumnsOptions {
|
|
242
|
+
baseUrl: string;
|
|
243
|
+
accessToken: string;
|
|
244
|
+
/** A vega catalog id, not a legacy data-connection datasource UUID. */
|
|
245
|
+
id: string;
|
|
246
|
+
keyword?: string;
|
|
247
|
+
limit?: number;
|
|
248
|
+
offset?: number;
|
|
249
|
+
businessDomain?: string;
|
|
250
|
+
autoScan?: boolean;
|
|
251
|
+
}
|
|
252
|
+
/**
|
|
253
|
+
* List tables with column details from a vega catalog.
|
|
254
|
+
*
|
|
255
|
+
* Two-stage fetch:
|
|
256
|
+
* 1. GET /api/vega-backend/v1/catalogs/{id}/resources?category=table — list summaries
|
|
257
|
+
* 2. For each resource: GET /api/vega-backend/v1/resources/{rid} — pull source_metadata.columns
|
|
258
|
+
*
|
|
259
|
+
* If the catalog has no resources and `autoScan=true`, triggers a discover and
|
|
260
|
+
* retries the list once. The optional `keyword` filters summaries client-side
|
|
261
|
+
* before the per-resource detail fetches — useful to keep N+1 down to k+1.
|
|
262
|
+
*
|
|
263
|
+
* `id` is a vega catalog id.
|
|
264
|
+
*/
|
|
265
|
+
export declare function listTablesWithColumns(options: ListTablesWithColumnsOptions): Promise<string>;
|
|
266
|
+
export interface ScanMetadataOptions {
|
|
267
|
+
baseUrl: string;
|
|
268
|
+
accessToken: string;
|
|
269
|
+
id: string;
|
|
270
|
+
/** Retained for signature compatibility; ignored — vega catalog already knows its connector_type. */
|
|
271
|
+
dsType?: string;
|
|
272
|
+
businessDomain?: string;
|
|
273
|
+
}
|
|
274
|
+
/**
|
|
275
|
+
* Trigger a metadata scan for a vega catalog and wait for completion.
|
|
276
|
+
* `id` is a vega catalog id (e.g. `d7nicrcjto2s73d9g67g`), not a legacy
|
|
277
|
+
* data-connection datasource UUID.
|
|
278
|
+
*/
|
|
279
|
+
export declare function scanMetadata(options: ScanMetadataOptions): Promise<string>;
|
|
280
|
+
export interface ScanDatasourceMetadataOptions {
|
|
281
|
+
baseUrl: string;
|
|
282
|
+
accessToken: string;
|
|
283
|
+
id: string;
|
|
284
|
+
businessDomain?: string;
|
|
285
|
+
}
|
|
286
|
+
/**
|
|
287
|
+
* Trigger a metadata scan and wait for completion. `id` is a vega catalog id.
|
|
288
|
+
*
|
|
289
|
+
* @deprecated Use {@link scanMetadata} directly. This wrapper exists only for
|
|
290
|
+
* backward compatibility with callers that used the old data-connection-based
|
|
291
|
+
* `scanDatasourceMetadata` signature.
|
|
292
|
+
*/
|
|
293
|
+
export declare function scanDatasourceMetadata(options: ScanDatasourceMetadataOptions): Promise<string>;
|
package/dist/api/vega.js
CHANGED
|
@@ -488,3 +488,147 @@ export async function listAllVegaResources(options) {
|
|
|
488
488
|
businessDomain,
|
|
489
489
|
});
|
|
490
490
|
}
|
|
491
|
+
/**
|
|
492
|
+
* List tables with column details from a vega catalog.
|
|
493
|
+
*
|
|
494
|
+
* Two-stage fetch:
|
|
495
|
+
* 1. GET /api/vega-backend/v1/catalogs/{id}/resources?category=table — list summaries
|
|
496
|
+
* 2. For each resource: GET /api/vega-backend/v1/resources/{rid} — pull source_metadata.columns
|
|
497
|
+
*
|
|
498
|
+
* If the catalog has no resources and `autoScan=true`, triggers a discover and
|
|
499
|
+
* retries the list once. The optional `keyword` filters summaries client-side
|
|
500
|
+
* before the per-resource detail fetches — useful to keep N+1 down to k+1.
|
|
501
|
+
*
|
|
502
|
+
* `id` is a vega catalog id.
|
|
503
|
+
*/
|
|
504
|
+
export async function listTablesWithColumns(options) {
|
|
505
|
+
const { baseUrl, accessToken, id, keyword, limit, offset, businessDomain = "bd_public", autoScan = true, } = options;
|
|
506
|
+
async function listResourceSummaries() {
|
|
507
|
+
const body = await listVegaCatalogResources({
|
|
508
|
+
baseUrl,
|
|
509
|
+
accessToken,
|
|
510
|
+
id,
|
|
511
|
+
category: "table",
|
|
512
|
+
limit,
|
|
513
|
+
offset,
|
|
514
|
+
businessDomain,
|
|
515
|
+
});
|
|
516
|
+
const parsed = JSON.parse(body);
|
|
517
|
+
return Array.isArray(parsed) ? parsed : (parsed.entries ?? parsed.data ?? []);
|
|
518
|
+
}
|
|
519
|
+
let summaries = await listResourceSummaries();
|
|
520
|
+
if (summaries.length === 0 && autoScan) {
|
|
521
|
+
await scanMetadata({ baseUrl, accessToken, id, businessDomain });
|
|
522
|
+
summaries = await listResourceSummaries();
|
|
523
|
+
}
|
|
524
|
+
// Keyword filter applied after autoScan guard: if the catalog has tables but
|
|
525
|
+
// keyword matches none, we must NOT trigger a redundant discover.
|
|
526
|
+
if (keyword) {
|
|
527
|
+
const k = keyword.toLowerCase();
|
|
528
|
+
summaries = summaries.filter((it) => it.name.toLowerCase().includes(k));
|
|
529
|
+
}
|
|
530
|
+
const details = await Promise.all(summaries.map(async (s) => {
|
|
531
|
+
let body;
|
|
532
|
+
try {
|
|
533
|
+
body = await getVegaResource({
|
|
534
|
+
baseUrl,
|
|
535
|
+
accessToken,
|
|
536
|
+
id: s.id,
|
|
537
|
+
businessDomain,
|
|
538
|
+
});
|
|
539
|
+
}
|
|
540
|
+
catch (err) {
|
|
541
|
+
const reason = err instanceof Error ? err.message : String(err);
|
|
542
|
+
throw new Error(`vega resource ${s.id} fetch failed: ${reason}`);
|
|
543
|
+
}
|
|
544
|
+
const parsed = JSON.parse(body);
|
|
545
|
+
if (Array.isArray(parsed.entries)) {
|
|
546
|
+
const arr = parsed.entries;
|
|
547
|
+
if (arr.length === 0) {
|
|
548
|
+
throw new Error(`vega resource ${s.id} returned empty entries`);
|
|
549
|
+
}
|
|
550
|
+
return arr[0];
|
|
551
|
+
}
|
|
552
|
+
if (Array.isArray(parsed.data)) {
|
|
553
|
+
const arr = parsed.data;
|
|
554
|
+
if (arr.length === 0) {
|
|
555
|
+
throw new Error(`vega resource ${s.id} returned empty data`);
|
|
556
|
+
}
|
|
557
|
+
return arr[0];
|
|
558
|
+
}
|
|
559
|
+
return parsed;
|
|
560
|
+
}));
|
|
561
|
+
const tables = [];
|
|
562
|
+
for (const d of details) {
|
|
563
|
+
const columnsRaw = (d.source_metadata?.columns ?? []);
|
|
564
|
+
const tablePkArray = extractPrimaryKeys(d);
|
|
565
|
+
const columns = columnsRaw.map((c) => {
|
|
566
|
+
const name = String(c.name ?? c.field_name ?? "");
|
|
567
|
+
const flagged = isColumnPrimaryKey(c) || tablePkArray.includes(name);
|
|
568
|
+
return {
|
|
569
|
+
name,
|
|
570
|
+
type: String(c.type ?? c.field_type ?? "varchar"),
|
|
571
|
+
comment: typeof c.description === "string"
|
|
572
|
+
? c.description
|
|
573
|
+
: (typeof c.comment === "string" ? c.comment : undefined),
|
|
574
|
+
...(flagged ? { isPrimaryKey: true } : {}),
|
|
575
|
+
};
|
|
576
|
+
});
|
|
577
|
+
const synthesizedPks = tablePkArray.length > 0
|
|
578
|
+
? tablePkArray
|
|
579
|
+
: columns.filter((c) => c.isPrimaryKey).map((c) => c.name);
|
|
580
|
+
tables.push({
|
|
581
|
+
name: d.name,
|
|
582
|
+
columns,
|
|
583
|
+
...(synthesizedPks.length > 0 ? { primaryKeys: synthesizedPks } : {}),
|
|
584
|
+
});
|
|
585
|
+
}
|
|
586
|
+
return JSON.stringify(tables);
|
|
587
|
+
}
|
|
588
|
+
// Two PK metadata shapes are recognized — both confirmed conventions:
|
|
589
|
+
// - per-column `is_primary_key: true` (data-connection metadata standard)
|
|
590
|
+
// - per-column `column_key === "PRI"` (MySQL INFORMATION_SCHEMA pass-through)
|
|
591
|
+
// - table-level `primary_keys: string[]` (composite-PK carrier)
|
|
592
|
+
// Other plausible spellings (camelCase, singular keys, SQLite `pk` integer) are
|
|
593
|
+
// intentionally NOT recognized here — adding them speculatively risks false
|
|
594
|
+
// matches and creates code paths the test suite can't pin down. Extend only when
|
|
595
|
+
// a real backend response demonstrates the need.
|
|
596
|
+
function isColumnPrimaryKey(col) {
|
|
597
|
+
if (col.is_primary_key === true)
|
|
598
|
+
return true;
|
|
599
|
+
if (typeof col.column_key === "string" && col.column_key.toUpperCase() === "PRI")
|
|
600
|
+
return true;
|
|
601
|
+
return false;
|
|
602
|
+
}
|
|
603
|
+
function extractPrimaryKeys(table) {
|
|
604
|
+
const arr = table.primary_keys;
|
|
605
|
+
if (Array.isArray(arr)) {
|
|
606
|
+
return arr.filter((x) => typeof x === "string");
|
|
607
|
+
}
|
|
608
|
+
return [];
|
|
609
|
+
}
|
|
610
|
+
/**
|
|
611
|
+
* Trigger a metadata scan for a vega catalog and wait for completion.
|
|
612
|
+
* `id` is a vega catalog id (e.g. `d7nicrcjto2s73d9g67g`), not a legacy
|
|
613
|
+
* data-connection datasource UUID.
|
|
614
|
+
*/
|
|
615
|
+
export async function scanMetadata(options) {
|
|
616
|
+
const { baseUrl, accessToken, id, businessDomain = "bd_public" } = options;
|
|
617
|
+
return discoverVegaCatalog({
|
|
618
|
+
baseUrl,
|
|
619
|
+
accessToken,
|
|
620
|
+
id,
|
|
621
|
+
wait: true,
|
|
622
|
+
businessDomain,
|
|
623
|
+
});
|
|
624
|
+
}
|
|
625
|
+
/**
|
|
626
|
+
* Trigger a metadata scan and wait for completion. `id` is a vega catalog id.
|
|
627
|
+
*
|
|
628
|
+
* @deprecated Use {@link scanMetadata} directly. This wrapper exists only for
|
|
629
|
+
* backward compatibility with callers that used the old data-connection-based
|
|
630
|
+
* `scanDatasourceMetadata` signature.
|
|
631
|
+
*/
|
|
632
|
+
export async function scanDatasourceMetadata(options) {
|
|
633
|
+
return scanMetadata(options);
|
|
634
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Static JSON templates shipped with the CLI under ``src/templates/model`` (copied to ``dist/templates/model``).
|
|
3
|
+
* Read-only convenience until mf-model-manager exposes template APIs.
|
|
4
|
+
*/
|
|
5
|
+
export interface BundledModelTemplateEntry {
|
|
6
|
+
id: string;
|
|
7
|
+
file: string;
|
|
8
|
+
summary: string;
|
|
9
|
+
}
|
|
10
|
+
export interface BundledModelTemplateManifest {
|
|
11
|
+
llm: BundledModelTemplateEntry[];
|
|
12
|
+
small: BundledModelTemplateEntry[];
|
|
13
|
+
}
|
|
14
|
+
/** Resolve ``…/dist/templates/model`` (or ``…/src/templates/model`` when running via tsx from src). */
|
|
15
|
+
export declare function bundledModelTemplatesDir(): string;
|
|
16
|
+
export declare function loadBundledModelTemplateManifest(): Promise<BundledModelTemplateManifest>;
|
|
17
|
+
export declare function readBundledModelTemplateFile(branch: "llm" | "small", templateId: string): Promise<string>;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Static JSON templates shipped with the CLI under ``src/templates/model`` (copied to ``dist/templates/model``).
|
|
3
|
+
* Read-only convenience until mf-model-manager exposes template APIs.
|
|
4
|
+
*/
|
|
5
|
+
import { readFile } from "node:fs/promises";
|
|
6
|
+
import { dirname, join } from "node:path";
|
|
7
|
+
import { fileURLToPath } from "node:url";
|
|
8
|
+
/** Resolve ``…/dist/templates/model`` (or ``…/src/templates/model`` when running via tsx from src). */
|
|
9
|
+
export function bundledModelTemplatesDir() {
|
|
10
|
+
return join(dirname(fileURLToPath(import.meta.url)), "templates", "model");
|
|
11
|
+
}
|
|
12
|
+
export async function loadBundledModelTemplateManifest() {
|
|
13
|
+
const raw = await readFile(join(bundledModelTemplatesDir(), "manifest.json"), "utf-8");
|
|
14
|
+
return JSON.parse(raw);
|
|
15
|
+
}
|
|
16
|
+
export async function readBundledModelTemplateFile(branch, templateId) {
|
|
17
|
+
const manifest = await loadBundledModelTemplateManifest();
|
|
18
|
+
const entries = branch === "llm" ? manifest.llm : manifest.small;
|
|
19
|
+
const hit = entries.find((e) => e.id === templateId);
|
|
20
|
+
if (!hit) {
|
|
21
|
+
throw new Error(`Unknown bundled template "${templateId}". Run: kweaver model ${branch} --template`);
|
|
22
|
+
}
|
|
23
|
+
return readFile(join(bundledModelTemplatesDir(), hit.file), "utf-8");
|
|
24
|
+
}
|
package/dist/cli.js
CHANGED
|
@@ -10,6 +10,7 @@ import { runDataflowCommand } from "./commands/dataflow.js";
|
|
|
10
10
|
import { runDsCommand } from "./commands/ds.js";
|
|
11
11
|
import { runExploreCommand } from "./commands/explore.js";
|
|
12
12
|
import { runDataviewCommand } from "./commands/dataview.js";
|
|
13
|
+
import { runModelCommand } from "./commands/model.js";
|
|
13
14
|
import { runSkillCommand } from "./commands/skill.js";
|
|
14
15
|
import { runTokenCommand } from "./commands/token.js";
|
|
15
16
|
import { runToolboxCommand } from "./commands/toolbox.js";
|
|
@@ -71,6 +72,10 @@ Usage:
|
|
|
71
72
|
kweaver dataflow runs <dagId> [--since <date-like>] [-bd value]
|
|
72
73
|
kweaver dataflow logs <dagId> <instanceId> [--detail] [-bd value]
|
|
73
74
|
|
|
75
|
+
kweaver model llm list|get|add|edit|delete|test|chat|--template ... (mf-model-manager + mf-model-api)
|
|
76
|
+
kweaver model small list|get|add|edit|delete|test|embeddings|rerank|--template ...
|
|
77
|
+
kweaver model --help
|
|
78
|
+
|
|
74
79
|
kweaver dataview list [--datasource-id id] [--type atomic|custom] [--limit n] [-bd value]
|
|
75
80
|
kweaver dataview find --name <name> [--exact] [--datasource-id id] [--wait] [--timeout ms] [-bd value]
|
|
76
81
|
kweaver dataview get <id> [-bd value]
|
|
@@ -91,6 +96,7 @@ Usage:
|
|
|
91
96
|
kweaver bkn push <directory> [--branch main] [-bd value] [--detect-encoding|--no-detect-encoding] [--source-encoding name]
|
|
92
97
|
kweaver bkn pull <kn-id> [directory] [--branch main] [-bd value]
|
|
93
98
|
kweaver bkn object-type list|get|create|update|delete|query|properties <kn-id> ...
|
|
99
|
+
kweaver bkn metric list|get|create|search|validate|update|delete|query|dry-run <kn-id> ...
|
|
94
100
|
kweaver bkn relation-type list|get|create|update|delete <kn-id> ...
|
|
95
101
|
kweaver bkn subgraph <kn-id> <body-json>
|
|
96
102
|
kweaver bkn action-type list|query|inputs|execute <kn-id> ... [--wait] [--no-wait] [--timeout N]
|
|
@@ -167,6 +173,7 @@ Commands:
|
|
|
167
173
|
agent Agent CRUD, chat, sessions, history, publish/unpublish
|
|
168
174
|
ds Manage datasources (list, get, delete, tables, connect)
|
|
169
175
|
dataflow Dataflow document workflows (list, run, runs, logs)
|
|
176
|
+
model Model factory: LLM/small-model CRUD (manager) and llm chat (OpenAI-compatible API)
|
|
170
177
|
dataview|dv List, find, get, query (SQL), delete data views (atomic / custom)
|
|
171
178
|
bkn Knowledge network (CRUD, build, validate, export, stats, push/pull,
|
|
172
179
|
object-type, relation-type, subgraph, action-type, action-execution, action-log)
|
|
@@ -176,6 +183,7 @@ Commands:
|
|
|
176
183
|
tool Tools inside a toolbox (upload OpenAPI spec, list, enable/disable)
|
|
177
184
|
vega Vega observability (catalog, resource, query/sql, connector-type, health/stats/inspect)
|
|
178
185
|
context-loader Context-loader MCP/HTTP (config, tools, resources, search-schema, tool-call, query-*, etc.)
|
|
186
|
+
trace Diagnose a single trace with rule-based analysis
|
|
179
187
|
help Show this message`);
|
|
180
188
|
}
|
|
181
189
|
export async function run(argv) {
|
|
@@ -244,6 +252,9 @@ export async function run(argv) {
|
|
|
244
252
|
if (command === "dataflow") {
|
|
245
253
|
return runDataflowCommand(rest);
|
|
246
254
|
}
|
|
255
|
+
if (command === "model") {
|
|
256
|
+
return runModelCommand(rest);
|
|
257
|
+
}
|
|
247
258
|
if (command === "dataview" || command === "dv") {
|
|
248
259
|
return runDataviewCommand(rest);
|
|
249
260
|
}
|
|
@@ -277,6 +288,10 @@ export async function run(argv) {
|
|
|
277
288
|
if (command === "context-loader" || command === "context") {
|
|
278
289
|
return runContextLoaderCommand(rest);
|
|
279
290
|
}
|
|
291
|
+
if (command === "trace") {
|
|
292
|
+
const { runTraceCommand } = await import("./commands/trace.js");
|
|
293
|
+
return await runTraceCommand(rest);
|
|
294
|
+
}
|
|
280
295
|
console.error(`Unknown command: ${command}`);
|
|
281
296
|
printHelp();
|
|
282
297
|
return 1;
|