diff --git a/entrypoint.sh b/entrypoint.sh index d3c8581f5..da37ae04b 100755 --- a/entrypoint.sh +++ b/entrypoint.sh @@ -1,3 +1,14 @@ +#!/bin/bash + +# Downloads the tools +python superagi/tool_manager.py + +# Set executable permissions for install_tool_dependencies.sh +chmod +x install_tool_dependencies.sh + +# Install dependencies +./install_tool_dependencies.sh + # Run Alembic migrations alembic upgrade head diff --git a/gui/package-lock.json b/gui/package-lock.json deleted file mode 100644 index 2585212da..000000000 --- a/gui/package-lock.json +++ /dev/null @@ -1,3455 +0,0 @@ -{ - "name": "super-agi", - "version": "0.1.0", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "name": "super-agi", - "version": "0.1.0", - "dependencies": { - "axios": "^1.4.0", - "bootstrap": "^5.2.3", - "date-fns": "^2.30.0", - "eslint": "8.40.0", - "eslint-config-next": "13.4.2", - "mitt": "^3.0.0", - "next": "13.4.2", - "react": "18.2.0", - "react-dom": "18.2.0", - "react-toastify": "^9.1.3" - } - }, - "node_modules/@babel/runtime": { - "version": "7.21.5", - "license": "MIT", - "dependencies": { - "regenerator-runtime": "^0.13.11" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@eslint-community/eslint-utils": { - "version": "4.4.0", - "license": "MIT", - "dependencies": { - "eslint-visitor-keys": "^3.3.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "peerDependencies": { - "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" - } - }, - "node_modules/@eslint-community/regexpp": { - "version": "4.5.1", - "license": "MIT", - "engines": { - "node": "^12.0.0 || ^14.0.0 || >=16.0.0" - } - }, - "node_modules/@eslint/eslintrc": { - "version": "2.0.3", - "license": "MIT", - "dependencies": { - "ajv": "^6.12.4", - "debug": "^4.3.2", - "espree": "^9.5.2", - "globals": "^13.19.0", - "ignore": "^5.2.0", - "import-fresh": "^3.2.1", - "js-yaml": "^4.1.0", - "minimatch": "^3.1.2", - "strip-json-comments": "^3.1.1" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/@eslint/js": { - "version": "8.40.0", - "license": "MIT", - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - } - }, - "node_modules/@humanwhocodes/config-array": { - "version": "0.11.8", - "license": "Apache-2.0", - "dependencies": { - "@humanwhocodes/object-schema": "^1.2.1", - "debug": "^4.1.1", - "minimatch": "^3.0.5" - }, - "engines": { - "node": ">=10.10.0" - } - }, - "node_modules/@humanwhocodes/module-importer": { - "version": "1.0.1", - "license": "Apache-2.0", - "engines": { - "node": ">=12.22" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/nzakas" - } - }, - "node_modules/@humanwhocodes/object-schema": { - "version": "1.2.1", - "license": "BSD-3-Clause" - }, - "node_modules/@next/env": { - "version": "13.4.2", - "license": "MIT" - }, - "node_modules/@next/eslint-plugin-next": { - "version": "13.4.2", - "license": "MIT", - "dependencies": { - "glob": "7.1.7" - } - }, - "node_modules/@next/swc-darwin-arm64": { - "version": "13.4.2", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@next/swc-darwin-x64": { - "version": "13.4.2", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-13.4.2.tgz", - "integrity": "sha512-iZuYr7ZvGLPjPmfhhMl0ISm+z8EiyLBC1bLyFwGBxkWmPXqdJ60mzuTaDSr5WezDwv0fz32HB7JHmRC6JVHSZg==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@next/swc-linux-arm64-gnu": { - "version": "13.4.2", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-13.4.2.tgz", - "integrity": "sha512-2xVabFtIge6BJTcJrW8YuUnYTuQjh4jEuRuS2mscyNVOj6zUZkom3CQg+egKOoS+zh2rrro66ffSKIS+ztFJTg==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@next/swc-linux-arm64-musl": { - "version": "13.4.2", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-13.4.2.tgz", - "integrity": "sha512-wKRCQ27xCUJx5d6IivfjYGq8oVngqIhlhSAJntgXLt7Uo9sRT/3EppMHqUZRfyuNBTbykEre1s5166z+pvRB5A==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@next/swc-linux-x64-gnu": { - "version": "13.4.2", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-13.4.2.tgz", - "integrity": "sha512-NpCa+UVhhuNeaFVUP1Bftm0uqtvLWq2JTm7+Ta48+2Uqj2mNXrDIvyn1DY/ZEfmW/1yvGBRaUAv9zkMkMRixQA==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@next/swc-linux-x64-musl": { - "version": "13.4.2", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-13.4.2.tgz", - "integrity": "sha512-ZWVC72x0lW4aj44e3khvBrj2oSYj1bD0jESmyah3zG/3DplEy/FOtYkMzbMjHTdDSheso7zH8GIlW6CDQnKhmQ==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@next/swc-win32-arm64-msvc": { - "version": "13.4.2", - "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-13.4.2.tgz", - "integrity": "sha512-pLT+OWYpzJig5K4VKhLttlIfBcVZfr2+Xbjra0Tjs83NQSkFS+y7xx+YhCwvpEmXYLIvaggj2ONPyjbiigOvHQ==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@next/swc-win32-ia32-msvc": { - "version": "13.4.2", - "resolved": "https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-13.4.2.tgz", - "integrity": "sha512-dhpiksQCyGca4WY0fJyzK3FxMDFoqMb0Cn+uDB+9GYjpU2K5//UGPQlCwiK4JHxuhg8oLMag5Nf3/IPSJNG8jw==", - "cpu": [ - "ia32" - ], - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@next/swc-win32-x64-msvc": { - "version": "13.4.2", - "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-13.4.2.tgz", - "integrity": "sha512-O7bort1Vld00cu8g0jHZq3cbSTUNMohOEvYqsqE10+yfohhdPHzvzO+ziJRz4Dyyr/fYKREwS7gR4JC0soSOMw==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@nodelib/fs.scandir": { - "version": "2.1.5", - "license": "MIT", - "dependencies": { - "@nodelib/fs.stat": "2.0.5", - "run-parallel": "^1.1.9" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@nodelib/fs.stat": { - "version": "2.0.5", - "license": "MIT", - "engines": { - "node": ">= 8" - } - }, - "node_modules/@nodelib/fs.walk": { - "version": "1.2.8", - "license": "MIT", - "dependencies": { - "@nodelib/fs.scandir": "2.1.5", - "fastq": "^1.6.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@pkgr/utils": { - "version": "2.4.0", - "license": "MIT", - "dependencies": { - "cross-spawn": "^7.0.3", - "fast-glob": "^3.2.12", - "is-glob": "^4.0.3", - "open": "^9.1.0", - "picocolors": "^1.0.0", - "tslib": "^2.5.0" - }, - "engines": { - "node": "^12.20.0 || ^14.18.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/unts" - } - }, - "node_modules/@popperjs/core": { - "version": "2.11.7", - "license": "MIT", - "peer": true, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/popperjs" - } - }, - "node_modules/@rushstack/eslint-patch": { - "version": "1.2.0", - "license": "MIT" - }, - "node_modules/@swc/helpers": { - "version": "0.5.1", - "license": "Apache-2.0", - "dependencies": { - "tslib": "^2.4.0" - } - }, - "node_modules/@types/json5": { - "version": "0.0.29", - "license": "MIT" - }, - "node_modules/@typescript-eslint/parser": { - "version": "5.59.6", - "license": "BSD-2-Clause", - "dependencies": { - "@typescript-eslint/scope-manager": "5.59.6", - "@typescript-eslint/types": "5.59.6", - "@typescript-eslint/typescript-estree": "5.59.6", - "debug": "^4.3.4" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/@typescript-eslint/scope-manager": { - "version": "5.59.6", - "license": "MIT", - "dependencies": { - "@typescript-eslint/types": "5.59.6", - "@typescript-eslint/visitor-keys": "5.59.6" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/types": { - "version": "5.59.6", - "license": "MIT", - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/typescript-estree": { - "version": "5.59.6", - "license": "BSD-2-Clause", - "dependencies": { - "@typescript-eslint/types": "5.59.6", - "@typescript-eslint/visitor-keys": "5.59.6", - "debug": "^4.3.4", - "globby": "^11.1.0", - "is-glob": "^4.0.3", - "semver": "^7.3.7", - "tsutils": "^3.21.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/@typescript-eslint/visitor-keys": { - "version": "5.59.6", - "license": "MIT", - "dependencies": { - "@typescript-eslint/types": "5.59.6", - "eslint-visitor-keys": "^3.3.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/acorn": { - "version": "8.8.2", - "license": "MIT", - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/acorn-jsx": { - "version": "5.3.2", - "license": "MIT", - "peerDependencies": { - "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" - } - }, - "node_modules/ajv": { - "version": "6.12.6", - "license": "MIT", - "dependencies": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/ansi-regex": { - "version": "5.0.1", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/ansi-styles": { - "version": "4.3.0", - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/argparse": { - "version": "2.0.1", - "license": "Python-2.0" - }, - "node_modules/aria-query": { - "version": "5.1.3", - "license": "Apache-2.0", - "dependencies": { - "deep-equal": "^2.0.5" - } - }, - "node_modules/array-buffer-byte-length": { - "version": "1.0.0", - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.2", - "is-array-buffer": "^3.0.1" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/array-includes": { - "version": "3.1.6", - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4", - "get-intrinsic": "^1.1.3", - "is-string": "^1.0.7" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/array-union": { - "version": "2.1.0", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/array.prototype.flat": { - "version": "1.3.1", - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4", - "es-shim-unscopables": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/array.prototype.flatmap": { - "version": "1.3.1", - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4", - "es-shim-unscopables": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/array.prototype.tosorted": { - "version": "1.1.1", - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4", - "es-shim-unscopables": "^1.0.0", - "get-intrinsic": "^1.1.3" - } - }, - "node_modules/ast-types-flow": { - "version": "0.0.7", - "license": "ISC" - }, - "node_modules/asynckit": { - "version": "0.4.0", - "license": "MIT" - }, - "node_modules/available-typed-arrays": { - "version": "1.0.5", - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/axe-core": { - "version": "4.7.1", - "license": "MPL-2.0", - "engines": { - "node": ">=4" - } - }, - "node_modules/axios": { - "version": "1.4.0", - "license": "MIT", - "dependencies": { - "follow-redirects": "^1.15.0", - "form-data": "^4.0.0", - "proxy-from-env": "^1.1.0" - } - }, - "node_modules/axobject-query": { - "version": "3.1.1", - "license": "Apache-2.0", - "dependencies": { - "deep-equal": "^2.0.5" - } - }, - "node_modules/balanced-match": { - "version": "1.0.2", - "license": "MIT" - }, - "node_modules/big-integer": { - "version": "1.6.51", - "license": "Unlicense", - "engines": { - "node": ">=0.6" - } - }, - "node_modules/bootstrap": { - "version": "5.2.3", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/twbs" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/bootstrap" - } - ], - "license": "MIT", - "peerDependencies": { - "@popperjs/core": "^2.11.6" - } - }, - "node_modules/bplist-parser": { - "version": "0.2.0", - "license": "MIT", - "dependencies": { - "big-integer": "^1.6.44" - }, - "engines": { - "node": ">= 5.10.0" - } - }, - "node_modules/brace-expansion": { - "version": "1.1.11", - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/braces": { - "version": "3.0.2", - "license": "MIT", - "dependencies": { - "fill-range": "^7.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/bundle-name": { - "version": "3.0.0", - "license": "MIT", - "dependencies": { - "run-applescript": "^5.0.0" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/busboy": { - "version": "1.6.0", - "dependencies": { - "streamsearch": "^1.1.0" - }, - "engines": { - "node": ">=10.16.0" - } - }, - "node_modules/call-bind": { - "version": "1.0.2", - "license": "MIT", - "dependencies": { - "function-bind": "^1.1.1", - "get-intrinsic": "^1.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/callsites": { - "version": "3.1.0", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/caniuse-lite": { - "version": "1.0.30001491", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001491.tgz", - "integrity": "sha512-17EYIi4TLnPiTzVKMveIxU5ETlxbSO3B6iPvMbprqnKh4qJsQGk5Nh1Lp4jIMAE0XfrujsJuWZAM3oJdMHaKBA==", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/caniuse-lite" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ] - }, - "node_modules/chalk": { - "version": "4.1.2", - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/client-only": { - "version": "0.0.1", - "license": "MIT" - }, - "node_modules/clsx": { - "version": "1.2.1", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/color-convert": { - "version": "2.0.1", - "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/color-name": { - "version": "1.1.4", - "license": "MIT" - }, - "node_modules/combined-stream": { - "version": "1.0.8", - "license": "MIT", - "dependencies": { - "delayed-stream": "~1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/concat-map": { - "version": "0.0.1", - "license": "MIT" - }, - "node_modules/cross-spawn": { - "version": "7.0.3", - "license": "MIT", - "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/damerau-levenshtein": { - "version": "1.0.8", - "license": "BSD-2-Clause" - }, - "node_modules/date-fns": { - "version": "2.30.0", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.21.0" - }, - "engines": { - "node": ">=0.11" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/date-fns" - } - }, - "node_modules/debug": { - "version": "4.3.4", - "license": "MIT", - "dependencies": { - "ms": "2.1.2" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/deep-equal": { - "version": "2.2.1", - "license": "MIT", - "dependencies": { - "array-buffer-byte-length": "^1.0.0", - "call-bind": "^1.0.2", - "es-get-iterator": "^1.1.3", - "get-intrinsic": "^1.2.0", - "is-arguments": "^1.1.1", - "is-array-buffer": "^3.0.2", - "is-date-object": "^1.0.5", - "is-regex": "^1.1.4", - "is-shared-array-buffer": "^1.0.2", - "isarray": "^2.0.5", - "object-is": "^1.1.5", - "object-keys": "^1.1.1", - "object.assign": "^4.1.4", - "regexp.prototype.flags": "^1.5.0", - "side-channel": "^1.0.4", - "which-boxed-primitive": "^1.0.2", - "which-collection": "^1.0.1", - "which-typed-array": "^1.1.9" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/deep-is": { - "version": "0.1.4", - "license": "MIT" - }, - "node_modules/default-browser": { - "version": "4.0.0", - "license": "MIT", - "dependencies": { - "bundle-name": "^3.0.0", - "default-browser-id": "^3.0.0", - "execa": "^7.1.1", - "titleize": "^3.0.0" - }, - "engines": { - "node": ">=14.16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/default-browser-id": { - "version": "3.0.0", - "license": "MIT", - "dependencies": { - "bplist-parser": "^0.2.0", - "untildify": "^4.0.0" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/define-lazy-prop": { - "version": "3.0.0", - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/define-properties": { - "version": "1.2.0", - "license": "MIT", - "dependencies": { - "has-property-descriptors": "^1.0.0", - "object-keys": "^1.1.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/delayed-stream": { - "version": "1.0.0", - "license": "MIT", - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/dir-glob": { - "version": "3.0.1", - "license": "MIT", - "dependencies": { - "path-type": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/doctrine": { - "version": "3.0.0", - "license": "Apache-2.0", - "dependencies": { - "esutils": "^2.0.2" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/emoji-regex": { - "version": "9.2.2", - "license": "MIT" - }, - "node_modules/enhanced-resolve": { - "version": "5.14.1", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.14.1.tgz", - "integrity": "sha512-Vklwq2vDKtl0y/vtwjSesgJ5MYS7Etuk5txS8VdKL4AOS1aUlD96zqIfsOSLQsdv3xgMRbtkWM8eG9XDfKUPow==", - "dependencies": { - "graceful-fs": "^4.2.4", - "tapable": "^2.2.0" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/es-abstract": { - "version": "1.21.2", - "license": "MIT", - "dependencies": { - "array-buffer-byte-length": "^1.0.0", - "available-typed-arrays": "^1.0.5", - "call-bind": "^1.0.2", - "es-set-tostringtag": "^2.0.1", - "es-to-primitive": "^1.2.1", - "function.prototype.name": "^1.1.5", - "get-intrinsic": "^1.2.0", - "get-symbol-description": "^1.0.0", - "globalthis": "^1.0.3", - "gopd": "^1.0.1", - "has": "^1.0.3", - "has-property-descriptors": "^1.0.0", - "has-proto": "^1.0.1", - "has-symbols": "^1.0.3", - "internal-slot": "^1.0.5", - "is-array-buffer": "^3.0.2", - "is-callable": "^1.2.7", - "is-negative-zero": "^2.0.2", - "is-regex": "^1.1.4", - "is-shared-array-buffer": "^1.0.2", - "is-string": "^1.0.7", - "is-typed-array": "^1.1.10", - "is-weakref": "^1.0.2", - "object-inspect": "^1.12.3", - "object-keys": "^1.1.1", - "object.assign": "^4.1.4", - "regexp.prototype.flags": "^1.4.3", - "safe-regex-test": "^1.0.0", - "string.prototype.trim": "^1.2.7", - "string.prototype.trimend": "^1.0.6", - "string.prototype.trimstart": "^1.0.6", - "typed-array-length": "^1.0.4", - "unbox-primitive": "^1.0.2", - "which-typed-array": "^1.1.9" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/es-get-iterator": { - "version": "1.1.3", - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.1.3", - "has-symbols": "^1.0.3", - "is-arguments": "^1.1.1", - "is-map": "^2.0.2", - "is-set": "^2.0.2", - "is-string": "^1.0.7", - "isarray": "^2.0.5", - "stop-iteration-iterator": "^1.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/es-set-tostringtag": { - "version": "2.0.1", - "license": "MIT", - "dependencies": { - "get-intrinsic": "^1.1.3", - "has": "^1.0.3", - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-shim-unscopables": { - "version": "1.0.0", - "license": "MIT", - "dependencies": { - "has": "^1.0.3" - } - }, - "node_modules/es-to-primitive": { - "version": "1.2.1", - "license": "MIT", - "dependencies": { - "is-callable": "^1.1.4", - "is-date-object": "^1.0.1", - "is-symbol": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/escape-string-regexp": { - "version": "4.0.0", - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/eslint": { - "version": "8.40.0", - "license": "MIT", - "dependencies": { - "@eslint-community/eslint-utils": "^4.2.0", - "@eslint-community/regexpp": "^4.4.0", - "@eslint/eslintrc": "^2.0.3", - "@eslint/js": "8.40.0", - "@humanwhocodes/config-array": "^0.11.8", - "@humanwhocodes/module-importer": "^1.0.1", - "@nodelib/fs.walk": "^1.2.8", - "ajv": "^6.10.0", - "chalk": "^4.0.0", - "cross-spawn": "^7.0.2", - "debug": "^4.3.2", - "doctrine": "^3.0.0", - "escape-string-regexp": "^4.0.0", - "eslint-scope": "^7.2.0", - "eslint-visitor-keys": "^3.4.1", - "espree": "^9.5.2", - "esquery": "^1.4.2", - "esutils": "^2.0.2", - "fast-deep-equal": "^3.1.3", - "file-entry-cache": "^6.0.1", - "find-up": "^5.0.0", - "glob-parent": "^6.0.2", - "globals": "^13.19.0", - "grapheme-splitter": "^1.0.4", - "ignore": "^5.2.0", - "import-fresh": "^3.0.0", - "imurmurhash": "^0.1.4", - "is-glob": "^4.0.0", - "is-path-inside": "^3.0.3", - "js-sdsl": "^4.1.4", - "js-yaml": "^4.1.0", - "json-stable-stringify-without-jsonify": "^1.0.1", - "levn": "^0.4.1", - "lodash.merge": "^4.6.2", - "minimatch": "^3.1.2", - "natural-compare": "^1.4.0", - "optionator": "^0.9.1", - "strip-ansi": "^6.0.1", - "strip-json-comments": "^3.1.0", - "text-table": "^0.2.0" - }, - "bin": { - "eslint": "bin/eslint.js" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/eslint-config-next": { - "version": "13.4.2", - "license": "MIT", - "dependencies": { - "@next/eslint-plugin-next": "13.4.2", - "@rushstack/eslint-patch": "^1.1.3", - "@typescript-eslint/parser": "^5.42.0", - "eslint-import-resolver-node": "^0.3.6", - "eslint-import-resolver-typescript": "^3.5.2", - "eslint-plugin-import": "^2.26.0", - "eslint-plugin-jsx-a11y": "^6.5.1", - "eslint-plugin-react": "^7.31.7", - "eslint-plugin-react-hooks": "^4.5.0" - }, - "peerDependencies": { - "eslint": "^7.23.0 || ^8.0.0", - "typescript": ">=3.3.1" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } - } - }, - "node_modules/eslint-import-resolver-node": { - "version": "0.3.7", - "license": "MIT", - "dependencies": { - "debug": "^3.2.7", - "is-core-module": "^2.11.0", - "resolve": "^1.22.1" - } - }, - "node_modules/eslint-import-resolver-node/node_modules/debug": { - "version": "3.2.7", - "license": "MIT", - "dependencies": { - "ms": "^2.1.1" - } - }, - "node_modules/eslint-import-resolver-typescript": { - "version": "3.5.5", - "license": "ISC", - "dependencies": { - "debug": "^4.3.4", - "enhanced-resolve": "^5.12.0", - "eslint-module-utils": "^2.7.4", - "get-tsconfig": "^4.5.0", - "globby": "^13.1.3", - "is-core-module": "^2.11.0", - "is-glob": "^4.0.3", - "synckit": "^0.8.5" - }, - "engines": { - "node": "^14.18.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/unts/projects/eslint-import-resolver-ts" - }, - "peerDependencies": { - "eslint": "*", - "eslint-plugin-import": "*" - } - }, - "node_modules/eslint-import-resolver-typescript/node_modules/globby": { - "version": "13.1.4", - "license": "MIT", - "dependencies": { - "dir-glob": "^3.0.1", - "fast-glob": "^3.2.11", - "ignore": "^5.2.0", - "merge2": "^1.4.1", - "slash": "^4.0.0" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/eslint-import-resolver-typescript/node_modules/slash": { - "version": "4.0.0", - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/eslint-module-utils": { - "version": "2.8.0", - "license": "MIT", - "dependencies": { - "debug": "^3.2.7" - }, - "engines": { - "node": ">=4" - }, - "peerDependenciesMeta": { - "eslint": { - "optional": true - } - } - }, - "node_modules/eslint-module-utils/node_modules/debug": { - "version": "3.2.7", - "license": "MIT", - "dependencies": { - "ms": "^2.1.1" - } - }, - "node_modules/eslint-plugin-import": { - "version": "2.27.5", - "license": "MIT", - "dependencies": { - "array-includes": "^3.1.6", - "array.prototype.flat": "^1.3.1", - "array.prototype.flatmap": "^1.3.1", - "debug": "^3.2.7", - "doctrine": "^2.1.0", - "eslint-import-resolver-node": "^0.3.7", - "eslint-module-utils": "^2.7.4", - "has": "^1.0.3", - "is-core-module": "^2.11.0", - "is-glob": "^4.0.3", - "minimatch": "^3.1.2", - "object.values": "^1.1.6", - "resolve": "^1.22.1", - "semver": "^6.3.0", - "tsconfig-paths": "^3.14.1" - }, - "engines": { - "node": ">=4" - }, - "peerDependencies": { - "eslint": "^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8" - } - }, - "node_modules/eslint-plugin-import/node_modules/debug": { - "version": "3.2.7", - "license": "MIT", - "dependencies": { - "ms": "^2.1.1" - } - }, - "node_modules/eslint-plugin-import/node_modules/doctrine": { - "version": "2.1.0", - "license": "Apache-2.0", - "dependencies": { - "esutils": "^2.0.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/eslint-plugin-import/node_modules/semver": { - "version": "6.3.0", - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/eslint-plugin-jsx-a11y": { - "version": "6.7.1", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.20.7", - "aria-query": "^5.1.3", - "array-includes": "^3.1.6", - "array.prototype.flatmap": "^1.3.1", - "ast-types-flow": "^0.0.7", - "axe-core": "^4.6.2", - "axobject-query": "^3.1.1", - "damerau-levenshtein": "^1.0.8", - "emoji-regex": "^9.2.2", - "has": "^1.0.3", - "jsx-ast-utils": "^3.3.3", - "language-tags": "=1.0.5", - "minimatch": "^3.1.2", - "object.entries": "^1.1.6", - "object.fromentries": "^2.0.6", - "semver": "^6.3.0" - }, - "engines": { - "node": ">=4.0" - }, - "peerDependencies": { - "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8" - } - }, - "node_modules/eslint-plugin-jsx-a11y/node_modules/semver": { - "version": "6.3.0", - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/eslint-plugin-react": { - "version": "7.32.2", - "license": "MIT", - "dependencies": { - "array-includes": "^3.1.6", - "array.prototype.flatmap": "^1.3.1", - "array.prototype.tosorted": "^1.1.1", - "doctrine": "^2.1.0", - "estraverse": "^5.3.0", - "jsx-ast-utils": "^2.4.1 || ^3.0.0", - "minimatch": "^3.1.2", - "object.entries": "^1.1.6", - "object.fromentries": "^2.0.6", - "object.hasown": "^1.1.2", - "object.values": "^1.1.6", - "prop-types": "^15.8.1", - "resolve": "^2.0.0-next.4", - "semver": "^6.3.0", - "string.prototype.matchall": "^4.0.8" - }, - "engines": { - "node": ">=4" - }, - "peerDependencies": { - "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8" - } - }, - "node_modules/eslint-plugin-react-hooks": { - "version": "4.6.0", - "license": "MIT", - "engines": { - "node": ">=10" - }, - "peerDependencies": { - "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0" - } - }, - "node_modules/eslint-plugin-react/node_modules/doctrine": { - "version": "2.1.0", - "license": "Apache-2.0", - "dependencies": { - "esutils": "^2.0.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/eslint-plugin-react/node_modules/resolve": { - "version": "2.0.0-next.4", - "license": "MIT", - "dependencies": { - "is-core-module": "^2.9.0", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" - }, - "bin": { - "resolve": "bin/resolve" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/eslint-plugin-react/node_modules/semver": { - "version": "6.3.0", - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/eslint-scope": { - "version": "7.2.0", - "license": "BSD-2-Clause", - "dependencies": { - "esrecurse": "^4.3.0", - "estraverse": "^5.2.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/eslint-visitor-keys": { - "version": "3.4.1", - "license": "Apache-2.0", - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/espree": { - "version": "9.5.2", - "license": "BSD-2-Clause", - "dependencies": { - "acorn": "^8.8.0", - "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^3.4.1" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/esquery": { - "version": "1.5.0", - "license": "BSD-3-Clause", - "dependencies": { - "estraverse": "^5.1.0" - }, - "engines": { - "node": ">=0.10" - } - }, - "node_modules/esrecurse": { - "version": "4.3.0", - "license": "BSD-2-Clause", - "dependencies": { - "estraverse": "^5.2.0" - }, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/estraverse": { - "version": "5.3.0", - "license": "BSD-2-Clause", - "engines": { - "node": ">=4.0" - } - }, - "node_modules/esutils": { - "version": "2.0.3", - "license": "BSD-2-Clause", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/execa": { - "version": "7.1.1", - "license": "MIT", - "dependencies": { - "cross-spawn": "^7.0.3", - "get-stream": "^6.0.1", - "human-signals": "^4.3.0", - "is-stream": "^3.0.0", - "merge-stream": "^2.0.0", - "npm-run-path": "^5.1.0", - "onetime": "^6.0.0", - "signal-exit": "^3.0.7", - "strip-final-newline": "^3.0.0" - }, - "engines": { - "node": "^14.18.0 || ^16.14.0 || >=18.0.0" - }, - "funding": { - "url": "https://github.com/sindresorhus/execa?sponsor=1" - } - }, - "node_modules/fast-deep-equal": { - "version": "3.1.3", - "license": "MIT" - }, - "node_modules/fast-glob": { - "version": "3.2.12", - "license": "MIT", - "dependencies": { - "@nodelib/fs.stat": "^2.0.2", - "@nodelib/fs.walk": "^1.2.3", - "glob-parent": "^5.1.2", - "merge2": "^1.3.0", - "micromatch": "^4.0.4" - }, - "engines": { - "node": ">=8.6.0" - } - }, - "node_modules/fast-glob/node_modules/glob-parent": { - "version": "5.1.2", - "license": "ISC", - "dependencies": { - "is-glob": "^4.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/fast-json-stable-stringify": { - "version": "2.1.0", - "license": "MIT" - }, - "node_modules/fast-levenshtein": { - "version": "2.0.6", - "license": "MIT" - }, - "node_modules/fastq": { - "version": "1.15.0", - "license": "ISC", - "dependencies": { - "reusify": "^1.0.4" - } - }, - "node_modules/file-entry-cache": { - "version": "6.0.1", - "license": "MIT", - "dependencies": { - "flat-cache": "^3.0.4" - }, - "engines": { - "node": "^10.12.0 || >=12.0.0" - } - }, - "node_modules/fill-range": { - "version": "7.0.1", - "license": "MIT", - "dependencies": { - "to-regex-range": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/find-up": { - "version": "5.0.0", - "license": "MIT", - "dependencies": { - "locate-path": "^6.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/flat-cache": { - "version": "3.0.4", - "license": "MIT", - "dependencies": { - "flatted": "^3.1.0", - "rimraf": "^3.0.2" - }, - "engines": { - "node": "^10.12.0 || >=12.0.0" - } - }, - "node_modules/flatted": { - "version": "3.2.7", - "license": "ISC" - }, - "node_modules/follow-redirects": { - "version": "1.15.2", - "funding": [ - { - "type": "individual", - "url": "https://github.com/sponsors/RubenVerborgh" - } - ], - "license": "MIT", - "engines": { - "node": ">=4.0" - }, - "peerDependenciesMeta": { - "debug": { - "optional": true - } - } - }, - "node_modules/for-each": { - "version": "0.3.3", - "license": "MIT", - "dependencies": { - "is-callable": "^1.1.3" - } - }, - "node_modules/form-data": { - "version": "4.0.0", - "license": "MIT", - "dependencies": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.8", - "mime-types": "^2.1.12" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/fs.realpath": { - "version": "1.0.0", - "license": "ISC" - }, - "node_modules/function-bind": { - "version": "1.1.1", - "license": "MIT" - }, - "node_modules/function.prototype.name": { - "version": "1.1.5", - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3", - "es-abstract": "^1.19.0", - "functions-have-names": "^1.2.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/functions-have-names": { - "version": "1.2.3", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/get-intrinsic": { - "version": "1.2.1", - "license": "MIT", - "dependencies": { - "function-bind": "^1.1.1", - "has": "^1.0.3", - "has-proto": "^1.0.1", - "has-symbols": "^1.0.3" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/get-stream": { - "version": "6.0.1", - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/get-symbol-description": { - "version": "1.0.0", - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.1.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/get-tsconfig": { - "version": "4.5.0", - "license": "MIT", - "funding": { - "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" - } - }, - "node_modules/glob": { - "version": "7.1.7", - "license": "ISC", - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/glob-parent": { - "version": "6.0.2", - "license": "ISC", - "dependencies": { - "is-glob": "^4.0.3" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/globals": { - "version": "13.20.0", - "license": "MIT", - "dependencies": { - "type-fest": "^0.20.2" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/globalthis": { - "version": "1.0.3", - "license": "MIT", - "dependencies": { - "define-properties": "^1.1.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/globby": { - "version": "11.1.0", - "license": "MIT", - "dependencies": { - "array-union": "^2.1.0", - "dir-glob": "^3.0.1", - "fast-glob": "^3.2.9", - "ignore": "^5.2.0", - "merge2": "^1.4.1", - "slash": "^3.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/gopd": { - "version": "1.0.1", - "license": "MIT", - "dependencies": { - "get-intrinsic": "^1.1.3" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/graceful-fs": { - "version": "4.2.11", - "license": "ISC" - }, - "node_modules/grapheme-splitter": { - "version": "1.0.4", - "license": "MIT" - }, - "node_modules/has": { - "version": "1.0.3", - "license": "MIT", - "dependencies": { - "function-bind": "^1.1.1" - }, - "engines": { - "node": ">= 0.4.0" - } - }, - "node_modules/has-bigints": { - "version": "1.0.2", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-flag": { - "version": "4.0.0", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/has-property-descriptors": { - "version": "1.0.0", - "license": "MIT", - "dependencies": { - "get-intrinsic": "^1.1.1" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-proto": { - "version": "1.0.1", - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-symbols": { - "version": "1.0.3", - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-tostringtag": { - "version": "1.0.0", - "license": "MIT", - "dependencies": { - "has-symbols": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/human-signals": { - "version": "4.3.1", - "license": "Apache-2.0", - "engines": { - "node": ">=14.18.0" - } - }, - "node_modules/ignore": { - "version": "5.2.4", - "license": "MIT", - "engines": { - "node": ">= 4" - } - }, - "node_modules/import-fresh": { - "version": "3.3.0", - "license": "MIT", - "dependencies": { - "parent-module": "^1.0.0", - "resolve-from": "^4.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/imurmurhash": { - "version": "0.1.4", - "license": "MIT", - "engines": { - "node": ">=0.8.19" - } - }, - "node_modules/inflight": { - "version": "1.0.6", - "license": "ISC", - "dependencies": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "node_modules/inherits": { - "version": "2.0.4", - "license": "ISC" - }, - "node_modules/internal-slot": { - "version": "1.0.5", - "license": "MIT", - "dependencies": { - "get-intrinsic": "^1.2.0", - "has": "^1.0.3", - "side-channel": "^1.0.4" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/is-arguments": { - "version": "1.1.1", - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-array-buffer": { - "version": "3.0.2", - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.2.0", - "is-typed-array": "^1.1.10" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-bigint": { - "version": "1.0.4", - "license": "MIT", - "dependencies": { - "has-bigints": "^1.0.1" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-boolean-object": { - "version": "1.1.2", - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-callable": { - "version": "1.2.7", - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-core-module": { - "version": "2.12.1", - "license": "MIT", - "dependencies": { - "has": "^1.0.3" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-date-object": { - "version": "1.0.5", - "license": "MIT", - "dependencies": { - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-docker": { - "version": "3.0.0", - "license": "MIT", - "bin": { - "is-docker": "cli.js" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/is-extglob": { - "version": "2.1.1", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-glob": { - "version": "4.0.3", - "license": "MIT", - "dependencies": { - "is-extglob": "^2.1.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-inside-container": { - "version": "1.0.0", - "license": "MIT", - "dependencies": { - "is-docker": "^3.0.0" - }, - "bin": { - "is-inside-container": "cli.js" - }, - "engines": { - "node": ">=14.16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/is-map": { - "version": "2.0.2", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-negative-zero": { - "version": "2.0.2", - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-number": { - "version": "7.0.0", - "license": "MIT", - "engines": { - "node": ">=0.12.0" - } - }, - "node_modules/is-number-object": { - "version": "1.0.7", - "license": "MIT", - "dependencies": { - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-path-inside": { - "version": "3.0.3", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/is-regex": { - "version": "1.1.4", - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.2", - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-set": { - "version": "2.0.2", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-shared-array-buffer": { - "version": "1.0.2", - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-stream": { - "version": "3.0.0", - "license": "MIT", - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/is-string": { - "version": "1.0.7", - "license": "MIT", - "dependencies": { - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-symbol": { - "version": "1.0.4", - "license": "MIT", - "dependencies": { - "has-symbols": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-typed-array": { - "version": "1.1.10", - "license": "MIT", - "dependencies": { - "available-typed-arrays": "^1.0.5", - "call-bind": "^1.0.2", - "for-each": "^0.3.3", - "gopd": "^1.0.1", - "has-tostringtag": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-weakmap": { - "version": "2.0.1", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-weakref": { - "version": "1.0.2", - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-weakset": { - "version": "2.0.2", - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.1.1" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-wsl": { - "version": "2.2.0", - "license": "MIT", - "dependencies": { - "is-docker": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/is-wsl/node_modules/is-docker": { - "version": "2.2.1", - "license": "MIT", - "bin": { - "is-docker": "cli.js" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/isarray": { - "version": "2.0.5", - "license": "MIT" - }, - "node_modules/isexe": { - "version": "2.0.0", - "license": "ISC" - }, - "node_modules/js-sdsl": { - "version": "4.4.0", - "license": "MIT", - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/js-sdsl" - } - }, - "node_modules/js-tokens": { - "version": "4.0.0", - "license": "MIT" - }, - "node_modules/js-yaml": { - "version": "4.1.0", - "license": "MIT", - "dependencies": { - "argparse": "^2.0.1" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/json-schema-traverse": { - "version": "0.4.1", - "license": "MIT" - }, - "node_modules/json-stable-stringify-without-jsonify": { - "version": "1.0.1", - "license": "MIT" - }, - "node_modules/json5": { - "version": "1.0.2", - "license": "MIT", - "dependencies": { - "minimist": "^1.2.0" - }, - "bin": { - "json5": "lib/cli.js" - } - }, - "node_modules/jsx-ast-utils": { - "version": "3.3.3", - "license": "MIT", - "dependencies": { - "array-includes": "^3.1.5", - "object.assign": "^4.1.3" - }, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/language-subtag-registry": { - "version": "0.3.22", - "license": "CC0-1.0" - }, - "node_modules/language-tags": { - "version": "1.0.5", - "license": "MIT", - "dependencies": { - "language-subtag-registry": "~0.3.2" - } - }, - "node_modules/levn": { - "version": "0.4.1", - "license": "MIT", - "dependencies": { - "prelude-ls": "^1.2.1", - "type-check": "~0.4.0" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/locate-path": { - "version": "6.0.0", - "license": "MIT", - "dependencies": { - "p-locate": "^5.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/lodash.merge": { - "version": "4.6.2", - "license": "MIT" - }, - "node_modules/loose-envify": { - "version": "1.4.0", - "license": "MIT", - "dependencies": { - "js-tokens": "^3.0.0 || ^4.0.0" - }, - "bin": { - "loose-envify": "cli.js" - } - }, - "node_modules/lru-cache": { - "version": "6.0.0", - "license": "ISC", - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/merge-stream": { - "version": "2.0.0", - "license": "MIT" - }, - "node_modules/merge2": { - "version": "1.4.1", - "license": "MIT", - "engines": { - "node": ">= 8" - } - }, - "node_modules/micromatch": { - "version": "4.0.5", - "license": "MIT", - "dependencies": { - "braces": "^3.0.2", - "picomatch": "^2.3.1" - }, - "engines": { - "node": ">=8.6" - } - }, - "node_modules/mime-db": { - "version": "1.52.0", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mime-types": { - "version": "2.1.35", - "license": "MIT", - "dependencies": { - "mime-db": "1.52.0" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mimic-fn": { - "version": "4.0.0", - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/minimatch": { - "version": "3.1.2", - "license": "ISC", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/minimist": { - "version": "1.2.8", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/mitt": { - "version": "3.0.0", - "license": "MIT" - }, - "node_modules/ms": { - "version": "2.1.2", - "license": "MIT" - }, - "node_modules/nanoid": { - "version": "3.3.6", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "bin": { - "nanoid": "bin/nanoid.cjs" - }, - "engines": { - "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" - } - }, - "node_modules/natural-compare": { - "version": "1.4.0", - "license": "MIT" - }, - "node_modules/next": { - "version": "13.4.2", - "license": "MIT", - "dependencies": { - "@next/env": "13.4.2", - "@swc/helpers": "0.5.1", - "busboy": "1.6.0", - "caniuse-lite": "^1.0.30001406", - "postcss": "8.4.14", - "styled-jsx": "5.1.1", - "zod": "3.21.4" - }, - "bin": { - "next": "dist/bin/next" - }, - "engines": { - "node": ">=16.8.0" - }, - "optionalDependencies": { - "@next/swc-darwin-arm64": "13.4.2", - "@next/swc-darwin-x64": "13.4.2", - "@next/swc-linux-arm64-gnu": "13.4.2", - "@next/swc-linux-arm64-musl": "13.4.2", - "@next/swc-linux-x64-gnu": "13.4.2", - "@next/swc-linux-x64-musl": "13.4.2", - "@next/swc-win32-arm64-msvc": "13.4.2", - "@next/swc-win32-ia32-msvc": "13.4.2", - "@next/swc-win32-x64-msvc": "13.4.2" - }, - "peerDependencies": { - "@opentelemetry/api": "^1.1.0", - "fibers": ">= 3.1.0", - "node-sass": "^6.0.0 || ^7.0.0", - "react": "^18.2.0", - "react-dom": "^18.2.0", - "sass": "^1.3.0" - }, - "peerDependenciesMeta": { - "@opentelemetry/api": { - "optional": true - }, - "fibers": { - "optional": true - }, - "node-sass": { - "optional": true - }, - "sass": { - "optional": true - } - } - }, - "node_modules/npm-run-path": { - "version": "5.1.0", - "license": "MIT", - "dependencies": { - "path-key": "^4.0.0" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/npm-run-path/node_modules/path-key": { - "version": "4.0.0", - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/object-assign": { - "version": "4.1.1", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/object-inspect": { - "version": "1.12.3", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/object-is": { - "version": "1.1.5", - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/object-keys": { - "version": "1.1.1", - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/object.assign": { - "version": "4.1.4", - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "has-symbols": "^1.0.3", - "object-keys": "^1.1.1" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/object.entries": { - "version": "1.1.6", - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/object.fromentries": { - "version": "2.0.6", - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/object.hasown": { - "version": "1.1.2", - "license": "MIT", - "dependencies": { - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/object.values": { - "version": "1.1.6", - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/once": { - "version": "1.4.0", - "license": "ISC", - "dependencies": { - "wrappy": "1" - } - }, - "node_modules/onetime": { - "version": "6.0.0", - "license": "MIT", - "dependencies": { - "mimic-fn": "^4.0.0" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/open": { - "version": "9.1.0", - "license": "MIT", - "dependencies": { - "default-browser": "^4.0.0", - "define-lazy-prop": "^3.0.0", - "is-inside-container": "^1.0.0", - "is-wsl": "^2.2.0" - }, - "engines": { - "node": ">=14.16" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/optionator": { - "version": "0.9.1", - "license": "MIT", - "dependencies": { - "deep-is": "^0.1.3", - "fast-levenshtein": "^2.0.6", - "levn": "^0.4.1", - "prelude-ls": "^1.2.1", - "type-check": "^0.4.0", - "word-wrap": "^1.2.3" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/p-limit": { - "version": "3.1.0", - "license": "MIT", - "dependencies": { - "yocto-queue": "^0.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-locate": { - "version": "5.0.0", - "license": "MIT", - "dependencies": { - "p-limit": "^3.0.2" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/parent-module": { - "version": "1.0.1", - "license": "MIT", - "dependencies": { - "callsites": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/path-exists": { - "version": "4.0.0", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/path-is-absolute": { - "version": "1.0.1", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/path-key": { - "version": "3.1.1", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/path-parse": { - "version": "1.0.7", - "license": "MIT" - }, - "node_modules/path-type": { - "version": "4.0.0", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/picocolors": { - "version": "1.0.0", - "license": "ISC" - }, - "node_modules/picomatch": { - "version": "2.3.1", - "license": "MIT", - "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/postcss": { - "version": "8.4.14", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/postcss" - } - ], - "license": "MIT", - "dependencies": { - "nanoid": "^3.3.4", - "picocolors": "^1.0.0", - "source-map-js": "^1.0.2" - }, - "engines": { - "node": "^10 || ^12 || >=14" - } - }, - "node_modules/prelude-ls": { - "version": "1.2.1", - "license": "MIT", - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/prop-types": { - "version": "15.8.1", - "license": "MIT", - "dependencies": { - "loose-envify": "^1.4.0", - "object-assign": "^4.1.1", - "react-is": "^16.13.1" - } - }, - "node_modules/proxy-from-env": { - "version": "1.1.0", - "license": "MIT" - }, - "node_modules/punycode": { - "version": "2.3.0", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/queue-microtask": { - "version": "1.2.3", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT" - }, - "node_modules/react": { - "version": "18.2.0", - "license": "MIT", - "dependencies": { - "loose-envify": "^1.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/react-dom": { - "version": "18.2.0", - "license": "MIT", - "dependencies": { - "loose-envify": "^1.1.0", - "scheduler": "^0.23.0" - }, - "peerDependencies": { - "react": "^18.2.0" - } - }, - "node_modules/react-is": { - "version": "16.13.1", - "license": "MIT" - }, - "node_modules/react-toastify": { - "version": "9.1.3", - "license": "MIT", - "dependencies": { - "clsx": "^1.1.1" - }, - "peerDependencies": { - "react": ">=16", - "react-dom": ">=16" - } - }, - "node_modules/regenerator-runtime": { - "version": "0.13.11", - "license": "MIT" - }, - "node_modules/regexp.prototype.flags": { - "version": "1.5.0", - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.2.0", - "functions-have-names": "^1.2.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/resolve": { - "version": "1.22.2", - "license": "MIT", - "dependencies": { - "is-core-module": "^2.11.0", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" - }, - "bin": { - "resolve": "bin/resolve" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/resolve-from": { - "version": "4.0.0", - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/reusify": { - "version": "1.0.4", - "license": "MIT", - "engines": { - "iojs": ">=1.0.0", - "node": ">=0.10.0" - } - }, - "node_modules/rimraf": { - "version": "3.0.2", - "license": "ISC", - "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/run-applescript": { - "version": "5.0.0", - "license": "MIT", - "dependencies": { - "execa": "^5.0.0" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/run-applescript/node_modules/execa": { - "version": "5.1.1", - "license": "MIT", - "dependencies": { - "cross-spawn": "^7.0.3", - "get-stream": "^6.0.0", - "human-signals": "^2.1.0", - "is-stream": "^2.0.0", - "merge-stream": "^2.0.0", - "npm-run-path": "^4.0.1", - "onetime": "^5.1.2", - "signal-exit": "^3.0.3", - "strip-final-newline": "^2.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sindresorhus/execa?sponsor=1" - } - }, - "node_modules/run-applescript/node_modules/human-signals": { - "version": "2.1.0", - "license": "Apache-2.0", - "engines": { - "node": ">=10.17.0" - } - }, - "node_modules/run-applescript/node_modules/is-stream": { - "version": "2.0.1", - "license": "MIT", - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/run-applescript/node_modules/mimic-fn": { - "version": "2.1.0", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/run-applescript/node_modules/npm-run-path": { - "version": "4.0.1", - "license": "MIT", - "dependencies": { - "path-key": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/run-applescript/node_modules/onetime": { - "version": "5.1.2", - "license": "MIT", - "dependencies": { - "mimic-fn": "^2.1.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/run-applescript/node_modules/strip-final-newline": { - "version": "2.0.0", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/run-parallel": { - "version": "1.2.0", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT", - "dependencies": { - "queue-microtask": "^1.2.2" - } - }, - "node_modules/safe-regex-test": { - "version": "1.0.0", - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.1.3", - "is-regex": "^1.1.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/scheduler": { - "version": "0.23.0", - "license": "MIT", - "dependencies": { - "loose-envify": "^1.1.0" - } - }, - "node_modules/semver": { - "version": "7.5.1", - "license": "ISC", - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/shebang-command": { - "version": "2.0.0", - "license": "MIT", - "dependencies": { - "shebang-regex": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/shebang-regex": { - "version": "3.0.0", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/side-channel": { - "version": "1.0.4", - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.0", - "get-intrinsic": "^1.0.2", - "object-inspect": "^1.9.0" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/signal-exit": { - "version": "3.0.7", - "license": "ISC" - }, - "node_modules/slash": { - "version": "3.0.0", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/source-map-js": { - "version": "1.0.2", - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/stop-iteration-iterator": { - "version": "1.0.0", - "license": "MIT", - "dependencies": { - "internal-slot": "^1.0.4" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/streamsearch": { - "version": "1.1.0", - "engines": { - "node": ">=10.0.0" - } - }, - "node_modules/string.prototype.matchall": { - "version": "4.0.8", - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4", - "get-intrinsic": "^1.1.3", - "has-symbols": "^1.0.3", - "internal-slot": "^1.0.3", - "regexp.prototype.flags": "^1.4.3", - "side-channel": "^1.0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/string.prototype.trim": { - "version": "1.2.7", - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/string.prototype.trimend": { - "version": "1.0.6", - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/string.prototype.trimstart": { - "version": "1.0.6", - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.2", - "define-properties": "^1.1.4", - "es-abstract": "^1.20.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/strip-ansi": { - "version": "6.0.1", - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-bom": { - "version": "3.0.0", - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/strip-final-newline": { - "version": "3.0.0", - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/strip-json-comments": { - "version": "3.1.1", - "license": "MIT", - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/styled-jsx": { - "version": "5.1.1", - "license": "MIT", - "dependencies": { - "client-only": "0.0.1" - }, - "engines": { - "node": ">= 12.0.0" - }, - "peerDependencies": { - "react": ">= 16.8.0 || 17.x.x || ^18.0.0-0" - }, - "peerDependenciesMeta": { - "@babel/core": { - "optional": true - }, - "babel-plugin-macros": { - "optional": true - } - } - }, - "node_modules/supports-color": { - "version": "7.2.0", - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/supports-preserve-symlinks-flag": { - "version": "1.0.0", - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/synckit": { - "version": "0.8.5", - "license": "MIT", - "dependencies": { - "@pkgr/utils": "^2.3.1", - "tslib": "^2.5.0" - }, - "engines": { - "node": "^14.18.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/unts" - } - }, - "node_modules/tapable": { - "version": "2.2.1", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/text-table": { - "version": "0.2.0", - "license": "MIT" - }, - "node_modules/titleize": { - "version": "3.0.0", - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/to-regex-range": { - "version": "5.0.1", - "license": "MIT", - "dependencies": { - "is-number": "^7.0.0" - }, - "engines": { - "node": ">=8.0" - } - }, - "node_modules/tsconfig-paths": { - "version": "3.14.2", - "license": "MIT", - "dependencies": { - "@types/json5": "^0.0.29", - "json5": "^1.0.2", - "minimist": "^1.2.6", - "strip-bom": "^3.0.0" - } - }, - "node_modules/tslib": { - "version": "2.5.0", - "license": "0BSD" - }, - "node_modules/tsutils": { - "version": "3.21.0", - "license": "MIT", - "dependencies": { - "tslib": "^1.8.1" - }, - "engines": { - "node": ">= 6" - }, - "peerDependencies": { - "typescript": ">=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta" - } - }, - "node_modules/tsutils/node_modules/tslib": { - "version": "1.14.1", - "license": "0BSD" - }, - "node_modules/type-check": { - "version": "0.4.0", - "license": "MIT", - "dependencies": { - "prelude-ls": "^1.2.1" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/type-fest": { - "version": "0.20.2", - "license": "(MIT OR CC0-1.0)", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/typed-array-length": { - "version": "1.0.4", - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.2", - "for-each": "^0.3.3", - "is-typed-array": "^1.1.9" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/typescript": { - "version": "5.0.4", - "license": "Apache-2.0", - "peer": true, - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=12.20" - } - }, - "node_modules/unbox-primitive": { - "version": "1.0.2", - "license": "MIT", - "dependencies": { - "call-bind": "^1.0.2", - "has-bigints": "^1.0.2", - "has-symbols": "^1.0.3", - "which-boxed-primitive": "^1.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/untildify": { - "version": "4.0.0", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/uri-js": { - "version": "4.4.1", - "license": "BSD-2-Clause", - "dependencies": { - "punycode": "^2.1.0" - } - }, - "node_modules/which": { - "version": "2.0.2", - "license": "ISC", - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/which-boxed-primitive": { - "version": "1.0.2", - "license": "MIT", - "dependencies": { - "is-bigint": "^1.0.1", - "is-boolean-object": "^1.1.0", - "is-number-object": "^1.0.4", - "is-string": "^1.0.5", - "is-symbol": "^1.0.3" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/which-collection": { - "version": "1.0.1", - "license": "MIT", - "dependencies": { - "is-map": "^2.0.1", - "is-set": "^2.0.1", - "is-weakmap": "^2.0.1", - "is-weakset": "^2.0.1" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/which-typed-array": { - "version": "1.1.9", - "license": "MIT", - "dependencies": { - "available-typed-arrays": "^1.0.5", - "call-bind": "^1.0.2", - "for-each": "^0.3.3", - "gopd": "^1.0.1", - "has-tostringtag": "^1.0.0", - "is-typed-array": "^1.1.10" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/word-wrap": { - "version": "1.2.3", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/wrappy": { - "version": "1.0.2", - "license": "ISC" - }, - "node_modules/yallist": { - "version": "4.0.0", - "license": "ISC" - }, - "node_modules/yocto-queue": { - "version": "0.1.0", - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/zod": { - "version": "3.21.4", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/colinhacks" - } - } - } -} diff --git a/install_tool_dependencies.sh b/install_tool_dependencies.sh new file mode 100755 index 000000000..87dc10c2e --- /dev/null +++ b/install_tool_dependencies.sh @@ -0,0 +1,12 @@ +#!/bin/bash + +# Run the project's main requirements.txt +pip install -r /app/requirements.txt + +# Loop through the tools directories and install their requirements.txt if they exist +for tool in /app/superagi/tools/* ; do + if [ -d "$tool" ] && [ -f "$tool/requirements.txt" ]; then + echo "Installing requirements for tool: $(basename "$tool")" + pip install -r "$tool/requirements.txt" + fi +done diff --git a/main.py b/main.py index 4705e6601..f1836c5cb 100644 --- a/main.py +++ b/main.py @@ -26,17 +26,23 @@ from superagi.controllers.agent_execution import router as agent_execution_router from superagi.controllers.agent_execution_feed import router as agent_execution_feed_router from superagi.controllers.agent_execution_permission import router as agent_execution_permission_router +from superagi.controllers.agent_template import router as agent_template_router +from superagi.controllers.agent_workflow import router as agent_workflow_router from superagi.controllers.budget import router as budget_router +from superagi.controllers.config import router as config_router from superagi.controllers.organisation import router as organisation_router from superagi.controllers.project import router as project_router from superagi.controllers.resources import router as resources_router from superagi.controllers.tool import router as tool_router +from superagi.controllers.tool_config import router as tool_config_router +from superagi.controllers.toolkit import router as toolkit_router from superagi.controllers.user import router as user_router -from superagi.controllers.config import router as config_router +from superagi.helper.tool_helper import register_toolkits +from superagi.lib.logger import logger +from superagi.llms.openai import OpenAi from superagi.models.agent_workflow import AgentWorkflow from superagi.models.agent_workflow_step import AgentWorkflowStep from superagi.models.organisation import Organisation -from superagi.models.tool import Tool from superagi.models.types.login_request import LoginRequest from superagi.models.user import User from superagi.tools.base_tool import BaseTool @@ -90,10 +96,11 @@ app.include_router(agent_execution_permission_router, prefix="/agentexecutionpermissions") app.include_router(resources_router, prefix="/resources") app.include_router(config_router, prefix="/configs") -app.include_router(agent_template_router,prefix="/agent_templates") -app.include_router(agent_workflow_router,prefix="/agent_workflows") - - +app.include_router(toolkit_router, prefix="/toolkits") +app.include_router(tool_config_router, prefix="/tool_configs") +app.include_router(config_router, prefix="/configs") +app.include_router(agent_template_router, prefix="/agent_templates") +app.include_router(agent_workflow_router, prefix="/agent_workflows") # in production you can use Settings management @@ -128,90 +135,13 @@ def authjwt_exception_handler(request: Request, exc: AuthJWTException): Session = sessionmaker(bind=engine) session = Session() -organisation = session.query(Organisation).filter_by(id=1).first() - - -def add_or_update_tool(db: Session, tool_name: str, folder_name: str, class_name: str, file_name: str): - # Check if a record with the given tool name already exists - tool = db.query(Tool).filter_by(name=tool_name).first() +default_user = session.query(User).filter(User.email == "super6@agi.com").first() +logger.info(default_user) +if default_user is not None: + organisation = session.query(Organisation).filter_by(id=default_user.organisation_id).first() + logger.info(organisation) + register_toolkits(session, organisation) - if tool: - # Update the attributes of the existing tool record - tool.folder_name = folder_name - tool.class_name = class_name - tool.file_name = file_name - else: - # Create a new tool record - tool = Tool(name=tool_name, folder_name=folder_name, class_name=class_name, file_name=file_name) - db.add(tool) - - db.commit() - return tool - - -def get_classes_in_file(file_path): - classes = [] - - # Load the module from the file - module = load_module_from_file(file_path) - - # Iterate over all members of the module - for name, member in inspect.getmembers(module): - # Check if the member is a class and extends BaseTool - if inspect.isclass(member) and issubclass(member, BaseTool) and member != BaseTool: - class_dict = {} - class_dict['class_name'] = member.__name__ - - class_obj = getattr(module, member.__name__) - try: - obj = class_obj() - class_dict['class_attribute'] = obj.name - classes.append(class_dict) - except: - class_dict['class_attribute'] = None - return classes - - -def load_module_from_file(file_path): - import importlib.util - - spec = importlib.util.spec_from_file_location("module_name", file_path) - module = importlib.util.module_from_spec(spec) - spec.loader.exec_module(module) - - return module - - -# Function to process the files and extract class information -def process_files(folder_path): - existing_tools = session.query(Tool).all() - existing_tools = [Tool(id=None, name=tool.name, folder_name=tool.folder_name, class_name=tool.class_name) for tool - in existing_tools] - - new_tools = [] - # Iterate over all subfolders - for folder_name in os.listdir(folder_path): - folder_dir = os.path.join(folder_path, folder_name) - - if os.path.isdir(folder_dir): - # Iterate over all files in the subfolder - for file_name in os.listdir(folder_dir): - file_path = os.path.join(folder_dir, file_name) - if file_name.endswith(".py") and not file_name.startswith("__init__"): - # Get clasess - classes = get_classes_in_file(file_path=file_path) - # filtered_classes = [clazz for clazz in classes if - # clazz["class_name"].endswith("Tool") and clazz["class_name"] != "BaseTool"] - for clazz in classes: - if clazz["class_attribute"] is not None: - new_tool = Tool(class_name=clazz["class_name"], folder_name=folder_name, - file_name=file_name, - name=clazz["class_attribute"]) - new_tools.append(new_tool) - - for tool in new_tools: - add_or_update_tool(session, tool_name=tool.name, file_name=tool.file_name, folder_name=tool.folder_name, - class_name=tool.class_name) def build_single_step_agent(): agent_workflow = session.query(AgentWorkflow).filter(AgentWorkflow.name == "Goal Based Agent").first() @@ -231,7 +161,7 @@ def build_single_step_agent(): agent_workflow_id=agent_workflow.id, output_type="tools", step_type="TRIGGER", history_enabled=True, - completion_prompt= "Determine which next tool to use, and respond using the format specified above:") + completion_prompt="Determine which next tool to use, and respond using the format specified above:") session.add(first_step) session.commit() else: @@ -243,6 +173,7 @@ def build_single_step_agent(): first_step.next_step_id = first_step.id session.commit() + def build_task_based_agents(): agent_workflow = session.query(AgentWorkflow).filter(AgentWorkflow.name == "Task Queue Agent With Seed").first() if agent_workflow is None: @@ -261,9 +192,9 @@ def build_task_based_agents(): output_type="tasks") session.add(workflow_step1) else: - workflow_step1.prompt=output["prompt"] - workflow_step1.variables=str(output["variables"]) - workflow_step1.output_type="tasks" + workflow_step1.prompt = output["prompt"] + workflow_step1.variables = str(output["variables"]) + workflow_step1.output_type = "tasks" session.commit() workflow_step2 = session.query(AgentWorkflowStep).filter(AgentWorkflowStep.unique_id == "tb2").first() @@ -276,9 +207,9 @@ def build_task_based_agents(): output_type="tasks") session.add(workflow_step2) else: - workflow_step2.prompt=output["prompt"] - workflow_step2.variables=str(output["variables"]) - workflow_step2.output_type="tasks" + workflow_step2.prompt = output["prompt"] + workflow_step2.variables = str(output["variables"]) + workflow_step2.output_type = "tasks" session.commit() workflow_step3 = session.query(AgentWorkflowStep).filter(AgentWorkflowStep.unique_id == "tb3").first() @@ -292,9 +223,9 @@ def build_task_based_agents(): session.add(workflow_step3) else: - workflow_step3.prompt=output["prompt"] - workflow_step3.variables=str(output["variables"]) - workflow_step3.output_type="tools" + workflow_step3.prompt = output["prompt"] + workflow_step3.variables = str(output["variables"]) + workflow_step3.output_type = "tools" session.commit() workflow_step4 = session.query(AgentWorkflowStep).filter(AgentWorkflowStep.unique_id == "tb4").first() @@ -303,13 +234,14 @@ def build_task_based_agents(): workflow_step4 = AgentWorkflowStep(unique_id="tb4", prompt=output["prompt"], variables=str(output["variables"]), step_type="NORMAL", - agent_workflow_id=agent_workflow.id, next_step_id=-1, output_type="replace_tasks") + agent_workflow_id=agent_workflow.id, next_step_id=-1, + output_type="replace_tasks") session.add(workflow_step4) else: - workflow_step4.prompt=output["prompt"] - workflow_step4.variables=str(output["variables"]) - workflow_step4.output_type="replace_tasks" + workflow_step4.prompt = output["prompt"] + workflow_step4.variables = str(output["variables"]) + workflow_step4.output_type = "replace_tasks" session.commit() session.commit() workflow_step1.next_step_id = workflow_step3.id @@ -318,30 +250,24 @@ def build_task_based_agents(): workflow_step4.next_step_id = workflow_step3.id session.commit() + build_single_step_agent() build_task_based_agents() - -# Specify the folder path -folder_path = superagi.config.config.get_config("TOOLS_DIR") -if folder_path is None: - folder_path = "superagi/tools" - -# Process the files and store class information -process_files(folder_path) session.close() + @app.post('/login') def login(request: LoginRequest, Authorize: AuthJWT = Depends()): """Login API for email and password based login""" email_to_find = request.email - user:User = db.session.query(User).filter(User.email == email_to_find).first() + user: User = db.session.query(User).filter(User.email == email_to_find).first() - if user ==None or request.email != user.email or request.password != user.password: - raise HTTPException(status_code=401,detail="Bad username or password") + if user == None or request.email != user.email or request.password != user.password: + raise HTTPException(status_code=401, detail="Bad username or password") # subject identifier for who this token is for example id or username from database - access_token = create_access_token(user.email,Authorize) + access_token = create_access_token(user.email, Authorize) return {"access_token": access_token} @@ -433,8 +359,6 @@ def github_auth_handler(code: str = Query(...), Authorize: AuthJWT = Depends()): redirect_url_success = f"{frontend_url}?access_token={jwt_token}" return RedirectResponse(url=redirect_url_success) - - user = User(name=user_data["name"], email=user_email) db.session.add(user) db.session.commit() @@ -478,9 +402,20 @@ def get_google_calendar_tool_configs(toolkit_id: int): "client_id": google_calendar_config.value } +@app.get("/validate-open-ai-key/{open_ai_key}") +async def root(open_ai_key: str, Authorize: AuthJWT = Depends()): + """API to validate Open AI Key""" + + try: + llm = OpenAi(api_key=open_ai_key) + response = llm.chat_completion([{"role": "system", "content": "Hey!"}]) + except: + raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="Invalid API Key") + + # #Unprotected route @app.get("/hello/{name}") -async def say_hello(name: str,Authorize:AuthJWT=Depends()): +async def say_hello(name: str, Authorize: AuthJWT = Depends()): Authorize.jwt_required() return {"message": f"Hello {name}"} diff --git a/migrations/versions/7a3e336c0fba_added_tools_related_models.py b/migrations/versions/7a3e336c0fba_added_tools_related_models.py new file mode 100644 index 000000000..8b17e2f20 --- /dev/null +++ b/migrations/versions/7a3e336c0fba_added_tools_related_models.py @@ -0,0 +1,49 @@ +"""added_tools_related_models + +Revision ID: 7a3e336c0fba +Revises: 516ecc1c723d +Create Date: 2023-06-18 11:05:35.801505 + +""" +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision = '7a3e336c0fba' +down_revision = '1d54db311055' +branch_labels = None +depends_on = None + + +def upgrade() -> None: + # ### commands auto generated by Alembic - please adjust! ### + op.create_table('toolkits', + sa.Column('created_at', sa.DateTime(), nullable=True), + sa.Column('updated_at', sa.DateTime(), nullable=True), + sa.Column('id', sa.Integer(), nullable=False), + sa.Column('name', sa.String(), nullable=True), + sa.Column('description', sa.String(), nullable=True), + sa.Column('show_toolkit', sa.Boolean(), nullable=True), + sa.Column('organisation_id', sa.Integer(), nullable=True), + sa.Column('tool_code_link', sa.String(), nullable=True), + sa.PrimaryKeyConstraint('id') + ) + + op.add_column('tool_configs', sa.Column('toolkit_id', sa.Integer(), nullable=True)) + op.drop_column('tool_configs', 'name') + op.drop_column('tool_configs', 'agent_id') + op.add_column('tools', sa.Column('description', sa.String(), nullable=True)) + op.add_column('tools', sa.Column('toolkit_id', sa.Integer(), nullable=True)) + # ### end Alembic commands ### + + +def downgrade() -> None: + # ### commands auto generated by Alembic - please adjust! ### + op.drop_column('tools', 'toolkit_id') + op.drop_column('tools', 'description') + op.add_column('tool_configs', sa.Column('agent_id', sa.INTEGER(), autoincrement=False, nullable=True)) + op.add_column('tool_configs', sa.Column('name', sa.VARCHAR(), autoincrement=False, nullable=True)) + op.drop_column('tool_configs', 'toolkit_id') + op.drop_table('toolkits') + # ### end Alembic commands ### diff --git a/package-lock.json b/package-lock.json index 28539e96f..be1d0fb5d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2,5 +2,293 @@ "name": "SuperAGI", "lockfileVersion": 2, "requires": true, - "packages": {} + "packages": { + "": { + "dependencies": { + "axios": "^1.4.0", + "react-toastify": "^9.1.3" + } + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" + }, + "node_modules/axios": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.4.0.tgz", + "integrity": "sha512-S4XCWMEmzvo64T9GfvQDOXgYRDJ/wsSZc7Jvdgx5u1sd0JwsuPLqb3SYmusag+edF6ziyMensPVqLTSc1PiSEA==", + "dependencies": { + "follow-redirects": "^1.15.0", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" + } + }, + "node_modules/clsx": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-1.2.1.tgz", + "integrity": "sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg==", + "engines": { + "node": ">=6" + } + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/follow-redirects": { + "version": "1.15.2", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz", + "integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/form-data": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "peer": true + }, + "node_modules/loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "peer": true, + "dependencies": { + "js-tokens": "^3.0.0 || ^4.0.0" + }, + "bin": { + "loose-envify": "cli.js" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" + }, + "node_modules/react": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react/-/react-18.2.0.tgz", + "integrity": "sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==", + "peer": true, + "dependencies": { + "loose-envify": "^1.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-dom": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz", + "integrity": "sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==", + "peer": true, + "dependencies": { + "loose-envify": "^1.1.0", + "scheduler": "^0.23.0" + }, + "peerDependencies": { + "react": "^18.2.0" + } + }, + "node_modules/react-toastify": { + "version": "9.1.3", + "resolved": "https://registry.npmjs.org/react-toastify/-/react-toastify-9.1.3.tgz", + "integrity": "sha512-fPfb8ghtn/XMxw3LkxQBk3IyagNpF/LIKjOBflbexr2AWxAH1MJgvnESwEwBn9liLFXgTKWgBSdZpw9m4OTHTg==", + "dependencies": { + "clsx": "^1.1.1" + }, + "peerDependencies": { + "react": ">=16", + "react-dom": ">=16" + } + }, + "node_modules/scheduler": { + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.0.tgz", + "integrity": "sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw==", + "peer": true, + "dependencies": { + "loose-envify": "^1.1.0" + } + } + }, + "dependencies": { + "asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" + }, + "axios": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.4.0.tgz", + "integrity": "sha512-S4XCWMEmzvo64T9GfvQDOXgYRDJ/wsSZc7Jvdgx5u1sd0JwsuPLqb3SYmusag+edF6ziyMensPVqLTSc1PiSEA==", + "requires": { + "follow-redirects": "^1.15.0", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" + } + }, + "clsx": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-1.2.1.tgz", + "integrity": "sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg==" + }, + "combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "requires": { + "delayed-stream": "~1.0.0" + } + }, + "delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==" + }, + "follow-redirects": { + "version": "1.15.2", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz", + "integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==" + }, + "form-data": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + } + }, + "js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "peer": true + }, + "loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "peer": true, + "requires": { + "js-tokens": "^3.0.0 || ^4.0.0" + } + }, + "mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==" + }, + "mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "requires": { + "mime-db": "1.52.0" + } + }, + "proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" + }, + "react": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react/-/react-18.2.0.tgz", + "integrity": "sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==", + "peer": true, + "requires": { + "loose-envify": "^1.1.0" + } + }, + "react-dom": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz", + "integrity": "sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==", + "peer": true, + "requires": { + "loose-envify": "^1.1.0", + "scheduler": "^0.23.0" + } + }, + "react-toastify": { + "version": "9.1.3", + "resolved": "https://registry.npmjs.org/react-toastify/-/react-toastify-9.1.3.tgz", + "integrity": "sha512-fPfb8ghtn/XMxw3LkxQBk3IyagNpF/LIKjOBflbexr2AWxAH1MJgvnESwEwBn9liLFXgTKWgBSdZpw9m4OTHTg==", + "requires": { + "clsx": "^1.1.1" + } + }, + "scheduler": { + "version": "0.23.0", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.0.tgz", + "integrity": "sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw==", + "peer": true, + "requires": { + "loose-envify": "^1.1.0" + } + } + } } diff --git a/package.json b/package.json new file mode 100644 index 000000000..17e4fbe66 --- /dev/null +++ b/package.json @@ -0,0 +1,6 @@ +{ + "dependencies": { + "axios": "^1.4.0", + "react-toastify": "^9.1.3" + } +} diff --git a/requirements.txt b/requirements.txt index 4828246a4..624a2d107 100644 --- a/requirements.txt +++ b/requirements.txt @@ -3,6 +3,7 @@ aiosignal==1.3.1 alembic==1.11.1 amqp==5.1.1 anyio==3.7.0 +apiclient==1.0.4 appdirs==1.4.4 async-timeout==4.0.2 attrs==23.1.0 @@ -38,7 +39,10 @@ filelock==3.12.0 frozenlist==1.3.3 google-search-results==2.4.2 google-serp-api==1.0.3 -google-api-python-client==2.90.0 +google-api-core==2.11.0 +google-api-python-client==2.88.0 +google-auth==2.19.1 +google-auth-httplib2==0.1.0 google-auth-oauthlib==1.0.0 greenlet==2.0.2 h11==0.14.0 @@ -73,6 +77,7 @@ nltk==3.8.1 numexpr==2.8.4 numpy==1.24.3 oauthlib==3.2.2 +oauth2client==4.1.3 openai==0.27.7 openapi-schema-pydantic==1.2.4 orjson==3.8.14 diff --git a/superagi/agent/agent_prompt_builder.py b/superagi/agent/agent_prompt_builder.py index 70dd5c79d..756fdfec4 100644 --- a/superagi/agent/agent_prompt_builder.py +++ b/superagi/agent/agent_prompt_builder.py @@ -3,7 +3,9 @@ from pydantic.types import List +from superagi.helper.prompt_reader import PromptReader from superagi.helper.token_counter import TokenCounter +from superagi.lib.logger import logger from superagi.tools.base_tool import BaseTool FINISH_NAME = "finish" @@ -71,35 +73,7 @@ def get_super_agi_single_prompt(cls): } formatted_response_format = json.dumps(response_format, indent=4) - super_agi_prompt = """You are SuperAGI an AI assistant to solve complex problems. Your decisions must always be made independently without seeking user assistance. - Play to your strengths as an LLM and pursue simple strategies with no legal complications. - If you have completed all your tasks or reached end state, make sure to use the "finish" tool. - - GOALS: - {goals} - - {instructions} - - CONSTRAINTS: - {constraints} - - TOOLS: - {tools} - - PERFORMANCE EVALUATION: - 1. Continuously review and analyze your actions to ensure you are performing to the best of your abilities. - 2. Use instruction to decide the flow of execution and decide the next steps for achieving the task. - 2. Constructively self-criticize your big-picture behavior constantly. - 3. Reflect on past decisions and strategies to refine your approach. - 4. Every tool has a cost, so be smart and efficient. - 5. Aim to complete tasks in the least number of steps. - - I should only respond in JSON format as described below. - Response Format: - {response_format} - - Ensure the response can be parsed by Python json.loads. - """ + super_agi_prompt = PromptReader.read_agent_prompt(__file__, "superagi.txt") super_agi_prompt = AgentPromptBuilder.clean_prompt(super_agi_prompt).replace("{response_format}", formatted_response_format) @@ -107,22 +81,7 @@ def get_super_agi_single_prompt(cls): @classmethod def start_task_based(cls): - super_agi_prompt = """You are a task-generating AI known as SuperAGI. You are not a part of any system or device. Your role is to understand the goals presented to you, identify important components, Go through the instruction provided by the user and construct a thorough execution plan. - - GOALS: - {goals} - - {task_instructions} - - Construct a sequence of actions, not exceeding 3 steps, to achieve this goal. - - Submit your response as a formatted ARRAY of strings, suitable for utilization with JSON.parse(). - - Example: ["{{TASK-1}}", "{{TASK-2}}"]. - - - - """ + super_agi_prompt = PromptReader.read_agent_prompt(__file__, "initialize_tasks.txt") return {"prompt": AgentPromptBuilder.clean_prompt(super_agi_prompt), "variables": ["goals", "instructions"]} # super_agi_prompt = super_agi_prompt.replace("{goals}", AgentPromptBuilder.add_list_items_to_string(goals)) @@ -132,35 +91,7 @@ def analyse_task(cls): constraints = [ 'Exclusively use the tools listed in double quotes e.g. "tool name"' ] - super_agi_prompt = """ - High level goal: - {goals} - - {task_instructions} - - Your Current Task: `{current_task}` - - Task History: - `{task_history}` - - Based on this, your job is to understand the current task, pick out key parts, and think smart and fast. - Explain why you are doing each action, create a plan, and mention any worries you might have. - Ensure next action tool is picked from the below tool list. - - TOOLS: - {tools} - - RESPONSE FORMAT: - { - "thoughts": { - "reasoning": "reasoning" - }, - "tool": {"name": "tool name", "args": {"arg name": "string value"}} - } - - Your answer must be something that JSON.parse() can read, and nothing else. - """ - + super_agi_prompt = PromptReader.read_agent_prompt(__file__, "analyse_task.txt") super_agi_prompt = AgentPromptBuilder.clean_prompt(super_agi_prompt) \ .replace("{constraints}", AgentPromptBuilder.add_list_items_to_string(constraints)) return {"prompt": super_agi_prompt, "variables": ["goals", "instructions", "tools", "current_task"]} @@ -168,47 +99,14 @@ def analyse_task(cls): @classmethod def create_tasks(cls): # just executed task `{last_task}` and got the result `{last_task_result}` - super_agi_prompt = """ - You are an AI assistant to create task. - - High level goal: - {goals} - - {task_instructions} - - You have following incomplete tasks `{pending_tasks}`. You have following completed tasks `{completed_tasks}`. - - Task History: - `{task_history}` - - Based on this, create a single task to be completed by your AI system ONLY IF REQUIRED to get closer to or fully reach your high level goal. - Don't create any task if it is already covered in incomplete or completed tasks. - Ensure your new task are not deviated from completing the goal. - - Your answer should be an array of strings that can be used with JSON.parse() and NOTHING ELSE. Return empty array if no new task is required. - """ + super_agi_prompt = PromptReader.read_agent_prompt(__file__, "create_tasks.txt") return {"prompt": AgentPromptBuilder.clean_prompt(super_agi_prompt), "variables": ["goals", "instructions", "last_task", "last_task_result", "pending_tasks"]} @classmethod def prioritize_tasks(cls): # just executed task `{last_task}` and got the result `{last_task_result}` - super_agi_prompt = """ - You are a task prioritization AI assistant. - - High level goal: - {goals} - - {task_instructions} - - You have following incomplete tasks `{pending_tasks}`. You have following completed tasks `{completed_tasks}`. - - Based on this, evaluate the incomplete tasks and sort them in the order of execution. In output first task will be executed first and so on. - Remove if any tasks are unnecessary or duplicate incomplete tasks. Remove tasks if they are already covered in completed tasks. - Remove tasks if it does not help in achieving the main goal. - - Your answer should be an array of strings that can be used with JSON.parse() and NOTHING ELSE. - """ + super_agi_prompt = PromptReader.read_agent_prompt(__file__, "prioritize_tasks.txt") return {"prompt": AgentPromptBuilder.clean_prompt(super_agi_prompt), "variables": ["goals", "instructions", "last_task", "last_task_result", "pending_tasks"]} @@ -226,7 +124,7 @@ def replace_main_variables(cls, super_agi_prompt: str, goals: List[str], instruc AgentPromptBuilder.add_list_items_to_string(constraints)) - print(tools) + logger.info(tools) tools_string = AgentPromptBuilder.add_tools_to_prompt(tools, add_finish_tool) super_agi_prompt = super_agi_prompt.replace("{tools}", tools_string) return super_agi_prompt diff --git a/superagi/agent/prompts/analyse_task.txt b/superagi/agent/prompts/analyse_task.txt new file mode 100644 index 000000000..43f4b80c2 --- /dev/null +++ b/superagi/agent/prompts/analyse_task.txt @@ -0,0 +1,26 @@ +High level goal: +{goals} + +{task_instructions} + +Your Current Task: `{current_task}` + +Task History: +`{task_history}` + +Based on this, your job is to understand the current task, pick out key parts, and think smart and fast. +Explain why you are doing each action, create a plan, and mention any worries you might have. +Ensure next action tool is picked from the below tool list. + +TOOLS: +{tools} + +RESPONSE FORMAT: +{ + "thoughts": { + "reasoning": "reasoning" + }, + "tool": {"name": "tool name", "args": {"arg name": "string value"}} +} + +Your answer must be something that JSON.parse() can read, and nothing else. \ No newline at end of file diff --git a/superagi/agent/prompts/create_tasks.txt b/superagi/agent/prompts/create_tasks.txt new file mode 100644 index 000000000..b37299717 --- /dev/null +++ b/superagi/agent/prompts/create_tasks.txt @@ -0,0 +1,17 @@ +You are an AI assistant to create task. + +High level goal: +{goals} + +{task_instructions} + +You have following incomplete tasks `{pending_tasks}`. You have following completed tasks `{completed_tasks}`. + +Task History: +`{task_history}` + +Based on this, create a single task to be completed by your AI system ONLY IF REQUIRED to get closer to or fully reach your high level goal. +Don't create any task if it is already covered in incomplete or completed tasks. +Ensure your new task are not deviated from completing the goal. + +Your answer should be an array of strings that can be used with JSON.parse() and NOTHING ELSE. Return empty array if no new task is required. \ No newline at end of file diff --git a/superagi/agent/prompts/initialize_tasks.txt b/superagi/agent/prompts/initialize_tasks.txt new file mode 100644 index 000000000..754faf7fb --- /dev/null +++ b/superagi/agent/prompts/initialize_tasks.txt @@ -0,0 +1,12 @@ +You are a task-generating AI known as SuperAGI. You are not a part of any system or device. Your role is to understand the goals presented to you, identify important components, Go through the instruction provided by the user and construct a thorough execution plan. + +GOALS: +{goals} + +{task_instructions} + +Construct a sequence of actions, not exceeding 3 steps, to achieve this goal. + +Submit your response as a formatted ARRAY of strings, suitable for utilization with JSON.parse(). + +Example: ["{{TASK-1}}", "{{TASK-2}}"]. \ No newline at end of file diff --git a/superagi/agent/prompts/prioritize_tasks.txt b/superagi/agent/prompts/prioritize_tasks.txt new file mode 100644 index 000000000..f86138c76 --- /dev/null +++ b/superagi/agent/prompts/prioritize_tasks.txt @@ -0,0 +1,14 @@ +You are a task prioritization AI assistant. + +High level goal: +{goals} + +{task_instructions} + +You have following incomplete tasks `{pending_tasks}`. You have following completed tasks `{completed_tasks}`. + +Based on this, evaluate the incomplete tasks and sort them in the order of execution. In output first task will be executed first and so on. +Remove if any tasks are unnecessary or duplicate incomplete tasks. Remove tasks if they are already covered in completed tasks. +Remove tasks if it does not help in achieving the main goal. + +Your answer should be an array of strings that can be used with JSON.parse() and NOTHING ELSE. \ No newline at end of file diff --git a/superagi/agent/prompts/superagi.txt b/superagi/agent/prompts/superagi.txt new file mode 100644 index 000000000..213c27614 --- /dev/null +++ b/superagi/agent/prompts/superagi.txt @@ -0,0 +1,28 @@ +You are SuperAGI an AI assistant to solve complex problems. Your decisions must always be made independently without seeking user assistance. +Play to your strengths as an LLM and pursue simple strategies with no legal complications. +If you have completed all your tasks or reached end state, make sure to use the "finish" tool. + +GOALS: +{goals} + +{instructions} + +CONSTRAINTS: +{constraints} + +TOOLS: +{tools} + +PERFORMANCE EVALUATION: +1. Continuously review and analyze your actions to ensure you are performing to the best of your abilities. +2. Use instruction to decide the flow of execution and decide the next steps for achieving the task. +2. Constructively self-criticize your big-picture behavior constantly. +3. Reflect on past decisions and strategies to refine your approach. +4. Every tool has a cost, so be smart and efficient. +5. Aim to complete tasks in the least number of steps. + +I should only respond in JSON format as described below. +Response Format: +{response_format} + +Ensure the response can be parsed by Python json.loads. \ No newline at end of file diff --git a/superagi/controllers/agent.py b/superagi/controllers/agent.py index 158104ac5..5d3fb2f84 100644 --- a/superagi/controllers/agent.py +++ b/superagi/controllers/agent.py @@ -157,9 +157,13 @@ def create_agent_with_config(agent_with_config: AgentWithConfig, if tool is None: # Tool does not exist, throw 404 or handle as desired raise HTTPException(status_code=404, detail=f"Tool with ID {tool_id} does not exist. 404 Not Found.") + + agent_toolkit_tools = AgentConfiguration.get_tools_from_agent_config(session=db.session, + agent_with_config=agent_with_config) + agent_with_config.tools.extend(agent_toolkit_tools) db_agent = Agent.create_agent_with_config(db, agent_with_config) start_step_id = AgentWorkflow.fetch_trigger_step_id(db.session, db_agent.agent_workflow_id) - # Creating an execution with CREATED status + # Creating an execution with RUNNING status execution = AgentExecution(status='RUNNING', last_execution_time=datetime.now(), agent_id=db_agent.id, name="New Run", current_step_id=start_step_id) diff --git a/superagi/controllers/organisation.py b/superagi/controllers/organisation.py index 17af35318..f4e984293 100644 --- a/superagi/controllers/organisation.py +++ b/superagi/controllers/organisation.py @@ -5,6 +5,7 @@ from pydantic_sqlalchemy import sqlalchemy_to_pydantic from superagi.helper.auth import check_auth +from superagi.helper.tool_helper import register_toolkits from superagi.models.organisation import Organisation from superagi.models.project import Project from superagi.models.user import User @@ -38,6 +39,7 @@ def create_organisation(organisation: sqlalchemy_to_pydantic(Organisation, exclu db.session.add(new_organisation) db.session.commit() db.session.flush() + register_toolkits(session=db.session, organisation=new_organisation) logger.info(new_organisation) return new_organisation @@ -97,7 +99,7 @@ def update_organisation(organisation_id: int, organisation: sqlalchemy_to_pydant @router.get("/get/user/{user_id}", response_model=sqlalchemy_to_pydantic(Organisation), status_code=201) def get_organisations_by_user(user_id: int): """ - Get organisations associated with a user. + Get organisations associated with a user.If Organisation does not exists a new organisation is created Args: user_id: ID of the user. diff --git a/superagi/controllers/tool.py b/superagi/controllers/tool.py index ed2106a9c..2d42491dc 100644 --- a/superagi/controllers/tool.py +++ b/superagi/controllers/tool.py @@ -4,8 +4,10 @@ from fastapi_sqlalchemy import db from pydantic_sqlalchemy import sqlalchemy_to_pydantic -from superagi.helper.auth import check_auth +from superagi.helper.auth import check_auth, get_user_organisation +from superagi.models.organisation import Organisation from superagi.models.tool import Tool +from superagi.models.toolkit import Toolkit router = APIRouter() @@ -66,6 +68,18 @@ def get_tool( return db_tool +@router.get("/get") +def get_tools( + organisation: Organisation = Depends(get_user_organisation)): + """Get all tools""" + toolkits = db.session.query(Toolkit).filter(Toolkit.organisation_id == organisation.id) + tools = [] + for toolkit in toolkits: + db_tools = db.session.query(Tool).filter(Toolkit.id == toolkit.id).all() + tools.extend(db_tools) + return tools + + @router.put("/update/{tool_id}", response_model=sqlalchemy_to_pydantic(Tool)) def update_tool( tool_id: int, @@ -99,17 +113,3 @@ def update_tool( db.session.add(db_tool) db.session.commit() return db_tool - - -@router.get("/get") -def get_tools(Authorize: AuthJWT = Depends(check_auth)): - """ - Get all tools. - - Returns: - List[Tool]: List of tools. - - """ - - db_tools = db.session.query(Tool).all() - return db_tools diff --git a/superagi/controllers/tool_config.py b/superagi/controllers/tool_config.py new file mode 100644 index 000000000..92c1b1c04 --- /dev/null +++ b/superagi/controllers/tool_config.py @@ -0,0 +1,160 @@ +from fastapi import APIRouter, HTTPException, Depends, Path +from fastapi_sqlalchemy import db +from pydantic_sqlalchemy import sqlalchemy_to_pydantic +from superagi.models.organisation import Organisation +from superagi.models.tool_config import ToolConfig +from superagi.models.toolkit import Toolkit +from fastapi_jwt_auth import AuthJWT +from superagi.helper.auth import check_auth +from superagi.helper.auth import get_user_organisation +from typing import List + +router = APIRouter() + + +@router.post("/add/{toolkit_name}", status_code=201) +def update_tool_config(toolkit_name: str, configs: list): + """ + Update tool configurations for a specific tool kit. + + Args: + toolkit_name (str): The name of the tool kit. + configs (list): A list of dictionaries containing the tool configurations. + Each dictionary should have the following keys: + - "key" (str): The key of the configuration. + - "value" (str): The new value for the configuration. + + Returns: + dict: A dictionary with the message "Tool configs updated successfully". + + Raises: + HTTPException (status_code=404): If the specified tool kit is not found. + HTTPException (status_code=500): If an unexpected error occurs during the update process. + """ + + try: + # Check if the tool kit exists + toolkit = Toolkit.get_toolkit_from_name(db.session, toolkit_name) + if toolkit is None: + raise HTTPException(status_code=404, detail="Tool kit not found") + + # Update existing tool configs + for config in configs: + key = config.get("key") + value = config.get("value") + if key is not None: + tool_config = db.session.query(ToolConfig).filter_by(toolkit_id=toolkit.id, key=key).first() + if tool_config: + # Update existing tool config + tool_config.value = value + db.session.commit() + + return {"message": "Tool configs updated successfully"} + + except Exception as e: + # db.session.rollback() + raise HTTPException(status_code=500, detail=str(e)) + + +@router.post("/create-or-update/{toolkit_name}", status_code=201, response_model=sqlalchemy_to_pydantic(ToolConfig)) +def create_or_update_tool_config(toolkit_name: str, tool_configs, + Authorize: AuthJWT = Depends(check_auth)): + """ + Create or update tool configurations for a specific tool kit. + + Args: + toolkit_name (str): The name of the tool kit. + tool_configs (list): A list of tool configuration objects. + + Returns: + Toolkit: The updated tool kit object. + + Raises: + HTTPException (status_code=404): If the specified tool kit is not found. + """ + + toolkit = db.session.query(Toolkit).filter_by(name=toolkit_name).first() + if not toolkit: + raise HTTPException(status_code=404, detail='ToolKit not found') + + # Iterate over the tool_configs list + for tool_config_data in tool_configs: + existing_tool_config = db.session.query(ToolConfig).filter( + ToolConfig.toolkit_id == toolkit.id, + ToolConfig.key == tool_config_data.key + ).first() + + if existing_tool_config: + # Update the existing tool config + existing_tool_config.value = tool_config_data.value + else: + # Create a new tool config + new_tool_config = ToolConfig(key=tool_config_data.key, value=tool_config_data.value, toolkit_id=toolkit.id) + db.session.add(new_tool_config) + + db.session.commit() + db.session.refresh(toolkit) + + return toolkit + + +@router.get("/get/toolkit/{toolkit_name}", status_code=200) +def get_all_tool_configs(toolkit_name: str, organisation: Organisation = Depends(get_user_organisation)): + """ + Get all tool configurations by Tool Kit Name. + + Args: + toolkit_name (str): The name of the tool kit. + organisation (Organisation): The organization associated with the user. + + Returns: + list: A list of tool configurations for the specified tool kit. + + Raises: + HTTPException (status_code=404): If the specified tool kit is not found. + HTTPException (status_code=403): If the user is not authorized to access the tool kit. + """ + user_toolkits = db.session.query(Toolkit).filter(Toolkit.organisation_id == organisation.id).all() + toolkit = db.session.query(Toolkit).filter_by(name=toolkit_name).first() + if not toolkit: + raise HTTPException(status_code=404, detail='ToolKit not found') + if toolkit.name not in [user_toolkit.name for user_toolkit in user_toolkits]: + raise HTTPException(status_code=403, detail='Unauthorized') + + tool_configs = db.session.query(ToolConfig).filter(ToolConfig.toolkit_id == toolkit.id).all() + return tool_configs + + +@router.get("/get/toolkit/{toolkit_name}/key/{key}", status_code=200) +def get_tool_config(toolkit: str, key: str, organisation: Organisation = Depends(get_user_organisation)): + """ + Get a specific tool configuration by tool kit name and key. + + Args: + toolkit_name (str): The name of the tool kit. + key (str): The key of the tool configuration. + organisation (Organisation): The organization associated with the user. + + Returns: + ToolConfig: The tool configuration with the specified key. + + Raises: + HTTPException (status_code=403): If the user is not authorized to access the tool kit. + HTTPException (status_code=404): If the specified tool kit or tool configuration is not found. + """ + + user_toolkits = db.session.query(Toolkit).filter(Toolkit.organisation_id == organisation.id).all() + + toolkit = db.session.query(Toolkit).filter_by(name=toolkit) + if toolkit not in user_toolkits: + raise HTTPException(status_code=403, detail='Unauthorized') + + tool_config = db.session.query(ToolConfig).filter( + ToolConfig.toolkit_id == toolkit.id, + ToolConfig.key == key + ).first() + + if not tool_config: + raise HTTPException(status_code=404, detail="Tool configuration not found") + + return tool_config diff --git a/superagi/controllers/toolkit.py b/superagi/controllers/toolkit.py new file mode 100644 index 000000000..ef6dcdf0e --- /dev/null +++ b/superagi/controllers/toolkit.py @@ -0,0 +1,288 @@ +from typing import Optional + +import requests +from fastapi import APIRouter, Body +from fastapi import HTTPException, Depends, Query +from fastapi_sqlalchemy import db +from superagi.config.config import get_config +from superagi.helper.auth import get_user_organisation +from superagi.helper.tool_helper import get_readme_content_from_code_link, download_tool,process_files,add_tool_to_json +from superagi.helper.github_helper import GithubHelper +from superagi.models.organisation import Organisation +from superagi.models.tool import Tool +from superagi.models.toolkit import Toolkit +from superagi.types.common import GitHubLinkRequest + +router = APIRouter() + + +# marketplace_url = "https://app.superagi.com/api" +# marketplace_url = "http://localhost:8001/" + + +#For internal use +@router.get("/marketplace/list/{page}") +def get_marketplace_toolkits( + page: int = 0, +): + """ + Get marketplace tool kits. + + Args: + page (int): The page number for pagination. + + Returns: + list: A list of tool kits in the marketplace. + + """ + + organisation_id = int(get_config("MARKETPLACE_ORGANISATION_ID")) + page_size = 30 + + # Apply search filter if provided + query = db.session.query(Toolkit).filter(Toolkit.organisation_id == organisation_id) + + # Paginate the results + toolkits = query.offset(page * page_size).limit(page_size).all() + + # Fetch tools for each tool kit + for toolkit in toolkits: + toolkit.tools = db.session.query(Tool).filter(Tool.toolkit_id == toolkit.id).all() + + return toolkits + +#For internal use +@router.get("/marketplace/details/{toolkit_name}") +def get_marketplace_toolkit_detail(toolkit_name: str): + """ + Get tool kit details from the marketplace. + + Args: + toolkit_name (str): The name of the tool kit. + + Returns: + Toolkit: The tool kit details from the marketplace. + + """ + + organisation_id = int(get_config("MARKETPLACE_ORGANISATION_ID")) + toolkit = db.session.query(Toolkit).filter(Toolkit.organisation_id == organisation_id, Toolkit.name == toolkit_name).first() + return toolkit + +#For internal use +@router.get("/marketplace/readme/{toolkit_name}") +def get_marketplace_toolkit_readme(toolkit_name: str): + """ + Get tool kit readme from the marketplace. + + Args: + toolkit_name (str): The name of the tool kit. + + Returns: + str: The content of the tool kit's readme file. + + Raises: + HTTPException (status_code=404): If the specified tool kit is not found. + + """ + + organisation_id = int(get_config("MARKETPLACE_ORGANISATION_ID")) + toolkit = db.session.query(Toolkit).filter(Toolkit.name == toolkit_name, + Toolkit.organisation_id == organisation_id).first() + if not toolkit: + raise HTTPException(status_code=404, detail='ToolKit not found') + return get_readme_content_from_code_link(toolkit.tool_code_link) + +#For internal use +@router.get("/marketplace/tools/{toolkit_name}") +def get_marketplace_toolkit_tools(toolkit_name: str): + """ + Get tools of a specific tool kit from the marketplace. + + Args: + toolkit_name (str): The name of the tool kit. + + Returns: + Tool: The tools associated with the tool kit. + + Raises: + HTTPException (status_code=404): If the specified tool kit is not found. + + """ + + organisation_id = int(get_config("MARKETPLACE_ORGANISATION_ID")) + toolkit = db.session.query(Toolkit).filter(Toolkit.name == toolkit_name, Toolkit.organisation_id == organisation_id).first() + if not toolkit: + raise HTTPException(status_code=404, detail="ToolKit not found") + tools = db.session.query(Tool).filter(Tool.toolkit_id == toolkit.id).first() + return tools + + +@router.get("/get/install/{toolkit_name}") +def install_toolkit_from_marketplace(toolkit_name: str, + organisation: Organisation = Depends(get_user_organisation)): + """ + Download and install a tool kit from the marketplace. + + Args: + toolkit_name (str): The name of the tool kit. + organisation (Organisation): The user's organisation. + + Returns: + dict: A message indicating the successful installation of the tool kit. + + """ + + # Check if the tool kit exists + toolkit = Toolkit.fetch_marketplace_detail(search_str="details", + toolkit_name=toolkit_name) + # download_and_install_tool(GitHubLinkRequest(github_link=toolkit['tool_code_link']), + # organisation=organisation) + if not GithubHelper.validate_github_link(toolkit['tool_code_link']): + raise HTTPException(status_code=400, detail="Invalid Github link") + add_tool_to_json(toolkit['tool_code_link']) + return {"message": "ToolKit installed successfully"} + + +@router.get("/get/toolkit_name/{toolkit_name}") +def get_installed_toolkit_details(toolkit_name: str, + organisation: Organisation = Depends(get_user_organisation)): + """ + Get details of a locally installed tool kit by its name, including the details of its tools. + + Args: + toolkit_name (str): The name of the tool kit. + organisation (Organisation): The user's organisation. + + Returns: + Toolkit: The tool kit object with its associated tools. + + Raises: + HTTPException (status_code=404): If the specified tool kit is not found. + + """ + + # Fetch the tool kit by its ID + toolkit = db.session.query(Toolkit).filter(Toolkit.name == toolkit_name, + Organisation.id == organisation.id).first() + + if not toolkit: + # Return an appropriate response if the tool kit doesn't exist + raise HTTPException(status_code=404, detail='ToolKit not found') + + # Fetch the tools associated with the tool kit + tools = db.session.query(Tool).filter(Tool.toolkit_id == toolkit.id).all() + # Add the tools to the tool kit object + toolkit.tools = tools + # readme_content = get_readme(toolkit.tool_code_link) + return toolkit + + +@router.post("/get/local/install", status_code=200) +def download_and_install_tool(github_link_request: GitHubLinkRequest = Body(...), + organisation: Organisation = Depends(get_user_organisation)): + """ + Install a tool locally from a GitHub link. + + Args: + github_link_request (GitHubLinkRequest): The GitHub link request object. + organisation (Organisation): The user's organisation. + + Returns: + None + + Raises: + HTTPException (status_code=400): If the GitHub link is invalid. + + """ + github_link = github_link_request.github_link + if not GithubHelper.validate_github_link(github_link): + raise HTTPException(status_code=400, detail="Invalid Github link") + # download_folder = get_config("TOOLS_DIR") + # download_tool(github_link, download_folder) + # process_files(download_folder, db.session, organisation, code_link=github_link) + add_tool_to_json(github_link) + +@router.get("/get/readme/{toolkit_name}") +def get_installed_toolkit_readme(toolkit_name: str, organisation: Organisation = Depends(get_user_organisation)): + """ + Get the readme content of a toolkit. + + Args: + toolkit_name (str): The name of the toolkit. + organisation (Organisation): The user's organisation. + + Returns: + str: The readme content of the toolkit. + + Raises: + HTTPException (status_code=404): If the toolkit is not found. + + """ + + toolkit = db.session.query(Toolkit).filter(Toolkit.name == toolkit_name, + Organisation.id == organisation.id).first() + if not toolkit: + raise HTTPException(status_code=404, detail='ToolKit not found') + readme_content = get_readme_content_from_code_link(toolkit.tool_code_link) + return readme_content + +# Following APIs will be used to get marketplace related information +@router.get("/get") +def handle_marketplace_operations( + search_str: str = Query(None, title="Search String"), + toolkit_name: str = Query(None, title="Tool Kit Name") +): + """ + Handle marketplace operations. + + Args: + search_str (str, optional): The search string to filter toolkits. Defaults to None. + toolkit_name (str, optional): The name of the toolkit. Defaults to None. + + Returns: + dict: The response containing the marketplace details. + + """ + response = Toolkit.fetch_marketplace_detail(search_str, toolkit_name) + return response + + +@router.get("/get/list") +def handle_marketplace_operations_list( + page: int = Query(None, title="Page Number"), +): + """ + Handle marketplace operation list. + + Args: + page (int, optional): The page number for pagination. Defaults to None. + + Returns: + dict: The response containing the marketplace list. + + """ + + response = Toolkit.fetch_marketplace_list(page=page) + return response + + +@router.get("/get/local/list") +def get_installed_toolkit_list(organisation: Organisation = Depends(get_user_organisation)): + """ + Get the list of installed tool kits. + + Args: + organisation (Organisation): The organisation associated with the tool kits. + + Returns: + list: The list of installed tool kits. + + """ + + toolkits = db.session.query(Toolkit).filter(Toolkit.organisation_id == organisation.id).all() + for toolkit in toolkits: + toolkit_tools = db.session.query(Tool).filter(Tool.toolkit_id == toolkit.id).all() + toolkit.tools = toolkit_tools + + return toolkits \ No newline at end of file diff --git a/superagi/helper/auth.py b/superagi/helper/auth.py index a44d11004..5a80677e6 100644 --- a/superagi/helper/auth.py +++ b/superagi/helper/auth.py @@ -1,8 +1,8 @@ -from fastapi import Depends +from fastapi import Depends, HTTPException from fastapi_jwt_auth import AuthJWT +from fastapi_sqlalchemy import db from superagi.config.config import get_config -from fastapi_sqlalchemy import db from superagi.models.organisation import Organisation from superagi.models.user import User @@ -43,5 +43,7 @@ def get_user_organisation(Authorize: AuthJWT = Depends(check_auth)): # Query the User table to find the user by their email user = db.session.query(User).filter(User.email == email).first() + if user is None: + raise HTTPException(status_code=401, detail="Unauthenticated") organisation = db.session.query(Organisation).filter(Organisation.id == user.organisation_id).first() return organisation \ No newline at end of file diff --git a/superagi/helper/github_helper.py b/superagi/helper/github_helper.py index 877e46b43..cdf1d7e62 100644 --- a/superagi/helper/github_helper.py +++ b/superagi/helper/github_helper.py @@ -1,4 +1,6 @@ import base64 +import re + import requests from superagi.lib.logger import logger @@ -107,7 +109,8 @@ def sync_branch(self, repository_owner, repository_name, base_branch, head_branc } response = requests.patch(head_branch_url, json=data, headers=headers) if response.status_code == 200: - logger.info(f'Successfully synced {self.github_username}:{head_branch} branch with {repository_owner}:{base_branch}') + logger.info( + f'Successfully synced {self.github_username}:{head_branch} branch with {repository_owner}:{base_branch}') else: logger.info('Failed to sync the branch. Check your inputs and permissions.') @@ -299,3 +302,18 @@ def get_content_in_file(self, repository_owner, repository_name, file_name, fold file_content = base64.b64decode(file_content).decode() return file_content + + @classmethod + def validate_github_link(cls, link: str) -> bool: + """ + Validate a GitHub link. + Returns True if the link is valid, False otherwise. + """ + # Regular expression pattern to match a GitHub link + pattern = r'^https?://(?:www\.)?github\.com/[\w\-]+/[\w\-]+$' + + # Check if the link matches the pattern + if re.match(pattern, link): + return True + + return False diff --git a/superagi/helper/imap_email.py b/superagi/helper/imap_email.py index cb93d2e0d..11d07cdf6 100644 --- a/superagi/helper/imap_email.py +++ b/superagi/helper/imap_email.py @@ -1,10 +1,9 @@ import imaplib -from superagi.config.config import get_config class ImapEmail: - def imap_open(self, imap_folder, email_sender, email_password) -> imaplib.IMAP4_SSL: + def imap_open(self, imap_folder, email_sender, email_password, imap_server) -> imaplib.IMAP4_SSL: """ Function to open an IMAP connection to the email server. @@ -16,7 +15,6 @@ def imap_open(self, imap_folder, email_sender, email_password) -> imaplib.IMAP4_ Returns: imaplib.IMAP4_SSL: The IMAP connection. """ - imap_server = get_config('EMAIL_IMAP_SERVER') conn = imaplib.IMAP4_SSL(imap_server) conn.login(email_sender, email_password) conn.select(imap_folder) diff --git a/superagi/helper/prompt_reader.py b/superagi/helper/prompt_reader.py new file mode 100644 index 000000000..08339a809 --- /dev/null +++ b/superagi/helper/prompt_reader.py @@ -0,0 +1,25 @@ +from pathlib import Path + + +class PromptReader: + @staticmethod + def read_tools_prompt(current_file: str, prompt_file: str) -> str: + file_path = str(Path(current_file).resolve().parent) + "/prompts/" + prompt_file + try: + f = open(file_path, "r") + file_content = f.read() + except FileNotFoundError as e: + print(e.__str__()) + raise e + return file_content + + @staticmethod + def read_agent_prompt(current_file: str, prompt_file: str) -> str: + file_path = str(Path(current_file).resolve().parent) + "/prompts/" + prompt_file + try: + f = open(file_path, "r") + file_content = f.read() + except FileNotFoundError as e: + print(e.__str__()) + raise e + return file_content diff --git a/superagi/helper/tool_helper.py b/superagi/helper/tool_helper.py new file mode 100644 index 000000000..ae70ad5b5 --- /dev/null +++ b/superagi/helper/tool_helper.py @@ -0,0 +1,271 @@ +import importlib.util +import inspect +import json +import os +import sys +import zipfile +from urllib.parse import urlparse + +import requests + +from superagi.config.config import get_config +from superagi.lib.logger import logger +from superagi.models.tool import Tool +from superagi.models.tool_config import ToolConfig +from superagi.models.toolkit import Toolkit +from superagi.tools.base_tool import BaseTool +from superagi.tools.base_tool import BaseToolkit + + +def parse_github_url(github_url): + parts = github_url.split('/') + owner = parts[3] + repo = parts[4] + branch = "main" + return f"{owner}/{repo}/{branch}" + + +def download_tool(tool_url, target_folder): + parsed_url = parse_github_url(tool_url) + parts = parsed_url.split("/") + path = "/" + owner, repo, branch = parts[0], parts[1], parts[2] + archive_url = f"https://api.github.com/repos/{owner}/{repo}/zipball/{branch}" + response = requests.get(archive_url) + tool_zip_file_path = os.path.join(target_folder, 'tool.zip') + + with open(tool_zip_file_path, 'wb') as f: + f.write(response.content) + + logger.info("Reading Zip") + with zipfile.ZipFile(tool_zip_file_path, 'r') as z: + members = [m for m in z.namelist() if m.startswith(f"{owner}-{repo}") and f"{path}" in m] + + # Extract only folders in the root directory + root_folders = [member for member in members if member.count('/') > 1] + for member in root_folders: + archive_folder = f"{owner}-{repo}" + target_name = member.replace(f"{archive_folder}/", "", 1) + + # Skip the unique hash folder while extracting: + segments = target_name.split('/', 1) + if len(segments) > 1: + target_name = segments[1] + else: + continue + + target_path = os.path.join(target_folder, target_name) + + if not target_name: + continue + + if member.endswith('/'): + os.makedirs(target_path, exist_ok=True) + else: + with open(target_path, 'wb') as outfile, z.open(member) as infile: + outfile.write(infile.read()) + logger.info("Donwload Success!") + os.remove(tool_zip_file_path) + + +def get_classes_in_file(file_path, clazz): + classes = [] + + module = load_module_from_file(file_path) + + for name, member in inspect.getmembers(module): + if inspect.isclass(member) and issubclass(member, clazz) and member != clazz: + class_dict = {} + class_dict['class_name'] = member.__name__ + + class_obj = getattr(module, member.__name__) + try: + obj = class_obj() + if clazz == BaseToolkit: + get_toolkit_info(class_dict, classes, obj) + elif clazz == BaseTool: + get_tool_info(class_dict, classes, obj) + except: + class_dict = None + return classes + + +def get_tool_info(class_dict, classes, obj): + """ + Get tool information from an object. + """ + class_dict['tool_name'] = obj.name + class_dict['tool_description'] = obj.description + classes.append(class_dict) + + +def get_toolkit_info(class_dict, classes, obj): + """ + Get toolkit information from an object. + """ + class_dict['toolkit_name'] = obj.name + class_dict['toolkit_description'] = obj.description + class_dict['toolkit_tools'] = obj.get_tools() + class_dict['toolkit_keys'] = obj.get_env_keys() + classes.append(class_dict) + + +def load_module_from_file(file_path): + spec = importlib.util.spec_from_file_location("module_name", file_path) + module = importlib.util.module_from_spec(spec) + spec.loader.exec_module(module) + + return module + + +def init_tools(folder_path, session, tool_name_to_toolkit): + # Iterate over all subfolders + for folder_name in os.listdir(folder_path): + folder_dir = os.path.join(folder_path, folder_name) + # Iterate over all files in the subfolder + if os.path.isdir(folder_dir): + # sys.path.append(os.path.abspath('superagi/tools/email')) + sys.path.append(folder_dir) + for file_name in os.listdir(folder_dir): + file_path = os.path.join(folder_dir, file_name) + if file_name.endswith(".py") and not file_name.startswith("__init__"): + # Get classes + classes = get_classes_in_file(file_path=file_path, clazz=BaseTool) + update_base_tool_class_info(classes, file_name, folder_name, session, tool_name_to_toolkit) + + +def update_base_tool_class_info(classes, file_name, folder_name, session, tool_name_to_toolkit): + for clazz in classes: + if clazz["class_name"] is not None: + tool_name = clazz['tool_name'] + tool_description = clazz['tool_description'] + toolkit_id = tool_name_to_toolkit.get((tool_name, folder_name), None) + if toolkit_id is not None: + new_tool = Tool.add_or_update(session, tool_name=tool_name, folder_name=folder_name, + class_name=clazz['class_name'], file_name=file_name, + toolkit_id=tool_name_to_toolkit[(tool_name, folder_name)], + description=tool_description) + + +def init_toolkits(code_link, existing_toolkits, folder_path, organisation, session): + tool_name_to_toolkit = {} + new_toolkits = [] + # Iterate over all subfolders + for folder_name in os.listdir(folder_path): + folder_dir = os.path.join(folder_path, folder_name) + + if os.path.isdir(folder_dir): + # sys.path.append(os.path.abspath('superagi/tools/email')) + sys.path.append(folder_dir) + # Iterate over all files in the subfolder + for file_name in os.listdir(folder_dir): + file_path = os.path.join(folder_dir, file_name) + if file_name.endswith(".py") and not file_name.startswith("__init__"): + # Get classes + classes = get_classes_in_file(file_path=file_path, clazz=BaseToolkit) + tool_name_to_toolkit = update_base_toolkit_info(classes, code_link, folder_name, new_toolkits, + organisation, session, tool_name_to_toolkit) + # Delete toolkits that are not present in the updated toolkits + delete_extra_toolkit(existing_toolkits, new_toolkits, session) + return tool_name_to_toolkit + + +def delete_extra_toolkit(existing_toolkits, new_toolkits, session): + for toolkit in existing_toolkits: + if toolkit.name not in [new_toolkit.name for new_toolkit in new_toolkits]: + session.query(Tool).filter(Tool.toolkit_id == toolkit.id).delete() + session.query(ToolConfig).filter(ToolConfig.toolkit_id == toolkit.id).delete() + session.delete(toolkit) + # Commit the changes to the database + session.commit() + + +def update_base_toolkit_info(classes, code_link, folder_name, new_toolkits, organisation, session, + tool_name_to_toolkit): + for clazz in classes: + if clazz["class_name"] is not None: + toolkit_name = clazz["toolkit_name"] + toolkit_description = clazz["toolkit_description"] + tools = clazz["toolkit_tools"] + tool_config_keys = clazz["toolkit_keys"] + # Create a new ToolKit object + new_toolkit = Toolkit.add_or_update( + session, + name=toolkit_name, + description=toolkit_description, + show_toolkit=True if len(tools) > 1 else False, + organisation_id=organisation.id, + tool_code_link=code_link + ) + new_toolkits.append(new_toolkit) + tool_mapping = {} + # Store the tools in the database + for tool in tools: + new_tool = Tool.add_or_update(session, tool_name=tool.name, folder_name=folder_name, + class_name=None, file_name=None, + toolkit_id=new_toolkit.id, description=tool.description) + tool_mapping[tool.name, folder_name] = new_toolkit.id + tool_name_to_toolkit = {**tool_mapping, **tool_name_to_toolkit} + + # Store the tools config in the database + for tool_config_key in tool_config_keys: + new_config = ToolConfig.add_or_update(session, toolkit_id=new_toolkit.id, + key=tool_config_key) + return tool_name_to_toolkit + + +def process_files(folder_path, session, organisation, code_link=None): + existing_toolkits = session.query(Toolkit).filter(Toolkit.organisation_id == organisation.id).all() + + tool_name_to_toolkit = init_toolkits(code_link, existing_toolkits, folder_path, organisation, session) + init_tools(folder_path, session, tool_name_to_toolkit) + + +def get_readme_content_from_code_link(tool_code_link): + parsed_url = urlparse(tool_code_link) + path_parts = parsed_url.path.split("/") + + # Extract username, repository, and branch from the URL + username = path_parts[1] + repository = path_parts[2] + branch = path_parts[4] if len(path_parts) > 4 else "main" + + readme_url = f"https://raw.githubusercontent.com/{username}/{repository}/{branch}/README.MD" + response = requests.get(readme_url) + if response.status_code == 404: + readme_url = f"https://raw.githubusercontent.com/{username}/{repository}/{branch}/README.md" + response = requests.get(readme_url) + readme_content = response.text + return readme_content + + +def register_toolkits(session, organisation): + folder_path = get_config("TOOLS_DIR") + if folder_path is None: + folder_path = "superagi/tools" + if organisation is not None: + process_files(folder_path, session, organisation) + + +def extract_repo_name(repo_link): + # Extract the repository name from the link + # Assuming the GitHub link format: https://github.com/username/repoName + repo_name = repo_link.rsplit('/', 1)[-1] + + return repo_name + + +def add_tool_to_json(repo_link): + # Read the content of the tools.json file + with open('tools.json', 'r') as file: + tools_data = json.load(file) + + # Extract the repository name from the link + repo_name = extract_repo_name(repo_link) + + # Add a new key-value pair to the tools object + tools_data['tools'][repo_name] = repo_link + + # Write the updated JSON object back to tools.json + with open('tools.json', 'w') as file: + json.dump(tools_data, file, indent=2) \ No newline at end of file diff --git a/superagi/jobs/agent_executor.py b/superagi/jobs/agent_executor.py index 8253b89dc..53ecdc33a 100644 --- a/superagi/jobs/agent_executor.py +++ b/superagi/jobs/agent_executor.py @@ -1,11 +1,14 @@ import importlib from datetime import datetime, timedelta -from fastapi import HTTPException +from fastapi import HTTPException from sqlalchemy.orm import sessionmaker -from superagi import worker +import superagi.worker from superagi.agent.super_agi import SuperAgi +from superagi.config.config import get_config +from superagi.helper.encyption_helper import decrypt_data +from superagi.lib.logger import logger from superagi.llms.openai import OpenAi from superagi.models.agent import Agent from superagi.models.agent_execution import AgentExecution @@ -17,19 +20,34 @@ from superagi.models.organisation import Organisation from superagi.models.project import Project from superagi.models.tool import Tool +from superagi.models.tool_config import ToolConfig +from superagi.tools.base_tool import BaseToolkitConfiguration from superagi.resource_manager.manager import ResourceManager from superagi.tools.thinking.tools import ThinkingTool from superagi.tools.tool_response_query_manager import ToolResponseQueryManager from superagi.vector_store.embedding.openai import OpenAiEmbedding from superagi.vector_store.vector_factory import VectorFactory -from superagi.helper.encyption_helper import decrypt_data -import superagi.worker -from superagi.lib.logger import logger +import yaml +# from superagi.helper.tool_helper import get_tool_config_by_key engine = connect_db() Session = sessionmaker(bind=engine) +class DBToolkitConfiguration(BaseToolkitConfiguration): + session: Session + toolkit_id: int + + def __init__(self, session=None, toolkit_id=None): + self.session = session + self.toolkit_id = toolkit_id + + def get_tool_config(self, key: str): + tool_config = self.session.query(ToolConfig).filter_by(key=key, toolkit_id=self.toolkit_id).first() + if tool_config: + return tool_config.value + return super().get_tool_config(key=key) + class AgentExecutor: @staticmethod def validate_filename(filename): @@ -47,29 +65,32 @@ def validate_filename(filename): return filename @staticmethod - def create_object(class_name, folder_name, file_name): + def create_object(tool,session): """ - Create an object of a class dynamically. + Create an object of a agent usable tool dynamically. Args: - class_name (str): The name of the class. - folder_name (str): The name of the folder. - file_name (str): The name of the file. + tool (Tool) : Tool object from which agent tool would be made. Returns: - object: The object of the class. + object: The object of the agent usable tool. """ - file_name = AgentExecutor.validate_filename(filename=file_name) - module_name = f"superagi.tools.{folder_name}.{file_name}" + file_name = AgentExecutor.validate_filename(filename=tool.file_name) + + tools_dir = get_config("TOOLS_DIR").rstrip("/") + module_name = ".".join(tools_dir.split("/") + [tool.folder_name, file_name]) + + # module_name = f"superagi.tools.{folder_name}.{file_name}" # Load the module dynamically module = importlib.import_module(module_name) # Get the class from the loaded module - obj_class = getattr(module, class_name) + obj_class = getattr(module, tool.class_name) # Create an instance of the class new_object = obj_class() + new_object.toolkit_config = DBToolkitConfiguration(session=session, toolkit_id=tool.toolkit_id) return new_object @staticmethod @@ -159,15 +180,12 @@ def execute_next_action(self, agent_execution_id): memory = None user_tools = session.query(Tool).filter(Tool.id.in_(parsed_config["tools"])).all() - for tool in user_tools: - tool = AgentExecutor.create_object(tool.class_name, tool.folder_name, tool.file_name) + tool = AgentExecutor.create_object(tool,session) tools.append(tool) - print(user_tools) tools = self.set_default_params_tools(tools, parsed_config, agent_execution.agent_id, model_api_key=model_api_key, session=session) - spawned_agent = SuperAgi(ai_name=parsed_config["name"], ai_role=parsed_config["description"], @@ -201,10 +219,6 @@ def execute_next_action(self, agent_execution_id): superagi.worker.execute_agent.delay(agent_execution_id, datetime.now()) session.close() - # except Exception as exception: - # print("Exception Occured in celery job", exception) - # print(str(exception)) - # finally: engine.dispose() def set_default_params_tools(self, tools, parsed_config, agent_id, model_api_key, session): diff --git a/superagi/models/agent_config.py b/superagi/models/agent_config.py index 4f4e1e9da..bf5d72bd9 100644 --- a/superagi/models/agent_config.py +++ b/superagi/models/agent_config.py @@ -1,6 +1,8 @@ +from fastapi import HTTPException from sqlalchemy import Column, Integer, Text, String from superagi.models.base_model import DBBaseModel +from superagi.models.tool import Tool class AgentConfiguration(DBBaseModel): @@ -30,3 +32,17 @@ def __repr__(self): """ return f"AgentConfiguration(id={self.id}, key={self.key}, value={self.value})" + + @classmethod + def get_tools_from_agent_config(cls, session, agent_with_config): + agent_toolkit_tools = [] + for toolkit_id in agent_with_config.toolkits: + toolkit_tools = session.query(Tool).filter(Tool.toolkit_id == toolkit_id).all() + for tool in toolkit_tools: + tool = session.query(Tool).filter(Tool.id == tool.id).first() + if tool is None: + # Tool does not exist, throw 404 + raise HTTPException(status_code=404, detail=f"Tool does not exist. 404 Not Found.") + else: + agent_toolkit_tools.append(tool.id) + return agent_toolkit_tools diff --git a/superagi/models/organisation.py b/superagi/models/organisation.py index fe1844ab6..994ea2ae9 100644 --- a/superagi/models/organisation.py +++ b/superagi/models/organisation.py @@ -1,4 +1,6 @@ from sqlalchemy import Column, Integer, String + +from superagi.helper.tool_helper import register_toolkits from superagi.models.base_model import DBBaseModel @@ -52,7 +54,6 @@ def find_or_create_organisation(cls, session, user): user.organisation_id = existing_organisation.id session.commit() return existing_organisation - new_organisation = Organisation( name="Default Organization - " + str(user.id), description="New default organiztaion", @@ -63,4 +64,5 @@ def find_or_create_organisation(cls, session, user): session.flush() user.organisation_id = new_organisation.id session.commit() + register_toolkits(session=session, organisation=new_organisation) return new_organisation \ No newline at end of file diff --git a/superagi/models/tool.py b/superagi/models/tool.py index 07b0dbd58..8e2e34260 100644 --- a/superagi/models/tool.py +++ b/superagi/models/tool.py @@ -1,4 +1,5 @@ from sqlalchemy import Column, Integer, String + from superagi.models.base_model import DBBaseModel @@ -20,9 +21,11 @@ class Tool(DBBaseModel): id = Column(Integer, primary_key=True, autoincrement=True) name = Column(String) + description = Column(String) folder_name = Column(String) class_name = Column(String) file_name = Column(String) + toolkit_id = Column(Integer) def __repr__(self): """ @@ -32,7 +35,39 @@ def __repr__(self): str: String representation of the Tool object. """ - return f"Tool(id={self.id}, name='{self.name}', folder_name='{self.folder_name}', class_name='{self.class_name}')" + return f"Tool(id={self.id}, name='{self.name}',description='{self.description}' folder_name='{self.folder_name}'," \ + f" file_name = {self.file_name}, class_name='{self.class_name}, toolkit_id={self.toolkit_id}')" + + @staticmethod + def add_or_update(session, tool_name: str, description: str, folder_name: str, class_name: str, file_name: str, + toolkit_id: int): + # Check if a record with the given tool name already exists inside a toolkit + tool = session.query(Tool).filter_by(name=tool_name, + toolkit_id=toolkit_id).first() + if tool is not None: + # Update the attributes of the existing tool record + tool.folder_name = folder_name + tool.class_name = class_name + tool.file_name = file_name + tool.description = description + else: + # Create a new tool record + tool = Tool(name=tool_name, description=description, folder_name=folder_name, class_name=class_name, + file_name=file_name, + toolkit_id=toolkit_id) + session.add(tool) + + session.commit() + session.flush() + return tool + + @staticmethod + def delete_tool(session, tool_name): + tool = session.query(Tool).filter(Tool.name == tool_name).first() + if tool: + session.delete(tool) + session.commit() + session.flush() @classmethod def convert_tool_names_to_ids(cls, db, tool_names): diff --git a/superagi/models/tool_config.py b/superagi/models/tool_config.py new file mode 100644 index 000000000..d0e3c3566 --- /dev/null +++ b/superagi/models/tool_config.py @@ -0,0 +1,63 @@ +from sqlalchemy import Column, Integer, String +from sqlalchemy.orm import Session, sessionmaker + +from superagi.models.base_model import DBBaseModel +import json +import yaml + + +class ToolConfig(DBBaseModel): + """ + Model representing a tool configuration. + + Attributes: + id (Integer): The primary key of the tool configuration. + key (String): The key of the tool configuration. + value (String): The value of the tool configuration. + toolkit_id (Integer): The identifier of the associated toolkit. + """ + __tablename__ = 'tool_configs' + + + id = Column(Integer, primary_key=True) + key = Column(String) + value = Column(String) + toolkit_id = Column(Integer) + + def __repr__(self): + return f"ToolConfig(id={self.id}, key='{self.key}', value='{self.value}, toolkit_id={self.toolkit_id}')" + + def to_dict(self): + return { + 'id': self.id, + 'key': self.key, + 'value': self.value, + 'toolkit_id': {self.toolkit_id}, + } + + def to_json(self): + return json.dumps(self.to_dict()) + + @classmethod + def from_json(cls, json_data): + data = json.loads(json_data) + return cls( + id=data['id'], + key=data['key'], + value=data['value'], + toolkit_id=data['toolkit_id'], + ) + + @staticmethod + def add_or_update(session: Session, toolkit_id: int, key: str, value: str = None): + tool_config = session.query(ToolConfig).filter_by(toolkit_id=toolkit_id, key=key).first() + if tool_config: + # Update existing tool config + if value is not None: + tool_config.value = value + else: + # Create new tool config + tool_config = ToolConfig(toolkit_id=toolkit_id, key=key, value=value) + session.add(tool_config) + + session.commit() diff --git a/superagi/models/toolkit.py b/superagi/models/toolkit.py new file mode 100644 index 000000000..06c2cdd01 --- /dev/null +++ b/superagi/models/toolkit.py @@ -0,0 +1,115 @@ +import json +import requests +from sqlalchemy import Column, Integer, String, Boolean + +from superagi.models.base_model import DBBaseModel + +# marketplace_url = "https://app.superagi.com/api/" + + +marketplace_url = "http://localhost:8001" + +class Toolkit(DBBaseModel): + """ToolKit - used to store tool kits""" + __tablename__ = 'toolkits' + + id = Column(Integer, primary_key=True) + """id - id of the tool kit""" + name = Column(String) + """name - name of the tool kit""" + description = Column(String) + """description - description of the tool kit""" + show_toolkit = Column(Boolean) + """show_toolkit - indicates whether the tool kit should be shown""" + organisation_id = Column(Integer) + """organisation_id - org id of the to which tool config is related""" + + tool_code_link = Column(String) + """tool_code_link stores the link to the code repo of the tool""" + + def __repr__(self): + return f"ToolKit(id={self.id}, name='{self.name}', description='{self.description}', " \ + f"show_toolkit={self.show_toolkit}," \ + f"organisation_id = {self.organisation_id}" + + def to_dict(self): + return { + 'id': self.id, + 'name': self.name, + 'description': self.description, + 'show_toolkit': self.show_toolkit, + 'organisation_id': self.organisation_id + } + + def to_json(self): + return json.dumps(self.to_dict()) + + @classmethod + def from_json(cls, json_data): + data = json.loads(json_data) + return cls( + id=data['id'], + name=data['name'], + description=data['description'], + show_toolkit=data['show_toolkit'], + organisation_id=data['organisation_id'] + ) + + @staticmethod + def add_or_update(session, name, description, show_toolkit, organisation_id, tool_code_link): + # Check if the toolkit exists + toolkit = session.query(Toolkit).filter(Toolkit.name == name, Toolkit.organisation_id == organisation_id).first() + + if toolkit: + # Update the existing toolkit + toolkit.name = name + toolkit.description = description + toolkit.show_toolkit = show_toolkit + toolkit.organisation_id = organisation_id + toolkit.tool_code_link = tool_code_link + else: + # Create a new toolkit + toolkit = Toolkit( + name=name, + description=description, + show_toolkit=show_toolkit, + organisation_id=organisation_id, + tool_code_link=tool_code_link + ) + + session.add(toolkit) + + session.commit() + session.flush() + return toolkit + + @classmethod + def fetch_marketplace_list(cls, page): + headers = {'Content-Type': 'application/json'} + response = requests.get( + marketplace_url + f"/toolkits/marketplace/list/{str(page)}", + headers=headers, timeout=10) + if response.status_code == 200: + return response.json() + else: + return [] + + @classmethod + def fetch_marketplace_detail(cls, search_str, toolkit_name): + headers = {'Content-Type': 'application/json'} + search_str = search_str.replace(' ', '%20') + toolkit_name = toolkit_name.replace(' ', '%20') + response = requests.get( + marketplace_url + f"/toolkits/marketplace/{search_str}/{toolkit_name}", + headers=headers, timeout=10) + if response.status_code == 200: + return response.json() + else: + return None + + @staticmethod + def get_toolkit_from_name(session, toolkit_name): + toolkit = session.query(Toolkit).filter_by(name=toolkit_name).first() + if toolkit: + return toolkit + return None diff --git a/superagi/models/tools_config.py b/superagi/models/tools_config.py deleted file mode 100644 index 5c1a122a3..000000000 --- a/superagi/models/tools_config.py +++ /dev/null @@ -1,35 +0,0 @@ -from sqlalchemy import Column, Integer, String -from superagi.models.base_model import DBBaseModel - - -# from pydantic import BaseModel - -class ToolConfig(DBBaseModel): - """ - Model representing a tool configuration. - - Attributes: - id (Integer): The primary key of the tool configuration. - name (String): The name of the tool configuration. - key (String): The key of the tool configuration. - value (String): The value of the tool configuration. - agent_id (Integer): The ID of the associated agent. - """ - - __tablename__ = 'tool_configs' - - id = Column(Integer, primary_key=True) - name = Column(String) - key = Column(String) - value = Column(String) - agent_id = Column(Integer) - - def __repr__(self): - """ - Returns a string representation of the ToolConfig object. - - Returns: - str: String representation of the ToolConfig object. - """ - - return f"ToolConfig(id={self.id}, name='{self.name}', key='{self.key}', value='{self.value}', agent_id={self.agent_id})" diff --git a/superagi/models/types/agent_with_config.py b/superagi/models/types/agent_with_config.py index f8024725c..a269a370c 100644 --- a/superagi/models/types/agent_with_config.py +++ b/superagi/models/types/agent_with_config.py @@ -10,6 +10,7 @@ class AgentWithConfig(BaseModel): instruction: List[str] agent_type: str constraints: List[str] + toolkits: List[int] tools: List[int] exit: str iteration_interval: int diff --git a/superagi/resource_manager/manager.py b/superagi/resource_manager/manager.py index 565aa6276..65b816eff 100644 --- a/superagi/resource_manager/manager.py +++ b/superagi/resource_manager/manager.py @@ -53,7 +53,7 @@ def write_file(self, file_name: str, content): file.write(content) file.close() self.write_to_s3(file_name, final_path) - logger.info(f"{file_name} saved successfully") - return f"{file_name} saved successfully" + logger.info(f"{file_name} - File written successfully") + return f"{file_name} - File written successfully" except Exception as err: return f"Error: {err}" diff --git a/superagi/tool_manager.py b/superagi/tool_manager.py new file mode 100644 index 000000000..54b983dd9 --- /dev/null +++ b/superagi/tool_manager.py @@ -0,0 +1,74 @@ +import os +from pathlib import Path + +import requests +import zipfile +import json + +def parse_github_url(github_url): + parts = github_url.split('/') + owner = parts[3] + repo = parts[4] + branch = "main" + return f"{owner}/{repo}/{branch}" + +def download_tool(tool_url, target_folder): + parsed_url = parse_github_url(tool_url) + parts = parsed_url.split("/") + owner, repo, branch, path = parts[0], parts[1], parts[2], "/".join(parts[3:]) + + archive_url = f"https://api.github.com/repos/{owner}/{repo}/zipball/{branch}" + + response = requests.get(archive_url) + + tool_zip_file_path = os.path.join(target_folder, 'tool.zip') + + with open(tool_zip_file_path, 'wb') as f: + f.write(response.content) + + with zipfile.ZipFile(tool_zip_file_path, 'r') as z: + members = [m for m in z.namelist() if m.startswith(f"{owner}-{repo}") and f"{path}" in m] + for member in members: + archive_folder = f"{owner}-{repo}" + target_name = member.replace(f"{archive_folder}/", "", 1) + + # Skip the unique hash folder while extracting: + segments = target_name.split('/', 1) + if len(segments) > 1: + target_name = segments[1] + else: + continue + + target_path = os.path.join(target_folder, target_name) + + if not target_name: + continue + + if member.endswith('/'): + os.makedirs(target_path, exist_ok=True) + else: + with open(target_path, 'wb') as outfile, z.open(member) as infile: + outfile.write(infile.read()) + + os.remove(tool_zip_file_path) + + +def load_tools_config(): + tool_config_path = str(Path(__file__).parent.parent) + with open(tool_config_path + "/tools.json", "r") as f: + config = json.load(f) + return config["tools"] + + +def download_and_extract_tools(): + tools_config = load_tools_config() + + for tool_name, tool_url in tools_config.items(): + tool_folder = os.path.join("", "tools", tool_name) + if not os.path.exists(tool_folder): + os.makedirs(tool_folder) + download_tool(tool_url, tool_folder) + + +if __name__ == "__main__": + download_and_extract_tools() \ No newline at end of file diff --git a/superagi/tools/base_tool.py b/superagi/tools/base_tool.py index 6e8c7677b..54aa49ac0 100644 --- a/superagi/tools/base_tool.py +++ b/superagi/tools/base_tool.py @@ -1,9 +1,11 @@ from abc import abstractmethod from functools import wraps +from inspect import signature +from typing import List from typing import Optional, Type, Callable, Any, Union, Dict, Tuple -from pydantic import BaseModel, Field, create_model, validate_arguments, Extra -from inspect import signature +import yaml +from pydantic import BaseModel, create_model, validate_arguments, Extra from superagi.config.config import get_config @@ -54,21 +56,34 @@ def create_function_schema( ) +class BaseToolkitConfiguration: + + def get_tool_config(key: str): + # Default implementation of the tool configuration retrieval logic + with open("config.yaml") as file: + config = yaml.safe_load(file) + + # Retrieve the value associated with the given key + return config.get(key) + + class BaseTool(BaseModel): name: str = None description: str args_schema: Type[BaseModel] = None permission_required: bool = True + toolkit_config: BaseToolkitConfiguration = BaseToolkitConfiguration() + + class Config: + arbitrary_types_allowed = True @property def args(self): - # print("args_schema", self.args_schema) if self.args_schema is not None: return self.args_schema.schema()["properties"] else: name = self.name args_schema = create_function_schema(f"{name}Schema", self.execute) - # print("args:", args_schema.schema()["properties"]) return args_schema.schema()["properties"] @abstractmethod @@ -79,7 +94,6 @@ def _execute(self, *args: Any, **kwargs: Any): def max_token_limit(self): return get_config("MAX_TOOL_TOKEN_LIMIT", 600) - def _parse_input( self, tool_input: Union[str, Dict], @@ -129,6 +143,9 @@ def from_function(cls, func: Callable, args_schema: Type[BaseModel] = None): else: return cls(description=func.__doc__) + def get_tool_config(self, key): + return self.toolkit_config.get_tool_config(key=key) + class FunctionalTool(BaseTool): name: str = None @@ -143,7 +160,6 @@ def args(self): else: name = self.name args_schema = create_function_schema(f"{name}Schema", self.execute) - # print("args:", args_schema.schema()["properties"]) return args_schema.schema()["properties"] def _execute(self, *args: Any, **kwargs: Any): @@ -183,3 +199,16 @@ def wrapper(*tool_args, **tool_kwargs): return decorator +class BaseToolkit(BaseModel): + name: str + description: str + + @abstractmethod + def get_tools(self) -> List[BaseTool]: + # Add file related tools object here + pass + + @abstractmethod + def get_env_keys(self) -> List[str]: + # Add file related config keys here + pass diff --git a/superagi/tools/code/coding_toolkit.py b/superagi/tools/code/coding_toolkit.py new file mode 100644 index 000000000..f96f9e800 --- /dev/null +++ b/superagi/tools/code/coding_toolkit.py @@ -0,0 +1,18 @@ +from abc import ABC +from typing import List + +from superagi.tools.base_tool import BaseToolkit, BaseTool +from superagi.tools.code.write_code import CodingTool +from superagi.tools.code.write_spec import WriteSpecTool +from superagi.tools.code.write_test import WriteTestTool + + +class CodingToolkit(BaseToolkit, ABC): + name: str = "CodingToolkit" + description: str = "Coding Tool kit contains all tools related to coding tasks" + + def get_tools(self) -> List[BaseTool]: + return [CodingTool(), WriteSpecTool(), WriteTestTool()] + + def get_env_keys(self) -> List[str]: + return [] diff --git a/superagi/tools/code/prompts/write_code.txt b/superagi/tools/code/prompts/write_code.txt new file mode 100644 index 000000000..17f8c37ae --- /dev/null +++ b/superagi/tools/code/prompts/write_code.txt @@ -0,0 +1,32 @@ +You are a super smart developer who practices good Development for writing code according to a specification. + +Your high-level goal is: +{goals} + +Coding task description: +{code_description} + +{spec} + +You will get instructions for code to write. +You need to write a detailed answer. Make sure all parts of the architecture are turned into code. +Think carefully about each step and make good choices to get it right. First, list the main classes, +functions, methods you'll use and a quick comment on their purpose. + +Then you will output the content of each file including ALL code. +Each file must strictly follow a markdown code block format, where the following tokens must be replaced such that +[FILENAME] is the lowercase file name including the file extension, +[LANG] is the markup code block language for the code's language, and [CODE] is the code: +[FILENAME] +```[LANG] +[CODE] +``` + +You will start with the "entrypoint" file, then go to the ones that are imported by that file, and so on. +Please note that the code should be fully functional. No placeholders. + +Follow a language and framework appropriate best practice file naming convention. +Make sure that files contain all imports, types etc. Make sure that code in different files are compatible with each other. +Ensure to implement all code, if you are unsure, write a plausible implementation. +Include module dependency or package manager dependency definition file. +Before you finish, double check that all parts of the architecture is present in the files. \ No newline at end of file diff --git a/superagi/tools/code/prompts/write_spec.txt b/superagi/tools/code/prompts/write_spec.txt new file mode 100644 index 000000000..85b651b91 --- /dev/null +++ b/superagi/tools/code/prompts/write_spec.txt @@ -0,0 +1,12 @@ +You are a super smart developer who has been asked to make a specification for a program. + +Your high-level goal is: +{goals} + +Please keep in mind the following when creating the specification: +1. Be super explicit about what the program should do, which features it should have, and give details about anything that might be unclear. +2. Lay out the names of the core classes, functions, methods that will be necessary, as well as a quick comment on their purpose. +3. List all non-standard dependencies that will have to be used. + +Write a specification for the following task: +{task} \ No newline at end of file diff --git a/superagi/tools/code/prompts/write_test.txt b/superagi/tools/code/prompts/write_test.txt new file mode 100644 index 000000000..518cbf3a5 --- /dev/null +++ b/superagi/tools/code/prompts/write_test.txt @@ -0,0 +1,11 @@ +You are a super smart developer who practices Test Driven Development for writing tests according to a specification. + +Your high-level goal is: +{goals} + +Test Description: +{test_description} + +{spec} + +The tests should be as simple as possible, but still cover all the functionality described in the specification. \ No newline at end of file diff --git a/superagi/tools/code/write_code.py b/superagi/tools/code/write_code.py index d322fc2d1..1a9092f3e 100644 --- a/superagi/tools/code/write_code.py +++ b/superagi/tools/code/write_code.py @@ -4,6 +4,7 @@ from pydantic import BaseModel, Field from superagi.agent.agent_prompt_builder import AgentPromptBuilder +from superagi.helper.prompt_reader import PromptReader from superagi.helper.token_counter import TokenCounter from superagi.lib.logger import logger from superagi.llms.base_llm import BaseLlm @@ -61,39 +62,7 @@ def _execute(self, code_description: str) -> str: Generated codes files or error message. """ try: - prompt = """You are a super smart developer who practices good Development for writing code according to a specification. - - Your high-level goal is: - {goals} - - Coding task description: - {code_description} - - {spec} - - You will get instructions for code to write. - You need to write a detailed answer. Make sure all parts of the architecture are turned into code. - Think carefully about each step and make good choices to get it right. First, list the main classes, - functions, methods you'll use and a quick comment on their purpose. - - Then you will output the content of each file including ALL code. - Each file must strictly follow a markdown code block format, where the following tokens must be replaced such that - [FILENAME] is the lowercase file name including the file extension, - [LANG] is the markup code block language for the code's language, and [CODE] is the code: - [FILENAME] - ```[LANG] - [CODE] - ``` - - You will start with the "entrypoint" file, then go to the ones that are imported by that file, and so on. - Please note that the code should be fully functional. No placeholders. - - Follow a language and framework appropriate best practice file naming convention. - Make sure that files contain all imports, types etc. Make sure that code in different files are compatible with each other. - Ensure to implement all code, if you are unsure, write a plausible implementation. - Include module dependency or package manager dependency definition file. - Before you finish, double check that all parts of the architecture is present in the files. - """ + prompt = PromptReader.read_tools_prompt(__file__, "write_code.txt") prompt = prompt.replace("{goals}", AgentPromptBuilder.add_list_items_to_string(self.goals)) prompt = prompt.replace("{code_description}", code_description) spec_response = self.tool_response_manager.get_last_response("WriteSpecTool") diff --git a/superagi/tools/code/write_spec.py b/superagi/tools/code/write_spec.py index f15c89b15..db0f39a50 100644 --- a/superagi/tools/code/write_spec.py +++ b/superagi/tools/code/write_spec.py @@ -1,19 +1,14 @@ from typing import Type, Optional, List from pydantic import BaseModel, Field -from superagi.config.config import get_config -from superagi.agent.agent_prompt_builder import AgentPromptBuilder -import os +from superagi.agent.agent_prompt_builder import AgentPromptBuilder +from superagi.helper.prompt_reader import PromptReader from superagi.helper.token_counter import TokenCounter +from superagi.lib.logger import logger from superagi.llms.base_llm import BaseLlm from superagi.resource_manager.manager import ResourceManager from superagi.tools.base_tool import BaseTool -from superagi.lib.logger import logger -from superagi.models.db import connect_db -from superagi.helper.resource_helper import ResourceHelper -from superagi.helper.s3_helper import S3Helper -from sqlalchemy.orm import sessionmaker class WriteSpecSchema(BaseModel): @@ -64,19 +59,7 @@ def _execute(self, task_description: str, spec_file_name: str) -> str: Generated specification or error message. """ try: - prompt = """You are a super smart developer who has been asked to make a specification for a program. - - Your high-level goal is: - {goals} - - Please keep in mind the following when creating the specification: - 1. Be super explicit about what the program should do, which features it should have, and give details about anything that might be unclear. - 2. Lay out the names of the core classes, functions, methods that will be necessary, as well as a quick comment on their purpose. - 3. List all non-standard dependencies that will have to be used. - - Write a specification for the following task: - {task} - """ + prompt = PromptReader.read_tools_prompt(__file__, "write_spec.txt") prompt = prompt.replace("{goals}", AgentPromptBuilder.add_list_items_to_string(self.goals)) prompt = prompt.replace("{task}", task_description) messages = [{"role": "system", "content": prompt}] diff --git a/superagi/tools/code/write_test.py b/superagi/tools/code/write_test.py index 0060b2cb3..aadf8875e 100644 --- a/superagi/tools/code/write_test.py +++ b/superagi/tools/code/write_test.py @@ -4,6 +4,7 @@ from pydantic import BaseModel, Field from superagi.agent.agent_prompt_builder import AgentPromptBuilder +from superagi.helper.prompt_reader import PromptReader from superagi.helper.token_counter import TokenCounter from superagi.lib.logger import logger from superagi.llms.base_llm import BaseLlm @@ -65,18 +66,7 @@ def _execute(self, test_description: str, test_file_name: str) -> str: Generated pytest unit tests or error message. """ try: - prompt = """You are a super smart developer who practices Test Driven Development for writing tests according to a specification. - - Your high-level goal is: - {goals} - - Test Description: - {test_description} - - {spec} - - The tests should be as simple as possible, but still cover all the functionality described in the specification. - """ + prompt = PromptReader.read_tools_prompt(__file__, "write_test.txt") prompt = prompt.replace("{goals}", AgentPromptBuilder.add_list_items_to_string(self.goals)) prompt = prompt.replace("{test_description}", test_description) diff --git a/superagi/tools/email/README.MD b/superagi/tools/email/README.MD deleted file mode 100644 index 47bd9224d..000000000 --- a/superagi/tools/email/README.MD +++ /dev/null @@ -1,83 +0,0 @@ -

