├── .nvmrc ├── .yarnrc.yml ├── .husky ├── commit-msg └── pre-commit ├── tsconfig.examples.json ├── prettier.config.js ├── .lintstagedrc.json ├── .env.test.example ├── examples ├── tsconfig.json ├── helpers │ └── io.ts └── agents │ ├── airTableToolAgent.ts │ └── manyToolsAgent.ts ├── commitlint.config.ts ├── .github ├── workflows │ ├── release.yml │ ├── main.yml │ └── e2e-tests.yml ├── ISSUE_TEMPLATE │ ├── feature_request.md │ └── bug_report.md └── PULL_REQUEST_TEMPLATE.md ├── .env.template ├── SECURITY.md ├── vitest.config.ts ├── .release-it.json ├── tests ├── e2e │ ├── dummy.test.ts │ ├── airtable.test.ts │ ├── openLibrary.test.ts │ └── imageDescription.test.ts └── setup.ts ├── src ├── version.test.ts ├── version.ts └── tools │ ├── helloWorld.test.ts │ ├── helloWorld.ts │ ├── imageDescription.test.ts │ ├── airtableTool.test.ts │ ├── airtableTestData.json │ ├── openLibrary.test.ts │ ├── imageDescription.ts │ ├── openLibrary.ts │ └── airtable.ts ├── docs └── assets │ ├── Bee_logo_white.svg │ ├── Bee_logo_black.svg │ └── Bee_Dark.svg ├── tsconfig.json ├── .secrets.baseline ├── scripts └── copyright.sh ├── CHANGELOG.md ├── .gitignore ├── eslint.config.js ├── CODE_OF_CONDUCT.md ├── tsup.config.ts ├── package.json ├── README.md ├── CONTRIBUTING.md └── LICENSE /.nvmrc: -------------------------------------------------------------------------------- 1 | 20.15.1 2 | -------------------------------------------------------------------------------- /.yarnrc.yml: -------------------------------------------------------------------------------- 1 | nodeLinker: node-modules 2 | -------------------------------------------------------------------------------- /.husky/commit-msg: -------------------------------------------------------------------------------- 1 | yarn commitlint --edit $1 2 | -------------------------------------------------------------------------------- /tsconfig.examples.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./examples/tsconfig.json" 3 | } 4 | -------------------------------------------------------------------------------- /.husky/pre-commit: -------------------------------------------------------------------------------- 1 | yarn lint-staged 2 | CI=true yarn test:unit && yarn copyright 3 | -------------------------------------------------------------------------------- /prettier.config.js: -------------------------------------------------------------------------------- 1 | // @ts-check 2 | 3 | /** @type {import("prettier").Config} */ 4 | const config = { 5 | printWidth: 100, 6 | quoteProps: "consistent", 7 | }; 8 | 9 | export default config; 10 | -------------------------------------------------------------------------------- /.lintstagedrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "*.{ts,js}": "eslint --fix", 3 | "*.ts": "tsc-files --noEmit", 4 | "*": "prettier --ignore-unknown --write", 5 | "!.secrets.baseline": "detect-secrets-hook --baseline .secrets.baseline" 6 | } 7 | -------------------------------------------------------------------------------- /.env.test.example: -------------------------------------------------------------------------------- 1 | # Required for the imageDescription unit test 2 | IMAGE_DESC_VLLM_API=https://api.openai.com 3 | IMAGE_DESC_MODEL_ID=llava-hf/llama3-llava-next-8b-hf 4 | OPENAI_API_KEY=abc123 # pragma: allowlist secret 5 | 6 | 7 | 8 | # Airtable Tool 9 | # AIRTABLE_TOKEN= 10 | # AIRTABLE_BASE= -------------------------------------------------------------------------------- /examples/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "compilerOptions": { 4 | "baseUrl": "..", 5 | "rootDir": "..", 6 | "paths": { 7 | "bee-community-tools/*": ["./src/*.js"], 8 | "@/*": ["./src/*"] 9 | } 10 | }, 11 | "references": [{ "path": "./src" }], 12 | "exclude": ["../tests", "../**/*.test.ts"] 13 | } 14 | -------------------------------------------------------------------------------- /commitlint.config.ts: -------------------------------------------------------------------------------- 1 | import type { UserConfig } from "@commitlint/types"; 2 | 3 | const Configuration: UserConfig = { 4 | extends: ["@commitlint/config-conventional"], 5 | ignores: [ 6 | function ignoreDependabot(commit: string) { 7 | return commit.includes("") && commit.includes("dependabot"); 8 | }, 9 | ], 10 | }; 11 | 12 | export default Configuration; 13 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Slackbot on release 2 | 3 | on: 4 | workflow_dispatch: 5 | release: 6 | types: 7 | - created 8 | 9 | jobs: 10 | notify_slack: 11 | runs-on: ubuntu-latest 12 | name: Notify Slack on Release 13 | steps: 14 | - name: Notify Slack on Releases 15 | uses: amendx/slackbot-release@1.0.1 16 | with: 17 | slack_webhook_url: ${{ secrets.SLACK_WEBHOOK_URL }} 18 | -------------------------------------------------------------------------------- /.env.template: -------------------------------------------------------------------------------- 1 | # Framework related 2 | BEE_FRAMEWORK_LOG_PRETTY=true 3 | BEE_FRAMEWORK_LOG_LEVEL="info" 4 | BEE_FRAMEWORK_LOG_SINGLE_LINE="false" 5 | 6 | # For BAM LLM Adapter 7 | # GENAI_API_KEY= 8 | 9 | # For Watsonx LLM Adapter 10 | # WATSONX_API_KEY= 11 | # WATSONX_PROJECT_ID= 12 | 13 | # For OpenAI LLM Adapter 14 | # OPENAI_API_KEY= 15 | 16 | # Image Description Tool 17 | IMAGE_DESC_VLLM_API=https://api.openai.com 18 | IMAGE_DESC_MODEL_ID=llava-hf/llama3-llava-next-8b-hf 19 | 20 | 21 | # Airtable Tool 22 | # AIRTABLE_TOKEN= 23 | # AIRTABLE_BASE= -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | # Security Policy 2 | 3 | ## Reporting a Vulnerability 4 | 5 | To report vulnerabilities, you can privately report a potential security issue via the GitHub security vulnerabilities feature. This can be done here: 6 | 7 | https://github.com/i-am-bee/bee-community-tools/security/advisories 8 | 9 | Please do **not** open a public issue about a potential security vulnerability. 10 | 11 | You can find more details on the security vulnerability feature in the GitHub documentation here: 12 | 13 | https://docs.github.com/en/code-security/security-advisories/guidance-on-reporting-and-writing/privately-reporting-a-security-vulnerability 14 | -------------------------------------------------------------------------------- /vitest.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from "vitest/config"; 2 | import tsConfigPaths from "vite-tsconfig-paths"; 3 | import packageJson from "./package.json"; 4 | 5 | export default defineConfig({ 6 | test: { 7 | globals: true, 8 | passWithNoTests: true, 9 | testTimeout: 120 * 1000, 10 | printConsoleTrace: true, 11 | setupFiles: ["./tests/setup.ts"], 12 | deps: { 13 | interopDefault: false, 14 | }, 15 | }, 16 | define: { 17 | __LIBRARY_VERSION: JSON.stringify(packageJson.version), 18 | }, 19 | plugins: [ 20 | tsConfigPaths({ 21 | projects: ["tsconfig.json"], 22 | }), 23 | ], 24 | }); 25 | -------------------------------------------------------------------------------- /.release-it.json: -------------------------------------------------------------------------------- 1 | { 2 | "plugins": { 3 | "@release-it/conventional-changelog": { 4 | "preset": { 5 | "name": "conventionalcommits" 6 | }, 7 | "header": "# Changelog", 8 | "infile": "CHANGELOG.md" 9 | } 10 | }, 11 | "git": { 12 | "commitMessage": "chore: release ${version}", 13 | "tagName": "v${version}" 14 | }, 15 | "npm": { 16 | "skipChecks": true, 17 | "publish": true 18 | }, 19 | "hooks": { 20 | "before:init": ["yarn lint", "yarn ts:check", "yarn test:all"], 21 | "after:bump": ["yarn build"] 22 | }, 23 | "github": { 24 | "release": true, 25 | "releaseName": "v${version}" 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: "" 5 | labels: "" 6 | assignees: "" 7 | --- 8 | 9 | **Is your feature request related to a problem? Please describe.** 10 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 11 | 12 | **Describe the solution you'd like** 13 | A clear and concise description of what you want to happen. 14 | 15 | **Describe alternatives you've considered** 16 | A clear and concise description of any alternative solutions or features you've considered. 17 | 18 | **Additional context** 19 | Add any other context or screenshots about the feature request here. 20 | -------------------------------------------------------------------------------- /tests/e2e/dummy.test.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2024 IBM Corp. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | describe("Test", () => { 18 | it("Runs", async () => { 19 | expect(1).toBe(1); 20 | }); 21 | }); 22 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: "" 5 | labels: bug 6 | assignees: "" 7 | --- 8 | 9 | **Describe the bug** 10 | A clear and concise description of what the bug is. 11 | 12 | **To Reproduce** 13 | Steps to reproduce the behavior: 14 | 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots / Code snippets** 24 | If applicable, add screenshots or code snippets to help explain your problem. 25 | 26 | **Set-up:** 27 | 28 | - Bee version: [e.g. v0.0.3] 29 | - Model provider [e.g. watsonx] 30 | 31 | **Additional context** 32 | Add any other context about the problem here. 33 | -------------------------------------------------------------------------------- /src/version.test.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2024 IBM Corp. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | import { Version } from "@/version.js"; 18 | 19 | describe("Version", () => { 20 | it("has a valid version", () => { 21 | expect(Version).toBeTruthy(); 22 | }); 23 | }); 24 | -------------------------------------------------------------------------------- /src/version.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2024 IBM Corp. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | declare const __LIBRARY_VERSION: string; 18 | 19 | let Version = "0.0.0"; 20 | try { 21 | Version = __LIBRARY_VERSION; 22 | } catch { 23 | /* empty */ 24 | } 25 | 26 | export { Version }; 27 | -------------------------------------------------------------------------------- /docs/assets/Bee_logo_white.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2022", 4 | "module": "NodeNext", 5 | "rootDir": ".", 6 | "moduleResolution": "NodeNext", 7 | "allowSyntheticDefaultImports": true, 8 | "isolatedModules": true, 9 | "useDefineForClassFields": true, 10 | "incremental": false, 11 | "moduleDetection": "auto", 12 | "baseUrl": ".", 13 | "types": ["node", "vitest/globals"], 14 | "declarationMap": false, 15 | "declaration": false, 16 | "resolveJsonModule": true, 17 | "sourceMap": false, 18 | "allowJs": true, 19 | "outDir": "./dist", 20 | "removeComments": true, 21 | "esModuleInterop": true, 22 | "forceConsistentCasingInFileNames": true, 23 | "experimentalDecorators": true, 24 | "emitDecoratorMetadata": true, 25 | "strict": true, 26 | "skipLibCheck": true, 27 | "paths": { 28 | "@/*": ["./src/*"], 29 | "@tests/*": ["./tests/*"] 30 | }, 31 | "useUnknownInCatchVariables": false 32 | }, 33 | "exclude": ["examples"] 34 | } 35 | -------------------------------------------------------------------------------- /docs/assets/Bee_logo_black.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /src/tools/helloWorld.test.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2024 IBM Corp. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | import { describe, test } from "vitest"; 18 | import { HelloWorldTool } from "./helloWorld.js"; 19 | 20 | describe("HelloWorldTool", () => { 21 | test("HelloWorld", () => { 22 | const helloWorldTool = new HelloWorldTool(); 23 | helloWorldTool 24 | .run({ identifier: "Bee" }) 25 | .then((res) => { 26 | expect(res.result).equals("Hello, Bee"); 27 | }) 28 | .catch((err) => { 29 | throw new Error(err); 30 | }); 31 | }); 32 | }); 33 | -------------------------------------------------------------------------------- /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | branches: ["main"] 6 | pull_request: 7 | branches: ["main"] 8 | 9 | concurrency: 10 | group: ${{ github.workflow }}-${{ github.ref }} 11 | cancel-in-progress: true 12 | 13 | jobs: 14 | main: 15 | timeout-minutes: 20 16 | name: Lint & Build & Test 17 | runs-on: ubuntu-latest 18 | steps: 19 | - uses: actions/checkout@v4 20 | with: 21 | fetch-depth: 0 22 | - name: Enable Corepack 23 | run: corepack enable 24 | - name: Use Node.js 25 | uses: actions/setup-node@v4 26 | with: 27 | node-version-file: ".nvmrc" 28 | cache: "yarn" 29 | - name: Install dependencies 30 | run: yarn install --immutable 31 | - name: Code Lint 32 | run: yarn lint 33 | - name: Code Format 34 | run: yarn format 35 | - name: Commits Lint 36 | run: yarn commitlint --verbose --from "${{ github.event.pull_request.base.sha || github.event.commits[0].id }}" --to "${{ github.event.pull_request.head.sha || github.event.head_commit.id }}" 37 | - name: Build 38 | run: yarn build 39 | - name: Unit Tests 40 | run: yarn test:unit 41 | -------------------------------------------------------------------------------- /.github/workflows/e2e-tests.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | branches: ["main"] 6 | paths-ignore: 7 | - "**/*.md" 8 | workflow_dispatch: 9 | 10 | concurrency: 11 | group: ${{ github.workflow }}-${{ github.ref }} 12 | cancel-in-progress: true 13 | 14 | jobs: 15 | test: 16 | name: Tests 17 | timeout-minutes: 20 18 | runs-on: ubuntu-latest 19 | 20 | strategy: 21 | fail-fast: false 22 | matrix: 23 | node-version: [18.x, 20.x, 22.x] 24 | 25 | steps: 26 | - uses: actions/checkout@v4 27 | - name: Enable Corepack 28 | run: corepack enable 29 | - name: Use Node.js ${{ matrix.node-version }} 30 | uses: actions/setup-node@v4 31 | with: 32 | node-version: ${{ matrix.node-version }} 33 | cache: "yarn" 34 | - name: Install dependencies 35 | run: yarn install --immutable 36 | - name: E2E Tests 37 | env: 38 | GENAI_API_KEY: ${{ secrets.GENAI_API_KEY }} 39 | IMAGE_DESC_VLLM_API: ${{ secrets.IMAGE_DESC_VLLM_API }} 40 | IMAGE_DESC_MODEL_ID: ${{ secrets.IMAGE_DESC_MODEL_ID }} 41 | AIRTABLE_BASE: ${{ secrets.AIRTABLE_BASE }} 42 | AIRTABLE_TOKEN: ${{ secrets.AIRTABLE_TOKEN }} 43 | run: yarn test:e2e 44 | -------------------------------------------------------------------------------- /tests/e2e/airtable.test.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2024 IBM Corp. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | import { AirtableTool } from "@/tools/airtable.js"; 18 | import { beforeEach, expect } from "vitest"; 19 | 20 | const AIRTABLE_TOKEN: string = process.env.AIRTABLE_TOKEN as string; 21 | const AIRTABLE_BASE: string = process.env.AIRTABLE_BASE as string; 22 | 23 | describe("AirtableTool", () => { 24 | let instance: AirtableTool; 25 | 26 | beforeEach(() => { 27 | instance = new AirtableTool({ apiToken: AIRTABLE_TOKEN, baseId: AIRTABLE_BASE }); 28 | }); 29 | 30 | it("Runs", async () => { 31 | const response = await instance.run({ action: "GET_SCHEMA" }); 32 | expect(response.isEmpty()).toBe(false); 33 | }); 34 | }); 35 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 4 | 5 | ### Which issue(s) does this pull-request address? 6 | 7 | 10 | 11 | Closes: # 12 | 13 | ### Description 14 | 15 | 16 | 17 | ### Checklist 18 | 19 | 20 | 21 | - [ ] I have read the [contributor guide](https://github.com/i-am-bee/bee-community-tools/blob/main/CONTRIBUTING.md) 22 | - [ ] Linting passes: `yarn lint` or `yarn lint:fix` 23 | - [ ] Formatting is applied: `yarn format` or `yarn format:fix` 24 | - [ ] Unit tests pass: `yarn test:unit` 25 | - [ ] E2E tests pass: `yarn test:e2e` 26 | - [ ] Tests are included 27 | - [ ] Documentation is changed or added 28 | - [ ] Commit messages and PR title follow [conventional commits](https://www.conventionalcommits.org/en/v1.0.0/#summary) 29 | -------------------------------------------------------------------------------- /src/tools/helloWorld.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2024 IBM Corp. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | import { 18 | BaseToolOptions, 19 | BaseToolRunOptions, 20 | StringToolOutput, 21 | Tool, 22 | ToolInput, 23 | } from "bee-agent-framework/tools/base"; 24 | import { z } from "zod"; 25 | 26 | type ToolOptions = BaseToolOptions; 27 | type ToolRunOptions = BaseToolRunOptions; 28 | 29 | export class HelloWorldTool extends Tool { 30 | name = "HelloWorld"; 31 | description = "Says hello when asked for a special greeting."; 32 | 33 | inputSchema() { 34 | return z.object({ 35 | identifier: z 36 | .string() 37 | .describe("The identifier (person, object, animal, etc.) used to when saying Hello"), 38 | }); 39 | } 40 | 41 | static { 42 | this.register(); 43 | } 44 | 45 | protected async _run(input: ToolInput): Promise { 46 | return new StringToolOutput(`Hello, ${input.identifier}`); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /docs/assets/Bee_Dark.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /.secrets.baseline: -------------------------------------------------------------------------------- 1 | { 2 | "exclude": { 3 | "files": "^.secrets.baseline$", 4 | "lines": null 5 | }, 6 | "generated_at": "2024-09-16T09:06:36Z", 7 | "plugins_used": [ 8 | { 9 | "name": "AWSKeyDetector" 10 | }, 11 | { 12 | "name": "ArtifactoryDetector" 13 | }, 14 | { 15 | "name": "AzureStorageKeyDetector" 16 | }, 17 | { 18 | "base64_limit": 4.5, 19 | "name": "Base64HighEntropyString" 20 | }, 21 | { 22 | "name": "BasicAuthDetector" 23 | }, 24 | { 25 | "name": "BoxDetector" 26 | }, 27 | { 28 | "name": "CloudantDetector" 29 | }, 30 | { 31 | "ghe_instance": "github.ibm.com", 32 | "name": "GheDetector" 33 | }, 34 | { 35 | "name": "GitHubTokenDetector" 36 | }, 37 | { 38 | "hex_limit": 3, 39 | "name": "HexHighEntropyString" 40 | }, 41 | { 42 | "name": "IbmCloudIamDetector" 43 | }, 44 | { 45 | "name": "IbmCosHmacDetector" 46 | }, 47 | { 48 | "name": "JwtTokenDetector" 49 | }, 50 | { 51 | "keyword_exclude": null, 52 | "name": "KeywordDetector" 53 | }, 54 | { 55 | "name": "MailchimpDetector" 56 | }, 57 | { 58 | "name": "NpmDetector" 59 | }, 60 | { 61 | "name": "PrivateKeyDetector" 62 | }, 63 | { 64 | "name": "SlackDetector" 65 | }, 66 | { 67 | "name": "SoftlayerDetector" 68 | }, 69 | { 70 | "name": "SquareOAuthDetector" 71 | }, 72 | { 73 | "name": "StripeDetector" 74 | }, 75 | { 76 | "name": "TwilioKeyDetector" 77 | } 78 | ], 79 | "results": {}, 80 | "version": "0.13.1+ibm.62.dss", 81 | "word_list": { 82 | "file": null, 83 | "hash": null 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /tests/e2e/openLibrary.test.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2024 IBM Corp. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | import { OpenLibraryTool } from "@/tools/openLibrary.js"; 18 | 19 | import { beforeEach, expect } from "vitest"; 20 | 21 | describe("Open Library", () => { 22 | let instance: OpenLibraryTool; 23 | 24 | beforeEach(() => { 25 | instance = new OpenLibraryTool(); 26 | }); 27 | 28 | it("Runs", async () => { 29 | const response = await instance.run( 30 | { 31 | author: "Alan Turing", 32 | }, 33 | { 34 | signal: AbortSignal.timeout(60 * 1000), 35 | }, 36 | ); 37 | 38 | expect(response.isEmpty()).toBe(false); 39 | expect(response.result.length).toBeGreaterThanOrEqual(20); 40 | response.result.forEach((book) => { 41 | expect(book).toMatchObject({ 42 | title: expect.any(String), 43 | author_name: expect.any(Array), 44 | contributor: expect.any(Array), 45 | first_publish_year: expect.any(Number), 46 | publish_date: expect.any(Array), 47 | language: expect.any(Array), 48 | publish_place: expect.any(Array), 49 | place: expect.any(Array), 50 | publisher: expect.any(Array), 51 | isbn: expect.any(Array), 52 | }); 53 | }); 54 | }); 55 | }); 56 | -------------------------------------------------------------------------------- /scripts/copyright.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Copyright 2024 IBM Corp. 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | 16 | set -e 17 | 18 | # Path to the package.json file 19 | PACKAGE_JSON_PATH="./package.json" 20 | 21 | # Check if the package.json file exists 22 | if [[ ! -f "$PACKAGE_JSON_PATH" ]]; then 23 | echo "Error: package.json file not found at $PACKAGE_JSON_PATH" 24 | exit 1 25 | fi 26 | 27 | # Retrieve the author property using jq 28 | AUTHOR=$(jq -r '.author' "$PACKAGE_JSON_PATH") 29 | 30 | # Check if the author property is not null or empty 31 | if [[ -z "$AUTHOR" ]]; then 32 | echo "Error: author property not found in package.json" 33 | exit 1 34 | fi 35 | 36 | # Check if 'nwa' command is not available and 'go' is available, then install 'nwa' 37 | if ! command -v nwa &> /dev/null && command -v go &> /dev/null; then 38 | echo "Installing 'nwa' via 'go' (https://github.com/B1NARY-GR0UP/nwa)" 39 | go install github.com/B1NARY-GR0UP/nwa@latest 40 | # Ensure the GOPATH is added to the PATH environment variable 41 | export PATH=$PATH:$(go env GOPATH)/bin 42 | fi 43 | 44 | if command -v nwa &> /dev/null; then 45 | nwa add -l apache -c "$AUTHOR" src dist tests scripts 46 | elif command -v docker &> /dev/null; then 47 | docker run -it -v "${PWD}:/src" ghcr.io/b1nary-gr0up/nwa:main add -l apache -c "$AUTHOR" src dist tests scripts 48 | else 49 | echo "Error: 'nwa' is not available. Either install it manually or install go/docker." 50 | exit 1 51 | fi 52 | -------------------------------------------------------------------------------- /tests/e2e/imageDescription.test.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2024 IBM Corp. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | import { ImageDescriptionTool } from "@/tools/imageDescription.js"; 18 | 19 | import { beforeEach, expect } from "vitest"; 20 | 21 | describe("ImageDescriptionTool", () => { 22 | let instance: ImageDescriptionTool; 23 | 24 | beforeEach(() => { 25 | instance = new ImageDescriptionTool(); 26 | }); 27 | 28 | it("Runs", async () => { 29 | const response = await instance.run( 30 | { 31 | imageUrl: "https://en.wikipedia.org/static/images/icons/wikipedia.png", 32 | prompt: "Describe the image", 33 | }, 34 | { 35 | signal: AbortSignal.timeout(60 * 1000), 36 | }, 37 | ); 38 | 39 | expect(response.isEmpty()).toBe(false); 40 | expect(response).toMatchObject({ 41 | result: expect.stringContaining("Description"), 42 | }); 43 | }); 44 | 45 | it("Throws", async () => { 46 | await expect( 47 | instance.run({ 48 | imageUrl: "https://en.wikipedia.org/static/images/icons/wikipedia.err", 49 | prompt: "Describe the image", 50 | }), 51 | ).rejects.toThrowErrorMatchingInlineSnapshot( 52 | `ToolError: Request to Vllm API has failed! Bad Request 53 | Error: {"object":"error","message":"404, message='Not Found', url=URL('https://en.wikipedia.org/static/images/icons/wikipedia.err')","type":"BadRequestError","param":null,"code":400}`, 54 | ); 55 | }); 56 | }); 57 | -------------------------------------------------------------------------------- /src/tools/imageDescription.test.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2024 IBM Corp. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | import { ImageDescriptionTool } from "@/tools/imageDescription.js"; 18 | 19 | import { afterAll, afterEach, beforeAll, expect, describe, test, vi } from "vitest"; 20 | import { setupServer } from "msw/node"; 21 | import { http, HttpResponse } from "msw"; 22 | 23 | const exampleDescription = "This is the image description text."; 24 | 25 | const handlers = [ 26 | http.post(`https://api.openai.com/v1/chat/completions`, () => { 27 | return HttpResponse.json({ 28 | choices: [ 29 | { 30 | message: { 31 | content: exampleDescription, 32 | }, 33 | }, 34 | ], 35 | }); 36 | }), 37 | ]; 38 | 39 | const server = setupServer(...handlers); 40 | 41 | describe("ImageDescriptionTool", () => { 42 | beforeAll(() => { 43 | vi.stubEnv("IMAGE_DESC_VLLM_API", "https://api.openai.com"); 44 | vi.stubEnv("IMAGE_DESC_MODEL_ID", "llava-hf/llama3-llava-next-8b-hf"); 45 | vi.stubEnv("OPENAI_API_KEY", "abc123"); 46 | server.listen(); 47 | }); 48 | 49 | afterEach(() => { 50 | server.resetHandlers(); 51 | }); 52 | 53 | afterAll(() => { 54 | server.close(); 55 | vi.unstubAllEnvs(); 56 | }); 57 | 58 | test("make a request to the vllm backend to describe an image", async () => { 59 | const imageDescriptionTool = new ImageDescriptionTool(); 60 | const imageDescResp = await imageDescriptionTool.run({ 61 | imageUrl: "http://image.com/image.jpg", 62 | }); 63 | expect(imageDescResp.result).toContain(exampleDescription); 64 | }); 65 | }); 66 | -------------------------------------------------------------------------------- /tests/setup.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2024 IBM Corp. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | import dotenv from "dotenv"; 18 | import { FrameworkError } from "bee-agent-framework/errors"; 19 | import * as util from "node:util"; 20 | import { hasProps } from "bee-agent-framework/internals/helpers/object"; 21 | dotenv.config(); 22 | dotenv.config({ 23 | path: ".env.test", 24 | override: true, 25 | }); 26 | dotenv.config({ 27 | path: ".env.test.local", 28 | override: true, 29 | }); 30 | 31 | function isFrameworkErrorLike(error: unknown): error is Record { 32 | const keys = ["errors", "context", "isRetryable", "isFatal"] as (keyof FrameworkError)[]; 33 | return hasProps(keys)(error as Record); 34 | } 35 | 36 | afterEach(() => { 37 | onTestFailed((testCase) => { 38 | const errors = testCase.errors ?? []; 39 | for (const error of errors) { 40 | if (isFrameworkErrorLike(error)) { 41 | error.message = util 42 | .inspect( 43 | { 44 | message: error.message, 45 | context: error.context, 46 | cause: error.cause, 47 | isFatal: error.isFatal, 48 | isRetryable: error.isRetryable, 49 | errors: error.errors, 50 | }, 51 | { 52 | compact: false, 53 | depth: Infinity, 54 | }, 55 | ) 56 | .replaceAll("[Object: null prototype]", ""); 57 | } 58 | } 59 | }); 60 | }); 61 | 62 | expect.addSnapshotSerializer({ 63 | serialize(val: FrameworkError): string { 64 | return val.explain(); 65 | }, 66 | test(val): boolean { 67 | return val && val instanceof FrameworkError; 68 | }, 69 | }); 70 | -------------------------------------------------------------------------------- /src/tools/airtableTool.test.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2024 IBM Corp. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | import { describe, test } from "vitest"; 18 | import { AirtableTool, AirtableToolOptions } from "@/tools/airtable.js"; 19 | 20 | import { setupServer } from "msw/node"; 21 | 22 | import { http, HttpResponse } from "msw"; 23 | 24 | import exampleData from "./airtableTestData.json"; 25 | 26 | const testToolOpts: AirtableToolOptions = { apiToken: "test_api_token", baseId: "test_base_id" }; 27 | 28 | const handlers = [ 29 | http.get(`https://api.airtable.com/v0/meta/bases/${testToolOpts.baseId}/tables`, () => { 30 | return HttpResponse.json(exampleData.schema); 31 | }), 32 | 33 | http.get( 34 | `https://api.airtable.com/v0/${testToolOpts.baseId}/${exampleData.schema.tables[0].id}`, 35 | () => { 36 | return HttpResponse.json(exampleData.tableget); 37 | }, 38 | ), 39 | ]; 40 | 41 | const server = setupServer(...handlers); 42 | 43 | describe("airtable tool", () => { 44 | beforeAll(() => { 45 | server.listen(); 46 | }); 47 | 48 | afterEach(() => { 49 | server.resetHandlers(); 50 | }); 51 | 52 | afterAll(() => { 53 | server.close(); 54 | }); 55 | 56 | test("the tool builds a description and schema based on the airtable schema", async () => { 57 | const airtableTool = new AirtableTool(testToolOpts); 58 | 59 | const toolSchema = await airtableTool.run({ action: "GET_SCHEMA" }); 60 | // The tool description should contain the description of one of our tables. 61 | expect(toolSchema.result[0].description).toContain(exampleData.schema.tables[0].description); 62 | }); 63 | 64 | test("the tool makes the correct airtable query when instructed", async () => { 65 | const airtableTool = new AirtableTool(testToolOpts); 66 | 67 | const airtable_response = await airtableTool.run({ 68 | action: "QUERY_TABLE", 69 | table: exampleData.schema.tables[0].id, 70 | filterFormula: "", 71 | }); 72 | 73 | expect(airtable_response.result[0].fields["ModelURL"]).equals( 74 | exampleData.tableget.records[0].fields.ModelURL, 75 | ); 76 | }); 77 | }); 78 | -------------------------------------------------------------------------------- /src/tools/airtableTestData.json: -------------------------------------------------------------------------------- 1 | { 2 | "schema": { 3 | "tables": [ 4 | { 5 | "id": "tblzpKUktDj2rN2Bs", 6 | "name": "model_lookup", 7 | "description": "Python script to pull from Hugging Face API will update the day after a new ModelURL is added to this table", 8 | "primaryFieldId": "fldZ9cELYOkV9ilgN", 9 | "fields": [ 10 | { 11 | "type": "url", 12 | "id": "fldZ9cELYOkV9ilgN", 13 | "name": "ModelURL" 14 | }, 15 | { 16 | "type": "multipleRecordLinks", 17 | "options": { 18 | "linkedTableId": "tblxHFbBgT6Ct6CC1", 19 | "isReversed": false, 20 | "prefersSingleRecordLink": false, 21 | "inverseLinkFieldId": "fldmymopvMUXDHjnt" 22 | }, 23 | "id": "fldotrqIjdo1gJ3sr", 24 | "name": "ModelProvider" 25 | }, 26 | { 27 | "type": "multipleLookupValues", 28 | "options": { 29 | "isValid": true, 30 | "recordLinkFieldId": "fldotrqIjdo1gJ3sr", 31 | "fieldIdInLinkedTable": "fldAZrelFqSOoHnEz", 32 | "result": { 33 | "type": "singleLineText" 34 | } 35 | }, 36 | "id": "fldJBr6E3ewbWSjGh", 37 | "name": "ModelProviderName" 38 | } 39 | ], 40 | "views": [ 41 | { 42 | "id": "viwMe6eLrcXtFqLEm", 43 | "name": "Grid view", 44 | "type": "grid" 45 | } 46 | ] 47 | } 48 | ] 49 | }, 50 | "tableget": { 51 | "records": [ 52 | { 53 | "id": "recwOidCakFZZ3lRz", 54 | "createdTime": "2024-08-15T08:27:49.000Z", 55 | "fields": { 56 | "ModelURL": "https://huggingface.co/api/models?author=mistralai", 57 | "ModelProvider": ["recOhbN9S3mXOSkxi"], 58 | "ModelProviderName": ["Mistral"] 59 | } 60 | }, 61 | { 62 | "id": "rec9UQAKpvFUc2gsB", 63 | "createdTime": "2024-08-15T08:27:49.000Z", 64 | "fields": { 65 | "ModelURL": "https://huggingface.co/api/models?author=meta-llama", 66 | "ModelProvider": ["receEXRIMroOQ65kv"], 67 | "ModelProviderName": ["Meta-llama"] 68 | } 69 | }, 70 | { 71 | "id": "recGfErjneyVVH412", 72 | "createdTime": "2024-08-15T08:27:49.000Z", 73 | "fields": { 74 | "ModelURL": "https://huggingface.co/api/models?search=gemma-2&author=google", 75 | "ModelProvider": ["recTQ0jjEd1TUxlOc"], 76 | "ModelProviderName": ["Google"] 77 | } 78 | } 79 | ] 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## 0.1.0 (2024-10-29) 4 | 5 | - chore: add env vars for unit tests ([10621d4](https://github.com/i-am-bee/bee-community-tools/commit/10621d4)) 6 | - chore: rename file ([e794961](https://github.com/i-am-bee/bee-community-tools/commit/e794961)) 7 | - chore: upgrade dependencies ([c04a5f2](https://github.com/i-am-bee/bee-community-tools/commit/c04a5f2)), closes [#12](https://github.com/i-am-bee/bee-community-tools/issues/12) 8 | - feat: airtable query tool (#10) ([c473aad](https://github.com/i-am-bee/bee-community-tools/commit/c473aad)), closes [#10](https://github.com/i-am-bee/bee-community-tools/issues/10) 9 | - fix: not stubbing env vars in unit tests ([3016ba6](https://github.com/i-am-bee/bee-community-tools/commit/3016ba6)) 10 | 11 | ## 0.0.2 (2024-09-26) 12 | 13 | ### Features 14 | 15 | - ability to publish tools as a library ([#6](https://github.com/i-am-bee/bee-community-tools/issues/6)) ([67b6664](https://github.com/i-am-bee/bee-community-tools/commit/67b6664b829562a996adaa47089d7f6ec399fe5e)) 16 | - e2e tests for the Image Description tool ([f590049](https://github.com/i-am-bee/bee-community-tools/commit/f5900497b84fb23b17ab47d9eabe178061c946a7)) 17 | - e2e tests for the Open Library tool ([adca059](https://github.com/i-am-bee/bee-community-tools/commit/adca059fd71ebd56497b4ce078c080b6fbcd8ce4)) 18 | - init project ([ef7bc5a](https://github.com/i-am-bee/bee-community-tools/commit/ef7bc5a3b3fdf974d86e9aaed89a25385d6c3317)) 19 | - tool integration ([#3](https://github.com/i-am-bee/bee-community-tools/issues/3)) ([77ceb13](https://github.com/i-am-bee/bee-community-tools/commit/77ceb1366b14fcbbf42f62b1b3b4b42472c1da83)) 20 | 21 | ### Bug Fixes 22 | 23 | - abort signal propagation ([42b0020](https://github.com/i-am-bee/bee-community-tools/commit/42b0020aae80490e735de9d863d731d767bb9d26)) 24 | - e2e failure case error message ([313f57c](https://github.com/i-am-bee/bee-community-tools/commit/313f57c42605ed8bed4ba86ebc879a1af5e98b09)) 25 | - env var access and var names ([59ef58b](https://github.com/i-am-bee/bee-community-tools/commit/59ef58bc1290b2e8a5bfdfc5c3a6e35a3a33343f)) 26 | - env var handling and visibility of class methods ([0c3466b](https://github.com/i-am-bee/bee-community-tools/commit/0c3466b54149d6e2d6ab787af53e4ab388f6c77a)) 27 | - mutation of prompt ([1124b9f](https://github.com/i-am-bee/bee-community-tools/commit/1124b9f9b26efbacd2da24a162d898941950e43c)) 28 | - project dependencies ([7c36e24](https://github.com/i-am-bee/bee-community-tools/commit/7c36e24569212ea7366f8db548a03986e138019d)) 29 | - tool extensibility ([335f840](https://github.com/i-am-bee/bee-community-tools/commit/335f84097a0ca1e6fd13ed8f1cda466c1e2c335e)) 30 | - tool serialisation ([e0e9095](https://github.com/i-am-bee/bee-community-tools/commit/e0e9095073040cd4545e27671e1322ea670964ba)) 31 | - tool-imports ([e8a050f](https://github.com/i-am-bee/bee-community-tools/commit/e8a050f6ed74cec00dec12172809c5298eb71f91)) 32 | -------------------------------------------------------------------------------- /examples/helpers/io.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2024 IBM Corp. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | import readline from "node:readline/promises"; 18 | import { stdin, stdout } from "node:process"; 19 | import picocolors from "picocolors"; 20 | import * as R from "remeda"; 21 | import stripAnsi from "strip-ansi"; 22 | 23 | interface ReadFromConsoleInput { 24 | fallback?: string; 25 | input?: string; 26 | allowEmpty?: boolean; 27 | } 28 | 29 | export function createConsoleReader({ 30 | fallback, 31 | input = "User 👤 : ", 32 | allowEmpty = false, 33 | }: ReadFromConsoleInput = {}) { 34 | const rl = readline.createInterface({ input: stdin, output: stdout, terminal: true }); 35 | let isActive = true; 36 | 37 | return { 38 | write(role: string, data: string) { 39 | rl.write( 40 | [role && R.piped(picocolors.red, picocolors.bold)(role), stripAnsi(data ?? "")] 41 | .filter(Boolean) 42 | .join(" ") 43 | .concat("\n"), 44 | ); 45 | }, 46 | async prompt(): Promise { 47 | for await (const { prompt } of this) { 48 | return prompt; 49 | } 50 | process.exit(0); 51 | }, 52 | async *[Symbol.asyncIterator]() { 53 | if (!isActive) { 54 | return; 55 | } 56 | 57 | try { 58 | rl.write( 59 | `${picocolors.dim(`Interactive session has started. To escape, input 'q' and submit.\n`)}`, 60 | ); 61 | 62 | for (let iteration = 1, prompt = ""; isActive; iteration++) { 63 | prompt = await rl.question(R.piped(picocolors.cyan, picocolors.bold)(input)); 64 | prompt = stripAnsi(prompt); 65 | 66 | if (prompt === "q") { 67 | break; 68 | } 69 | if (!prompt.trim() || prompt === "\n") { 70 | prompt = fallback ?? ""; 71 | } 72 | if (allowEmpty !== false && !prompt.trim()) { 73 | rl.write("Error: Empty prompt is not allowed. Please try again.\n"); 74 | iteration -= 1; 75 | continue; 76 | } 77 | yield { prompt, iteration }; 78 | } 79 | } catch (e) { 80 | if (e.code === "ERR_USE_AFTER_CLOSE") { 81 | return; 82 | } 83 | } finally { 84 | isActive = false; 85 | rl.close(); 86 | } 87 | }, 88 | }; 89 | } 90 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ### Node template 2 | # Logs 3 | logs 4 | *.log 5 | npm-debug.log* 6 | yarn-debug.log* 7 | yarn-error.log* 8 | lerna-debug.log* 9 | .pnpm-debug.log* 10 | 11 | # Diagnostic reports (https://nodejs.org/api/report.html) 12 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 13 | 14 | # Runtime data 15 | pids 16 | *.pid 17 | *.seed 18 | *.pid.lock 19 | 20 | # Directory for instrumented libs generated by jscoverage/JSCover 21 | lib-cov 22 | 23 | # Coverage directory used by tools like istanbul 24 | coverage 25 | *.lcov 26 | 27 | # nyc test coverage 28 | .nyc_output 29 | 30 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 31 | .grunt 32 | 33 | # Bower dependency directory (https://bower.io/) 34 | bower_components 35 | 36 | # node-waf configuration 37 | .lock-wscript 38 | 39 | # Compiled binary addons (https://nodejs.org/api/addons.html) 40 | build/Release 41 | 42 | # Dependency directories 43 | node_modules/ 44 | jspm_packages/ 45 | 46 | # Snowpack dependency directory (https://snowpack.dev/) 47 | web_modules/ 48 | 49 | # TypeScript cache 50 | *.tsbuildinfo 51 | 52 | # Optional npm cache directory 53 | .npm 54 | 55 | # Optional eslint cache 56 | .eslintcache 57 | 58 | # Optional stylelint cache 59 | .stylelintcache 60 | 61 | # Microbundle cache 62 | .rpt2_cache/ 63 | .rts2_cache_cjs/ 64 | .rts2_cache_es/ 65 | .rts2_cache_umd/ 66 | 67 | # Optional REPL history 68 | .node_repl_history 69 | 70 | # Output of 'npm pack' 71 | *.tgz 72 | 73 | # Yarn Integrity file 74 | .yarn-integrity 75 | 76 | # dotenv environment variable files 77 | .env 78 | .env.development.local 79 | .env.test.local 80 | .env.production.local 81 | .env.local 82 | 83 | # parcel-bundler cache (https://parceljs.org/) 84 | .cache 85 | .parcel-cache 86 | 87 | # Next.js build output 88 | .next 89 | out 90 | 91 | # Nuxt.js build / generate output 92 | .nuxt 93 | dist 94 | 95 | # Gatsby files 96 | .cache/ 97 | # Comment in the public line in if your project uses Gatsby and not Next.js 98 | # https://nextjs.org/blog/next-9-1#public-directory-support 99 | # public 100 | 101 | # vuepress build output 102 | .vuepress/dist 103 | 104 | # vuepress v2.x temp and cache directory 105 | .temp 106 | .cache 107 | 108 | # Docusaurus cache and generated files 109 | .docusaurus 110 | 111 | # Serverless directories 112 | .serverless/ 113 | 114 | # FuseBox cache 115 | .fusebox/ 116 | 117 | # DynamoDB Local files 118 | .dynamodb/ 119 | 120 | # TernJS port file 121 | .tern-port 122 | 123 | # Stores VSCode versions used for testing VSCode extensions 124 | .vscode-test 125 | 126 | # yarn v2 127 | .yarn/cache 128 | .yarn/unplugged 129 | .yarn/build-state.yml 130 | .yarn/install-state.gz 131 | .pnp.* 132 | 133 | .idea 134 | /experiments 135 | .npmrc 136 | 137 | # Code interpreter data 138 | /examples/tmp/code_interpreter/* 139 | /examples/tmp/local/* 140 | -------------------------------------------------------------------------------- /eslint.config.js: -------------------------------------------------------------------------------- 1 | // @ts-check 2 | 3 | import eslint from "@eslint/js"; 4 | import tseslint from "typescript-eslint"; 5 | import prettierConfig from "eslint-config-prettier"; 6 | import unusedImports from "eslint-plugin-unused-imports"; 7 | import markdown from "@eslint/markdown"; 8 | 9 | export default tseslint.config( 10 | { 11 | ignores: ["node_modules/**", "dist/**"], 12 | }, 13 | eslint.configs.recommended, 14 | ...tseslint.configs.strict, 15 | ...tseslint.configs.stylistic, 16 | { 17 | files: ["**/*.md/**"], 18 | languageOptions: { 19 | parserOptions: { 20 | project: null, 21 | }, 22 | }, 23 | rules: { 24 | "@typescript-eslint/no-unused-vars": "off", 25 | }, 26 | }, 27 | { 28 | ignores: ["**/*.md/**"], 29 | languageOptions: { 30 | parserOptions: { 31 | project: "./tsconfig.json", 32 | }, 33 | }, 34 | plugins: { 35 | "unused-imports": unusedImports, 36 | }, 37 | rules: { 38 | "no-restricted-imports": [ 39 | "error", 40 | { 41 | patterns: [ 42 | { 43 | group: ["../", "src/"], 44 | message: "Relative imports are not allowed.", 45 | }, 46 | ], 47 | }, 48 | ], 49 | "@typescript-eslint/no-explicit-any": "off", 50 | "@typescript-eslint/no-unused-vars": "off", 51 | "@typescript-eslint/no-empty-function": "off", 52 | "@typescript-eslint/no-extraneous-class": "off", 53 | "@typescript-eslint/no-empty-interface": "off", 54 | "@typescript-eslint/no-non-null-assertion": "off", 55 | "@typescript-eslint/no-floating-promises": "error", 56 | "unused-imports/no-unused-imports": "error", 57 | "unused-imports/no-unused-vars": [ 58 | "warn", 59 | { 60 | vars: "all", 61 | varsIgnorePattern: "^_", 62 | args: "after-used", 63 | argsIgnorePattern: "^_", 64 | }, 65 | ], 66 | "@typescript-eslint/ban-ts-comment": "off", 67 | "@typescript-eslint/no-empty-object-type": "off", 68 | "quote-props": ["error", "consistent"], 69 | }, 70 | }, 71 | { 72 | files: ["examples/**/*.ts"], 73 | languageOptions: { 74 | parserOptions: { 75 | project: "./tsconfig.examples.json", 76 | }, 77 | }, 78 | rules: { 79 | "@typescript-eslint/no-unused-vars": "off", 80 | "unused-imports/no-unused-vars": "off", 81 | "no-restricted-imports": [ 82 | "error", 83 | { 84 | patterns: [ 85 | { 86 | group: ["@/"], 87 | message: "Use 'bee-community-tools' instead.", 88 | }, 89 | ], 90 | }, 91 | ], 92 | }, 93 | }, 94 | // @ts-expect-error wrong types 95 | ...markdown.configs.processor, 96 | prettierConfig, 97 | { 98 | rules: { 99 | curly: ["error", "all"], 100 | }, 101 | }, 102 | ); 103 | -------------------------------------------------------------------------------- /src/tools/openLibrary.test.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2024 IBM Corp. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | import { describe, test } from "vitest"; 18 | import { OpenLibraryTool } from "./openLibrary.js"; 19 | import { setupServer } from "msw/node"; 20 | import { http, HttpResponse } from "msw"; 21 | import { ToolError } from "bee-agent-framework/tools/base"; 22 | 23 | const exampleResponse = { 24 | numFound: 1, 25 | start: 0, 26 | numFoundExact: true, 27 | docs: [ 28 | { 29 | title: "Bee Framework", 30 | first_publish_year: 2024, 31 | publish_date: [2024], 32 | author_name: [], 33 | contributor: [], 34 | isbn: [], 35 | language: [], 36 | place: [], 37 | publish_place: [], 38 | publisher: [], 39 | }, 40 | ], 41 | num_found: 1, 42 | q: "", 43 | offset: null, 44 | }; 45 | 46 | const server = setupServer( 47 | http.get("https://openlibrary.org/search.json", async ({ request }) => { 48 | const url = new URL(request.url); 49 | const title = url.searchParams.get("title"); 50 | if (title === "Bee Framework") { 51 | return HttpResponse.json(exampleResponse); 52 | } else if (title === "Invalid") { 53 | return HttpResponse.text("invalid json"); 54 | } else if (title === "Error") { 55 | return new HttpResponse(null, { status: 404 }); 56 | } 57 | }), 58 | ); 59 | 60 | describe("OpenLibraryTool", () => { 61 | const openLibraryTool = new OpenLibraryTool(); 62 | 63 | beforeAll(() => { 64 | server.listen(); 65 | }); 66 | 67 | afterEach(() => { 68 | server.resetHandlers(); 69 | }); 70 | 71 | afterAll(() => { 72 | server.close(); 73 | }); 74 | 75 | test("Book successfully found by title", async () => { 76 | const openLibraryResponse = await openLibraryTool.run({ 77 | title: "Bee Framework", 78 | }); 79 | expect(openLibraryResponse.result).toEqual(exampleResponse.docs); 80 | }); 81 | 82 | test("Invalid json response", async () => { 83 | try { 84 | await openLibraryTool.run({ 85 | title: "Invalid", 86 | }); 87 | } catch (e) { 88 | expect(e).toBeInstanceOf(ToolError); 89 | expect(e.message).toEqual("Request to Open Library has failed to parse!"); 90 | expect(e.errors.length).toEqual(1); 91 | } 92 | }); 93 | 94 | test("Book not found by title", async () => { 95 | try { 96 | await openLibraryTool.run({ 97 | title: "Error", 98 | }); 99 | } catch (e) { 100 | expect(e).toBeInstanceOf(ToolError); 101 | expect(e.message).toEqual("Request to Open Library API has failed!"); 102 | expect(e.errors.length).toEqual(1); 103 | } 104 | }); 105 | }); 106 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as 6 | contributors and maintainers pledge to making participation in our project and 7 | our community a harassment-free experience for everyone, regardless of age, body 8 | size, disability, ethnicity, sex characteristics, gender identity and expression, 9 | level of experience, education, socio-economic status, nationality, personal 10 | appearance, race, religion, or sexual identity and orientation. 11 | 12 | ## Our Standards 13 | 14 | Examples of behavior that contributes to creating a positive environment 15 | include: 16 | 17 | - Using welcoming and inclusive language 18 | - Being respectful of differing viewpoints and experiences 19 | - Gracefully accepting constructive criticism 20 | - Focusing on what is best for the community 21 | - Showing empathy towards other community members 22 | 23 | Examples of unacceptable behavior by participants include: 24 | 25 | - The use of sexualized language or imagery and unwelcome sexual attention or 26 | advances 27 | - Trolling, insulting/derogatory comments, and personal or political attacks 28 | - Public or private harassment 29 | - Publishing others' private information, such as a physical or electronic 30 | address, without explicit permission 31 | - Other conduct which could reasonably be considered inappropriate in a 32 | professional setting 33 | 34 | ## Our Responsibilities 35 | 36 | Project maintainers are responsible for clarifying the standards of acceptable 37 | behavior and are expected to take appropriate and fair corrective action in 38 | response to any instances of unacceptable behavior. 39 | 40 | Project maintainers have the right and responsibility to remove, edit, or 41 | reject comments, commits, code, wiki edits, issues, and other contributions 42 | that are not aligned to this Code of Conduct, or to ban temporarily or 43 | permanently any contributor for other behaviors that they deem inappropriate, 44 | threatening, offensive, or harmful. 45 | 46 | ## Scope 47 | 48 | This Code of Conduct applies both within project spaces and in public spaces 49 | when an individual is representing the project or its community. Examples of 50 | representing a project or community include using an official project e-mail 51 | address, posting via an official social media account, or acting as an appointed 52 | representative at an online or offline event. Representation of a project may be 53 | further defined and clarified by project maintainers. 54 | 55 | ## Enforcement 56 | 57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 58 | reported by contacting the project team at [IBM Cloud Support](https://www.ibm.com/cloud/support). 59 | All complaints will be reviewed and investigated and will result in a response that 60 | is deemed necessary and appropriate to the circumstances. The project team is 61 | obligated to maintain confidentiality with regard to the reporter of an incident. 62 | Further details of specific enforcement policies may be posted separately. 63 | 64 | Project maintainers who do not follow or enforce the Code of Conduct in good 65 | faith may face temporary or permanent repercussions as determined by other 66 | members of the project's leadership. 67 | 68 | ## Attribution 69 | 70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, 71 | available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html 72 | 73 | [homepage]: https://www.contributor-covenant.org 74 | 75 | For answers to common questions about this code of conduct, see 76 | https://www.contributor-covenant.org/faq 77 | -------------------------------------------------------------------------------- /tsup.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from "tsup"; 2 | import packageJson from "./package.json"; 3 | import swc, { JscConfig } from "@swc/core"; 4 | import path from "node:path"; 5 | 6 | import tsConfig from "./tsconfig.json"; 7 | import { JscTarget } from "@swc/types"; 8 | 9 | export default defineConfig({ 10 | entry: ["src/**/*.{ts,js}", "!src/**/*.test.ts"], 11 | tsconfig: "./tsconfig.json", 12 | sourcemap: true, 13 | dts: true, 14 | format: ["esm", "cjs"], 15 | plugins: [ 16 | { 17 | name: "fix-cjs-imports", 18 | renderChunk(code) { 19 | if (this.format === "cjs") { 20 | const regexCjs = /require\((?['"])(?\.[^'"]+)\.js['"]\)/g; 21 | const regexEsm = /from(?[\s]*)(?['"])(?\.[^'"]+)\.js['"]/g; 22 | return { 23 | code: code 24 | .replace(regexCjs, "require($$.cjs$)") 25 | .replace(regexEsm, "from$$$.cjs$"), 26 | }; 27 | } 28 | }, 29 | }, 30 | { 31 | name: "override-swc", 32 | esbuildOptions: (options) => { 33 | const plugin = options.plugins?.find((p) => p.name === "swc"); 34 | if (plugin) { 35 | // Original Source: https://github.com/egoist/tsup/blob/49c11c3073ce977a01c84e7848fc070d5de0a652/src/esbuild/swc.ts#L14-L67 36 | // Reason: tsup does not provide a way to modify 'jsc' config 37 | plugin.setup = (build) => { 38 | // Force esbuild to keep class names as well 39 | build.initialOptions.keepNames = true; 40 | 41 | build.onLoad({ filter: /\.[jt]sx?$/ }, async (args: any) => { 42 | const isTs = /\.tsx?$/.test(args.path); 43 | 44 | const jsc: JscConfig = { 45 | parser: { 46 | syntax: isTs ? "typescript" : "ecmascript", 47 | decorators: true, 48 | }, 49 | transform: { 50 | legacyDecorator: true, 51 | decoratorMetadata: true, 52 | }, 53 | baseUrl: path.resolve(__dirname, tsConfig.compilerOptions.baseUrl || "."), 54 | paths: tsConfig.compilerOptions.paths, 55 | keepClassNames: true, 56 | preserveAllComments: true, 57 | target: (tsConfig.compilerOptions.target || "es2022").toLowerCase() as JscTarget, 58 | }; 59 | 60 | const result = await swc.transformFile(args.path, { 61 | jsc, 62 | sourceMaps: true, 63 | configFile: false, 64 | swcrc: false, 65 | }); 66 | 67 | let code = result.code; 68 | if (result.map) { 69 | const map: { sources: string[] } = JSON.parse(result.map); 70 | // Make sure sources are relative path 71 | map.sources = map.sources.map((source) => { 72 | return path.isAbsolute(source) 73 | ? path.relative(path.dirname(args.path), source) 74 | : source; 75 | }); 76 | code += `//# sourceMappingURL=data:application/json;base64,${Buffer.from( 77 | JSON.stringify(map), 78 | ).toString("base64")}`; 79 | } 80 | return { 81 | contents: code, 82 | }; 83 | }); 84 | }; 85 | } 86 | }, 87 | }, 88 | ], 89 | treeshake: true, 90 | shims: true, 91 | skipNodeModulesBundle: true, 92 | legacyOutput: false, 93 | bundle: false, 94 | splitting: false, 95 | silent: false, 96 | clean: true, 97 | define: { 98 | __LIBRARY_VERSION: JSON.stringify(packageJson.version), 99 | }, 100 | }); 101 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "bee-community-tools", 3 | "version": "0.1.0", 4 | "license": "Apache-2.0", 5 | "description": "Agentic tools for the Bee Agent Framework", 6 | "author": "IBM Corp.", 7 | "contributors": [ 8 | "Tomas Dvorak ", 9 | "Graham White ", 10 | "James Sutton ", 11 | "Michael Desmond " 12 | ], 13 | "keywords": [ 14 | "Bee Agent Framework Community Tools" 15 | ], 16 | "packageManager": "yarn@4.1.1", 17 | "type": "module", 18 | "main": "./dist/index.js", 19 | "types": "./dist/index.d.ts", 20 | "sideEffects": false, 21 | "exports": { 22 | "./package.json": "./package.json", 23 | ".": { 24 | "import": { 25 | "types": "./dist/index.d.ts", 26 | "default": "./dist/index.js" 27 | }, 28 | "require": { 29 | "types": "./dist/index.d.cts", 30 | "default": "./dist/index.cjs" 31 | } 32 | }, 33 | "./*": { 34 | "import": { 35 | "types": "./dist/*.d.ts", 36 | "default": "./dist/*.js" 37 | }, 38 | "require": { 39 | "types": "./dist/*.d.cts", 40 | "default": "./dist/*.cjs" 41 | } 42 | } 43 | }, 44 | "files": [ 45 | "dist/**/*" 46 | ], 47 | "homepage": "https://github.com/i-am-bee/bee-community-tools#readme", 48 | "repository": { 49 | "type": "git", 50 | "url": "git+https://github.com/i-am-bee/bee-community-tools.git" 51 | }, 52 | "bugs": { 53 | "url": "https://github.com/i-am-bee/bee-community-tools/issues" 54 | }, 55 | "scripts": { 56 | "clean": "rimraf dist", 57 | "build": "yarn clean && yarn ts:check && tsup", 58 | "ts:check": "tsc --noEmit && tsc -p tsconfig.examples.json --noEmit", 59 | "start": "tsx --tsconfig tsconfig.examples.json examples/agents/manyToolsAgent.ts", 60 | "lint": "yarn eslint", 61 | "lint:fix": "yarn eslint --fix", 62 | "format": "yarn prettier --check .", 63 | "format:fix": "yarn prettier --write .", 64 | "test:unit": "vitest run src", 65 | "test:unit:watch": "vitest run src", 66 | "test:e2e": "vitest run tests", 67 | "test:e2e:watch": "vitest watch tests", 68 | "test:all": "vitest run", 69 | "test:watch": "vitest watch", 70 | "prepare": "husky", 71 | "copyright": "./scripts/copyright.sh", 72 | "release": "release-it", 73 | "_ensure_env": "cp -n .env.template .env || true" 74 | }, 75 | "peerDependencies": { 76 | "airtable": "^0.12.2", 77 | "bee-agent-framework": "^0.0.34" 78 | }, 79 | "devDependencies": { 80 | "@commitlint/cli": "^19.5.0", 81 | "@commitlint/config-conventional": "^19.5.0", 82 | "@eslint/js": "^9.13.0", 83 | "@eslint/markdown": "^6.2.1", 84 | "@ibm-generative-ai/node-sdk": "^3.2.3", 85 | "@langchain/community": "^0.3.10", 86 | "@langchain/core": "^0.3.15", 87 | "@langchain/langgraph": "^0.2.18", 88 | "@release-it/conventional-changelog": "^9.0.2", 89 | "@rollup/plugin-commonjs": "^28.0.1", 90 | "@swc/core": "^1.7.40", 91 | "@types/eslint": "^9.6.1", 92 | "@types/eslint-config-prettier": "^6.11.3", 93 | "@types/eslint__js": "^8.42.3", 94 | "@types/node": "^22.8.2", 95 | "airtable": "^0.12.2", 96 | "bee-agent-framework": "^0.0.34", 97 | "dotenv": "^16.4.5", 98 | "eslint": "^9.13.0", 99 | "eslint-config-prettier": "^9.1.0", 100 | "eslint-plugin-unused-imports": "^4.1.4", 101 | "glob": "^11.0.0", 102 | "groq-sdk": "^0.7.0", 103 | "husky": "^9.1.6", 104 | "lint-staged": "^15.2.10", 105 | "msw": "^2.5.2", 106 | "ollama": "^0.5.9", 107 | "openai": "^4.68.4", 108 | "openai-chat-tokens": "^0.2.8", 109 | "picocolors": "^1.1.1", 110 | "pino-pretty": "^11.3.0", 111 | "pino-test": "^1.1.0", 112 | "prettier": "^3.3.3", 113 | "release-it": "^17.10.0", 114 | "rimraf": "^6.0.1", 115 | "strip-ansi": "^7.1.0", 116 | "temp-dir": "^3.0.0", 117 | "tsc-files": "^1.1.4", 118 | "tsup": "^8.3.5", 119 | "tsx": "^4.19.2", 120 | "typescript": "^5.6.3", 121 | "typescript-eslint": "^8.12.1", 122 | "vite-tsconfig-paths": "^5.0.1", 123 | "vitest": "^2.1.4" 124 | }, 125 | "dependencies": { 126 | "zod": "^3.23.8" 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | > [!WARNING] 2 | > 3 | > This repository has been archived and is no longer actively maintained or updated. It is provided here for historical reference and may contain outdated or unsupported code. Users are encouraged to fork or adapt the contents as needed, but please note that no further updates, bug fixes, or security patches will be applied by the original maintainers. 4 | 5 |

