diff --git a/README.md b/README.md index 4ca6fc4..d497bf2 100644 --- a/README.md +++ b/README.md @@ -8,8 +8,8 @@ ChainForge is built on [ReactFlow](https://reactflow.dev) and is in active devel # Features A key goal of ChainForge is facilitating **comparison** and **evaluation** of prompts and models, and (in the near future) prompt chains. Basic features are: -- **Prompt permutations**: Setup a prompt template and feed it variations of input variables. ChainForge will prompt all selected LLMs with all possible permutations of the input prompt, so that you can get a better sense of prompt quality. -- **Evaluation nodes**: Probe LLM responses in a chain and test them for some desired behavior. Initially, Python script based. +- **Prompt permutations**: Setup a prompt template and feed it variations of input variables. ChainForge will prompt all selected LLMs with all possible permutations of the input prompt, so that you can get a better sense of prompt quality. You can also chain prompt templates at arbitrary depth (e.g., to compare templates). +- **Evaluation nodes**: Probe LLM responses in a chain and test them (classically) for some desired behavior. At a basic level, this is Python script based. We plan to add preset evaluator nodes for common use cases in the near future (e.g., name-entity recognition). Note that you can also chain LLM responses into prompt templates to help evaluate outputs cheaply before more extensive evaluation methods. - **Visualization nodes**: Visualize evaluation results on plots like box-and-whisker and 3D scatterplots. Taken together, these three features let you easily: @@ -40,6 +40,10 @@ This spins up two local servers: a React server through npm, and a Python backen All ChainForge node graphs are importable/exportable as JSON specs. You can freely share prompt chains you develop (alongside any custom analysis code), whether to the public or within your organization. +## Example: Test LLM robustness to prompt injection + +... + # Development ChainForge is developed by research scientists at Harvard University in the [Harvard HCI](https://hci.seas.harvard.edu) group: diff --git a/chain-forge/package-lock.json b/chain-forge/package-lock.json index aa182ea..eabd204 100644 --- a/chain-forge/package-lock.json +++ b/chain-forge/package-lock.json @@ -14,6 +14,7 @@ "@reactflow/background": "^11.2.0", "@reactflow/controls": "^11.1.11", "@reactflow/core": "^11.7.0", + "@tabler/icons-react": "^2.17.0", "@testing-library/jest-dom": "^5.16.5", "@testing-library/react": "^13.4.0", "@testing-library/user-event": "^13.5.0", @@ -31,11 +32,14 @@ "plotly.js": "^2.21.0", "react": "^18.2.0", "react-ace": "^10.1.0", + "react-beautiful-dnd": "^13.1.1", "react-dom": "^18.2.0", "react-edit-text": "^5.1.0", "react-flow-renderer": "^10.3.17", "react-plotly.js": "^2.6.0", "react-scripts": "5.0.1", + "styled-components": "^5.3.10", + "uuidv4": "^6.2.13", "web-vitals": "^2.1.4", "zustand": "^4.3.7" } @@ -2328,11 +2332,18 @@ "integrity": "sha512-14FtKiHhy2QoPIzdTcvh//8OyBlknNs2nXRwIhG904opCby3l+9Xaf/wuPvICBF0rc1ZCNBd3nKe9cd2mecVkQ==", "peer": true }, + "node_modules/@emotion/is-prop-valid": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-1.2.0.tgz", + "integrity": "sha512-3aDpDprjM0AwaxGE09bOPkNxHpBd+kA6jty3RnaEXdweX1DF1U3VQpPYb0g1IStAuK7SVQ1cy+bNBBKp4W3Fjg==", + "dependencies": { + "@emotion/memoize": "^0.8.0" + } + }, "node_modules/@emotion/memoize": { "version": "0.8.0", "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.8.0.tgz", - "integrity": "sha512-G/YwXTkv7Den9mXDO7AhLWkE3q+I92B+VqAE+dYG4NGPaHZGvt3G8Q0p9vmE+sq7rTGphUbAvmQ9YpbfMQGGlA==", - "peer": true + "integrity": "sha512-G/YwXTkv7Den9mXDO7AhLWkE3q+I92B+VqAE+dYG4NGPaHZGvt3G8Q0p9vmE+sq7rTGphUbAvmQ9YpbfMQGGlA==" }, "node_modules/@emotion/react": { "version": "11.10.8", @@ -2377,6 +2388,11 @@ "integrity": "sha512-zxRBwl93sHMsOj4zs+OslQKg/uhF38MB+OMKoCrVuS0nyTkqnau+BM3WGEoOptg9Oz45T/aIGs1qbVAsEFo3nA==", "peer": true }, + "node_modules/@emotion/stylis": { + "version": "0.8.5", + "resolved": "https://registry.npmjs.org/@emotion/stylis/-/stylis-0.8.5.tgz", + "integrity": "sha512-h6KtPihKFn3T9fuIrwvXXUOwlx3rfUvfZIcP5a6rh8Y7zjE3O06hT5Ss4S/YI1AYhuZ1kjaE/5EaOOI2NqSylQ==" + }, "node_modules/@emotion/unitless": { "version": "0.8.0", "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.8.0.tgz", @@ -4115,6 +4131,31 @@ "url": "https://github.com/sponsors/gregberge" } }, + "node_modules/@tabler/icons": { + "version": "2.17.0", + "resolved": "https://registry.npmjs.org/@tabler/icons/-/icons-2.17.0.tgz", + "integrity": "sha512-UeJaylOGNRhQKyDlgZfrQ3UPSGlfVQuXcmCsTYeXioKKepibW6VZ3H36Lo1jvBTBkQD2e9m+k2NxwkztOTXwrA==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/codecalm" + } + }, + "node_modules/@tabler/icons-react": { + "version": "2.17.0", + "resolved": "https://registry.npmjs.org/@tabler/icons-react/-/icons-react-2.17.0.tgz", + "integrity": "sha512-kuEW+qNwRqcK5iMl7qTapzX2NiMOwPg4Az01d+IZ1DIMwaZ7iKPJaIor2ihKFLPYrT9D5BZHXB8R5mSkw0FETg==", + "dependencies": { + "@tabler/icons": "2.17.0", + "prop-types": "^15.7.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/codecalm" + }, + "peerDependencies": { + "react": "^16.5.1 || ^17.0.0 || ^18.0.0" + } + }, "node_modules/@testing-library/dom": { "version": "9.2.0", "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-9.2.0.tgz", @@ -4826,6 +4867,15 @@ "@types/node": "*" } }, + "node_modules/@types/hoist-non-react-statics": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.1.tgz", + "integrity": "sha512-iMIqiko6ooLrTh1joXodJK5X9xeEALT1kM5G3ZLhD3hszxBdIEd5C75U834D9mLcINgD4OyZf5uQXjkuYydWvA==", + "dependencies": { + "@types/react": "*", + "hoist-non-react-statics": "^3.3.0" + } + }, "node_modules/@types/html-minifier-terser": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/@types/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz", @@ -5164,6 +5214,17 @@ "@types/react": "*" } }, + "node_modules/@types/react-redux": { + "version": "7.1.25", + "resolved": "https://registry.npmjs.org/@types/react-redux/-/react-redux-7.1.25.tgz", + "integrity": "sha512-bAGh4e+w5D8dajd6InASVIyCo4pZLJ66oLb80F9OBLO1gKESbZcRCJpTT6uLXX+HAB57zw1WTdwJdAsewuTweg==", + "dependencies": { + "@types/hoist-non-react-statics": "^3.3.0", + "@types/react": "*", + "hoist-non-react-statics": "^3.3.0", + "redux": "^4.0.0" + } + }, "node_modules/@types/resize-observer-browser": { "version": "0.1.7", "resolved": "https://registry.npmjs.org/@types/resize-observer-browser/-/resize-observer-browser-0.1.7.tgz", @@ -5235,6 +5296,11 @@ "resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.3.tgz", "integrity": "sha512-NfQ4gyz38SL8sDNrSixxU2Os1a5xcdFxipAFxYEuLUlvU2uDwS4NUpsImcf1//SlWItCVMMLiylsxbmNMToV/g==" }, + "node_modules/@types/uuid": { + "version": "8.3.4", + "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-8.3.4.tgz", + "integrity": "sha512-c/I8ZRb51j+pYGAu5CrFMRxqZ2ke4y2grEBO5AUjgSkSk+qT2Ea+OdWElz/OiMf5MNpn2b17kuVBwZLQJXzihw==" + }, "node_modules/@types/ws": { "version": "8.5.4", "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.4.tgz", @@ -6446,6 +6512,26 @@ "@babel/core": "^7.0.0-0" } }, + "node_modules/babel-plugin-styled-components": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/babel-plugin-styled-components/-/babel-plugin-styled-components-2.1.1.tgz", + "integrity": "sha512-c8lJlszObVQPguHkI+akXv8+Jgb9Ccujx0EetL7oIvwU100LxO6XAGe45qry37wUL40a5U9f23SYrivro2XKhA==", + "dependencies": { + "@babel/helper-annotate-as-pure": "^7.16.0", + "@babel/helper-module-imports": "^7.16.0", + "babel-plugin-syntax-jsx": "^6.18.0", + "lodash": "^4.17.21", + "picomatch": "^2.3.0" + }, + "peerDependencies": { + "styled-components": ">= 2" + } + }, + "node_modules/babel-plugin-syntax-jsx": { + "version": "6.18.0", + "resolved": "https://registry.npmjs.org/babel-plugin-syntax-jsx/-/babel-plugin-syntax-jsx-6.18.0.tgz", + "integrity": "sha512-qrPaCSo9c8RHNRHIotaufGbuOBN8rtdC4QrrFFc43vyWCCz7Kl7GL1PGaXtMGQZUXrkCjNEgxDfmAuAabr/rlw==" + }, "node_modules/babel-plugin-transform-react-remove-prop-types": { "version": "0.4.24", "resolved": "https://registry.npmjs.org/babel-plugin-transform-react-remove-prop-types/-/babel-plugin-transform-react-remove-prop-types-0.4.24.tgz", @@ -6815,6 +6901,14 @@ "node": ">= 6" } }, + "node_modules/camelize": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/camelize/-/camelize-1.0.1.tgz", + "integrity": "sha512-dU+Tx2fsypxTgtLoE36npi3UqcjSSMNYfkqgmoEhtZrraP5VWq0K7FkWVTYa8eMPtnU/G2txVsfdCJTn9uzpuQ==", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/caniuse-api": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/caniuse-api/-/caniuse-api-3.0.0.tgz", @@ -7446,6 +7540,22 @@ "postcss": "^8.4" } }, + "node_modules/css-box-model": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/css-box-model/-/css-box-model-1.2.1.tgz", + "integrity": "sha512-a7Vr4Q/kd/aw96bnJG332W9V9LkJO69JRcaCYDUqjp6/z0w6VcZjgAcTbgFxEPfBgdnAwlh3iwu+hLopa+flJw==", + "dependencies": { + "tiny-invariant": "^1.0.6" + } + }, + "node_modules/css-color-keywords": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/css-color-keywords/-/css-color-keywords-1.0.0.tgz", + "integrity": "sha512-FyyrDHZKEjXDpNJYvVsV960FiqQyXc/LlYmsxl2BcdMb2WPx0OGRVgTg55rPSyLSNMqP52R9r8geSp7apN3Ofg==", + "engines": { + "node": ">=4" + } + }, "node_modules/css-declaration-sorter": { "version": "6.4.0", "resolved": "https://registry.npmjs.org/css-declaration-sorter/-/css-declaration-sorter-6.4.0.tgz", @@ -7673,6 +7783,16 @@ "resolved": "https://registry.npmjs.org/css-system-font-keywords/-/css-system-font-keywords-1.0.0.tgz", "integrity": "sha512-1umTtVd/fXS25ftfjB71eASCrYhilmEsvDEI6wG/QplnmlfmVM5HkZ/ZX46DT5K3eblFPgLUHt5BRCb0YXkSFA==" }, + "node_modules/css-to-react-native": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/css-to-react-native/-/css-to-react-native-3.2.0.tgz", + "integrity": "sha512-e8RKaLXMOFii+02mOlqwjbD00KSEKqblnpO9e++1aXS1fPQOpS1YoqdVHBqPjHNoxeF2mimzVqawm2KCbEdtHQ==", + "dependencies": { + "camelize": "^1.0.0", + "css-color-keywords": "^1.0.0", + "postcss-value-parser": "^4.0.2" + } + }, "node_modules/css-tree": { "version": "1.0.0-alpha.37", "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.0.0-alpha.37.tgz", @@ -10902,7 +11022,6 @@ "version": "3.3.2", "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz", "integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==", - "peer": true, "dependencies": { "react-is": "^16.7.0" } @@ -10910,8 +11029,7 @@ "node_modules/hoist-non-react-statics/node_modules/react-is": { "version": "16.13.1", "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", - "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", - "peer": true + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" }, "node_modules/hoopy": { "version": "0.1.4", @@ -14339,6 +14457,11 @@ "node": ">= 4.0.0" } }, + "node_modules/memoize-one": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/memoize-one/-/memoize-one-5.2.1.tgz", + "integrity": "sha512-zYiwtZUcYyXKo/np96AGZAckk+FWWsUdJ3cHGGmld7+AhvcWmQyGCYUh1hc4Q/pkOhb65dQR/pqCyK0cOaHz4Q==" + }, "node_modules/merge-descriptors": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", @@ -16814,6 +16937,11 @@ "performance-now": "^2.1.0" } }, + "node_modules/raf-schd": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/raf-schd/-/raf-schd-4.0.3.tgz", + "integrity": "sha512-tQkJl2GRWh83ui2DiPTJz9wEiMN20syf+5oKfB03yYP7ioZcJwsIK8FjrtLwH1m7C7e+Tt2yYBlrOpdT+dyeIQ==" + }, "node_modules/randombytes": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", @@ -16912,6 +17040,24 @@ "node": ">=14" } }, + "node_modules/react-beautiful-dnd": { + "version": "13.1.1", + "resolved": "https://registry.npmjs.org/react-beautiful-dnd/-/react-beautiful-dnd-13.1.1.tgz", + "integrity": "sha512-0Lvs4tq2VcrEjEgDXHjT98r+63drkKEgqyxdA7qD3mvKwga6a5SscbdLPO2IExotU1jW8L0Ksdl0Cj2AF67nPQ==", + "dependencies": { + "@babel/runtime": "^7.9.2", + "css-box-model": "^1.2.0", + "memoize-one": "^5.1.1", + "raf-schd": "^4.0.2", + "react-redux": "^7.2.0", + "redux": "^4.0.4", + "use-memo-one": "^1.1.1" + }, + "peerDependencies": { + "react": "^16.8.5 || ^17.0.0 || ^18.0.0", + "react-dom": "^16.8.5 || ^17.0.0 || ^18.0.0" + } + }, "node_modules/react-dev-utils": { "version": "12.0.1", "resolved": "https://registry.npmjs.org/react-dev-utils/-/react-dev-utils-12.0.1.tgz", @@ -17118,6 +17264,30 @@ "react": ">0.13.0" } }, + "node_modules/react-redux": { + "version": "7.2.9", + "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-7.2.9.tgz", + "integrity": "sha512-Gx4L3uM182jEEayZfRbI/G11ZpYdNAnBs70lFVMNdHJI76XYtR+7m0MN+eAs7UHBPhWXcnFPaS+9owSCJQHNpQ==", + "dependencies": { + "@babel/runtime": "^7.15.4", + "@types/react-redux": "^7.1.20", + "hoist-non-react-statics": "^3.3.2", + "loose-envify": "^1.4.0", + "prop-types": "^15.7.2", + "react-is": "^17.0.2" + }, + "peerDependencies": { + "react": "^16.8.3 || ^17 || ^18" + }, + "peerDependenciesMeta": { + "react-dom": { + "optional": true + }, + "react-native": { + "optional": true + } + } + }, "node_modules/react-refresh": { "version": "0.11.0", "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.11.0.tgz", @@ -17336,6 +17506,14 @@ "node": ">=8" } }, + "node_modules/redux": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/redux/-/redux-4.2.1.tgz", + "integrity": "sha512-LAUYz4lc+Do8/g7aeRa8JkyDErK6ekstQaqWQrNRW//MY1TvCEpMtpTWvlQ+FPbWCx+Xixu/6SHt5N0HR+SB4w==", + "dependencies": { + "@babel/runtime": "^7.9.2" + } + }, "node_modules/regenerate": { "version": "1.4.2", "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.2.tgz", @@ -18092,6 +18270,11 @@ "resolved": "https://registry.npmjs.org/shallow-copy/-/shallow-copy-0.0.1.tgz", "integrity": "sha512-b6i4ZpVuUxB9h5gfCxPiusKYkqTMOjEbBs4wMaFbkfia4yFv92UKZ6Df8WXcKbn08JNL/abvg3FnMAOfakDvUw==" }, + "node_modules/shallowequal": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/shallowequal/-/shallowequal-1.1.0.tgz", + "integrity": "sha512-y0m1JoUZSlPAjXVtPPW70aZWfIL/dSP7AFkRnniLCrK/8MDKog3TySTBmckD+RObVxH0v4Tox67+F14PdED2oQ==" + }, "node_modules/shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", @@ -18653,6 +18836,40 @@ "resolved": "https://registry.npmjs.org/style-mod/-/style-mod-4.0.2.tgz", "integrity": "sha512-C4myMmRTO8iaC5Gg+N1ftK2WT4eXUTMAa+HEFPPrfVeO/NtqLTtAmV1HbqnuGtLwCek44Ra76fdGUkSqjiMPcQ==" }, + "node_modules/styled-components": { + "version": "5.3.10", + "resolved": "https://registry.npmjs.org/styled-components/-/styled-components-5.3.10.tgz", + "integrity": "sha512-3kSzSBN0TiCnGJM04UwO1HklIQQSXW7rCARUk+VyMR7clz8XVlA3jijtf5ypqoDIdNMKx3la4VvaPFR855SFcg==", + "dependencies": { + "@babel/helper-module-imports": "^7.0.0", + "@babel/traverse": "^7.4.5", + "@emotion/is-prop-valid": "^1.1.0", + "@emotion/stylis": "^0.8.4", + "@emotion/unitless": "^0.7.4", + "babel-plugin-styled-components": ">= 1.12.0", + "css-to-react-native": "^3.0.0", + "hoist-non-react-statics": "^3.0.0", + "shallowequal": "^1.1.0", + "supports-color": "^5.5.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/styled-components" + }, + "peerDependencies": { + "react": ">= 16.8.0", + "react-dom": ">= 16.8.0", + "react-is": ">= 16.8.0" + } + }, + "node_modules/styled-components/node_modules/@emotion/unitless": { + "version": "0.7.5", + "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.7.5.tgz", + "integrity": "sha512-OWORNpfjMsSSUBVrRBVGECkhWcULOAJz9ZW8uK9qgxD+87M7jHRcvh/A96XXNhXTLmKcoYSQtBEX7lHMO7YRwg==" + }, "node_modules/stylehacks": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/stylehacks/-/stylehacks-5.1.1.tgz", @@ -19167,6 +19384,11 @@ "resolved": "https://registry.npmjs.org/thunky/-/thunky-1.1.0.tgz", "integrity": "sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==" }, + "node_modules/tiny-invariant": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.3.1.tgz", + "integrity": "sha512-AD5ih2NlSssTCwsMznbvwMZpJ1cbhkGd2uueNxzv2jDlEeZdU04JQfRnggJQ8DrcVBGjAsCKwFBbDlVNtEMlzw==" + }, "node_modules/tinycolor2": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/tinycolor2/-/tinycolor2-1.6.0.tgz", @@ -19654,6 +19876,14 @@ } } }, + "node_modules/use-memo-one": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/use-memo-one/-/use-memo-one-1.1.3.tgz", + "integrity": "sha512-g66/K7ZQGYrI6dy8GLpVcMsBp4s17xNkYJVSMvTEevGy3nDxHOfE6z8BVE22+5G5x7t3+bhzrlTDB7ObrEE0cQ==", + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + } + }, "node_modules/use-sidecar": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/use-sidecar/-/use-sidecar-1.1.2.tgz", @@ -19766,6 +19996,15 @@ "uuid": "dist/bin/uuid" } }, + "node_modules/uuidv4": { + "version": "6.2.13", + "resolved": "https://registry.npmjs.org/uuidv4/-/uuidv4-6.2.13.tgz", + "integrity": "sha512-AXyzMjazYB3ovL3q051VLH06Ixj//Knx7QnUSi1T//Ie3io6CpsPu9nVMOx5MoLWh6xV0B9J0hIaxungxXUbPQ==", + "dependencies": { + "@types/uuid": "8.3.4", + "uuid": "8.3.2" + } + }, "node_modules/v8-to-istanbul": { "version": "8.1.1", "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-8.1.1.tgz", diff --git a/chain-forge/package.json b/chain-forge/package.json index c6a8d39..e799d6e 100644 --- a/chain-forge/package.json +++ b/chain-forge/package.json @@ -9,6 +9,7 @@ "@reactflow/background": "^11.2.0", "@reactflow/controls": "^11.1.11", "@reactflow/core": "^11.7.0", + "@tabler/icons-react": "^2.17.0", "@testing-library/jest-dom": "^5.16.5", "@testing-library/react": "^13.4.0", "@testing-library/user-event": "^13.5.0", @@ -26,11 +27,14 @@ "plotly.js": "^2.21.0", "react": "^18.2.0", "react-ace": "^10.1.0", + "react-beautiful-dnd": "^13.1.1", "react-dom": "^18.2.0", "react-edit-text": "^5.1.0", "react-flow-renderer": "^10.3.17", "react-plotly.js": "^2.6.0", "react-scripts": "5.0.1", + "styled-components": "^5.3.10", + "uuidv4": "^6.2.13", "web-vitals": "^2.1.4", "zustand": "^4.3.7" }, diff --git a/chain-forge/src/AlertModal.js b/chain-forge/src/AlertModal.js new file mode 100644 index 0000000..53fa4d0 --- /dev/null +++ b/chain-forge/src/AlertModal.js @@ -0,0 +1,28 @@ +/** An alert popup for displaying errors */ +import React, { useState, forwardRef, useImperativeHandle } from 'react'; +import { useDisclosure } from '@mantine/hooks'; +import { Modal } from '@mantine/core'; + +const AlertModal = forwardRef((props, ref) => { + // Mantine modal popover for alerts + const [opened, { open, close }] = useDisclosure(false); + const [alertMsg, setAlertMsg] = useState(''); + + // This gives the parent access to triggering the modal alert + const trigger = (msg) => { + console.error(msg); + setAlertMsg(msg); + open(); + }; + useImperativeHandle(ref, () => ({ + trigger, + })); + + return ( + +

{alertMsg}

+
+ ); +}); + +export default AlertModal; \ No newline at end of file diff --git a/chain-forge/src/EvaluatorNode.js b/chain-forge/src/EvaluatorNode.js index c779c81..bed0344 100644 --- a/chain-forge/src/EvaluatorNode.js +++ b/chain-forge/src/EvaluatorNode.js @@ -1,12 +1,9 @@ -import React, { useState, useEffect } from 'react'; +import React, { useState, useEffect, useRef } from 'react'; import { Handle } from 'react-flow-renderer'; import useStore from './store'; import StatusIndicator from './StatusIndicatorComponent' import NodeLabel from './NodeLabelComponent' - -// Mantine modal -import { useDisclosure } from '@mantine/hooks'; -import { Modal } from '@mantine/core'; +import AlertModal from './AlertModal' // Ace code editor import AceEditor from "react-ace"; @@ -14,28 +11,6 @@ import "ace-builds/src-noconflict/mode-python"; import "ace-builds/src-noconflict/theme-xcode"; import "ace-builds/src-noconflict/ext-language_tools"; -// CodeMirror text editor -// import CodeMirror from '@uiw/react-codemirror'; -// import { globalCompletion, python } from '@codemirror/lang-python'; -// // import { Decoration, DecorationSet, EditorView, ViewPlugin, ViewUpdate} from '@codemirror/view'; -// import { indentUnit } from '@codemirror/language'; -// import { okaidia } from '@uiw/codemirror-theme-okaidia'; // dark theme -// import { solarizedDark } from '@uiw/codemirror-theme-solarized'; // dark theme; warm -// import { noctisLilac } from '@uiw/codemirror-theme-noctis-lilac'; // light theme NOTE: Unfortunately this does not show selected text, no idea why. -// import { materialLight } from '@uiw/codemirror-theme-material'; // light theme, material -// import { xcodeDark, xcodeLight } from '@uiw/codemirror-theme-xcode'; // light theme, xcode -// import { sublime } from '@uiw/codemirror-theme-sublime'; - -// Experimenting with making the 'def evaluator' line read-only -// import readOnlyRangesExtension from 'codemirror-readonly-ranges' -// const getReadOnlyRanges = (editorState) => { -// return [{ -// from: editorState.doc.line(2).from, //same as: editorState.doc.line(0).from or 0 -// to: editorState.doc.line(2).to -// }] -// } -// // Add readOnlyRangesExtension(getReadOnlyRanges) to extensions prop of CodeMirror component - const EvaluatorNode = ({ data, id }) => { const inputEdgesForNode = useStore((state) => state.inputEdgesForNode); @@ -45,9 +20,8 @@ const EvaluatorNode = ({ data, id }) => { const [status, setStatus] = useState('none'); const nodes = useStore((state) => state.nodes); - // Mantine modal popover for alerts - const [opened, { open, close }] = useDisclosure(false); - const [alertMsg, setAlertMsg] = useState(''); + // For displaying error messages to user + const alertModal = useRef(null); const [hovered, setHovered] = useState(false); const [codeText, setCodeText] = useState(data.code); @@ -75,13 +49,6 @@ const EvaluatorNode = ({ data, id }) => { setDataPropsForNode(id, {code: code}); }; - // Trigger an alert modal popover with Mantine: - const triggerErrorAlert = (msg) => { - console.error(msg); - setAlertMsg(msg); - open(); - }; - const handleRunClick = (event) => { // Get the ids from the connected input nodes: @@ -96,7 +63,7 @@ const EvaluatorNode = ({ data, id }) => { if (codeText.search(/def\s+evaluate\s*(.*):/) === -1) { const err_msg = `Could not find required function 'evaluate'. Make sure you have defined an 'evaluate' function.`; setStatus('error'); - triggerErrorAlert(err_msg); + alertModal.current.trigger(err_msg); return; } @@ -104,7 +71,7 @@ const EvaluatorNode = ({ data, id }) => { const rejected = (err_msg) => { setStatus('error'); - triggerErrorAlert(err_msg); + alertModal.current.trigger(err_msg); }; // Get all the script nodes, and get all the folder paths @@ -133,7 +100,7 @@ const EvaluatorNode = ({ data, id }) => { // Check if there's an error; if so, bubble it up to user and exit: if (json.error) { setStatus('error'); - triggerErrorAlert(json.error); + alertModal.current.trigger(json.error); return; } @@ -217,9 +184,7 @@ const EvaluatorNode = ({ data, id }) => { id="output" style={{ top: '50%', background: '#555' }} /> - -

