UI for AI thoughts (#385)

This commit is contained in:
Chris Weaver 2023-09-01 20:32:22 -07:00 committed by GitHub
parent 06c1afce42
commit c68afbe9d0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 1258 additions and 82 deletions

View File

@ -46,27 +46,27 @@ logger = setup_logger()
router = APIRouter()
@router.get("/search-intent")
@router.post("/search-intent")
def get_search_type(
question: QuestionRequest = Depends(), _: User = Depends(current_user)
question: QuestionRequest, _: User = Depends(current_user)
) -> HelperResponse:
query = question.query
use_keyword = question.use_keyword if question.use_keyword is not None else False
return recommend_search_flow(query, use_keyword)
@router.get("/query-validation")
@router.post("/query-validation")
def query_validation(
question: QuestionRequest = Depends(), _: User = Depends(current_user)
question: QuestionRequest, _: User = Depends(current_user)
) -> QueryValidationResponse:
query = question.query
reasoning, answerable = get_query_answerability(query)
return QueryValidationResponse(reasoning=reasoning, answerable=answerable)
@router.get("/stream-query-validation")
@router.post("/stream-query-validation")
def stream_query_validation(
question: QuestionRequest = Depends(), _: User = Depends(current_user)
question: QuestionRequest, _: User = Depends(current_user)
) -> StreamingResponse:
query = question.query
return StreamingResponse(

View File

@ -29,6 +29,11 @@ const nextConfig = {
destination: "http://127.0.0.1:8080/stream-direct-qa:params*", // Proxy to Backend
permanent: true,
},
{
source: "/api/stream-query-validation:params*",
destination: "http://127.0.0.1:8080/stream-query-validation:params*", // Proxy to Backend
permanent: true,
},
];
},
};

681
web/package-lock.json generated
View File