6 | 7 | 8 | 9 | Bee Framework logo 10 | 11 |

12 | 13 |

Bee Community Tools

14 | 15 |

16 | 17 | 18 | 19 |

Agentic tools that support the Bee Agent Framework

20 |

21 | 22 | The tools in this repository are additional to those provided within the core bee-agent-framework. They provide access to various functions that enable agents to connect to a variety of different capabilities. More information about developing tools for Bee can be found in the [tools documentation](https://github.com/i-am-bee/bee-agent-framework/blob/main/docs/tools.md). 23 | 24 | ### 🛠️ Tools 25 | 26 | | Name | Description | 27 | | ----------------- | ------------------------------------------------------- | 28 | | Hello World | Trivial example tool | 29 | | Image Description | Use an LLM to get a text description for an image | 30 | | Open Library | Connect to the Open Library for information about books | 31 | | Airtable | Query the tables within an airtable base | 32 | 33 | ➕ [Request](https://github.com/i-am-bee/bee-community-tools/discussions) 34 | 35 | ## Getting started with Bee Community Tools 36 | 37 | ### Installation 38 | 39 | ```shell 40 | yarn install 41 | ``` 42 | 43 | ### Run an example agent with tools 44 | 45 | We provide example agents for tool usage in `examples/agents/` that you can use to test tools. 46 | 47 | ```shell 48 | yarn start 49 | ``` 50 | 51 | The `allToolsAgent` example agent is configured to use a BAM, Watsonx, OpenAI hosted LLM, or a local Ollama LLM. 52 | If you are using a hosted LLM make sure to create .env (from .env.template) and fill in the necessary API_KEY. 53 | 54 | > [!NOTE] 55 | > The Hello World example tool is not enabled by default. 56 | 57 | > [!TIP] 58 | > Tools can be enabled/disabled in `examples/agents/allToolsAgent.ts` 59 | 60 | ## Contribution guidelines 61 | 62 | Bee Community Tools is an open-source project and we ❤️ contributions. 63 | 64 | If you'd like to contribute to an existing tool or create a new one, please take a look at our [contribution guidelines](./CONTRIBUTING.md). 65 | 66 | ### 🐛 Bugs 67 | 68 | We are using [GitHub Issues](https://github.com/i-am-bee/bee-community-tools/issues) to manage our public bugs. We keep a close eye on this, so before filing a new issue, please check to make sure it hasn't already been logged. 69 | 70 | ### 🗒 Code of conduct 71 | 72 | This project and everyone participating in it are governed by the [Code of Conduct](./CODE_OF_CONDUCT.md). By participating, you are expected to uphold this code. Please read the [full text](./CODE_OF_CONDUCT.md) so that you can read which actions may or may not be tolerated. 73 | 74 | ## 🗒 Legal notice 75 | 76 | All content in these repositories including code has been provided by IBM under the associated open source software license and IBM is under no obligation to provide enhancements, updates, or support. IBM developers produced this code as an open source project (not as an IBM product), and IBM makes no assertions as to the level of quality nor security, and will not be maintaining this code going forward. 77 | 78 | ## Contributors 79 | 80 | Special thanks to our contributors for helping us improve Bee Community Tools. 81 | 82 | 83 | 84 | 85 | -------------------------------------------------------------------------------- /src/tools/imageDescription.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2024 IBM Corp. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | import { 18 | BaseToolOptions, 19 | BaseToolRunOptions, 20 | StringToolOutput, 21 | ToolInput, 22 | ToolError, 23 | Tool, 24 | } from "bee-agent-framework/tools/base"; 25 | import { getEnv, parseEnv } from "bee-agent-framework/internals/env"; 26 | import { z } from "zod"; 27 | 28 | type ToolOptions = BaseToolOptions; 29 | type ToolRunOptions = BaseToolRunOptions; 30 | 31 | export interface VllmChatCompletionImageURL { 32 | url: string; 33 | } 34 | 35 | export interface VllmChatCompletionContent { 36 | type: string; 37 | text?: string; 38 | image_url?: VllmChatCompletionImageURL; 39 | } 40 | export interface VllmChatCompletionMessage { 41 | role: string; 42 | content: VllmChatCompletionContent[]; 43 | } 44 | 45 | export interface VllmChatCompletionPrompt { 46 | model: string; 47 | messages: VllmChatCompletionMessage[]; 48 | } 49 | 50 | export class ImageDescriptionTool extends Tool { 51 | name = "ImageDescription"; 52 | description = "Describes the content of an image provided."; 53 | 54 | protected vllmApiEndpoint = parseEnv("IMAGE_DESC_VLLM_API", z.string().url()); 55 | protected vllmApiModelId = parseEnv("IMAGE_DESC_MODEL_ID", z.string()); 56 | protected openApiKey = getEnv("OPENAI_API_KEY"); 57 | 58 | inputSchema() { 59 | return z.object({ 60 | imageUrl: z.string().describe("The URL of an image."), 61 | prompt: z.string().optional().describe("Image specific prompt from the user."), 62 | }); 63 | } 64 | 65 | static { 66 | this.register(); 67 | } 68 | 69 | protected async _run( 70 | { prompt = "Describe this image.", imageUrl }: ToolInput, 71 | _options?: BaseToolRunOptions, 72 | ): Promise { 73 | const imageDescriptionOutput = await this.requestImageDescriptionForURL( 74 | imageUrl, 75 | prompt, 76 | _options?.signal, 77 | ); 78 | 79 | return new StringToolOutput( 80 | `Description: ${imageDescriptionOutput}. Ignore any misleading information that may be in the URL.`, 81 | ); 82 | } 83 | 84 | createSnapshot() { 85 | return { 86 | ...super.createSnapshot(), 87 | vllmApiEndpoint: this.vllmApiEndpoint, 88 | vllmApiModelId: this.vllmApiModelId, 89 | openApiKey: this.openApiKey, // pragma: allowlist secret 90 | }; 91 | } 92 | 93 | loadSnapshot({ 94 | vllmApiEndpoint, 95 | vllmApiModelId, 96 | openApiKey, 97 | ...snapshot 98 | }: ReturnType) { 99 | super.loadSnapshot(snapshot); 100 | Object.assign(this, { 101 | vllmApiEndpoint, 102 | vllmApiModelId, 103 | openApiKey, 104 | }); 105 | } 106 | 107 | protected async queryVllmAPI(completionPrompt: VllmChatCompletionPrompt, signal?: AbortSignal) { 108 | const vllmApiUrl = new URL("/v1/chat/completions", this.vllmApiEndpoint); 109 | const headers = { 110 | "accept": "application/json", 111 | "Content-Type": "application/json", 112 | }; 113 | if (this.openApiKey !== undefined) { 114 | Object.assign(headers, { Authorization: `Bearer ${this.openApiKey}` }); 115 | } 116 | const vllmResponse = await fetch(vllmApiUrl, { 117 | method: "POST", 118 | body: JSON.stringify(completionPrompt), 119 | headers: headers, 120 | signal: signal, 121 | }); 122 | 123 | if (!vllmResponse.ok) { 124 | throw new ToolError(`Request to Vllm API has failed! ${vllmResponse.statusText}`, [ 125 | new Error(await vllmResponse.text()), 126 | ]); 127 | } 128 | try { 129 | const json = await vllmResponse.json(); 130 | if (json.choices.length > 0) { 131 | // We have an answer 132 | const content = json.choices[0].message.content; 133 | return content; 134 | } else { 135 | return "The model could not identify the image."; 136 | } 137 | } catch (e) { 138 | throw new ToolError(`Request to Vllm has failed to parse! ${e}`, [e]); 139 | } 140 | } 141 | 142 | /** 143 | * Requests the description of an image from the vLLM Backend 144 | * 145 | * @param imageUrl - The Image Url 146 | * @param prompt - The prompt to provide to the model alongside the image 147 | * 148 | * @returns A String description of the image. 149 | */ 150 | protected async requestImageDescriptionForURL( 151 | imageUrl: string, 152 | prompt: string, 153 | signal?: AbortSignal, 154 | ): Promise { 155 | const modelPrompt: VllmChatCompletionPrompt = { 156 | model: this.vllmApiModelId, 157 | messages: [ 158 | { 159 | role: "user", 160 | content: [ 161 | { type: "text", text: prompt }, 162 | { type: "image_url", image_url: { url: imageUrl } }, 163 | ], 164 | }, 165 | ], 166 | }; 167 | 168 | const modelResponse = await this.queryVllmAPI(modelPrompt, signal); 169 | return modelResponse; 170 | } 171 | } 172 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | Bee Agent Framework is an open-source project committed to bringing LLM agents to people of all backgrounds. This page describes how you can join the Bee community in this goal. 4 | 5 | ## Before you start 6 | 7 | If you are new to Bee contributing, we recommend you do the following before diving into the code: 8 | 9 | - Read [Bee Overview](https://github.com/i-am-bee/bee-agent-framework/blob/main/docs/overview.md) to understand core concepts. 10 | - Read [Code of Conduct](./CODE_OF_CONDUCT.md). 11 | 12 | ## Choose an issue to work on 13 | 14 | Bee uses the following labels to help non-maintainers find issues best suited to their interests and experience level: 15 | 16 | - [good first issue](https://github.com/i-am-bee/bee-community-tools/issues?q=is%3Aopen+is%3Aissue+label%3A%22good+first+issue%22) - these issues are typically the simplest available to work on, ideal for newcomers. They should already be fully scoped, with a straightforward approach outlined in the descriptions. 17 | - [help wanted](https://github.com/i-am-bee/bee-community-tools/issues?q=is%3Aopen+is%3Aissue+label%3A%22help+wanted%22) - these issues are generally more complex than good first issues. They typically cover work that core maintainers don't currently have the capacity to implement and may require more investigation/discussion. These are great options for experienced contributors looking for something more challenging. 18 | 19 | ## Set up a development environment 20 | 21 | To start contributing tooling to the Bee Agent framework, follow these steps to set up your development environment: 22 | 23 | 1. **Install a Node Version Manager (NVM or n):** We use `.nvmrc` to specify the required Node.js version. Install [nvm](https://github.com/nvm-sh/nvm) or or [n](https://github.com/tj/n) by following their official installation instructions. 24 | 25 | 1. **Install the Correct Node.js Version:** Use `nvm` to install and use the Node.js version specified in the `.nvmrc` file: 26 | 27 | ```bash 28 | nvm install && nvm use || n auto 29 | ``` 30 | 31 | 1. **Install IBM Detect Secrets:** We use [IBM Detect Secrets](https://github.com/IBM/detect-secrets) to help ensure that secrets are not leaked into the code base. This tool is run as part a pre-commit hook so will be required before you can make commits. 32 | 33 | 1. **Install [Yarn](https://yarnpkg.com/) via Corepack:** This project uses Yarn as the package manager. Ensure you have Corepack enabled and install Yarn: 34 | 35 | ```bash 36 | corepack enable 37 | ``` 38 | 39 | 1. **Install Dependencies:** Install all project dependencies by running: 40 | 41 | ```bash 42 | yarn install 43 | ``` 44 | 45 | 1. **Setup environmental variables:** See the [.env.template](.env.template) file for an example and fill out the appropriate values 46 | 47 | ```bash 48 | cp .env.template .env 49 | ``` 50 | 51 | 1. **Follow Conventional Commit Messages:** We use [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/#summary) to structure our commit messages. This helps maintain a clean and manageable commit history. Please use the following format: 52 | 53 | ``` 54 | (): 55 | Type: feat, fix, chore, docs, style, refactor, perf, test, etc. 56 | Scope: The area of the codebase your changes affect (optional). 57 | Subject: A short description of the changes (required). 58 | ``` 59 | 60 | _Example:_ 61 | 62 | ``` 63 | feat(watsonx): add llm streaming support 64 | 65 | Ref: #15 66 | ``` 67 | 68 | 1. **Run Linters/Formatters:** Ensure your changes meet code quality standards. Run the following commands: 69 | 70 | ```shell 71 | yarn lint # or yarn lint:fix 72 | yarn format # or yarn lint:format 73 | ``` 74 | 75 | 1. **Run Tests:** Ensure your changes pass all tests (unit, integration, E2E). Run the following commands: 76 | 77 | ```shell 78 | yarn test:unit 79 | yarn test:e2e 80 | ``` 81 | 82 | By following the above steps, you'll be all set to contribute to our project! If you encounter any issues during the setup process, please feel free to open an issue. 83 | 84 | ## Style and lint 85 | 86 | Bee uses the following tools to meet code quality standards and ensure a unified code style across the codebase. 87 | 88 | - [ESLint](https://eslint.org/) - Linting Utility 89 | - [Prettier](https://prettier.io/) - Code Formatter 90 | - [commitlint](https://commitlint.js.org/) - Lint commit messages according to [Conventional Commits](https://www.conventionalcommits.org/). 91 | 92 | ## Issues and pull requests 93 | 94 | We use GitHub pull requests to accept contributions. 95 | 96 | While not required, opening a new issue about the bug you're fixing or the feature you're working on before you open a pull request is important in starting a discussion with the community about your work. The issue gives us a place to talk about the idea and how we can work together to implement it in the code. It also lets the community know what you're working on, and if you need help, you can reference the issue when discussing it with other community and team members. 97 | 98 | If you've written some code but need help finishing it, want to get initial feedback on it before finishing it, or want to share it and discuss it prior to completing the implementation, you can open a Draft pull request and prepend the title with the [WIP] tag (for Work In Progress). This will indicate to reviewers that the code in the PR isn't in its final state and will change. It also means we will only merge the commit once it is finished. You or a reviewer can remove the [WIP] tag when the code is ready to be thoroughly reviewed for merging. 99 | 100 | ## Contributor Licensing Agreement 101 | 102 | Before you can submit any code, all contributors must sign a contributor license agreement (CLA). By signing a CLA, you're attesting that you are the author of the contribution, and that you're freely contributing it under the terms of the Apache-2.0 license. 103 | -------------------------------------------------------------------------------- /src/tools/openLibrary.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2024 IBM Corp. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | import { 18 | BaseToolOptions, 19 | BaseToolRunOptions, 20 | Tool, 21 | ToolInput, 22 | JSONToolOutput, 23 | ToolError, 24 | } from "bee-agent-framework/tools/base"; 25 | import { z } from "zod"; 26 | import { createURLParams } from "bee-agent-framework/internals/fetcher"; 27 | 28 | type ToolOptions = BaseToolOptions; 29 | type ToolRunOptions = BaseToolRunOptions; 30 | 31 | export interface OpenLibraryAPIResponse { 32 | numFound: number; 33 | start: number; 34 | numFoundExact: boolean; 35 | num_found: number; 36 | q: string; 37 | offset: number; 38 | docs: { 39 | _version_: number; 40 | key: string; 41 | title: string; 42 | subtitle: string; 43 | alternative_title: string; 44 | alternative_subtitle: string; 45 | cover_i: number; 46 | ebook_access: string; 47 | ebook_count_i: number; 48 | edition_count: number; 49 | edition_key: string[]; 50 | format: string[]; 51 | publish_date: string[]; 52 | lccn: string[]; 53 | ia: string[]; 54 | oclc: string[]; 55 | public_scan_b: boolean; 56 | isbn: string[]; 57 | contributor: string[]; 58 | publish_place: string[]; 59 | publisher: string[]; 60 | seed: string[]; 61 | first_sentence: string[]; 62 | author_key: string[]; 63 | author_name: string[]; 64 | author_alternative_name: string[]; 65 | subject: string[]; 66 | person: string[]; 67 | place: string[]; 68 | time: string[]; 69 | has_fulltext: boolean; 70 | title_suggest: string; 71 | title_sort: string; 72 | type: string; 73 | publish_year: number[]; 74 | language: string[]; 75 | last_modified_i: number; 76 | number_of_pages_median: number; 77 | place_facet: string[]; 78 | publisher_facet: string[]; 79 | author_facet: string[]; 80 | first_publish_year: number; 81 | ratings_count_1: number; 82 | ratings_count_2: number; 83 | ratings_count_3: number; 84 | ratings_count_4: number; 85 | ratings_count_5: number; 86 | ratings_average: number; 87 | ratings_sortable: number; 88 | ratings_count: number; 89 | readinglog_count: number; 90 | want_to_read_count: number; 91 | currently_reading_count: number; 92 | already_read_count: number; 93 | subject_key: string[]; 94 | person_key: string[]; 95 | place_key: string[]; 96 | subject_facet: string[]; 97 | time_key: string[]; 98 | lcc: string[]; 99 | ddc: string[]; 100 | lcc_sort: string; 101 | ddc_sort: string; 102 | }[]; 103 | } 104 | 105 | interface OpenLibraryResponse { 106 | title: string; 107 | author_name: string[]; 108 | contributor: string[]; 109 | first_publish_year: number; 110 | publish_date: string[]; 111 | language: string[]; 112 | publish_place: string[]; 113 | place: string[]; 114 | publisher: string[]; 115 | isbn: string[]; 116 | } 117 | 118 | export interface OpenLibraryResponseList extends Array {} 119 | 120 | export class OpenLibraryToolOutput extends JSONToolOutput {} 121 | 122 | export class OpenLibraryTool extends Tool { 123 | name = "OpenLibrary"; 124 | description = 125 | "Provides access to a library of books with information about book titles, authors, contributors, publication dates, publisher and isbn."; 126 | 127 | inputSchema() { 128 | return z 129 | .object({ 130 | title: z.string().describe("The title or name of the book."), 131 | author: z.string().describe("The author's name."), 132 | isbn: z.string().describe("The book's International Standard Book Number (ISBN)."), 133 | subject: z.string().describe("The subject or topic about which the book is written."), 134 | place: z.string().describe("The place about which the book is written."), 135 | person: z.string().describe("The person about which the book is written."), 136 | publisher: z.string().describe("The company or name of the book's publisher."), 137 | }) 138 | .partial(); 139 | } 140 | 141 | static { 142 | this.register(); 143 | } 144 | 145 | protected async _run(input: ToolInput, options?: ToolRunOptions) { 146 | const params = createURLParams(input); 147 | const url = `https://openlibrary.org/search.json?${decodeURIComponent(params.toString())}`; 148 | const response = await fetch(url, { 149 | signal: options?.signal, 150 | }); 151 | if (!response.ok) { 152 | throw new ToolError("Request to Open Library API has failed!", [ 153 | new Error(await response.text()), 154 | ]); 155 | } 156 | try { 157 | const responseJson: OpenLibraryAPIResponse = await response.json(); 158 | const json: OpenLibraryResponseList = responseJson.docs.map((doc) => { 159 | return { 160 | title: doc.title || "Unknown", 161 | author_name: doc.author_name || [], 162 | contributor: doc.contributor || [], 163 | first_publish_year: doc.first_publish_year || 0, 164 | publish_date: doc.publish_date || [], 165 | language: doc.language || [], 166 | publish_place: doc.publish_place || [], 167 | place: doc.place || [], 168 | publisher: doc.publisher || [], 169 | isbn: doc.isbn || [], 170 | }; 171 | }); 172 | return new OpenLibraryToolOutput(json); 173 | } catch (e) { 174 | throw new ToolError("Request to Open Library has failed to parse!", [e]); 175 | } 176 | } 177 | } 178 | -------------------------------------------------------------------------------- /examples/agents/airTableToolAgent.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2024 IBM Corp. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | import "dotenv/config.js"; 18 | import { BAMChatLLM } from "bee-agent-framework/adapters/bam/chat"; 19 | import { BeeAgent } from "bee-agent-framework/agents/bee/agent"; 20 | import { createConsoleReader } from "../helpers/io.js"; 21 | import { FrameworkError } from "bee-agent-framework/errors"; 22 | import { TokenMemory } from "bee-agent-framework/memory/tokenMemory"; 23 | import { Logger } from "bee-agent-framework/logger/logger"; 24 | import { OllamaChatLLM } from "bee-agent-framework/adapters/ollama/chat"; 25 | import { OpenAIChatLLM } from "bee-agent-framework/adapters/openai/chat"; 26 | import { PromptTemplate } from "bee-agent-framework/template"; 27 | import { WatsonXChatLLM } from "bee-agent-framework/adapters/watsonx/chat"; 28 | import { parseEnv } from "bee-agent-framework/internals/env"; 29 | import { z } from "zod"; 30 | import { 31 | BeeSystemPrompt, 32 | BeeToolErrorPrompt, 33 | BeeToolInputErrorPrompt, 34 | BeeToolNoResultsPrompt, 35 | } from "bee-agent-framework/agents/bee/prompts"; 36 | 37 | // core tools 38 | import { DuckDuckGoSearchTool } from "bee-agent-framework/tools/search/duckDuckGoSearch"; 39 | import { WikipediaTool } from "bee-agent-framework/tools/search/wikipedia"; 40 | 41 | // AirTable tool 42 | import { AirtableTool } from "bee-community-tools/tools/airtable"; 43 | 44 | const AIRTABLE_TOKEN: string = parseEnv("AIRTABLE_TOKEN", z.string()); 45 | const AIRTABLE_BASE: string = parseEnv("AIRTABLE_BASE", z.string()); 46 | 47 | Logger.root.level = "silent"; // disable internal logs 48 | const logger = new Logger({ name: "app", level: "trace" }); 49 | 50 | async function runBeeAgent() { 51 | // use BAM if GENAI_API_KEY env var is defined 52 | // else use Watsonx if WATSONX_API_KEY and WATSONX_PROJECT_ID env vars are defined 53 | // else use OpenAI if OPENAI_API_KEY env var is defined 54 | // else use Ollama 55 | const llm = 56 | process.env.GENAI_API_KEY !== undefined 57 | ? BAMChatLLM.fromPreset("meta-llama/llama-3-1-70b-instruct") 58 | : process.env.WATSONX_API_KEY !== undefined && process.env.WATSONX_PROJECT_ID !== undefined 59 | ? WatsonXChatLLM.fromPreset("meta-llama/llama-3-1-70b-instruct", { 60 | apiKey: process.env.WATSONX_API_KEY, //pragma: allowlist secret 61 | projectId: process.env.WATSONX_PROJECT_ID, 62 | parameters: { 63 | decoding_method: "greedy", 64 | min_new_tokens: 5, 65 | max_new_tokens: 50, 66 | }, 67 | }) 68 | : process.env.OPENAI_API_KEY !== undefined 69 | ? new OpenAIChatLLM() 70 | : new OllamaChatLLM({ modelId: "llama3.1" }); 71 | 72 | const agent = new BeeAgent({ 73 | llm, 74 | memory: new TokenMemory({ llm }), 75 | tools: [ 76 | new DuckDuckGoSearchTool(), 77 | new WikipediaTool(), 78 | new AirtableTool({ apiToken: AIRTABLE_TOKEN, baseId: AIRTABLE_BASE }), 79 | ], 80 | templates: { 81 | user: new PromptTemplate({ 82 | schema: z 83 | .object({ 84 | input: z.string(), 85 | }) 86 | .passthrough(), 87 | template: `User: {{input}}`, 88 | }), 89 | system: BeeSystemPrompt.fork((old) => ({ 90 | ...old, 91 | defaults: { 92 | instructions: "You are a helpful assistant that uses tools to answer questions.", 93 | }, 94 | })), 95 | toolError: BeeToolErrorPrompt, 96 | toolInputError: BeeToolInputErrorPrompt, 97 | toolNoResultError: BeeToolNoResultsPrompt.fork((old) => ({ 98 | ...old, 99 | template: `${old.template}\nPlease reformat your input.`, 100 | })), 101 | toolNotFoundError: new PromptTemplate({ 102 | schema: z 103 | .object({ 104 | tools: z.array(z.object({ name: z.string() }).passthrough()), 105 | }) 106 | .passthrough(), 107 | template: `Tool does not exist! 108 | {{#tools.length}} 109 | Use one of the following tools: {{#trim}}{{#tools}}{{name}},{{/tools}}{{/trim}} 110 | {{/tools.length}}`, 111 | }), 112 | }, 113 | }); 114 | 115 | const reader = createConsoleReader(); 116 | 117 | try { 118 | for await (const { prompt } of reader) { 119 | const response = await agent 120 | .run( 121 | { prompt }, 122 | { 123 | execution: { 124 | maxRetriesPerStep: 3, 125 | totalMaxRetries: 10, 126 | maxIterations: 20, 127 | }, 128 | signal: AbortSignal.timeout(2 * 60 * 1000), 129 | }, 130 | ) 131 | .observe((emitter) => { 132 | emitter.on("start", () => { 133 | reader.write(`Agent 🤖 : `, "Starting new iteration"); 134 | }); 135 | emitter.on("error", ({ error }) => { 136 | reader.write(`Agent 🤖 : `, FrameworkError.ensure(error).dump()); 137 | }); 138 | emitter.on("retry", () => { 139 | reader.write(`Agent 🤖 : `, "retrying the action..."); 140 | }); 141 | emitter.on("update", async ({ update }) => { 142 | // log 'data' to see the whole state 143 | // to log only valid runs (no errors), check if meta.success === true 144 | reader.write(`Agent (${update.key}) 🤖 : `, update.value); 145 | }); 146 | }); 147 | 148 | reader.write(`Agent 🤖 : `, response.result.text); 149 | } 150 | } catch (error) { 151 | logger.error(FrameworkError.ensure(error).dump()); 152 | } finally { 153 | process.exit(0); 154 | } 155 | } 156 | 157 | void runBeeAgent(); 158 | -------------------------------------------------------------------------------- /examples/agents/manyToolsAgent.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2024 IBM Corp. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | import "dotenv/config.js"; 18 | import { BAMChatLLM } from "bee-agent-framework/adapters/bam/chat"; 19 | import { BeeAgent } from "bee-agent-framework/agents/bee/agent"; 20 | import { createConsoleReader } from "../helpers/io.js"; 21 | import { FrameworkError } from "bee-agent-framework/errors"; 22 | import { TokenMemory } from "bee-agent-framework/memory/tokenMemory"; 23 | import { Logger } from "bee-agent-framework/logger/logger"; 24 | import { OllamaChatLLM } from "bee-agent-framework/adapters/ollama/chat"; 25 | import { OpenAIChatLLM } from "bee-agent-framework/adapters/openai/chat"; 26 | import { PromptTemplate } from "bee-agent-framework/template"; 27 | import { WatsonXChatLLM } from "bee-agent-framework/adapters/watsonx/chat"; 28 | import { z } from "zod"; 29 | import { 30 | BeeSystemPrompt, 31 | BeeToolErrorPrompt, 32 | BeeToolInputErrorPrompt, 33 | BeeToolNoResultsPrompt, 34 | } from "bee-agent-framework/agents/bee/prompts"; 35 | 36 | // core tools 37 | import { DuckDuckGoSearchTool } from "bee-agent-framework/tools/search/duckDuckGoSearch"; 38 | import { WikipediaTool } from "bee-agent-framework/tools/search/wikipedia"; 39 | // import { OpenMeteoTool } from "bee-agent-framework/tools/weather/openMeteo"; 40 | // import { ArXivTool } from "bee-agent-framework/tools/arxiv"; 41 | 42 | // contrib tools 43 | // import { HelloWorldTool } from "@/tools/helloWorld.js"; 44 | import { OpenLibraryTool } from "bee-community-tools/tools/openLibrary"; 45 | import { ImageDescriptionTool } from "bee-community-tools/tools/imageDescription"; 46 | 47 | Logger.root.level = "silent"; // disable internal logs 48 | const logger = new Logger({ name: "app", level: "trace" }); 49 | 50 | async function runBeeAgent() { 51 | // use BAM if GENAI_API_KEY env var is defined 52 | // else use Watsonx if WATSONX_API_KEY and WATSONX_PROJECT_ID env vars are defined 53 | // else use OpenAI if OPENAI_API_KEY env var is defined 54 | // else use Ollama 55 | const llm = 56 | process.env.GENAI_API_KEY !== undefined 57 | ? BAMChatLLM.fromPreset("meta-llama/llama-3-1-70b-instruct") 58 | : process.env.WATSONX_API_KEY !== undefined && process.env.WATSONX_PROJECT_ID !== undefined 59 | ? WatsonXChatLLM.fromPreset("meta-llama/llama-3-1-70b-instruct", { 60 | apiKey: process.env.WATSONX_API_KEY, //pragma: allowlist secret 61 | projectId: process.env.WATSONX_PROJECT_ID, 62 | parameters: { 63 | decoding_method: "greedy", 64 | min_new_tokens: 5, 65 | max_new_tokens: 50, 66 | }, 67 | }) 68 | : process.env.OPENAI_API_KEY !== undefined 69 | ? new OpenAIChatLLM() 70 | : new OllamaChatLLM({ modelId: "llama3.1" }); 71 | 72 | const agent = new BeeAgent({ 73 | llm, 74 | memory: new TokenMemory({ llm }), 75 | tools: [ 76 | new DuckDuckGoSearchTool(), 77 | new WikipediaTool(), 78 | // new OpenMeteoTool(), 79 | // new ArXivTool(), 80 | // new HelloWorldTool(), 81 | new OpenLibraryTool(), 82 | new ImageDescriptionTool(), 83 | ], 84 | templates: { 85 | user: new PromptTemplate({ 86 | schema: z 87 | .object({ 88 | input: z.string(), 89 | }) 90 | .passthrough(), 91 | template: `User: {{input}}`, 92 | }), 93 | system: BeeSystemPrompt.fork((old) => ({ 94 | ...old, 95 | defaults: { 96 | instructions: "You are a helpful assistant that uses tools to answer questions.", 97 | }, 98 | })), 99 | toolError: BeeToolErrorPrompt, 100 | toolInputError: BeeToolInputErrorPrompt, 101 | toolNoResultError: BeeToolNoResultsPrompt.fork((old) => ({ 102 | ...old, 103 | template: `${old.template}\nPlease reformat your input.`, 104 | })), 105 | toolNotFoundError: new PromptTemplate({ 106 | schema: z 107 | .object({ 108 | tools: z.array(z.object({ name: z.string() }).passthrough()), 109 | }) 110 | .passthrough(), 111 | template: `Tool does not exist! 112 | {{#tools.length}} 113 | Use one of the following tools: {{#trim}}{{#tools}}{{name}},{{/tools}}{{/trim}} 114 | {{/tools.length}}`, 115 | }), 116 | }, 117 | }); 118 | 119 | const reader = createConsoleReader(); 120 | 121 | try { 122 | for await (const { prompt } of reader) { 123 | const response = await agent 124 | .run( 125 | { prompt }, 126 | { 127 | execution: { 128 | maxRetriesPerStep: 3, 129 | totalMaxRetries: 10, 130 | maxIterations: 20, 131 | }, 132 | signal: AbortSignal.timeout(2 * 60 * 1000), 133 | }, 134 | ) 135 | .observe((emitter) => { 136 | emitter.on("start", () => { 137 | reader.write(`Agent 🤖 : `, "Starting new iteration"); 138 | }); 139 | emitter.on("error", ({ error }) => { 140 | reader.write(`Agent 🤖 : `, FrameworkError.ensure(error).dump()); 141 | }); 142 | emitter.on("retry", () => { 143 | reader.write(`Agent 🤖 : `, "retrying the action..."); 144 | }); 145 | emitter.on("update", async ({ update }) => { 146 | // log 'data' to see the whole state 147 | // to log only valid runs (no errors), check if meta.success === true 148 | reader.write(`Agent (${update.key}) 🤖 : `, update.value); 149 | }); 150 | }); 151 | 152 | reader.write(`Agent 🤖 : `, response.result.text); 153 | } 154 | } catch (error) { 155 | logger.error(FrameworkError.ensure(error).dump()); 156 | } finally { 157 | process.exit(0); 158 | } 159 | } 160 | 161 | void runBeeAgent(); 162 | -------------------------------------------------------------------------------- /src/tools/airtable.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2024 IBM Corp. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | import { 18 | BaseToolOptions, 19 | BaseToolRunOptions, 20 | JSONToolOutput, 21 | ToolInput, 22 | Tool, 23 | ToolError, 24 | } from "bee-agent-framework/tools/base"; 25 | import { z } from "zod"; 26 | import Airtable, { FieldSet, Records, SelectOptions } from "airtable"; 27 | 28 | type ToolRunOptions = BaseToolRunOptions; 29 | 30 | export interface SummarisedAirtableFieldSchema { 31 | name: string; 32 | type?: string; 33 | } 34 | 35 | export interface SummarisedAirtableTableSchema { 36 | name: string; 37 | id: string; 38 | description?: string; 39 | fields: SummarisedAirtableFieldSchema[]; 40 | } 41 | 42 | export interface AirtableToolOptions extends BaseToolOptions { 43 | apiToken: string; 44 | baseId: string; 45 | } 46 | 47 | /** 48 | * Airtable tool for the bee-agent-framework. 49 | * 50 | * This tool allows the agent to retrieve information from the tables within an airtbale base. 51 | * The agent core model will need to be able to construct Airtable Filter formulas in order to 52 | * filter the data in the requested table. 53 | * See: (https://support.airtable.com/docs/airtable-web-api-using-filterbyformula-or-sort-parameters) 54 | * 55 | * The data is retuned in JSON form to the agent as it would be from the API. 56 | */ 57 | export class AirtableTool extends Tool, AirtableToolOptions, ToolRunOptions> { 58 | public readonly airtable: Airtable; 59 | public readonly base: Airtable.Base; 60 | airtableApi = "https://api.airtable.com"; 61 | 62 | name = "Airtable"; 63 | description = 64 | "Can Query records from airtable tables in an airtable base. Use the action GET_SCHEMA to learn about the structure of the airtable base and QUERY_TABLE to request data from a table within the base."; 65 | 66 | inputSchema() { 67 | return z.object({ 68 | action: z 69 | .enum(["GET_SCHEMA", "QUERY_TABLE"]) 70 | .describe( 71 | "The action for the tool to take. GET_SCHEMA requests the airtable base table schema. QUERY_TABLE requests data from a selected table.", 72 | ), 73 | table: z 74 | .string() 75 | .optional() 76 | .describe("The table ID to query when using the QUERY_TABLE action."), 77 | fields: z 78 | .array(z.string()) 79 | .optional() 80 | .describe("The fields to return from the table query when using the QUERY_TABLE action."), 81 | filterFormula: z 82 | .string() 83 | .optional() 84 | .describe( 85 | "The airtable filter formula to refine the query when using the QUERY_TABLE action.", 86 | ), 87 | }); 88 | } 89 | 90 | static { 91 | this.register(); 92 | } 93 | 94 | public constructor(public readonly options: AirtableToolOptions) { 95 | super(options); 96 | this.airtable = new Airtable({ endpointUrl: this.airtableApi, apiKey: options.apiToken }); // pragma: allowlist secret 97 | this.base = this.airtable.base(options.baseId); 98 | } 99 | 100 | protected async _run(input: ToolInput, _options?: BaseToolRunOptions) { 101 | if (input.action === "GET_SCHEMA") { 102 | const response = await this.getBaseTableSchema(_options?.signal); 103 | return new JSONToolOutput(response); 104 | } else if (input.action === "QUERY_TABLE" && input.table != undefined) { 105 | const response = await this.getTableContents(input.table, input.fields, input.filterFormula); 106 | return new JSONToolOutput(response); 107 | } else { 108 | throw new ToolError("Invalid Action."); 109 | } 110 | } 111 | 112 | /** 113 | * Requests the Airtable Base Schema. 114 | * 115 | * The airtable module does not have this functionality, the tool requests directly 116 | * from the HTTP API. 117 | * @returns - SummarisedAirtableTableSchema 118 | */ 119 | private async getBaseTableSchema(signal?: AbortSignal): Promise { 120 | const atResponse = await fetch( 121 | `${this.airtableApi}/v0/meta/bases/${this.options.baseId}/tables`, 122 | { 123 | headers: { 124 | Authorization: `Bearer ${this.options.apiToken}`, 125 | }, 126 | signal: signal, 127 | }, 128 | ); 129 | 130 | if (atResponse.ok) { 131 | const schemadata = await atResponse.json(); 132 | const tableSchema: SummarisedAirtableTableSchema[] = schemadata.tables.map( 133 | (table: { name: any; id: any; description: any; fields: any[] }) => ({ 134 | name: table.name, 135 | id: table.id, 136 | description: table.description, 137 | fields: table.fields.map((field: SummarisedAirtableFieldSchema) => ({ 138 | name: field.name, 139 | type: field.type, 140 | })), 141 | }), 142 | ); 143 | 144 | return tableSchema; 145 | } else { 146 | throw new ToolError(`Error occured getting airtable base schema: ${atResponse.text()}`); 147 | } 148 | } 149 | 150 | /** 151 | * Function to request rows from an airtable table using a filter generated by the agent. 152 | * 153 | * Currently this returns ALL rows that the filterFormula matches because it is assumed 154 | * that the agent will need all data available and there is no easy way to allow the agent 155 | * to iterate through pages. 156 | * 157 | * @param tableId - The ID or name of the table 158 | * @param fields - An array of the fields to return 159 | * @param filterFormula - The filterformula to use 160 | * @returns 161 | */ 162 | private getTableContents( 163 | tableId: string, 164 | fields?: string[], 165 | filterFormula?: string, 166 | ): Promise> { 167 | const selectOpts: SelectOptions
= {}; 168 | if (fields != undefined) { 169 | selectOpts.fields = fields; 170 | } 171 | if (filterFormula != undefined) { 172 | selectOpts.filterByFormula = filterFormula; 173 | } 174 | 175 | return this.base(tableId) 176 | .select(selectOpts) 177 | .all() 178 | .then((records: Records
) => records.map((rec) => rec._rawJson)); 179 | } 180 | 181 | createSnapshot() { 182 | return { 183 | ...super.createSnapshot(), 184 | airtableApi: this.airtableApi, 185 | base: this.base, 186 | }; 187 | } 188 | 189 | loadSnapshot({ airtableApi, base, ...snapshot }: ReturnType) { 190 | super.loadSnapshot(snapshot); 191 | Object.assign(this, { 192 | airtableApi, 193 | base, 194 | }); 195 | } 196 | } 197 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | --------------------------------------------------------------------------------