@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.
Files changed (88) hide show
  1. package/README.md +49 -0
  2. package/README.zh.md +44 -0
  3. package/bin/kweaver.js +12 -11
  4. package/dist/api/agent-observability.d.ts +51 -0
  5. package/dist/api/agent-observability.js +108 -0
  6. package/dist/api/bkn-backend.d.ts +1 -0
  7. package/dist/api/bkn-backend.js +1 -1
  8. package/dist/api/bkn-metrics.d.ts +59 -0
  9. package/dist/api/bkn-metrics.js +129 -0
  10. package/dist/api/conversations.d.ts +43 -2
  11. package/dist/api/conversations.js +77 -23
  12. package/dist/api/datasources.d.ts +2 -20
  13. package/dist/api/datasources.js +7 -86
  14. package/dist/api/model-invocation.d.ts +58 -0
  15. package/dist/api/model-invocation.js +203 -0
  16. package/dist/api/models.d.ts +79 -0
  17. package/dist/api/models.js +183 -0
  18. package/dist/api/ontology-query-metrics.d.ts +14 -0
  19. package/dist/api/ontology-query-metrics.js +30 -0
  20. package/dist/api/trace.d.ts +44 -0
  21. package/dist/api/trace.js +81 -0
  22. package/dist/api/vega.d.ts +53 -0
  23. package/dist/api/vega.js +144 -0
  24. package/dist/bundled-model-templates.d.ts +17 -0
  25. package/dist/bundled-model-templates.js +24 -0
  26. package/dist/cli.js +15 -0
  27. package/dist/client.d.ts +3 -0
  28. package/dist/client.js +5 -0
  29. package/dist/commands/agent.d.ts +7 -1
  30. package/dist/commands/agent.js +75 -21
  31. package/dist/commands/bkn-metric.d.ts +1 -0
  32. package/dist/commands/bkn-metric.js +406 -0
  33. package/dist/commands/bkn-ops.js +28 -16
  34. package/dist/commands/bkn-utils.d.ts +38 -0
  35. package/dist/commands/bkn-utils.js +54 -0
  36. package/dist/commands/bkn.js +4 -0
  37. package/dist/commands/ds.js +14 -3
  38. package/dist/commands/explore-chat.js +2 -2
  39. package/dist/commands/model.d.ts +72 -0
  40. package/dist/commands/model.js +1315 -0
  41. package/dist/commands/trace.d.ts +14 -0
  42. package/dist/commands/trace.js +168 -0
  43. package/dist/index.d.ts +9 -0
  44. package/dist/index.js +5 -0
  45. package/dist/resources/datasources.js +2 -1
  46. package/dist/resources/models.d.ts +40 -0
  47. package/dist/resources/models.js +88 -0
  48. package/dist/templates/model/llm-basic.json +13 -0
  49. package/dist/templates/model/manifest.json +16 -0
  50. package/dist/templates/model/small-basic.json +6 -0
  51. package/dist/trace-core/diagnose/builtin-rules/excessive-tool-calls-per-turn.d.ts +2 -0
  52. package/dist/trace-core/diagnose/builtin-rules/excessive-tool-calls-per-turn.js +15 -0
  53. package/dist/trace-core/diagnose/builtin-rules/excessive-tool-calls-per-turn.yaml +16 -0
  54. package/dist/trace-core/diagnose/builtin-rules/llm-response-truncated-no-continue.d.ts +2 -0
  55. package/dist/trace-core/diagnose/builtin-rules/llm-response-truncated-no-continue.js +44 -0
  56. package/dist/trace-core/diagnose/builtin-rules/llm-response-truncated-no-continue.yaml +15 -0
  57. package/dist/trace-core/diagnose/builtin-rules/register.d.ts +1 -0
  58. package/dist/trace-core/diagnose/builtin-rules/register.js +11 -0
  59. package/dist/trace-core/diagnose/builtin-rules/retrieval-empty-no-fallback.d.ts +2 -0
  60. package/dist/trace-core/diagnose/builtin-rules/retrieval-empty-no-fallback.js +29 -0
  61. package/dist/trace-core/diagnose/builtin-rules/retrieval-empty-no-fallback.yaml +15 -0
  62. package/dist/trace-core/diagnose/builtin-rules/tool-error-swallowed.d.ts +2 -0
  63. package/dist/trace-core/diagnose/builtin-rules/tool-error-swallowed.js +45 -0
  64. package/dist/trace-core/diagnose/builtin-rules/tool-error-swallowed.yaml +15 -0
  65. package/dist/trace-core/diagnose/builtin-rules/tool-loop-no-state-change.d.ts +2 -0
  66. package/dist/trace-core/diagnose/builtin-rules/tool-loop-no-state-change.js +38 -0
  67. package/dist/trace-core/diagnose/builtin-rules/tool-loop-no-state-change.yaml +16 -0
  68. package/dist/trace-core/diagnose/index.d.ts +9 -0
  69. package/dist/trace-core/diagnose/index.js +104 -0
  70. package/dist/trace-core/diagnose/predicate-registry.d.ts +7 -0
  71. package/dist/trace-core/diagnose/predicate-registry.js +30 -0
  72. package/dist/trace-core/diagnose/report-assembler.d.ts +12 -0
  73. package/dist/trace-core/diagnose/report-assembler.js +90 -0
  74. package/dist/trace-core/diagnose/rule-loader.d.ts +11 -0
  75. package/dist/trace-core/diagnose/rule-loader.js +86 -0
  76. package/dist/trace-core/diagnose/schemas.d.ts +109 -0
  77. package/dist/trace-core/diagnose/schemas.js +94 -0
  78. package/dist/trace-core/diagnose/signal-probe.d.ts +5 -0
  79. package/dist/trace-core/diagnose/signal-probe.js +21 -0
  80. package/dist/trace-core/diagnose/synthesizer-template.d.ts +2 -0
  81. package/dist/trace-core/diagnose/synthesizer-template.js +49 -0
  82. package/dist/trace-core/diagnose/trace-shaper.d.ts +3 -0
  83. package/dist/trace-core/diagnose/trace-shaper.js +72 -0
  84. package/dist/trace-core/diagnose/types.d.ts +124 -0
  85. package/dist/trace-core/diagnose/types.js +1 -0
  86. package/dist/utils/trace-views.d.ts +44 -0
  87. package/dist/utils/trace-views.js +425 -0
  88. 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
- if (process.stdout.writableNeedDrain || process.stderr.writableNeedDrain) {
5
- const done = () => {
6
- if (!process.stdout.writableNeedDrain && !process.stderr.writableNeedDrain) {
7
- process.exit(code);
8
- }
9
- };
10
- process.stdout.once("drain", done);
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
  }
@@ -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
- export declare function getTracesByConversation(opts: GetTracesOptions): Promise<string>;
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).