Add initial support for web browsing (#237)
Browse files* basic poc for web search
* * Hide feature when serpapi_key is not defined
* handle error case where serpapi failed
* only use user queries for generating the query
* Update src/lib/buildPrompt.ts
Co-authored-by: Eliott C. <coyotte508@gmail.com>
* Update src/routes/+layout.server.ts
Co-authored-by: Eliott C. <coyotte508@gmail.com>
* refactored getQueryFromPrompt
* add jsdom to package.json
* begin work on fetching webpage content
* Update .env
Co-authored-by: Eliott C. <coyotte508@gmail.com>
* prettier fix
* Add feature for scraping webpages
* refactored search functionality
- now gets triggered from a separate endpoint
- results are stored in db
- results can be displayed in their own endpoint
* Added a stream to send updates from backend on web-search endpoint
* made stream more reliable
* Add front-end to web search feature
* made sure the web results button appears on newly posted messages
* close modal when message is done generating
* removed log statements
* Add button to open modal on loading messages too
* replace modal by collapsable menu
* make sure shared conversations also show search details
* Use spinner for collapse menu
* Fix alignment of "stop generating" button
* Fix loading indicators
- spinner only shows when web search is searching
- text loader shows after the web search is done
* fix loading icon when web search is disabled
* Update search messages & clean up summary string
* Fix alignment of timeline
* Use existing switch
* Add a background to tooltip & center it
* fix like making search messages disappear
* use correct spinner
* fix state issues
* lint
* fix bug with empty search messages
* fix like bug ?
* fix modal bug
* error handling
* fix like bug
* slice scraped text so it fits in context
* misc UI
* bottom buttons
simplify and fix
* made sure snap scrolling also works on web search updates
* loader
* margin
* remove unused function
* linter
* quickfix duplicate websearch
---------
Co-authored-by: Eliott C. <coyotte508@gmail.com>
Co-authored-by: Victor Mustar <victor.mustar@gmail.com>
- .env +3 -0
- package-lock.json +430 -6
- package.json +3 -0
- src/lib/buildPrompt.ts +29 -8
- src/lib/components/OpenWebSearchResults.svelte +133 -0
- src/lib/components/StopGeneratingBtn.svelte +4 -8
- src/lib/components/Switch.svelte +3 -1
- src/lib/components/WebSearchToggle.svelte +27 -0
- src/lib/components/chat/ChatMessage.svelte +27 -3
- src/lib/components/chat/ChatMessages.svelte +21 -11
- src/lib/components/chat/ChatWindow.svelte +16 -4
- src/lib/components/icons/IconLoading.svelte +14 -27
- src/lib/server/database.ts +4 -0
- src/lib/server/generateFromDefaultEndpoint.ts +43 -0
- src/lib/server/searchWeb.ts +20 -0
- src/lib/stores/webSearchParameters.ts +9 -0
- src/lib/types/Message.ts +1 -0
- src/lib/types/WebSearch.ts +40 -0
- src/routes/+layout.server.ts +2 -0
- src/routes/conversation/[id]/+page.svelte +72 -4
- src/routes/conversation/[id]/+server.ts +4 -4
- src/routes/conversation/[id]/message/[messageId]/prompt/+server.ts +1 -1
- src/routes/conversation/[id]/summarize/+server.ts +4 -30
- src/routes/conversation/[id]/web-search/+server.ts +232 -0
- src/routes/r/[id]/message/[messageId]/prompt/+server.ts +1 -1
- src/routes/search/[id]/+server.ts +39 -0
@@ -8,6 +8,9 @@ MONGODB_DIRECT_CONNECTION=false
|
|
8 |
COOKIE_NAME=hf-chat
|
9 |
HF_ACCESS_TOKEN=#hf_<token> from from https://huggingface.co/settings/token
|
10 |
|
|
|
|
|
|
|
11 |
# Parameters to enable "Sign in with HF"
|
12 |
OPENID_CLIENT_ID=
|
13 |
OPENID_CLIENT_SECRET=
|
|
|
8 |
COOKIE_NAME=hf-chat
|
9 |
HF_ACCESS_TOKEN=#hf_<token> from from https://huggingface.co/settings/token
|
10 |
|
11 |
+
# used to activate search with web functionality. disabled if not defined
|
12 |
+
SERPAPI_KEY=#your serpapi key here
|
13 |
+
|
14 |
# Parameters to enable "Sign in with HF"
|
15 |
OPENID_CLIENT_ID=
|
16 |
OPENID_CLIENT_SECRET=
|
@@ -14,12 +14,14 @@
|
|
14 |
"date-fns": "^2.29.3",
|
15 |
"dotenv": "^16.0.3",
|
16 |
"highlight.js": "^11.7.0",
|
|
|
17 |
"marked": "^4.3.0",
|
18 |
"mongodb": "^5.3.0",
|
19 |
"nanoid": "^4.0.2",
|
20 |
"openid-client": "^5.4.2",
|
21 |
"parquetjs": "^0.11.2",
|
22 |
"postcss": "^8.4.21",
|
|
|
23 |
"tailwind-scrollbar": "^3.0.0",
|
24 |
"tailwindcss": "^3.3.1",
|
25 |
"zod": "^3.21.4"
|
@@ -30,6 +32,7 @@
|
|
30 |
"@sveltejs/adapter-node": "^1.2.4",
|
31 |
"@sveltejs/kit": "^1.15.10",
|
32 |
"@tailwindcss/typography": "^0.5.9",
|
|
|
33 |
"@types/marked": "^4.0.8",
|
34 |
"@types/parquetjs": "^0.10.3",
|
35 |
"@typescript-eslint/eslint-plugin": "^5.45.0",
|
@@ -890,6 +893,14 @@
|
|
890 |
"node": ">=4"
|
891 |
}
|
892 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
893 |
"node_modules/@types/chai": {
|
894 |
"version": "4.3.5",
|
895 |
"resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.3.5.tgz",
|
@@ -917,6 +928,17 @@
|
|
917 |
"integrity": "sha512-WulqXMDUTYAXCjZnk6JtIHPigp55cVtDgDrO2gHRwhyJto21+1zbVCtOYB2L1F9w4qCQ0rOGWBnBe0FNTiEJIQ==",
|
918 |
"dev": true
|
919 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
920 |
"node_modules/@types/json-schema": {
|
921 |
"version": "7.0.11",
|
922 |
"resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz",
|
@@ -970,6 +992,12 @@
|
|
970 |
"integrity": "sha512-21cFJr9z3g5dW8B0CVI9g2O9beqaThGQ6ZFBqHfwhzLDKUxaqTIy3vnfah/UPkfOiF2pLq+tGz+W8RyCskuslw==",
|
971 |
"dev": true
|
972 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
973 |
"node_modules/@types/webidl-conversions": {
|
974 |
"version": "7.0.0",
|
975 |
"resolved": "https://registry.npmjs.org/@types/webidl-conversions/-/webidl-conversions-7.0.0.tgz",
|
@@ -1268,6 +1296,11 @@
|
|
1268 |
"url": "https://opencollective.com/vitest"
|
1269 |
}
|
1270 |
},
|
|
|
|
|
|
|
|
|
|
|
1271 |
"node_modules/acorn": {
|
1272 |
"version": "8.8.2",
|
1273 |
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.2.tgz",
|
@@ -1298,6 +1331,17 @@
|
|
1298 |
"node": ">=0.4.0"
|
1299 |
}
|
1300 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1301 |
"node_modules/ajv": {
|
1302 |
"version": "6.12.6",
|
1303 |
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
|
@@ -1384,6 +1428,11 @@
|
|
1384 |
"node": "*"
|
1385 |
}
|
1386 |
},
|
|
|
|
|
|
|
|
|
|
|
1387 |
"node_modules/autoprefixer": {
|
1388 |
"version": "10.4.14",
|
1389 |
"resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.14.tgz",
|
@@ -1548,7 +1597,6 @@
|
|
1548 |
"version": "1.6.0",
|
1549 |
"resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz",
|
1550 |
"integrity": "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==",
|
1551 |
-
"dev": true,
|
1552 |
"dependencies": {
|
1553 |
"streamsearch": "^1.1.0"
|
1554 |
},
|
@@ -1698,6 +1746,17 @@
|
|
1698 |
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
|
1699 |
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
|
1700 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1701 |
"node_modules/commander": {
|
1702 |
"version": "4.1.1",
|
1703 |
"resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz",
|
@@ -1770,6 +1829,53 @@
|
|
1770 |
"node": ">=4"
|
1771 |
}
|
1772 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1773 |
"node_modules/date-fns": {
|
1774 |
"version": "2.29.3",
|
1775 |
"resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.29.3.tgz",
|
@@ -1798,7 +1904,6 @@
|
|
1798 |
"version": "4.3.4",
|
1799 |
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
|
1800 |
"integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==",
|
1801 |
-
"dev": true,
|
1802 |
"dependencies": {
|
1803 |
"ms": "2.1.2"
|
1804 |
},
|
@@ -1811,6 +1916,11 @@
|
|
1811 |
}
|
1812 |
}
|
1813 |
},
|
|
|
|
|
|
|
|
|
|
|
1814 |
"node_modules/deep-eql": {
|
1815 |
"version": "4.1.3",
|
1816 |
"resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-4.1.3.tgz",
|
@@ -1838,6 +1948,14 @@
|
|
1838 |
"node": ">=0.10.0"
|
1839 |
}
|
1840 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1841 |
"node_modules/detect-indent": {
|
1842 |
"version": "6.1.0",
|
1843 |
"resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-6.1.0.tgz",
|
@@ -1887,6 +2005,17 @@
|
|
1887 |
"node": ">=6.0.0"
|
1888 |
}
|
1889 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1890 |
"node_modules/dotenv": {
|
1891 |
"version": "16.0.3",
|
1892 |
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.0.3.tgz",
|
@@ -1900,6 +2029,17 @@
|
|
1900 |
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.359.tgz",
|
1901 |
"integrity": "sha512-OoVcngKCIuNXtZnsYoqlCvr0Cf3NIPzDIgwUfI9bdTFjXCrr79lI0kwQstLPZ7WhCezLlGksZk/BFAzoXC7GDw=="
|
1902 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1903 |
"node_modules/es6-promise": {
|
1904 |
"version": "3.3.1",
|
1905 |
"resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-3.3.1.tgz",
|
@@ -2366,6 +2506,19 @@
|
|
2366 |
"integrity": "sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==",
|
2367 |
"dev": true
|
2368 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2369 |
"node_modules/fraction.js": {
|
2370 |
"version": "4.2.0",
|
2371 |
"resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.2.0.tgz",
|
@@ -2545,6 +2698,42 @@
|
|
2545 |
"node": ">=12.0.0"
|
2546 |
}
|
2547 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2548 |
"node_modules/human-signals": {
|
2549 |
"version": "2.1.0",
|
2550 |
"resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz",
|
@@ -2554,6 +2743,17 @@
|
|
2554 |
"node": ">=10.17.0"
|
2555 |
}
|
2556 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2557 |
"node_modules/ignore": {
|
2558 |
"version": "5.2.4",
|
2559 |
"resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz",
|
@@ -2691,6 +2891,11 @@
|
|
2691 |
"node": ">=8"
|
2692 |
}
|
2693 |
},
|
|
|
|
|
|
|
|
|
|
|
2694 |
"node_modules/is-reference": {
|
2695 |
"version": "1.2.1",
|
2696 |
"resolved": "https://registry.npmjs.org/is-reference/-/is-reference-1.2.1.tgz",
|
@@ -2765,6 +2970,70 @@
|
|
2765 |
"js-yaml": "bin/js-yaml.js"
|
2766 |
}
|
2767 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2768 |
"node_modules/json-schema-traverse": {
|
2769 |
"version": "0.4.1",
|
2770 |
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
|
@@ -2990,6 +3259,25 @@
|
|
2990 |
"node": ">=10.0.0"
|
2991 |
}
|
2992 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2993 |
"node_modules/mimic-fn": {
|
2994 |
"version": "2.1.0",
|
2995 |
"resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz",
|
@@ -3114,8 +3402,7 @@
|
|
3114 |
"node_modules/ms": {
|
3115 |
"version": "2.1.2",
|
3116 |
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
|
3117 |
-
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
|
3118 |
-
"dev": true
|
3119 |
},
|
3120 |
"node_modules/mz": {
|
3121 |
"version": "2.7.0",
|
@@ -3194,6 +3481,11 @@
|
|
3194 |
"node": ">=8"
|
3195 |
}
|
3196 |
},
|
|
|
|
|
|
|
|
|
|
|
3197 |
"node_modules/object-assign": {
|
3198 |
"version": "4.1.1",
|
3199 |
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
|
@@ -3358,6 +3650,17 @@
|
|
3358 |
"node": ">=0.6.19"
|
3359 |
}
|
3360 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
3361 |
"node_modules/path-exists": {
|
3362 |
"version": "4.0.0",
|
3363 |
"resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
|
@@ -3747,6 +4050,11 @@
|
|
3747 |
"url": "https://github.com/chalk/ansi-styles?sponsor=1"
|
3748 |
}
|
3749 |
},
|
|
|
|
|
|
|
|
|
|
|
3750 |
"node_modules/punycode": {
|
3751 |
"version": "2.3.0",
|
3752 |
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz",
|
@@ -3764,6 +4072,11 @@
|
|
3764 |
"teleport": ">=0.2.0"
|
3765 |
}
|
3766 |
},
|
|
|
|
|
|
|
|
|
|
|
3767 |
"node_modules/queue-microtask": {
|
3768 |
"version": "1.2.3",
|
3769 |
"resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz",
|
@@ -3831,6 +4144,11 @@
|
|
3831 |
"url": "https://github.com/sponsors/mysticatea"
|
3832 |
}
|
3833 |
},
|
|
|
|
|
|
|
|
|
|
|
3834 |
"node_modules/resolve": {
|
3835 |
"version": "1.22.1",
|
3836 |
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz",
|
@@ -3896,6 +4214,11 @@
|
|
3896 |
"fsevents": "~2.3.2"
|
3897 |
}
|
3898 |
},
|
|
|
|
|
|
|
|
|
|
|
3899 |
"node_modules/run-parallel": {
|
3900 |
"version": "1.2.0",
|
3901 |
"resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz",
|
@@ -3930,6 +4253,11 @@
|
|
3930 |
"node": ">=6"
|
3931 |
}
|
3932 |
},
|
|
|
|
|
|
|
|
|
|
|
3933 |
"node_modules/sander": {
|
3934 |
"version": "0.5.1",
|
3935 |
"resolved": "https://registry.npmjs.org/sander/-/sander-0.5.1.tgz",
|
@@ -3966,6 +4294,17 @@
|
|
3966 |
"node": ">=6"
|
3967 |
}
|
3968 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
3969 |
"node_modules/semver": {
|
3970 |
"version": "7.3.8",
|
3971 |
"resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz",
|
@@ -3981,6 +4320,14 @@
|
|
3981 |
"node": ">=10"
|
3982 |
}
|
3983 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
3984 |
"node_modules/set-cookie-parser": {
|
3985 |
"version": "2.6.0",
|
3986 |
"resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.6.0.tgz",
|
@@ -4118,7 +4465,6 @@
|
|
4118 |
"version": "1.1.0",
|
4119 |
"resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz",
|
4120 |
"integrity": "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==",
|
4121 |
-
"dev": true,
|
4122 |
"engines": {
|
4123 |
"node": ">=10.0.0"
|
4124 |
}
|
@@ -4423,6 +4769,11 @@
|
|
4423 |
"node": ">=12"
|
4424 |
}
|
4425 |
},
|
|
|
|
|
|
|
|
|
|
|
4426 |
"node_modules/tailwind-scrollbar": {
|
4427 |
"version": "3.0.0",
|
4428 |
"resolved": "https://registry.npmjs.org/tailwind-scrollbar/-/tailwind-scrollbar-3.0.0.tgz",
|
@@ -4576,6 +4927,20 @@
|
|
4576 |
"node": ">=6"
|
4577 |
}
|
4578 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
4579 |
"node_modules/tr46": {
|
4580 |
"version": "3.0.0",
|
4581 |
"resolved": "https://registry.npmjs.org/tr46/-/tr46-3.0.0.tgz",
|
@@ -4675,7 +5040,6 @@
|
|
4675 |
"version": "5.22.0",
|
4676 |
"resolved": "https://registry.npmjs.org/undici/-/undici-5.22.0.tgz",
|
4677 |
"integrity": "sha512-fR9RXCc+6Dxav4P9VV/sp5w3eFiSdOjJYsbtWfd4s5L5C4ogyuVpdKIVHeW0vV1MloM65/f7W45nR9ZxwVdyiA==",
|
4678 |
-
"dev": true,
|
4679 |
"dependencies": {
|
4680 |
"busboy": "^1.6.0"
|
4681 |
},
|
@@ -4683,6 +5047,14 @@
|
|
4683 |
"node": ">=14.0"
|
4684 |
}
|
4685 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
4686 |
"node_modules/unplugin": {
|
4687 |
"version": "1.3.1",
|
4688 |
"resolved": "https://registry.npmjs.org/unplugin/-/unplugin-1.3.1.tgz",
|
@@ -4767,6 +5139,15 @@
|
|
4767 |
"punycode": "^2.1.0"
|
4768 |
}
|
4769 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
4770 |
"node_modules/util-deprecate": {
|
4771 |
"version": "1.0.2",
|
4772 |
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
|
@@ -4940,6 +5321,17 @@
|
|
4940 |
}
|
4941 |
}
|
4942 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
4943 |
"node_modules/webidl-conversions": {
|
4944 |
"version": "7.0.0",
|
4945 |
"resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz",
|
@@ -4972,6 +5364,25 @@
|
|
4972 |
"node": ">=6"
|
4973 |
}
|
4974 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
4975 |
"node_modules/whatwg-url": {
|
4976 |
"version": "11.0.0",
|
4977 |
"resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-11.0.0.tgz",
|
@@ -5049,6 +5460,19 @@
|
|
5049 |
}
|
5050 |
}
|
5051 |
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
5052 |
"node_modules/yallist": {
|
5053 |
"version": "4.0.0",
|
5054 |
"resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
|
|
|
14 |
"date-fns": "^2.29.3",
|
15 |
"dotenv": "^16.0.3",
|
16 |
"highlight.js": "^11.7.0",
|
17 |
+
"jsdom": "^22.0.0",
|
18 |
"marked": "^4.3.0",
|
19 |
"mongodb": "^5.3.0",
|
20 |
"nanoid": "^4.0.2",
|
21 |
"openid-client": "^5.4.2",
|
22 |
"parquetjs": "^0.11.2",
|
23 |
"postcss": "^8.4.21",
|
24 |
+
"serpapi": "^1.1.1",
|
25 |
"tailwind-scrollbar": "^3.0.0",
|
26 |
"tailwindcss": "^3.3.1",
|
27 |
"zod": "^3.21.4"
|
|
|
32 |
"@sveltejs/adapter-node": "^1.2.4",
|
33 |
"@sveltejs/kit": "^1.15.10",
|
34 |
"@tailwindcss/typography": "^0.5.9",
|
35 |
+
"@types/jsdom": "^21.1.1",
|
36 |
"@types/marked": "^4.0.8",
|
37 |
"@types/parquetjs": "^0.10.3",
|
38 |
"@typescript-eslint/eslint-plugin": "^5.45.0",
|
|
|
893 |
"node": ">=4"
|
894 |
}
|
895 |
},
|
896 |
+
"node_modules/@tootallnate/once": {
|
897 |
+
"version": "2.0.0",
|
898 |
+
"resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz",
|
899 |
+
"integrity": "sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==",
|
900 |
+
"engines": {
|
901 |
+
"node": ">= 10"
|
902 |
+
}
|
903 |
+
},
|
904 |
"node_modules/@types/chai": {
|
905 |
"version": "4.3.5",
|
906 |
"resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.3.5.tgz",
|
|
|
928 |
"integrity": "sha512-WulqXMDUTYAXCjZnk6JtIHPigp55cVtDgDrO2gHRwhyJto21+1zbVCtOYB2L1F9w4qCQ0rOGWBnBe0FNTiEJIQ==",
|
929 |
"dev": true
|
930 |
},
|
931 |
+
"node_modules/@types/jsdom": {
|
932 |
+
"version": "21.1.1",
|
933 |
+
"resolved": "https://registry.npmjs.org/@types/jsdom/-/jsdom-21.1.1.tgz",
|
934 |
+
"integrity": "sha512-cZFuoVLtzKP3gmq9eNosUL1R50U+USkbLtUQ1bYVgl/lKp0FZM7Cq4aIHAL8oIvQ17uSHi7jXPtfDOdjPwBE7A==",
|
935 |
+
"dev": true,
|
936 |
+
"dependencies": {
|
937 |
+
"@types/node": "*",
|
938 |
+
"@types/tough-cookie": "*",
|
939 |
+
"parse5": "^7.0.0"
|
940 |
+
}
|
941 |
+
},
|
942 |
"node_modules/@types/json-schema": {
|
943 |
"version": "7.0.11",
|
944 |
"resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz",
|
|
|
992 |
"integrity": "sha512-21cFJr9z3g5dW8B0CVI9g2O9beqaThGQ6ZFBqHfwhzLDKUxaqTIy3vnfah/UPkfOiF2pLq+tGz+W8RyCskuslw==",
|
993 |
"dev": true
|
994 |
},
|
995 |
+
"node_modules/@types/tough-cookie": {
|
996 |
+
"version": "4.0.2",
|
997 |
+
"resolved": "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-4.0.2.tgz",
|
998 |
+
"integrity": "sha512-Q5vtl1W5ue16D+nIaW8JWebSSraJVlK+EthKn7e7UcD4KWsaSJ8BqGPXNaPghgtcn/fhvrN17Tv8ksUsQpiplw==",
|
999 |
+
"dev": true
|
1000 |
+
},
|
1001 |
"node_modules/@types/webidl-conversions": {
|
1002 |
"version": "7.0.0",
|
1003 |
"resolved": "https://registry.npmjs.org/@types/webidl-conversions/-/webidl-conversions-7.0.0.tgz",
|
|
|
1296 |
"url": "https://opencollective.com/vitest"
|
1297 |
}
|
1298 |
},
|
1299 |
+
"node_modules/abab": {
|
1300 |
+
"version": "2.0.6",
|
1301 |
+
"resolved": "https://registry.npmjs.org/abab/-/abab-2.0.6.tgz",
|
1302 |
+
"integrity": "sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA=="
|
1303 |
+
},
|
1304 |
"node_modules/acorn": {
|
1305 |
"version": "8.8.2",
|
1306 |
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.2.tgz",
|
|
|
1331 |
"node": ">=0.4.0"
|
1332 |
}
|
1333 |
},
|
1334 |
+
"node_modules/agent-base": {
|
1335 |
+
"version": "6.0.2",
|
1336 |
+
"resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz",
|
1337 |
+
"integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==",
|
1338 |
+
"dependencies": {
|
1339 |
+
"debug": "4"
|
1340 |
+
},
|
1341 |
+
"engines": {
|
1342 |
+
"node": ">= 6.0.0"
|
1343 |
+
}
|
1344 |
+
},
|
1345 |
"node_modules/ajv": {
|
1346 |
"version": "6.12.6",
|
1347 |
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
|
|
|
1428 |
"node": "*"
|
1429 |
}
|
1430 |
},
|
1431 |
+
"node_modules/asynckit": {
|
1432 |
+
"version": "0.4.0",
|
1433 |
+
"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
|
1434 |
+
"integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="
|
1435 |
+
},
|
1436 |
"node_modules/autoprefixer": {
|
1437 |
"version": "10.4.14",
|
1438 |
"resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.14.tgz",
|
|
|
1597 |
"version": "1.6.0",
|
1598 |
"resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz",
|
1599 |
"integrity": "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==",
|
|
|
1600 |
"dependencies": {
|
1601 |
"streamsearch": "^1.1.0"
|
1602 |
},
|
|
|
1746 |
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
|
1747 |
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="
|
1748 |
},
|
1749 |
+
"node_modules/combined-stream": {
|
1750 |
+
"version": "1.0.8",
|
1751 |
+
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
|
1752 |
+
"integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
|
1753 |
+
"dependencies": {
|
1754 |
+
"delayed-stream": "~1.0.0"
|
1755 |
+
},
|
1756 |
+
"engines": {
|
1757 |
+
"node": ">= 0.8"
|
1758 |
+
}
|
1759 |
+
},
|
1760 |
"node_modules/commander": {
|
1761 |
"version": "4.1.1",
|
1762 |
"resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz",
|
|
|
1829 |
"node": ">=4"
|
1830 |
}
|
1831 |
},
|
1832 |
+
"node_modules/cssstyle": {
|
1833 |
+
"version": "3.0.0",
|
1834 |
+
"resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-3.0.0.tgz",
|
1835 |
+
"integrity": "sha512-N4u2ABATi3Qplzf0hWbVCdjenim8F3ojEXpBDF5hBpjzW182MjNGLqfmQ0SkSPeQ+V86ZXgeH8aXj6kayd4jgg==",
|
1836 |
+
"dependencies": {
|
1837 |
+
"rrweb-cssom": "^0.6.0"
|
1838 |
+
},
|
1839 |
+
"engines": {
|
1840 |
+
"node": ">=14"
|
1841 |
+
}
|
1842 |
+
},
|
1843 |
+
"node_modules/data-urls": {
|
1844 |
+
"version": "4.0.0",
|
1845 |
+
"resolved": "https://registry.npmjs.org/data-urls/-/data-urls-4.0.0.tgz",
|
1846 |
+
"integrity": "sha512-/mMTei/JXPqvFqQtfyTowxmJVwr2PVAeCcDxyFf6LhoOu/09TX2OX3kb2wzi4DMXcfj4OItwDOnhl5oziPnT6g==",
|
1847 |
+
"dependencies": {
|
1848 |
+
"abab": "^2.0.6",
|
1849 |
+
"whatwg-mimetype": "^3.0.0",
|
1850 |
+
"whatwg-url": "^12.0.0"
|
1851 |
+
},
|
1852 |
+
"engines": {
|
1853 |
+
"node": ">=14"
|
1854 |
+
}
|
1855 |
+
},
|
1856 |
+
"node_modules/data-urls/node_modules/tr46": {
|
1857 |
+
"version": "4.1.1",
|
1858 |
+
"resolved": "https://registry.npmjs.org/tr46/-/tr46-4.1.1.tgz",
|
1859 |
+
"integrity": "sha512-2lv/66T7e5yNyhAAC4NaKe5nVavzuGJQVVtRYLyQ2OI8tsJ61PMLlelehb0wi2Hx6+hT/OJUWZcw8MjlSRnxvw==",
|
1860 |
+
"dependencies": {
|
1861 |
+
"punycode": "^2.3.0"
|
1862 |
+
},
|
1863 |
+
"engines": {
|
1864 |
+
"node": ">=14"
|
1865 |
+
}
|
1866 |
+
},
|
1867 |
+
"node_modules/data-urls/node_modules/whatwg-url": {
|
1868 |
+
"version": "12.0.1",
|
1869 |
+
"resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-12.0.1.tgz",
|
1870 |
+
"integrity": "sha512-Ed/LrqB8EPlGxjS+TrsXcpUond1mhccS3pchLhzSgPCnTimUCKj3IZE75pAs5m6heB2U2TMerKFUXheyHY+VDQ==",
|
1871 |
+
"dependencies": {
|
1872 |
+
"tr46": "^4.1.1",
|
1873 |
+
"webidl-conversions": "^7.0.0"
|
1874 |
+
},
|
1875 |
+
"engines": {
|
1876 |
+
"node": ">=14"
|
1877 |
+
}
|
1878 |
+
},
|
1879 |
"node_modules/date-fns": {
|
1880 |
"version": "2.29.3",
|
1881 |
"resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.29.3.tgz",
|
|
|
1904 |
"version": "4.3.4",
|
1905 |
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
|
1906 |
"integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==",
|
|
|
1907 |
"dependencies": {
|
1908 |
"ms": "2.1.2"
|
1909 |
},
|
|
|
1916 |
}
|
1917 |
}
|
1918 |
},
|
1919 |
+
"node_modules/decimal.js": {
|
1920 |
+
"version": "10.4.3",
|
1921 |
+
"resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.4.3.tgz",
|
1922 |
+
"integrity": "sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA=="
|
1923 |
+
},
|
1924 |
"node_modules/deep-eql": {
|
1925 |
"version": "4.1.3",
|
1926 |
"resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-4.1.3.tgz",
|
|
|
1948 |
"node": ">=0.10.0"
|
1949 |
}
|
1950 |
},
|
1951 |
+
"node_modules/delayed-stream": {
|
1952 |
+
"version": "1.0.0",
|
1953 |
+
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
|
1954 |
+
"integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
|
1955 |
+
"engines": {
|
1956 |
+
"node": ">=0.4.0"
|
1957 |
+
}
|
1958 |
+
},
|
1959 |
"node_modules/detect-indent": {
|
1960 |
"version": "6.1.0",
|
1961 |
"resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-6.1.0.tgz",
|
|
|
2005 |
"node": ">=6.0.0"
|
2006 |
}
|
2007 |
},
|
2008 |
+
"node_modules/domexception": {
|
2009 |
+
"version": "4.0.0",
|
2010 |
+
"resolved": "https://registry.npmjs.org/domexception/-/domexception-4.0.0.tgz",
|
2011 |
+
"integrity": "sha512-A2is4PLG+eeSfoTMA95/s4pvAoSo2mKtiM5jlHkAVewmiO8ISFTFKZjH7UAM1Atli/OT/7JHOrJRJiMKUZKYBw==",
|
2012 |
+
"dependencies": {
|
2013 |
+
"webidl-conversions": "^7.0.0"
|
2014 |
+
},
|
2015 |
+
"engines": {
|
2016 |
+
"node": ">=12"
|
2017 |
+
}
|
2018 |
+
},
|
2019 |
"node_modules/dotenv": {
|
2020 |
"version": "16.0.3",
|
2021 |
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.0.3.tgz",
|
|
|
2029 |
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.359.tgz",
|
2030 |
"integrity": "sha512-OoVcngKCIuNXtZnsYoqlCvr0Cf3NIPzDIgwUfI9bdTFjXCrr79lI0kwQstLPZ7WhCezLlGksZk/BFAzoXC7GDw=="
|
2031 |
},
|
2032 |
+
"node_modules/entities": {
|
2033 |
+
"version": "4.5.0",
|
2034 |
+
"resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz",
|
2035 |
+
"integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==",
|
2036 |
+
"engines": {
|
2037 |
+
"node": ">=0.12"
|
2038 |
+
},
|
2039 |
+
"funding": {
|
2040 |
+
"url": "https://github.com/fb55/entities?sponsor=1"
|
2041 |
+
}
|
2042 |
+
},
|
2043 |
"node_modules/es6-promise": {
|
2044 |
"version": "3.3.1",
|
2045 |
"resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-3.3.1.tgz",
|
|
|
2506 |
"integrity": "sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==",
|
2507 |
"dev": true
|
2508 |
},
|
2509 |
+
"node_modules/form-data": {
|
2510 |
+
"version": "4.0.0",
|
2511 |
+
"resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz",
|
2512 |
+
"integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==",
|
2513 |
+
"dependencies": {
|
2514 |
+
"asynckit": "^0.4.0",
|
2515 |
+
"combined-stream": "^1.0.8",
|
2516 |
+
"mime-types": "^2.1.12"
|
2517 |
+
},
|
2518 |
+
"engines": {
|
2519 |
+
"node": ">= 6"
|
2520 |
+
}
|
2521 |
+
},
|
2522 |
"node_modules/fraction.js": {
|
2523 |
"version": "4.2.0",
|
2524 |
"resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.2.0.tgz",
|
|
|
2698 |
"node": ">=12.0.0"
|
2699 |
}
|
2700 |
},
|
2701 |
+
"node_modules/html-encoding-sniffer": {
|
2702 |
+
"version": "3.0.0",
|
2703 |
+
"resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-3.0.0.tgz",
|
2704 |
+
"integrity": "sha512-oWv4T4yJ52iKrufjnyZPkrN0CH3QnrUqdB6In1g5Fe1mia8GmF36gnfNySxoZtxD5+NmYw1EElVXiBk93UeskA==",
|
2705 |
+
"dependencies": {
|
2706 |
+
"whatwg-encoding": "^2.0.0"
|
2707 |
+
},
|
2708 |
+
"engines": {
|
2709 |
+
"node": ">=12"
|
2710 |
+
}
|
2711 |
+
},
|
2712 |
+
"node_modules/http-proxy-agent": {
|
2713 |
+
"version": "5.0.0",
|
2714 |
+
"resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz",
|
2715 |
+
"integrity": "sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==",
|
2716 |
+
"dependencies": {
|
2717 |
+
"@tootallnate/once": "2",
|
2718 |
+
"agent-base": "6",
|
2719 |
+
"debug": "4"
|
2720 |
+
},
|
2721 |
+
"engines": {
|
2722 |
+
"node": ">= 6"
|
2723 |
+
}
|
2724 |
+
},
|
2725 |
+
"node_modules/https-proxy-agent": {
|
2726 |
+
"version": "5.0.1",
|
2727 |
+
"resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz",
|
2728 |
+
"integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==",
|
2729 |
+
"dependencies": {
|
2730 |
+
"agent-base": "6",
|
2731 |
+
"debug": "4"
|
2732 |
+
},
|
2733 |
+
"engines": {
|
2734 |
+
"node": ">= 6"
|
2735 |
+
}
|
2736 |
+
},
|
2737 |
"node_modules/human-signals": {
|
2738 |
"version": "2.1.0",
|
2739 |
"resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz",
|
|
|
2743 |
"node": ">=10.17.0"
|
2744 |
}
|
2745 |
},
|
2746 |
+
"node_modules/iconv-lite": {
|
2747 |
+
"version": "0.6.3",
|
2748 |
+
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
|
2749 |
+
"integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==",
|
2750 |
+
"dependencies": {
|
2751 |
+
"safer-buffer": ">= 2.1.2 < 3.0.0"
|
2752 |
+
},
|
2753 |
+
"engines": {
|
2754 |
+
"node": ">=0.10.0"
|
2755 |
+
}
|
2756 |
+
},
|
2757 |
"node_modules/ignore": {
|
2758 |
"version": "5.2.4",
|
2759 |
"resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz",
|
|
|
2891 |
"node": ">=8"
|
2892 |
}
|
2893 |
},
|
2894 |
+
"node_modules/is-potential-custom-element-name": {
|
2895 |
+
"version": "1.0.1",
|
2896 |
+
"resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz",
|
2897 |
+
"integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ=="
|
2898 |
+
},
|
2899 |
"node_modules/is-reference": {
|
2900 |
"version": "1.2.1",
|
2901 |
"resolved": "https://registry.npmjs.org/is-reference/-/is-reference-1.2.1.tgz",
|
|
|
2970 |
"js-yaml": "bin/js-yaml.js"
|
2971 |
}
|
2972 |
},
|
2973 |
+
"node_modules/jsdom": {
|
2974 |
+
"version": "22.0.0",
|
2975 |
+
"resolved": "https://registry.npmjs.org/jsdom/-/jsdom-22.0.0.tgz",
|
2976 |
+
"integrity": "sha512-p5ZTEb5h+O+iU02t0GfEjAnkdYPrQSkfuTSMkMYyIoMvUNEHsbG0bHHbfXIcfTqD2UfvjQX7mmgiFsyRwGscVw==",
|
2977 |
+
"dependencies": {
|
2978 |
+
"abab": "^2.0.6",
|
2979 |
+
"cssstyle": "^3.0.0",
|
2980 |
+
"data-urls": "^4.0.0",
|
2981 |
+
"decimal.js": "^10.4.3",
|
2982 |
+
"domexception": "^4.0.0",
|
2983 |
+
"form-data": "^4.0.0",
|
2984 |
+
"html-encoding-sniffer": "^3.0.0",
|
2985 |
+
"http-proxy-agent": "^5.0.0",
|
2986 |
+
"https-proxy-agent": "^5.0.1",
|
2987 |
+
"is-potential-custom-element-name": "^1.0.1",
|
2988 |
+
"nwsapi": "^2.2.4",
|
2989 |
+
"parse5": "^7.1.2",
|
2990 |
+
"rrweb-cssom": "^0.6.0",
|
2991 |
+
"saxes": "^6.0.0",
|
2992 |
+
"symbol-tree": "^3.2.4",
|
2993 |
+
"tough-cookie": "^4.1.2",
|
2994 |
+
"w3c-xmlserializer": "^4.0.0",
|
2995 |
+
"webidl-conversions": "^7.0.0",
|
2996 |
+
"whatwg-encoding": "^2.0.0",
|
2997 |
+
"whatwg-mimetype": "^3.0.0",
|
2998 |
+
"whatwg-url": "^12.0.1",
|
2999 |
+
"ws": "^8.13.0",
|
3000 |
+
"xml-name-validator": "^4.0.0"
|
3001 |
+
},
|
3002 |
+
"engines": {
|
3003 |
+
"node": ">=16"
|
3004 |
+
},
|
3005 |
+
"peerDependencies": {
|
3006 |
+
"canvas": "^2.5.0"
|
3007 |
+
},
|
3008 |
+
"peerDependenciesMeta": {
|
3009 |
+
"canvas": {
|
3010 |
+
"optional": true
|
3011 |
+
}
|
3012 |
+
}
|
3013 |
+
},
|
3014 |
+
"node_modules/jsdom/node_modules/tr46": {
|
3015 |
+
"version": "4.1.1",
|
3016 |
+
"resolved": "https://registry.npmjs.org/tr46/-/tr46-4.1.1.tgz",
|
3017 |
+
"integrity": "sha512-2lv/66T7e5yNyhAAC4NaKe5nVavzuGJQVVtRYLyQ2OI8tsJ61PMLlelehb0wi2Hx6+hT/OJUWZcw8MjlSRnxvw==",
|
3018 |
+
"dependencies": {
|
3019 |
+
"punycode": "^2.3.0"
|
3020 |
+
},
|
3021 |
+
"engines": {
|
3022 |
+
"node": ">=14"
|
3023 |
+
}
|
3024 |
+
},
|
3025 |
+
"node_modules/jsdom/node_modules/whatwg-url": {
|
3026 |
+
"version": "12.0.1",
|
3027 |
+
"resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-12.0.1.tgz",
|
3028 |
+
"integrity": "sha512-Ed/LrqB8EPlGxjS+TrsXcpUond1mhccS3pchLhzSgPCnTimUCKj3IZE75pAs5m6heB2U2TMerKFUXheyHY+VDQ==",
|
3029 |
+
"dependencies": {
|
3030 |
+
"tr46": "^4.1.1",
|
3031 |
+
"webidl-conversions": "^7.0.0"
|
3032 |
+
},
|
3033 |
+
"engines": {
|
3034 |
+
"node": ">=14"
|
3035 |
+
}
|
3036 |
+
},
|
3037 |
"node_modules/json-schema-traverse": {
|
3038 |
"version": "0.4.1",
|
3039 |
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
|
|
|
3259 |
"node": ">=10.0.0"
|
3260 |
}
|
3261 |
},
|
3262 |
+
"node_modules/mime-db": {
|
3263 |
+
"version": "1.52.0",
|
3264 |
+
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
|
3265 |
+
"integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
|
3266 |
+
"engines": {
|
3267 |
+
"node": ">= 0.6"
|
3268 |
+
}
|
3269 |
+
},
|
3270 |
+
"node_modules/mime-types": {
|
3271 |
+
"version": "2.1.35",
|
3272 |
+
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
|
3273 |
+
"integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
|
3274 |
+
"dependencies": {
|
3275 |
+
"mime-db": "1.52.0"
|
3276 |
+
},
|
3277 |
+
"engines": {
|
3278 |
+
"node": ">= 0.6"
|
3279 |
+
}
|
3280 |
+
},
|
3281 |
"node_modules/mimic-fn": {
|
3282 |
"version": "2.1.0",
|
3283 |
"resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz",
|
|
|
3402 |
"node_modules/ms": {
|
3403 |
"version": "2.1.2",
|
3404 |
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
|
3405 |
+
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
|
|
|
3406 |
},
|
3407 |
"node_modules/mz": {
|
3408 |
"version": "2.7.0",
|
|
|
3481 |
"node": ">=8"
|
3482 |
}
|
3483 |
},
|
3484 |
+
"node_modules/nwsapi": {
|
3485 |
+
"version": "2.2.4",
|
3486 |
+
"resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.4.tgz",
|
3487 |
+
"integrity": "sha512-NHj4rzRo0tQdijE9ZqAx6kYDcoRwYwSYzCA8MY3JzfxlrvEU0jhnhJT9BhqhJs7I/dKcrDm6TyulaRqZPIhN5g=="
|
3488 |
+
},
|
3489 |
"node_modules/object-assign": {
|
3490 |
"version": "4.1.1",
|
3491 |
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
|
|
|
3650 |
"node": ">=0.6.19"
|
3651 |
}
|
3652 |
},
|
3653 |
+
"node_modules/parse5": {
|
3654 |
+
"version": "7.1.2",
|
3655 |
+
"resolved": "https://registry.npmjs.org/parse5/-/parse5-7.1.2.tgz",
|
3656 |
+
"integrity": "sha512-Czj1WaSVpaoj0wbhMzLmWD69anp2WH7FXMB9n1Sy8/ZFF9jolSQVMu1Ij5WIyGmcBmhk7EOndpO4mIpihVqAXw==",
|
3657 |
+
"dependencies": {
|
3658 |
+
"entities": "^4.4.0"
|
3659 |
+
},
|
3660 |
+
"funding": {
|
3661 |
+
"url": "https://github.com/inikulin/parse5?sponsor=1"
|
3662 |
+
}
|
3663 |
+
},
|
3664 |
"node_modules/path-exists": {
|
3665 |
"version": "4.0.0",
|
3666 |
"resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
|
|
|
4050 |
"url": "https://github.com/chalk/ansi-styles?sponsor=1"
|
4051 |
}
|
4052 |
},
|
4053 |
+
"node_modules/psl": {
|
4054 |
+
"version": "1.9.0",
|
4055 |
+
"resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz",
|
4056 |
+
"integrity": "sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag=="
|
4057 |
+
},
|
4058 |
"node_modules/punycode": {
|
4059 |
"version": "2.3.0",
|
4060 |
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz",
|
|
|
4072 |
"teleport": ">=0.2.0"
|
4073 |
}
|
4074 |
},
|
4075 |
+
"node_modules/querystringify": {
|
4076 |
+
"version": "2.2.0",
|
4077 |
+
"resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz",
|
4078 |
+
"integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ=="
|
4079 |
+
},
|
4080 |
"node_modules/queue-microtask": {
|
4081 |
"version": "1.2.3",
|
4082 |
"resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz",
|
|
|
4144 |
"url": "https://github.com/sponsors/mysticatea"
|
4145 |
}
|
4146 |
},
|
4147 |
+
"node_modules/requires-port": {
|
4148 |
+
"version": "1.0.0",
|
4149 |
+
"resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz",
|
4150 |
+
"integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ=="
|
4151 |
+
},
|
4152 |
"node_modules/resolve": {
|
4153 |
"version": "1.22.1",
|
4154 |
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz",
|
|
|
4214 |
"fsevents": "~2.3.2"
|
4215 |
}
|
4216 |
},
|
4217 |
+
"node_modules/rrweb-cssom": {
|
4218 |
+
"version": "0.6.0",
|
4219 |
+
"resolved": "https://registry.npmjs.org/rrweb-cssom/-/rrweb-cssom-0.6.0.tgz",
|
4220 |
+
"integrity": "sha512-APM0Gt1KoXBz0iIkkdB/kfvGOwC4UuJFeG/c+yV7wSc7q96cG/kJ0HiYCnzivD9SB53cLV1MlHFNfOuPaadYSw=="
|
4221 |
+
},
|
4222 |
"node_modules/run-parallel": {
|
4223 |
"version": "1.2.0",
|
4224 |
"resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz",
|
|
|
4253 |
"node": ">=6"
|
4254 |
}
|
4255 |
},
|
4256 |
+
"node_modules/safer-buffer": {
|
4257 |
+
"version": "2.1.2",
|
4258 |
+
"resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
|
4259 |
+
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
|
4260 |
+
},
|
4261 |
"node_modules/sander": {
|
4262 |
"version": "0.5.1",
|
4263 |
"resolved": "https://registry.npmjs.org/sander/-/sander-0.5.1.tgz",
|
|
|
4294 |
"node": ">=6"
|
4295 |
}
|
4296 |
},
|
4297 |
+
"node_modules/saxes": {
|
4298 |
+
"version": "6.0.0",
|
4299 |
+
"resolved": "https://registry.npmjs.org/saxes/-/saxes-6.0.0.tgz",
|
4300 |
+
"integrity": "sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==",
|
4301 |
+
"dependencies": {
|
4302 |
+
"xmlchars": "^2.2.0"
|
4303 |
+
},
|
4304 |
+
"engines": {
|
4305 |
+
"node": ">=v12.22.7"
|
4306 |
+
}
|
4307 |
+
},
|
4308 |
"node_modules/semver": {
|
4309 |
"version": "7.3.8",
|
4310 |
"resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz",
|
|
|
4320 |
"node": ">=10"
|
4321 |
}
|
4322 |
},
|
4323 |
+
"node_modules/serpapi": {
|
4324 |
+
"version": "1.1.1",
|
4325 |
+
"resolved": "https://registry.npmjs.org/serpapi/-/serpapi-1.1.1.tgz",
|
4326 |
+
"integrity": "sha512-t5Bqu/6VMJ9naX8K+qCgUStpZOaNQFvIM4AudhMJLS6sqQT/EHaYrhGidDZHVx8QvcEdY6y1wNlxizOCtvJtUQ==",
|
4327 |
+
"dependencies": {
|
4328 |
+
"undici": "^5.12.0"
|
4329 |
+
}
|
4330 |
+
},
|
4331 |
"node_modules/set-cookie-parser": {
|
4332 |
"version": "2.6.0",
|
4333 |
"resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.6.0.tgz",
|
|
|
4465 |
"version": "1.1.0",
|
4466 |
"resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz",
|
4467 |
"integrity": "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==",
|
|
|
4468 |
"engines": {
|
4469 |
"node": ">=10.0.0"
|
4470 |
}
|
|
|
4769 |
"node": ">=12"
|
4770 |
}
|
4771 |
},
|
4772 |
+
"node_modules/symbol-tree": {
|
4773 |
+
"version": "3.2.4",
|
4774 |
+
"resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz",
|
4775 |
+
"integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw=="
|
4776 |
+
},
|
4777 |
"node_modules/tailwind-scrollbar": {
|
4778 |
"version": "3.0.0",
|
4779 |
"resolved": "https://registry.npmjs.org/tailwind-scrollbar/-/tailwind-scrollbar-3.0.0.tgz",
|
|
|
4927 |
"node": ">=6"
|
4928 |
}
|
4929 |
},
|
4930 |
+
"node_modules/tough-cookie": {
|
4931 |
+
"version": "4.1.2",
|
4932 |
+
"resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.2.tgz",
|
4933 |
+
"integrity": "sha512-G9fqXWoYFZgTc2z8Q5zaHy/vJMjm+WV0AkAeHxVCQiEB1b+dGvWzFW6QV07cY5jQ5gRkeid2qIkzkxUnmoQZUQ==",
|
4934 |
+
"dependencies": {
|
4935 |
+
"psl": "^1.1.33",
|
4936 |
+
"punycode": "^2.1.1",
|
4937 |
+
"universalify": "^0.2.0",
|
4938 |
+
"url-parse": "^1.5.3"
|
4939 |
+
},
|
4940 |
+
"engines": {
|
4941 |
+
"node": ">=6"
|
4942 |
+
}
|
4943 |
+
},
|
4944 |
"node_modules/tr46": {
|
4945 |
"version": "3.0.0",
|
4946 |
"resolved": "https://registry.npmjs.org/tr46/-/tr46-3.0.0.tgz",
|
|
|
5040 |
"version": "5.22.0",
|
5041 |
"resolved": "https://registry.npmjs.org/undici/-/undici-5.22.0.tgz",
|
5042 |
"integrity": "sha512-fR9RXCc+6Dxav4P9VV/sp5w3eFiSdOjJYsbtWfd4s5L5C4ogyuVpdKIVHeW0vV1MloM65/f7W45nR9ZxwVdyiA==",
|
|
|
5043 |
"dependencies": {
|
5044 |
"busboy": "^1.6.0"
|
5045 |
},
|
|
|
5047 |
"node": ">=14.0"
|
5048 |
}
|
5049 |
},
|
5050 |
+
"node_modules/universalify": {
|
5051 |
+
"version": "0.2.0",
|
5052 |
+
"resolved": "https://registry.npmjs.org/universalify/-/universalify-0.2.0.tgz",
|
5053 |
+
"integrity": "sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==",
|
5054 |
+
"engines": {
|
5055 |
+
"node": ">= 4.0.0"
|
5056 |
+
}
|
5057 |
+
},
|
5058 |
"node_modules/unplugin": {
|
5059 |
"version": "1.3.1",
|
5060 |
"resolved": "https://registry.npmjs.org/unplugin/-/unplugin-1.3.1.tgz",
|
|
|
5139 |
"punycode": "^2.1.0"
|
5140 |
}
|
5141 |
},
|
5142 |
+
"node_modules/url-parse": {
|
5143 |
+
"version": "1.5.10",
|
5144 |
+
"resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz",
|
5145 |
+
"integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==",
|
5146 |
+
"dependencies": {
|
5147 |
+
"querystringify": "^2.1.1",
|
5148 |
+
"requires-port": "^1.0.0"
|
5149 |
+
}
|
5150 |
+
},
|
5151 |
"node_modules/util-deprecate": {
|
5152 |
"version": "1.0.2",
|
5153 |
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
|
|
|
5321 |
}
|
5322 |
}
|
5323 |
},
|
5324 |
+
"node_modules/w3c-xmlserializer": {
|
5325 |
+
"version": "4.0.0",
|
5326 |
+
"resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-4.0.0.tgz",
|
5327 |
+
"integrity": "sha512-d+BFHzbiCx6zGfz0HyQ6Rg69w9k19nviJspaj4yNscGjrHu94sVP+aRm75yEbCh+r2/yR+7q6hux9LVtbuTGBw==",
|
5328 |
+
"dependencies": {
|
5329 |
+
"xml-name-validator": "^4.0.0"
|
5330 |
+
},
|
5331 |
+
"engines": {
|
5332 |
+
"node": ">=14"
|
5333 |
+
}
|
5334 |
+
},
|
5335 |
"node_modules/webidl-conversions": {
|
5336 |
"version": "7.0.0",
|
5337 |
"resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz",
|
|
|
5364 |
"node": ">=6"
|
5365 |
}
|
5366 |
},
|
5367 |
+
"node_modules/whatwg-encoding": {
|
5368 |
+
"version": "2.0.0",
|
5369 |
+
"resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-2.0.0.tgz",
|
5370 |
+
"integrity": "sha512-p41ogyeMUrw3jWclHWTQg1k05DSVXPLcVxRTYsXUk+ZooOCZLcoYgPZ/HL/D/N+uQPOtcp1me1WhBEaX02mhWg==",
|
5371 |
+
"dependencies": {
|
5372 |
+
"iconv-lite": "0.6.3"
|
5373 |
+
},
|
5374 |
+
"engines": {
|
5375 |
+
"node": ">=12"
|
5376 |
+
}
|
5377 |
+
},
|
5378 |
+
"node_modules/whatwg-mimetype": {
|
5379 |
+
"version": "3.0.0",
|
5380 |
+
"resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-3.0.0.tgz",
|
5381 |
+
"integrity": "sha512-nt+N2dzIutVRxARx1nghPKGv1xHikU7HKdfafKkLNLindmPU/ch3U31NOCGGA/dmPcmb1VlofO0vnKAcsm0o/Q==",
|
5382 |
+
"engines": {
|
5383 |
+
"node": ">=12"
|
5384 |
+
}
|
5385 |
+
},
|
5386 |
"node_modules/whatwg-url": {
|
5387 |
"version": "11.0.0",
|
5388 |
"resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-11.0.0.tgz",
|
|
|
5460 |
}
|
5461 |
}
|
5462 |
},
|
5463 |
+
"node_modules/xml-name-validator": {
|
5464 |
+
"version": "4.0.0",
|
5465 |
+
"resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-4.0.0.tgz",
|
5466 |
+
"integrity": "sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw==",
|
5467 |
+
"engines": {
|
5468 |
+
"node": ">=12"
|
5469 |
+
}
|
5470 |
+
},
|
5471 |
+
"node_modules/xmlchars": {
|
5472 |
+
"version": "2.2.0",
|
5473 |
+
"resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz",
|
5474 |
+
"integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw=="
|
5475 |
+
},
|
5476 |
"node_modules/yallist": {
|
5477 |
"version": "4.0.0",
|
5478 |
"resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
|
@@ -19,6 +19,7 @@
|
|
19 |
"@sveltejs/adapter-node": "^1.2.4",
|
20 |
"@sveltejs/kit": "^1.15.10",
|
21 |
"@tailwindcss/typography": "^0.5.9",
|
|
|
22 |
"@types/marked": "^4.0.8",
|
23 |
"@types/parquetjs": "^0.10.3",
|
24 |
"@typescript-eslint/eslint-plugin": "^5.45.0",
|
@@ -45,12 +46,14 @@
|
|
45 |
"date-fns": "^2.29.3",
|
46 |
"dotenv": "^16.0.3",
|
47 |
"highlight.js": "^11.7.0",
|
|
|
48 |
"marked": "^4.3.0",
|
49 |
"mongodb": "^5.3.0",
|
50 |
"nanoid": "^4.0.2",
|
51 |
"openid-client": "^5.4.2",
|
52 |
"parquetjs": "^0.11.2",
|
53 |
"postcss": "^8.4.21",
|
|
|
54 |
"tailwind-scrollbar": "^3.0.0",
|
55 |
"tailwindcss": "^3.3.1",
|
56 |
"zod": "^3.21.4"
|
|
|
19 |
"@sveltejs/adapter-node": "^1.2.4",
|
20 |
"@sveltejs/kit": "^1.15.10",
|
21 |
"@tailwindcss/typography": "^0.5.9",
|
22 |
+
"@types/jsdom": "^21.1.1",
|
23 |
"@types/marked": "^4.0.8",
|
24 |
"@types/parquetjs": "^0.10.3",
|
25 |
"@typescript-eslint/eslint-plugin": "^5.45.0",
|
|
|
46 |
"date-fns": "^2.29.3",
|
47 |
"dotenv": "^16.0.3",
|
48 |
"highlight.js": "^11.7.0",
|
49 |
+
"jsdom": "^22.0.0",
|
50 |
"marked": "^4.3.0",
|
51 |
"mongodb": "^5.3.0",
|
52 |
"nanoid": "^4.0.2",
|
53 |
"openid-client": "^5.4.2",
|
54 |
"parquetjs": "^0.11.2",
|
55 |
"postcss": "^8.4.21",
|
56 |
+
"serpapi": "^1.1.1",
|
57 |
"tailwind-scrollbar": "^3.0.0",
|
58 |
"tailwindcss": "^3.3.1",
|
59 |
"zod": "^3.21.4"
|
@@ -1,15 +1,18 @@
|
|
1 |
import type { BackendModel } from "./server/models";
|
2 |
import type { Message } from "./types/Message";
|
3 |
-
|
|
|
4 |
/**
|
5 |
* Convert [{user: "assistant", content: "hi"}, {user: "user", content: "hello"}] to:
|
6 |
*
|
7 |
* <|assistant|>hi<|endoftext|><|prompter|>hello<|endoftext|><|assistant|>
|
8 |
*/
|
9 |
-
|
|
|
10 |
messages: Pick<Message, "from" | "content">[],
|
11 |
-
model: BackendModel
|
12 |
-
|
|
|
13 |
const prompt =
|
14 |
messages
|
15 |
.map(
|
@@ -25,12 +28,30 @@ export function buildPrompt(
|
|
25 |
)
|
26 |
.join("") + model.assistantMessageToken;
|
27 |
|
28 |
-
|
29 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
30 |
model.preprompt +
|
|
|
31 |
prompt
|
32 |
.split(" ")
|
33 |
.slice(-(model.parameters?.truncate ?? 0))
|
34 |
-
.join(" ")
|
35 |
-
|
|
|
|
|
36 |
}
|
|
|
1 |
import type { BackendModel } from "./server/models";
|
2 |
import type { Message } from "./types/Message";
|
3 |
+
import { collections } from "$lib/server/database";
|
4 |
+
import { ObjectId } from "mongodb";
|
5 |
/**
|
6 |
* Convert [{user: "assistant", content: "hi"}, {user: "user", content: "hello"}] to:
|
7 |
*
|
8 |
* <|assistant|>hi<|endoftext|><|prompter|>hello<|endoftext|><|assistant|>
|
9 |
*/
|
10 |
+
|
11 |
+
export async function buildPrompt(
|
12 |
messages: Pick<Message, "from" | "content">[],
|
13 |
+
model: BackendModel,
|
14 |
+
webSearchId?: string
|
15 |
+
): Promise<string> {
|
16 |
const prompt =
|
17 |
messages
|
18 |
.map(
|
|
|
28 |
)
|
29 |
.join("") + model.assistantMessageToken;
|
30 |
|
31 |
+
let webPrompt = "";
|
32 |
+
|
33 |
+
if (webSearchId) {
|
34 |
+
const webSearch = await collections.webSearches.findOne({
|
35 |
+
_id: new ObjectId(webSearchId),
|
36 |
+
});
|
37 |
+
|
38 |
+
if (!webSearch) throw new Error("Web search not found");
|
39 |
+
|
40 |
+
if (webSearch.summary) {
|
41 |
+
webPrompt =
|
42 |
+
model.assistantMessageToken +
|
43 |
+
`The following context was found while searching the internet: ${webSearch.summary}` +
|
44 |
+
model.messageEndToken;
|
45 |
+
}
|
46 |
+
}
|
47 |
+
const finalPrompt =
|
48 |
model.preprompt +
|
49 |
+
webPrompt +
|
50 |
prompt
|
51 |
.split(" ")
|
52 |
.slice(-(model.parameters?.truncate ?? 0))
|
53 |
+
.join(" ");
|
54 |
+
|
55 |
+
// Not super precise, but it's truncated in the model's backend anyway
|
56 |
+
return finalPrompt;
|
57 |
}
|
@@ -0,0 +1,133 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<script lang="ts">
|
2 |
+
import type { WebSearchMessage } from "$lib/types/WebSearch";
|
3 |
+
import CarbonCaretRight from "~icons/carbon/caret-right";
|
4 |
+
|
5 |
+
import CarbonCheckmark from "~icons/carbon/checkmark-filled";
|
6 |
+
import CarbonError from "~icons/carbon/error-filled";
|
7 |
+
|
8 |
+
import EosIconsLoading from "~icons/eos-icons/loading";
|
9 |
+
|
10 |
+
import { base } from "$app/paths";
|
11 |
+
import { onMount } from "svelte";
|
12 |
+
|
13 |
+
export let loading = false;
|
14 |
+
export let classNames = "";
|
15 |
+
export let webSearchId: string | undefined;
|
16 |
+
export let webSearchMessages: WebSearchMessage[] = [];
|
17 |
+
|
18 |
+
let detailsOpen: boolean;
|
19 |
+
let error: boolean;
|
20 |
+
onMount(() => {
|
21 |
+
if (webSearchMessages.length === 0 && webSearchId) {
|
22 |
+
fetch(`${base}/search/${webSearchId}`)
|
23 |
+
.then((res) => res.json())
|
24 |
+
.then((res) => {
|
25 |
+
webSearchMessages = [...res.messages, { type: "result", id: webSearchId }];
|
26 |
+
})
|
27 |
+
.catch((err) => console.log(err));
|
28 |
+
}
|
29 |
+
});
|
30 |
+
$: error = webSearchMessages.some((message) => message.type === "error");
|
31 |
+
</script>
|
32 |
+
|
33 |
+
<details
|
34 |
+
class="details flex w-fit rounded-xl border border-gray-200 bg-white shadow-sm dark:border-gray-800 dark:bg-gray-900 {classNames}"
|
35 |
+
on:toggle={() => {
|
36 |
+
if (webSearchMessages.length === 0 && webSearchId) {
|
37 |
+
fetch(`${base}/search/${webSearchId}`)
|
38 |
+
.then((res) => res.json())
|
39 |
+
.then((res) => {
|
40 |
+
webSearchMessages = [...res.messages, { type: "result", id: webSearchId }];
|
41 |
+
})
|
42 |
+
.catch((err) => console.log(err));
|
43 |
+
}
|
44 |
+
}}
|
45 |
+
bind:open={detailsOpen}
|
46 |
+
>
|
47 |
+
<summary
|
48 |
+
class="align-center flex cursor-pointer select-none list-none py-1 pl-2.5 pr-2 align-text-top transition-all"
|
49 |
+
>
|
50 |
+
{#if error}
|
51 |
+
<CarbonError class="my-auto text-red-700 dark:text-red-500" />
|
52 |
+
{:else if loading}
|
53 |
+
<EosIconsLoading class="my-auto text-gray-500" />
|
54 |
+
{:else}
|
55 |
+
<CarbonCheckmark class="my-auto text-gray-500" />
|
56 |
+
{/if}
|
57 |
+
<span class="px-2 font-medium" class:text-red-700={error} class:dark:text-red-500={error}
|
58 |
+
>Web search
|
59 |
+
</span>
|
60 |
+
<div class="my-auto transition-all" class:rotate-90={detailsOpen}>
|
61 |
+
<CarbonCaretRight />
|
62 |
+
</div>
|
63 |
+
</summary>
|
64 |
+
|
65 |
+
<div class="content p-5 pb-1">
|
66 |
+
{#if webSearchMessages.length === 0}
|
67 |
+
<div class="mx-auto w-fit">
|
68 |
+
<EosIconsLoading class="mb-3 h-10 w-10" />
|
69 |
+
</div>
|
70 |
+
{:else}
|
71 |
+
<ol class="relative border-l border-gray-200 dark:border-gray-600">
|
72 |
+
{#each webSearchMessages as message}
|
73 |
+
{#if message.type === "update"}
|
74 |
+
<li class="mb-4 ml-4">
|
75 |
+
<div
|
76 |
+
class="h-3 w-3 -translate-x-[1.4rem] rounded-full bg-gray-200 dark:bg-gray-600"
|
77 |
+
/>
|
78 |
+
<h3 class="text-md -translate-y-[1.1rem] text-gray-800 dark:text-gray-100">
|
79 |
+
{message.message}
|
80 |
+
</h3>
|
81 |
+
{#if message.args}
|
82 |
+
<p
|
83 |
+
class="mb-4 -translate-y-[1.1rem] font-normal text-gray-500 dark:text-gray-400 "
|
84 |
+
>
|
85 |
+
{message.args}
|
86 |
+
</p>
|
87 |
+
{/if}
|
88 |
+
</li>
|
89 |
+
{:else if message.type === "error"}
|
90 |
+
<li class="mb-4 ml-4">
|
91 |
+
<div
|
92 |
+
class="h-3 w-3 -translate-x-[1.4rem] rounded-full text-red-700 dark:text-red-500"
|
93 |
+
>
|
94 |
+
<CarbonError class="h-3 w-3" />
|
95 |
+
</div>
|
96 |
+
<h3 class="text-md -translate-y-[1.1rem] text-red-700 dark:text-red-500">
|
97 |
+
{message.message}
|
98 |
+
</h3>
|
99 |
+
{#if message.args}
|
100 |
+
<p class="mb-4 -translate-y-[1.1rem] font-normal text-gray-500 dark:text-gray-400 ">
|
101 |
+
{message.args}
|
102 |
+
</p>
|
103 |
+
{/if}
|
104 |
+
</li>
|
105 |
+
{/if}
|
106 |
+
<p />
|
107 |
+
{/each}
|
108 |
+
</ol>
|
109 |
+
{/if}
|
110 |
+
</div>
|
111 |
+
</details>
|
112 |
+
|
113 |
+
<style>
|
114 |
+
@keyframes grow {
|
115 |
+
0% {
|
116 |
+
font-size: 0;
|
117 |
+
opacity: 0;
|
118 |
+
}
|
119 |
+
30% {
|
120 |
+
font-size: 1em;
|
121 |
+
opacity: 0;
|
122 |
+
}
|
123 |
+
100% {
|
124 |
+
opacity: 1;
|
125 |
+
}
|
126 |
+
}
|
127 |
+
|
128 |
+
.details[open] .content {
|
129 |
+
animation-name: grow;
|
130 |
+
animation-duration: 300ms;
|
131 |
+
animation-delay: 0ms;
|
132 |
+
}
|
133 |
+
</style>
|
@@ -1,17 +1,13 @@
|
|
1 |
<script lang="ts">
|
2 |
-
import
|
3 |
|
4 |
-
export let
|
5 |
-
export let className = "";
|
6 |
</script>
|
7 |
|
8 |
<button
|
9 |
type="button"
|
10 |
on:click
|
11 |
-
class="btn flex rounded-lg border bg-white px-3 py-1 shadow-sm transition-all hover:bg-gray-100 dark:border-gray-600 dark:bg-gray-700 dark:hover:bg-gray-600
|
12 |
-
{className}
|
13 |
-
{visible ? 'visible opacity-100' : 'invisible opacity-0'}
|
14 |
-
"
|
15 |
>
|
16 |
-
<
|
17 |
</button>
|
|
|
1 |
<script lang="ts">
|
2 |
+
import CarbonStopFilledAlt from "~icons/carbon/stop-filled-alt";
|
3 |
|
4 |
+
export let classNames = "";
|
|
|
5 |
</script>
|
6 |
|
7 |
<button
|
8 |
type="button"
|
9 |
on:click
|
10 |
+
class="btn flex h-9 rounded-lg border bg-white px-3 py-1 shadow-sm transition-all hover:bg-gray-100 dark:border-gray-600 dark:bg-gray-700 dark:hover:bg-gray-600 {classNames}"
|
|
|
|
|
|
|
11 |
>
|
12 |
+
<CarbonStopFilledAlt class="-ml-1 mr-1 h-[1.25rem] w-[1.1875rem] text-gray-400" /> Stop generating
|
13 |
</button>
|
@@ -5,7 +5,9 @@
|
|
5 |
|
6 |
<input bind:checked type="checkbox" {name} class="peer pointer-events-none absolute opacity-0" />
|
7 |
<div
|
8 |
-
|
|
|
|
|
9 |
>
|
10 |
<div class="h-3.5 w-3.5 rounded-full bg-white shadow-sm transition-all" />
|
11 |
</div>
|
|
|
5 |
|
6 |
<input bind:checked type="checkbox" {name} class="peer pointer-events-none absolute opacity-0" />
|
7 |
<div
|
8 |
+
on:click
|
9 |
+
on:keypress
|
10 |
+
class="relative inline-flex h-5 w-9 shrink-0 items-center rounded-full bg-gray-300 p-1 shadow-inner ring-gray-400 transition-all peer-checked:bg-blue-600 peer-focus-visible:ring peer-focus-visible:ring-offset-1 hover:bg-gray-400 dark:bg-gray-600 peer-checked:[&>div]:translate-x-3.5"
|
11 |
>
|
12 |
<div class="h-3.5 w-3.5 rounded-full bg-white shadow-sm transition-all" />
|
13 |
</div>
|
@@ -0,0 +1,27 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<script lang="ts">
|
2 |
+
import { webSearchParameters } from "$lib/stores/webSearchParameters";
|
3 |
+
import CarbonInformation from "~icons/carbon/information";
|
4 |
+
import Switch from "./Switch.svelte";
|
5 |
+
|
6 |
+
const toggle = () => ($webSearchParameters.useSearch = !$webSearchParameters.useSearch);
|
7 |
+
</script>
|
8 |
+
|
9 |
+
<div
|
10 |
+
class="flex h-9 cursor-pointer select-none items-center gap-2 rounded-xl border bg-white p-1.5 shadow-sm hover:shadow-none dark:border-gray-800 dark:bg-gray-900"
|
11 |
+
on:click={toggle}
|
12 |
+
on:keypress={toggle}
|
13 |
+
>
|
14 |
+
<Switch name="useSearch" bind:checked={$webSearchParameters.useSearch} on:click on:keypress />
|
15 |
+
<div class="whitespace-nowrap text-sm text-gray-800 dark:text-gray-200">Search web</div>
|
16 |
+
<div class="group relative w-max">
|
17 |
+
<CarbonInformation class="text-xs text-gray-500" />
|
18 |
+
<div
|
19 |
+
class="pointer-events-none absolute -top-20 left-1/2 w-max -translate-x-1/2 rounded-md bg-gray-100 p-2 opacity-0 transition-opacity group-hover:opacity-100 dark:bg-gray-800"
|
20 |
+
>
|
21 |
+
<p class="max-w-sm text-sm text-gray-800 dark:text-gray-200">
|
22 |
+
When enabled, the model will try to complement its answer with information queried from the
|
23 |
+
web.
|
24 |
+
</p>
|
25 |
+
</div>
|
26 |
+
</div>
|
27 |
+
</div>
|
@@ -13,6 +13,9 @@
|
|
13 |
import CarbonThumbsDown from "~icons/carbon/thumbs-down";
|
14 |
import { PUBLIC_SEP_TOKEN } from "$lib/constants/publicSepToken";
|
15 |
import type { Model } from "$lib/types/Model";
|
|
|
|
|
|
|
16 |
|
17 |
function sanitizeMd(md: string) {
|
18 |
let ret = md
|
@@ -43,6 +46,9 @@
|
|
43 |
export let isAuthor = true;
|
44 |
export let readOnly = false;
|
45 |
export let isTapped = false;
|
|
|
|
|
|
|
46 |
|
47 |
const dispatch = createEventDispatcher<{
|
48 |
retry: { content: string; id: Message["id"] };
|
@@ -89,6 +95,13 @@
|
|
89 |
|
90 |
$: downloadLink =
|
91 |
message.from === "user" ? `${$page.url.pathname}/message/${message.id}/prompt` : undefined;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
92 |
</script>
|
93 |
|
94 |
{#if message.from === "assistant"}
|
@@ -103,11 +116,22 @@
|
|
103 |
class="mt-5 h-3 w-3 flex-none select-none rounded-full shadow-lg"
|
104 |
/>
|
105 |
<div
|
106 |
-
class="relative min-h-[calc(2rem+theme(spacing[3.5])*2)] min-w-[
|
107 |
>
|
108 |
-
{#if
|
109 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
110 |
{/if}
|
|
|
111 |
<div
|
112 |
class="prose max-w-none dark:prose-invert max-sm:prose-sm prose-headings:font-semibold prose-h1:text-lg prose-h2:text-base prose-h3:text-base prose-pre:bg-gray-800 dark:prose-pre:bg-gray-900"
|
113 |
bind:this={contentEl}
|
|
|
13 |
import CarbonThumbsDown from "~icons/carbon/thumbs-down";
|
14 |
import { PUBLIC_SEP_TOKEN } from "$lib/constants/publicSepToken";
|
15 |
import type { Model } from "$lib/types/Model";
|
16 |
+
import type { WebSearchMessage } from "$lib/types/WebSearch";
|
17 |
+
|
18 |
+
import OpenWebSearchResults from "../OpenWebSearchResults.svelte";
|
19 |
|
20 |
function sanitizeMd(md: string) {
|
21 |
let ret = md
|
|
|
46 |
export let isAuthor = true;
|
47 |
export let readOnly = false;
|
48 |
export let isTapped = false;
|
49 |
+
export let isLast = false;
|
50 |
+
|
51 |
+
export let webSearchMessages: WebSearchMessage[] = [];
|
52 |
|
53 |
const dispatch = createEventDispatcher<{
|
54 |
retry: { content: string; id: Message["id"] };
|
|
|
95 |
|
96 |
$: downloadLink =
|
97 |
message.from === "user" ? `${$page.url.pathname}/message/${message.id}/prompt` : undefined;
|
98 |
+
|
99 |
+
let webSearchIsDone = true;
|
100 |
+
|
101 |
+
$: webSearchIsDone =
|
102 |
+
!!message.webSearchId ||
|
103 |
+
(webSearchMessages.length > 0 &&
|
104 |
+
webSearchMessages[webSearchMessages.length - 1].type === "result");
|
105 |
</script>
|
106 |
|
107 |
{#if message.from === "assistant"}
|
|
|
116 |
class="mt-5 h-3 w-3 flex-none select-none rounded-full shadow-lg"
|
117 |
/>
|
118 |
<div
|
119 |
+
class="relative min-h-[calc(2rem+theme(spacing[3.5])*2)] min-w-[60px] break-words rounded-2xl border border-gray-100 bg-gradient-to-br from-gray-50 px-5 py-3.5 text-gray-600 prose-pre:my-2 dark:border-gray-800 dark:from-gray-800/40 dark:text-gray-300"
|
120 |
>
|
121 |
+
{#if message.webSearchId || (webSearchMessages.length > 0 && isLast)}
|
122 |
+
{#key (message.webSearchId, message.score, loading)}
|
123 |
+
<OpenWebSearchResults
|
124 |
+
classNames={tokens.length ? "mb-3" : ""}
|
125 |
+
webSearchId={message.webSearchId}
|
126 |
+
{webSearchMessages}
|
127 |
+
loading={!webSearchIsDone}
|
128 |
+
/>
|
129 |
+
{/key}
|
130 |
+
{/if}
|
131 |
+
{#if !message.content && (webSearchIsDone || webSearchMessages.length === 0)}
|
132 |
+
<IconLoading />
|
133 |
{/if}
|
134 |
+
|
135 |
<div
|
136 |
class="prose max-w-none dark:prose-invert max-sm:prose-sm prose-headings:font-semibold prose-h1:text-lg prose-h2:text-base prose-h3:text-base prose-pre:bg-gray-800 dark:prose-pre:bg-gray-900"
|
137 |
bind:this={contentEl}
|
@@ -8,6 +8,8 @@
|
|
8 |
import type { LayoutData } from "../../../routes/$types";
|
9 |
import ChatIntroduction from "./ChatIntroduction.svelte";
|
10 |
import ChatMessage from "./ChatMessage.svelte";
|
|
|
|
|
11 |
|
12 |
export let messages: Message[];
|
13 |
export let loading: boolean;
|
@@ -20,6 +22,8 @@
|
|
20 |
|
21 |
let chatContainer: HTMLElement;
|
22 |
|
|
|
|
|
23 |
async function scrollToBottom() {
|
24 |
await tick();
|
25 |
chatContainer.scrollTop = chatContainer.scrollHeight;
|
@@ -33,20 +37,24 @@
|
|
33 |
|
34 |
<div
|
35 |
class="scrollbar-custom mr-1 h-full overflow-y-auto"
|
36 |
-
use:snapScrollToBottom={messages.length ? messages : false}
|
37 |
bind:this={chatContainer}
|
38 |
>
|
39 |
<div class="mx-auto flex h-full max-w-3xl flex-col gap-6 px-5 pt-6 sm:gap-8 xl:max-w-4xl">
|
40 |
{#each messages as message, i}
|
41 |
-
|
42 |
-
|
43 |
-
|
44 |
-
|
45 |
-
|
46 |
-
|
47 |
-
|
48 |
-
|
49 |
-
|
|
|
|
|
|
|
|
|
50 |
{:else}
|
51 |
<ChatIntroduction {settings} {models} {currentModel} on:message />
|
52 |
{/each}
|
@@ -54,9 +62,11 @@
|
|
54 |
<ChatMessage
|
55 |
message={{ from: "assistant", content: "", id: randomUUID() }}
|
56 |
model={currentModel}
|
|
|
|
|
57 |
/>
|
58 |
{/if}
|
59 |
-
<div class="h-
|
60 |
</div>
|
61 |
<ScrollToBottomBtn
|
62 |
class="bottom-36 right-4 max-md:hidden lg:right-10"
|
|
|
8 |
import type { LayoutData } from "../../../routes/$types";
|
9 |
import ChatIntroduction from "./ChatIntroduction.svelte";
|
10 |
import ChatMessage from "./ChatMessage.svelte";
|
11 |
+
import type { WebSearchMessage } from "$lib/types/WebSearch";
|
12 |
+
import { page } from "$app/stores";
|
13 |
|
14 |
export let messages: Message[];
|
15 |
export let loading: boolean;
|
|
|
22 |
|
23 |
let chatContainer: HTMLElement;
|
24 |
|
25 |
+
export let webSearchMessages: WebSearchMessage[] = [];
|
26 |
+
|
27 |
async function scrollToBottom() {
|
28 |
await tick();
|
29 |
chatContainer.scrollTop = chatContainer.scrollHeight;
|
|
|
37 |
|
38 |
<div
|
39 |
class="scrollbar-custom mr-1 h-full overflow-y-auto"
|
40 |
+
use:snapScrollToBottom={messages.length ? [...messages, ...webSearchMessages] : false}
|
41 |
bind:this={chatContainer}
|
42 |
>
|
43 |
<div class="mx-auto flex h-full max-w-3xl flex-col gap-6 px-5 pt-6 sm:gap-8 xl:max-w-4xl">
|
44 |
{#each messages as message, i}
|
45 |
+
{#key (message.id, $page.params.id)}
|
46 |
+
<ChatMessage
|
47 |
+
loading={loading && i === messages.length - 1}
|
48 |
+
{message}
|
49 |
+
{isAuthor}
|
50 |
+
{readOnly}
|
51 |
+
model={currentModel}
|
52 |
+
{webSearchMessages}
|
53 |
+
isLast={i === messages.length - 1}
|
54 |
+
on:retry
|
55 |
+
on:vote
|
56 |
+
/>
|
57 |
+
{/key}
|
58 |
{:else}
|
59 |
<ChatIntroduction {settings} {models} {currentModel} on:message />
|
60 |
{/each}
|
|
|
62 |
<ChatMessage
|
63 |
message={{ from: "assistant", content: "", id: randomUUID() }}
|
64 |
model={currentModel}
|
65 |
+
isLast={true}
|
66 |
+
{webSearchMessages}
|
67 |
/>
|
68 |
{/if}
|
69 |
+
<div class="h-44 flex-none" />
|
70 |
</div>
|
71 |
<ScrollToBottomBtn
|
72 |
class="bottom-36 right-4 max-md:hidden lg:right-10"
|
@@ -4,7 +4,7 @@
|
|
4 |
|
5 |
import CarbonSendAltFilled from "~icons/carbon/send-alt-filled";
|
6 |
import CarbonExport from "~icons/carbon/export";
|
7 |
-
import
|
8 |
import EosIconsLoading from "~icons/eos-icons/loading";
|
9 |
|
10 |
import ChatMessages from "./ChatMessages.svelte";
|
@@ -12,6 +12,8 @@
|
|
12 |
import StopGeneratingBtn from "../StopGeneratingBtn.svelte";
|
13 |
import type { Model } from "$lib/types/Model";
|
14 |
import type { LayoutData } from "../../../routes/$types";
|
|
|
|
|
15 |
import LoginModal from "../LoginModal.svelte";
|
16 |
|
17 |
export let messages: Message[] = [];
|
@@ -21,6 +23,7 @@
|
|
21 |
export let currentModel: Model;
|
22 |
export let models: Model[];
|
23 |
export let settings: LayoutData["settings"];
|
|
|
24 |
|
25 |
export let loginRequired = false;
|
26 |
$: isReadOnly = !models.some((model) => model.id === currentModel.id);
|
@@ -55,6 +58,7 @@
|
|
55 |
{messages}
|
56 |
readOnly={isReadOnly}
|
57 |
isAuthor={!shared}
|
|
|
58 |
on:message
|
59 |
on:vote
|
60 |
on:retry={(ev) => {
|
@@ -64,8 +68,16 @@
|
|
64 |
<div
|
65 |
class="dark:via-gray-80 pointer-events-none absolute inset-x-0 bottom-0 z-0 mx-auto flex w-full max-w-3xl flex-col items-center justify-center bg-gradient-to-t from-white via-white/80 to-white/0 px-3.5 py-4 dark:border-gray-800 dark:from-gray-900 dark:to-gray-900/0 max-md:border-t max-md:bg-white max-md:dark:bg-gray-900 sm:px-5 md:py-8 xl:max-w-4xl [&>*]:pointer-events-auto"
|
66 |
>
|
67 |
-
<div class="
|
68 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
69 |
</div>
|
70 |
<form
|
71 |
on:submit|preventDefault={handleSubmit}
|
@@ -89,7 +101,7 @@
|
|
89 |
class="btn mx-1 my-1 inline-block h-[2.4rem] self-end rounded-lg bg-transparent p-1 px-[0.7rem] text-gray-400 disabled:opacity-60 enabled:hover:text-gray-700 dark:disabled:opacity-40 enabled:dark:hover:text-gray-100 md:hidden"
|
90 |
on:click={() => dispatch("stop")}
|
91 |
>
|
92 |
-
<
|
93 |
</button>
|
94 |
<div
|
95 |
class="mx-1 my-1 hidden h-[2.4rem] items-center p-1 px-[0.7rem] text-gray-400 disabled:opacity-60 enabled:hover:text-gray-700 dark:disabled:opacity-40 enabled:dark:hover:text-gray-100 md:flex"
|
|
|
4 |
|
5 |
import CarbonSendAltFilled from "~icons/carbon/send-alt-filled";
|
6 |
import CarbonExport from "~icons/carbon/export";
|
7 |
+
import CarbonStopFilledAlt from "~icons/carbon/stop-filled-alt";
|
8 |
import EosIconsLoading from "~icons/eos-icons/loading";
|
9 |
|
10 |
import ChatMessages from "./ChatMessages.svelte";
|
|
|
12 |
import StopGeneratingBtn from "../StopGeneratingBtn.svelte";
|
13 |
import type { Model } from "$lib/types/Model";
|
14 |
import type { LayoutData } from "../../../routes/$types";
|
15 |
+
import WebSearchToggle from "../WebSearchToggle.svelte";
|
16 |
+
import type { WebSearchMessage } from "$lib/types/WebSearch";
|
17 |
import LoginModal from "../LoginModal.svelte";
|
18 |
|
19 |
export let messages: Message[] = [];
|
|
|
23 |
export let currentModel: Model;
|
24 |
export let models: Model[];
|
25 |
export let settings: LayoutData["settings"];
|
26 |
+
export let webSearchMessages: WebSearchMessage[] = [];
|
27 |
|
28 |
export let loginRequired = false;
|
29 |
$: isReadOnly = !models.some((model) => model.id === currentModel.id);
|
|
|
58 |
{messages}
|
59 |
readOnly={isReadOnly}
|
60 |
isAuthor={!shared}
|
61 |
+
{webSearchMessages}
|
62 |
on:message
|
63 |
on:vote
|
64 |
on:retry={(ev) => {
|
|
|
68 |
<div
|
69 |
class="dark:via-gray-80 pointer-events-none absolute inset-x-0 bottom-0 z-0 mx-auto flex w-full max-w-3xl flex-col items-center justify-center bg-gradient-to-t from-white via-white/80 to-white/0 px-3.5 py-4 dark:border-gray-800 dark:from-gray-900 dark:to-gray-900/0 max-md:border-t max-md:bg-white max-md:dark:bg-gray-900 sm:px-5 md:py-8 xl:max-w-4xl [&>*]:pointer-events-auto"
|
70 |
>
|
71 |
+
<div class="flex w-full pb-3 max-md:justify-between">
|
72 |
+
{#if settings?.searchEnabled}
|
73 |
+
<WebSearchToggle />
|
74 |
+
{/if}
|
75 |
+
{#if loading}
|
76 |
+
<StopGeneratingBtn
|
77 |
+
classNames={settings?.searchEnabled ? "md:-translate-x-1/2 md:mx-auto" : "mx-auto"}
|
78 |
+
on:click={() => dispatch("stop")}
|
79 |
+
/>
|
80 |
+
{/if}
|
81 |
</div>
|
82 |
<form
|
83 |
on:submit|preventDefault={handleSubmit}
|
|
|
101 |
class="btn mx-1 my-1 inline-block h-[2.4rem] self-end rounded-lg bg-transparent p-1 px-[0.7rem] text-gray-400 disabled:opacity-60 enabled:hover:text-gray-700 dark:disabled:opacity-40 enabled:dark:hover:text-gray-100 md:hidden"
|
102 |
on:click={() => dispatch("stop")}
|
103 |
>
|
104 |
+
<CarbonStopFilledAlt />
|
105 |
</button>
|
106 |
<div
|
107 |
class="mx-1 my-1 hidden h-[2.4rem] items-center p-1 px-[0.7rem] text-gray-400 disabled:opacity-60 enabled:hover:text-gray-700 dark:disabled:opacity-40 enabled:dark:hover:text-gray-100 md:flex"
|
@@ -2,30 +2,17 @@
|
|
2 |
export let classNames = "";
|
3 |
</script>
|
4 |
|
5 |
-
<
|
6 |
-
|
7 |
-
|
8 |
-
|
9 |
-
|
10 |
-
|
11 |
-
|
12 |
-
|
13 |
-
|
14 |
-
|
15 |
-
|
16 |
-
|
17 |
-
|
18 |
-
|
19 |
-
type="scale"
|
20 |
-
begin={`${-0.375 + 0.15 * index}s`}
|
21 |
-
calcMode="spline"
|
22 |
-
keySplines="0.3 0 0.7 1;0.3 0 0.7 1"
|
23 |
-
values="0.5;1;0.5"
|
24 |
-
keyTimes="0;0.5;1"
|
25 |
-
dur="1s"
|
26 |
-
repeatCount="indefinite"
|
27 |
-
/>
|
28 |
-
</circle>
|
29 |
-
</g>
|
30 |
-
{/each}
|
31 |
-
</svg>
|
|
|
2 |
export let classNames = "";
|
3 |
</script>
|
4 |
|
5 |
+
<div class={"inline-flex h-8 flex-none items-center gap-1 " + classNames}>
|
6 |
+
<div
|
7 |
+
class="h-1 w-1 animate-bounce rounded-full bg-gray-500 dark:bg-gray-400"
|
8 |
+
style="animation-delay: 0.25s;"
|
9 |
+
/>
|
10 |
+
<div
|
11 |
+
class="h-1 w-1 animate-bounce rounded-full bg-gray-500 dark:bg-gray-400"
|
12 |
+
style="animation-delay: 0.5s;"
|
13 |
+
/>
|
14 |
+
<div
|
15 |
+
class="h-1 w-1 animate-bounce rounded-full bg-gray-500 dark:bg-gray-400"
|
16 |
+
style="animation-delay: 0.75s;"
|
17 |
+
/>
|
18 |
+
</div>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@@ -2,6 +2,7 @@ import { MONGODB_URL, MONGODB_DB_NAME, MONGODB_DIRECT_CONNECTION } from "$env/st
|
|
2 |
import { MongoClient } from "mongodb";
|
3 |
import type { Conversation } from "$lib/types/Conversation";
|
4 |
import type { SharedConversation } from "$lib/types/SharedConversation";
|
|
|
5 |
import type { AbortedGeneration } from "$lib/types/AbortedGeneration";
|
6 |
import type { Settings } from "$lib/types/Settings";
|
7 |
import type { User } from "$lib/types/User";
|
@@ -25,6 +26,7 @@ const sharedConversations = db.collection<SharedConversation>("sharedConversatio
|
|
25 |
const abortedGenerations = db.collection<AbortedGeneration>("abortedGenerations");
|
26 |
const settings = db.collection<Settings>("settings");
|
27 |
const users = db.collection<User>("users");
|
|
|
28 |
|
29 |
export { client, db };
|
30 |
export const collections = {
|
@@ -33,6 +35,7 @@ export const collections = {
|
|
33 |
abortedGenerations,
|
34 |
settings,
|
35 |
users,
|
|
|
36 |
};
|
37 |
|
38 |
client.on("open", () => {
|
@@ -48,6 +51,7 @@ client.on("open", () => {
|
|
48 |
{ partialFilterExpression: { userId: { $exists: true } } }
|
49 |
)
|
50 |
.catch(console.error);
|
|
|
51 |
abortedGenerations.createIndex({ updatedAt: 1 }, { expireAfterSeconds: 30 }).catch(console.error);
|
52 |
abortedGenerations.createIndex({ conversationId: 1 }, { unique: true }).catch(console.error);
|
53 |
sharedConversations.createIndex({ hash: 1 }, { unique: true }).catch(console.error);
|
|
|
2 |
import { MongoClient } from "mongodb";
|
3 |
import type { Conversation } from "$lib/types/Conversation";
|
4 |
import type { SharedConversation } from "$lib/types/SharedConversation";
|
5 |
+
import type { WebSearch } from "$lib/types/WebSearch";
|
6 |
import type { AbortedGeneration } from "$lib/types/AbortedGeneration";
|
7 |
import type { Settings } from "$lib/types/Settings";
|
8 |
import type { User } from "$lib/types/User";
|
|
|
26 |
const abortedGenerations = db.collection<AbortedGeneration>("abortedGenerations");
|
27 |
const settings = db.collection<Settings>("settings");
|
28 |
const users = db.collection<User>("users");
|
29 |
+
const webSearches = db.collection<WebSearch>("webSearches");
|
30 |
|
31 |
export { client, db };
|
32 |
export const collections = {
|
|
|
35 |
abortedGenerations,
|
36 |
settings,
|
37 |
users,
|
38 |
+
webSearches,
|
39 |
};
|
40 |
|
41 |
client.on("open", () => {
|
|
|
51 |
{ partialFilterExpression: { userId: { $exists: true } } }
|
52 |
)
|
53 |
.catch(console.error);
|
54 |
+
webSearches.createIndex({ sessionId: 1, updatedAt: -1 }).catch(console.error);
|
55 |
abortedGenerations.createIndex({ updatedAt: 1 }, { expireAfterSeconds: 30 }).catch(console.error);
|
56 |
abortedGenerations.createIndex({ conversationId: 1 }, { unique: true }).catch(console.error);
|
57 |
sharedConversations.createIndex({ hash: 1 }, { unique: true }).catch(console.error);
|
@@ -0,0 +1,43 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import { defaultModel } from "$lib/server/models";
|
2 |
+
import { modelEndpoint } from "./modelEndpoint";
|
3 |
+
import { textGeneration } from "@huggingface/inference";
|
4 |
+
import { trimSuffix } from "$lib/utils/trimSuffix";
|
5 |
+
import { trimPrefix } from "$lib/utils/trimPrefix";
|
6 |
+
import { PUBLIC_SEP_TOKEN } from "$lib/constants/publicSepToken";
|
7 |
+
|
8 |
+
interface Parameters {
|
9 |
+
temperature: number;
|
10 |
+
truncate: number;
|
11 |
+
max_new_tokens: number;
|
12 |
+
stop: string[];
|
13 |
+
}
|
14 |
+
export async function generateFromDefaultEndpoint(
|
15 |
+
prompt: string,
|
16 |
+
parameters?: Partial<Parameters>
|
17 |
+
) {
|
18 |
+
const newParameters = {
|
19 |
+
...defaultModel.parameters,
|
20 |
+
...parameters,
|
21 |
+
return_full_text: false,
|
22 |
+
};
|
23 |
+
|
24 |
+
const endpoint = modelEndpoint(defaultModel);
|
25 |
+
let { generated_text } = await textGeneration(
|
26 |
+
{
|
27 |
+
model: endpoint.url,
|
28 |
+
inputs: prompt,
|
29 |
+
parameters: newParameters,
|
30 |
+
},
|
31 |
+
{
|
32 |
+
fetch: (url, options) =>
|
33 |
+
fetch(url, {
|
34 |
+
...options,
|
35 |
+
headers: { ...options?.headers, Authorization: endpoint.authorization },
|
36 |
+
}),
|
37 |
+
}
|
38 |
+
);
|
39 |
+
|
40 |
+
generated_text = trimSuffix(trimPrefix(generated_text, "<|startoftext|>"), PUBLIC_SEP_TOKEN);
|
41 |
+
|
42 |
+
return generated_text;
|
43 |
+
}
|
@@ -0,0 +1,20 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import { SERPAPI_KEY } from "$env/static/private";
|
2 |
+
|
3 |
+
import { getJson } from "serpapi";
|
4 |
+
import type { GoogleParameters } from "serpapi";
|
5 |
+
|
6 |
+
// Show result as JSON
|
7 |
+
export async function searchWeb(query: string) {
|
8 |
+
const params = {
|
9 |
+
q: query,
|
10 |
+
hl: "en",
|
11 |
+
gl: "us",
|
12 |
+
google_domain: "google.com",
|
13 |
+
api_key: SERPAPI_KEY,
|
14 |
+
} satisfies GoogleParameters;
|
15 |
+
|
16 |
+
// Show result as JSON
|
17 |
+
const response = await getJson("google", params);
|
18 |
+
|
19 |
+
return response;
|
20 |
+
}
|
@@ -0,0 +1,9 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import { writable } from "svelte/store";
|
2 |
+
export interface WebSearchParameters {
|
3 |
+
useSearch: boolean;
|
4 |
+
nItems: number;
|
5 |
+
}
|
6 |
+
export const webSearchParameters = writable<WebSearchParameters>({
|
7 |
+
useSearch: false,
|
8 |
+
nItems: 5,
|
9 |
+
});
|
@@ -2,5 +2,6 @@ export interface Message {
|
|
2 |
from: "user" | "assistant";
|
3 |
id: ReturnType<typeof crypto.randomUUID>;
|
4 |
content: string;
|
|
|
5 |
score?: -1 | 0 | 1;
|
6 |
}
|
|
|
2 |
from: "user" | "assistant";
|
3 |
id: ReturnType<typeof crypto.randomUUID>;
|
4 |
content: string;
|
5 |
+
webSearchId?: string;
|
6 |
score?: -1 | 0 | 1;
|
7 |
}
|
@@ -0,0 +1,40 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import type { ObjectId } from "mongodb";
|
2 |
+
import type { Conversation } from "./Conversation";
|
3 |
+
import type { Timestamps } from "./Timestamps";
|
4 |
+
|
5 |
+
export interface WebSearch extends Timestamps {
|
6 |
+
_id: ObjectId;
|
7 |
+
|
8 |
+
convId: Conversation["_id"];
|
9 |
+
|
10 |
+
prompt: string;
|
11 |
+
|
12 |
+
searchQuery: string;
|
13 |
+
results: string[];
|
14 |
+
knowledgeGraph: string;
|
15 |
+
summary: string;
|
16 |
+
|
17 |
+
messages: WebSearchMessage[];
|
18 |
+
}
|
19 |
+
|
20 |
+
export type WebSearchMessageUpdate = {
|
21 |
+
type: "update";
|
22 |
+
message: string;
|
23 |
+
args?: string[];
|
24 |
+
};
|
25 |
+
|
26 |
+
export type WebSearchMessageError = {
|
27 |
+
type: "error";
|
28 |
+
message: string;
|
29 |
+
args?: string[];
|
30 |
+
};
|
31 |
+
|
32 |
+
export type WebSearchMessageResult = {
|
33 |
+
type: "result";
|
34 |
+
id: string;
|
35 |
+
};
|
36 |
+
|
37 |
+
export type WebSearchMessage =
|
38 |
+
| WebSearchMessageUpdate
|
39 |
+
| WebSearchMessageResult
|
40 |
+
| WebSearchMessageError;
|
@@ -6,6 +6,7 @@ import { UrlDependency } from "$lib/types/UrlDependency";
|
|
6 |
import { defaultModel, models, oldModels, validateModel } from "$lib/server/models";
|
7 |
import { authCondition, requiresUser } from "$lib/server/auth";
|
8 |
import { DEFAULT_SETTINGS } from "$lib/types/Settings";
|
|
|
9 |
|
10 |
export const load: LayoutServerLoad = async ({ locals, depends, url }) => {
|
11 |
const { conversations } = collections;
|
@@ -60,6 +61,7 @@ export const load: LayoutServerLoad = async ({ locals, depends, url }) => {
|
|
60 |
DEFAULT_SETTINGS.shareConversationsWithModelAuthors,
|
61 |
ethicsModalAcceptedAt: settings?.ethicsModalAcceptedAt ?? null,
|
62 |
activeModel: settings?.activeModel ?? DEFAULT_SETTINGS.activeModel,
|
|
|
63 |
},
|
64 |
models: models.map((model) => ({
|
65 |
id: model.id,
|
|
|
6 |
import { defaultModel, models, oldModels, validateModel } from "$lib/server/models";
|
7 |
import { authCondition, requiresUser } from "$lib/server/auth";
|
8 |
import { DEFAULT_SETTINGS } from "$lib/types/Settings";
|
9 |
+
import { SERPAPI_KEY } from "$env/static/private";
|
10 |
|
11 |
export const load: LayoutServerLoad = async ({ locals, depends, url }) => {
|
12 |
const { conversations } = collections;
|
|
|
61 |
DEFAULT_SETTINGS.shareConversationsWithModelAuthors,
|
62 |
ethicsModalAcceptedAt: settings?.ethicsModalAcceptedAt ?? null,
|
63 |
activeModel: settings?.activeModel ?? DEFAULT_SETTINGS.activeModel,
|
64 |
+
searchEnabled: !!SERPAPI_KEY,
|
65 |
},
|
66 |
models: models.map((model) => ({
|
67 |
id: model.id,
|
@@ -12,6 +12,8 @@
|
|
12 |
import { ERROR_MESSAGES, error } from "$lib/stores/errors";
|
13 |
import { randomUUID } from "$lib/utils/randomUuid";
|
14 |
import { findCurrentModel } from "$lib/utils/models";
|
|
|
|
|
15 |
import type { Message } from "$lib/types/Message";
|
16 |
|
17 |
export let data;
|
@@ -20,6 +22,8 @@
|
|
20 |
let lastLoadedMessages = data.messages;
|
21 |
let isAborted = false;
|
22 |
|
|
|
|
|
23 |
// Since we modify the messages array locally, we don't want to reset it if an old version is passed
|
24 |
$: if (data.messages !== lastLoadedMessages) {
|
25 |
messages = data.messages;
|
@@ -29,8 +33,13 @@
|
|
29 |
let loading = false;
|
30 |
let pending = false;
|
31 |
|
32 |
-
async function getTextGenerationStream(
|
33 |
-
|
|
|
|
|
|
|
|
|
|
|
34 |
const responseId = randomUUID();
|
35 |
|
36 |
const response = textGenerationStream(
|
@@ -47,6 +56,7 @@
|
|
47 |
response_id: responseId,
|
48 |
is_retry: isRetry,
|
49 |
use_cache: false,
|
|
|
50 |
} as Options
|
51 |
);
|
52 |
|
@@ -78,6 +88,7 @@
|
|
78 |
|
79 |
if (lastMessage) {
|
80 |
lastMessage.content = output.generated_text;
|
|
|
81 |
messages = [...messages];
|
82 |
}
|
83 |
break;
|
@@ -126,7 +137,63 @@
|
|
126 |
{ from: "user", content: message, id: messageId },
|
127 |
];
|
128 |
|
129 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
130 |
|
131 |
if (messages.filter((m) => m.from === "user").length === 1) {
|
132 |
summarizeTitle($page.params.id)
|
@@ -186,7 +253,7 @@
|
|
186 |
writeMessage(val, messageId);
|
187 |
}
|
188 |
});
|
189 |
-
|
190 |
$: title = data.conversations.find((conv) => conv.id === $page.params.id)?.title ?? data.title;
|
191 |
</script>
|
192 |
|
@@ -198,6 +265,7 @@
|
|
198 |
{loading}
|
199 |
{pending}
|
200 |
{messages}
|
|
|
201 |
on:message={(event) => writeMessage(event.detail)}
|
202 |
on:retry={(event) => writeMessage(event.detail.content, event.detail.id)}
|
203 |
on:vote={(event) => voteMessage(event.detail.score, event.detail.id)}
|
|
|
12 |
import { ERROR_MESSAGES, error } from "$lib/stores/errors";
|
13 |
import { randomUUID } from "$lib/utils/randomUuid";
|
14 |
import { findCurrentModel } from "$lib/utils/models";
|
15 |
+
import { webSearchParameters } from "$lib/stores/webSearchParameters";
|
16 |
+
import type { WebSearchMessage } from "$lib/types/WebSearch.js";
|
17 |
import type { Message } from "$lib/types/Message";
|
18 |
|
19 |
export let data;
|
|
|
22 |
let lastLoadedMessages = data.messages;
|
23 |
let isAborted = false;
|
24 |
|
25 |
+
let webSearchMessages: WebSearchMessage[] = [];
|
26 |
+
|
27 |
// Since we modify the messages array locally, we don't want to reset it if an old version is passed
|
28 |
$: if (data.messages !== lastLoadedMessages) {
|
29 |
messages = data.messages;
|
|
|
33 |
let loading = false;
|
34 |
let pending = false;
|
35 |
|
36 |
+
async function getTextGenerationStream(
|
37 |
+
inputs: string,
|
38 |
+
messageId: string,
|
39 |
+
isRetry = false,
|
40 |
+
webSearchId?: string
|
41 |
+
) {
|
42 |
+
let conversationId = $page.params.id;
|
43 |
const responseId = randomUUID();
|
44 |
|
45 |
const response = textGenerationStream(
|
|
|
56 |
response_id: responseId,
|
57 |
is_retry: isRetry,
|
58 |
use_cache: false,
|
59 |
+
web_search_id: webSearchId,
|
60 |
} as Options
|
61 |
);
|
62 |
|
|
|
88 |
|
89 |
if (lastMessage) {
|
90 |
lastMessage.content = output.generated_text;
|
91 |
+
lastMessage.webSearchId = webSearchId;
|
92 |
messages = [...messages];
|
93 |
}
|
94 |
break;
|
|
|
137 |
{ from: "user", content: message, id: messageId },
|
138 |
];
|
139 |
|
140 |
+
let searchResponseId: string | null = "";
|
141 |
+
if ($webSearchParameters.useSearch) {
|
142 |
+
webSearchMessages = [];
|
143 |
+
|
144 |
+
const res = await fetch(
|
145 |
+
`${base}/conversation/${$page.params.id}/web-search?` +
|
146 |
+
new URLSearchParams({ prompt: message }),
|
147 |
+
{
|
148 |
+
method: "GET",
|
149 |
+
}
|
150 |
+
);
|
151 |
+
|
152 |
+
// required bc linting doesn't see TextDecoderStream for some reason?
|
153 |
+
// eslint-disable-next-line no-undef
|
154 |
+
const encoder = new TextDecoderStream();
|
155 |
+
const reader = res?.body?.pipeThrough(encoder).getReader();
|
156 |
+
|
157 |
+
while (searchResponseId === "") {
|
158 |
+
await new Promise((r) => setTimeout(r, 25));
|
159 |
+
|
160 |
+
if (isAborted) {
|
161 |
+
reader?.cancel();
|
162 |
+
return;
|
163 |
+
}
|
164 |
+
|
165 |
+
reader
|
166 |
+
?.read()
|
167 |
+
.then(async ({ done, value }) => {
|
168 |
+
if (done) {
|
169 |
+
reader.cancel();
|
170 |
+
return;
|
171 |
+
}
|
172 |
+
|
173 |
+
try {
|
174 |
+
webSearchMessages = (JSON.parse(value) as { messages: WebSearchMessage[] })
|
175 |
+
.messages;
|
176 |
+
} catch (parseError) {
|
177 |
+
// in case of parsing error we wait for the next message
|
178 |
+
return;
|
179 |
+
}
|
180 |
+
|
181 |
+
const lastSearchMessage = webSearchMessages[webSearchMessages.length - 1];
|
182 |
+
if (lastSearchMessage.type === "result") {
|
183 |
+
searchResponseId = lastSearchMessage.id;
|
184 |
+
reader.cancel();
|
185 |
+
return;
|
186 |
+
}
|
187 |
+
})
|
188 |
+
.catch(() => {
|
189 |
+
searchResponseId = null;
|
190 |
+
});
|
191 |
+
}
|
192 |
+
}
|
193 |
+
|
194 |
+
await getTextGenerationStream(message, messageId, isRetry, searchResponseId ?? undefined);
|
195 |
+
|
196 |
+
webSearchMessages = [];
|
197 |
|
198 |
if (messages.filter((m) => m.from === "user").length === 1) {
|
199 |
summarizeTitle($page.params.id)
|
|
|
253 |
writeMessage(val, messageId);
|
254 |
}
|
255 |
});
|
256 |
+
$: $page.params.id, (isAborted = true);
|
257 |
$: title = data.conversations.find((conv) => conv.id === $page.params.id)?.title ?? data.title;
|
258 |
</script>
|
259 |
|
|
|
265 |
{loading}
|
266 |
{pending}
|
267 |
{messages}
|
268 |
+
bind:webSearchMessages
|
269 |
on:message={(event) => writeMessage(event.detail)}
|
270 |
on:retry={(event) => writeMessage(event.detail.content, event.detail.id)}
|
271 |
on:vote={(event) => voteMessage(event.detail.score, event.detail.id)}
|
@@ -38,7 +38,7 @@ export async function POST({ request, fetch, locals, params }) {
|
|
38 |
const json = await request.json();
|
39 |
const {
|
40 |
inputs: newPrompt,
|
41 |
-
options: { id: messageId, is_retry, response_id: responseId },
|
42 |
} = z
|
43 |
.object({
|
44 |
inputs: z.string().trim().min(1),
|
@@ -46,6 +46,7 @@ export async function POST({ request, fetch, locals, params }) {
|
|
46 |
id: z.optional(z.string().uuid()),
|
47 |
response_id: z.optional(z.string().uuid()),
|
48 |
is_retry: z.optional(z.boolean()),
|
|
|
49 |
}),
|
50 |
})
|
51 |
.parse(json);
|
@@ -67,8 +68,7 @@ export async function POST({ request, fetch, locals, params }) {
|
|
67 |
];
|
68 |
})() satisfies Message[];
|
69 |
|
70 |
-
const prompt = buildPrompt(messages, model);
|
71 |
-
|
72 |
const randomEndpoint = modelEndpoint(model);
|
73 |
|
74 |
const abortController = new AbortController();
|
@@ -114,6 +114,7 @@ export async function POST({ request, fetch, locals, params }) {
|
|
114 |
messages.push({
|
115 |
from: "assistant",
|
116 |
content: generated_text,
|
|
|
117 |
id: (responseId as Message["id"]) || crypto.randomUUID(),
|
118 |
});
|
119 |
|
@@ -131,7 +132,6 @@ export async function POST({ request, fetch, locals, params }) {
|
|
131 |
}
|
132 |
|
133 |
saveMessage().catch(console.error);
|
134 |
-
|
135 |
// Todo: maybe we should wait for the message to be saved before ending the response - in case of errors
|
136 |
return new Response(stream1, {
|
137 |
headers: Object.fromEntries(resp.headers.entries()),
|
|
|
38 |
const json = await request.json();
|
39 |
const {
|
40 |
inputs: newPrompt,
|
41 |
+
options: { id: messageId, is_retry, web_search_id, response_id: responseId },
|
42 |
} = z
|
43 |
.object({
|
44 |
inputs: z.string().trim().min(1),
|
|
|
46 |
id: z.optional(z.string().uuid()),
|
47 |
response_id: z.optional(z.string().uuid()),
|
48 |
is_retry: z.optional(z.boolean()),
|
49 |
+
web_search_id: z.ostring(),
|
50 |
}),
|
51 |
})
|
52 |
.parse(json);
|
|
|
68 |
];
|
69 |
})() satisfies Message[];
|
70 |
|
71 |
+
const prompt = await buildPrompt(messages, model, web_search_id);
|
|
|
72 |
const randomEndpoint = modelEndpoint(model);
|
73 |
|
74 |
const abortController = new AbortController();
|
|
|
114 |
messages.push({
|
115 |
from: "assistant",
|
116 |
content: generated_text,
|
117 |
+
webSearchId: web_search_id,
|
118 |
id: (responseId as Message["id"]) || crypto.randomUUID(),
|
119 |
});
|
120 |
|
|
|
132 |
}
|
133 |
|
134 |
saveMessage().catch(console.error);
|
|
|
135 |
// Todo: maybe we should wait for the message to be saved before ending the response - in case of errors
|
136 |
return new Response(stream1, {
|
137 |
headers: Object.fromEntries(resp.headers.entries()),
|
@@ -31,7 +31,7 @@ export async function GET({ params, locals }) {
|
|
31 |
throw error(404, "Conversation model not found");
|
32 |
}
|
33 |
|
34 |
-
const prompt = buildPrompt(conv.messages.slice(0, messageIndex + 1), model);
|
35 |
|
36 |
return new Response(
|
37 |
JSON.stringify(
|
|
|
31 |
throw error(404, "Conversation model not found");
|
32 |
}
|
33 |
|
34 |
+
const prompt = await buildPrompt(conv.messages.slice(0, messageIndex + 1), model);
|
35 |
|
36 |
return new Response(
|
37 |
JSON.stringify(
|
@@ -1,16 +1,12 @@
|
|
1 |
import { buildPrompt } from "$lib/buildPrompt";
|
2 |
-
import { PUBLIC_SEP_TOKEN } from "$lib/constants/publicSepToken";
|
3 |
import { authCondition } from "$lib/server/auth";
|
4 |
import { collections } from "$lib/server/database";
|
5 |
-
import {
|
6 |
import { defaultModel } from "$lib/server/models";
|
7 |
-
import { trimPrefix } from "$lib/utils/trimPrefix";
|
8 |
-
import { trimSuffix } from "$lib/utils/trimSuffix";
|
9 |
-
import { textGeneration } from "@huggingface/inference";
|
10 |
import { error } from "@sveltejs/kit";
|
11 |
import { ObjectId } from "mongodb";
|
12 |
|
13 |
-
export async function POST({ params, locals
|
14 |
const convId = new ObjectId(params.id);
|
15 |
|
16 |
const conversation = await collections.conversations.findOne({
|
@@ -28,30 +24,8 @@ export async function POST({ params, locals, fetch }) {
|
|
28 |
`Please summarize the following message as a single sentence of less than 5 words:\n` +
|
29 |
firstMessage?.content;
|
30 |
|
31 |
-
const prompt = buildPrompt([{ from: "user", content: userPrompt }], defaultModel);
|
32 |
-
|
33 |
-
const parameters = {
|
34 |
-
...defaultModel.parameters,
|
35 |
-
return_full_text: false,
|
36 |
-
};
|
37 |
-
|
38 |
-
const endpoint = modelEndpoint(defaultModel);
|
39 |
-
let { generated_text } = await textGeneration(
|
40 |
-
{
|
41 |
-
model: endpoint.url,
|
42 |
-
inputs: prompt,
|
43 |
-
parameters,
|
44 |
-
},
|
45 |
-
{
|
46 |
-
fetch: (url, options) =>
|
47 |
-
fetch(url, {
|
48 |
-
...options,
|
49 |
-
headers: { ...options?.headers, Authorization: endpoint.authorization },
|
50 |
-
}),
|
51 |
-
}
|
52 |
-
);
|
53 |
-
|
54 |
-
generated_text = trimSuffix(trimPrefix(generated_text, "<|startoftext|>"), PUBLIC_SEP_TOKEN);
|
55 |
|
56 |
if (generated_text) {
|
57 |
await collections.conversations.updateOne(
|
|
|
1 |
import { buildPrompt } from "$lib/buildPrompt";
|
|
|
2 |
import { authCondition } from "$lib/server/auth";
|
3 |
import { collections } from "$lib/server/database";
|
4 |
+
import { generateFromDefaultEndpoint } from "$lib/server/generateFromDefaultEndpoint.js";
|
5 |
import { defaultModel } from "$lib/server/models";
|
|
|
|
|
|
|
6 |
import { error } from "@sveltejs/kit";
|
7 |
import { ObjectId } from "mongodb";
|
8 |
|
9 |
+
export async function POST({ params, locals }) {
|
10 |
const convId = new ObjectId(params.id);
|
11 |
|
12 |
const conversation = await collections.conversations.findOne({
|
|
|
24 |
`Please summarize the following message as a single sentence of less than 5 words:\n` +
|
25 |
firstMessage?.content;
|
26 |
|
27 |
+
const prompt = await buildPrompt([{ from: "user", content: userPrompt }], defaultModel);
|
28 |
+
const generated_text = await generateFromDefaultEndpoint(prompt);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
29 |
|
30 |
if (generated_text) {
|
31 |
await collections.conversations.updateOne(
|
@@ -0,0 +1,232 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import { authCondition } from "$lib/server/auth";
|
2 |
+
import { collections } from "$lib/server/database";
|
3 |
+
import { generateFromDefaultEndpoint } from "$lib/server/generateFromDefaultEndpoint.js";
|
4 |
+
import { defaultModel } from "$lib/server/models";
|
5 |
+
import { searchWeb } from "$lib/server/searchWeb.js";
|
6 |
+
import type { Message } from "$lib/types/Message.js";
|
7 |
+
import { error } from "@sveltejs/kit";
|
8 |
+
import { ObjectId } from "mongodb";
|
9 |
+
import { z } from "zod";
|
10 |
+
import { JSDOM, VirtualConsole } from "jsdom";
|
11 |
+
import type { WebSearch } from "$lib/types/WebSearch.js";
|
12 |
+
|
13 |
+
function removeTags(node: Node) {
|
14 |
+
if (node.hasChildNodes()) {
|
15 |
+
node.childNodes.forEach((childNode) => {
|
16 |
+
if (node.nodeName === "SCRIPT" || node.nodeName === "STYLE") {
|
17 |
+
node.removeChild(childNode);
|
18 |
+
} else {
|
19 |
+
removeTags(childNode);
|
20 |
+
}
|
21 |
+
});
|
22 |
+
}
|
23 |
+
}
|
24 |
+
function naiveInnerText(node: Node): string {
|
25 |
+
const Node = node; // We need Node(DOM's Node) for the constants, but Node doesn't exist in the nodejs global space, and any Node instance references the constants through the prototype chain
|
26 |
+
return [...node.childNodes]
|
27 |
+
.map((childNode) => {
|
28 |
+
switch (childNode.nodeType) {
|
29 |
+
case Node.TEXT_NODE:
|
30 |
+
return node.textContent;
|
31 |
+
case Node.ELEMENT_NODE:
|
32 |
+
return naiveInnerText(childNode);
|
33 |
+
default:
|
34 |
+
return "";
|
35 |
+
}
|
36 |
+
})
|
37 |
+
.join("\n");
|
38 |
+
}
|
39 |
+
|
40 |
+
interface GenericObject {
|
41 |
+
[key: string]: GenericObject | unknown;
|
42 |
+
}
|
43 |
+
|
44 |
+
function removeLinks(obj: GenericObject) {
|
45 |
+
for (const prop in obj) {
|
46 |
+
if (prop.endsWith("link")) delete obj[prop];
|
47 |
+
else if (typeof obj[prop] === "object") removeLinks(obj[prop] as GenericObject);
|
48 |
+
}
|
49 |
+
return obj;
|
50 |
+
}
|
51 |
+
export async function GET({ params, locals, url }) {
|
52 |
+
const model = defaultModel;
|
53 |
+
const convId = new ObjectId(params.id);
|
54 |
+
const searchId = new ObjectId();
|
55 |
+
|
56 |
+
const conv = await collections.conversations.findOne({
|
57 |
+
_id: convId,
|
58 |
+
...authCondition(locals),
|
59 |
+
});
|
60 |
+
|
61 |
+
if (!conv) {
|
62 |
+
throw error(404, "Conversation not found");
|
63 |
+
}
|
64 |
+
|
65 |
+
const prompt = z.string().trim().min(1).parse(url.searchParams.get("prompt"));
|
66 |
+
|
67 |
+
const messages = (() => {
|
68 |
+
return [...conv.messages, { content: prompt, from: "user", id: crypto.randomUUID() }];
|
69 |
+
})() satisfies Message[];
|
70 |
+
|
71 |
+
const stream = new ReadableStream({
|
72 |
+
async start(controller) {
|
73 |
+
const webSearch: WebSearch = {
|
74 |
+
_id: searchId,
|
75 |
+
convId: convId,
|
76 |
+
prompt: prompt,
|
77 |
+
searchQuery: "",
|
78 |
+
knowledgeGraph: "",
|
79 |
+
results: [],
|
80 |
+
summary: "",
|
81 |
+
messages: [],
|
82 |
+
createdAt: new Date(),
|
83 |
+
updatedAt: new Date(),
|
84 |
+
};
|
85 |
+
try {
|
86 |
+
webSearch.messages.push({
|
87 |
+
type: "update",
|
88 |
+
message: "Generating search query",
|
89 |
+
});
|
90 |
+
controller.enqueue(JSON.stringify({ messages: webSearch.messages }));
|
91 |
+
|
92 |
+
const promptSearchQuery =
|
93 |
+
model.userMessageToken +
|
94 |
+
"The following messages were written by a user, trying to answer a question." +
|
95 |
+
model.messageEndToken +
|
96 |
+
messages
|
97 |
+
.filter((message) => message.from === "user")
|
98 |
+
.map((message) => model.userMessageToken + message.content + model.messageEndToken) +
|
99 |
+
model.userMessageToken +
|
100 |
+
"What plain-text english sentence would you input into Google to answer the last question? Answer with a short (10 words max) simple sentence." +
|
101 |
+
model.messageEndToken +
|
102 |
+
model.assistantMessageToken +
|
103 |
+
"Query: ";
|
104 |
+
|
105 |
+
webSearch.searchQuery = await generateFromDefaultEndpoint(promptSearchQuery).then(
|
106 |
+
(query) => {
|
107 |
+
const arr = query.split(/\r?\n/);
|
108 |
+
return arr[0].length > 0 ? arr[0] : arr[1];
|
109 |
+
}
|
110 |
+
);
|
111 |
+
// the model has a tendency to continue answering even when we tell it not to, so the split makes
|
112 |
+
// sure we only get the first line of the response
|
113 |
+
|
114 |
+
webSearch.messages.push({
|
115 |
+
type: "update",
|
116 |
+
message: "Searching Google",
|
117 |
+
args: [webSearch.searchQuery],
|
118 |
+
});
|
119 |
+
controller.enqueue(JSON.stringify({ messages: webSearch.messages }));
|
120 |
+
|
121 |
+
const results = await searchWeb(webSearch.searchQuery);
|
122 |
+
let text = "";
|
123 |
+
|
124 |
+
webSearch.results =
|
125 |
+
(results.organic_results &&
|
126 |
+
results.organic_results.map((el: { link: string }) => el.link)) ??
|
127 |
+
[];
|
128 |
+
|
129 |
+
if (results.knowledge_graph) {
|
130 |
+
// if google returns a knowledge graph, we use it
|
131 |
+
webSearch.knowledgeGraph = JSON.stringify(removeLinks(results.knowledge_graph));
|
132 |
+
|
133 |
+
text = webSearch.knowledgeGraph;
|
134 |
+
|
135 |
+
webSearch.messages.push({
|
136 |
+
type: "update",
|
137 |
+
message: "Found a Google knowledge page",
|
138 |
+
});
|
139 |
+
controller.enqueue(JSON.stringify({ messages: webSearch.messages }));
|
140 |
+
} else if (webSearch.results.length > 0) {
|
141 |
+
// otherwise we use the top result from search
|
142 |
+
const topUrl = webSearch.results[0];
|
143 |
+
|
144 |
+
webSearch.messages.push({
|
145 |
+
type: "update",
|
146 |
+
message: "Browsing first result",
|
147 |
+
args: [JSON.stringify(topUrl)],
|
148 |
+
});
|
149 |
+
controller.enqueue(JSON.stringify({ messages: webSearch.messages }));
|
150 |
+
|
151 |
+
// fetch the webpage
|
152 |
+
//10 second timeout:
|
153 |
+
const abortController = new AbortController();
|
154 |
+
setTimeout(() => abortController.abort(), 10000);
|
155 |
+
const htmlString = await fetch(topUrl, { signal: abortController.signal })
|
156 |
+
.then((response) => response.text())
|
157 |
+
.catch((err) => console.log(err));
|
158 |
+
|
159 |
+
const virtualConsole = new VirtualConsole();
|
160 |
+
virtualConsole.on("error", () => {
|
161 |
+
// No-op to skip console errors.
|
162 |
+
});
|
163 |
+
|
164 |
+
// put the html string into a DOM
|
165 |
+
const dom = new JSDOM(htmlString ?? "", {
|
166 |
+
virtualConsole,
|
167 |
+
});
|
168 |
+
|
169 |
+
const body = dom.window.document.querySelector("body");
|
170 |
+
if (!body) throw new Error("body of the webpage is null");
|
171 |
+
|
172 |
+
removeTags(body);
|
173 |
+
|
174 |
+
// recursively extract text content from the body and then remove newlines and multiple spaces
|
175 |
+
text = (naiveInnerText(body) ?? "").replace(/ {2}|\r\n|\n|\r/gm, "");
|
176 |
+
|
177 |
+
if (!text) throw new Error("text of the webpage is null");
|
178 |
+
} else {
|
179 |
+
throw new Error("No results found for this search query");
|
180 |
+
}
|
181 |
+
|
182 |
+
webSearch.messages.push({
|
183 |
+
type: "update",
|
184 |
+
message: "Creating summary",
|
185 |
+
});
|
186 |
+
controller.enqueue(JSON.stringify({ messages: webSearch.messages }));
|
187 |
+
|
188 |
+
const summaryPrompt =
|
189 |
+
model.userMessageToken +
|
190 |
+
text
|
191 |
+
.split(" ")
|
192 |
+
.slice(0, model.parameters?.truncate ?? 0)
|
193 |
+
.join(" ") +
|
194 |
+
model.messageEndToken +
|
195 |
+
model.userMessageToken +
|
196 |
+
`The text above should be summarized to best answer the query: ${webSearch.searchQuery}.` +
|
197 |
+
model.messageEndToken +
|
198 |
+
model.assistantMessageToken +
|
199 |
+
"Summary: ";
|
200 |
+
|
201 |
+
webSearch.summary = await generateFromDefaultEndpoint(summaryPrompt).then((txt: string) =>
|
202 |
+
txt.trim()
|
203 |
+
);
|
204 |
+
|
205 |
+
webSearch.messages.push({
|
206 |
+
type: "update",
|
207 |
+
message: "Injecting summary",
|
208 |
+
args: [JSON.stringify(webSearch.summary)],
|
209 |
+
});
|
210 |
+
controller.enqueue(JSON.stringify({ messages: webSearch.messages }));
|
211 |
+
} catch (searchError) {
|
212 |
+
if (searchError instanceof Error) {
|
213 |
+
webSearch.messages.push({
|
214 |
+
type: "error",
|
215 |
+
message: "An error occurred with the web search",
|
216 |
+
args: [JSON.stringify(searchError.message)],
|
217 |
+
});
|
218 |
+
}
|
219 |
+
}
|
220 |
+
|
221 |
+
const res = await collections.webSearches.insertOne(webSearch);
|
222 |
+
|
223 |
+
webSearch.messages.push({
|
224 |
+
type: "result",
|
225 |
+
id: res.insertedId.toString(),
|
226 |
+
});
|
227 |
+
controller.enqueue(JSON.stringify({ messages: webSearch.messages }));
|
228 |
+
},
|
229 |
+
});
|
230 |
+
|
231 |
+
return new Response(stream, { headers: { "Content-Type": "application/json" } });
|
232 |
+
}
|
@@ -26,7 +26,7 @@ export async function GET({ params }) {
|
|
26 |
throw error(404, "Conversation model not found");
|
27 |
}
|
28 |
|
29 |
-
const prompt = buildPrompt(conv.messages.slice(0, messageIndex + 1), model);
|
30 |
|
31 |
return new Response(
|
32 |
JSON.stringify(
|
|
|
26 |
throw error(404, "Conversation model not found");
|
27 |
}
|
28 |
|
29 |
+
const prompt = await buildPrompt(conv.messages.slice(0, messageIndex + 1), model);
|
30 |
|
31 |
return new Response(
|
32 |
JSON.stringify(
|
@@ -0,0 +1,39 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import { collections } from "$lib/server/database";
|
2 |
+
import { sha256 } from "$lib/utils/sha256.js";
|
3 |
+
import { error } from "@sveltejs/kit";
|
4 |
+
import { ObjectId } from "mongodb";
|
5 |
+
|
6 |
+
export async function GET({ params, locals }) {
|
7 |
+
const searchId = new ObjectId(params.id);
|
8 |
+
|
9 |
+
const search = await collections.webSearches.findOne({
|
10 |
+
_id: searchId,
|
11 |
+
});
|
12 |
+
|
13 |
+
if (!search) {
|
14 |
+
throw error(404, "Search query not found");
|
15 |
+
}
|
16 |
+
|
17 |
+
const conv = await collections.conversations.findOne({
|
18 |
+
_id: search.convId,
|
19 |
+
});
|
20 |
+
|
21 |
+
if (!conv) {
|
22 |
+
throw error(404, "Conversation not found");
|
23 |
+
}
|
24 |
+
|
25 |
+
// there's no better way to see if a conversation has been shared, so we hash the messages and see if there's a shared conversation with the same hash
|
26 |
+
const hash = await sha256(JSON.stringify(conv.messages));
|
27 |
+
const sharedConv = await collections.sharedConversations.findOne({
|
28 |
+
hash: hash,
|
29 |
+
});
|
30 |
+
|
31 |
+
const userShouldSeeConv =
|
32 |
+
(conv.userId && locals.user?._id.toString() === conv.userId.toString()) || sharedConv !== null;
|
33 |
+
|
34 |
+
if (!userShouldSeeConv) {
|
35 |
+
throw error(403, "You don't have access to the conversation here.");
|
36 |
+
}
|
37 |
+
|
38 |
+
return new Response(JSON.stringify(search), { headers: { "Content-Type": "application/json" } });
|
39 |
+
}
|