@ -22,6 +22,7 @@
"react-dom": "^18.2.0",
"react-dropzone": "^14.2.3",
"react-icons": "^4.8.0",
"react-loader-spinner": "^5.4.5",
"semver": "^7.5.4",
"swr": "^2.1.5",
"tailwindcss": "^3.3.1",
@ -54,6 +55,453 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/@ampproject/remapping": {
"version": "2.2.1",
"resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.1.tgz",
"integrity": "sha512-lFMjJTrFL3j7L9yBxwYfCq2k6qqwHyzuUl/XBnif78PWTJYyL/dfowQHWE3sp6U6ZzqWiiIZnpTMO96zhkjwtg==",
"peer": true,
"dependencies": {
"@jridgewell/gen-mapping": "^0.3.0",
"@jridgewell/trace-mapping": "^0.3.9"
},
"engines": {
"node": ">=6.0.0"
}
},
"node_modules/@babel/code-frame": {
"version": "7.22.13",
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.13.tgz",
"integrity": "sha512-XktuhWlJ5g+3TJXc5upd9Ks1HutSArik6jf2eAjYFyIOf4ej3RN+184cZbzDvbPnuTJIUhPKKJE3cIsYTiAT3w==",
"dependencies": {
"@babel/highlight": "^7.22.13",
"chalk": "^2.4.2"
},
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/code-frame/node_modules/ansi-styles": {
"version": "3.2.1",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
"integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
"dependencies": {
"color-convert": "^1.9.0"
},
"engines": {
"node": ">=4"
}
},
"node_modules/@babel/code-frame/node_modules/chalk": {
"version": "2.4.2",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
"integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
"dependencies": {
"ansi-styles": "^3.2.1",
"escape-string-regexp": "^1.0.5",
"supports-color": "^5.3.0"
},
"engines": {
"node": ">=4"
}
},
"node_modules/@babel/code-frame/node_modules/color-convert": {
"version": "1.9.3",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
"integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
"dependencies": {
"color-name": "1.1.3"
}
},
"node_modules/@babel/code-frame/node_modules/color-name": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
"integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw=="
},
"node_modules/@babel/code-frame/node_modules/escape-string-regexp": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
"integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==",
"engines": {
"node": ">=0.8.0"
}
},
"node_modules/@babel/code-frame/node_modules/has-flag": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
"integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==",
"engines": {
"node": ">=4"
}
},
"node_modules/@babel/code-frame/node_modules/supports-color": {
"version": "5.5.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
"integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
"dependencies": {
"has-flag": "^3.0.0"
},
"engines": {
"node": ">=4"
}
},
"node_modules/@babel/compat-data": {
"version": "7.22.9",
"resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.22.9.tgz",
"integrity": "sha512-5UamI7xkUcJ3i9qVDS+KFDEK8/7oJ55/sJMB1Ge7IEapr7KfdfV/HErR+koZwOfd+SgtFKOKRhRakdg++DcJpQ==",
"peer": true,
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/core": {
"version": "7.22.11",
"resolved": "https://registry.npmjs.org/@babel/core/-/core-7.22.11.tgz",
"integrity": "sha512-lh7RJrtPdhibbxndr6/xx0w8+CVlY5FJZiaSz908Fpy+G0xkBFTvwLcKJFF4PJxVfGhVWNebikpWGnOoC71juQ==",
"peer": true,
"dependencies": {
"@ampproject/remapping": "^2.2.0",
"@babel/code-frame": "^7.22.10",
"@babel/generator": "^7.22.10",
"@babel/helper-compilation-targets": "^7.22.10",
"@babel/helper-module-transforms": "^7.22.9",
"@babel/helpers": "^7.22.11",
"@babel/parser": "^7.22.11",
"@babel/template": "^7.22.5",
"@babel/traverse": "^7.22.11",
"@babel/types": "^7.22.11",
"convert-source-map": "^1.7.0",
"debug": "^4.1.0",
"gensync": "^1.0.0-beta.2",
"json5": "^2.2.3",
"semver": "^6.3.1"
},
"engines": {
"node": ">=6.9.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/babel"
}
},
"node_modules/@babel/core/node_modules/json5": {
"version": "2.2.3",
"resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz",
"integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==",
"peer": true,
"bin": {
"json5": "lib/cli.js"
},
"engines": {
"node": ">=6"
}
},
"node_modules/@babel/core/node_modules/semver": {
"version": "6.3.1",
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
"integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
"peer": true,
"bin": {
"semver": "bin/semver.js"
}
},
"node_modules/@babel/generator": {
"version": "7.22.10",
"resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.22.10.tgz",
"integrity": "sha512-79KIf7YiWjjdZ81JnLujDRApWtl7BxTqWD88+FFdQEIOG8LJ0etDOM7CXuIgGJa55sGOwZVwuEsaLEm0PJ5/+A==",
"dependencies": {
"@babel/types": "^7.22.10",
"@jridgewell/gen-mapping": "^0.3.2",
"@jridgewell/trace-mapping": "^0.3.17",
"jsesc": "^2.5.1"
},
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/helper-annotate-as-pure": {
"version": "7.22.5",
"resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.22.5.tgz",
"integrity": "sha512-LvBTxu8bQSQkcyKOU+a1btnNFQ1dMAd0R6PyW3arXes06F6QLWLIrd681bxRPIXlrMGR3XYnW9JyML7dP3qgxg==",
"dependencies": {
"@babel/types": "^7.22.5"
},
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/helper-compilation-targets": {
"version": "7.22.10",
"resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.22.10.tgz",
"integrity": "sha512-JMSwHD4J7SLod0idLq5PKgI+6g/hLD/iuWBq08ZX49xE14VpVEojJ5rHWptpirV2j020MvypRLAXAO50igCJ5Q==",
"peer": true,
"dependencies": {
"@babel/compat-data": "^7.22.9",
"@babel/helper-validator-option": "^7.22.5",
"browserslist": "^4.21.9",
"lru-cache": "^5.1.1",
"semver": "^6.3.1"
},
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/helper-compilation-targets/node_modules/lru-cache": {
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz",
"integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==",
"peer": true,
"dependencies": {
"yallist": "^3.0.2"
}
},
"node_modules/@babel/helper-compilation-targets/node_modules/semver": {
"version": "6.3.1",
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
"integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
"peer": true,
"bin": {
"semver": "bin/semver.js"
}
},
"node_modules/@babel/helper-compilation-targets/node_modules/yallist": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz",
"integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==",
"peer": true
},
"node_modules/@babel/helper-environment-visitor": {
"version": "7.22.5",
"resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.5.tgz",
"integrity": "sha512-XGmhECfVA/5sAt+H+xpSg0mfrHq6FzNr9Oxh7PSEBBRUb/mL7Kz3NICXb194rCqAEdxkhPT1a88teizAFyvk8Q==",
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/helper-function-name": {
"version": "7.22.5",
"resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.22.5.tgz",
"integrity": "sha512-wtHSq6jMRE3uF2otvfuD3DIvVhOsSNshQl0Qrd7qC9oQJzHvOL4qQXlQn2916+CXGywIjpGuIkoyZRRxHPiNQQ==",
"dependencies": {
"@babel/template": "^7.22.5",
"@babel/types": "^7.22.5"
},
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/helper-hoist-variables": {
"version": "7.22.5",
"resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz",
"integrity": "sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==",
"dependencies": {
"@babel/types": "^7.22.5"
},
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/helper-module-imports": {
"version": "7.22.5",
"resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.22.5.tgz",
"integrity": "sha512-8Dl6+HD/cKifutF5qGd/8ZJi84QeAKh+CEe1sBzz8UayBBGg1dAIJrdHOcOM5b2MpzWL2yuotJTtGjETq0qjXg==",
"dependencies": {
"@babel/types": "^7.22.5"
},
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/helper-module-transforms": {
"version": "7.22.9",
"resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.22.9.tgz",
"integrity": "sha512-t+WA2Xn5K+rTeGtC8jCsdAH52bjggG5TKRuRrAGNM/mjIbO4GxvlLMFOEz9wXY5I2XQ60PMFsAG2WIcG82dQMQ==",
"peer": true,
"dependencies": {
"@babel/helper-environment-visitor": "^7.22.5",
"@babel/helper-module-imports": "^7.22.5",
"@babel/helper-simple-access": "^7.22.5",
"@babel/helper-split-export-declaration": "^7.22.6",
"@babel/helper-validator-identifier": "^7.22.5"
},
"engines": {
"node": ">=6.9.0"
},
"peerDependencies": {
"@babel/core": "^7.0.0"
}
},
"node_modules/@babel/helper-plugin-utils": {
"version": "7.22.5",
"resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.22.5.tgz",
"integrity": "sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg==",
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/helper-simple-access": {
"version": "7.22.5",
"resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.22.5.tgz",
"integrity": "sha512-n0H99E/K+Bika3++WNL17POvo4rKWZ7lZEp1Q+fStVbUi8nxPQEBOlTmCOxW/0JsS56SKKQ+ojAe2pHKJHN35w==",
"peer": true,
"dependencies": {
"@babel/types": "^7.22.5"
},
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/helper-split-export-declaration": {
"version": "7.22.6",
"resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz",
"integrity": "sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==",
"dependencies": {
"@babel/types": "^7.22.5"
},
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/helper-string-parser": {
"version": "7.22.5",
"resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.22.5.tgz",
"integrity": "sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw==",
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/helper-validator-identifier": {
"version": "7.22.5",
"resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.5.tgz",
"integrity": "sha512-aJXu+6lErq8ltp+JhkJUfk1MTGyuA4v7f3pA+BJ5HLfNC6nAQ0Cpi9uOquUj8Hehg0aUiHzWQbOVJGao6ztBAQ==",
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/helper-validator-option": {
"version": "7.22.5",
"resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.22.5.tgz",
"integrity": "sha512-R3oB6xlIVKUnxNUxbmgq7pKjxpru24zlimpE8WK47fACIlM0II/Hm1RS8IaOI7NgCr6LNS+jl5l75m20npAziw==",
"peer": true,
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/helpers": {
"version": "7.22.11",
"resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.22.11.tgz",
"integrity": "sha512-vyOXC8PBWaGc5h7GMsNx68OH33cypkEDJCHvYVVgVbbxJDROYVtexSk0gK5iCF1xNjRIN2s8ai7hwkWDq5szWg==",
"peer": true,
"dependencies": {
"@babel/template": "^7.22.5",
"@babel/traverse": "^7.22.11",
"@babel/types": "^7.22.11"
},
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/highlight": {
"version": "7.22.13",
"resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.13.tgz",
"integrity": "sha512-C/BaXcnnvBCmHTpz/VGZ8jgtE2aYlW4hxDhseJAWZb7gqGM/qtCK6iZUb0TyKFf7BOUsBH7Q7fkRsDRhg1XklQ==",
"dependencies": {
"@babel/helper-validator-identifier": "^7.22.5",
"chalk": "^2.4.2",
"js-tokens": "^4.0.0"
},
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/highlight/node_modules/ansi-styles": {
"version": "3.2.1",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
"integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
"dependencies": {
"color-convert": "^1.9.0"
},
"engines": {
"node": ">=4"
}
},
"node_modules/@babel/highlight/node_modules/chalk": {
"version": "2.4.2",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
"integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
"dependencies": {
"ansi-styles": "^3.2.1",
"escape-string-regexp": "^1.0.5",
"supports-color": "^5.3.0"
},
"engines": {
"node": ">=4"
}
},
"node_modules/@babel/highlight/node_modules/color-convert": {
"version": "1.9.3",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
"integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==",
"dependencies": {
"color-name": "1.1.3"
}
},
"node_modules/@babel/highlight/node_modules/color-name": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz",
"integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw=="
},
"node_modules/@babel/highlight/node_modules/escape-string-regexp": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
"integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==",
"engines": {
"node": ">=0.8.0"
}
},
"node_modules/@babel/highlight/node_modules/has-flag": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
"integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==",
"engines": {
"node": ">=4"
}
},
"node_modules/@babel/highlight/node_modules/supports-color": {
"version": "5.5.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
"integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
"dependencies": {
"has-flag": "^3.0.0"
},
"engines": {
"node": ">=4"
}
},
"node_modules/@babel/parser": {
"version": "7.22.14",
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.22.14.tgz",
"integrity": "sha512-1KucTHgOvaw/LzCVrEOAyXkr9rQlp0A1HiHRYnSUE9dmb8PvPW7o5sscg+5169r54n3vGlbx6GevTE/Iw/P3AQ==",
"bin": {
"parser": "bin/babel-parser.js"
},
"engines": {
"node": ">=6.0.0"
}
},
"node_modules/@babel/plugin-syntax-jsx": {
"version": "7.22.5",
"resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.22.5.tgz",
"integrity": "sha512-gvyP4hZrgrs/wWMaocvxZ44Hw0b3W8Pe+cMxc8V1ULQ07oh8VNbIRaoD1LRZVTvD+0nieDKjfgKg89sD7rrKrg==",
"dependencies": {
"@babel/helper-plugin-utils": "^7.22.5"
},
"engines": {
"node": ">=6.9.0"
},
"peerDependencies": {
"@babel/core": "^7.0.0-0"
}
},
"node_modules/@babel/runtime": {
"version": "7.22.11",
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.22.11.tgz",
@ -66,6 +514,83 @@
"node": ">=6.9.0"
}
},
"node_modules/@babel/template": {
"version": "7.22.5",
"resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.5.tgz",
"integrity": "sha512-X7yV7eiwAxdj9k94NEylvbVHLiVG1nvzCV2EAowhxLTwODV1jl9UzZ48leOC0sH7OnuHrIkllaBgneUykIcZaw==",
"dependencies": {
"@babel/code-frame": "^7.22.5",
"@babel/parser": "^7.22.5",
"@babel/types": "^7.22.5"
},
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/traverse": {
"version": "7.22.11",
"resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.22.11.tgz",
"integrity": "sha512-mzAenteTfomcB7mfPtyi+4oe5BZ6MXxWcn4CX+h4IRJ+OOGXBrWU6jDQavkQI9Vuc5P+donFabBfFCcmWka9lQ==",
"dependencies": {
"@babel/code-frame": "^7.22.10",
"@babel/generator": "^7.22.10",
"@babel/helper-environment-visitor": "^7.22.5",
"@babel/helper-function-name": "^7.22.5",
"@babel/helper-hoist-variables": "^7.22.5",
"@babel/helper-split-export-declaration": "^7.22.6",
"@babel/parser": "^7.22.11",
"@babel/types": "^7.22.11",
"debug": "^4.1.0",
"globals": "^11.1.0"
},
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/traverse/node_modules/globals": {
"version": "11.12.0",
"resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz",
"integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==",
"engines": {
"node": ">=4"
}
},
"node_modules/@babel/types": {
"version": "7.22.11",
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.22.11.tgz",
"integrity": "sha512-siazHiGuZRz9aB9NpHy9GOs9xiQPKnMzgdr493iI1M67vRXpnEq8ZOOKzezC5q7zwuQ6sDhdSp4SD9ixKSqKZg==",
"dependencies": {
"@babel/helper-string-parser": "^7.22.5",
"@babel/helper-validator-identifier": "^7.22.5",
"to-fast-properties": "^2.0.0"
},
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@emotion/is-prop-valid": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-1.2.1.tgz",
"integrity": "sha512-61Mf7Ufx4aDxx1xlDeOm8aFFigGHE4z+0sKCa+IHCeZKiyP9RLD0Mmx7m8b9/Cf37f7NAvQOOJAbQQGVr5uERw==",
"dependencies": {
"@emotion/memoize": "^0.8.1"
}
},
"node_modules/@emotion/memoize": {
"version": "0.8.1",
"resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.8.1.tgz",
"integrity": "sha512-W2P2c/VRW1/1tLox0mVUalvnWXxavmv/Oum2aPsRcoDJuob75FC3Y8FbpfLwUegRcxINtGUMPq0tFCvYNTBXNA=="
},
"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.7.5",
"resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.7.5.tgz",
"integrity": "sha512-OWORNpfjMsSSUBVrRBVGECkhWcULOAJz9ZW8uK9qgxD+87M7jHRcvh/A96XXNhXTLmKcoYSQtBEX7lHMO7YRwg=="
},
"node_modules/@eslint-community/eslint-utils": {
"version": "4.4.0",
"resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz",
@ -867,6 +1392,21 @@
"dequal": "^2.0.3"
}
},
"node_modules/babel-plugin-styled-components": {
"version": "2.1.4",
"resolved": "https://registry.npmjs.org/babel-plugin-styled-components/-/babel-plugin-styled-components-2.1.4.tgz",
"integrity": "sha512-Xgp9g+A/cG47sUyRwwYxGM4bR/jDRg5N6it/8+HxCnbT5XNKSKDT9xm4oag/osgqjC2It/vH0yXsomOG6k558g==",
"dependencies": {
"@babel/helper-annotate-as-pure": "^7.22.5",
"@babel/helper-module-imports": "^7.22.5",
"@babel/plugin-syntax-jsx": "^7.22.5",
"lodash": "^4.17.21",
"picomatch": "^2.3.1"
},
"peerDependencies": {
"styled-components": ">= 2"
}
},
"node_modules/balanced-match": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
@ -972,6 +1512,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-lite": {
"version": "1.0.30001524",
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001524.tgz",
@ -1080,6 +1628,12 @@
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
"integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg=="
},
"node_modules/convert-source-map": {
"version": "1.9.0",
"resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz",
"integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==",
"peer": true
},
"node_modules/cross-spawn": {
"version": "7.0.3",
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz",
@ -1094,6 +1648,24 @@
"node": ">= 8"
}
},
"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-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/cssesc": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz",
@ -1120,7 +1692,6 @@
"version": "4.3.4",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
"integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==",
"dev": true,
"dependencies": {
"ms": "2.1.2"
},
@ -1999,6 +2570,15 @@
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/gensync": {
"version": "1.0.0-beta.2",
"resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz",
"integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==",
"peer": true,
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/get-intrinsic": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.1.tgz",
@ -2680,6 +3260,17 @@
"js-yaml": "bin/js-yaml.js"
}
},
"node_modules/jsesc": {
"version": "2.5.2",
"resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz",
"integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==",
"bin": {
"jsesc": "bin/jsesc"
},
"engines": {
"node": ">=4"
}
},
"node_modules/json-buffer": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz",
@ -2871,8 +3462,7 @@
"node_modules/ms": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
"dev": true
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
},
"node_modules/mz": {
"version": "2.7.0",
@ -3509,6 +4099,25 @@
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
"integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="
},
"node_modules/react-loader-spinner": {
"version": "5.4.5",
"resolved": "https://registry.npmjs.org/react-loader-spinner/-/react-loader-spinner-5.4.5.tgz",
"integrity": "sha512-32f+sb/v2tnNfyvnCCOS4fpyVHsGXjSyNo6QLniHcaj1XjKLxx14L2z0h6szRugOL8IEJ+53GPwNAdbkDqmy4g==",
"dependencies": {
"react-is": "^18.2.0",
"styled-components": "^5.3.5",
"styled-tools": "^1.7.2"
},
"peerDependencies": {
"react": "^16.0.0 || ^17.0.0 || ^18.0.0",
"react-dom": "^16.0.0 || ^17.0.0 || ^18.0.0"
}
},
"node_modules/react-loader-spinner/node_modules/react-is": {
"version": "18.2.0",
"resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz",
"integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w=="
},
"node_modules/read-cache": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz",
@ -3705,6 +4314,11 @@
"node": ">=10"
}
},
"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",
@ -3862,6 +4476,54 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/styled-components": {
"version": "5.3.11",
"resolved": "https://registry.npmjs.org/styled-components/-/styled-components-5.3.11.tgz",
"integrity": "sha512-uuzIIfnVkagcVHv9nE0VPlHPSCmXIUGKfJ42LNjxCCTDTL5sgnJ8Z7GZBq0EnLYGln77tPpEpExt2+qa+cZqSw==",
"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/has-flag": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
"integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==",
"engines": {
"node": ">=4"
}
},
"node_modules/styled-components/node_modules/supports-color": {
"version": "5.5.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
"integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
"dependencies": {
"has-flag": "^3.0.0"
},
"engines": {
"node": ">=4"
}
},
"node_modules/styled-jsx": {
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/styled-jsx/-/styled-jsx-5.1.1.tgz",
@ -3884,6 +4546,11 @@
}
}
},
"node_modules/styled-tools": {
"version": "1.7.2",
"resolved": "https://registry.npmjs.org/styled-tools/-/styled-tools-1.7.2.tgz",
"integrity": "sha512-IjLxzM20RMwAsx8M1QoRlCG/Kmq8lKzCGyospjtSXt/BTIIcvgTonaxQAsKnBrsZNwhpHzO9ADx5te0h76ILVg=="
},
"node_modules/sucrase": {
"version": "3.34.0",
"resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.34.0.tgz",
@ -4039,6 +4706,14 @@
"resolved": "https://registry.npmjs.org/tiny-warning/-/tiny-warning-1.0.3.tgz",
"integrity": "sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA=="
},
"node_modules/to-fast-properties": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz",
"integrity": "sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==",
"engines": {
"node": ">=4"
}
},
"node_modules/to-regex-range": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",

