Merge branch 'main' into pv

This commit is contained in:
Priyan Vaithilingam 2023-05-03 11:16:49 -04:00
commit fb8c1ccd46
14 changed files with 640 additions and 111 deletions

View File

@ -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:

View File

@ -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",

View File

@ -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"
},

View File

@ -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 (
<Modal opened={opened} onClose={close} title="Error" styles={{header: {backgroundColor: '#E52A2A', color: 'white'}}}>
<p>{alertMsg}</p>
</Modal>
);
});
export default AlertModal;

View File

@ -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' }}
/>
<Modal opened={opened} onClose={close} title="Error" styles={{header: {backgroundColor: '#E52A2A', color: 'white'}}}>
<p>{alertMsg}</p>
</Modal>
<AlertModal ref={alertModal} />
<div className="core-mirror-field">
<div className="code-mirror-field-header">Function to map over each &nbsp;
<select name="mapscope" id="mapscope" onChange={handleOnMapScopeSelect}>

View File

@ -0,0 +1,20 @@
import { useDisclosure } from '@mantine/hooks';
import { Modal, Button, Group } from '@mantine/core';
import { IconSettings, IconTrash } from '@tabler/icons-react';
export default function LLMItemButtonGroup( {onClickTrash, onClickSettings} ) {
const [opened, { open, close }] = useDisclosure(false);
return (
<div>
<Modal opened={opened} onClose={close} title="Authentication">
{/* Modal content */}
</Modal>
<Group position="right" style={{float: 'right', height:'20px'}}>
<Button onClick={onClickTrash} size="xs" variant="light" compact color="red" style={{padding: '0px'}} ><IconTrash size={"95%"} /></Button>
<Button onClick={onClickSettings} size="xs" variant="light" compact>Settings&nbsp;<IconSettings size={"110%"} /></Button>
</Group>
</div>
);
}

View File

@ -0,0 +1,77 @@
import { useState, useEffect, useCallback } from "react";
import { DragDropContext, Droppable, Draggable } from "react-beautiful-dnd";
import LLMListItem, { DragItem, LLMListItemClone } from "./LLMListItem";
import { StrictModeDroppable } from './StrictModeDroppable'
export default function LLMList({llms, onItemsChange}) {
const [items, setItems] = useState(llms);
const updateItems = useCallback((new_items) => {
setItems(new_items);
onItemsChange(new_items);
}, [onItemsChange]);
const onDragEnd = (result) => {
const { destination, source } = result;
if (!destination) return;
if (
destination.droppableId === source.droppableId &&
destination.index === source.index
) {
return;
}
const newItems = Array.from(items);
const [removed] = newItems.splice(result.source.index, 1);
newItems.splice(result.destination.index, 0, removed);
setItems(newItems);
};
const removeItem = useCallback((item_key) => {
// Double-check that the item we want to remove is in the list of items...
if (!items.find(i => i.key === item_key)) {
console.error(`Could not remove model from LLM list: Could not find item with key ${item_key}.`);
return;
}
// Remove it
updateItems(items.filter(i => i.key !== item_key));
}, [items, updateItems]);
useEffect(() => {
// When LLMs list changes, we need to add new items
// while preserving the current order of 'items'.
// Check for new items and for each, add to end:
let new_items = Array.from(items);
llms.forEach(item => {
if (!items.find(i => i.key === item.key))
new_items.push(item);
});
updateItems(new_items);
}, [llms, updateItems]);
return (
<div className="list nowheel nodrag">
<DragDropContext onDragEnd={onDragEnd}>
<StrictModeDroppable
droppableId="llm-list-droppable"
renderClone={(provided, snapshot, rubric) => (
<LLMListItemClone provided={provided} snapshot={snapshot} item={items[rubric.source.index]} />
)}
>
{(provided) => (
<div {...provided.droppableProps} ref={provided.innerRef}>
{items.map((item, index) => (
<Draggable key={item.key} draggableId={item.key} index={index}>
{(provided, snapshot) => (
<LLMListItem provided={provided} snapshot={snapshot} item={item} removeCallback={removeItem} />
)}
</Draggable>
))}
{provided.placeholder}
</div>
)}
</StrictModeDroppable>
</DragDropContext>
</div>
);
}

View File