- -

- -# SuperAGI Email Tool - -The robust SuperAGI Email Tool lets users send and read emails while providing a foundation for other fascinating use cases. - -## 💡 Features - -1.**Read Emails:** With SuperAGI's Email Tool, you can effortlessly manage your inbox and ensure that you never overlook a critical detail. - -2.**Send Emails:** SuperAGI's Email Tool uses its comprehensive language model capabilities to create personalised, context-aware emails, sparing you effort and time. - -3.**Save Emails to Drafts Folder:** By allowing SuperAGI to develop email draughts that you can examine and modify before sending, you'll gain greater control and make sure your messages are tailored to your tastes. - -4.**Send Emails with Attachments:** Send attachments in emails with ease to enrich and expand the scope of your message. - -5.**Custom Email Signature:** Create a unique signature for each email you send to add a touch of customization and automation. - -6.**Auto-Reply and Answer Questions:** Allow SuperAGI to read, analyse, and respond to incoming emails with precise answers to streamline your email responses. - -## ⚙️ Installation - -### 🛠 **Setting Up of SuperAGI** -Set up the SuperAGI by following the instructions given (https://github.com/TransformerOptimus/SuperAGI/blob/main/README.MD) - -### 🔧 **Add Email configuration settings in config.yaml** -Add the following configuration settings in the file: - -1. _Email address and password:_ - - Set 'EMAIL_ADDRESS' to sender's email address - - Set 'EMAIL_PASSWORD' to your Password. If using Gmail, use App Password (Follow the steps given below to obtain your app password.) - -2. _Provider-specific settings:_ - - If not using Gmail, modify 'EMAIL_SMTP_HOST', 'EMAIL_SMTP_PORT', AND 'EMAIL_IMAP_HOST' according to your email service provider. - -3. _Optional Settings:_ - - Change the 'EMAIL_SIGNATURE' to your personalise signature. - - Add the draft folder for your email service provider to prevent it from being sent and instead save it in drafts. - - Add the path of the directory from where you want to attach files in 'EMAIL_ATTACHMENT_BASE_PATH'. - -## Obtain your App Password - -To obtain App password for your Gmail Account follow the steps: - -- Navigate to the link (https://myaccount.google.com/apppasswords) - -![app_password](https://github.com/TransformerOptimus/SuperAGI/assets/97586318/ec1e6222-e5d4-4b88-a69c-1fd5774ae0ea) - -- To get the App Password ensure that you have set up 2-Step Verification for your email address. - -- Generate the password by creating a custom app - -![password](https://github.com/TransformerOptimus/SuperAGI/assets/97586318/32219756-8715-4f5a-bb1c-0b2cae4e73a3) - -- Copy the password generated and use it for 'EMAIL_PASSWORD' - -- Also make sure IMAP Access is enabled for your Gmail Address (Settings > See all settings > Forwarding and POP/IMAP > Enable IMAP) - -![imap_enable](https://github.com/TransformerOptimus/SuperAGI/assets/97586318/50ef3e0c-c2ff-4848-aba7-8a6bd4a800ab) - - -## Running SuperAGI Email Tool - -1. **Read an email** - -By default SuperAGI's email tool reads last 10 emails from your inbox, to change the limit you can modify the default limit in read_email.py - -2. **Send an email** - -To send an email to a particular receiver, mention the receiver id in your goal. Email will be stored in drafts if in case receiver's email address is not mentioned. - -![send_email](https://github.com/TransformerOptimus/SuperAGI/assets/97586318/c4dc52b9-ab68-4db3-b1f9-3431c00710c4) - -3. **Send an email with attachment** - -![goals](https://github.com/TransformerOptimus/SuperAGI/assets/97586318/286194d2-05c6-4087-82c5-640e23eeb2a8) - -To attach a file with your email from local device, add the path of the directory to config.yaml else you can also mention the full path of the file in the goal. - -![attachment](https://github.com/TransformerOptimus/SuperAGI/assets/97586318/de112910-a623-469d-a0db-99063fb8572e) - diff --git a/superagi/tools/email/email_toolkit.py b/superagi/tools/email/email_toolkit.py new file mode 100644 index 000000000..8801189d8 --- /dev/null +++ b/superagi/tools/email/email_toolkit.py @@ -0,0 +1,18 @@ +from abc import ABC +from superagi.tools.base_tool import BaseToolkit, BaseTool +from typing import Type, List +from read_email import ReadEmailTool +from send_email import SendEmailTool +from send_email_attachment import SendEmailAttachmentTool + + +class EmailToolkit(BaseToolkit, ABC): + name: str = "Email Toolkit" + description: str = "Email Tool kit contains all tools related to sending email" + + def get_tools(self) -> List[BaseTool]: + return [ReadEmailTool(), SendEmailTool(),SendEmailAttachmentTool()] + + + def get_env_keys(self) -> List[str]: + return ["EMAIL_ADDRESS", "EMAIL_PASSWORD", "EMAIL_SIGNATURE", "EMAIL_DRAFT_MODE", "EMAIL_DRAFT_FOLDER", "EMAIL_SMTP_HOST", "EMAIL_SMTP_PORT","EMAIL_IMAP_SERVER"] \ No newline at end of file diff --git a/superagi/tools/email/read_email.py b/superagi/tools/email/read_email.py index 1208ab74e..78362ed71 100644 --- a/superagi/tools/email/read_email.py +++ b/superagi/tools/email/read_email.py @@ -4,7 +4,6 @@ from superagi.helper.token_counter import TokenCounter from superagi.tools.base_tool import BaseTool -from superagi.config.config import get_config from superagi.helper.imap_email import ImapEmail from superagi.helper.read_email import ReadEmail import email @@ -43,13 +42,14 @@ def _execute(self, imap_folder: str = "INBOX", page: int = 0, limit: int = 5) -> Returns: email content or error message """ - email_sender = get_config('EMAIL_ADDRESS') - email_password = get_config('EMAIL_PASSWORD') + email_sender = self.get_tool_config('EMAIL_ADDRESS') + email_password = self.get_tool_config('EMAIL_PASSWORD') if email_sender == "": return "Error: Email Not Sent. Enter a valid Email Address." if email_password == "": return "Error: Email Not Sent. Enter a valid Email Password." - conn = ImapEmail().imap_open(imap_folder, email_sender, email_password) + imap_server= self.get_tool_config('EMAIL_IMAP_SERVER') + conn = ImapEmail().imap_open(imap_folder, email_sender, email_password, imap_server) status, messages = conn.select("INBOX") num_of_messages = int(messages[0]) messages = [] diff --git a/superagi/tools/email/send_email.py b/superagi/tools/email/send_email.py index d475e2a71..bfd506e3d 100644 --- a/superagi/tools/email/send_email.py +++ b/superagi/tools/email/send_email.py @@ -5,8 +5,6 @@ from typing import Type from pydantic import BaseModel, Field - -from superagi.config.config import get_config from superagi.helper.imap_email import ImapEmail from superagi.tools.base_tool import BaseTool @@ -42,8 +40,8 @@ def _execute(self, to: str, subject: str, body: str) -> str: Returns: """ - email_sender = get_config('EMAIL_ADDRESS') - email_password = get_config('EMAIL_PASSWORD') + email_sender = self.get_tool_config('EMAIL_ADDRESS') + email_password = self.get_tool_config('EMAIL_PASSWORD') if email_sender == "" or email_sender.isspace(): return "Error: Email Not Sent. Enter a valid Email Address." if email_password == "" or email_password.isspace(): @@ -52,14 +50,21 @@ def _execute(self, to: str, subject: str, body: str) -> str: message["Subject"] = subject message["From"] = email_sender message["To"] = to - signature = get_config('EMAIL_SIGNATURE') + signature = self.get_tool_config('EMAIL_SIGNATURE') if signature: body += f"\n{signature}" message.set_content(body) - draft_folder = get_config('EMAIL_DRAFT_MODE_WITH_FOLDER') - send_to_draft = draft_folder is not None and draft_folder != "YOUR_DRAFTS_FOLDER" + + send_to_draft = self.get_tool_config('EMAIL_DRAFT_MODE') + if send_to_draft.upper() == "TRUE": + send_to_draft = True + else: + send_to_draft = False + if message["To"] == "example@example.com" or send_to_draft: - conn = ImapEmail().imap_open(draft_folder, email_sender, email_password) + draft_folder = self.get_tool_config('EMAIL_DRAFT_FOLDER') + imap_server = self.get_tool_config('EMAIL_IMAP_SERVER') + conn = ImapEmail().imap_open(draft_folder, email_sender, email_password, imap_server) conn.append( draft_folder, "", @@ -68,8 +73,8 @@ def _execute(self, to: str, subject: str, body: str) -> str: ) return f"Email went to {draft_folder}" else: - smtp_host = get_config('EMAIL_SMTP_HOST') - smtp_port = get_config('EMAIL_SMTP_PORT') + smtp_host = self.get_tool_config('EMAIL_SMTP_HOST') + smtp_port = self.get_tool_config('EMAIL_SMTP_PORT') with smtplib.SMTP(smtp_host, smtp_port) as smtp: smtp.ehlo() smtp.starttls() diff --git a/superagi/tools/email/send_email_attachment.py b/superagi/tools/email/send_email_attachment.py index 0406b9924..eeb6ac275 100644 --- a/superagi/tools/email/send_email_attachment.py +++ b/superagi/tools/email/send_email_attachment.py @@ -7,8 +7,6 @@ from typing import Type from pydantic import BaseModel, Field - -from superagi.config.config import get_config from superagi.helper.imap_email import ImapEmail from superagi.tools.base_tool import BaseTool @@ -46,13 +44,23 @@ def _execute(self, to: str, subject: str, body: str, filename: str) -> str: Returns: """ - base_path = get_config('EMAIL_ATTACHMENT_BASE_PATH') - if not base_path: - base_path = "" - base_path = base_path + filename - attachmentpath = base_path - attachment = os.path.basename(attachmentpath) - return self.send_email_with_attachment(to, subject, body, attachmentpath, attachment) + input_root_dir = self.get_tool_config('RESOURCES_INPUT_ROOT_DIR') + output_root_dir = self.get_tool_config('RESOURCES_OUTPUT_ROOT_DIR') + final_path = None + + if input_root_dir is not None: + input_root_dir = input_root_dir if input_root_dir.startswith("/") else os.getcwd() + "/" + input_root_dir + input_root_dir = input_root_dir if input_root_dir.endswith("/") else input_root_dir + "/" + final_path = input_root_dir + filename + + if final_path is None or not os.path.exists(final_path): + if output_root_dir is not None: + output_root_dir = output_root_dir if output_root_dir.startswith( + "/") else os.getcwd() + "/" + output_root_dir + output_root_dir = output_root_dir if output_root_dir.endswith("/") else output_root_dir + "/" + final_path = output_root_dir + filename + attachment = os.path.basename(final_path) + return self.send_email_with_attachment(to, subject, body, final_path, attachment) def send_email_with_attachment(self, to, subject, body, attachment_path, attachment) -> str: """ @@ -68,8 +76,8 @@ def send_email_with_attachment(self, to, subject, body, attachment_path, attachm Returns: """ - email_sender = get_config('EMAIL_ADDRESS') - email_password = get_config('EMAIL_PASSWORD') + email_sender = self.get_tool_config('EMAIL_ADDRESS') + email_password = self.get_tool_config('EMAIL_PASSWORD') if email_sender == "" or email_sender.isspace(): return "Error: Email Not Sent. Enter a valid Email Address." if email_password == "" or email_password.isspace(): @@ -78,7 +86,7 @@ def send_email_with_attachment(self, to, subject, body, attachment_path, attachm message["Subject"] = subject message["From"] = email_sender message["To"] = to - signature = get_config('EMAIL_SIGNATURE') + signature = self.get_tool_config('EMAIL_SIGNATURE') if signature: body += f"\n{signature}" message.set_content(body) @@ -89,10 +97,16 @@ def send_email_with_attachment(self, to, subject, body, attachment_path, attachm maintype, subtype = ctype.split("/", 1) with open(attachment_path, "rb") as file: message.add_attachment(file.read(), maintype=maintype, subtype=subtype, filename=attachment) - draft_folder = get_config('EMAIL_DRAFT_MODE_WITH_FOLDER') - - if message["To"] == "example@example.com" or draft_folder: - conn = ImapEmail().imap_open(draft_folder, email_sender, email_password) + + send_to_draft = self.get_tool_config('EMAIL_DRAFT_MODE') + if send_to_draft.upper() == "TRUE": + send_to_draft = True + else: + send_to_draft = False + if message["To"] == "example@example.com" or send_to_draft: + draft_folder = self.get_tool_config('EMAIL_DRAFT_FOLDER') + imap_server = self.get_tool_config('EMAIL_IMAP_SERVER') + conn = ImapEmail().imap_open(draft_folder, email_sender, email_password, imap_server) conn.append( draft_folder, "", @@ -101,8 +115,8 @@ def send_email_with_attachment(self, to, subject, body, attachment_path, attachm ) return f"Email went to {draft_folder}" else: - smtp_host = get_config('EMAIL_SMTP_HOST') - smtp_port = get_config('EMAIL_SMTP_PORT') + smtp_host = self.get_tool_config('EMAIL_SMTP_HOST') + smtp_port = self.get_tool_config('EMAIL_SMTP_PORT') with smtplib.SMTP(smtp_host, smtp_port) as smtp: smtp.ehlo() smtp.starttls() diff --git a/superagi/tools/file/append_file.py b/superagi/tools/file/append_file.py index 72f3fae5f..fa10d615d 100644 --- a/superagi/tools/file/append_file.py +++ b/superagi/tools/file/append_file.py @@ -2,13 +2,11 @@ from typing import Type from pydantic import BaseModel, Field -from superagi.config.config import get_config -from superagi.helper.resource_helper import ResourceHelper +from superagi.helper.resource_helper import ResourceHelper from superagi.tools.base_tool import BaseTool - class AppendFileInput(BaseModel): """Input for CopyFileTool.""" file_name: str = Field(..., description="Name of the file to write") diff --git a/superagi/tools/file/delete_file.py b/superagi/tools/file/delete_file.py index 3917f0a1a..fc65b0eff 100644 --- a/superagi/tools/file/delete_file.py +++ b/superagi/tools/file/delete_file.py @@ -5,8 +5,6 @@ from superagi.helper.resource_helper import ResourceHelper from superagi.tools.base_tool import BaseTool -from superagi.config.config import get_config - class DeleteFileInput(BaseModel): diff --git a/superagi/tools/file/file_toolkit.py b/superagi/tools/file/file_toolkit.py new file mode 100644 index 000000000..ba3c49a11 --- /dev/null +++ b/superagi/tools/file/file_toolkit.py @@ -0,0 +1,19 @@ +from abc import ABC +from typing import List +from superagi.tools.base_tool import BaseTool, BaseToolkit +from superagi.tools.file.append_file import AppendFileTool +from superagi.tools.file.delete_file import DeleteFileTool +from superagi.tools.file.list_files import ListFileTool +from superagi.tools.file.read_file import ReadFileTool +from superagi.tools.file.write_file import WriteFileTool + + +class FileToolkit(BaseToolkit, ABC): + name: str = "File Toolkit" + description: str = "File Tool kit contains all tools related to file operations" + + def get_tools(self) -> List[BaseTool]: + return [AppendFileTool(), DeleteFileTool(), ListFileTool(), ReadFileTool(), WriteFileTool()] + + def get_env_keys(self) -> List[str]: + return [] diff --git a/superagi/tools/file/read_file.py b/superagi/tools/file/read_file.py index 6f5aa306a..076a1d4a0 100644 --- a/superagi/tools/file/read_file.py +++ b/superagi/tools/file/read_file.py @@ -4,7 +4,6 @@ from pydantic import BaseModel, Field from superagi.tools.base_tool import BaseTool -from superagi.config.config import get_config class ReadFileSchema(BaseModel): @@ -35,8 +34,8 @@ def _execute(self, file_name: str): Returns: The file content """ - input_root_dir = get_config('RESOURCES_INPUT_ROOT_DIR') - output_root_dir = get_config('RESOURCES_OUTPUT_ROOT_DIR') + input_root_dir = self.get_tool_config('RESOURCES_INPUT_ROOT_DIR') + output_root_dir = self.get_tool_config('RESOURCES_OUTPUT_ROOT_DIR') final_path = None if input_root_dir is not None: diff --git a/superagi/tools/file/write_file.py b/superagi/tools/file/write_file.py index f425a3186..4a153d06a 100644 --- a/superagi/tools/file/write_file.py +++ b/superagi/tools/file/write_file.py @@ -2,6 +2,7 @@ from pydantic import BaseModel, Field +# from superagi.helper.s3_helper import upload_to_s3 from superagi.resource_manager.manager import ResourceManager from superagi.tools.base_tool import BaseTool @@ -44,5 +45,4 @@ def _execute(self, file_name: str, content: str): Returns: file written to successfully. or error message. """ - self.resource_manager.write_file(file_name, content) - + return self.resource_manager.write_file(file_name, content) diff --git a/superagi/tools/github/add_file.py b/superagi/tools/github/add_file.py index 67d13a91a..caf89de10 100644 --- a/superagi/tools/github/add_file.py +++ b/superagi/tools/github/add_file.py @@ -1,17 +1,9 @@ -import string -import re -import base64 - -import requests -import os from typing import Type from pydantic import BaseModel, Field -from superagi.config.config import get_config - -from superagi.tools.base_tool import BaseTool from superagi.helper.github_helper import GithubHelper +from superagi.tools.base_tool import BaseTool class GithubAddFileSchema(BaseModel): @@ -77,8 +69,8 @@ def _execute(self, repository_name: str, base_branch: str, body: str, commit_mes Pull request to add file/folder has been created. or error message. """ try: - github_access_token = get_config("GITHUB_ACCESS_TOKEN") - github_username = get_config("GITHUB_USERNAME") + github_access_token = self.get_tool_config("GITHUB_ACCESS_TOKEN") + github_username = self.get_tool_config("GITHUB_USERNAME") github_helper = GithubHelper(github_access_token, github_username) head_branch = 'new-file' headers = { diff --git a/superagi/tools/github/delete_file.py b/superagi/tools/github/delete_file.py index 2b4f8a7be..d940704a9 100644 --- a/superagi/tools/github/delete_file.py +++ b/superagi/tools/github/delete_file.py @@ -1,7 +1,6 @@ from typing import Type from pydantic import BaseModel, Field -from superagi.config.config import get_config from superagi.tools.base_tool import BaseTool from superagi.helper.github_helper import GithubHelper from superagi.lib.logger import logger @@ -66,8 +65,8 @@ def _execute(self, repository_name: str, base_branch: str, file_name: str, commi """ try: - github_access_token = get_config("GITHUB_ACCESS_TOKEN") - github_username = get_config("GITHUB_USERNAME") + github_access_token = self.get_tool_config("GITHUB_ACCESS_TOKEN") + github_username = self.get_tool_config("GITHUB_USERNAME") github_helper = GithubHelper(github_access_token, github_username) head_branch = 'new-file' headers = { diff --git a/superagi/tools/github/github_toolkit.py b/superagi/tools/github/github_toolkit.py new file mode 100644 index 000000000..bb8e3e9cd --- /dev/null +++ b/superagi/tools/github/github_toolkit.py @@ -0,0 +1,21 @@ +from abc import ABC +from typing import List +from superagi.tools.base_tool import BaseTool, BaseToolkit +from superagi.tools.github.add_file import GithubAddFileTool +from superagi.tools.github.delete_file import GithubDeleteFileTool +from superagi.tools.github.search_repo import GithubRepoSearchTool + + +class GitHubToolkit(BaseToolkit, ABC): + name: str = "GitHub Toolkit" + description: str = "GitHub Tool Kit contains all github related to tool" + + def get_tools(self) -> List[BaseTool]: + return [GithubAddFileTool(), GithubDeleteFileTool(), GithubRepoSearchTool()] + + def get_env_keys(self) -> List[str]: + return [ + "GITHUB_ACCESS_TOKEN", + "GITHUB_USERNAME", + # Add more file related config keys here + ] diff --git a/superagi/tools/github/search_repo.py b/superagi/tools/github/search_repo.py index 7827c854d..4c5c86800 100644 --- a/superagi/tools/github/search_repo.py +++ b/superagi/tools/github/search_repo.py @@ -1,8 +1,6 @@ from typing import Type from pydantic import BaseModel, Field - -from superagi.config.config import get_config from superagi.helper.github_helper import GithubHelper from superagi.tools.base_tool import BaseTool @@ -54,8 +52,8 @@ def _execute(self, repository_owner: str, repository_name: str, file_name: str, Returns: The content of the file. """ - github_access_token = get_config("GITHUB_ACCESS_TOKEN") - github_username = get_config("GITHUB_USERNAME") + github_access_token = self.get_tool_config("GITHUB_ACCESS_TOKEN") + github_username = self.get_tool_config("GITHUB_USERNAME") github_repo_search = GithubHelper(github_access_token, github_username) content = github_repo_search.get_content_in_file(repository_owner, repository_name, file_name, folder_path) diff --git a/superagi/tools/google_search/google_search.py b/superagi/tools/google_search/google_search.py index 3df123a8f..4bd3e427e 100644 --- a/superagi/tools/google_search/google_search.py +++ b/superagi/tools/google_search/google_search.py @@ -1,15 +1,12 @@ -from typing import Type, List +import json +from typing import Type, Optional + from pydantic import BaseModel, Field -from typing import Type, Optional, List from superagi.helper.google_search import GoogleSearchWrap from superagi.helper.token_counter import TokenCounter from superagi.llms.base_llm import BaseLlm from superagi.tools.base_tool import BaseTool -import os -import json -from superagi.config.config import get_config - class GoogleSearchSchema(BaseModel): @@ -48,13 +45,12 @@ def _execute(self, query: str) -> tuple: Returns: A tuple of (snippets, webpages, links) """ - api_key = get_config("GOOGLE_API_KEY") - search_engine_id = get_config("SEARCH_ENGINE_ID") + api_key = self.get_tool_config("GOOGLE_API_KEY") + search_engine_id = self.get_tool_config("SEARCH_ENGINE_ID") num_results = 10 num_pages = 1 num_extracts = 3 - #print("query: ", query) google_search = GoogleSearchWrap(api_key, search_engine_id, num_results, num_pages, num_extracts) snippets, webpages, links = google_search.get_result(query) diff --git a/superagi/tools/google_search/google_search_toolkit.py b/superagi/tools/google_search/google_search_toolkit.py new file mode 100644 index 000000000..5ccfc217d --- /dev/null +++ b/superagi/tools/google_search/google_search_toolkit.py @@ -0,0 +1,19 @@ +from abc import ABC +from typing import List +from superagi.tools.base_tool import BaseTool, BaseToolkit +from superagi.tools.google_search.google_search import GoogleSearchTool + + +class GoogleSearchToolkit(BaseToolkit, ABC): + name: str = "Google Search Toolkit" + description: str = "Toolkit containing tools for performing Google search and extracting snippets and webpages" + + def get_tools(self) -> List[BaseTool]: + return [GoogleSearchTool()] + + def get_env_keys(self) -> List[str]: + return [ + "GOOGLE_API_KEY", + "SEARCH_ENGINE_ID" + # Add more config keys specific to your project + ] diff --git a/superagi/tools/google_serp_search/google_serp_search.py b/superagi/tools/google_serp_search/google_serp_search.py index 83c1f6987..d3a69ae05 100644 --- a/superagi/tools/google_serp_search/google_serp_search.py +++ b/superagi/tools/google_serp_search/google_serp_search.py @@ -4,8 +4,6 @@ from superagi.helper.google_serp import GoogleSerpApiWrap from superagi.llms.base_llm import BaseLlm from superagi.tools.base_tool import BaseTool -from superagi.config.config import get_config - import os import json @@ -49,7 +47,7 @@ def _execute(self, query: str) -> tuple: Returns: A tuple of (snippets, webpages, links) """ - api_key = get_config("SERP_API_KEY") + api_key = self.get_tool_config("SERP_API_KEY") serp_api = GoogleSerpApiWrap(api_key) response = serp_api.search_run(query) summary = self.summarise_result(query, response["snippets"]) diff --git a/superagi/tools/google_serp_search/google_serp_search_toolkit.py b/superagi/tools/google_serp_search/google_serp_search_toolkit.py new file mode 100644 index 000000000..bd91b42b3 --- /dev/null +++ b/superagi/tools/google_serp_search/google_serp_search_toolkit.py @@ -0,0 +1,18 @@ +from abc import ABC +from typing import List +from superagi.tools.base_tool import BaseTool, BaseToolkit +from superagi.tools.google_serp_search.google_serp_search import GoogleSerpTool + + +class GoogleSerpToolkit(BaseToolkit, ABC): + name: str = "Google SERP Toolkit" + description: str = "Toolkit containing tools for performing Google SERP search and extracting snippets and webpages" + + def get_tools(self) -> List[BaseTool]: + return [GoogleSerpTool()] + + def get_env_keys(self) -> List[str]: + return [ + "SERP_API_KEY" + # Add more config keys specific to your project + ] diff --git a/superagi/tools/human/tool.py b/superagi/tools/human/tool.py deleted file mode 100644 index 0dd6959d4..000000000 --- a/superagi/tools/human/tool.py +++ /dev/null @@ -1,51 +0,0 @@ -from typing import Callable, Type - -from pydantic import Field, BaseModel - -from superagi.tools.base_tool import BaseTool -from superagi.lib.logger import logger - -def print_func(text: str) -> None: - logger.info("\n") - logger.info(text) - -class HumanInputSchema(BaseModel): - query: str = Field( - ..., - description="Question for the human", - ) - -class HumanInput(BaseTool): - """ - Human tool - - Attributes: - name : The name. - description : The description. - args_schema : The args schema. - """ - name = "Human" - description = ( - "You can ask a human for guidance when you think you " - "got stuck or you are not sure what to do next. " - "The input should be a question for the human." - ) - args_schema: Type[HumanInputSchema] = HumanInputSchema - prompt_func: Callable[[str], None] = Field(default_factory=lambda: print_func) - input_func: Callable = Field(default_factory=lambda: input) - - def _execute( - self, - query: str - ) -> str: - """ - Execute the human tool. - - Args: - query : The question for the human. - - Returns: - The answer from the human. - """ - self.prompt_func(query) - return self.input_func() \ No newline at end of file diff --git a/superagi/tools/image_generation/image_generation_toolkit.py b/superagi/tools/image_generation/image_generation_toolkit.py new file mode 100644 index 000000000..d4533c5bb --- /dev/null +++ b/superagi/tools/image_generation/image_generation_toolkit.py @@ -0,0 +1,17 @@ +from abc import ABC +from typing import List + +from superagi.tools.base_tool import BaseTool, BaseToolkit +from superagi.tools.image_generation.dalle_image_gen import DalleImageGenTool +from superagi.tools.image_generation.stable_diffusion_image_gen import StableDiffusionImageGenTool + + +class ImageGenToolkit(BaseToolkit, ABC): + name: str = "Image Generation Toolkit" + description: str = "Toolkit containing a tool for generating images" + + def get_tools(self) -> List[BaseTool]: + return [DalleImageGenTool(), StableDiffusionImageGenTool()] + + def get_env_keys(self) -> List[str]: + return ["STABILITY_API_KEY", "ENGINE_ID"] diff --git a/superagi/tools/image_generation/stable_diffusion_image_gen.py b/superagi/tools/image_generation/stable_diffusion_image_gen.py index 3f615d650..ffc27f49a 100644 --- a/superagi/tools/image_generation/stable_diffusion_image_gen.py +++ b/superagi/tools/image_generation/stable_diffusion_image_gen.py @@ -5,7 +5,7 @@ import requests from PIL import Image from pydantic import BaseModel, Field -from superagi.config.config import get_config + from superagi.resource_manager.manager import ResourceManager from superagi.tools.base_tool import BaseTool @@ -42,7 +42,7 @@ class Config: def _execute(self, prompt: str, image_names: list, width: int = 512, height: int = 512, num: int = 2, steps: int = 50): - api_key = get_config("STABILITY_API_KEY") + api_key = self.get_tool_config("STABILITY_API_KEY") if api_key is None: return "Error: Missing Stability API key." @@ -72,7 +72,7 @@ def _execute(self, prompt: str, image_names: list, width: int = 512, height: int return "Images downloaded and saved successfully" def call_stable_diffusion(self, api_key, width, height, num, prompt, steps): - engine_id = get_config("ENGINE_ID") + engine_id = self.get_tool_config("ENGINE_ID") if "768" in engine_id: if height < 768: height = 768 diff --git a/superagi/tools/jira/jira_toolkit.py b/superagi/tools/jira/jira_toolkit.py new file mode 100644 index 000000000..3d8b4d81e --- /dev/null +++ b/superagi/tools/jira/jira_toolkit.py @@ -0,0 +1,27 @@ +from abc import ABC +from typing import List +from superagi.tools.base_tool import BaseTool, BaseToolkit +from superagi.tools.jira.create_issue import CreateIssueTool +from superagi.tools.jira.edit_issue import EditIssueTool +from superagi.tools.jira.get_projects import GetProjectsTool +from superagi.tools.jira.search_issues import SearchJiraTool + + +class JiraToolkit(BaseToolkit, ABC): + name: str = "Jira Toolkit" + description: str = "Toolkit containing tools for Jira integration" + + def get_tools(self) -> List[BaseTool]: + return [ + CreateIssueTool(), + EditIssueTool(), + GetProjectsTool(), + SearchJiraTool(), + ] + + def get_env_keys(self) -> List[str]: + return [ + "JIRA_INSTANCE_URL", + "JIRA_USERNAME", + "JIRA_API_TOKEN", + ] diff --git a/superagi/tools/jira/tool.py b/superagi/tools/jira/tool.py index 2d823a74e..ef5f0de4f 100644 --- a/superagi/tools/jira/tool.py +++ b/superagi/tools/jira/tool.py @@ -36,9 +36,9 @@ def build_jira_instance(self) -> dict: Returns: The Jira instance. """ - jira_instance_url = get_config("JIRA_INSTANCE_URL") - jira_username = get_config("JIRA_USERNAME") - jira_api_token = get_config("JIRA_API_TOKEN") + jira_instance_url = self.get_tool_config("JIRA_INSTANCE_URL") + jira_username = self.get_tool_config("JIRA_USERNAME") + jira_api_token = self.get_tool_config("JIRA_API_TOKEN") jira = JIRA( server=jira_instance_url, basic_auth=(jira_username, jira_api_token) diff --git a/superagi/tools/slack/send_message.py b/superagi/tools/slack/send_message.py index f8947419a..83a4023ba 100644 --- a/superagi/tools/slack/send_message.py +++ b/superagi/tools/slack/send_message.py @@ -6,6 +6,7 @@ from superagi.config.config import get_config from slack_sdk import WebClient + class SlackMessageSchema(BaseModel): channel: str = Field( ..., @@ -13,9 +14,10 @@ class SlackMessageSchema(BaseModel): ) message: str = Field( ..., - description = "Text Message to be sent to a person or a group or people" + description="Text Message to be sent to a person or a group or people" ) + class SlackMessageTool(BaseTool): """ Slack Message Tool @@ -32,7 +34,7 @@ class SlackMessageTool(BaseTool): name = "SendSlackMessage" description = "Send text message in Slack" args_schema: Type[SlackMessageSchema] = SlackMessageSchema - + def _execute(self, channel: str, message: str): """ Execute the Slack Message Tool. @@ -46,7 +48,7 @@ def _execute(self, channel: str, message: str): """ slack = self.build_slack_web_client() response = slack.chat_postMessage(channel=channel, text=message) - + if response['ok']: return f'Message sent to {channel} Successfully' else: diff --git a/superagi/tools/slack/slack_toolkit.py b/superagi/tools/slack/slack_toolkit.py new file mode 100644 index 000000000..1353ef889 --- /dev/null +++ b/superagi/tools/slack/slack_toolkit.py @@ -0,0 +1,19 @@ +from abc import ABC +from typing import List +from superagi.tools.base_tool import BaseTool, BaseToolkit +from superagi.tools.slack.send_message import SlackMessageTool + + +class SlackToolkit(BaseToolkit, ABC): + name: str = "Slack Toolkit" + description: str = "Toolkit containing tools for Slack integration" + + def get_tools(self) -> List[BaseTool]: + return [ + SlackMessageTool(), + ] + + def get_env_keys(self) -> List[str]: + return [ + "SLACK_BOT_TOKEN", + ] diff --git a/superagi/tools/thinking/prompts/thinking.txt b/superagi/tools/thinking/prompts/thinking.txt new file mode 100644 index 000000000..c5fdb002b --- /dev/null +++ b/superagi/tools/thinking/prompts/thinking.txt @@ -0,0 +1,12 @@ +Given the following overall objective +Objective: +{goals} + +and the following task, `{task_description}`. + +Below is last tool response: +`{last_tool_response}` + +Perform the task by understanding the problem, extracting variables, and being smart +and efficient. Provide a descriptive response, make decisions yourself when +confronted with choices and provide reasoning for ideas / decisions. \ No newline at end of file diff --git a/superagi/tools/thinking/thinking_toolkit.py b/superagi/tools/thinking/thinking_toolkit.py new file mode 100644 index 000000000..48bdf6f39 --- /dev/null +++ b/superagi/tools/thinking/thinking_toolkit.py @@ -0,0 +1,17 @@ +from abc import ABC +from typing import List +from superagi.tools.base_tool import BaseTool, BaseToolkit +from superagi.tools.thinking.tools import ThinkingTool + + +class ThinkingToolkit(BaseToolkit, ABC): + name: str = "Thinking Toolkit" + description: str = "Toolkit containing tools for intelligent problem-solving" + + def get_tools(self) -> List[BaseTool]: + return [ + ThinkingTool(), + ] + + def get_env_keys(self) -> List[str]: + return [] diff --git a/superagi/tools/thinking/tools.py b/superagi/tools/thinking/tools.py index 302b3374d..0e8850999 100644 --- a/superagi/tools/thinking/tools.py +++ b/superagi/tools/thinking/tools.py @@ -3,6 +3,7 @@ from pydantic import BaseModel, Field from superagi.agent.agent_prompt_builder import AgentPromptBuilder +from superagi.helper.prompt_reader import PromptReader from superagi.lib.logger import logger from superagi.llms.base_llm import BaseLlm from superagi.tools.base_tool import BaseTool @@ -28,7 +29,7 @@ class ThinkingTool(BaseTool): llm: Optional[BaseLlm] = None name = "ThinkingTool" description = ( - "Intelligent problem-solving assistant that comprehends tasks, identifies key variables, and makes efficient decisions, all while providing detailed, self-driven reasoning for its choices." + "Intelligent problem-solving assistant that comprehends tasks, identifies key variables, and makes efficient decisions, all while providing detailed, self-driven reasoning for its choices. Do not assume anything, take the details from given data only." ) args_schema: Type[ThinkingSchema] = ThinkingSchema goals: List[str] = [] @@ -50,19 +51,7 @@ def _execute(self, task_description: str): response from the Thinking tool. or error message. """ try: - prompt = """Given the following overall objective - Objective: - {goals} - - and the following task, `{task_description}`. - - Below is last tool response: - `{last_tool_response}` - - Perform the task by understanding the problem, extracting variables, and being smart - and efficient. Provide a descriptive response, make decisions yourself when - confronted with choices and provide reasoning for ideas / decisions. - """ + prompt = PromptReader.read_tools_prompt(__file__, "thinking.txt") prompt = prompt.replace("{goals}", AgentPromptBuilder.add_list_items_to_string(self.goals)) prompt = prompt.replace("{task_description}", task_description) last_tool_response = self.tool_response_manager.get_last_response() diff --git a/superagi/tools/webscaper/web_scraper_toolkit.py b/superagi/tools/webscaper/web_scraper_toolkit.py new file mode 100644 index 000000000..ed122bbd8 --- /dev/null +++ b/superagi/tools/webscaper/web_scraper_toolkit.py @@ -0,0 +1,17 @@ +from abc import ABC +from typing import List +from superagi.tools.base_tool import BaseTool, BaseToolkit +from superagi.tools.webscaper.tools import WebScraperTool + + +class WebScrapperToolkit(BaseToolkit, ABC): + name: str = "Web Scrapper Toolkit" + description: str = "Web Scrapper tool kit is used to scrape web" + + def get_tools(self) -> List[BaseTool]: + return [ + WebScraperTool(), + ] + + def get_env_keys(self) -> List[str]: + return [] diff --git a/superagi/types/common.py b/superagi/types/common.py index 634dd6388..fa7c55260 100644 --- a/superagi/types/common.py +++ b/superagi/types/common.py @@ -1,43 +1,47 @@ from abc import abstractmethod - from pydantic import BaseModel, Field class BaseMessage(BaseModel): - """Base message object.""" + """Base message object.""" - content: str - additional_kwargs: dict = Field(default_factory=dict) + content: str + additional_kwargs: dict = Field(default_factory=dict) - @property - @abstractmethod - def type(self) -> str: - """Message type used.""" + @property + @abstractmethod + def type(self) -> str: + """Message type used.""" class HumanMessage(BaseMessage): - """Message by human.""" + """Message by human.""" - example: bool = False + example: bool = False - @property - def type(self) -> str: - return "user" + @property + def type(self) -> str: + return "user" class AIMessage(BaseMessage): - """Type of message that is spoken by the AI.""" + """Type of message that is spoken by the AI.""" - example: bool = False + example: bool = False - @property - def type(self) -> str: - return "assistant" + @property + def type(self) -> str: + return "assistant" class SystemMessage(BaseMessage): - """Used when message is system message.""" + """Used when message is system message.""" + + @property + def type(self) -> str: + return "system" + - @property - def type(self) -> str: - return "system" +class GitHubLinkRequest(BaseModel): + """Used for Request body in install API""" + github_link: str diff --git a/test.py b/test.py index eb466739b..5a4fdcd61 100644 --- a/test.py +++ b/test.py @@ -85,8 +85,6 @@ def run_superagi_cli(agent_name=None, agent_description=None, agent_goals=None): "memory_window": 10 } - # print("Id is ") - # print(db_agent.id) agent_configurations = [ AgentConfiguration(agent_id=agent.id, key=key, value=str(value)) for key, value in agent_config_values.items() diff --git a/tests/tools/email/__init__.py b/tests/tools/email/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/tests/tools/email/test_send_email.py b/tests/tools/email/test_send_email.py deleted file mode 100644 index 16d802477..000000000 --- a/tests/tools/email/test_send_email.py +++ /dev/null @@ -1,70 +0,0 @@ -from unittest.mock import MagicMock - -import pytest -import imaplib -import time -from email.message import EmailMessage - -from superagi.config.config import get_config -from superagi.helper.imap_email import ImapEmail -from superagi.tools.email import send_email -from superagi.tools.email.send_email import SendEmailTool - -def test_send_to_draft(mocker): - - mock_get_config = mocker.patch('superagi.tools.email.send_email.get_config', autospec=True) - mock_get_config.side_effect = [ - 'test_sender@test.com', # EMAIL_ADDRESS - 'password', # EMAIL_PASSWORD - 'Test Signature', # EMAIL_SIGNATURE - "Draft", # EMAIL_DRAFT_MODE_WITH_FOLDER - 'smtp_host', # EMAIL_SMTP_HOST - 'smtp_port' # EMAIL_SMTP_PORT - ] - - - # Mocking the ImapEmail call - mock_imap_email = mocker.patch('superagi.tools.email.send_email.ImapEmail') - mock_imap_instance = mock_imap_email.return_value.imap_open.return_value - - # Mocking the SMTP call - mock_smtp = mocker.patch('smtplib.SMTP') - smtp_instance = mock_smtp.return_value - - # Test the SendEmailTool's execute method - send_email_tool = SendEmailTool() - result = send_email_tool._execute('mukunda@contlo.com', 'Test Subject', 'Test Body') - - # Assert the return value - assert result == 'Email went to Draft' - -def test_send_to_mailbox(mocker): - # Mocking the get_config calls - mock_get_config = mocker.patch('superagi.tools.email.send_email.get_config') - mock_get_config.side_effect = [ - 'test_sender@test.com', # EMAIL_ADDRESS - 'password', # EMAIL_PASSWORD - 'Test Signature', # EMAIL_SIGNATURE - "YOUR_DRAFTS_FOLDER", # EMAIL_DRAFT_MODE_WITH_FOLDER - 'smtp_host', # EMAIL_SMTP_HOST - 'smtp_port' # EMAIL_SMTP_PORT - ] - - # mock_get_config.return_value = 'True' - # Mocking the ImapEmail call - mock_imap_email = mocker.patch('superagi.tools.email.send_email.ImapEmail') - mock_imap_instance = mock_imap_email.return_value.imap_open.return_value - - # Mocking the SMTP call - mock_smtp = mocker.patch('smtplib.SMTP') - smtp_instance = mock_smtp.return_value - - # Test the SendEmailTool's execute method - send_email_tool = SendEmailTool() - result = send_email_tool._execute('test_receiver@test.com', 'Test Subject', 'Test Body') - - # Assert that the ImapEmail was not called (no draft mode) - mock_imap_email.assert_not_called() - - # Assert the return value - assert result == 'Email was sent to test_receiver@test.com' \ No newline at end of file diff --git a/tests/unit_tests/agent_permissions/test_handle_wait_for_permission.py b/tests/unit_tests/agent_permissions/test_handle_wait_for_permission.py index 6b752ac7e..fcc3efb65 100644 --- a/tests/unit_tests/agent_permissions/test_handle_wait_for_permission.py +++ b/tests/unit_tests/agent_permissions/test_handle_wait_for_permission.py @@ -3,6 +3,7 @@ from sqlalchemy.orm import sessionmaker import superagi.models.agent_execution +from superagi.lib.logger import logger from superagi.models.agent_execution_feed import AgentExecutionFeed from superagi.models.agent_execution_permission import AgentExecutionPermission from superagi.models.base_model import DBBaseModel as Base @@ -50,7 +51,7 @@ def test_handle_wait_for_permission(): agent_execution_feed = session.query(AgentExecutionFeed).filter( AgentExecutionFeed.agent_execution_id == agent_execution_id).first() - print(agent_execution_feed) + logger.info(agent_execution_feed) assert agent_execution_feed is not None assert agent_execution.status == "RUNNING" assert agent_execution_feed.feed == "Approved" diff --git a/superagi/tools/human/__init__.py b/tests/unit_tests/controllers/__init__.py similarity index 100% rename from superagi/tools/human/__init__.py rename to tests/unit_tests/controllers/__init__.py diff --git a/tests/unit_tests/helper/test_tool_helper.py b/tests/unit_tests/helper/test_tool_helper.py new file mode 100644 index 000000000..386aeec58 --- /dev/null +++ b/tests/unit_tests/helper/test_tool_helper.py @@ -0,0 +1,93 @@ +import json +import os +from pathlib import Path + +import pytest + +from superagi.helper.tool_helper import ( + parse_github_url, + load_module_from_file, + extract_repo_name, + add_tool_to_json, get_readme_content_from_code_link +) + + +@pytest.fixture +def mock_requests_get(monkeypatch): + class MockResponse: + def __init__(self, content, status_code): + self.content = content + self.status_code = status_code + self.text = content.decode() if content is not None else None + + def mock_get(url): + if url == 'https://api.github.com/repos/owner/repo/zipball/main': + return MockResponse(b'ZIP_CONTENT', 200) + elif url == 'https://raw.githubusercontent.com/username/repo/main/README.MD': + return MockResponse(b'README_CONTENT', 200) + elif url == 'https://raw.githubusercontent.com/username/repo/main/README.md': + return MockResponse(b'README_CONTENT', 200) + else: + return MockResponse(None, 404) + + monkeypatch.setattr('requests.get', mock_get) + + +def test_parse_github_url(): + github_url = 'https://github.com/owner/repo' + expected_result = 'owner/repo/main' + assert parse_github_url(github_url) == expected_result + + +def test_load_module_from_file(tmp_path): + current_dir = os.getcwd() + file_path = Path(current_dir) / 'test_module.py' + + # Corrected code with proper indentation + file_content = ''' +def hello(): + return 'Hello, world!' +''' + file_path.write_text(file_content) + + module = load_module_from_file(file_path) + assert module.hello() == 'Hello, world!' + + # Delete the test_module.py file + file_path.unlink() + + +def test_get_readme_content_from_code_link(mock_requests_get): + tool_code_link = 'https://github.com/username/repo' + expected_result = 'README_CONTENT' + assert get_readme_content_from_code_link(tool_code_link) == expected_result + + +def test_extract_repo_name(): + repo_link = 'https://github.com/username/repo' + expected_result = 'repo' + assert extract_repo_name(repo_link) == expected_result + + +def test_add_tool_to_json(tmp_path): + current_dir = os.getcwd() + file_path = Path(current_dir) / 'tools.json' + + file_path.write_text(''' + { + "tools": { + "repo1": "https://github.com/username/repo1", + "repo2": "https://github.com/username/repo2" + } + } + ''') + repo_link = 'https://github.com/username/repo3' + add_tool_to_json(repo_link) + with open(file_path) as file: + tools_data = json.load(file) + assert tools_data['tools']['repo1'] == 'https://github.com/username/repo1' + assert tools_data['tools']['repo2'] == 'https://github.com/username/repo2' + assert tools_data['tools']['repo3'] == 'https://github.com/username/repo3' + + # Delete the tools.json file + file_path.unlink() diff --git a/tests/unit_tests/models/test_tool_config.py b/tests/unit_tests/models/test_tool_config.py new file mode 100644 index 000000000..70aeee9c7 --- /dev/null +++ b/tests/unit_tests/models/test_tool_config.py @@ -0,0 +1,42 @@ +from unittest.mock import MagicMock, patch + +import pytest + +from superagi.models.tool_config import ToolConfig +from superagi.models.toolkit import Toolkit + + +@pytest.fixture +def mock_session(): + return MagicMock() + + +def test_add_or_update_existing_tool_config(mock_session): + # Arrange + toolkit_id = 1 + key = "example_key" + value = "example_value" + existing_tool_config = ToolConfig(toolkit_id=toolkit_id, key=key, value="old_value") + mock_session.query.return_value.filter_by.return_value.first.return_value = existing_tool_config + + # Act + ToolConfig.add_or_update(mock_session, toolkit_id, key, value) + + # Assert + assert existing_tool_config.value == value + mock_session.commit.assert_called_once() + + +def test_add_or_update_new_tool_config(mock_session): + # Arrange + toolkit_id = 1 + key = "example_key" + value = "example_value" + mock_session.query.return_value.filter_by.return_value.first.return_value = None + + # Act + ToolConfig.add_or_update(mock_session, toolkit_id, key, value) + + # Assert + # mock_session.add.assert_called_once_with(ToolConfig(toolkit_id=toolkit_id, key=key, value=value)) + mock_session.commit.assert_called_once() \ No newline at end of file diff --git a/tests/unit_tests/models/test_toolkit.py b/tests/unit_tests/models/test_toolkit.py new file mode 100644 index 000000000..5eb9b5a03 --- /dev/null +++ b/tests/unit_tests/models/test_toolkit.py @@ -0,0 +1,183 @@ +from unittest.mock import MagicMock, patch + +import pytest + +from superagi.models.toolkit import Toolkit +@pytest.fixture +def mock_session(): + return MagicMock() + +marketplace_url = "http://localhost:8001" + + +def test_add_or_update_existing_toolkit(mock_session): + # Arrange + name = "example_toolkit" + description = "Example toolkit description" + show_toolkit = True + organisation_id = 1 + tool_code_link = "https://example.com/toolkit" + + existing_toolkit = Toolkit( + name=name, + description="Old description", + show_toolkit=False, + organisation_id=organisation_id, + tool_code_link="https://old-link.com" + ) + + mock_session.query.return_value.filter.return_value.first.return_value = existing_toolkit + + # Act + result = Toolkit.add_or_update(mock_session, name, description, show_toolkit, organisation_id, tool_code_link) + + # Assert + assert result == existing_toolkit + assert result.name == name + assert result.description == description + assert result.show_toolkit == show_toolkit + assert result.organisation_id == organisation_id + assert result.tool_code_link == tool_code_link + mock_session.add.assert_not_called() # Make sure add was not called + mock_session.commit.assert_called_once() + mock_session.flush.assert_called_once() + + +def test_add_or_update_new_toolkit(mock_session): + # Arrange + name = "example_toolkit" + description = "Example toolkit description" + show_toolkit = True + organisation_id = 1 + tool_code_link = "https://example.com/toolkit" + + mock_session.query.return_value.filter.return_value.first.return_value = None + + # Act + result = Toolkit.add_or_update(mock_session, name, description, show_toolkit, organisation_id, tool_code_link) + + # Assert + assert isinstance(result, Toolkit) + assert result.name == name + assert result.description == description + assert result.show_toolkit == show_toolkit + assert result.organisation_id == organisation_id + assert result.tool_code_link == tool_code_link + mock_session.add.assert_called_once_with(result) + mock_session.commit.assert_called_once() + mock_session.flush.assert_called_once() + + + +def test_fetch_marketplace_list_success(): + # Arrange + page = 1 + expected_response = [ + { + "id": 1, + "name": "ToolKit 1", + "description": "Description 1" + }, + { + "id": 2, + "name": "ToolKit 2", + "description": "Description 2" + } + ] + + # Mock the requests.get method + with patch('requests.get') as mock_get: + mock_get.return_value.status_code = 200 + mock_get.return_value.json.return_value = expected_response + + # Act + result = Toolkit.fetch_marketplace_list(page) + + # Assert + assert result == expected_response + mock_get.assert_called_once_with( + f"{marketplace_url}/toolkits/marketplace/list/{str(page)}", + headers={'Content-Type': 'application/json'}, + timeout=10 + ) + +def test_fetch_marketplace_detail_success(): + # Arrange + search_str = "search string" + toolkit_name = "tool kit name" + expected_response = { + "id": 1, + "name": "ToolKit 1", + "description": "Description 1" + } + + # Mock the requests.get method + with patch('requests.get') as mock_get: + mock_get.return_value.status_code = 200 + mock_get.return_value.json.return_value = expected_response + + # Act + result = Toolkit.fetch_marketplace_detail(search_str, toolkit_name) + + # Assert + assert result == expected_response + mock_get.assert_called_once_with( + f"{marketplace_url}/toolkits/marketplace/{search_str.replace(' ', '%20')}/{toolkit_name.replace(' ', '%20')}", + headers={'Content-Type': 'application/json'}, + timeout=10 + ) + +def test_fetch_marketplace_detail_error(): + # Arrange + search_str = "search string" + toolkit_name = "tool kit name" + + # Mock the requests.get method to simulate an error response + with patch('requests.get') as mock_get: + mock_get.return_value.status_code = 500 + + # Act + result = Toolkit.fetch_marketplace_detail(search_str, toolkit_name) + + # Assert + assert result is None + mock_get.assert_called_once_with( + f"{marketplace_url}/toolkits/marketplace/{search_str.replace(' ', '%20')}/{toolkit_name.replace(' ', '%20')}", + headers={'Content-Type': 'application/json'}, + timeout=10 + ) + + + +def test_get_toolkit_from_name_existing_toolkit(mock_session): + # Arrange + toolkit_name = "example_toolkit" + expected_toolkit = Toolkit(name=toolkit_name) + + # Mock the session.query method + mock_session.query.return_value.filter_by.return_value.first.return_value = expected_toolkit + + # Act + result = Toolkit.get_toolkit_from_name(mock_session, toolkit_name) + + # Assert + assert result == expected_toolkit + mock_session.query.assert_called_once_with(Toolkit) + mock_session.query.return_value.filter_by.assert_called_once_with(name=toolkit_name) + mock_session.query.return_value.filter_by.return_value.first.assert_called_once() + +def test_get_toolkit_from_name_nonexistent_toolkit(mock_session): + # Arrange + toolkit_name = "nonexistent_toolkit" + + # Mock the session.query method to return None + mock_session.query.return_value.filter_by.return_value.first.return_value = None + + # Act + result = Toolkit.get_toolkit_from_name(mock_session, toolkit_name) + + # Assert + assert result is None + mock_session.query.assert_called_once_with(Toolkit) + mock_session.query.return_value.filter_by.assert_called_once_with(name=toolkit_name) + mock_session.query.return_value.filter_by.return_value.first.assert_called_once() diff --git a/tests/unit_tests/resource_manager/test_resource_manager.py b/tests/unit_tests/resource_manager/test_resource_manager.py index a4630f0d5..2c0fc624b 100644 --- a/tests/unit_tests/resource_manager/test_resource_manager.py +++ b/tests/unit_tests/resource_manager/test_resource_manager.py @@ -33,5 +33,5 @@ def test_write_file(resource_manager): patch.object(S3Helper, 'upload_file'), \ patch.object(logger, 'info') as logger_mock: result = resource_manager.write_file('test.txt', 'content') - assert result == "test.txt saved successfully" - logger_mock.assert_called_once_with("test.txt saved successfully") + assert result == "test.txt - File written successfully" + logger_mock.assert_called_once_with("test.txt - File written successfully") diff --git a/tests/unit_tests/tools/test_send_email.py b/tests/unit_tests/tools/test_send_email.py index b0d80928e..20000fbf9 100644 --- a/tests/unit_tests/tools/test_send_email.py +++ b/tests/unit_tests/tools/test_send_email.py @@ -1,61 +1,65 @@ -from superagi.tools.email.send_email import SendEmailTool -import pytest +from unittest.mock import patch, Mock, MagicMock -def test_send_to_draft(mocker): +# from torch.testing._internal.distributed.rpc.jit.rpc_test import return_value - mock_get_config = mocker.patch('superagi.tools.email.send_email.get_config', autospec=True) - mock_get_config.side_effect = [ - 'test_sender@test.com', # EMAIL_ADDRESS - 'password', # EMAIL_PASSWORD - 'Test Signature', # EMAIL_SIGNATURE - "Draft", # EMAIL_DRAFT_MODE_WITH_FOLDER - 'smtp_host', # EMAIL_SMTP_HOST - 'smtp_port' # EMAIL_SMTP_PORT - ] +from superagi.tools.email.send_email import SendEmailTool +def mock_get_tool_config(key): + configs = { + 'EMAIL_ADDRESS': 'sender@example.com', + 'EMAIL_PASSWORD': 'password', + 'EMAIL_SIGNATURE': '', + 'EMAIL_DRAFT_MODE': 'False', + 'EMAIL_DRAFT_FOLDER': 'Drafts', + 'EMAIL_IMAP_SERVER': 'imap.example.com', + 'EMAIL_SMTP_HOST': 'host', + 'EMAIL_SMTP_PORT': 'port', + } + return configs.get(key) - # Mocking the ImapEmail call - mock_imap_email = mocker.patch('superagi.tools.email.send_email.ImapEmail') - mock_imap_instance = mock_imap_email.return_value.imap_open.return_value +def mock_get_draft_tool_config(key): + configs = { + 'EMAIL_ADDRESS': 'sender@example.com', + 'EMAIL_PASSWORD': 'password', + 'EMAIL_SIGNATURE': '', + 'EMAIL_DRAFT_MODE': 'True', + 'EMAIL_DRAFT_FOLDER': 'Drafts', + 'EMAIL_IMAP_SERVER': 'imap.example.com', + 'EMAIL_SMTP_HOST': 'host', + 'EMAIL_SMTP_PORT': 'port', + } + return configs.get(key) - # Mocking the SMTP call - mock_smtp = mocker.patch('smtplib.SMTP') - smtp_instance = mock_smtp.return_value - # Test the SendEmailTool's execute method +@patch('smtplib.SMTP') +@patch('superagi.helper.imap_email.ImapEmail.imap_open') +def test_execute_sends_email(mock_imap_open, mock_smtp): + # Given send_email_tool = SendEmailTool() - result = send_email_tool._execute('mukunda@contlo.com', 'Test Subject', 'Test Body') - - # Assert the return value - assert result == 'Email went to Draft' - -def test_send_to_mailbox(mocker): - # Mocking the get_config calls - mock_get_config = mocker.patch('superagi.tools.email.send_email.get_config') - mock_get_config.side_effect = [ - 'test_sender@test.com', # EMAIL_ADDRESS - 'password', # EMAIL_PASSWORD - 'Test Signature', # EMAIL_SIGNATURE - "YOUR_DRAFTS_FOLDER", # EMAIL_DRAFT_MODE_WITH_FOLDER - 'smtp_host', # EMAIL_SMTP_HOST - 'smtp_port' # EMAIL_SMTP_PORT - ] - - # mock_get_config.return_value = 'True' - # Mocking the ImapEmail call - mock_imap_email = mocker.patch('superagi.tools.email.send_email.ImapEmail') - mock_imap_instance = mock_imap_email.return_value.imap_open.return_value - - # Mocking the SMTP call - mock_smtp = mocker.patch('smtplib.SMTP') - smtp_instance = mock_smtp.return_value - - # Test the SendEmailTool's execute method + mock_resp = MagicMock() + mock_resp.raise_for_status.return_value = None + mock_resp.json.return_value = 'data' + + send_email_tool.toolkit_config.get_tool_config = mock_get_tool_config + + # When + result = send_email_tool._execute('receiver@example.com', 'test subject', 'test body') + + # Then + assert result == 'Email was sent to receiver@example.com' + mock_smtp.assert_called_once_with('host', 'port') + + +@patch('smtplib.SMTP') +@patch('superagi.helper.imap_email.ImapEmail.imap_open') +def test_execute_sends_email_to_draft(mock_imap_open, mock_smtp): send_email_tool = SendEmailTool() - result = send_email_tool._execute('test_receiver@test.com', 'Test Subject', 'Test Body') + send_email_tool.toolkit_config.get_tool_config = mock_get_draft_tool_config - # Assert that the ImapEmail was not called (no draft mode) - mock_imap_email.assert_not_called() + result = send_email_tool._execute('receiver@example.com', 'test subject', 'test body') - # Assert the return value - assert result == 'Email was sent to test_receiver@test.com' \ No newline at end of file + assert result == 'Email went to Drafts' + mock_imap_open.assert_called_once_with('Drafts', 'sender@example.com', 'password', 'imap.example.com') + mock_imap_instance = mock_imap_open.return_value + mock_imap_instance.append.assert_called_once() + mock_smtp.assert_not_called() \ No newline at end of file diff --git a/tests/unit_tests/tools/test_stable_diffusion_image_gen.py b/tests/unit_tests/tools/test_stable_diffusion_image_gen.py index 6d2dc75c0..be12f7969 100644 --- a/tests/unit_tests/tools/test_stable_diffusion_image_gen.py +++ b/tests/unit_tests/tools/test_stable_diffusion_image_gen.py @@ -7,6 +7,13 @@ from superagi.tools.image_generation.stable_diffusion_image_gen import StableDiffusionImageGenTool +def mock_get_tool_config(key): + configs = { + 'STABILITY_API_KEY': 'fake_api_key', + 'ENGINE_ID': 'engine_id_1', + } + return configs.get(key) + def create_sample_image_base64(): image = Image.new('RGBA', size=(50, 50), color=(73, 109, 137)) @@ -18,10 +25,9 @@ def create_sample_image_base64(): @pytest.fixture def stable_diffusion_tool(): - with patch('superagi.tools.image_generation.stable_diffusion_image_gen.get_config') as get_config_mock, \ - patch('superagi.tools.image_generation.stable_diffusion_image_gen.requests.post') as post_mock, \ - patch('superagi.tools.image_generation.stable_diffusion_image_gen.ResourceManager') as resource_manager_mock: - get_config_mock.return_value = 'fake_api_key' + with patch('superagi.tools.image_generation.stable_diffusion_image_gen.requests.post') as post_mock, \ + patch( + 'superagi.tools.image_generation.stable_diffusion_image_gen.ResourceManager') as resource_manager_mock: # Create a mock response object response_mock = Mock() @@ -38,6 +44,9 @@ def stable_diffusion_tool(): def test_execute(stable_diffusion_tool): tool = StableDiffusionImageGenTool() tool.resource_manager = Mock() + tool.toolkit_config.get_tool_config = mock_get_tool_config + + result = tool._execute('prompt', ['img1.png', 'img2.png']) assert result == 'Images downloaded and saved successfully' @@ -45,6 +54,7 @@ def test_execute(stable_diffusion_tool): def test_call_stable_diffusion(stable_diffusion_tool): tool = StableDiffusionImageGenTool() + tool.toolkit_config.get_tool_config = mock_get_tool_config response = tool.call_stable_diffusion('fake_api_key', 512, 512, 2, 'prompt', 50) assert response.status_code == 200 diff --git a/tools.json b/tools.json new file mode 100644 index 000000000..94de16ccc --- /dev/null +++ b/tools.json @@ -0,0 +1,4 @@ +{ + "tools": { + } +} \ No newline at end of file