{alertMsg}

-
+
Function to map over each  
-
-

LLMs:

-
- - - - - - +
+
+ Models to query: +
+ + + + + + {allLLMs.map(item => ( addModel(item.model)} icon={item.emoji}>{item.name}))} + + +
+
+ +
+ + {/* + + + + + */} +
-
{responsePreviews}
diff --git a/chain-forge/src/SettingsIcon.js b/chain-forge/src/SettingsIcon.js new file mode 100644 index 0000000..e69de29 diff --git a/chain-forge/src/StrictModeDroppable.js b/chain-forge/src/StrictModeDroppable.js new file mode 100644 index 0000000..6e52970 --- /dev/null +++ b/chain-forge/src/StrictModeDroppable.js @@ -0,0 +1,19 @@ +// StrictModeDroppable.tsx +// Credits to https://github.com/GiovanniACamacho and https://github.com/Meligy for the TypeScript version +// Original post: https://github.com/atlassian/react-beautiful-dnd/issues/2399#issuecomment-1175638194 +import { useEffect, useState } from "react"; +import { Droppable } from "react-beautiful-dnd"; +export const StrictModeDroppable = ({ children, ...props }) => { + const [enabled, setEnabled] = useState(false); + useEffect(() => { + const animation = requestAnimationFrame(() => setEnabled(true)); + return () => { + cancelAnimationFrame(animation); + setEnabled(false); + }; + }, []); + if (!enabled) { + return null; + } + return {children}; +}; \ No newline at end of file diff --git a/chain-forge/src/TemperatureSliderComponent.js b/chain-forge/src/TemperatureSliderComponent.js new file mode 100644 index 0000000..eecd6fe --- /dev/null +++ b/chain-forge/src/TemperatureSliderComponent.js @@ -0,0 +1,31 @@ +import { Slider, RangeSlider } from '@mantine/core'; +import { IconTemperature, IconTemperatureOff } from '@tabler/icons-react'; + +const styles = { thumb: { borderWidth: 2, height: 24, width: 24, padding: 3 } }; + +export default function TemperatureSlider({style}) { + return ( +
+ } + color="red" + label={null} + defaultValue={40} + styles={styles} + onPointerDown={e => {e.preventDefault();}} + /> + + {/* , + , + ]} + /> */} +
+ ); +} \ No newline at end of file diff --git a/chain-forge/src/TemplateHooksComponent.js b/chain-forge/src/TemplateHooksComponent.js index 8589f47..d3f19dc 100644 --- a/chain-forge/src/TemplateHooksComponent.js +++ b/chain-forge/src/TemplateHooksComponent.js @@ -1,16 +1,17 @@ import React, { useCallback, useEffect, useState } from 'react'; import { Handle } from 'react-flow-renderer'; +import { Badge } from '@mantine/core'; import useStore from './store' export default function TemplateHooks({ vars, nodeId, startY }) { const genTemplateHooks = useCallback((temp_var_names, names_to_blink) => { return temp_var_names.map((name, idx) => { - const className = (names_to_blink.includes(name)) ? 'text-blink' : ''; + const className = (names_to_blink.includes(name)) ? 'hook-tag text-blink' : 'hook-tag'; const pos = (idx * 35) + startY + 'px'; const style = { top: pos, background: '#555' }; return (
-

{name}

+ {name}
) }); diff --git a/chain-forge/src/text-fields-node.css b/chain-forge/src/text-fields-node.css index 9b74b7b..0694cca 100644 --- a/chain-forge/src/text-fields-node.css +++ b/chain-forge/src/text-fields-node.css @@ -5,6 +5,11 @@ border-radius: 5px; } + .small-standard-font { + font-size: 10pt; + font-family: -apple-system, 'Segoe UI', 'Roboto', 'Oxygen', 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif; + } + .status-icon { display: inline-block; font-size: 14pt; @@ -147,25 +152,26 @@ border-color: #aaa; padding: 2px; margin: 0px; - background-color: #fff; + background-color: rgba(255, 255, 255, 0.4); white-space: pre-wrap; } .llm-response-container { max-width: 450px; - background-color: #fff; } .llm-response-container h1 { + font-weight: 400; font-size: 10pt; - font-weight: bold; - margin: 4px; + margin: 6px 8px 0px 8px; padding-top: 2px; - padding-bottom: 2px; + padding-bottom: 0px; color: #222; } .response-preview-container { - max-height: 180px; + margin: 10px -9px -9px -9px; + max-height: 100px; overflow-y: auto; + background-color: #ace1aeb1; /* ACE1AF */ } .response-box { padding: 2px; @@ -174,7 +180,6 @@ .response-tag { font-size: 9pt; color: #555; - font-weight: 700; margin: 0px 0px 0px 2px; } @@ -198,6 +203,11 @@ font-family: 'Courier New', Courier, monospace; } + .hook-tag { + padding-top: 11px;; + padding-bottom: 3px; + } + .code-mirror-field-header { font-size: 10pt; color: #333; @@ -214,6 +224,29 @@ justify-content: center; } + .add-llm-model-btn { + display: flex; + align-items: right; + justify-content: right; + float: right; + height: 20px; + } + .add-llm-model-btn button { + border-style: solid; + border-color: #777; + border-width: 1pt; + border-radius: 3px; + color: #777; + margin-right: 2px; + } + .add-llm-model-btn:hover button { + background-color: white; + } + .add-llm-model-btn:active button { + background-color: black; + color: white; + } + /** CSS Button style from https://css-buttons-hover.netlify.app/ */ .AmitSahoo45-button-3 { position: relative;