View File

@ -23,6 +23,7 @@
"react-dom": "^18.2.0",
"react-dropzone": "^14.2.3",
"react-icons": "^4.8.0",
"react-loader-spinner": "^5.4.5",
"semver": "^7.5.4",
"swr": "^2.1.5",
"tailwindcss": "^3.3.1",
@ -30,8 +31,8 @@
"yup": "^1.1.1"
},
"devDependencies": {
"prettier": "2.8.8",
"eslint": "^8.48.0",
"eslint-config-next": "^13.4.9"
"eslint-config-next": "^13.4.9",
"prettier": "2.8.8"
}
}

View File

@ -23,6 +23,12 @@ import {
FiGlobe,
FiThumbsDown,
FiThumbsUp,
FiChevronDown,
FiChevronUp,
FiAlertCircle,
FiChevronRight,
FiChevronLeft,
FiAlertTriangle,
} from "react-icons/fi";
import { SiBookstack } from "react-icons/si";
import Image from "next/image";
@ -172,6 +178,34 @@ export const ChevronsDownIcon = ({
return <FiChevronsDown size={size} className={className} />;
};
export const ChevronUpIcon = ({
size = 16,
className = defaultTailwindCSS,
}: IconProps) => {
return <FiChevronUp size={size} className={className} />;
};
export const ChevronDownIcon = ({
size = 16,
className = defaultTailwindCSS,
}: IconProps) => {
return <FiChevronDown size={size} className={className} />;
};
export const ChevronRightIcon = ({
size = 16,
className = defaultTailwindCSS,
}: IconProps) => {
return <FiChevronRight size={size} className={className} />;
};
export const ChevronLeftIcon = ({
size = 16,
className = defaultTailwindCSS,
}: IconProps) => {
return <FiChevronLeft size={size} className={className} />;
};
export const CheckmarkIcon = ({
size = 16,
className = defaultTailwindCSS,
@ -179,6 +213,20 @@ export const CheckmarkIcon = ({
return <FiCheck size={size} className={className} />;
};
export const AlertIcon = ({
size = 16,
className = defaultTailwindCSS,
}: IconProps) => {
return <FiAlertCircle size={size} className={className} />;
};
export const TriangleAlertIcon = ({
size = 16,
className = defaultTailwindCSS,
}: IconProps) => {
return <FiAlertTriangle size={size} className={className} />;
};
//
// COMPANY LOGOS
//

View File

@ -1,16 +1,24 @@
"use client";
import React from "react";
import { getSourceIcon } from "../source";
import { LoadingAnimation } from "../Loading";
import { InfoIcon } from "../icons/icons";
import {
DanswerDocument,
SearchResponse,
Quote,
FlowType,
SearchDefaultOverrides,
ValidQuestionResponse,
} from "@/lib/search/interfaces";
import { QAFeedbackBlock } from "./QAFeedback";
import { DocumentDisplay } from "./DocumentDisplay";
import { ResponseSection, StatusOptions } from "./results/ResponseSection";
import { QuotesSection } from "./results/QuotesSection";
import { AnswerSection } from "./results/AnswerSection";
import {
getAIThoughtsIsOpenSavedValue,
setAIThoughtsIsOpenSavedValue,
} from "@/lib/search/aiThoughtUtils";
import { Grid, ThreeDots } from "react-loader-spinner";
const removeDuplicateDocs = (documents: DanswerDocument[]) => {
const seen = new Set<string>();
@ -26,15 +34,25 @@ const removeDuplicateDocs = (documents: DanswerDocument[]) => {
interface SearchResultsDisplayProps {
searchResponse: SearchResponse | null;
validQuestionResponse: ValidQuestionResponse;
isFetching: boolean;
defaultOverrides: SearchDefaultOverrides;
}
export const SearchResultsDisplay: React.FC<SearchResultsDisplayProps> = ({
searchResponse,
validQuestionResponse,
isFetching,
defaultOverrides,
}) => {
const [isAIThoughtsOpen, setIsAIThoughtsOpen] = React.useState<boolean>(
getAIThoughtsIsOpenSavedValue()
);
const handleAIThoughtToggle = (newAIThoughtsOpenValue: boolean) => {
setAIThoughtsIsOpenSavedValue(newAIThoughtsOpenValue);
setIsAIThoughtsOpen(newAIThoughtsOpenValue);
};
if (!searchResponse) {
return null;
}
@ -45,7 +63,16 @@ export const SearchResultsDisplay: React.FC<SearchResultsDisplayProps> = ({
return (
<div className="flex">
<div className="mx-auto">
<LoadingAnimation />
<ThreeDots
height="30"
width="40"
color="#3b82f6"
ariaLabel="grid-loading"
radius="12.5"
wrapperStyle={{}}
wrapperClass=""
visible={true}
/>
</div>
</div>
);
@ -70,81 +97,63 @@ export const SearchResultsDisplay: React.FC<SearchResultsDisplayProps> = ({
searchResponse.suggestedFlowType === FlowType.QUESTION_ANSWER ||
defaultOverrides.forceDisplayQA;
let answerDisplay = <LoadingAnimation text="" size="text-sm" />;
if (error) {
answerDisplay = (
<div className="flex">
<InfoIcon
size={20}
className="text-red-500 my-auto flex flex-shrink-0"
/>
<div className="text-red-500 text-sm my-auto ml-1">{error}</div>
</div>
);
} else if (answer) {
answerDisplay = <p className="mb-4">{answer}</p>;
} else if (!isFetching) {
answerDisplay = (
<div className="text-sm my-auto text-gray-300">Information not found</div>
);
let questionValidityCheckStatus: StatusOptions = "in-progress";
if (validQuestionResponse.answerable) {
questionValidityCheckStatus = "success";
} else if (validQuestionResponse.answerable === false) {
questionValidityCheckStatus = "failed";
}
return (
<>
{shouldDisplayQA && (
<div className="min-h-[14rem]">
<div className="p-4 border-2 rounded-md border-gray-700">
<div className="min-h-[16rem] p-4 border-2 rounded-md border-gray-700 relative">
<div>
<div className="flex mb-1">
<h2 className="text font-bold my-auto">AI Answer</h2>
<h2 className="text font-bold my-auto mb-1 w-full">AI Answer</h2>
</div>
<div className="mb-2 w-full">
<ResponseSection
status={questionValidityCheckStatus}
header={
validQuestionResponse.answerable === null ? (
<div className="flex ml-2">Evaluating question...</div>
) : (
<div className="flex ml-2">AI thoughts</div>
)
}
body={<div>{validQuestionResponse.reasoning}</div>}
desiredOpenStatus={isAIThoughtsOpen}
setDesiredOpenStatus={handleAIThoughtToggle}
/>
</div>
<div className="mb-2 pt-1 border-t border-gray-700 w-full">
<AnswerSection
answer={answer}
quotes={quotes}
error={error}
isAnswerable={validQuestionResponse.answerable}
isFetching={isFetching}
aiThoughtsIsOpen={isAIThoughtsOpen}
/>
</div>
{answerDisplay}
{quotes !== null && answer && (
<>
<h2 className="text-sm font-bold">Sources</h2>
{isFetching && dedupedQuotes.length === 0 ? (
<LoadingAnimation text="Finding quotes" size="text-sm" />
) : (
<div className="flex flex-wrap">
{dedupedQuotes.length > 0 ? (
dedupedQuotes.map((quoteInfo) => (
<a
key={quoteInfo.document_id}
className="p-2 ml-1 mt-2 border border-gray-800 rounded-lg text-sm flex max-w-[280px] hover:bg-gray-800"
href={quoteInfo.link || undefined}
target="_blank"
rel="noopener noreferrer"
>
{getSourceIcon(quoteInfo.source_type, 20)}
<p className="truncate break-all ml-2">
{quoteInfo.semantic_identifier ||
quoteInfo.document_id}
</p>
</a>
))
) : (
<div className="flex">
<InfoIcon
size={20}
className="text-red-500 my-auto flex flex-shrink-0"
/>
<div className="text-red-500 text-sm my-auto ml-1">
Did not find any exact quotes to support the above
answer.
</div>
</div>
)}
<div className="pt-1 border-t border-gray-700 w-full">
<QuotesSection
quotes={dedupedQuotes}
isFetching={isFetching}
isAnswerable={validQuestionResponse.answerable}
/>
{searchResponse.queryEventId !== null && (
<div className="ml-auto mt-auto">
<QAFeedbackBlock
queryId={searchResponse.queryEventId}
/>
</div>
)}
{searchResponse.queryEventId !== null && (
<div className="absolute right-3 bottom-3">
<QAFeedbackBlock queryId={searchResponse.queryEventId} />
</div>
)}
</>
</div>
)}
</div>
</div>

View File

@ -15,6 +15,7 @@ import {
SearchType,
SearchDefaultOverrides,
SearchRequestOverrides,
ValidQuestionResponse,
} from "@/lib/search/interfaces";
import { searchRequestStreamed } from "@/lib/search/streamingQa";
import Cookies from "js-cookie";
@ -22,12 +23,19 @@ import { SearchHelper } from "./SearchHelper";
import { CancellationToken, cancellable } from "@/lib/search/cancellable";
import { NEXT_PUBLIC_DISABLE_STREAMING } from "@/lib/constants";
import { searchRequest } from "@/lib/search/qa";
import { useObjectState } from "@/lib/hooks";
import { questionValidationStreamed } from "@/lib/search/streamingQuestionValidation";
const SEARCH_DEFAULT_OVERRIDES_START: SearchDefaultOverrides = {
forceDisplayQA: false,
offset: 0,
};
const VALID_QUESTION_RESPONSE_DEFAULT: ValidQuestionResponse = {
reasoning: null,
answerable: null,
};
interface SearchSectionProps {
connectors: Connector<any>[];
defaultSearchType: SearchType;
@ -46,6 +54,9 @@ export const SearchSection: React.FC<SearchSectionProps> = ({
);
const [isFetching, setIsFetching] = useState(false);
const [validQuestionResponse, setValidQuestionResponse] =
useObjectState<ValidQuestionResponse>(VALID_QUESTION_RESPONSE_DEFAULT);
// Filters
const [sources, setSources] = useState<Source[]>([]);
@ -116,11 +127,12 @@ export const SearchSection: React.FC<SearchSectionProps> = ({
setIsFetching(true);
setSearchResponse(initialSearchResponse);
setValidQuestionResponse(VALID_QUESTION_RESPONSE_DEFAULT);
const searchFn = NEXT_PUBLIC_DISABLE_STREAMING
? searchRequest
: searchRequestStreamed;
await searchFn({
const searchFnArgs = {
query,
sources,
updateCurrentAnswer: cancellable({
@ -153,7 +165,17 @@ export const SearchSection: React.FC<SearchSectionProps> = ({
}),
selectedSearchType: searchType ?? selectedSearchType,
offset: offset ?? defaultOverrides.offset,
});
};
const questionValidationArgs = {
query,
update: setValidQuestionResponse,
};
await Promise.all([
searchFn(searchFnArgs),
questionValidationStreamed(questionValidationArgs),
]);
setIsFetching(false);
};
@ -213,6 +235,7 @@ export const SearchSection: React.FC<SearchSectionProps> = ({
<div className="mt-2">
<SearchResultsDisplay
searchResponse={searchResponse}
validQuestionResponse={validQuestionResponse}
isFetching={isFetching}
defaultOverrides={defaultOverrides}
/>

View File

@ -0,0 +1,84 @@
import { Quote } from "@/lib/search/interfaces";
import { ResponseSection, StatusOptions } from "./ResponseSection";
interface AnswerSectionProps {
answer: string | null;
quotes: Quote[] | null;
error: string | null;
isAnswerable: boolean | null;
isFetching: boolean;
aiThoughtsIsOpen: boolean;
}
const AnswerHeader = ({
answer,
error,
quotes,
isAnswerable,
isFetching,
}: AnswerSectionProps) => {
if (error) {
return <>Error while building answer</>;
} else if ((answer && quotes !== null) || !isFetching) {
if (isAnswerable === false) {
return <>Best effort AI answer</>;
}
return <>AI answer</>;
}
if (isAnswerable === false) {
return <>Building best effort AI answer...</>;
}
return <>Building answer...</>;
};
const AnswerBody = ({ answer, error, isFetching }: AnswerSectionProps) => {
if (error) {
return (
<div className="flex">
<div className="text-red-500 my-auto ml-1">{error}</div>
</div>
);
} else if (answer) {
return <p className="">{answer}</p>;
} else if (!isFetching) {
return <div className="text-gray-300">Information not found</div>;
}
return null;
};
export const AnswerSection = (props: AnswerSectionProps) => {
let status = "in-progress" as StatusOptions;
if (props.error) {
status = "failed";
}
// if AI thoughts is visible, don't mark this as a success until that section
// is complete
else if (!props.aiThoughtsIsOpen || props.isAnswerable !== null) {
if (props.isAnswerable === false) {
status = "warning";
} else if ((props.quotes !== null && props.answer) || !props.isFetching) {
status = "success";
}
}
return (
<ResponseSection
status={status}
header={
<div className="flex">
<div className="ml-2">{<AnswerHeader {...props} />}</div>
</div>
}
body={
<div className="">
<AnswerBody {...props} />
</div>
}
desiredOpenStatus={
props.aiThoughtsIsOpen ? props.isAnswerable !== null : true
}
isNotControllable={true}
/>
);
};

View File

@ -0,0 +1,82 @@
import { Quote } from "@/lib/search/interfaces";
import { ResponseSection, StatusOptions } from "./ResponseSection";
import { getSourceIcon } from "@/components/source";
interface QuotesSectionProps {
quotes: Quote[] | null;
isAnswerable: boolean | null;
isFetching: boolean;
}
const QuotesHeader = ({ quotes, isFetching }: QuotesSectionProps) => {
if ((!quotes || quotes.length === 0) && isFetching) {
return <>Extracting quotes...</>;
}
if (!quotes || quotes.length === 0) {
return <>No quotes found</>;
}
return <>Quotes</>;
};
const QuotesBody = ({ quotes, isFetching }: QuotesSectionProps) => {
if (!quotes && isFetching) {
// height of quotes section to avoid extra "jumps" from the quotes loading
return <div className="h-[42px]"></div>;
}
if (!isFetching && (!quotes || !quotes.length)) {
return (
<div className="flex">
<div className="text-red-500 text-sm my-auto">
Did not find any exact quotes to support the above answer.
</div>
</div>
);
}
return (
<div className="flex flex-wrap">
{quotes!.map((quoteInfo) => (
<a
key={quoteInfo.document_id}
className="p-2 mr-1 border border-gray-800 rounded-lg text-sm flex max-w-[280px] hover:bg-gray-800 w-fit"
href={quoteInfo.link || undefined}
target="_blank"
rel="noopener noreferrer"
>
{getSourceIcon(quoteInfo.source_type, 20)}
<p className="truncate break-all ml-2 mr-1">
{quoteInfo.semantic_identifier || quoteInfo.document_id}
</p>
</a>
))}
</div>
);
};
export const QuotesSection = (props: QuotesSectionProps) => {
let status: StatusOptions = "in-progress";
if (!props.isFetching) {
if (props.quotes && props.quotes.length > 0) {
if (props.isAnswerable === false) {
status = "warning";
} else {
status = "success";
}
} else {
status = "failed";
}
}
return (
<ResponseSection
status={status}
header={<div className="ml-2">{<QuotesHeader {...props} />}</div>}
body={<QuotesBody {...props} />}
desiredOpenStatus={true}
isNotControllable={true}
/>
);
};

View File

@ -0,0 +1,101 @@
import {
AlertIcon,
CheckmarkIcon,
ChevronDownIcon,
ChevronLeftIcon,
TriangleAlertIcon,
} from "@/components/icons/icons";
import { useState } from "react";
import { Grid } from "react-loader-spinner";
export type StatusOptions = "in-progress" | "failed" | "warning" | "success";
interface ResponseSectionProps {
header: JSX.Element | string;
body: JSX.Element | string;
status: StatusOptions;
desiredOpenStatus: boolean;
setDesiredOpenStatus?: (isOpen: boolean) => void;
isNotControllable?: boolean;
}
export const ResponseSection = ({
header,
body,
status,
desiredOpenStatus,
setDesiredOpenStatus,
isNotControllable,
}: ResponseSectionProps) => {
const [isOpen, setIsOpen] = useState<boolean | null>(null);
let icon = null;
if (status === "in-progress") {
icon = (
<div className="m-auto">
<Grid
height="12"
width="12"
color="#3b82f6"
ariaLabel="grid-loading"
radius="12.5"
wrapperStyle={{}}
wrapperClass=""
visible={true}
/>
</div>
);
}
if (status === "failed") {
icon = <AlertIcon size={16} className="text-red-500" />;
}
if (status === "success") {
icon = <CheckmarkIcon size={16} className="text-green-600" />;
}
if (status === "warning") {
icon = <TriangleAlertIcon size={16} className="text-yellow-600" />;
}
// use `desiredOpenStatus` if user has not clicked to open/close, otherwise use
// `isOpen` state
const finalIsOpen = isOpen !== null ? isOpen : desiredOpenStatus;
return (
<div>
<div
className={`
flex
my-1
p-1
rounded
select-none
${isNotControllable ? "" : "hover:bg-gray-800 cursor-pointer"}`}
onClick={() => {
if (!isNotControllable) {
if (isOpen === null) {
setIsOpen(!desiredOpenStatus);
} else {
setIsOpen(!isOpen);
}
}
if (setDesiredOpenStatus) {
setDesiredOpenStatus(!desiredOpenStatus);
}
}}
>
<div className="my-auto">{icon}</div>
<div className="my-auto text-sm text-gray-200 italic">{header}</div>
{!isNotControllable && (
<div className="ml-auto">
{finalIsOpen ? (
<ChevronDownIcon size={16} className="text-gray-400" />
) : (
<ChevronLeftIcon size={16} className="text-gray-400" />
)}
</div>
)}
</div>
{finalIsOpen && <div className="pb-1 mx-2 text-sm mb-1">{body}</div>}
</div>
);
};

View File

@ -1,6 +1,7 @@
import { Credential, DocumentBoostStatus } from "@/lib/types";
import useSWR, { mutate, useSWRConfig } from "swr";
import { fetcher } from "./fetcher";
import { useState } from "react";
const CREDENTIAL_URL = "/api/manage/admin/credential";
@ -14,8 +15,6 @@ export const usePublicCredentials = () => {
};
};
const MOST_REACTED_DOCS_URL = "/api/manage/doc-boosts";
const buildReactedDocsUrl = (ascending: boolean, limit: number) => {
return `/api/manage/admin/doc-boosts?ascending=${ascending}&limit=${limit}`;
};
@ -32,3 +31,18 @@ export const useMostReactedToDocuments = (
refreshDocs: () => mutate(url),
};
};
export const useObjectState = <T>(
initialValue: T
): [T, (update: Partial<T>) => void] => {
const [state, setState] = useState<T>(initialValue);
const set = (update: Partial<T>) => {
setState((prevState) => {
return {
...prevState,
...update,
};
});
};
return [state, set];
};

View File

@ -0,0 +1,16 @@
const IS_AI_THOUGHTS_OPEN_LOCAL_STORAGE_KEY = "isAIThoughtsOpen";
export const getAIThoughtsIsOpenSavedValue = () => {
// wrapping in `try / catch` to avoid SSR errors during development
try {
return (
localStorage.getItem(IS_AI_THOUGHTS_OPEN_LOCAL_STORAGE_KEY) === "true"
);
} catch (e) {
return false;
}
};
export const setAIThoughtsIsOpenSavedValue = (isOpen: boolean) => {
localStorage.setItem(IS_AI_THOUGHTS_OPEN_LOCAL_STORAGE_KEY, String(isOpen));
};

View File

@ -12,6 +12,10 @@ export const SearchType = {
};
export type SearchType = (typeof SearchType)[keyof typeof SearchType];
export interface AnswerPiece {
answer_piece: string;
}
export interface Quote {
quote: string;
document_id: string;
@ -69,3 +73,8 @@ export interface SearchRequestOverrides {
searchType?: SearchType;
offset?: number;
}
export interface ValidQuestionResponse {
answerable: boolean | null;
reasoning: string | null;
}

View File

@ -0,0 +1,62 @@
import { AnswerPiece, ValidQuestionResponse } from "./interfaces";
import { processRawChunkString } from "./streamingUtils";
export interface QuestionValidationArgs {
query: string;
update: (update: Partial<ValidQuestionResponse>) => void;
}
export const questionValidationStreamed = async <T>({
query,
update,
}: QuestionValidationArgs) => {
const response = await fetch("/api/stream-query-validation", {
method: "POST",
body: JSON.stringify({
query,
collection: "danswer_index",
use_keyword: null,
filters: null,
offset: null,
}),
headers: {
"Content-Type": "application/json",
},
});
const reader = response.body?.getReader();
const decoder = new TextDecoder("utf-8");
let reasoning = "";
let previousPartialChunk: string | null = null;
while (true) {
const rawChunk = await reader?.read();
if (!rawChunk) {
throw new Error("Unable to process chunk");
}
const { done, value } = rawChunk;
if (done) {
break;
}
const [completedChunks, partialChunk] = processRawChunkString<
AnswerPiece | ValidQuestionResponse
>(decoder.decode(value, { stream: true }), previousPartialChunk);
if (!completedChunks.length && !partialChunk) {
break;
}
previousPartialChunk = partialChunk as string | null;
completedChunks.forEach((chunk) => {
if (Object.hasOwn(chunk, "answer_piece")) {
reasoning += (chunk as AnswerPiece).answer_piece;
update({
reasoning,
});
}
if (Object.hasOwn(chunk, "answerable")) {
update({ answerable: (chunk as ValidQuestionResponse).answerable });
}
});
}
};

View File

@ -0,0 +1,47 @@
type NonEmptyObject = { [k: string]: any };
const processSingleChunk = <T extends NonEmptyObject>(
chunk: string,
currPartialChunk: string | null
): [T | null, string | null] => {
const completeChunk = (currPartialChunk || "") + chunk;
try {
// every complete chunk should be valid JSON
const chunkJson = JSON.parse(completeChunk);
return [chunkJson, null];
} catch (err) {
// if it's not valid JSON, then it's probably an incomplete chunk
return [null, completeChunk];
}
};
export const processRawChunkString = <T extends NonEmptyObject>(
rawChunkString: string,
previousPartialChunk: string | null
): [T[], string | null] => {
/* This is required because, in practice, we see that nginx does not send over
each chunk one at a time even with buffering turned off. Instead,
chunks are sometimes in batches or are sometimes incomplete */
if (!rawChunkString) {
return [[], null];
}
const chunkSections = rawChunkString
.split("\n")
.filter((chunk) => chunk.length > 0);
let parsedChunkSections: T[] = [];
let currPartialChunk = previousPartialChunk;
chunkSections.forEach((chunk) => {
const [processedChunk, partialChunk] = processSingleChunk<T>(
chunk,
currPartialChunk
);
if (processedChunk) {
parsedChunkSections.push(processedChunk);
currPartialChunk = null;
} else {
currPartialChunk = partialChunk;
}
});
return [parsedChunkSections, currPartialChunk];
};