@ -0,0 +1,59 @@
/** Thanks to Kabir Haruna: https://codesandbox.io/s/i0rxsj */
import React from "react";
import styled from "styled-components";
import LLMItemButtonGroup from "./LLMItemButtonGroup"
const CardHeader = styled.div`
font-weight: 500;
font-size: 10pt;
font-family: -apple-system, 'Segoe UI', 'Roboto', 'Oxygen', 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif;
text-align: start;
float: left;
margin-top: 1px;
`;
export const DragItem = styled.div`
padding: 10px;
border-radius: 6px;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.12), 0 1px 2px rgba(0, 0, 0, 0.24);
background: white;
margin: 0 0 8px 0;
display: grid;
grid-gap: 20px;
flex-direction: column;
`;
const LLMListItem = ({ item, provided, snapshot, removeCallback }) => {
return (
<DragItem
ref={provided.innerRef}
snapshot={snapshot}
{...provided.draggableProps}
{...provided.dragHandleProps}
>
<div>
<CardHeader>{item.emoji}&nbsp;{item.name}</CardHeader>
<LLMItemButtonGroup onClickTrash={() => removeCallback(item.key)} />
</div>
</DragItem>
);
};
export const LLMListItemClone = ({ item, provided, snapshot }) => {
return (
<DragItem
ref={provided.innerRef}
{...provided.draggableProps}
{...provided.dragHandleProps}
snapshot={snapshot}
>
<div>
<CardHeader>{item.emoji}&nbsp;{item.name}</CardHeader>
<LLMItemButtonGroup />
</div>
</DragItem>
);
};
export default LLMListItem;

View File

