@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
package/README.md
CHANGED
|
@@ -16,6 +16,26 @@ npm install @kweaver-ai/kweaver-sdk
|
|
|
16
16
|
|
|
17
17
|
Requires **Node.js >= 22**.
|
|
18
18
|
|
|
19
|
+
## API reference (TypeDoc)
|
|
20
|
+
|
|
21
|
+
Generate HTML from source + TSDoc, then open `docs/reference/typescript-api-html/index.html` (gitignored), or serve locally:
|
|
22
|
+
|
|
23
|
+
HTML reference auto-discovers **`src/resources/*`**, **`src/api/*`**, and **`src/auth/*`** via TypeDoc's `entryPointStrategy: "expand"` (`typedoc.json`), so newly added modules appear without editing the config. The English build uses `README.md` as the cover page; the Chinese build uses `README.zh.md`. **"Defined in"** GitHub links read `gitRevision` from `TYPEDOC_GIT_REVISION` → `GITHUB_SHA` → fallback `"main"`; CI should pin links to the build SHA: `TYPEDOC_GIT_REVISION=$GITHUB_SHA npm run docs`.
|
|
24
|
+
|
|
25
|
+
TypeDoc does **not** ship a single-site EN/ZH toggle. Use two outputs: English UI (default) and Chinese UI strings (`docs:zh`, primarily localizes navigation chrome). API descriptions come from TSDoc and stay English unless you maintain duplicate comments elsewhere.
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
cd packages/typescript
|
|
29
|
+
npm install
|
|
30
|
+
npm run docs # English UI → docs/reference/typescript-api-html/
|
|
31
|
+
npm run docs:serve # generate + serve http://127.0.0.1:8766
|
|
32
|
+
npm run docs:zh # Chinese UI + README.zh.md → docs/reference/typescript-api-html-zh/
|
|
33
|
+
npm run docs:serve:zh # generate + serve http://127.0.0.1:8767
|
|
34
|
+
npm run docs:all # both folders
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
> Files inside `docs/reference/**` are generated. Edit `packages/typescript/README*.md` and TSDoc comments in source instead — anything copied into `media/` is overwritten on the next build.
|
|
38
|
+
|
|
19
39
|
## Quick Start
|
|
20
40
|
|
|
21
41
|
### Authenticate
|
|
@@ -174,12 +194,15 @@ kweaver token
|
|
|
174
194
|
kweaver ds list/get/delete/tables/connect
|
|
175
195
|
kweaver ds import-csv <ds_id> --files <glob> [--table-prefix <p>] [--batch-size 500] [--recreate]
|
|
176
196
|
kweaver dataflow list/run/runs/logs
|
|
197
|
+
kweaver model llm list/get/add/edit/delete/test/chat/--template
|
|
198
|
+
kweaver model small list/get/add/edit/delete/test/embeddings/rerank/--template
|
|
177
199
|
kweaver dataview list/find/get/query/delete
|
|
178
200
|
kweaver bkn list/get/stats/export/create/update/delete
|
|
179
201
|
kweaver bkn create-from-ds <ds_id> --name <name> [--tables t1,t2] [--build]
|
|
180
202
|
kweaver bkn create-from-csv <ds_id> --files <glob> --name <name> [--build]
|
|
181
203
|
kweaver bkn validate/push/pull
|
|
182
204
|
kweaver bkn object-type list/get/create/update/delete/query/properties
|
|
205
|
+
kweaver bkn metric list/get/create/search/validate/update/delete/query/dry-run
|
|
183
206
|
kweaver bkn relation-type list/get/create/update/delete
|
|
184
207
|
kweaver bkn action-type list/query/execute
|
|
185
208
|
kweaver bkn subgraph / search
|
|
@@ -211,6 +234,30 @@ kweaver dataflow logs <dagId> <instanceId> --detail
|
|
|
211
234
|
|
|
212
235
|
`kweaver dataflow runs --since` filters one local natural day. If the value cannot be parsed by `new Date(...)`, the CLI falls back to the most recent 20 runs. `kweaver dataflow logs` defaults to summary output; add `--detail` to print indented `input` and `output` payloads.
|
|
213
236
|
|
|
237
|
+
### Model factory CLI examples
|
|
238
|
+
|
|
239
|
+
`model` talks to **mf-model-manager** (`/api/mf-model-manager/v1`) for CRUD and **mf-model-api** (`/api/mf-model-api/v1`) for OpenAI-compatible **`chat`**, **`small embeddings`**, and **`small rerank`**. Override origins with `--mf-base-url` / `--mf-api-base-url` or `KWEAVER_MF_MODEL_MANAGER_URL` / `KWEAVER_MF_MODEL_API_URL`. `model llm --template` / `model small --template` prints one offline **`basic`** JSON stub per branch (no API calls). **LLM** `model_type` on the platform is one of **`llm`** (text), **`rlm`** (reasoning), or **`vu`** (vision / multimodal); filter with `kweaver model llm list --type …`. See [`skills/kweaver-core/references/model.md`](../../skills/kweaver-core/references/model.md#llm-model-types).
|
|
240
|
+
|
|
241
|
+
```bash
|
|
242
|
+
kweaver model llm list --limit 30
|
|
243
|
+
kweaver model llm list --type rlm
|
|
244
|
+
kweaver model llm get <model_id>
|
|
245
|
+
kweaver model llm add --body-file ./llm.json --upstream-url https://dashscope-intl.aliyuncs.com/compatible-mode/v1/chat/completions --api-model qwen-plus --api-key-file ~/.dashscope_key
|
|
246
|
+
kweaver model llm chat <model_id> -m "Hello" --no-stream
|
|
247
|
+
kweaver model llm --template > ./llm.json
|
|
248
|
+
kweaver model small list
|
|
249
|
+
kweaver model small add --name my-emb --type embedding --batch-size 8 --max-tokens 512 --embedding-dim 1536 \
|
|
250
|
+
--model-config-file ./cfg.json
|
|
251
|
+
kweaver model small add --name my-emb --type embedding --batch-size 8 --max-tokens 8192 --embedding-dim 1024 \
|
|
252
|
+
--upstream-url https://dashscope-intl.aliyuncs.com/compatible-mode/v1/embeddings --api-model text-embedding-v4 --api-key-file ~/.dashscope_key
|
|
253
|
+
kweaver model small test <model_id>
|
|
254
|
+
kweaver model small embeddings <model_id> -i "hello" -i "world"
|
|
255
|
+
kweaver model small rerank <model_id> -q "query" -d "doc a" -d "doc b"
|
|
256
|
+
kweaver model small --template > ./embedding-model-config.json
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
Full flags: `kweaver model --help` and [`skills/kweaver-core/references/model.md`](../../skills/kweaver-core/references/model.md).
|
|
260
|
+
|
|
214
261
|
### Vega `sql` CLI examples
|
|
215
262
|
|
|
216
263
|
Direct SQL against catalog-backed resources (`POST /api/vega-backend/v1/resources/query`). In SQL, use **`{{<resource_id>}}`** or **`{{.<resource_id>}}`** (Vega resource id from `vega resource list` / `get`) so the backend resolves the physical table and connector. `--resource-type` accepts the connector type of the target data source (run `kweaver vega connector-type list` to see available types). In simple mode, **quote the entire `--query` value** so the shell does not treat `{` / `}` specially.
|
|
@@ -256,6 +303,8 @@ kweaver tool debug --toolbox <BOX_ID> <TOOL_ID> \
|
|
|
256
303
|
| Variable | Description |
|
|
257
304
|
|---|---|
|
|
258
305
|
| `KWEAVER_BASE_URL` | KWeaver instance URL |
|
|
306
|
+
| `KWEAVER_MF_MODEL_MANAGER_URL` | Optional override for mf-model-manager API (defaults from `KWEAVER_BASE_URL` + `/api/mf-model-manager/v1`) |
|
|
307
|
+
| `KWEAVER_MF_MODEL_API_URL` | Optional override for mf-model-api (defaults from `KWEAVER_BASE_URL` + `/api/mf-model-api/v1`) |
|
|
259
308
|
| `KWEAVER_BUSINESS_DOMAIN` | Business domain identifier |
|
|
260
309
|
| `KWEAVER_TOKEN` | Access token |
|
|
261
310
|
| `KWEAVER_TOKEN_SOURCE` | Internal sentinel set by the CLI when `--token` is passed; do not set manually |
|
package/README.zh.md
CHANGED
|
@@ -16,6 +16,24 @@ npm install @kweaver-ai/kweaver-sdk
|
|
|
16
16
|
|
|
17
17
|
需要 **Node.js >= 22**。
|
|
18
18
|
|
|
19
|
+
## API 参考(TypeDoc)
|
|
20
|
+
|
|
21
|
+
由源码与 TSDoc 生成 HTML,产物在 `docs/reference/typescript-api-html/index.html`(已 gitignore)。`typedoc.json` 用 **`entryPointStrategy: "expand"`** 自动展开 **`src/resources`**、**`src/api`**、**`src/auth`** 整目录,新增模块无需改配置。英文构建用 `README.md`,中文构建用 `README.zh.md`。**「Defined in」** 链接的 `gitRevision` 取 `TYPEDOC_GIT_REVISION` → `GITHUB_SHA` → 回退 `"main"`;CI 中固定到当前 SHA:`TYPEDOC_GIT_REVISION=$GITHUB_SHA npm run docs`。
|
|
22
|
+
|
|
23
|
+
TypeDoc **不会在同一个站点里提供中英文切换**。做法是生成两套目录:英文界面(默认)与中文界面(**`npm run docs:zh`**),主要翻译导航等界面文案;**API 说明仍以源码里的 TSDoc(英文)为准**。
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
cd packages/typescript
|
|
27
|
+
npm install
|
|
28
|
+
npm run docs # 英文界面 → docs/reference/typescript-api-html/
|
|
29
|
+
npm run docs:serve # 生成并访问 http://127.0.0.1:8766
|
|
30
|
+
npm run docs:zh # 中文界面 + README.zh.md → docs/reference/typescript-api-html-zh/
|
|
31
|
+
npm run docs:serve:zh # 生成并访问 http://127.0.0.1:8767
|
|
32
|
+
npm run docs:all # 两套产物都生成
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
> `docs/reference/**` 下的所有文件均为生成产物。请编辑 `packages/typescript/README*.md` 与源码 TSDoc 注释;`media/` 下的拷贝在下次构建时会被覆盖。
|
|
36
|
+
|
|
19
37
|
## 快速上手
|
|
20
38
|
|
|
21
39
|
### 认证
|
|
@@ -165,9 +183,12 @@ kweaver config show / list-bd / set-bd <value> # 业务域;show/list-bd 在
|
|
|
165
183
|
kweaver token
|
|
166
184
|
kweaver ds list/get/delete/tables/connect
|
|
167
185
|
kweaver dataflow list/run/runs/logs
|
|
186
|
+
kweaver model llm list/get/add/edit/delete/test/chat/--template
|
|
187
|
+
kweaver model small list/get/add/edit/delete/test/embeddings/rerank/--template
|
|
168
188
|
kweaver dataview list/find/get/query/delete
|
|
169
189
|
kweaver bkn list/get/stats/export/create/update/delete
|
|
170
190
|
kweaver bkn object-type list/get/create/update/delete/query/properties
|
|
191
|
+
kweaver bkn metric list/get/create/search/validate/update/delete/query/dry-run
|
|
171
192
|
kweaver bkn relation-type list/get/create/update/delete
|
|
172
193
|
kweaver bkn action-type list/query/execute
|
|
173
194
|
kweaver bkn subgraph
|
|
@@ -197,6 +218,27 @@ kweaver dataflow logs <dagId> <instanceId> --detail
|
|
|
197
218
|
|
|
198
219
|
`kweaver dataflow runs --since` 会按本地自然日过滤;如果参数无法被 `new Date(...)` 解析,CLI 会回退到最近 20 条运行记录。`kweaver dataflow logs` 默认输出摘要;加上 `--detail` 会打印带缩进的 `input` 和 `output` 载荷。
|
|
199
220
|
|
|
221
|
+
### 模型工厂 CLI 示例
|
|
222
|
+
|
|
223
|
+
- **mf-model-manager**:`/api/mf-model-manager/v1`(配置 CRUD、`test`)
|
|
224
|
+
- **mf-model-api**:`/api/mf-model-api/v1`(OpenAI 兼容 **`chat`**,以及小模型 **`embeddings` / `rerank`**)
|
|
225
|
+
|
|
226
|
+
离线模版:`model llm --template` / `model small --template` 输出内置 **`basic`** JSON(不调后端)。大模型 **`model_type`** 在后端仅为 **`llm`**(文本)、**`rlm`**(推理)、**`vu`**(视觉/多模态,接口缩写不是 `vlm`);列表过滤:`kweaver model llm list --type …`。详见 [`skills/kweaver-core/references/model.md`](../../skills/kweaver-core/references/model.md#llm-model-types)。
|
|
227
|
+
|
|
228
|
+
```bash
|
|
229
|
+
kweaver model llm list --limit 30
|
|
230
|
+
kweaver model llm list --type vu
|
|
231
|
+
kweaver model llm chat <model_id> -m "你好" --no-stream
|
|
232
|
+
kweaver model llm --template > ./llm.json
|
|
233
|
+
kweaver model small add --name my-emb --type embedding --batch-size 8 --max-tokens 512 --embedding-dim 1536 \
|
|
234
|
+
--model-config-file ./cfg.json
|
|
235
|
+
kweaver model small embeddings <model_id> -i "hello" -i "world"
|
|
236
|
+
kweaver model small rerank <model_id> -q "查询" -d "文档a" -d "文档b"
|
|
237
|
+
kweaver model small --template > ./embedding-model-config.json
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
完整说明见 [`skills/kweaver-core/references/model.md`](../../skills/kweaver-core/references/model.md) 与 `kweaver model --help`。
|
|
241
|
+
|
|
200
242
|
### Vega `sql` CLI 示例
|
|
201
243
|
|
|
202
244
|
对 Catalog 资源执行直连 SQL(`POST /api/vega-backend/v1/resources/query`)。SQL 中使用 **`{{<resource_id>}}`** 或 **`{{.<resource_id>}}`**(资源 id 来自 `vega resource list` / `get`),后端据此解析物理表与 connector。`--resource-type` 为目标数据源的连接器类型,可通过 `kweaver vega connector-type list` 查看。简单模式下请**用引号包住整个 `--query` 参数**,避免 shell 对花括号做特殊处理。
|
|
@@ -218,6 +260,8 @@ kweaver vega sql -d '{"resource_type":"mysql","query":"SELECT * FROM {{res-1}} L
|
|
|
218
260
|
| 变量 | 说明 |
|
|
219
261
|
|---|---|
|
|
220
262
|
| `KWEAVER_BASE_URL` | KWeaver 实例地址 |
|
|
263
|
+
| `KWEAVER_MF_MODEL_MANAGER_URL` | 可选:mf-model-manager API 根地址(默认 `KWEAVER_BASE_URL` + `/api/mf-model-manager/v1`) |
|
|
264
|
+
| `KWEAVER_MF_MODEL_API_URL` | 可选:mf-model-api 根地址(默认 `KWEAVER_BASE_URL` + `/api/mf-model-api/v1`) |
|
|
221
265
|
| `KWEAVER_BUSINESS_DOMAIN` | 业务域标识 |
|
|
222
266
|
| `KWEAVER_TOKEN` | 访问令牌 |
|
|
223
267
|
| `KWEAVER_TOKEN_SOURCE` | CLI 传入 `--token` 时由程序设置的内部标记;请勿手动设置 |
|
package/bin/kweaver.js
CHANGED
|
@@ -1,17 +1,18 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
+
// Wait for stdout and stderr to fully flush before terminating. Checking
|
|
4
|
+
// writableNeedDrain alone is not enough — a single console.log under the
|
|
5
|
+
// highWaterMark returns synchronously while bytes are still queued, so
|
|
6
|
+
// process.exit() can truncate piped output (~7-8KB) under spawn capture.
|
|
7
|
+
// Empty write + callback fires only after all preceding writes drain.
|
|
3
8
|
function exit(code) {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
process.stderr.once("drain", done);
|
|
12
|
-
} else {
|
|
13
|
-
process.exit(code);
|
|
14
|
-
}
|
|
9
|
+
let pending = 2;
|
|
10
|
+
const done = () => {
|
|
11
|
+
pending -= 1;
|
|
12
|
+
if (pending === 0) process.exit(code);
|
|
13
|
+
};
|
|
14
|
+
process.stdout.write("", done);
|
|
15
|
+
process.stderr.write("", done);
|
|
15
16
|
}
|
|
16
17
|
|
|
17
18
|
import("../dist/cli.js").then(({ run }) => {
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Single source of truth for `/api/agent-observability/v1/traces/_search` —
|
|
3
|
+
* the OpenSearch-style endpoint that backs both `kweaver agent trace` and
|
|
4
|
+
* `kweaver trace diagnose`.
|
|
5
|
+
*
|
|
6
|
+
* Owns: endpoint URL, auth/headers (via `./headers.ts`), the two-hop strategy
|
|
7
|
+
* (conversation_id → traceIds → spans), and HTTP error handling.
|
|
8
|
+
*
|
|
9
|
+
* Does NOT own normalization: callers receive raw OpenSearch `_source` objects
|
|
10
|
+
* and shape them as needed (TraceSpan for UI rendering, RawSpan for diagnose
|
|
11
|
+
* rules). This keeps the wire contract in one place while letting each consumer
|
|
12
|
+
* pick its own minimal field set.
|
|
13
|
+
*/
|
|
14
|
+
export declare const TRACE_SEARCH_PATH = "/api/agent-observability/v1/traces/_search";
|
|
15
|
+
export declare class TraceFetchError extends Error {
|
|
16
|
+
readonly status?: number | undefined;
|
|
17
|
+
readonly url?: string | undefined;
|
|
18
|
+
constructor(message: string, status?: number | undefined, url?: string | undefined);
|
|
19
|
+
}
|
|
20
|
+
export interface FetchRawSpansByConversationOpts {
|
|
21
|
+
baseUrl: string;
|
|
22
|
+
accessToken: string;
|
|
23
|
+
businessDomain: string;
|
|
24
|
+
conversationId: string;
|
|
25
|
+
/** Cap on `terms` aggregation bucket count. Default 100. */
|
|
26
|
+
maxTraceIds?: number;
|
|
27
|
+
/** Cap on spans returned by the second query. Default 2000. */
|
|
28
|
+
maxSpans?: number;
|
|
29
|
+
}
|
|
30
|
+
export interface FetchRawSpansByConversationResult {
|
|
31
|
+
/** Distinct traceIds observed for this conversation, in agg-bucket order. */
|
|
32
|
+
traceIds: string[];
|
|
33
|
+
/** Raw `_source` objects, unmodified. Callers do their own normalization. */
|
|
34
|
+
rawSources: Array<Record<string, unknown>>;
|
|
35
|
+
/** True if the agg saw `sum_other_doc_count > 0` (more traceIds than maxTraceIds). */
|
|
36
|
+
truncated: boolean;
|
|
37
|
+
}
|
|
38
|
+
export declare function postTraceSearch(baseUrl: string, accessToken: string, businessDomain: string, body: unknown): Promise<Record<string, unknown>>;
|
|
39
|
+
/**
|
|
40
|
+
* Two-hop fetch of all `_source` documents belonging to a conversation.
|
|
41
|
+
*
|
|
42
|
+
* Hop 1: aggregate `traceId.keyword` for spans tagged with
|
|
43
|
+
* `attributes.gen_ai.conversation.id.keyword == conversationId`.
|
|
44
|
+
* Hop 2: fetch every span whose `traceId.keyword` is in the agg buckets.
|
|
45
|
+
*
|
|
46
|
+
* Fixture-compat fast path: when the first response carries no `aggregations`
|
|
47
|
+
* but does carry `hits.hits`, that is taken as a flat spans payload and hop 2
|
|
48
|
+
* is skipped. Existing e2e fixtures (single OpenSearch payload per file) thus
|
|
49
|
+
* remain usable with a single mock-fetch response.
|
|
50
|
+
*/
|
|
51
|
+
export declare function fetchRawSpansByConversation(opts: FetchRawSpansByConversationOpts): Promise<FetchRawSpansByConversationResult>;
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Single source of truth for `/api/agent-observability/v1/traces/_search` —
|
|
3
|
+
* the OpenSearch-style endpoint that backs both `kweaver agent trace` and
|
|
4
|
+
* `kweaver trace diagnose`.
|
|
5
|
+
*
|
|
6
|
+
* Owns: endpoint URL, auth/headers (via `./headers.ts`), the two-hop strategy
|
|
7
|
+
* (conversation_id → traceIds → spans), and HTTP error handling.
|
|
8
|
+
*
|
|
9
|
+
* Does NOT own normalization: callers receive raw OpenSearch `_source` objects
|
|
10
|
+
* and shape them as needed (TraceSpan for UI rendering, RawSpan for diagnose
|
|
11
|
+
* rules). This keeps the wire contract in one place while letting each consumer
|
|
12
|
+
* pick its own minimal field set.
|
|
13
|
+
*/
|
|
14
|
+
import { buildHeaders } from "./headers.js";
|
|
15
|
+
export const TRACE_SEARCH_PATH = "/api/agent-observability/v1/traces/_search";
|
|
16
|
+
export class TraceFetchError extends Error {
|
|
17
|
+
status;
|
|
18
|
+
url;
|
|
19
|
+
constructor(message, status, url) {
|
|
20
|
+
super(message);
|
|
21
|
+
this.status = status;
|
|
22
|
+
this.url = url;
|
|
23
|
+
this.name = "TraceFetchError";
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
export async function postTraceSearch(baseUrl, accessToken, businessDomain, body) {
|
|
27
|
+
const url = `${baseUrl.replace(/\/+$/, "")}${TRACE_SEARCH_PATH}`;
|
|
28
|
+
const res = await fetch(url, {
|
|
29
|
+
method: "POST",
|
|
30
|
+
headers: {
|
|
31
|
+
"Content-Type": "application/json",
|
|
32
|
+
...buildHeaders(accessToken, businessDomain),
|
|
33
|
+
},
|
|
34
|
+
body: JSON.stringify(body),
|
|
35
|
+
});
|
|
36
|
+
const text = await res.text();
|
|
37
|
+
if (!res.ok) {
|
|
38
|
+
throw new TraceFetchError(`trace search failed: HTTP ${res.status} ${res.statusText} — ${text.slice(0, 200)}`, res.status, url);
|
|
39
|
+
}
|
|
40
|
+
if (!text)
|
|
41
|
+
return {};
|
|
42
|
+
try {
|
|
43
|
+
return JSON.parse(text);
|
|
44
|
+
}
|
|
45
|
+
catch (err) {
|
|
46
|
+
throw new TraceFetchError(`trace search: invalid JSON response — ${err.message}`);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Two-hop fetch of all `_source` documents belonging to a conversation.
|
|
51
|
+
*
|
|
52
|
+
* Hop 1: aggregate `traceId.keyword` for spans tagged with
|
|
53
|
+
* `attributes.gen_ai.conversation.id.keyword == conversationId`.
|
|
54
|
+
* Hop 2: fetch every span whose `traceId.keyword` is in the agg buckets.
|
|
55
|
+
*
|
|
56
|
+
* Fixture-compat fast path: when the first response carries no `aggregations`
|
|
57
|
+
* but does carry `hits.hits`, that is taken as a flat spans payload and hop 2
|
|
58
|
+
* is skipped. Existing e2e fixtures (single OpenSearch payload per file) thus
|
|
59
|
+
* remain usable with a single mock-fetch response.
|
|
60
|
+
*/
|
|
61
|
+
export async function fetchRawSpansByConversation(opts) {
|
|
62
|
+
const { baseUrl, accessToken, businessDomain, conversationId } = opts;
|
|
63
|
+
const maxTraceIds = opts.maxTraceIds ?? 100;
|
|
64
|
+
const maxSpans = opts.maxSpans ?? 2000;
|
|
65
|
+
const aggResult = await postTraceSearch(baseUrl, accessToken, businessDomain, {
|
|
66
|
+
size: 0,
|
|
67
|
+
query: { term: { "attributes.gen_ai.conversation.id.keyword": conversationId } },
|
|
68
|
+
aggs: { tids: { terms: { field: "traceId.keyword", size: maxTraceIds } } },
|
|
69
|
+
});
|
|
70
|
+
const aggregations = aggResult.aggregations;
|
|
71
|
+
if (!aggregations) {
|
|
72
|
+
const directHits = aggResult.hits?.hits;
|
|
73
|
+
if (Array.isArray(directHits)) {
|
|
74
|
+
const rawSources = [];
|
|
75
|
+
const traceIds = new Set();
|
|
76
|
+
for (const h of directHits) {
|
|
77
|
+
if (!h._source)
|
|
78
|
+
continue;
|
|
79
|
+
rawSources.push(h._source);
|
|
80
|
+
const tid = h._source.traceId ?? h._source.trace_id;
|
|
81
|
+
if (typeof tid === "string" && tid.length > 0)
|
|
82
|
+
traceIds.add(tid);
|
|
83
|
+
}
|
|
84
|
+
return { traceIds: [...traceIds], rawSources, truncated: false };
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
const tids = aggregations?.tids;
|
|
88
|
+
const buckets = tids?.buckets ?? [];
|
|
89
|
+
const truncated = (tids?.sum_other_doc_count ?? 0) > 0;
|
|
90
|
+
const traceIds = buckets
|
|
91
|
+
.map((b) => b.key)
|
|
92
|
+
.filter((k) => typeof k === "string" && k.length > 0);
|
|
93
|
+
if (traceIds.length === 0) {
|
|
94
|
+
return { traceIds: [], rawSources: [], truncated: false };
|
|
95
|
+
}
|
|
96
|
+
const spansResult = await postTraceSearch(baseUrl, accessToken, businessDomain, {
|
|
97
|
+
size: maxSpans,
|
|
98
|
+
query: { terms: { "traceId.keyword": traceIds } },
|
|
99
|
+
sort: [{ startTime: "asc" }],
|
|
100
|
+
});
|
|
101
|
+
const hits = spansResult.hits?.hits ?? [];
|
|
102
|
+
const rawSources = [];
|
|
103
|
+
for (const h of hits) {
|
|
104
|
+
if (h._source)
|
|
105
|
+
rawSources.push(h._source);
|
|
106
|
+
}
|
|
107
|
+
return { traceIds, rawSources, truncated };
|
|
108
|
+
}
|
|
@@ -14,6 +14,7 @@ export interface BknBackendBaseOptions {
|
|
|
14
14
|
export interface BknBackendKnOptions extends BknBackendBaseOptions {
|
|
15
15
|
knId: string;
|
|
16
16
|
}
|
|
17
|
+
export declare function knUrl(baseUrl: string, knId: string, path: string): string;
|
|
17
18
|
export interface ConceptGroupOptions extends BknBackendKnOptions {
|
|
18
19
|
cgId: string;
|
|
19
20
|
}
|
package/dist/api/bkn-backend.js
CHANGED
|
@@ -20,7 +20,7 @@ export async function uploadBkn(options) {
|
|
|
20
20
|
return body;
|
|
21
21
|
}
|
|
22
22
|
const BKN_BASE = "/api/bkn-backend/v1";
|
|
23
|
-
function knUrl(baseUrl, knId, path) {
|
|
23
|
+
export function knUrl(baseUrl, knId, path) {
|
|
24
24
|
const base = baseUrl.replace(/\/+$/, "");
|
|
25
25
|
return `${base}${BKN_BASE}/knowledge-networks/${encodeURIComponent(knId)}/${path}`;
|
|
26
26
|
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import type { BknBackendKnOptions } from "./bkn-backend.js";
|
|
2
|
+
export interface ListMetricsOptions extends BknBackendKnOptions {
|
|
3
|
+
namePattern?: string;
|
|
4
|
+
sort?: "update_time" | "name";
|
|
5
|
+
direction?: "asc" | "desc";
|
|
6
|
+
offset?: number;
|
|
7
|
+
limit?: number;
|
|
8
|
+
tag?: string;
|
|
9
|
+
groupId?: string;
|
|
10
|
+
branch?: string;
|
|
11
|
+
}
|
|
12
|
+
export declare function listMetrics(options: ListMetricsOptions): Promise<string>;
|
|
13
|
+
export interface CreateMetricsOptions extends BknBackendKnOptions {
|
|
14
|
+
body: string;
|
|
15
|
+
branch?: string;
|
|
16
|
+
strictMode?: boolean;
|
|
17
|
+
}
|
|
18
|
+
export declare function createMetrics(options: CreateMetricsOptions): Promise<string>;
|
|
19
|
+
export interface SearchMetricsOptions extends BknBackendKnOptions {
|
|
20
|
+
body: string;
|
|
21
|
+
branch?: string;
|
|
22
|
+
strictMode?: boolean;
|
|
23
|
+
}
|
|
24
|
+
export declare function searchMetrics(options: SearchMetricsOptions): Promise<string>;
|
|
25
|
+
export interface ValidateMetricsOptions extends BknBackendKnOptions {
|
|
26
|
+
body: string;
|
|
27
|
+
branch?: string;
|
|
28
|
+
strictMode?: boolean;
|
|
29
|
+
importMode?: "normal" | "ignore" | "overwrite";
|
|
30
|
+
}
|
|
31
|
+
export declare function validateMetrics(options: ValidateMetricsOptions): Promise<string>;
|
|
32
|
+
export interface GetMetricOptions extends BknBackendKnOptions {
|
|
33
|
+
metricId: string;
|
|
34
|
+
branch?: string;
|
|
35
|
+
}
|
|
36
|
+
export declare function getMetric(options: GetMetricOptions): Promise<string>;
|
|
37
|
+
export interface UpdateMetricOptions extends BknBackendKnOptions {
|
|
38
|
+
metricId: string;
|
|
39
|
+
body: string;
|
|
40
|
+
branch?: string;
|
|
41
|
+
strictMode?: boolean;
|
|
42
|
+
}
|
|
43
|
+
export declare function updateMetric(options: UpdateMetricOptions): Promise<string>;
|
|
44
|
+
export interface DeleteMetricOptions extends BknBackendKnOptions {
|
|
45
|
+
metricId: string;
|
|
46
|
+
branch?: string;
|
|
47
|
+
}
|
|
48
|
+
export declare function deleteMetric(options: DeleteMetricOptions): Promise<string>;
|
|
49
|
+
export interface GetMetricsByIdsOptions extends BknBackendKnOptions {
|
|
50
|
+
/** Comma-separated metric IDs in a single path segment. */
|
|
51
|
+
metricIds: string;
|
|
52
|
+
branch?: string;
|
|
53
|
+
}
|
|
54
|
+
export declare function getMetrics(options: GetMetricsByIdsOptions): Promise<string>;
|
|
55
|
+
export interface DeleteMetricsByIdsOptions extends BknBackendKnOptions {
|
|
56
|
+
metricIds: string;
|
|
57
|
+
branch?: string;
|
|
58
|
+
}
|
|
59
|
+
export declare function deleteMetrics(options: DeleteMetricsByIdsOptions): Promise<string>;
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import { HttpError } from "../utils/http.js";
|
|
2
|
+
import { buildHeaders } from "./headers.js";
|
|
3
|
+
import { knUrl } from "./bkn-backend.js";
|
|
4
|
+
async function readTextOrThrow(response) {
|
|
5
|
+
const text = await response.text();
|
|
6
|
+
if (!response.ok) {
|
|
7
|
+
throw new HttpError(response.status, response.statusText, text);
|
|
8
|
+
}
|
|
9
|
+
return text;
|
|
10
|
+
}
|
|
11
|
+
function appendSearchParams(url, entries) {
|
|
12
|
+
for (const [k, v] of entries) {
|
|
13
|
+
if (v === undefined)
|
|
14
|
+
continue;
|
|
15
|
+
url.searchParams.set(k, String(v));
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
export async function listMetrics(options) {
|
|
19
|
+
const { baseUrl, accessToken, knId, businessDomain = "bd_public", namePattern, sort, direction, offset, limit, tag, groupId, branch, } = options;
|
|
20
|
+
const url = new URL(knUrl(baseUrl, knId, "metrics"));
|
|
21
|
+
appendSearchParams(url, [
|
|
22
|
+
["name_pattern", namePattern],
|
|
23
|
+
["sort", sort],
|
|
24
|
+
["direction", direction],
|
|
25
|
+
["offset", offset],
|
|
26
|
+
["limit", limit],
|
|
27
|
+
["tag", tag],
|
|
28
|
+
["group_id", groupId],
|
|
29
|
+
["branch", branch],
|
|
30
|
+
]);
|
|
31
|
+
const response = await fetch(url.toString(), {
|
|
32
|
+
method: "GET",
|
|
33
|
+
headers: buildHeaders(accessToken, businessDomain),
|
|
34
|
+
});
|
|
35
|
+
return readTextOrThrow(response);
|
|
36
|
+
}
|
|
37
|
+
export async function createMetrics(options) {
|
|
38
|
+
return postWithMethodOverride(options, "POST");
|
|
39
|
+
}
|
|
40
|
+
export async function searchMetrics(options) {
|
|
41
|
+
return postWithMethodOverride(options, "GET");
|
|
42
|
+
}
|
|
43
|
+
async function postWithMethodOverride(options, methodOverride) {
|
|
44
|
+
const { baseUrl, accessToken, knId, body, businessDomain = "bd_public", branch, strictMode } = options;
|
|
45
|
+
const url = new URL(knUrl(baseUrl, knId, "metrics"));
|
|
46
|
+
appendSearchParams(url, [
|
|
47
|
+
["branch", branch],
|
|
48
|
+
["strict_mode", strictMode],
|
|
49
|
+
]);
|
|
50
|
+
const response = await fetch(url.toString(), {
|
|
51
|
+
method: "POST",
|
|
52
|
+
headers: {
|
|
53
|
+
...buildHeaders(accessToken, businessDomain),
|
|
54
|
+
"content-type": "application/json",
|
|
55
|
+
"X-HTTP-Method-Override": methodOverride,
|
|
56
|
+
},
|
|
57
|
+
body,
|
|
58
|
+
});
|
|
59
|
+
return readTextOrThrow(response);
|
|
60
|
+
}
|
|
61
|
+
export async function validateMetrics(options) {
|
|
62
|
+
const { baseUrl, accessToken, knId, body, businessDomain = "bd_public", branch, strictMode, importMode } = options;
|
|
63
|
+
const url = new URL(knUrl(baseUrl, knId, "metrics/validation"));
|
|
64
|
+
appendSearchParams(url, [
|
|
65
|
+
["branch", branch],
|
|
66
|
+
["strict_mode", strictMode],
|
|
67
|
+
["import_mode", importMode],
|
|
68
|
+
]);
|
|
69
|
+
const response = await fetch(url.toString(), {
|
|
70
|
+
method: "POST",
|
|
71
|
+
headers: { ...buildHeaders(accessToken, businessDomain), "content-type": "application/json" },
|
|
72
|
+
body,
|
|
73
|
+
});
|
|
74
|
+
return readTextOrThrow(response);
|
|
75
|
+
}
|
|
76
|
+
export async function getMetric(options) {
|
|
77
|
+
const { baseUrl, accessToken, knId, metricId, businessDomain = "bd_public", branch } = options;
|
|
78
|
+
const url = new URL(knUrl(baseUrl, knId, `metrics/${encodeURIComponent(metricId)}`));
|
|
79
|
+
appendSearchParams(url, [["branch", branch]]);
|
|
80
|
+
const response = await fetch(url.toString(), {
|
|
81
|
+
method: "GET",
|
|
82
|
+
headers: buildHeaders(accessToken, businessDomain),
|
|
83
|
+
});
|
|
84
|
+
return readTextOrThrow(response);
|
|
85
|
+
}
|
|
86
|
+
export async function updateMetric(options) {
|
|
87
|
+
const { baseUrl, accessToken, knId, metricId, body, businessDomain = "bd_public", branch, strictMode } = options;
|
|
88
|
+
const url = new URL(knUrl(baseUrl, knId, `metrics/${encodeURIComponent(metricId)}`));
|
|
89
|
+
appendSearchParams(url, [
|
|
90
|
+
["branch", branch],
|
|
91
|
+
["strict_mode", strictMode],
|
|
92
|
+
]);
|
|
93
|
+
const response = await fetch(url.toString(), {
|
|
94
|
+
method: "PUT",
|
|
95
|
+
headers: { ...buildHeaders(accessToken, businessDomain), "content-type": "application/json" },
|
|
96
|
+
body,
|
|
97
|
+
});
|
|
98
|
+
return readTextOrThrow(response);
|
|
99
|
+
}
|
|
100
|
+
export async function deleteMetric(options) {
|
|
101
|
+
const { baseUrl, accessToken, knId, metricId, businessDomain = "bd_public", branch } = options;
|
|
102
|
+
const url = new URL(knUrl(baseUrl, knId, `metrics/${encodeURIComponent(metricId)}`));
|
|
103
|
+
appendSearchParams(url, [["branch", branch]]);
|
|
104
|
+
const response = await fetch(url.toString(), {
|
|
105
|
+
method: "DELETE",
|
|
106
|
+
headers: buildHeaders(accessToken, businessDomain),
|
|
107
|
+
});
|
|
108
|
+
return readTextOrThrow(response);
|
|
109
|
+
}
|
|
110
|
+
export async function getMetrics(options) {
|
|
111
|
+
const { baseUrl, accessToken, knId, metricIds, businessDomain = "bd_public", branch } = options;
|
|
112
|
+
const url = new URL(knUrl(baseUrl, knId, `metrics/${metricIds}`));
|
|
113
|
+
appendSearchParams(url, [["branch", branch]]);
|
|
114
|
+
const response = await fetch(url.toString(), {
|
|
115
|
+
method: "GET",
|
|
116
|
+
headers: buildHeaders(accessToken, businessDomain),
|
|
117
|
+
});
|
|
118
|
+
return readTextOrThrow(response);
|
|
119
|
+
}
|
|
120
|
+
export async function deleteMetrics(options) {
|
|
121
|
+
const { baseUrl, accessToken, knId, metricIds, businessDomain = "bd_public", branch } = options;
|
|
122
|
+
const url = new URL(knUrl(baseUrl, knId, `metrics/${metricIds}`));
|
|
123
|
+
appendSearchParams(url, [["branch", branch]]);
|
|
124
|
+
const response = await fetch(url.toString(), {
|
|
125
|
+
method: "DELETE",
|
|
126
|
+
headers: buildHeaders(accessToken, businessDomain),
|
|
127
|
+
});
|
|
128
|
+
return readTextOrThrow(response);
|
|
129
|
+
}
|
|
@@ -16,16 +16,57 @@ export interface ListMessagesOptions {
|
|
|
16
16
|
export interface GetTracesOptions {
|
|
17
17
|
baseUrl: string;
|
|
18
18
|
accessToken: string;
|
|
19
|
-
agentId: string;
|
|
20
19
|
conversationId: string;
|
|
20
|
+
/** Deprecated. trace-ai keys spans by conversation_id only; kept for CLI compatibility. */
|
|
21
|
+
agentId?: string;
|
|
21
22
|
businessDomain?: string;
|
|
23
|
+
/** Max distinct traceIds to fetch for one conversation. Default 100. */
|
|
24
|
+
maxTraceIds?: number;
|
|
25
|
+
/** Max spans returned by the second hop. Default 2000. */
|
|
26
|
+
maxSpans?: number;
|
|
27
|
+
}
|
|
28
|
+
export interface TraceSpan {
|
|
29
|
+
traceId: string;
|
|
30
|
+
spanId: string;
|
|
31
|
+
parentSpanId?: string;
|
|
32
|
+
name: string;
|
|
33
|
+
kind?: string;
|
|
34
|
+
startTime: string;
|
|
35
|
+
endTime?: string;
|
|
36
|
+
durationInNanos?: number;
|
|
37
|
+
status?: {
|
|
38
|
+
code?: string | number;
|
|
39
|
+
message?: string;
|
|
40
|
+
};
|
|
41
|
+
serviceName?: string;
|
|
42
|
+
attributes?: Record<string, unknown>;
|
|
43
|
+
events?: Array<{
|
|
44
|
+
name?: string;
|
|
45
|
+
time?: string;
|
|
46
|
+
attributes?: Record<string, unknown>;
|
|
47
|
+
}>;
|
|
48
|
+
/** Raw _source object from the trace store, kept verbatim for formatters that need extra fields. */
|
|
49
|
+
raw?: Record<string, unknown>;
|
|
50
|
+
}
|
|
51
|
+
export interface TracesByConversationResult {
|
|
52
|
+
conversationId: string;
|
|
53
|
+
traceIds: string[];
|
|
54
|
+
spans: TraceSpan[];
|
|
55
|
+
/** True if the traceId aggregation hit its bucket cap and may be incomplete. */
|
|
56
|
+
truncated: boolean;
|
|
22
57
|
}
|
|
23
58
|
/**
|
|
24
59
|
* List conversations for an agent.
|
|
25
60
|
* Returns empty array on 404 (endpoint may not be available in all deployments).
|
|
26
61
|
*/
|
|
27
62
|
export declare function listConversations(opts: ListConversationsOptions): Promise<string>;
|
|
28
|
-
|
|
63
|
+
/**
|
|
64
|
+
* Fetch all spans belonging to a conversation, shaped as `TraceSpan[]` for UI
|
|
65
|
+
* rendering (tree/perf/evidence/reasoning views). The wire-level two-hop and
|
|
66
|
+
* auth/header concerns live in `./agent-observability`; this function only
|
|
67
|
+
* normalizes the raw `_source` documents.
|
|
68
|
+
*/
|
|
69
|
+
export declare function getTracesByConversation(opts: GetTracesOptions): Promise<TracesByConversationResult>;
|
|
29
70
|
/**
|
|
30
71
|
* List messages for a conversation.
|
|
31
72
|
* Returns empty array on 404 (endpoint may not be available in all deployments).
|