diff --git a/examples/agents/manyToolsAgent.ts b/examples/agents/manyToolsAgent.ts index 3bd2154..1acb7f4 100644 --- a/examples/agents/manyToolsAgent.ts +++ b/examples/agents/manyToolsAgent.ts @@ -40,9 +40,9 @@ import { WikipediaTool } from "bee-agent-framework/tools/search/wikipedia"; // import { ArXivTool } from "bee-agent-framework/tools/arxiv"; // contrib tools -// import { HelloWorldTool } from "@/tools/helloWorld.js"; -import { OpenLibraryTool } from "bee-community-tools/tools/openLibrary"; -import { ImageDescriptionTool } from "bee-community-tools/tools/imageDescription"; +// import { HelloWorldTool } from "./tools/helloWorld.js"; +import { OpenLibraryTool } from "bee-community-tools/tools/openLibrary/openLibrary"; +import { ImageDescriptionTool } from "bee-community-tools/tools/imageDescriptions/imageDescription"; Logger.root.level = "silent"; // disable internal logs const logger = new Logger({ name: "app", level: "trace" }); diff --git a/examples/tsconfig.json b/examples/tsconfig.json index 95d325d..cc60569 100644 --- a/examples/tsconfig.json +++ b/examples/tsconfig.json @@ -4,8 +4,10 @@ "baseUrl": "..", "rootDir": "..", "paths": { - "bee-community-tools/*": ["./src/*.js"], - "@/*": ["./src/*"] + "bee-community-tools/tools/*": ["./tools/*.js"], + "@/*": ["./src/*"], + "@tools/*": ["./tools/*"], + "@helpers/*": ["./tools/examples/helpers/*"] } }, "references": [{ "path": "./src" }], diff --git a/package.json b/package.json index d3cfb16..2d2dbd9 100644 --- a/package.json +++ b/package.json @@ -61,9 +61,9 @@ "lint:fix": "yarn eslint --fix", "format": "yarn prettier --check .", "format:fix": "yarn prettier --write .", - "test:unit": "vitest run src", + "test:unit": "vitest run tools -t \"Unit Test\"", "test:unit:watch": "vitest run src", - "test:e2e": "vitest run tests", + "test:e2e": "vitest run tools -t \"e2e Test\"", "test:e2e:watch": "vitest watch tests", "test:all": "vitest run", "test:watch": "vitest watch", diff --git a/src/tools/airtable.ts b/tools/airtable/airtable.ts similarity index 100% rename from src/tools/airtable.ts rename to tools/airtable/airtable.ts diff --git a/src/tools/airtableTestData.json b/tools/airtable/airtableTestData.json similarity index 100% rename from src/tools/airtableTestData.json rename to tools/airtable/airtableTestData.json diff --git a/src/tools/airtableTool.test.ts b/tools/airtable/airtableTool.ts similarity index 96% rename from src/tools/airtableTool.test.ts rename to tools/airtable/airtableTool.ts index 2c8dfe4..48eca72 100644 --- a/src/tools/airtableTool.test.ts +++ b/tools/airtable/airtableTool.ts @@ -15,7 +15,7 @@ */ import { describe, test } from "vitest"; -import { AirtableTool, AirtableToolOptions } from "@/tools/airtable.js"; +import { AirtableTool, AirtableToolOptions } from "@tools/airtable/airtable.js"; import { setupServer } from "msw/node"; diff --git a/examples/agents/airTableToolAgent.ts b/tools/airtable/examples/airTableToolAgent.ts similarity index 97% rename from examples/agents/airTableToolAgent.ts rename to tools/airtable/examples/airTableToolAgent.ts index 65a4367..9d74112 100644 --- a/examples/agents/airTableToolAgent.ts +++ b/tools/airtable/examples/airTableToolAgent.ts @@ -17,7 +17,7 @@ import "dotenv/config.js"; import { BAMChatLLM } from "bee-agent-framework/adapters/bam/chat"; import { BeeAgent } from "bee-agent-framework/agents/bee/agent"; -import { createConsoleReader } from "../helpers/io.js"; +import { createConsoleReader } from "@tools/examples/helpers/io.js"; import { FrameworkError } from "bee-agent-framework/errors"; import { TokenMemory } from "bee-agent-framework/memory/tokenMemory"; import { Logger } from "bee-agent-framework/logger/logger"; @@ -39,7 +39,7 @@ import { DuckDuckGoSearchTool } from "bee-agent-framework/tools/search/duckDuckG import { WikipediaTool } from "bee-agent-framework/tools/search/wikipedia"; // AirTable tool -import { AirtableTool } from "bee-community-tools/tools/airtable"; +import { AirtableTool } from "@tools/airtable/airtable.js"; const AIRTABLE_TOKEN: string = parseEnv("AIRTABLE_TOKEN", z.string()); const AIRTABLE_BASE: string = parseEnv("AIRTABLE_BASE", z.string()); diff --git a/tests/e2e/airtable.test.ts b/tools/airtable/tests/e2e/airtable.test.ts similarity index 88% rename from tests/e2e/airtable.test.ts rename to tools/airtable/tests/e2e/airtable.test.ts index b6a6031..668d901 100644 --- a/tests/e2e/airtable.test.ts +++ b/tools/airtable/tests/e2e/airtable.test.ts @@ -14,13 +14,14 @@ * limitations under the License. */ -import { AirtableTool } from "@/tools/airtable.js"; +// eslint-disable-next-line no-restricted-imports +import { AirtableTool } from "../../airtable.js"; import { beforeEach, expect } from "vitest"; const AIRTABLE_TOKEN: string = process.env.AIRTABLE_TOKEN as string; const AIRTABLE_BASE: string = process.env.AIRTABLE_BASE as string; -describe("AirtableTool", () => { +describe("AirtableTool e2e Test", () => { let instance: AirtableTool; beforeEach(() => { diff --git a/tools/examples/helpers/io.ts b/tools/examples/helpers/io.ts new file mode 100644 index 0000000..62cbe95 --- /dev/null +++ b/tools/examples/helpers/io.ts @@ -0,0 +1,89 @@ +/** + * Copyright 2024 IBM Corp. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import readline from "node:readline/promises"; +import { stdin, stdout } from "node:process"; +import picocolors from "picocolors"; +import * as R from "remeda"; +import stripAnsi from "strip-ansi"; + +interface ReadFromConsoleInput { + fallback?: string; + input?: string; + allowEmpty?: boolean; +} + +export function createConsoleReader({ + fallback, + input = "User 👤 : ", + allowEmpty = false, +}: ReadFromConsoleInput = {}) { + const rl = readline.createInterface({ input: stdin, output: stdout, terminal: true }); + let isActive = true; + + return { + write(role: string, data: string) { + rl.write( + [role && R.piped(picocolors.red, picocolors.bold)(role), stripAnsi(data ?? "")] + .filter(Boolean) + .join(" ") + .concat("\n"), + ); + }, + async prompt(): Promise { + for await (const { prompt } of this) { + return prompt; + } + process.exit(0); + }, + async *[Symbol.asyncIterator]() { + if (!isActive) { + return; + } + + try { + rl.write( + `${picocolors.dim(`Interactive session has started. To escape, input 'q' and submit.\n`)}`, + ); + + for (let iteration = 1, prompt = ""; isActive; iteration++) { + prompt = await rl.question(R.piped(picocolors.cyan, picocolors.bold)(input)); + prompt = stripAnsi(prompt); + + if (prompt === "q") { + break; + } + if (!prompt.trim() || prompt === "\n") { + prompt = fallback ?? ""; + } + if (allowEmpty !== false && !prompt.trim()) { + rl.write("Error: Empty prompt is not allowed. Please try again.\n"); + iteration -= 1; + continue; + } + yield { prompt, iteration }; + } + } catch (e) { + if (e.code === "ERR_USE_AFTER_CLOSE") { + return; + } + } finally { + isActive = false; + rl.close(); + } + }, + }; +} diff --git a/src/tools/helloWorld.ts b/tools/helloWorld/helloWorld.ts similarity index 100% rename from src/tools/helloWorld.ts rename to tools/helloWorld/helloWorld.ts diff --git a/tests/e2e/dummy.test.ts b/tools/helloWorld/tests/e2e/helloWorld.test.ts similarity index 95% rename from tests/e2e/dummy.test.ts rename to tools/helloWorld/tests/e2e/helloWorld.test.ts index 4dc860d..0fa066b 100644 --- a/tests/e2e/dummy.test.ts +++ b/tools/helloWorld/tests/e2e/helloWorld.test.ts @@ -14,7 +14,7 @@ * limitations under the License. */ -describe("Test", () => { +describe("Test e2e Test", () => { it("Runs", async () => { expect(1).toBe(1); }); diff --git a/src/tools/helloWorld.test.ts b/tools/helloWorld/tests/helloWorld.test.ts similarity index 86% rename from src/tools/helloWorld.test.ts rename to tools/helloWorld/tests/helloWorld.test.ts index bc58acf..8ed464d 100644 --- a/src/tools/helloWorld.test.ts +++ b/tools/helloWorld/tests/helloWorld.test.ts @@ -15,9 +15,10 @@ */ import { describe, test } from "vitest"; -import { HelloWorldTool } from "./helloWorld.js"; +// eslint-disable-next-line no-restricted-imports +import { HelloWorldTool } from "../helloWorld.js"; -describe("HelloWorldTool", () => { +describe("HelloWorldTool Unit Test", () => { test("HelloWorld", () => { const helloWorldTool = new HelloWorldTool(); helloWorldTool diff --git a/src/tools/imageDescription.ts b/tools/imageDescriptions/imageDescription.ts similarity index 100% rename from src/tools/imageDescription.ts rename to tools/imageDescriptions/imageDescription.ts diff --git a/tests/e2e/imageDescription.test.ts b/tools/imageDescriptions/tests/e2e/imageDescription.test.ts similarity index 91% rename from tests/e2e/imageDescription.test.ts rename to tools/imageDescriptions/tests/e2e/imageDescription.test.ts index bc30425..6542f56 100644 --- a/tests/e2e/imageDescription.test.ts +++ b/tools/imageDescriptions/tests/e2e/imageDescription.test.ts @@ -14,11 +14,12 @@ * limitations under the License. */ -import { ImageDescriptionTool } from "@/tools/imageDescription.js"; +// eslint-disable-next-line no-restricted-imports +import { ImageDescriptionTool } from "../../imageDescription.js"; import { beforeEach, expect } from "vitest"; -describe("ImageDescriptionTool", () => { +describe("ImageDescriptionTool e2e Test", () => { let instance: ImageDescriptionTool; beforeEach(() => { diff --git a/src/tools/imageDescription.test.ts b/tools/imageDescriptions/tests/imageDescription.test.ts similarity index 91% rename from src/tools/imageDescription.test.ts rename to tools/imageDescriptions/tests/imageDescription.test.ts index 4f8e16b..26c60ab 100644 --- a/src/tools/imageDescription.test.ts +++ b/tools/imageDescriptions/tests/imageDescription.test.ts @@ -14,7 +14,8 @@ * limitations under the License. */ -import { ImageDescriptionTool } from "@/tools/imageDescription.js"; +// eslint-disable-next-line no-restricted-imports +import { ImageDescriptionTool } from "../imageDescription.js"; import { afterAll, afterEach, beforeAll, expect, describe, test, vi } from "vitest"; import { setupServer } from "msw/node"; @@ -38,7 +39,7 @@ const handlers = [ const server = setupServer(...handlers); -describe("ImageDescriptionTool", () => { +describe("ImageDescriptionTool Unit Test", () => { beforeAll(() => { vi.stubEnv("IMAGE_DESC_VLLM_API", "https://api.openai.com"); vi.stubEnv("IMAGE_DESC_MODEL_ID", "llava-hf/llama3-llava-next-8b-hf"); diff --git a/src/tools/openLibrary.ts b/tools/openLibrary/openLibrary.ts similarity index 100% rename from src/tools/openLibrary.ts rename to tools/openLibrary/openLibrary.ts diff --git a/tests/e2e/openLibrary.test.ts b/tools/openLibrary/tests/e2e/openLibrary.test.ts similarity index 91% rename from tests/e2e/openLibrary.test.ts rename to tools/openLibrary/tests/e2e/openLibrary.test.ts index b05dfe7..629d4ea 100644 --- a/tests/e2e/openLibrary.test.ts +++ b/tools/openLibrary/tests/e2e/openLibrary.test.ts @@ -14,11 +14,12 @@ * limitations under the License. */ -import { OpenLibraryTool } from "@/tools/openLibrary.js"; +// eslint-disable-next-line no-restricted-imports +import { OpenLibraryTool } from "../../openLibrary.js"; import { beforeEach, expect } from "vitest"; -describe("Open Library", () => { +describe("Open Library e2e Test", () => { let instance: OpenLibraryTool; beforeEach(() => { diff --git a/src/tools/openLibrary.test.ts b/tools/openLibrary/tests/openLibrary.test.ts similarity index 94% rename from src/tools/openLibrary.test.ts rename to tools/openLibrary/tests/openLibrary.test.ts index 02a8755..cfa647d 100644 --- a/src/tools/openLibrary.test.ts +++ b/tools/openLibrary/tests/openLibrary.test.ts @@ -15,7 +15,8 @@ */ import { describe, test } from "vitest"; -import { OpenLibraryTool } from "./openLibrary.js"; +// eslint-disable-next-line no-restricted-imports +import { OpenLibraryTool } from "../openLibrary.js"; import { setupServer } from "msw/node"; import { http, HttpResponse } from "msw"; import { ToolError } from "bee-agent-framework/tools/base"; @@ -57,7 +58,7 @@ const server = setupServer( }), ); -describe("OpenLibraryTool", () => { +describe("OpenLibraryTool Unit Test", () => { const openLibraryTool = new OpenLibraryTool(); beforeAll(() => { diff --git a/src/version.test.ts b/tools/version.test.ts similarity index 89% rename from src/version.test.ts rename to tools/version.test.ts index 7a06eed..a968cd3 100644 --- a/src/version.test.ts +++ b/tools/version.test.ts @@ -14,9 +14,9 @@ * limitations under the License. */ -import { Version } from "@/version.js"; +import { Version } from "./version.js"; -describe("Version", () => { +describe("Version Unit Test", () => { it("has a valid version", () => { expect(Version).toBeTruthy(); }); diff --git a/src/version.ts b/tools/version.ts similarity index 100% rename from src/version.ts rename to tools/version.ts diff --git a/tsup.config.ts b/tsup.config.ts index c3673e4..49838b5 100644 --- a/tsup.config.ts +++ b/tsup.config.ts @@ -7,7 +7,7 @@ import tsConfig from "./tsconfig.json"; import { JscTarget } from "@swc/types"; export default defineConfig({ - entry: ["src/**/*.{ts,js}", "!src/**/*.test.ts"], + entry: ["tools/**/*.{ts,js}", "!tools/**/*.test.ts"], tsconfig: "./tsconfig.json", sourcemap: true, dts: true,