@ -1,9 +1,23 @@
import React, { useEffect, useState, useCallback } from 'react';
import React, { useEffect, useState, useRef, useCallback } from 'react';
import { Handle } from 'react-flow-renderer';
import { Menu, Badge } from '@mantine/core';
import { v4 as uuid } from 'uuid';
import useStore from './store';
import StatusIndicator from './StatusIndicatorComponent'
import NodeLabel from './NodeLabelComponent'
import TemplateHooks from './TemplateHooksComponent'
import LLMList from './LLMListComponent'
import AlertModal from './AlertModal'
// Available LLMs
const allLLMs = [
{ name: "GPT3.5", emoji: "🙂", model: "gpt3.5", temp: 1.0 },
{ name: "GPT4", emoji: "🥵", model: "gpt4", temp: 1.0 },
{ name: "Alpaca 7B", emoji: "🦙", model: "alpaca.7B", temp: 0.5 },
{ name: "Claude v1", emoji: "📚", model: "claude.v1", temp: 0.5 },
{ name: "Ian Chatbot", emoji: "💩", model: "test", temp: 0.5 }
];
const initLLMs = [allLLMs[0]];
// Helper funcs
const truncStr = (s, maxLen) => {
@ -17,7 +31,7 @@ const vars_to_str = (vars) => {
const s = truncStr(vars[varname].trim(), 12);
return `${varname} = '${s}'`;
});
return pairs.join('; ');
return pairs;
};
const bucketResponsesByLLM = (responses) => {
let responses_by_llm = {};
@ -42,10 +56,36 @@ const PromptNode = ({ data, id }) => {
const [templateVars, setTemplateVars] = useState(data.vars || []);
const [promptText, setPromptText] = useState(data.prompt);
const [promptTextOnLastRun, setPromptTextOnLastRun] = useState(null);
const [selectedLLMs, setSelectedLLMs] = useState(['gpt3.5']);
const [status, setStatus] = useState('none');
const [responsePreviews, setReponsePreviews] = useState([]);
const [numGenerations, setNumGenerations] = useState(data.n || 1);
// For displaying error messages to user
const alertModal = useRef(null);
// Selecting LLM models to prompt
const [llmItems, setLLMItems] = useState(initLLMs.map((i, idx) => ({key: uuid(), ...i})));
const [llmItemsCurrState, setLLMItemsCurrState] = useState([]);
const addModel = useCallback((model) => {
// Get the item for that model
let item = allLLMs.find(llm => llm.model === model);
if (!item) { // This should never trigger, but in case it does:
alertModal.current.trigger(`Could not find model named '${model}' in list of available LLMs.`);
return;
}
// Give it a uid as a unique key (this is needed for the draggable list to support multiple same-model items; keys must be unique)
item = {key: uuid(), ...item};
// Add model to LLM list (regardless of it's present already or not).
setLLMItems(llmItemsCurrState.concat([item]))
}, [llmItemsCurrState]);
const onLLMListItemsChange = useCallback((new_items) => {
setLLMItemsCurrState(new_items);
}, [setLLMItemsCurrState]);
const handleMouseEnter = () => {
setHovered(true);
@ -96,7 +136,7 @@ const PromptNode = ({ data, id }) => {
console.log('Connected!');
// Check that there is at least one LLM selected:
if (selectedLLMs.length === 0) {
if (llmItemsCurrState.length === 0) {
alert('Please select at least one LLM to prompt.')
return;
}
@ -104,7 +144,7 @@ const PromptNode = ({ data, id }) => {
// Set status indicator
setStatus('loading');
// Pull data from each source:
// Pull data from each source, recursively:
const pulled_data = {};
const get_outputs = (varnames, nodeId) => {
console.log(varnames);
@ -140,13 +180,18 @@ const PromptNode = ({ data, id }) => {
pulled_data[varname] = pulled_data[varname].map(val => to_py_template_format(val));
});
const rejected = (err) => {
setStatus('error');
alertModal.current.trigger(err.message);
};
// Run all prompt permutations through the LLM to generate + cache responses:
fetch('http://localhost:8000/queryllm', {
method: 'POST',
headers: {'Content-Type': 'application/json', 'Access-Control-Allow-Origin': '*'},
body: JSON.stringify({
id: id,
llm: selectedLLMs,
llm: llmItemsCurrState.map(item => item.model),
prompt: py_prompt_template,
vars: pulled_data,
params: {
@ -155,10 +200,14 @@ const PromptNode = ({ data, id }) => {
},
no_cache: false,
}),
}).then(function(response) {
}, rejected).then(function(response) {
return response.json();
}).then(function(json) {
if (json.responses) {
}, rejected).then(function(json) {
if (!json) {
setStatus('error');
alertModal.current.trigger('Request was sent and received by backend server, but there was no response.');
}
else if (json.responses) {
// Success! Change status to 'ready':
setStatus('ready');
@ -169,7 +218,8 @@ const PromptNode = ({ data, id }) => {
// Save preview strings of responses, for quick glance
// Bucket responses by LLM:
const responses_by_llm = bucketResponsesByLLM(json.responses);
const colors = ['#cbf078', '#f1b963', '#e46161', '#f8f398', '#defcf9', '#cadefc', '#c3bef0', '#cca8e9'];
// const colors = ['#cbf078', '#f1b963', '#e46161', '#f8f398', '#defcf9', '#cadefc', '#c3bef0', '#cca8e9'];
// const colors = ['green', 'yellow', 'orange', 'red', 'pink', 'grape', 'violet', 'indigo', 'blue', 'gray', 'cyan', 'lime'];
setReponsePreviews(Object.keys(responses_by_llm).map((llm, llm_idx) => {
const resp_boxes = responses_by_llm[llm].map((res_obj, idx) => {
const num_resp = res_obj['responses'].length;
@ -177,9 +227,13 @@ const PromptNode = ({ data, id }) => {
(<pre className="small-response" key={i}><i>{truncStr(r, 40)}</i><b>({i+1} of {num_resp})</b></pre>)
);
const vars = vars_to_str(res_obj.vars);
const var_tags = vars.map((v, i) => (
<Badge key={v} color="green" size="xs">{v}</Badge>
));
return (
<div key={idx} className="response-box" style={{ backgroundColor: colors[llm_idx % colors.length] }}>
<p className="response-tag">{vars}</p>
<div key={idx} className="response-box">
{var_tags}
{/* <p className="response-tag">{vars}</p> */}
{resp_prevs}
</div>
);
@ -195,34 +249,20 @@ const PromptNode = ({ data, id }) => {
// Log responses for debugging:
console.log(json.responses);
} else {
console.error(json.error || 'Unknown error when querying LLM');
setStatus('error');
alertModal.current.trigger(json.error || 'Unknown error when querying LLM');
}
});
}, rejected);
console.log(pulled_data);
} else {
console.log('Not connected! :(');
alertModal.current.trigger('Missing inputs to one or more template variables.')
// TODO: Blink the names of unconnected params
}
}
const handleLLMChecked = (event) => {
console.log(event.target.value, event.target.checked);
if (event.target.checked) {
if (!selectedLLMs.includes(event.target.value)) {
// Add the selected LLM to the list:
setSelectedLLMs(selectedLLMs.concat([event.target.value]))
}
} else {
if (selectedLLMs.includes(event.target.value)) {
// Remove the LLM from the selected list:
const removeByIndex = (array, index) => array.filter((_, i) => i !== index);
setSelectedLLMs(removeByIndex(selectedLLMs, selectedLLMs.indexOf(event.target.value)));
}
}
}
const handleNumGenChange = (event) => {
let n = event.target.value;
if (!isNaN(n) && n.length > 0 && /^\d+$/.test(n)) {
@ -233,16 +273,6 @@ const PromptNode = ({ data, id }) => {
}
};
// const nodeLabelRef = React.useRef(null);
// const makeEditable = () => {
// if (nodeLabelRef.current) {
// for (const child of nodeLabelRef.current.children) {
// console.log(child.children);
// child.contentEditable = 'true';
// }
// }
// };
const hideStatusIndicator = () => {
if (status !== 'none') { setStatus('none'); }
};
@ -263,6 +293,7 @@ const PromptNode = ({ data, id }) => {
nodeId={id}
onEdit={hideStatusIndicator} />
<StatusIndicator status={status} />
<AlertModal ref={alertModal} />
<button className="AmitSahoo45-button-3 nodrag" onClick={handleRunClick}><div className="play-button"></div></button>
</div>
<div className="input-field">
@ -281,23 +312,41 @@ const PromptNode = ({ data, id }) => {
/>
</div>
<TemplateHooks vars={templateVars} nodeId={id} startY={140} />
<hr />
<div>
<hr />
<div>
<div style={{marginBottom: '10px', padding: '4px'}}>
<label htmlFor="num-generations" style={{fontSize: '10pt'}}>Num responses per prompt:&nbsp;</label>
<input id="num-generations" name="num-generations" type="number" min={1} max={50} defaultValue={data.n || 1} onChange={handleNumGenChange} className="nodrag"></input>
</div>
<hr />
<p style={{marginTop: 0}} >LLMs:</p>
<div className="nodrag">
<input type="checkbox" id="gpt3.5" name="gpt3.5" value="gpt3.5" defaultChecked={true} onChange={handleLLMChecked} />
<label htmlFor="gpt3.5">GPT3.5 </label>
<input type="checkbox" id="gpt4" name="gpt4" value="gpt4" defaultChecked={false} onChange={handleLLMChecked} />
<label htmlFor="gpt4">GPT4 </label>
<input type="checkbox" id="alpaca.7B" name="alpaca.7B" value="alpaca.7B" defaultChecked={false} onChange={handleLLMChecked} />
<label htmlFor="alpaca.7B">Alpaca 7B</label>
<div id="llms-list" className="nowheel" style={{backgroundColor: '#eee', borderRadius: '4px', padding: '8px', overflowY: 'auto', maxHeight: '175px'}}>
<div style={{marginTop: '6px', marginBottom: '6px', marginLeft: '6px', paddingBottom: '4px', textAlign: 'left', fontSize: '10pt', color: '#777'}}>
Models to query:
<div className="add-llm-model-btn nodrag">
<Menu transitionProps={{ transition: 'pop-top-left' }}
position="bottom-start"
width={220}
withinPortal={true}
>
<Menu.Target>
<button>Add +</button>
</Menu.Target>
<Menu.Dropdown>
{allLLMs.map(item => (<Menu.Item key={item.model} onClick={() => addModel(item.model)} icon={item.emoji}>{item.name}</Menu.Item>))}
</Menu.Dropdown>
</Menu>
</div>
</div>
<div className="nodrag">
<LLMList llms={llmItems} onItemsChange={onLLMListItemsChange} />
{/* <input type="checkbox" id="gpt3.5" name="gpt3.5" value="gpt3.5" defaultChecked={true} onChange={handleLLMChecked} />
<label htmlFor="gpt3.5">GPT3.5 </label>
<input type="checkbox" id="gpt4" name="gpt4" value="gpt4" defaultChecked={false} onChange={handleLLMChecked} />
<label htmlFor="gpt4">GPT4 </label>
<input type="checkbox" id="alpaca.7B" name="alpaca.7B" value="alpaca.7B" defaultChecked={false} onChange={handleLLMChecked} />
<label htmlFor="alpaca.7B">Alpaca 7B</label> */}
</div>
</div>
<hr />
<div className="response-preview-container nowheel">
{responsePreviews}
</div>

View File

View File

@ -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 <Droppable {...props}>{children}</Droppable>;
};

View File

@ -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 (
<div style={style}>
<Slider
thumbChildren={<IconTemperature size="1rem" stroke={1.5} />}
color="red"
label={null}
defaultValue={40}
styles={styles}
onPointerDown={e => {e.preventDefault();}}
/>
{/* <RangeSlider
mt="xl"
styles={styles}
color="red"
label={null}
defaultValue={[20, 60]}
thumbChildren={[
<IconTemperature size="1rem" stroke={1.5} key="1" />,
<IconTemperatureOff size="1rem" stroke={1.5} key="2" />,
]}
/> */}
</div>
);
}

View File

@ -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 (<div key={name} className={className} >
<p>{name}</p>
<Badge color="indigo" size="md" radius="sm">{name}</Badge>
<Handle type="target" position="left" id={name} style={style} />
</div>)
});

View File

@ -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;