From cda1e326f390b5a892d830221edf42543f3c41ed Mon Sep 17 00:00:00 2001 From: "zeyu.fz" Date: Mon, 17 Aug 2026 15:07:38 +0800 Subject: [PATCH] =?UTF-8?q?refactor(tool-bailian-kb):=20=E7=A7=BB=E9=99=A4?= =?UTF-8?q?=20kb=5Fservice=5Flist=20=E5=B7=A5=E5=85=B7=EF=BC=8C=E7=BB=9F?= =?UTF-8?q?=E4=B8=80=E6=9C=8D=E5=8A=A1=E5=8F=91=E7=8E=B0=E6=96=B9=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 移除 kb_service_list 相关代码,包括接口定义、服务查询函数和工具定义 - 服务发现改为通过 kscli 命令行工具 `kscli service list` 查询 - 更新文档和注释,删除关于 kb_service_list 的描述和示例 - 删除对服务列表接口路径的引用,保留 kb_search 和 kb_chat 两个工具 - 调整错误处理逻辑,不再附加可用服务信息,错误直接透传 - 修改默认服务 id 提示文本,均改为引用 `kscli service list` 作为服务发现手段 - 移除相关测试内容,包括对 kb_service_list 注册和服务查询测试 - 保持其他功能和接口不变,确保兼容和功能完整性 --- packages/tool-bailian-kb/README.md | 7 +- packages/tool-bailian-kb/package.json | 2 +- .../skills/bailian-kb-management/SKILL.md | 2 +- packages/tool-bailian-kb/src/api-types.ts | 23 +---- packages/tool-bailian-kb/src/endpoints.ts | 3 +- packages/tool-bailian-kb/src/index.ts | 4 +- packages/tool-bailian-kb/src/services.ts | 59 ------------- packages/tool-bailian-kb/src/tools.ts | 88 +++---------------- packages/tool-bailian-kb/src/web/locales.ts | 16 ++-- .../tool-bailian-kb/tests/endpoints.test.ts | 2 +- .../tool-bailian-kb/tests/services.test.ts | 49 ----------- packages/tool-bailian-kb/tests/tools.test.ts | 15 ++-- 12 files changed, 36 insertions(+), 234 deletions(-) delete mode 100644 packages/tool-bailian-kb/src/services.ts delete mode 100644 packages/tool-bailian-kb/tests/services.test.ts diff --git a/packages/tool-bailian-kb/README.md b/packages/tool-bailian-kb/README.md index c64ad17..7fd5b84 100644 --- a/packages/tool-bailian-kb/README.md +++ b/packages/tool-bailian-kb/README.md @@ -1,6 +1,6 @@ # dsh-tool-bailian-kb -百炼知识库的 dsh 插件本体:在 `ctx.tools` 注册三个模型工具,并在 skills 服务可用时注册管理面 skill。 +百炼知识库的 dsh 插件本体:在 `ctx.tools` 注册两个检索模型工具(kb_search、kb_chat),并在 skills 服务可用时注册管理面 skill。服务发现通过 kscli CLI 完成。 ## Config @@ -21,13 +21,14 @@ Config 同时注册为 `bailian-kb` settings namespace(`installSettingsSection | 工具 | 参数 | 返回 | |---|---|---| -| `kb_service_list` | `scene?`(chat\|search,省略查双场景合并)、`name_filter?` | 服务清单(agent_id、名称、scene、status、绑定知识库)+ total + truncated;分页内部消化(单 scene 100 条上限) | | `kb_search` | `query`、`agent_id`(见 defaultRetrieveAgentId)、`top_k?`(默认 5,**客户端截断**——服务端无此参数)、`images?` | chunks(text/score/来源)+ total | | `kb_chat` | `message`、`agent_id`(见 defaultChatAgentId) | 完整答案(内部消费 SSE 流缓冲返回)+ request_id | +服务发现(`kb_service_list` 已移除):通过 `kscli service list` CLI 命令查询可用检索/对话服务及其 agent_id。 + ## 错误语义 -- HTTP 错误(除 401/403 鉴权类):错误信息**附当前服务清单**,模型可一步纠正无效 `agent_id`(5xx 也附,但通常代表服务端异常); +- HTTP 错误:原始错误透传,模型可通过 `kscli service list` 发现可用服务以纠正无效 `agent_id`; - 凭证缺失:指向 `~/.dsh/.env` / `.credentials.yaml` 配置方式与控制台取 key 页面; - chat 超时:说明服务端多轮检索特性,建议重试或改用 `kb_search`; - 服务端错误体截断至 500 字符进入错误信息(优先 `code: message`)。 diff --git a/packages/tool-bailian-kb/package.json b/packages/tool-bailian-kb/package.json index 433aead..0069895 100644 --- a/packages/tool-bailian-kb/package.json +++ b/packages/tool-bailian-kb/package.json @@ -1,7 +1,7 @@ { "name": "dsh-tool-bailian-kb", "version": "0.1.0", - "description": "Bailian knowledge-base tools for DeepSeek Harness: kb_service_list, kb_search, kb_chat over the DashScope RAG API, plus the kscli management skill.", + "description": "Bailian knowledge-base tools for DeepSeek Harness: kb_search and kb_chat over the DashScope RAG API, plus the kscli management skill.", "type": "module", "main": "lib/index.js", "types": "lib/index.d.ts", diff --git a/packages/tool-bailian-kb/skills/bailian-kb-management/SKILL.md b/packages/tool-bailian-kb/skills/bailian-kb-management/SKILL.md index cd17081..b957183 100644 --- a/packages/tool-bailian-kb/skills/bailian-kb-management/SKILL.md +++ b/packages/tool-bailian-kb/skills/bailian-kb-management/SKILL.md @@ -22,7 +22,7 @@ kscli doc status --kb-id --doc-id # 3. 轮 kscli service create ... && kscli service deploy ... # 4. 建/部署检索服务 → 得到 agent_id ``` -部署完成后用 `kb_service_list` 确认服务可见,再用 `kb_search` 带该 `agent_id` 验证检索。 +部署完成后用 `kscli service list` 确认服务可见,再用 `kb_search` 带该 `agent_id` 验证检索。 ## 命令组速查 diff --git a/packages/tool-bailian-kb/src/api-types.ts b/packages/tool-bailian-kb/src/api-types.ts index 73534e4..2870372 100644 --- a/packages/tool-bailian-kb/src/api-types.ts +++ b/packages/tool-bailian-kb/src/api-types.ts @@ -1,25 +1,4 @@ -/** Request/response fields of the three DashScope knowledge endpoints, mirrored from the verified kscli types. */ - -export interface ServiceListRequest { - agent_scene: 'chat' | 'search' - agent_name?: string - page_number: number - page_size: number -} - -export interface ServiceListRow { - agent_id?: string - agent_name?: string - agent_scene?: string - agent_status?: string - pipeline_list?: { pipeline_id?: string; pipeline_name?: string }[] -} - -export interface ServiceListResponse { - code?: string - message?: string - data?: { total_count?: number; rows?: ServiceListRow[] } -} +/** Request/response fields of the DashScope search and chat endpoints, mirrored from the verified kscli types. */ export interface SearchRequest { query: string diff --git a/packages/tool-bailian-kb/src/endpoints.ts b/packages/tool-bailian-kb/src/endpoints.ts index 3ecce53..ab48045 100644 --- a/packages/tool-bailian-kb/src/endpoints.ts +++ b/packages/tool-bailian-kb/src/endpoints.ts @@ -1,8 +1,7 @@ /** Protocol path constants and the workspace-subdomain URL builder (external API spec; not configurable). */ -/** DashScope knowledge API paths, mirrored from the verified kscli endpoint table. */ +/** DashScope knowledge API paths for the two native tools (search, chat); service listing is handled by kscli. */ export const KB_PATHS = { - serviceList: '/api/v1/indices/rag/app/list', search: '/api/v1/indices/knowledge/search', chat: '/api/v2/apps/knowledge/chat', } as const diff --git a/packages/tool-bailian-kb/src/index.ts b/packages/tool-bailian-kb/src/index.ts index 9576ddf..569821c 100644 --- a/packages/tool-bailian-kb/src/index.ts +++ b/packages/tool-bailian-kb/src/index.ts @@ -1,6 +1,6 @@ /** - * Bailian knowledge-base consumer plugin: registers kb_service_list, kb_search, - * and kb_chat over the DashScope RAG API, plus the kscli management skill. + * Bailian knowledge-base consumer plugin: registers kb_search and kb_chat over the DashScope RAG API, + * plus the kscli management skill. * @module dsh-tool-bailian-kb */ diff --git a/packages/tool-bailian-kb/src/services.ts b/packages/tool-bailian-kb/src/services.ts deleted file mode 100644 index 9d37dd4..0000000 --- a/packages/tool-bailian-kb/src/services.ts +++ /dev/null @@ -1,59 +0,0 @@ -/** Retrieval-service discovery: per-scene queries merged into one model-facing list; pagination stays internal. */ - -import type { ServiceListResponse } from './api-types.js' -import type { KbClient } from './client.js' -import { KB_PATHS } from './endpoints.js' - -/** Server page-size maximum; one page per scene covers ordinary workspaces. */ -const MAX_PAGE_SIZE = 100 - -export interface ServiceEntry { - agent_id: string - name: string - scene: string - status: string - knowledge_bases: string[] -} - -export interface ServiceList { - services: ServiceEntry[] - total: number - /** True when some scene reported more rows than one max page returned. */ - truncated: boolean -} - -export interface ListServicesQuery { - scene?: 'chat' | 'search' - nameFilter?: string -} - -/** - * List retrieval/Q&A services. An omitted scene fans out to both scenes and merges. - * @param client - the shared knowledge API client. - * @param query - optional scene and fuzzy name filter. - * @returns merged entries, the server-reported total, and the truncation flag. - */ -export async function listServices(client: KbClient, query: ListServicesQuery): Promise { - const scenes: ('chat' | 'search')[] = query.scene ? [query.scene] : ['chat', 'search'] - const services: ServiceEntry[] = [] - let total = 0 - for (const scene of scenes) { - const res = await client.postJson(KB_PATHS.serviceList, { - agent_scene: scene, - ...(query.nameFilter ? { agent_name: query.nameFilter } : {}), - page_number: 1, - page_size: MAX_PAGE_SIZE, - }) - total += res.data?.total_count ?? 0 - for (const row of res.data?.rows ?? []) { - services.push({ - agent_id: row.agent_id ?? '', - name: row.agent_name ?? '', - scene: row.agent_scene ?? scene, - status: row.agent_status ?? '', - knowledge_bases: (row.pipeline_list ?? []).map(p => p.pipeline_name ?? p.pipeline_id ?? '').filter(Boolean), - }) - } - } - return { services, total, truncated: total > services.length } -} diff --git a/packages/tool-bailian-kb/src/tools.ts b/packages/tool-bailian-kb/src/tools.ts index 0ecceee..9192af6 100644 --- a/packages/tool-bailian-kb/src/tools.ts +++ b/packages/tool-bailian-kb/src/tools.ts @@ -1,5 +1,5 @@ /** - * The three model-facing knowledge tools. agent_id stays optional in the schema + * The two model-facing knowledge tools (kb_search, kb_chat). agent_id stays optional in the schema * regardless of deployment: the default services (patch config or credential) * can change at runtime through the credentials domain, so the fallback runs * per call and a missing default surfaces as an executable error instead of a @@ -11,7 +11,6 @@ import type { SearchRequest, SearchResponse } from './api-types.js' import { KbApiError, type KbClient } from './client.js' import { consumeChatStream } from './chat.js' import { KB_PATHS } from './endpoints.js' -import { listServices } from './services.js' /** Client-side chunk cap applied when the model omits top_k. */ const DEFAULT_TOP_K = 5 @@ -26,32 +25,17 @@ export interface KbToolDeps { chatTimeoutMs: number } -/** Format one service list into the error-hint / result text form. */ -function formatServices(services: { agent_id: string; name: string; scene: string; status: string }[]): string { - return services.map(s => `${s.agent_id} (${s.name}, scene=${s.scene}, ${s.status})`).join('; ') -} - /** - * Append the current service list to a client error so the model can correct - * an invalid agent_id in one step. Auth failures (401/403) keep their own message. - * @param client - the shared knowledge API client used for best-effort discovery. - * @param err - the failure being enriched; always rethrown. - * @returns never; the original or enriched error is thrown. + * Forward the original tool error unchanged; service discovery now lives + * in the kscli management skill (`kscli service list`), so the tool no + * longer makes a best-effort API round-trip to enrich the message. */ -async function withServiceHint(client: KbClient, err: unknown): Promise { - if (err instanceof KbApiError && err.status !== undefined && err.status >= 400 && err.status !== 401 && err.status !== 403) { - let hint: string | undefined - try { - const { services } = await listServices(client, {}) - if (services.length > 0) hint = formatServices(services) - } catch { /* discovery is best-effort; the original error already carries the failure */ } - if (hint !== undefined) throw new KbApiError(`${err.message}. Available services: ${hint}`, err.status) - } +async function withServiceHint(_client: KbClient, err: unknown): Promise { throw err } /** - * Build the three tool definitions over one shared client. + * Build the two tool definitions over one shared client. * @param deps - client plus the deployment's explicit pinning and timeout choices. * @returns definitions ready for `ctx.tools.register()`. */ @@ -61,13 +45,13 @@ export function createKbTools(deps: KbToolDeps) { const { client, resolveDefaultRetrieveAgentId, resolveDefaultChatAgentId } = deps const agentIdParam = { type: 'string' as const, - description: 'Retrieval/Q&A service id; omit to use the default service when this deployment configures one (find ids via kb_service_list).', + description: 'Retrieval/Q&A service id; omit to use the default service when this deployment configures one (find ids via `kscli service list`).', } const resolveRetrieveAgentId = async (supplied: string | undefined): Promise => { if (supplied !== undefined) return supplied const defaultId = resolveDefaultRetrieveAgentId === undefined ? undefined : await resolveDefaultRetrieveAgentId() if (defaultId === undefined) { - throw new Error('agent_id is required: no default retrieval service is configured; discover services with kb_service_list') + throw new Error('agent_id is required: no default retrieval service is configured; discover services with `kscli service list`') } return defaultId } @@ -75,63 +59,11 @@ export function createKbTools(deps: KbToolDeps) { if (supplied !== undefined) return supplied const defaultId = resolveDefaultChatAgentId === undefined ? undefined : await resolveDefaultChatAgentId() if (defaultId === undefined) { - throw new Error('agent_id is required: no default chat service is configured; discover services with kb_service_list') + throw new Error('agent_id is required: no default chat service is configured; discover services with `kscli service list`') } return defaultId } - const serviceList = defineTool({ - name: 'kb_service_list', - description: - 'List the Bailian knowledge retrieval/Q&A services available in this workspace. ' - + 'Each entry names the service id (agent_id) to pass to kb_search (scene=search) or kb_chat (scene=chat), ' - + 'its bound knowledge bases, and its status (prefer deployed). ' - + 'Omit scene to see both kinds; narrow large workspaces with name_filter.', - parameters: { - scene: { type: 'string', enum: ['chat', 'search'], description: 'Only list services for this scene; omitted lists both.' }, - name_filter: { type: 'string', description: 'Fuzzy match on the service name.' }, - }, - output: { - schema: { - type: 'object', - additionalProperties: false, - properties: { - services: { - type: 'array', - required: true, - items: { - type: 'object', - additionalProperties: false, - properties: { - agent_id: { type: 'string', required: true }, - name: { type: 'string', required: true }, - scene: { type: 'string', required: true }, - status: { type: 'string', required: true }, - knowledge_bases: { type: 'array', required: true, items: { type: 'string' } }, - }, - }, - }, - total: { type: 'integer', required: true }, - truncated: { type: 'boolean', required: true }, - }, - }, - render: (_args, value) => [{ - type: 'text', - text: value.services.length === 0 - ? 'No knowledge services found.' - : `${value.services.length} service(s): ${formatServices(value.services)}` - + (value.truncated ? ` — listed first ${value.services.length} of ${value.total}; narrow with name_filter.` : ''), - }], - }, - async execute(args) { - return await listServices(client, { - ...(args.scene === 'chat' || args.scene === 'search' ? { scene: args.scene } : {}), - ...(args.name_filter ? { nameFilter: args.name_filter } : {}), - }) - }, - presentCall: args => ({ card: 'generic', title: 'List knowledge services', kind: 'other', rawInput: args }), - }) - const search = defineTool({ name: 'kb_search', description: @@ -251,5 +183,5 @@ export function createKbTools(deps: KbToolDeps) { presentCall: args => ({ card: 'generic', title: 'Ask knowledge base (may take a few minutes)', kind: 'fetch', rawInput: args }), }) - return [serviceList, search, chat] + return [search, chat] } diff --git a/packages/tool-bailian-kb/src/web/locales.ts b/packages/tool-bailian-kb/src/web/locales.ts index 1bc9d92..77b20cc 100644 --- a/packages/tool-bailian-kb/src/web/locales.ts +++ b/packages/tool-bailian-kb/src/web/locales.ts @@ -34,13 +34,13 @@ export const en: Record = { workspaceIdSet: 'A workspace is configured.', workspaceIdUnset: 'No workspace is configured; knowledge tools fail until one is.', retrieveAgentId: 'Default retrieval service id', - retrieveAgentIdHint: 'agent_id of the default retrieval service (kb_search); when unset, every call must name one (kb_service_list discovers ids). Stored in the settings document.', - retrieveAgentIdHintFallback: 'agent_id of the default retrieval service (kb_search); when unset, every call must name one (kb_service_list discovers ids). Leave blank to keep the current one.', + retrieveAgentIdHint: 'agent_id of the default retrieval service (kb_search); when unset, every call must name one (`kscli service list` discovers ids). Stored in the settings document.', + retrieveAgentIdHintFallback: 'agent_id of the default retrieval service (kb_search); when unset, every call must name one (`kscli service list` discovers ids). Leave blank to keep the current one.', retrieveAgentIdSet: 'A default retrieval service is configured.', retrieveAgentIdUnset: 'No default retrieval service; every kb_search call must name one.', chatAgentId: 'Default chat service id', - chatAgentIdHint: 'agent_id of the default Q&A service (kb_chat); when unset, every call must name one (kb_service_list discovers ids). Stored in the settings document.', - chatAgentIdHintFallback: 'agent_id of the default Q&A service (kb_chat); when unset, every call must name one (kb_service_list discovers ids). Leave blank to keep the current one.', + chatAgentIdHint: 'agent_id of the default Q&A service (kb_chat); when unset, every call must name one (`kscli service list` discovers ids). Stored in the settings document.', + chatAgentIdHintFallback: 'agent_id of the default Q&A service (kb_chat); when unset, every call must name one (`kscli service list` discovers ids). Leave blank to keep the current one.', chatAgentIdSet: 'A default chat service is configured.', chatAgentIdUnset: 'No default chat service; every kb_chat call must name one.', fromEnv: 'Set by the environment (read-only here)', @@ -70,13 +70,13 @@ export const zh: Record = { workspaceIdSet: '已配置工作空间。', workspaceIdUnset: '未配置工作空间;配置前知识库工具不可用。', retrieveAgentId: '默认检索服务 ID', - retrieveAgentIdHint: '默认检索服务(kb_search)的 agent_id;未设置时每次调用都需显式指定(可用 kb_service_list 发现 id)。存入设置文档。', - retrieveAgentIdHintFallback: '默认检索服务(kb_search)的 agent_id;未设置时每次调用都需显式指定(可用 kb_service_list 发现 id)。留空表示保持当前值。', + retrieveAgentIdHint: '默认检索服务(kb_search)的 agent_id;未设置时每次调用都需显式指定(可用 `kscli service list` 发现 id)。存入设置文档。', + retrieveAgentIdHintFallback: '默认检索服务(kb_search)的 agent_id;未设置时每次调用都需显式指定(可用 `kscli service list` 发现 id)。留空表示保持当前值。', retrieveAgentIdSet: '已配置默认检索服务。', retrieveAgentIdUnset: '未配置默认检索服务;每次 kb_search 调用需显式指定。', chatAgentId: '默认对话服务 ID', - chatAgentIdHint: '默认对话服务(kb_chat)的 agent_id;未设置时每次调用都需显式指定(可用 kb_service_list 发现 id)。存入设置文档。', - chatAgentIdHintFallback: '默认对话服务(kb_chat)的 agent_id;未设置时每次调用都需显式指定(可用 kb_service_list 发现 id)。留空表示保持当前值。', + chatAgentIdHint: '默认对话服务(kb_chat)的 agent_id;未设置时每次调用都需显式指定(可用 `kscli service list` 发现 id)。存入设置文档。', + chatAgentIdHintFallback: '默认对话服务(kb_chat)的 agent_id;未设置时每次调用都需显式指定(可用 `kscli service list` 发现 id)。留空表示保持当前值。', chatAgentIdSet: '已配置默认对话服务。', chatAgentIdUnset: '未配置默认对话服务;每次 kb_chat 调用需显式指定。', fromEnv: '来自环境变量(此处只读)', diff --git a/packages/tool-bailian-kb/tests/endpoints.test.ts b/packages/tool-bailian-kb/tests/endpoints.test.ts index a64ee98..d1976be 100644 --- a/packages/tool-bailian-kb/tests/endpoints.test.ts +++ b/packages/tool-bailian-kb/tests/endpoints.test.ts @@ -9,6 +9,6 @@ describe('kbEndpoint', () => { it('keeps protocol paths as constants', () => { expect(KB_PATHS.chat).toBe('/api/v2/apps/knowledge/chat') - expect(KB_PATHS.serviceList).toBe('/api/v1/indices/rag/app/list') + expect(KB_PATHS.search).toBe('/api/v1/indices/knowledge/search') }) }) diff --git a/packages/tool-bailian-kb/tests/services.test.ts b/packages/tool-bailian-kb/tests/services.test.ts deleted file mode 100644 index f5853cc..0000000 --- a/packages/tool-bailian-kb/tests/services.test.ts +++ /dev/null @@ -1,49 +0,0 @@ -import { describe, expect, it, vi } from 'vitest' -import type { ServiceListResponse } from '../src/api-types.js' -import type { KbClient } from '../src/client.js' -import { listServices } from '../src/services.js' - -function fakeClient(byScene: Record) { - const postJson = vi.fn(async (_path: string, body: { agent_scene: string }) => byScene[body.agent_scene]) - return { client: { postJson } as unknown as KbClient, postJson } -} - -const row = (id: string, scene: string) => ({ - agent_id: id, agent_name: `svc-${id}`, agent_scene: scene, agent_status: 'deployed', - pipeline_list: [{ pipeline_id: 'p1', pipeline_name: 'kb-one' }], -}) - -describe('listServices', () => { - it('queries both scenes when scene is omitted and merges rows with scene tags', async () => { - const { client, postJson } = fakeClient({ - chat: { data: { total_count: 1, rows: [row('a', 'chat')] } }, - search: { data: { total_count: 1, rows: [row('b', 'search')] } }, - }) - const out = await listServices(client, {}) - expect(postJson).toHaveBeenCalledTimes(2) - expect(out.services.map(s => [s.agent_id, s.scene])).toEqual([['a', 'chat'], ['b', 'search']]) - expect(out.services[0]!.knowledge_bases).toEqual(['kb-one']) - expect(out.total).toBe(2) - expect(out.truncated).toBe(false) - const body = postJson.mock.calls[0]![1] as unknown as Record - expect(body.page_number).toBe(1) - expect(body.page_size).toBe(100) - }) - - it('queries one scene and forwards the name filter', async () => { - const { client, postJson } = fakeClient({ search: { data: { total_count: 0, rows: [] } } }) - await listServices(client, { scene: 'search', nameFilter: '客服' }) - expect(postJson).toHaveBeenCalledTimes(1) - expect((postJson.mock.calls[0]![1] as unknown as Record).agent_name).toBe('客服') - }) - - it('flags truncation when a scene exceeds one max page', async () => { - const { client } = fakeClient({ - chat: { data: { total_count: 250, rows: [row('a', 'chat')] } }, - search: { data: { total_count: 0, rows: [] } }, - }) - const out = await listServices(client, {}) - expect(out.truncated).toBe(true) - expect(out.total).toBe(250) - }) -}) diff --git a/packages/tool-bailian-kb/tests/tools.test.ts b/packages/tool-bailian-kb/tests/tools.test.ts index 0404c6a..60621a9 100644 --- a/packages/tool-bailian-kb/tests/tools.test.ts +++ b/packages/tool-bailian-kb/tests/tools.test.ts @@ -21,9 +21,9 @@ const searchResponse = { } describe('createKbTools', () => { - it('registers exactly kb_service_list, kb_search, kb_chat', () => { + it('registers kb_search and kb_chat', () => { const { list } = toolsWith(vi.fn()) - expect(list.map(t => t.name).sort()).toEqual(['kb_chat', 'kb_search', 'kb_service_list']) + expect(list.map(t => t.name).sort()).toEqual(['kb_chat', 'kb_search']) }) it('kb_search truncates nodes client-side to top_k and never sends top_k to the server', async () => { @@ -59,7 +59,7 @@ describe('createKbTools', () => { const postJson = vi.fn(async (_path: string, _body: unknown) => searchResponse) const { byName } = toolsWith(postJson) const err = await byName.kb_search!.execute({ query: 'q' }, EXEC).catch((e: unknown) => e) - expect((err as Error).message).toContain('kb_service_list') + expect((err as Error).message).toContain('kscli service list') }) it('kb_search re-resolves the default per call (credential hot-swap contract)', async () => { @@ -76,14 +76,13 @@ describe('createKbTools', () => { expect(postJson).toHaveBeenCalledTimes(1) }) - it('a 4xx failure appends the current service list to the error', async () => { - const postJson = vi.fn(async (path: string) => { - if (path === '/api/v1/indices/knowledge/search') throw new KbApiError('agent not found', 400) - return { data: { total_count: 1, rows: [{ agent_id: 'aid-9', agent_name: 'faq', agent_scene: 'search', agent_status: 'deployed' }] } } + it('a 4xx failure passes the original error through unchanged', async () => { + const postJson = vi.fn(async (_path: string) => { + throw new KbApiError('agent not found', 400) }) const { byName } = toolsWith(postJson) const err = await byName.kb_search!.execute({ query: 'q', agent_id: 'bad' }, EXEC).catch((e: unknown) => e) - expect((err as Error).message).toContain('aid-9') + expect((err as Error).message).toBe('agent not found') }) it('kb_chat buffers the SSE stream into one answer', async () => {