diff --git a/.gitea/workflows/publish.yml b/.gitea/workflows/publish.yml index 25ba696..92d98c0 100644 --- a/.gitea/workflows/publish.yml +++ b/.gitea/workflows/publish.yml @@ -20,7 +20,7 @@ jobs: - run: pnpm install --frozen-lockfile - run: pnpm install vitest - run: pnpm -r build - - run: pnpm -r test + - run: pnpm test - run: pnpm -r pack publish: diff --git a/package.json b/package.json index 756f703..49e0646 100644 --- a/package.json +++ b/package.json @@ -10,7 +10,11 @@ }, "scripts": { "build": "pnpm -r run build", - "test": "pnpm -r run test", + "test": "vitest run", "pack": "pnpm -r pack" + }, + "devDependencies": { + "@types/node": "^22.20.0", + "vitest": "^4.1.8" } } \ No newline at end of file diff --git a/packages/tool-lab/package.json b/packages/tool-lab/package.json index d66b49b..43539d7 100644 --- a/packages/tool-lab/package.json +++ b/packages/tool-lab/package.json @@ -35,8 +35,7 @@ "node": ">=22.19" }, "scripts": { - "build": "tsc -p tsconfig.json", - "test": "vitest run" + "build": "tsc -p tsconfig.json" }, "peerDependencies": { "@deepseek-ai/cordis": "^4.0.1", diff --git a/packages/tool-lab/tests/tool-lab.spec.ts b/packages/tool-lab/tests/tool-lab.spec.ts index 2b662cc..a6ce886 100644 --- a/packages/tool-lab/tests/tool-lab.spec.ts +++ b/packages/tool-lab/tests/tool-lab.spec.ts @@ -1,79 +1,14 @@ -import { describe, expect, it, vi, afterEach } from 'vitest' -import { Context } from '@deepseek-ai/cordis' -import { CallId } from '@deepseek-ai/dsh-llm' -import ToolRuntime from '@deepseek-ai/dsh-tools' -import { FileSystem } from '@deepseek-ai/dsh-fs' -import type { FsTarget, FsDirEntry, FsEditOutcome, FsEditRequest, FsInfo, FsPathInfo, FsWriteIntent, FsWriteOutcome, FsVersion, FsTargetKey } from '@deepseek-ai/dsh-fs' -import * as ToolLab from '@deepseek-ai/dsh-tool-lab' +import { afterEach, describe, expect, it, vi } from 'vitest' import { runComfyImage, resolveImageArgs, buildWorkflow } from '@deepseek-ai/dsh-tool-lab/src/comfy.ts' import { runDoclingOcr } from '@deepseek-ai/dsh-tool-lab/src/docling.ts' import { runWhishTranscribe } from '@deepseek-ai/dsh-tool-lab/src/whish.ts' -import type { SandboxExecutionPolicy, SandboxMode } from '@deepseek-ai/dsh-sandbox' -const testToolSignal = new AbortController().signal - -/** In-memory fake ctx.fs backend for upload tools. */ -class FakeFs extends FileSystem { - files = new Map() - override async resolve(path: string): Promise { - return { targetKey: FsTargetKey(`key:${path}`), displayPath: `/abs/${path}` } - } - override processPath(target: FsTarget): string { return String(target.targetKey) } - override fileUrl(target: FsTarget): string { return `file://${target.targetKey}` } - override contains(parent: FsTarget, child: FsTarget): boolean { - return child.targetKey === parent.targetKey || String(child.targetKey).startsWith(`${parent.targetKey}/`) - } - override async stat(): Promise { return undefined } - override async lstat(): Promise { return undefined } - override async readText(target: FsTarget): Promise { - return new TextDecoder().decode(this.files.get(target.targetKey) ?? new Uint8Array()) - } - override async streamText(target: FsTarget): Promise> { - const text = await this.readText(target) - return (async function* () { yield text })() - } - override async readBytes(target: FsTarget, _signal: AbortSignal | undefined, maxBytes: number): Promise { - const bytes = this.files.get(target.targetKey) ?? new Uint8Array() - if (bytes.length > maxBytes) throw new Error('FS_TOO_LARGE') - return bytes - } - override async listDir(): Promise { return [] } - override async writeText(target: FsTarget, content: string): Promise { - const before = this.files.get(target.targetKey) ?? null - this.files.set(target.targetKey, new TextEncoder().encode(content)) - return { operation: before !== null ? 'update' : 'create', version: FsVersion('v2'), before, after: content } - } - override async editText(target: FsTarget, edit: FsEditRequest): Promise { - const content = new TextDecoder().decode(this.files.get(target.targetKey) ?? new Uint8Array()) - const after = content.split(edit.oldString).join(edit.newString) - this.files.set(target.targetKey, new TextEncoder().encode(after)) - return { version: FsVersion('v3'), before: content, after } - } -} - -async function mount(opts: { files?: Record } = {}) { - const ctx = new Context() - await ctx.plugin(ToolRuntime) - const fs = new FakeFs() - if (opts.files) for (const [k, v] of Object.entries(opts.files)) fs.files.set(`key:${k}`, v) - await ctx.plugin(fs, {}) - await ctx.plugin(ToolLab, {}) - let counter = 0 - const call = (name: string, args: unknown) => ctx.tools.execute({ - signal: testToolSignal, - callId: CallId(`call-${++counter}`), - name, - arguments: args, - }) - return { ctx, fs, call } -} +const pngBytes = new Uint8Array([137, 80, 78, 71, 13, 10, 26, 10, 0, 0, 0, 13]) function jsonResponse(body: unknown, { status = 200, headers }: { status?: number; headers?: Record } = {}): Response { return new Response(JSON.stringify(body), { status, headers: { 'content-type': 'application/json', ...headers } }) } -const pngBytes = new Uint8Array([137, 80, 78, 71, 13, 10, 26, 10, 0, 0, 0, 13]) - afterEach(() => { vi.unstubAllGlobals() }) describe('lab_generate_image (ComfyUI)', () => { @@ -86,8 +21,6 @@ describe('lab_generate_image (ComfyUI)', () => { if (url.includes('/history/p-1')) return jsonResponse({ 'p-1': { outputs: { '9': { images: [{ filename: 'dsh_00001_.png' }] } } } }) throw new Error(`unexpected fetch ${url}`) })) - const { fs } = await mountTarget({ files: { 'x.png': pngBytes } }) - void fs const out = await runComfyImage('http://192.168.31.240:8188', resolveImageArgs({ prompt: 'a cat' }), undefined, 30_000) expect(out).toBe('http://192.168.31.240:8188/view?filename=dsh_00001_.png&subfolder=&type=output') @@ -154,9 +87,4 @@ describe('lab_transcribe_audio (Whishper)', () => { await expect(runWhishTranscribe('http://x', new Uint8Array(), 'a.wav', undefined, undefined, undefined, 30_000)) .rejects.toThrow(/no id/) }) -}) - -// keep imports referenced for the fake implementation; these types come from -// @deepseek-ai/dsh-fs and are used by FakeFs above. -type _ = SandboxExecutionPolicy | SandboxMode -export { FakeFs } \ No newline at end of file +}) \ No newline at end of file diff --git a/packages/web-search-searxng/package.json b/packages/web-search-searxng/package.json index 4c4b060..b89e3cc 100644 --- a/packages/web-search-searxng/package.json +++ b/packages/web-search-searxng/package.json @@ -35,8 +35,7 @@ "node": ">=22.19" }, "scripts": { - "build": "tsc -p tsconfig.json", - "test": "vitest run" + "build": "tsc -p tsconfig.json" }, "peerDependencies": { "@deepseek-ai/cordis": "^4.0.1", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 21f59e7..feb7919 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -6,19 +6,14 @@ settings: importers: - .: {} - - .test-dsh/profiles/web: - dependencies: - '@deepseek-ai/dsh-telegram-remote': - specifier: workspace:* - version: link:../../../packages/telegram-remote - '@deepseek-ai/dsh-tool-lab': - specifier: workspace:* - version: link:../../../packages/tool-lab - '@deepseek-ai/dsh-web-search-searxng': - specifier: workspace:* - version: link:../../../packages/web-search-searxng + .: + devDependencies: + '@types/node': + specifier: ^22.20.0 + version: 22.20.1 + vitest: + specifier: ^4.1.8 + version: 4.1.11(@types/node@22.20.1)(vite@8.2.2(@types/node@22.20.1)) packages/telegram-remote: {}