Enhancements (#79)
Browse files- Improve checkpoints menu UX
- Allow running conversations individually
- Hide model page if its a custom model
- Regen buttons on messages
---------
Co-authored-by: Victor Muštar <victor.mustar@gmail.com>
- package.json +2 -2
- pnpm-lock.yaml +56 -56
- src/lib/actions/click-outside.ts +32 -5
- src/lib/components/icon-custom.svelte +20 -0
- src/lib/components/inference-playground/checkpoints-menu.svelte +23 -8
- src/lib/components/inference-playground/conversation.svelte +18 -1
- src/lib/components/inference-playground/custom-model-config.svelte +49 -3
- src/lib/components/inference-playground/message.svelte +28 -1
- src/lib/components/inference-playground/playground.svelte +153 -162
- src/lib/components/inference-playground/utils.ts +7 -27
- src/lib/spells/abort-manager.svelte.ts +10 -1
- src/lib/spells/create-init.svelte.ts +13 -9
- src/lib/state/session.svelte.ts +137 -0
- src/lib/types.ts +7 -0
- src/lib/utils/array.ts +34 -0
- src/lib/utils/platform.ts +3 -0
- src/routes/+layout.svelte +1 -1
package.json
CHANGED
@@ -38,14 +38,14 @@
|
|
38 |
"globals": "^16.0.0",
|
39 |
"highlight.js": "^11.10.0",
|
40 |
"jiti": "^2.4.2",
|
41 |
-
"melt": "^0.
|
42 |
"openai": "^4.90.0",
|
43 |
"postcss": "^8.4.38",
|
44 |
"prettier": "^3.1.1",
|
45 |
"prettier-plugin-svelte": "^3.2.6",
|
46 |
"prettier-plugin-tailwindcss": "^0.6.11",
|
47 |
"runed": "^0.25.0",
|
48 |
-
"svelte": "^5.
|
49 |
"svelte-check": "^4.0.0",
|
50 |
"tailwind-merge": "^3.0.2",
|
51 |
"tailwindcss": "^4.0.9",
|
|
|
38 |
"globals": "^16.0.0",
|
39 |
"highlight.js": "^11.10.0",
|
40 |
"jiti": "^2.4.2",
|
41 |
+
"melt": "^0.30.1",
|
42 |
"openai": "^4.90.0",
|
43 |
"postcss": "^8.4.38",
|
44 |
"prettier": "^3.1.1",
|
45 |
"prettier-plugin-svelte": "^3.2.6",
|
46 |
"prettier-plugin-tailwindcss": "^0.6.11",
|
47 |
"runed": "^0.25.0",
|
48 |
+
"svelte": "^5.28.2",
|
49 |
"svelte-check": "^4.0.0",
|
50 |
"tailwind-merge": "^3.0.2",
|
51 |
"tailwindcss": "^4.0.9",
|
pnpm-lock.yaml
CHANGED
@@ -10,7 +10,7 @@ importers:
|
|
10 |
dependencies:
|
11 |
eslint-plugin-svelte:
|
12 |
specifier: ^3.3.1
|
13 |
-
version: 3.3.1(eslint@9.22.0(jiti@2.4.2))(svelte@5.
|
14 |
typia:
|
15 |
specifier: ^8.0.0
|
16 |
version: 8.0.0(@samchon/openapi@3.0.0)(typescript@5.8.2)
|
@@ -50,16 +50,16 @@ importers:
|
|
50 |
version: 3.0.0
|
51 |
'@sveltejs/adapter-auto':
|
52 |
specifier: ^3.2.2
|
53 |
-
version: 3.3.1(@sveltejs/kit@2.18.0(@sveltejs/vite-plugin-svelte@4.0.4(svelte@5.
|
54 |
'@sveltejs/adapter-node':
|
55 |
specifier: ^5.2.0
|
56 |
-
version: 5.2.12(@sveltejs/kit@2.18.0(@sveltejs/vite-plugin-svelte@4.0.4(svelte@5.
|
57 |
'@sveltejs/kit':
|
58 |
specifier: ^2.5.27
|
59 |
-
version: 2.18.0(@sveltejs/vite-plugin-svelte@4.0.4(svelte@5.
|
60 |
'@sveltejs/vite-plugin-svelte':
|
61 |
specifier: ^4.0.0
|
62 |
-
version: 4.0.4(svelte@5.
|
63 |
'@tailwindcss/container-queries':
|
64 |
specifier: ^0.1.1
|
65 |
version: 0.1.1(tailwindcss@4.0.9)
|
@@ -88,8 +88,8 @@ importers:
|
|
88 |
specifier: ^2.4.2
|
89 |
version: 2.4.2
|
90 |
melt:
|
91 |
-
specifier: ^0.
|
92 |
-
version: 0.
|
93 |
openai:
|
94 |
specifier: ^4.90.0
|
95 |
version: 4.90.0
|
@@ -101,19 +101,19 @@ importers:
|
|
101 |
version: 3.5.3
|
102 |
prettier-plugin-svelte:
|
103 |
specifier: ^3.2.6
|
104 |
-
version: 3.3.3(prettier@3.5.3)(svelte@5.
|
105 |
prettier-plugin-tailwindcss:
|
106 |
specifier: ^0.6.11
|
107 |
-
version: 0.6.11(prettier-plugin-svelte@3.3.3(prettier@3.5.3)(svelte@5.
|
108 |
runed:
|
109 |
specifier: ^0.25.0
|
110 |
-
version: 0.25.0(svelte@5.
|
111 |
svelte:
|
112 |
-
specifier: ^5.
|
113 |
-
version: 5.
|
114 |
svelte-check:
|
115 |
specifier: ^4.0.0
|
116 |
-
version: 4.1.5(picomatch@4.0.2)(svelte@5.
|
117 |
tailwind-merge:
|
118 |
specifier: ^3.0.2
|
119 |
version: 3.0.2
|
@@ -134,7 +134,7 @@ importers:
|
|
134 |
version: 8.26.1(eslint@9.22.0(jiti@2.4.2))(typescript@5.8.2)
|
135 |
unplugin-icons:
|
136 |
specifier: ^22.1.0
|
137 |
-
version: 22.1.0(svelte@5.
|
138 |
vite:
|
139 |
specifier: ^5.4.4
|
140 |
version: 5.4.14(@types/node@18.19.84)(lightningcss@1.29.1)
|
@@ -1407,8 +1407,8 @@ packages:
|
|
1407 |
resolution: {integrity: sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==}
|
1408 |
engines: {node: '>=0.10'}
|
1409 |
|
1410 |
-
esrap@1.4.
|
1411 |
-
resolution: {integrity: sha512-
|
1412 |
|
1413 |
esrecurse@4.3.0:
|
1414 |
resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==}
|
@@ -1825,8 +1825,8 @@ packages:
|
|
1825 |
resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==}
|
1826 |
engines: {node: '>= 0.4'}
|
1827 |
|
1828 |
-
melt@0.
|
1829 |
-
resolution: {integrity: sha512-
|
1830 |
peerDependencies:
|
1831 |
'@floating-ui/dom': ^1.6.0
|
1832 |
svelte: ^5.0.0
|
@@ -2299,8 +2299,8 @@ packages:
|
|
2299 |
svelte:
|
2300 |
optional: true
|
2301 |
|
2302 |
-
svelte@5.
|
2303 |
-
resolution: {integrity: sha512-
|
2304 |
engines: {node: '>=18'}
|
2305 |
|
2306 |
synckit@0.9.2:
|
@@ -3125,22 +3125,22 @@ snapshots:
|
|
3125 |
dependencies:
|
3126 |
acorn: 8.14.0
|
3127 |
|
3128 |
-
'@sveltejs/adapter-auto@3.3.1(@sveltejs/kit@2.18.0(@sveltejs/vite-plugin-svelte@4.0.4(svelte@5.
|
3129 |
dependencies:
|
3130 |
-
'@sveltejs/kit': 2.18.0(@sveltejs/vite-plugin-svelte@4.0.4(svelte@5.
|
3131 |
import-meta-resolve: 4.1.0
|
3132 |
|
3133 |
-
'@sveltejs/adapter-node@5.2.12(@sveltejs/kit@2.18.0(@sveltejs/vite-plugin-svelte@4.0.4(svelte@5.
|
3134 |
dependencies:
|
3135 |
'@rollup/plugin-commonjs': 28.0.2(rollup@4.34.9)
|
3136 |
'@rollup/plugin-json': 6.1.0(rollup@4.34.9)
|
3137 |
'@rollup/plugin-node-resolve': 16.0.0(rollup@4.34.9)
|
3138 |
-
'@sveltejs/kit': 2.18.0(@sveltejs/vite-plugin-svelte@4.0.4(svelte@5.
|
3139 |
rollup: 4.34.9
|
3140 |
|
3141 |
-
'@sveltejs/kit@2.18.0(@sveltejs/vite-plugin-svelte@4.0.4(svelte@5.
|
3142 |
dependencies:
|
3143 |
-
'@sveltejs/vite-plugin-svelte': 4.0.4(svelte@5.
|
3144 |
'@types/cookie': 0.6.0
|
3145 |
cookie: 0.6.0
|
3146 |
devalue: 5.1.1
|
@@ -3152,26 +3152,26 @@ snapshots:
|
|
3152 |
sade: 1.8.1
|
3153 |
set-cookie-parser: 2.7.1
|
3154 |
sirv: 3.0.1
|
3155 |
-
svelte: 5.
|
3156 |
vite: 5.4.14(@types/node@18.19.84)(lightningcss@1.29.1)
|
3157 |
|
3158 |
-
'@sveltejs/vite-plugin-svelte-inspector@3.0.1(@sveltejs/vite-plugin-svelte@4.0.4(svelte@5.
|
3159 |
dependencies:
|
3160 |
-
'@sveltejs/vite-plugin-svelte': 4.0.4(svelte@5.
|
3161 |
debug: 4.4.0
|
3162 |
-
svelte: 5.
|
3163 |
vite: 5.4.14(@types/node@18.19.84)(lightningcss@1.29.1)
|
3164 |
transitivePeerDependencies:
|
3165 |
- supports-color
|
3166 |
|
3167 |
-
'@sveltejs/vite-plugin-svelte@4.0.4(svelte@5.
|
3168 |
dependencies:
|
3169 |
-
'@sveltejs/vite-plugin-svelte-inspector': 3.0.1(@sveltejs/vite-plugin-svelte@4.0.4(svelte@5.
|
3170 |
debug: 4.4.0
|
3171 |
deepmerge: 4.3.1
|
3172 |
kleur: 4.1.5
|
3173 |
magic-string: 0.30.17
|
3174 |
-
svelte: 5.
|
3175 |
vite: 5.4.14(@types/node@18.19.84)(lightningcss@1.29.1)
|
3176 |
vitefu: 1.0.6(vite@5.4.14(@types/node@18.19.84)(lightningcss@1.29.1))
|
3177 |
transitivePeerDependencies:
|
@@ -3630,7 +3630,7 @@ snapshots:
|
|
3630 |
optionalDependencies:
|
3631 |
eslint-config-prettier: 10.1.1(eslint@9.22.0(jiti@2.4.2))
|
3632 |
|
3633 |
-
eslint-plugin-svelte@3.3.1(eslint@9.22.0(jiti@2.4.2))(svelte@5.
|
3634 |
dependencies:
|
3635 |
'@eslint-community/eslint-utils': 4.4.1(eslint@9.22.0(jiti@2.4.2))
|
3636 |
'@jridgewell/sourcemap-codec': 1.5.0
|
@@ -3642,9 +3642,9 @@ snapshots:
|
|
3642 |
postcss-load-config: 3.1.4(postcss@8.5.3)
|
3643 |
postcss-safe-parser: 7.0.1(postcss@8.5.3)
|
3644 |
semver: 7.7.1
|
3645 |
-
svelte-eslint-parser: 1.1.0(svelte@5.
|
3646 |
optionalDependencies:
|
3647 |
-
svelte: 5.
|
3648 |
transitivePeerDependencies:
|
3649 |
- ts-node
|
3650 |
|
@@ -3713,7 +3713,7 @@ snapshots:
|
|
3713 |
dependencies:
|
3714 |
estraverse: 5.3.0
|
3715 |
|
3716 |
-
esrap@1.4.
|
3717 |
dependencies:
|
3718 |
'@jridgewell/sourcemap-codec': 1.5.0
|
3719 |
|
@@ -4087,14 +4087,14 @@ snapshots:
|
|
4087 |
|
4088 |
math-intrinsics@1.1.0: {}
|
4089 |
|
4090 |
-
melt@0.
|
4091 |
dependencies:
|
4092 |
'@floating-ui/dom': 1.6.13
|
4093 |
dequal: 2.0.3
|
4094 |
jest-axe: 9.0.0
|
4095 |
nanoid: 5.1.5
|
4096 |
-
runed: 0.23.4(svelte@5.
|
4097 |
-
svelte: 5.
|
4098 |
|
4099 |
merge2@1.4.1: {}
|
4100 |
|
@@ -4305,16 +4305,16 @@ snapshots:
|
|
4305 |
dependencies:
|
4306 |
fast-diff: 1.3.0
|
4307 |
|
4308 |
-
prettier-plugin-svelte@3.3.3(prettier@3.5.3)(svelte@5.
|
4309 |
dependencies:
|
4310 |
prettier: 3.5.3
|
4311 |
-
svelte: 5.
|
4312 |
|
4313 |
-
prettier-plugin-tailwindcss@0.6.11(prettier-plugin-svelte@3.3.3(prettier@3.5.3)(svelte@5.
|
4314 |
dependencies:
|
4315 |
prettier: 3.5.3
|
4316 |
optionalDependencies:
|
4317 |
-
prettier-plugin-svelte: 3.3.3(prettier@3.5.3)(svelte@5.
|
4318 |
|
4319 |
prettier@3.5.3: {}
|
4320 |
|
@@ -4410,15 +4410,15 @@ snapshots:
|
|
4410 |
dependencies:
|
4411 |
queue-microtask: 1.2.3
|
4412 |
|
4413 |
-
runed@0.23.4(svelte@5.
|
4414 |
dependencies:
|
4415 |
esm-env: 1.2.2
|
4416 |
-
svelte: 5.
|
4417 |
|
4418 |
-
runed@0.25.0(svelte@5.
|
4419 |
dependencies:
|
4420 |
esm-env: 1.2.2
|
4421 |
-
svelte: 5.
|
4422 |
|
4423 |
rxjs@7.8.2:
|
4424 |
dependencies:
|
@@ -4504,19 +4504,19 @@ snapshots:
|
|
4504 |
|
4505 |
supports-preserve-symlinks-flag@1.0.0: {}
|
4506 |
|
4507 |
-
svelte-check@4.1.5(picomatch@4.0.2)(svelte@5.
|
4508 |
dependencies:
|
4509 |
'@jridgewell/trace-mapping': 0.3.25
|
4510 |
chokidar: 4.0.3
|
4511 |
fdir: 6.4.3(picomatch@4.0.2)
|
4512 |
picocolors: 1.1.1
|
4513 |
sade: 1.8.1
|
4514 |
-
svelte: 5.
|
4515 |
typescript: 5.8.2
|
4516 |
transitivePeerDependencies:
|
4517 |
- picomatch
|
4518 |
|
4519 |
-
svelte-eslint-parser@1.1.0(svelte@5.
|
4520 |
dependencies:
|
4521 |
eslint-scope: 8.3.0
|
4522 |
eslint-visitor-keys: 4.2.0
|
@@ -4525,9 +4525,9 @@ snapshots:
|
|
4525 |
postcss-scss: 4.0.9(postcss@8.5.3)
|
4526 |
postcss-selector-parser: 7.1.0
|
4527 |
optionalDependencies:
|
4528 |
-
svelte: 5.
|
4529 |
|
4530 |
-
svelte@5.
|
4531 |
dependencies:
|
4532 |
'@ampproject/remapping': 2.3.0
|
4533 |
'@jridgewell/sourcemap-codec': 1.5.0
|
@@ -4538,7 +4538,7 @@ snapshots:
|
|
4538 |
axobject-query: 4.1.0
|
4539 |
clsx: 2.1.1
|
4540 |
esm-env: 1.2.2
|
4541 |
-
esrap: 1.4.
|
4542 |
is-reference: 3.0.3
|
4543 |
locate-character: 3.0.0
|
4544 |
magic-string: 0.30.17
|
@@ -4641,7 +4641,7 @@ snapshots:
|
|
4641 |
|
4642 |
undici-types@5.26.5: {}
|
4643 |
|
4644 |
-
unplugin-icons@22.1.0(svelte@5.
|
4645 |
dependencies:
|
4646 |
'@antfu/install-pkg': 1.0.0
|
4647 |
'@iconify/utils': 2.3.0
|
@@ -4649,7 +4649,7 @@ snapshots:
|
|
4649 |
local-pkg: 1.1.1
|
4650 |
unplugin: 2.2.0
|
4651 |
optionalDependencies:
|
4652 |
-
svelte: 5.
|
4653 |
transitivePeerDependencies:
|
4654 |
- supports-color
|
4655 |
|
|
|
10 |
dependencies:
|
11 |
eslint-plugin-svelte:
|
12 |
specifier: ^3.3.1
|
13 |
+
version: 3.3.1(eslint@9.22.0(jiti@2.4.2))(svelte@5.28.2)
|
14 |
typia:
|
15 |
specifier: ^8.0.0
|
16 |
version: 8.0.0(@samchon/openapi@3.0.0)(typescript@5.8.2)
|
|
|
50 |
version: 3.0.0
|
51 |
'@sveltejs/adapter-auto':
|
52 |
specifier: ^3.2.2
|
53 |
+
version: 3.3.1(@sveltejs/kit@2.18.0(@sveltejs/vite-plugin-svelte@4.0.4(svelte@5.28.2)(vite@5.4.14(@types/node@18.19.84)(lightningcss@1.29.1)))(svelte@5.28.2)(vite@5.4.14(@types/node@18.19.84)(lightningcss@1.29.1)))
|
54 |
'@sveltejs/adapter-node':
|
55 |
specifier: ^5.2.0
|
56 |
+
version: 5.2.12(@sveltejs/kit@2.18.0(@sveltejs/vite-plugin-svelte@4.0.4(svelte@5.28.2)(vite@5.4.14(@types/node@18.19.84)(lightningcss@1.29.1)))(svelte@5.28.2)(vite@5.4.14(@types/node@18.19.84)(lightningcss@1.29.1)))
|
57 |
'@sveltejs/kit':
|
58 |
specifier: ^2.5.27
|
59 |
+
version: 2.18.0(@sveltejs/vite-plugin-svelte@4.0.4(svelte@5.28.2)(vite@5.4.14(@types/node@18.19.84)(lightningcss@1.29.1)))(svelte@5.28.2)(vite@5.4.14(@types/node@18.19.84)(lightningcss@1.29.1))
|
60 |
'@sveltejs/vite-plugin-svelte':
|
61 |
specifier: ^4.0.0
|
62 |
+
version: 4.0.4(svelte@5.28.2)(vite@5.4.14(@types/node@18.19.84)(lightningcss@1.29.1))
|
63 |
'@tailwindcss/container-queries':
|
64 |
specifier: ^0.1.1
|
65 |
version: 0.1.1(tailwindcss@4.0.9)
|
|
|
88 |
specifier: ^2.4.2
|
89 |
version: 2.4.2
|
90 |
melt:
|
91 |
+
specifier: ^0.30.1
|
92 |
+
version: 0.30.1(@floating-ui/dom@1.6.13)(svelte@5.28.2)
|
93 |
openai:
|
94 |
specifier: ^4.90.0
|
95 |
version: 4.90.0
|
|
|
101 |
version: 3.5.3
|
102 |
prettier-plugin-svelte:
|
103 |
specifier: ^3.2.6
|
104 |
+
version: 3.3.3(prettier@3.5.3)(svelte@5.28.2)
|
105 |
prettier-plugin-tailwindcss:
|
106 |
specifier: ^0.6.11
|
107 |
+
version: 0.6.11(prettier-plugin-svelte@3.3.3(prettier@3.5.3)(svelte@5.28.2))(prettier@3.5.3)
|
108 |
runed:
|
109 |
specifier: ^0.25.0
|
110 |
+
version: 0.25.0(svelte@5.28.2)
|
111 |
svelte:
|
112 |
+
specifier: ^5.28.2
|
113 |
+
version: 5.28.2
|
114 |
svelte-check:
|
115 |
specifier: ^4.0.0
|
116 |
+
version: 4.1.5(picomatch@4.0.2)(svelte@5.28.2)(typescript@5.8.2)
|
117 |
tailwind-merge:
|
118 |
specifier: ^3.0.2
|
119 |
version: 3.0.2
|
|
|
134 |
version: 8.26.1(eslint@9.22.0(jiti@2.4.2))(typescript@5.8.2)
|
135 |
unplugin-icons:
|
136 |
specifier: ^22.1.0
|
137 |
+
version: 22.1.0(svelte@5.28.2)
|
138 |
vite:
|
139 |
specifier: ^5.4.4
|
140 |
version: 5.4.14(@types/node@18.19.84)(lightningcss@1.29.1)
|
|
|
1407 |
resolution: {integrity: sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==}
|
1408 |
engines: {node: '>=0.10'}
|
1409 |
|
1410 |
+
esrap@1.4.6:
|
1411 |
+
resolution: {integrity: sha512-F/D2mADJ9SHY3IwksD4DAXjTt7qt7GWUf3/8RhCNWmC/67tyb55dpimHmy7EplakFaflV0R/PC+fdSPqrRHAQw==}
|
1412 |
|
1413 |
esrecurse@4.3.0:
|
1414 |
resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==}
|
|
|
1825 |
resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==}
|
1826 |
engines: {node: '>= 0.4'}
|
1827 |
|
1828 |
+
melt@0.30.1:
|
1829 |
+
resolution: {integrity: sha512-Z3X3IMknWSbXFlzQA6On18kdGf1a+Kgqu/TxxvchjGGiS3RINd96PrlLU2Bl/SOxF+UWLLYmH1fohwiMz9UsQQ==}
|
1830 |
peerDependencies:
|
1831 |
'@floating-ui/dom': ^1.6.0
|
1832 |
svelte: ^5.0.0
|
|
|
2299 |
svelte:
|
2300 |
optional: true
|
2301 |
|
2302 |
+
svelte@5.28.2:
|
2303 |
+
resolution: {integrity: sha512-FbWBxgWOpQfhKvoGJv/TFwzqb4EhJbwCD17dB0tEpQiw1XyUEKZJtgm4nA4xq3LLsMo7hu5UY/BOFmroAxKTMg==}
|
2304 |
engines: {node: '>=18'}
|
2305 |
|
2306 |
synckit@0.9.2:
|
|
|
3125 |
dependencies:
|
3126 |
acorn: 8.14.0
|
3127 |
|
3128 |
+
'@sveltejs/adapter-auto@3.3.1(@sveltejs/kit@2.18.0(@sveltejs/vite-plugin-svelte@4.0.4(svelte@5.28.2)(vite@5.4.14(@types/node@18.19.84)(lightningcss@1.29.1)))(svelte@5.28.2)(vite@5.4.14(@types/node@18.19.84)(lightningcss@1.29.1)))':
|
3129 |
dependencies:
|
3130 |
+
'@sveltejs/kit': 2.18.0(@sveltejs/vite-plugin-svelte@4.0.4(svelte@5.28.2)(vite@5.4.14(@types/node@18.19.84)(lightningcss@1.29.1)))(svelte@5.28.2)(vite@5.4.14(@types/node@18.19.84)(lightningcss@1.29.1))
|
3131 |
import-meta-resolve: 4.1.0
|
3132 |
|
3133 |
+
'@sveltejs/adapter-node@5.2.12(@sveltejs/kit@2.18.0(@sveltejs/vite-plugin-svelte@4.0.4(svelte@5.28.2)(vite@5.4.14(@types/node@18.19.84)(lightningcss@1.29.1)))(svelte@5.28.2)(vite@5.4.14(@types/node@18.19.84)(lightningcss@1.29.1)))':
|
3134 |
dependencies:
|
3135 |
'@rollup/plugin-commonjs': 28.0.2(rollup@4.34.9)
|
3136 |
'@rollup/plugin-json': 6.1.0(rollup@4.34.9)
|
3137 |
'@rollup/plugin-node-resolve': 16.0.0(rollup@4.34.9)
|
3138 |
+
'@sveltejs/kit': 2.18.0(@sveltejs/vite-plugin-svelte@4.0.4(svelte@5.28.2)(vite@5.4.14(@types/node@18.19.84)(lightningcss@1.29.1)))(svelte@5.28.2)(vite@5.4.14(@types/node@18.19.84)(lightningcss@1.29.1))
|
3139 |
rollup: 4.34.9
|
3140 |
|
3141 |
+
'@sveltejs/kit@2.18.0(@sveltejs/vite-plugin-svelte@4.0.4(svelte@5.28.2)(vite@5.4.14(@types/node@18.19.84)(lightningcss@1.29.1)))(svelte@5.28.2)(vite@5.4.14(@types/node@18.19.84)(lightningcss@1.29.1))':
|
3142 |
dependencies:
|
3143 |
+
'@sveltejs/vite-plugin-svelte': 4.0.4(svelte@5.28.2)(vite@5.4.14(@types/node@18.19.84)(lightningcss@1.29.1))
|
3144 |
'@types/cookie': 0.6.0
|
3145 |
cookie: 0.6.0
|
3146 |
devalue: 5.1.1
|
|
|
3152 |
sade: 1.8.1
|
3153 |
set-cookie-parser: 2.7.1
|
3154 |
sirv: 3.0.1
|
3155 |
+
svelte: 5.28.2
|
3156 |
vite: 5.4.14(@types/node@18.19.84)(lightningcss@1.29.1)
|
3157 |
|
3158 |
+
'@sveltejs/vite-plugin-svelte-inspector@3.0.1(@sveltejs/vite-plugin-svelte@4.0.4(svelte@5.28.2)(vite@5.4.14(@types/node@18.19.84)(lightningcss@1.29.1)))(svelte@5.28.2)(vite@5.4.14(@types/node@18.19.84)(lightningcss@1.29.1))':
|
3159 |
dependencies:
|
3160 |
+
'@sveltejs/vite-plugin-svelte': 4.0.4(svelte@5.28.2)(vite@5.4.14(@types/node@18.19.84)(lightningcss@1.29.1))
|
3161 |
debug: 4.4.0
|
3162 |
+
svelte: 5.28.2
|
3163 |
vite: 5.4.14(@types/node@18.19.84)(lightningcss@1.29.1)
|
3164 |
transitivePeerDependencies:
|
3165 |
- supports-color
|
3166 |
|
3167 |
+
'@sveltejs/vite-plugin-svelte@4.0.4(svelte@5.28.2)(vite@5.4.14(@types/node@18.19.84)(lightningcss@1.29.1))':
|
3168 |
dependencies:
|
3169 |
+
'@sveltejs/vite-plugin-svelte-inspector': 3.0.1(@sveltejs/vite-plugin-svelte@4.0.4(svelte@5.28.2)(vite@5.4.14(@types/node@18.19.84)(lightningcss@1.29.1)))(svelte@5.28.2)(vite@5.4.14(@types/node@18.19.84)(lightningcss@1.29.1))
|
3170 |
debug: 4.4.0
|
3171 |
deepmerge: 4.3.1
|
3172 |
kleur: 4.1.5
|
3173 |
magic-string: 0.30.17
|
3174 |
+
svelte: 5.28.2
|
3175 |
vite: 5.4.14(@types/node@18.19.84)(lightningcss@1.29.1)
|
3176 |
vitefu: 1.0.6(vite@5.4.14(@types/node@18.19.84)(lightningcss@1.29.1))
|
3177 |
transitivePeerDependencies:
|
|
|
3630 |
optionalDependencies:
|
3631 |
eslint-config-prettier: 10.1.1(eslint@9.22.0(jiti@2.4.2))
|
3632 |
|
3633 |
+
eslint-plugin-svelte@3.3.1(eslint@9.22.0(jiti@2.4.2))(svelte@5.28.2):
|
3634 |
dependencies:
|
3635 |
'@eslint-community/eslint-utils': 4.4.1(eslint@9.22.0(jiti@2.4.2))
|
3636 |
'@jridgewell/sourcemap-codec': 1.5.0
|
|
|
3642 |
postcss-load-config: 3.1.4(postcss@8.5.3)
|
3643 |
postcss-safe-parser: 7.0.1(postcss@8.5.3)
|
3644 |
semver: 7.7.1
|
3645 |
+
svelte-eslint-parser: 1.1.0(svelte@5.28.2)
|
3646 |
optionalDependencies:
|
3647 |
+
svelte: 5.28.2
|
3648 |
transitivePeerDependencies:
|
3649 |
- ts-node
|
3650 |
|
|
|
3713 |
dependencies:
|
3714 |
estraverse: 5.3.0
|
3715 |
|
3716 |
+
esrap@1.4.6:
|
3717 |
dependencies:
|
3718 |
'@jridgewell/sourcemap-codec': 1.5.0
|
3719 |
|
|
|
4087 |
|
4088 |
math-intrinsics@1.1.0: {}
|
4089 |
|
4090 |
+
melt@0.30.1(@floating-ui/dom@1.6.13)(svelte@5.28.2):
|
4091 |
dependencies:
|
4092 |
'@floating-ui/dom': 1.6.13
|
4093 |
dequal: 2.0.3
|
4094 |
jest-axe: 9.0.0
|
4095 |
nanoid: 5.1.5
|
4096 |
+
runed: 0.23.4(svelte@5.28.2)
|
4097 |
+
svelte: 5.28.2
|
4098 |
|
4099 |
merge2@1.4.1: {}
|
4100 |
|
|
|
4305 |
dependencies:
|
4306 |
fast-diff: 1.3.0
|
4307 |
|
4308 |
+
prettier-plugin-svelte@3.3.3(prettier@3.5.3)(svelte@5.28.2):
|
4309 |
dependencies:
|
4310 |
prettier: 3.5.3
|
4311 |
+
svelte: 5.28.2
|
4312 |
|
4313 |
+
prettier-plugin-tailwindcss@0.6.11(prettier-plugin-svelte@3.3.3(prettier@3.5.3)(svelte@5.28.2))(prettier@3.5.3):
|
4314 |
dependencies:
|
4315 |
prettier: 3.5.3
|
4316 |
optionalDependencies:
|
4317 |
+
prettier-plugin-svelte: 3.3.3(prettier@3.5.3)(svelte@5.28.2)
|
4318 |
|
4319 |
prettier@3.5.3: {}
|
4320 |
|
|
|
4410 |
dependencies:
|
4411 |
queue-microtask: 1.2.3
|
4412 |
|
4413 |
+
runed@0.23.4(svelte@5.28.2):
|
4414 |
dependencies:
|
4415 |
esm-env: 1.2.2
|
4416 |
+
svelte: 5.28.2
|
4417 |
|
4418 |
+
runed@0.25.0(svelte@5.28.2):
|
4419 |
dependencies:
|
4420 |
esm-env: 1.2.2
|
4421 |
+
svelte: 5.28.2
|
4422 |
|
4423 |
rxjs@7.8.2:
|
4424 |
dependencies:
|
|
|
4504 |
|
4505 |
supports-preserve-symlinks-flag@1.0.0: {}
|
4506 |
|
4507 |
+
svelte-check@4.1.5(picomatch@4.0.2)(svelte@5.28.2)(typescript@5.8.2):
|
4508 |
dependencies:
|
4509 |
'@jridgewell/trace-mapping': 0.3.25
|
4510 |
chokidar: 4.0.3
|
4511 |
fdir: 6.4.3(picomatch@4.0.2)
|
4512 |
picocolors: 1.1.1
|
4513 |
sade: 1.8.1
|
4514 |
+
svelte: 5.28.2
|
4515 |
typescript: 5.8.2
|
4516 |
transitivePeerDependencies:
|
4517 |
- picomatch
|
4518 |
|
4519 |
+
svelte-eslint-parser@1.1.0(svelte@5.28.2):
|
4520 |
dependencies:
|
4521 |
eslint-scope: 8.3.0
|
4522 |
eslint-visitor-keys: 4.2.0
|
|
|
4525 |
postcss-scss: 4.0.9(postcss@8.5.3)
|
4526 |
postcss-selector-parser: 7.1.0
|
4527 |
optionalDependencies:
|
4528 |
+
svelte: 5.28.2
|
4529 |
|
4530 |
+
svelte@5.28.2:
|
4531 |
dependencies:
|
4532 |
'@ampproject/remapping': 2.3.0
|
4533 |
'@jridgewell/sourcemap-codec': 1.5.0
|
|
|
4538 |
axobject-query: 4.1.0
|
4539 |
clsx: 2.1.1
|
4540 |
esm-env: 1.2.2
|
4541 |
+
esrap: 1.4.6
|
4542 |
is-reference: 3.0.3
|
4543 |
locate-character: 3.0.0
|
4544 |
magic-string: 0.30.17
|
|
|
4641 |
|
4642 |
undici-types@5.26.5: {}
|
4643 |
|
4644 |
+
unplugin-icons@22.1.0(svelte@5.28.2):
|
4645 |
dependencies:
|
4646 |
'@antfu/install-pkg': 1.0.0
|
4647 |
'@iconify/utils': 2.3.0
|
|
|
4649 |
local-pkg: 1.1.1
|
4650 |
unplugin: 2.2.0
|
4651 |
optionalDependencies:
|
4652 |
+
svelte: 5.28.2
|
4653 |
transitivePeerDependencies:
|
4654 |
- supports-color
|
4655 |
|
src/lib/actions/click-outside.ts
CHANGED
@@ -3,8 +3,8 @@ import type { Action } from "svelte/action";
|
|
3 |
export const clickOutside: Action<HTMLElement, () => void> = (node, callback) => {
|
4 |
let _callback = callback;
|
5 |
|
6 |
-
function update(
|
7 |
-
_callback =
|
8 |
}
|
9 |
|
10 |
function handleClick(event: MouseEvent) {
|
@@ -12,17 +12,44 @@ export const clickOutside: Action<HTMLElement, () => void> = (node, callback) =>
|
|
12 |
// Don't close if text is selected
|
13 |
return;
|
14 |
}
|
15 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
16 |
_callback();
|
17 |
}
|
18 |
}
|
19 |
|
20 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
21 |
|
22 |
return {
|
23 |
update,
|
24 |
destroy() {
|
25 |
-
|
|
|
|
|
|
|
|
|
26 |
},
|
27 |
};
|
28 |
};
|
|
|
3 |
export const clickOutside: Action<HTMLElement, () => void> = (node, callback) => {
|
4 |
let _callback = callback;
|
5 |
|
6 |
+
function update(newCallback: () => void) {
|
7 |
+
_callback = newCallback;
|
8 |
}
|
9 |
|
10 |
function handleClick(event: MouseEvent) {
|
|
|
12 |
// Don't close if text is selected
|
13 |
return;
|
14 |
}
|
15 |
+
|
16 |
+
// For dialog elements, check if click was on the backdrop
|
17 |
+
if (node instanceof HTMLDialogElement) {
|
18 |
+
const rect = node.getBoundingClientRect();
|
19 |
+
const isInDialog =
|
20 |
+
event.clientX >= rect.left &&
|
21 |
+
event.clientX <= rect.right &&
|
22 |
+
event.clientY >= rect.top &&
|
23 |
+
event.clientY <= rect.bottom;
|
24 |
+
|
25 |
+
if (!isInDialog) {
|
26 |
+
_callback();
|
27 |
+
return;
|
28 |
+
}
|
29 |
+
}
|
30 |
+
|
31 |
+
// For non-dialog elements, use the standard contains check
|
32 |
+
if (!node.contains(event.target as Node) && !event.defaultPrevented) {
|
33 |
_callback();
|
34 |
}
|
35 |
}
|
36 |
|
37 |
+
// For dialogs, listen on the element itself
|
38 |
+
if (node instanceof HTMLDialogElement) {
|
39 |
+
node.addEventListener("click", handleClick);
|
40 |
+
} else {
|
41 |
+
// For other elements, listen on the document
|
42 |
+
document.addEventListener("click", handleClick, true);
|
43 |
+
}
|
44 |
|
45 |
return {
|
46 |
update,
|
47 |
destroy() {
|
48 |
+
if (node instanceof HTMLDialogElement) {
|
49 |
+
node.removeEventListener("click", handleClick);
|
50 |
+
} else {
|
51 |
+
document.removeEventListener("click", handleClick, true);
|
52 |
+
}
|
53 |
},
|
54 |
};
|
55 |
};
|
src/lib/components/icon-custom.svelte
ADDED
@@ -0,0 +1,20 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<script lang="ts">
|
2 |
+
import type { SVGAttributes } from "svelte/elements";
|
3 |
+
|
4 |
+
type Icon = "regen";
|
5 |
+
|
6 |
+
interface Props extends SVGAttributes<SVGElement> {
|
7 |
+
icon: Icon;
|
8 |
+
}
|
9 |
+
|
10 |
+
let { icon, ...rest }: Props = $props();
|
11 |
+
</script>
|
12 |
+
|
13 |
+
{#if icon === "regen"}
|
14 |
+
<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg" {...rest}>
|
15 |
+
<path
|
16 |
+
d="M9.01857 2.5C7.62667 2.49856 6.26819 2.92612 5.12839 3.7244C3.9886 4.52267 3.12295 5.65278 2.6495 6.96064C2.17605 8.26851 2.11784 9.69045 2.48281 11.0326C2.84778 12.3747 3.61816 13.5717 4.68891 14.4603H2.77351V15.5H6.93688V11.34H5.89639V14.0549C4.86194 13.3824 4.0727 12.3939 3.64631 11.2367C3.21992 10.0795 3.17921 8.81556 3.53025 7.63335C3.88129 6.45115 4.60529 5.41395 5.59432 4.67637C6.58335 3.93878 7.78444 3.54033 9.01857 3.54037V2.5ZM11.1003 3.01983V7.17989H12.1415V4.46559C12.8436 4.92312 13.4366 5.52914 13.8785 6.24083C14.3205 6.95252 14.6007 7.7525 14.6992 8.58426H15.7521C15.576 6.81965 14.7129 5.19487 13.3489 4.06019H15.2636V3.01983H11.1003ZM11.3866 16.8117C11.349 16.8117 11.3118 16.8043 11.277 16.7899C11.2423 16.7756 11.2107 16.7545 11.1841 16.7279C11.1575 16.7013 11.1364 16.6698 11.1221 16.6351C11.1077 16.6004 11.1003 16.5632 11.1003 16.5256V10.2311C11.1003 10.1814 11.1132 10.1325 11.1379 10.0893C11.1626 10.0462 11.1981 10.0101 11.2409 9.98485C11.2837 9.95955 11.3324 9.94584 11.3822 9.94507C11.4319 9.9443 11.481 9.95651 11.5246 9.98047L17.2515 13.1277C17.2964 13.1524 17.3338 13.1887 17.3599 13.2328C17.3859 13.2769 17.3997 13.3272 17.3997 13.3784C17.3997 13.4296 17.3859 13.4798 17.3599 13.5239C17.3338 13.568 17.2964 13.6043 17.2515 13.629L11.5246 16.7762C11.4824 16.7995 11.4349 16.8117 11.3866 16.8117Z"
|
17 |
+
fill="currentColor"
|
18 |
+
/>
|
19 |
+
</svg>
|
20 |
+
{/if}
|
src/lib/components/inference-playground/checkpoints-menu.svelte
CHANGED
@@ -1,20 +1,27 @@
|
|
1 |
<script lang="ts">
|
|
|
2 |
import { checkpoints } from "$lib/state/checkpoints.svelte";
|
3 |
import { session } from "$lib/state/session.svelte.js";
|
|
|
4 |
import { Popover } from "melt/builders";
|
5 |
import { Tooltip } from "melt/components";
|
6 |
import { fly } from "svelte/transition";
|
|
|
7 |
import IconHistory from "~icons/carbon/recently-viewed";
|
8 |
-
import IconDelete from "~icons/carbon/trash-can";
|
9 |
import IconStar from "~icons/carbon/star";
|
10 |
import IconStarFilled from "~icons/carbon/star-filled";
|
11 |
-
import
|
12 |
|
13 |
const popover = new Popover({
|
14 |
floatingConfig: {
|
15 |
offset: { crossAxis: -12 },
|
16 |
},
|
|
|
|
|
|
|
|
|
17 |
});
|
|
|
18 |
|
19 |
const projCheckpoints = $derived(checkpoints.for(session.project.id));
|
20 |
</script>
|
@@ -26,10 +33,16 @@
|
|
26 |
{/if}
|
27 |
</button>
|
28 |
|
29 |
-
<
|
30 |
-
|
|
|
|
|
31 |
{...popover.content}
|
32 |
>
|
|
|
|
|
|
|
|
|
33 |
<div class="max-h-120 w-80 overflow-x-clip overflow-y-auto p-3 pb-1">
|
34 |
<div class="mb-2 flex items-center justify-between px-1">
|
35 |
<h3 class="text-sm font-medium dark:text-white">Checkpoints</h3>
|
@@ -114,7 +127,10 @@
|
|
114 |
{...tooltip.content}
|
115 |
transition:fly={{ x: -2 }}
|
116 |
>
|
117 |
-
<div
|
|
|
|
|
|
|
118 |
{#each state.conversations as conversation, i}
|
119 |
{@const msgs = conversation.messages}
|
120 |
{@const sliced = msgs.slice(0, 4)}
|
@@ -129,8 +145,7 @@
|
|
129 |
temp: {conversation.config.temperature}
|
130 |
| max tokens: {conversation.config.max_tokens}
|
131 |
</p>
|
132 |
-
{#each sliced as msg,
|
133 |
-
{@const isLast = i === sliced.length - 1}
|
134 |
<div class="flex flex-col gap-1 p-2">
|
135 |
<p class="font-mono text-xs font-medium text-gray-400 uppercase">{msg.role}</p>
|
136 |
{#if msg.content?.trim()}
|
@@ -155,4 +170,4 @@
|
|
155 |
</div>
|
156 |
{/each}
|
157 |
</div>
|
158 |
-
</
|
|
|
1 |
<script lang="ts">
|
2 |
+
import { clickOutside } from "$lib/actions/click-outside.js";
|
3 |
import { checkpoints } from "$lib/state/checkpoints.svelte";
|
4 |
import { session } from "$lib/state/session.svelte.js";
|
5 |
+
import { iterate } from "$lib/utils/array.js";
|
6 |
import { Popover } from "melt/builders";
|
7 |
import { Tooltip } from "melt/components";
|
8 |
import { fly } from "svelte/transition";
|
9 |
+
import IconCompare from "~icons/carbon/compare";
|
10 |
import IconHistory from "~icons/carbon/recently-viewed";
|
|
|
11 |
import IconStar from "~icons/carbon/star";
|
12 |
import IconStarFilled from "~icons/carbon/star-filled";
|
13 |
+
import IconDelete from "~icons/carbon/trash-can";
|
14 |
|
15 |
const popover = new Popover({
|
16 |
floatingConfig: {
|
17 |
offset: { crossAxis: -12 },
|
18 |
},
|
19 |
+
onOpenChange: open => {
|
20 |
+
if (open) dialog?.showModal();
|
21 |
+
else dialog?.close();
|
22 |
+
},
|
23 |
});
|
24 |
+
let dialog = $state<HTMLDialogElement>();
|
25 |
|
26 |
const projCheckpoints = $derived(checkpoints.for(session.project.id));
|
27 |
</script>
|
|
|
33 |
{/if}
|
34 |
</button>
|
35 |
|
36 |
+
<dialog
|
37 |
+
bind:this={dialog}
|
38 |
+
class="mb-2 !overflow-visible rounded-xl border border-gray-200 bg-white shadow-lg dark:border-gray-700 dark:bg-gray-800"
|
39 |
+
use:clickOutside={() => (popover.open = false)}
|
40 |
{...popover.content}
|
41 |
>
|
42 |
+
<div
|
43 |
+
class="size-4 translate-x-3 rounded-tl border-t border-l border-gray-200 dark:border-gray-700"
|
44 |
+
{...popover.arrow}
|
45 |
+
></div>
|
46 |
<div class="max-h-120 w-80 overflow-x-clip overflow-y-auto p-3 pb-1">
|
47 |
<div class="mb-2 flex items-center justify-between px-1">
|
48 |
<h3 class="text-sm font-medium dark:text-white">Checkpoints</h3>
|
|
|
127 |
{...tooltip.content}
|
128 |
transition:fly={{ x: -2 }}
|
129 |
>
|
130 |
+
<div
|
131 |
+
class="size-4 rounded-tl border-t border-l border-gray-200 dark:border-gray-700"
|
132 |
+
{...tooltip.arrow}
|
133 |
+
></div>
|
134 |
{#each state.conversations as conversation, i}
|
135 |
{@const msgs = conversation.messages}
|
136 |
{@const sliced = msgs.slice(0, 4)}
|
|
|
145 |
temp: {conversation.config.temperature}
|
146 |
| max tokens: {conversation.config.max_tokens}
|
147 |
</p>
|
148 |
+
{#each iterate(sliced) as [msg, isLast]}
|
|
|
149 |
<div class="flex flex-col gap-1 p-2">
|
150 |
<p class="font-mono text-xs font-medium text-gray-400 uppercase">{msg.role}</p>
|
151 |
{#if msg.content?.trim()}
|
|
|
170 |
</div>
|
171 |
{/each}
|
172 |
</div>
|
173 |
+
</dialog>
|
src/lib/components/inference-playground/conversation.svelte
CHANGED
@@ -7,6 +7,8 @@
|
|
7 |
import IconPlus from "~icons/carbon/add";
|
8 |
import CodeSnippets from "./code-snippets.svelte";
|
9 |
import Message from "./message.svelte";
|
|
|
|
|
10 |
|
11 |
interface Props {
|
12 |
conversation: Conversation;
|
@@ -52,6 +54,19 @@
|
|
52 |
function deleteMessage(idx: number) {
|
53 |
conversation.messages = conversation.messages.slice(0, idx);
|
54 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
55 |
</script>
|
56 |
|
57 |
<div
|
@@ -61,13 +76,15 @@
|
|
61 |
id="test-this"
|
62 |
>
|
63 |
{#if !viewCode}
|
64 |
-
{#each conversation.messages as _msg, idx}
|
65 |
<Message
|
66 |
bind:message={conversation.messages[idx]!}
|
67 |
{conversation}
|
68 |
autofocus={idx === conversation.messages.length - 1}
|
69 |
{loading}
|
70 |
onDelete={() => deleteMessage(idx)}
|
|
|
|
|
71 |
/>
|
72 |
{/each}
|
73 |
|
|
|
7 |
import IconPlus from "~icons/carbon/add";
|
8 |
import CodeSnippets from "./code-snippets.svelte";
|
9 |
import Message from "./message.svelte";
|
10 |
+
import { iterate } from "$lib/utils/array.js";
|
11 |
+
import { session } from "$lib/state/session.svelte";
|
12 |
|
13 |
interface Props {
|
14 |
conversation: Conversation;
|
|
|
54 |
function deleteMessage(idx: number) {
|
55 |
conversation.messages = conversation.messages.slice(0, idx);
|
56 |
}
|
57 |
+
|
58 |
+
function regenMessage(idx: number) {
|
59 |
+
const msg = conversation.messages[idx];
|
60 |
+
if (!msg) return;
|
61 |
+
if (msg.role === "user") {
|
62 |
+
conversation.messages = conversation.messages.slice(0, idx + 1);
|
63 |
+
} else {
|
64 |
+
conversation.messages = conversation.messages.slice(0, idx);
|
65 |
+
}
|
66 |
+
|
67 |
+
session.stopGenerating();
|
68 |
+
session.run(conversation);
|
69 |
+
}
|
70 |
</script>
|
71 |
|
72 |
<div
|
|
|
76 |
id="test-this"
|
77 |
>
|
78 |
{#if !viewCode}
|
79 |
+
{#each iterate(conversation.messages) as [_msg, { isLast }], idx}
|
80 |
<Message
|
81 |
bind:message={conversation.messages[idx]!}
|
82 |
{conversation}
|
83 |
autofocus={idx === conversation.messages.length - 1}
|
84 |
{loading}
|
85 |
onDelete={() => deleteMessage(idx)}
|
86 |
+
onRegen={() => regenMessage(idx)}
|
87 |
+
{isLast}
|
88 |
/>
|
89 |
{/each}
|
90 |
|
src/lib/components/inference-playground/custom-model-config.svelte
CHANGED
@@ -21,10 +21,10 @@
|
|
21 |
|
22 |
<script lang="ts">
|
23 |
import { autofocus } from "$lib/actions/autofocus.js";
|
24 |
-
|
25 |
import { clickOutside } from "$lib/actions/click-outside.js";
|
26 |
import { models } from "$lib/state/models.svelte";
|
27 |
-
import { type Conversation, type CustomModel } from "$lib/types.js";
|
28 |
import type { HTMLFormAttributes } from "svelte/elements";
|
29 |
import { fade, scale } from "svelte/transition";
|
30 |
import IconCross from "~icons/carbon/close";
|
@@ -34,6 +34,8 @@
|
|
34 |
import Tooltip from "../tooltip.svelte";
|
35 |
import { createFieldValidation } from "$lib/utils/form.svelte.js";
|
36 |
import { isValidURL } from "$lib/utils/url.js";
|
|
|
|
|
37 |
|
38 |
let dialog: HTMLDialogElement | undefined = $state();
|
39 |
const exists = $derived(!!models.custom.find(m => m._id === model?._id));
|
@@ -187,7 +189,7 @@
|
|
187 |
<p class="text-xs text-red-300">{endpointValidation.msg}</p>
|
188 |
</label>
|
189 |
<label class="flex flex-col gap-2">
|
190 |
-
<p class="block text-sm font-medium text-gray-900 dark:text-white">Access
|
191 |
<input
|
192 |
bind:value={model.accessToken}
|
193 |
placeholder="XXXXXXXXXXXXXXXXXXXX"
|
@@ -197,6 +199,50 @@
|
|
197 |
<p class="text-sm text-gray-500">Stored locally - not sent to our server</p>
|
198 |
</label>
|
199 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
200 |
{#if message}
|
201 |
<div
|
202 |
class={[
|
|
|
21 |
|
22 |
<script lang="ts">
|
23 |
import { autofocus } from "$lib/actions/autofocus.js";
|
24 |
+
import IconCaret from "~icons/carbon/chevron-down";
|
25 |
import { clickOutside } from "$lib/actions/click-outside.js";
|
26 |
import { models } from "$lib/state/models.svelte";
|
27 |
+
import { PipelineTag, pipelineTagLabel, type Conversation, type CustomModel } from "$lib/types.js";
|
28 |
import type { HTMLFormAttributes } from "svelte/elements";
|
29 |
import { fade, scale } from "svelte/transition";
|
30 |
import IconCross from "~icons/carbon/close";
|
|
|
34 |
import Tooltip from "../tooltip.svelte";
|
35 |
import { createFieldValidation } from "$lib/utils/form.svelte.js";
|
36 |
import { isValidURL } from "$lib/utils/url.js";
|
37 |
+
import { Select } from "melt/components";
|
38 |
+
import { keys } from "$lib/utils/object.js";
|
39 |
|
40 |
let dialog: HTMLDialogElement | undefined = $state();
|
41 |
const exists = $derived(!!models.custom.find(m => m._id === model?._id));
|
|
|
189 |
<p class="text-xs text-red-300">{endpointValidation.msg}</p>
|
190 |
</label>
|
191 |
<label class="flex flex-col gap-2">
|
192 |
+
<p class="block text-sm font-medium text-gray-900 dark:text-white">Access token</p>
|
193 |
<input
|
194 |
bind:value={model.accessToken}
|
195 |
placeholder="XXXXXXXXXXXXXXXXXXXX"
|
|
|
199 |
<p class="text-sm text-gray-500">Stored locally - not sent to our server</p>
|
200 |
</label>
|
201 |
|
202 |
+
<div class="flex flex-col gap-2">
|
203 |
+
<Select bind:value={model.pipeline_tag}>
|
204 |
+
{#snippet children(select)}
|
205 |
+
<label for={select.ids.trigger} class="block text-sm font-medium text-gray-900 dark:text-white">
|
206 |
+
Supported task
|
207 |
+
</label>
|
208 |
+
|
209 |
+
<button
|
210 |
+
{...select.trigger}
|
211 |
+
class={[
|
212 |
+
"relative flex grow items-center justify-between gap-6 overflow-hidden rounded-lg border bg-gray-100/80 px-3 py-1.5 leading-tight whitespace-nowrap shadow-sm",
|
213 |
+
"hover:brightness-95 dark:border-gray-700 dark:bg-gray-800 dark:hover:brightness-110",
|
214 |
+
]}
|
215 |
+
>
|
216 |
+
<div class="flex items-center gap-1 text-sm">
|
217 |
+
{pipelineTagLabel[model?.pipeline_tag ?? PipelineTag.TextGeneration]}
|
218 |
+
</div>
|
219 |
+
<div
|
220 |
+
class="absolute right-2 grid size-4 flex-none place-items-center rounded-sm bg-gray-100 text-xs dark:bg-gray-600"
|
221 |
+
>
|
222 |
+
<IconCaret />
|
223 |
+
</div>
|
224 |
+
</button>
|
225 |
+
|
226 |
+
<div {...select.content} class="rounded-lg border bg-gray-100 dark:border-gray-700 dark:bg-gray-800">
|
227 |
+
{#each keys(PipelineTag) as key (key)}
|
228 |
+
{@const tag = PipelineTag[key]}
|
229 |
+
{@const label = pipelineTagLabel[tag]}
|
230 |
+
{@const option = select.getOption(tag)}
|
231 |
+
<div {...option} class="group block w-full p-1 text-sm dark:text-white">
|
232 |
+
<div
|
233 |
+
class="rounded-md py-1.5 pr-1 pl-2 group-data-[highlighted]:bg-gray-200 dark:group-data-[highlighted]:bg-gray-700"
|
234 |
+
>
|
235 |
+
<span>
|
236 |
+
{label}
|
237 |
+
</span>
|
238 |
+
</div>
|
239 |
+
</div>
|
240 |
+
{/each}
|
241 |
+
</div>
|
242 |
+
{/snippet}
|
243 |
+
</Select>
|
244 |
+
</div>
|
245 |
+
|
246 |
{#if message}
|
247 |
<div
|
248 |
class={[
|
src/lib/components/inference-playground/message.svelte
CHANGED
@@ -8,6 +8,7 @@
|
|
8 |
import { fade } from "svelte/transition";
|
9 |
import IconImage from "~icons/carbon/image-reference";
|
10 |
import IconMaximize from "~icons/carbon/maximize";
|
|
|
11 |
import ImgPreview from "./img-preview.svelte";
|
12 |
|
13 |
type Props = {
|
@@ -16,9 +17,11 @@
|
|
16 |
loading?: boolean;
|
17 |
autofocus?: boolean;
|
18 |
onDelete?: () => void;
|
|
|
|
|
19 |
};
|
20 |
|
21 |
-
let { message = $bindable(), conversation, loading, autofocus, onDelete }: Props = $props();
|
22 |
|
23 |
let element = $state<HTMLTextAreaElement>();
|
24 |
new TextareaAutosize({
|
@@ -48,6 +51,11 @@
|
|
48 |
});
|
49 |
|
50 |
let previewImg = $state<string>();
|
|
|
|
|
|
|
|
|
|
|
51 |
</script>
|
52 |
|
53 |
<div
|
@@ -104,6 +112,25 @@
|
|
104 |
</Tooltip>
|
105 |
{/if}
|
106 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
107 |
<Tooltip>
|
108 |
{#snippet trigger(tooltip)}
|
109 |
<button
|
|
|
8 |
import { fade } from "svelte/transition";
|
9 |
import IconImage from "~icons/carbon/image-reference";
|
10 |
import IconMaximize from "~icons/carbon/maximize";
|
11 |
+
import IconCustom from "../icon-custom.svelte";
|
12 |
import ImgPreview from "./img-preview.svelte";
|
13 |
|
14 |
type Props = {
|
|
|
17 |
loading?: boolean;
|
18 |
autofocus?: boolean;
|
19 |
onDelete?: () => void;
|
20 |
+
onRegen?: () => void;
|
21 |
+
isLast?: boolean;
|
22 |
};
|
23 |
|
24 |
+
let { message = $bindable(), conversation, loading, autofocus, onDelete, onRegen, isLast }: Props = $props();
|
25 |
|
26 |
let element = $state<HTMLTextAreaElement>();
|
27 |
new TextareaAutosize({
|
|
|
51 |
});
|
52 |
|
53 |
let previewImg = $state<string>();
|
54 |
+
|
55 |
+
const regenLabel = $derived.by(() => {
|
56 |
+
if (message.role === "assistant") return "Regenerate";
|
57 |
+
return isLast ? "Generate from here" : "Regenerate from here";
|
58 |
+
});
|
59 |
</script>
|
60 |
|
61 |
<div
|
|
|
112 |
</Tooltip>
|
113 |
{/if}
|
114 |
|
115 |
+
<Tooltip>
|
116 |
+
{#snippet trigger(tooltip)}
|
117 |
+
<button
|
118 |
+
tabindex="0"
|
119 |
+
onclick={onRegen}
|
120 |
+
type="button"
|
121 |
+
class="mt-1.5 -mr-2 grid size-8 place-items-center rounded-lg border border-gray-200 bg-white text-xs font-medium text-gray-900
|
122 |
+
group-focus-within/message:visible group-hover/message:visible hover:bg-gray-100
|
123 |
+
hover:text-blue-700 focus:z-10 focus:ring-4
|
124 |
+
focus:ring-gray-100 focus:outline-hidden sm:invisible dark:border-gray-600 dark:bg-gray-800
|
125 |
+
dark:text-gray-400 dark:hover:bg-gray-700 dark:hover:text-white dark:focus:ring-gray-700"
|
126 |
+
{...tooltip.trigger}
|
127 |
+
>
|
128 |
+
<IconCustom icon="regen" />
|
129 |
+
</button>
|
130 |
+
{/snippet}
|
131 |
+
{regenLabel}
|
132 |
+
</Tooltip>
|
133 |
+
|
134 |
<Tooltip>
|
135 |
{#snippet trigger(tooltip)}
|
136 |
<button
|
src/lib/components/inference-playground/playground.svelte
CHANGED
@@ -1,25 +1,16 @@
|
|
1 |
<script lang="ts">
|
2 |
import { observe, observed, ObservedElements } from "$lib/actions/observe.svelte.js";
|
3 |
-
import { AbortManager } from "$lib/spells/abort-manager.svelte.js";
|
4 |
import { models } from "$lib/state/models.svelte.js";
|
5 |
import { session } from "$lib/state/session.svelte.js";
|
6 |
import { token } from "$lib/state/token.svelte.js";
|
7 |
-
import { type ConversationMessage, type Model, type Project } from "$lib/types.js";
|
8 |
-
import {
|
|
|
9 |
import { watch } from "runed";
|
10 |
import typia from "typia";
|
11 |
-
import IconExternal from "~icons/carbon/arrow-up-right";
|
12 |
-
import IconCode from "~icons/carbon/code";
|
13 |
-
import IconCompare from "~icons/carbon/compare";
|
14 |
-
import IconInfo from "~icons/carbon/information";
|
15 |
-
import IconSettings from "~icons/carbon/settings";
|
16 |
-
import IconShare from "~icons/carbon/share";
|
17 |
-
import IconWaterfall from "~icons/carbon/chart-waterfall";
|
18 |
import { default as IconDelete } from "~icons/carbon/trash-can";
|
19 |
-
import { showQuotaModal } from "../quota-modal.svelte";
|
20 |
import { showShareModal } from "../share-modal.svelte";
|
21 |
import Toaster from "../toaster.svelte";
|
22 |
-
import { addToast } from "../toaster.svelte.js";
|
23 |
import Tooltip from "../tooltip.svelte";
|
24 |
import PlaygroundConversationHeader from "./conversation-header.svelte";
|
25 |
import PlaygroundConversation from "./conversation.svelte";
|
@@ -28,32 +19,39 @@
|
|
28 |
import ModelSelectorModal from "./model-selector-modal.svelte";
|
29 |
import ModelSelector from "./model-selector.svelte";
|
30 |
import ProjectSelect from "./project-select.svelte";
|
31 |
-
import { getTokens,
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
32 |
|
33 |
const startMessageUser: ConversationMessage = { role: "user", content: "" };
|
34 |
|
35 |
let viewCode = $state(false);
|
36 |
let viewSettings = $state(false);
|
37 |
-
|
38 |
|
39 |
-
const abortManager = new AbortManager();
|
40 |
let selectCompareModelOpen = $state(false);
|
41 |
|
42 |
-
interface GenerationStatistics {
|
43 |
-
latency: number;
|
44 |
-
generatedTokensCount: number;
|
45 |
-
}
|
46 |
-
let generationStats = $state(
|
47 |
-
session.project.conversations.map(_ => ({ latency: 0, generatedTokensCount: 0 })) as
|
48 |
-
| [GenerationStatistics]
|
49 |
-
| [GenerationStatistics, GenerationStatistics]
|
50 |
-
);
|
51 |
-
|
52 |
watch(
|
53 |
() => $state.snapshot(session.project),
|
54 |
() => {
|
55 |
session.project.conversations.forEach(async (c, i) => {
|
56 |
-
generationStats[i] = {
|
|
|
|
|
|
|
|
|
57 |
});
|
58 |
}
|
59 |
);
|
@@ -74,99 +72,15 @@
|
|
74 |
if (typia.is<Project["conversations"]>(c)) session.project.conversations = c;
|
75 |
}
|
76 |
|
77 |
-
async function runInference(conversationIdx: number) {
|
78 |
-
const conversation = session.project.conversations[conversationIdx];
|
79 |
-
if (!conversation) return;
|
80 |
-
|
81 |
-
const startTime = performance.now();
|
82 |
-
|
83 |
-
if (conversation.streaming) {
|
84 |
-
let addedMessage = false;
|
85 |
-
let streamingMessage = $state({ role: "assistant", content: "" });
|
86 |
-
|
87 |
-
await handleStreamingResponse(
|
88 |
-
conversation,
|
89 |
-
content => {
|
90 |
-
if (!streamingMessage) return;
|
91 |
-
streamingMessage.content = content;
|
92 |
-
if (!addedMessage) {
|
93 |
-
conversation.messages = [...conversation.messages, streamingMessage];
|
94 |
-
addedMessage = true;
|
95 |
-
}
|
96 |
-
},
|
97 |
-
abortManager.createController()
|
98 |
-
);
|
99 |
-
} else {
|
100 |
-
const { message: newMessage, completion_tokens: newTokensCount } = await handleNonStreamingResponse(conversation);
|
101 |
-
conversation.messages = [...conversation.messages, newMessage];
|
102 |
-
const c = generationStats[conversationIdx];
|
103 |
-
if (c) c.generatedTokensCount += newTokensCount;
|
104 |
-
}
|
105 |
-
|
106 |
-
const endTime = performance.now();
|
107 |
-
const c = generationStats[conversationIdx];
|
108 |
-
if (c) c.latency = Math.round(endTime - startTime);
|
109 |
-
}
|
110 |
-
|
111 |
-
async function submit() {
|
112 |
-
if (!token.value) {
|
113 |
-
token.showModal = true;
|
114 |
-
return;
|
115 |
-
}
|
116 |
-
|
117 |
-
for (const [idx, conversation] of session.project.conversations.entries()) {
|
118 |
-
if (conversation.messages.at(-1)?.role !== "assistant") continue;
|
119 |
-
let prefix = "";
|
120 |
-
if (session.project.conversations.length === 2) {
|
121 |
-
prefix = `Error on ${idx === 0 ? "left" : "right"} conversation. `;
|
122 |
-
}
|
123 |
-
return addToast({
|
124 |
-
title: "Failed to run inference",
|
125 |
-
description: `${prefix}Messages must alternate between user/assistant roles.`,
|
126 |
-
variant: "error",
|
127 |
-
});
|
128 |
-
}
|
129 |
-
|
130 |
-
(document.activeElement as HTMLElement).blur();
|
131 |
-
loading = true;
|
132 |
-
|
133 |
-
try {
|
134 |
-
const promises = session.project.conversations.map((_, idx) => runInference(idx));
|
135 |
-
await Promise.all(promises);
|
136 |
-
} catch (error) {
|
137 |
-
for (const conversation of session.project.conversations) {
|
138 |
-
if (conversation.messages.at(-1)?.role === "assistant" && !conversation.messages.at(-1)?.content?.trim()) {
|
139 |
-
conversation.messages.pop();
|
140 |
-
conversation.messages = [...conversation.messages];
|
141 |
-
}
|
142 |
-
session.$ = session.$;
|
143 |
-
}
|
144 |
-
|
145 |
-
if (error instanceof Error) {
|
146 |
-
const msg = error.message;
|
147 |
-
if (msg.toLowerCase().includes("montly") || msg.toLowerCase().includes("pro")) {
|
148 |
-
showQuotaModal();
|
149 |
-
}
|
150 |
-
|
151 |
-
if (error.message.includes("token seems invalid")) {
|
152 |
-
token.reset();
|
153 |
-
}
|
154 |
-
|
155 |
-
if (error.name !== "AbortError") {
|
156 |
-
addToast({ title: "Error", description: error.message, variant: "error" });
|
157 |
-
}
|
158 |
-
} else {
|
159 |
-
addToast({ title: "Error", description: "An unknown error occurred", variant: "error" });
|
160 |
-
}
|
161 |
-
} finally {
|
162 |
-
loading = false;
|
163 |
-
abortManager.clear();
|
164 |
-
}
|
165 |
-
}
|
166 |
-
|
167 |
function onKeydown(event: KeyboardEvent) {
|
168 |
if ((event.ctrlKey || event.metaKey) && event.key === "Enter") {
|
169 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
170 |
}
|
171 |
}
|
172 |
|
@@ -191,14 +105,14 @@
|
|
191 |
}
|
192 |
const newConversation = { ...JSON.parse(JSON.stringify(session.project.conversations[0])), model };
|
193 |
session.project.conversations = [...session.project.conversations, newConversation];
|
194 |
-
generationStats = [generationStats[0], { latency: 0, generatedTokensCount: 0 }];
|
195 |
}
|
196 |
|
197 |
function removeCompareModal(conversationIdx: number) {
|
198 |
session.project.conversations.splice(conversationIdx, 1)[0];
|
199 |
session.$ = session.$;
|
200 |
-
generationStats.splice(conversationIdx, 1)[0];
|
201 |
-
generationStats = generationStats;
|
202 |
}
|
203 |
</script>
|
204 |
|
@@ -307,8 +221,7 @@
|
|
307 |
<div
|
308 |
class="pointer-events-none absolute inset-0 flex flex-1 shrink-0 items-center justify-around gap-x-8 text-center text-sm text-gray-500 max-xl:hidden"
|
309 |
>
|
310 |
-
{#each generationStats as { latency, generatedTokensCount },
|
311 |
-
{@const isLast = index === generationStats.length - 1}
|
312 |
{@const baLeft = observed["bottom-actions"].rect.left}
|
313 |
{@const tceRight = observed["token-count-end"].offset.right}
|
314 |
<span
|
@@ -328,39 +241,115 @@
|
|
328 |
<IconCode />
|
329 |
{!viewCode ? "View Code" : "Hide Code"}
|
330 |
</button>
|
331 |
-
<
|
332 |
-
|
333 |
-
|
334 |
-
|
335 |
-
|
336 |
-
|
337 |
-
|
338 |
-
|
339 |
-
|
340 |
-
|
341 |
-
|
342 |
-
|
343 |
-
|
344 |
-
|
345 |
-
|
346 |
-
|
347 |
-
|
348 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
349 |
</span>
|
350 |
-
|
351 |
-
|
352 |
-
|
353 |
-
|
354 |
-
|
355 |
-
|
356 |
-
|
357 |
-
|
358 |
-
|
359 |
-
|
360 |
-
|
361 |
>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
362 |
{/if}
|
363 |
-
</
|
364 |
</div>
|
365 |
</div>
|
366 |
</div>
|
@@ -370,7 +359,7 @@
|
|
370 |
<div class={[viewSettings && "max-md:fixed max-md:inset-0 max-md:bottom-20 max-md:backdrop-blur-lg"]}>
|
371 |
<div
|
372 |
class={[
|
373 |
-
"flex h-full flex-col
|
374 |
viewSettings ? "max-md:fixed" : "max-md:hidden",
|
375 |
]}
|
376 |
>
|
@@ -387,15 +376,17 @@
|
|
387 |
<IconCompare />
|
388 |
Compare
|
389 |
</button>
|
390 |
-
|
391 |
-
|
392 |
-
.project.conversations[0]?.
|
393 |
-
|
394 |
-
|
395 |
-
|
396 |
-
|
397 |
-
|
398 |
-
|
|
|
|
|
399 |
</div>
|
400 |
</div>
|
401 |
|
|
|
1 |
<script lang="ts">
|
2 |
import { observe, observed, ObservedElements } from "$lib/actions/observe.svelte.js";
|
|
|
3 |
import { models } from "$lib/state/models.svelte.js";
|
4 |
import { session } from "$lib/state/session.svelte.js";
|
5 |
import { token } from "$lib/state/token.svelte.js";
|
6 |
+
import { isConversationWithHFModel, type ConversationMessage, type Model, type Project } from "$lib/types.js";
|
7 |
+
import { cmdOrCtrl, optOrAlt } from "$lib/utils/platform.js";
|
8 |
+
import { Popover } from "melt/components";
|
9 |
import { watch } from "runed";
|
10 |
import typia from "typia";
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
11 |
import { default as IconDelete } from "~icons/carbon/trash-can";
|
|
|
12 |
import { showShareModal } from "../share-modal.svelte";
|
13 |
import Toaster from "../toaster.svelte";
|
|
|
14 |
import Tooltip from "../tooltip.svelte";
|
15 |
import PlaygroundConversationHeader from "./conversation-header.svelte";
|
16 |
import PlaygroundConversation from "./conversation.svelte";
|
|
|
19 |
import ModelSelectorModal from "./model-selector-modal.svelte";
|
20 |
import ModelSelector from "./model-selector.svelte";
|
21 |
import ProjectSelect from "./project-select.svelte";
|
22 |
+
import { getTokens, isSystemPromptSupported } from "./utils.js";
|
23 |
+
|
24 |
+
import { iterate } from "$lib/utils/array.js";
|
25 |
+
import IconChatLeft from "~icons/carbon/align-box-bottom-left";
|
26 |
+
import IconChatRight from "~icons/carbon/align-box-bottom-right";
|
27 |
+
import IconExternal from "~icons/carbon/arrow-up-right";
|
28 |
+
import IconWaterfall from "~icons/carbon/chart-waterfall";
|
29 |
+
import IconChevronDown from "~icons/carbon/chevron-down";
|
30 |
+
import IconCode from "~icons/carbon/code";
|
31 |
+
import IconCompare from "~icons/carbon/compare";
|
32 |
+
import IconInfo from "~icons/carbon/information";
|
33 |
+
import IconSettings from "~icons/carbon/settings";
|
34 |
+
import IconShare from "~icons/carbon/share";
|
35 |
+
|
36 |
+
const multiple = $derived(session.project.conversations.length > 1);
|
37 |
|
38 |
const startMessageUser: ConversationMessage = { role: "user", content: "" };
|
39 |
|
40 |
let viewCode = $state(false);
|
41 |
let viewSettings = $state(false);
|
42 |
+
const loading = $derived(session.generating);
|
43 |
|
|
|
44 |
let selectCompareModelOpen = $state(false);
|
45 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
46 |
watch(
|
47 |
() => $state.snapshot(session.project),
|
48 |
() => {
|
49 |
session.project.conversations.forEach(async (c, i) => {
|
50 |
+
session.generationStats[i] = {
|
51 |
+
latency: 0,
|
52 |
+
...session.generationStats[i],
|
53 |
+
generatedTokensCount: await getTokens(c),
|
54 |
+
};
|
55 |
});
|
56 |
}
|
57 |
);
|
|
|
72 |
if (typia.is<Project["conversations"]>(c)) session.project.conversations = c;
|
73 |
}
|
74 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
75 |
function onKeydown(event: KeyboardEvent) {
|
76 |
if ((event.ctrlKey || event.metaKey) && event.key === "Enter") {
|
77 |
+
session.run();
|
78 |
+
}
|
79 |
+
if ((event.ctrlKey || event.metaKey) && event.altKey && event.key === "l") {
|
80 |
+
session.run("left");
|
81 |
+
}
|
82 |
+
if ((event.ctrlKey || event.metaKey) && event.altKey && event.key === "r") {
|
83 |
+
session.run("right");
|
84 |
}
|
85 |
}
|
86 |
|
|
|
105 |
}
|
106 |
const newConversation = { ...JSON.parse(JSON.stringify(session.project.conversations[0])), model };
|
107 |
session.project.conversations = [...session.project.conversations, newConversation];
|
108 |
+
session.generationStats = [session.generationStats[0], { latency: 0, generatedTokensCount: 0 }];
|
109 |
}
|
110 |
|
111 |
function removeCompareModal(conversationIdx: number) {
|
112 |
session.project.conversations.splice(conversationIdx, 1)[0];
|
113 |
session.$ = session.$;
|
114 |
+
session.generationStats.splice(conversationIdx, 1)[0];
|
115 |
+
session.generationStats = session.generationStats;
|
116 |
}
|
117 |
</script>
|
118 |
|
|
|
221 |
<div
|
222 |
class="pointer-events-none absolute inset-0 flex flex-1 shrink-0 items-center justify-around gap-x-8 text-center text-sm text-gray-500 max-xl:hidden"
|
223 |
>
|
224 |
+
{#each iterate(session.generationStats) as [{ latency, generatedTokensCount }, isLast]}
|
|
|
225 |
{@const baLeft = observed["bottom-actions"].rect.left}
|
226 |
{@const tceRight = observed["token-count-end"].offset.right}
|
227 |
<span
|
|
|
241 |
<IconCode />
|
242 |
{!viewCode ? "View Code" : "Hide Code"}
|
243 |
</button>
|
244 |
+
<div class="flex">
|
245 |
+
<button
|
246 |
+
onclick={() => {
|
247 |
+
viewCode = false;
|
248 |
+
session.runOrStop();
|
249 |
+
}}
|
250 |
+
type="button"
|
251 |
+
class={[
|
252 |
+
"flex h-[39px] items-center justify-center gap-2 rounded-l-lg px-3.5 py-2.5 text-sm font-medium text-white focus:ring-4 focus:ring-gray-300 focus:outline-hidden dark:focus:ring-gray-700",
|
253 |
+
multiple ? "rounded-l-lg" : "rounded-lg",
|
254 |
+
loading && "bg-red-900 hover:bg-red-800 dark:bg-red-600 dark:hover:bg-red-700",
|
255 |
+
!loading && "bg-black hover:bg-gray-900 dark:bg-blue-600 dark:hover:bg-blue-700",
|
256 |
+
]}
|
257 |
+
>
|
258 |
+
{#if loading}
|
259 |
+
<div class="flex flex-none items-center gap-[3px]">
|
260 |
+
<span class="mr-2">
|
261 |
+
{#if session.project.conversations[0]?.streaming || session.project.conversations[1]?.streaming}
|
262 |
+
Stop
|
263 |
+
{:else}
|
264 |
+
Cancel
|
265 |
+
{/if}
|
266 |
+
</span>
|
267 |
+
{#each { length: 3 } as _, i}
|
268 |
+
<div
|
269 |
+
class="h-1 w-1 flex-none animate-bounce rounded-full bg-gray-500 dark:bg-gray-100"
|
270 |
+
style="animation-delay: {(i + 1) * 0.25}s;"
|
271 |
+
></div>
|
272 |
+
{/each}
|
273 |
+
</div>
|
274 |
+
{:else}
|
275 |
+
{multiple ? "Run all" : "Run"}
|
276 |
+
<span
|
277 |
+
class="inline-flex gap-0.5 rounded-sm border border-white/20 bg-white/10 px-0.5 text-xs text-white/70"
|
278 |
+
>
|
279 |
+
{cmdOrCtrl}<span class="translate-y-px">↵</span>
|
280 |
</span>
|
281 |
+
{/if}
|
282 |
+
</button>
|
283 |
+
{#if multiple}
|
284 |
+
<div class="w-[1px] bg-gray-800" aria-hidden="true"></div>
|
285 |
+
<Popover
|
286 |
+
open
|
287 |
+
floatingConfig={{
|
288 |
+
computePosition: {
|
289 |
+
placement: "top-end",
|
290 |
+
},
|
291 |
+
}}
|
292 |
>
|
293 |
+
{#snippet children(popover)}
|
294 |
+
<button
|
295 |
+
class={[
|
296 |
+
"flex items-center justify-center gap-2 rounded-r-lg px-1.5 text-sm font-medium text-white",
|
297 |
+
"focus:ring-4 focus:ring-gray-300 focus:outline-hidden dark:focus:ring-gray-700",
|
298 |
+
loading && "bg-red-900 hover:bg-red-800 dark:bg-red-600 dark:hover:bg-red-700",
|
299 |
+
!loading && "bg-black hover:bg-gray-900 dark:bg-blue-600 dark:hover:bg-blue-700",
|
300 |
+
]}
|
301 |
+
{...popover.trigger}
|
302 |
+
disabled={loading}
|
303 |
+
>
|
304 |
+
<IconChevronDown />
|
305 |
+
</button>
|
306 |
+
<div
|
307 |
+
class={["flex-col rounded-lg bg-white px-2 py-1 shadow dark:bg-gray-800", popover.open && "flex"]}
|
308 |
+
{...popover.content}
|
309 |
+
>
|
310 |
+
<button
|
311 |
+
class="group py-1 text-sm"
|
312 |
+
onclick={() => {
|
313 |
+
viewCode = false;
|
314 |
+
session.runOrStop("left");
|
315 |
+
popover.open = false;
|
316 |
+
}}
|
317 |
+
>
|
318 |
+
<div
|
319 |
+
class="flex items-center gap-2 rounded p-1 group-hover:bg-gray-200 dark:group-hover:bg-gray-700"
|
320 |
+
>
|
321 |
+
<IconChatLeft />
|
322 |
+
<span class="mr-2">Only run left conversation</span>
|
323 |
+
<span class="ml-auto rounded-sm border border-white/20 bg-gray-500/10 px-0.5 text-xs">
|
324 |
+
{cmdOrCtrl}
|
325 |
+
{optOrAlt} L
|
326 |
+
</span>
|
327 |
+
</div>
|
328 |
+
</button>
|
329 |
+
<button
|
330 |
+
class="group py-1 text-sm"
|
331 |
+
onclick={() => {
|
332 |
+
viewCode = false;
|
333 |
+
session.runOrStop("right");
|
334 |
+
popover.open = false;
|
335 |
+
}}
|
336 |
+
>
|
337 |
+
<div
|
338 |
+
class="flex items-center gap-2 rounded p-1 group-hover:bg-gray-200 dark:group-hover:bg-gray-700"
|
339 |
+
>
|
340 |
+
<IconChatRight />
|
341 |
+
<span class="mr-2">Only run right conversation</span>
|
342 |
+
<span class="ml-auto rounded-sm border border-white/20 bg-gray-500/10 px-0.5 text-xs">
|
343 |
+
{cmdOrCtrl}
|
344 |
+
{optOrAlt} R
|
345 |
+
</span>
|
346 |
+
</div>
|
347 |
+
</button>
|
348 |
+
</div>
|
349 |
+
{/snippet}
|
350 |
+
</Popover>
|
351 |
{/if}
|
352 |
+
</div>
|
353 |
</div>
|
354 |
</div>
|
355 |
</div>
|
|
|
359 |
<div class={[viewSettings && "max-md:fixed max-md:inset-0 max-md:bottom-20 max-md:backdrop-blur-lg"]}>
|
360 |
<div
|
361 |
class={[
|
362 |
+
"flex h-full flex-col p-3 max-md:absolute max-md:inset-x-0 max-md:bottom-0",
|
363 |
viewSettings ? "max-md:fixed" : "max-md:hidden",
|
364 |
]}
|
365 |
>
|
|
|
376 |
<IconCompare />
|
377 |
Compare
|
378 |
</button>
|
379 |
+
{#if isConversationWithHFModel(session.project.conversations[0])}
|
380 |
+
<a
|
381 |
+
href="https://huggingface.co/{session.project.conversations[0]?.model.id}?inference_provider={session
|
382 |
+
.project.conversations[0]?.provider}"
|
383 |
+
target="_blank"
|
384 |
+
class="flex items-center gap-0.5 text-gray-500 hover:text-gray-700 dark:text-gray-400 dark:hover:text-gray-300"
|
385 |
+
>
|
386 |
+
<IconExternal class="text-2xs" />
|
387 |
+
Model page
|
388 |
+
</a>
|
389 |
+
{/if}
|
390 |
</div>
|
391 |
</div>
|
392 |
|
src/lib/components/inference-playground/utils.ts
CHANGED
@@ -48,31 +48,14 @@ type OpenAICompletionMetadata = {
|
|
48 |
|
49 |
type CompletionMetadata = HFCompletionMetadata | OpenAICompletionMetadata;
|
50 |
|
51 |
-
function parseOpenAIMessages(
|
52 |
-
messages: ConversationMessage[],
|
53 |
-
systemMessage?: ConversationMessage
|
54 |
-
): OpenAI.ChatCompletionMessageParam[] {
|
55 |
-
const parsedMessages: OpenAI.ChatCompletionMessageParam[] = [];
|
56 |
-
|
57 |
-
if (systemMessage?.content) {
|
58 |
-
parsedMessages.push({
|
59 |
-
role: "system",
|
60 |
-
content: systemMessage.content,
|
61 |
-
});
|
62 |
-
}
|
63 |
-
|
64 |
-
return [
|
65 |
-
...parsedMessages,
|
66 |
-
...messages.map(msg => ({
|
67 |
-
role: msg.role === "assistant" ? ("assistant" as const) : ("user" as const),
|
68 |
-
content: msg.content || "",
|
69 |
-
})),
|
70 |
-
];
|
71 |
-
}
|
72 |
-
|
73 |
function getCompletionMetadata(conversation: Conversation, signal?: AbortSignal): CompletionMetadata {
|
74 |
const { model, systemMessage } = conversation;
|
75 |
|
|
|
|
|
|
|
|
|
|
|
76 |
// Handle OpenAI-compatible models
|
77 |
if (isCustomModel(model)) {
|
78 |
const openai = new OpenAI({
|
@@ -88,17 +71,14 @@ function getCompletionMetadata(conversation: Conversation, signal?: AbortSignal)
|
|
88 |
type: "openai",
|
89 |
client: openai,
|
90 |
args: {
|
91 |
-
messages:
|
|
|
92 |
model: model.id,
|
93 |
},
|
94 |
};
|
95 |
}
|
96 |
|
97 |
// Handle HuggingFace models
|
98 |
-
const messages = [
|
99 |
-
...(isSystemPromptSupported(model) && systemMessage.content?.length ? [systemMessage] : []),
|
100 |
-
...conversation.messages,
|
101 |
-
];
|
102 |
|
103 |
return {
|
104 |
type: "huggingface",
|
|
|
48 |
|
49 |
type CompletionMetadata = HFCompletionMetadata | OpenAICompletionMetadata;
|
50 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
51 |
function getCompletionMetadata(conversation: Conversation, signal?: AbortSignal): CompletionMetadata {
|
52 |
const { model, systemMessage } = conversation;
|
53 |
|
54 |
+
const messages = [
|
55 |
+
...(isSystemPromptSupported(model) && systemMessage.content?.length ? [systemMessage] : []),
|
56 |
+
...conversation.messages,
|
57 |
+
];
|
58 |
+
|
59 |
// Handle OpenAI-compatible models
|
60 |
if (isCustomModel(model)) {
|
61 |
const openai = new OpenAI({
|
|
|
71 |
type: "openai",
|
72 |
client: openai,
|
73 |
args: {
|
74 |
+
messages: messages.map(parseMessage) as OpenAI.ChatCompletionMessageParam[],
|
75 |
+
...conversation.config,
|
76 |
model: model.id,
|
77 |
},
|
78 |
};
|
79 |
}
|
80 |
|
81 |
// Handle HuggingFace models
|
|
|
|
|
|
|
|
|
82 |
|
83 |
return {
|
84 |
type: "huggingface",
|
src/lib/spells/abort-manager.svelte.ts
CHANGED
@@ -1,4 +1,5 @@
|
|
1 |
import { onDestroy } from "svelte";
|
|
|
2 |
|
3 |
/**
|
4 |
* Manages abort controllers, and aborts them when the component unmounts.
|
@@ -7,9 +8,17 @@ export class AbortManager {
|
|
7 |
private controllers: AbortController[] = [];
|
8 |
|
9 |
constructor() {
|
10 |
-
|
11 |
}
|
12 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
13 |
/**
|
14 |
* Creates a new abort controller and adds it to the manager.
|
15 |
*/
|
|
|
1 |
import { onDestroy } from "svelte";
|
2 |
+
import { createInit } from "./create-init.svelte";
|
3 |
|
4 |
/**
|
5 |
* Manages abort controllers, and aborts them when the component unmounts.
|
|
|
8 |
private controllers: AbortController[] = [];
|
9 |
|
10 |
constructor() {
|
11 |
+
this.init();
|
12 |
}
|
13 |
|
14 |
+
init = createInit(() => {
|
15 |
+
try {
|
16 |
+
onDestroy(() => this.abortAll());
|
17 |
+
} catch {
|
18 |
+
// no-op
|
19 |
+
}
|
20 |
+
});
|
21 |
+
|
22 |
/**
|
23 |
* Creates a new abort controller and adds it to the manager.
|
24 |
*/
|
src/lib/spells/create-init.svelte.ts
CHANGED
@@ -1,14 +1,18 @@
|
|
1 |
export function createInit(cb: () => void) {
|
2 |
let called = $state(false);
|
3 |
|
4 |
-
|
5 |
-
|
6 |
-
|
7 |
-
|
8 |
-
|
9 |
-
|
10 |
-
|
11 |
-
|
|
|
|
|
|
|
|
|
12 |
},
|
13 |
-
};
|
14 |
}
|
|
|
1 |
export function createInit(cb: () => void) {
|
2 |
let called = $state(false);
|
3 |
|
4 |
+
function init() {
|
5 |
+
if (called) return;
|
6 |
+
called = true;
|
7 |
+
cb();
|
8 |
+
}
|
9 |
+
|
10 |
+
return Object.defineProperties(init, {
|
11 |
+
called: {
|
12 |
+
get() {
|
13 |
+
return called;
|
14 |
+
},
|
15 |
+
enumerable: true,
|
16 |
},
|
17 |
+
}) as typeof init & { readonly called: boolean };
|
18 |
}
|
src/lib/state/session.svelte.ts
CHANGED
@@ -1,4 +1,7 @@
|
|
1 |
import { defaultGenerationConfig } from "$lib/components/inference-playground/generation-config-settings.js";
|
|
|
|
|
|
|
2 |
import { createInit } from "$lib/spells/create-init.svelte.js";
|
3 |
import {
|
4 |
PipelineTag,
|
@@ -13,9 +16,18 @@ import { safeParse } from "$lib/utils/json.js";
|
|
13 |
import typia from "typia";
|
14 |
import { models } from "./models.svelte";
|
15 |
import { checkpoints } from "./checkpoints.svelte";
|
|
|
|
|
|
|
|
|
16 |
|
17 |
const LOCAL_STORAGE_KEY = "hf_inference_playground_session";
|
18 |
|
|
|
|
|
|
|
|
|
|
|
19 |
const startMessageUser: ConversationMessage = { role: "user", content: "" };
|
20 |
const systemMessage: ConversationMessage = {
|
21 |
role: "system",
|
@@ -59,6 +71,13 @@ function getDefaults() {
|
|
59 |
class SessionState {
|
60 |
#value = $state<Session>({} as Session);
|
61 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
62 |
// Call once in layout
|
63 |
init = createInit(() => {
|
64 |
const { defaultConversation, defaultProject } = getDefaults();
|
@@ -111,6 +130,10 @@ class SessionState {
|
|
111 |
}
|
112 |
|
113 |
this.$ = savedSession;
|
|
|
|
|
|
|
|
|
114 |
});
|
115 |
|
116 |
constructor() {
|
@@ -190,6 +213,120 @@ class SessionState {
|
|
190 |
const projects = this.$.projects.map(p => (p.id === np.id ? np : p));
|
191 |
this.#setAnySession({ ...this.$, projects });
|
192 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
193 |
}
|
194 |
|
195 |
export const session = new SessionState();
|
|
|
1 |
import { defaultGenerationConfig } from "$lib/components/inference-playground/generation-config-settings.js";
|
2 |
+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
3 |
+
// @ts-ignore - Svelte imports are broken in TS files
|
4 |
+
import { showQuotaModal } from "$lib/components/quota-modal.svelte";
|
5 |
import { createInit } from "$lib/spells/create-init.svelte.js";
|
6 |
import {
|
7 |
PipelineTag,
|
|
|
16 |
import typia from "typia";
|
17 |
import { models } from "./models.svelte";
|
18 |
import { checkpoints } from "./checkpoints.svelte";
|
19 |
+
import { handleNonStreamingResponse, handleStreamingResponse } from "$lib/components/inference-playground/utils.js";
|
20 |
+
import { AbortManager } from "$lib/spells/abort-manager.svelte";
|
21 |
+
import { addToast } from "$lib/components/toaster.svelte.js";
|
22 |
+
import { token } from "./token.svelte";
|
23 |
|
24 |
const LOCAL_STORAGE_KEY = "hf_inference_playground_session";
|
25 |
|
26 |
+
interface GenerationStatistics {
|
27 |
+
latency: number;
|
28 |
+
generatedTokensCount: number;
|
29 |
+
}
|
30 |
+
|
31 |
const startMessageUser: ConversationMessage = { role: "user", content: "" };
|
32 |
const systemMessage: ConversationMessage = {
|
33 |
role: "system",
|
|
|
71 |
class SessionState {
|
72 |
#value = $state<Session>({} as Session);
|
73 |
|
74 |
+
generationStats = $state([{ latency: 0, generatedTokensCount: 0 }] as
|
75 |
+
| [GenerationStatistics]
|
76 |
+
| [GenerationStatistics, GenerationStatistics]);
|
77 |
+
generating = $state(false);
|
78 |
+
|
79 |
+
#abortManager = new AbortManager();
|
80 |
+
|
81 |
// Call once in layout
|
82 |
init = createInit(() => {
|
83 |
const { defaultConversation, defaultProject } = getDefaults();
|
|
|
130 |
}
|
131 |
|
132 |
this.$ = savedSession;
|
133 |
+
session.generationStats = session.project.conversations.map(_ => ({ latency: 0, generatedTokensCount: 0 })) as
|
134 |
+
| [GenerationStatistics]
|
135 |
+
| [GenerationStatistics, GenerationStatistics];
|
136 |
+
this.#abortManager.init();
|
137 |
});
|
138 |
|
139 |
constructor() {
|
|
|
213 |
const projects = this.$.projects.map(p => (p.id === np.id ? np : p));
|
214 |
this.#setAnySession({ ...this.$, projects });
|
215 |
}
|
216 |
+
|
217 |
+
async #runInference(conversation: Conversation) {
|
218 |
+
const idx = session.project.conversations.indexOf(conversation);
|
219 |
+
|
220 |
+
const startTime = performance.now();
|
221 |
+
|
222 |
+
if (conversation.streaming) {
|
223 |
+
let addedMessage = false;
|
224 |
+
const streamingMessage = $state({ role: "assistant", content: "" });
|
225 |
+
|
226 |
+
await handleStreamingResponse(
|
227 |
+
conversation,
|
228 |
+
content => {
|
229 |
+
if (!streamingMessage) return;
|
230 |
+
streamingMessage.content = content;
|
231 |
+
if (!addedMessage) {
|
232 |
+
conversation.messages = [...conversation.messages, streamingMessage];
|
233 |
+
addedMessage = true;
|
234 |
+
}
|
235 |
+
},
|
236 |
+
this.#abortManager.createController()
|
237 |
+
);
|
238 |
+
} else {
|
239 |
+
const { message: newMessage, completion_tokens: newTokensCount } = await handleNonStreamingResponse(conversation);
|
240 |
+
conversation.messages = [...conversation.messages, newMessage];
|
241 |
+
const c = session.generationStats[idx];
|
242 |
+
if (c) c.generatedTokensCount += newTokensCount;
|
243 |
+
}
|
244 |
+
|
245 |
+
const endTime = performance.now();
|
246 |
+
const c = session.generationStats[idx];
|
247 |
+
if (c) c.latency = Math.round(endTime - startTime);
|
248 |
+
}
|
249 |
+
|
250 |
+
async run(conv: "left" | "right" | "both" | Conversation = "both") {
|
251 |
+
if (!token.value) {
|
252 |
+
token.showModal = true;
|
253 |
+
return;
|
254 |
+
}
|
255 |
+
|
256 |
+
const conversations = (() => {
|
257 |
+
if (typeof conv === "string") {
|
258 |
+
return session.project.conversations.filter((_, idx) => {
|
259 |
+
return conv === "both" || (conv === "left" ? idx === 0 : idx === 1);
|
260 |
+
});
|
261 |
+
}
|
262 |
+
return [conv];
|
263 |
+
})();
|
264 |
+
|
265 |
+
for (let idx = 0; idx < conversations.length; idx++) {
|
266 |
+
const conversation = conversations[idx];
|
267 |
+
if (!conversation || conversation.messages.at(-1)?.role !== "assistant") continue;
|
268 |
+
|
269 |
+
let prefix = "";
|
270 |
+
if (session.project.conversations.length === 2) {
|
271 |
+
prefix = `Error on ${idx === 0 ? "left" : "right"} conversation. `;
|
272 |
+
}
|
273 |
+
return addToast({
|
274 |
+
title: "Failed to run inference",
|
275 |
+
description: `${prefix}Messages must alternate between user/assistant roles.`,
|
276 |
+
variant: "error",
|
277 |
+
});
|
278 |
+
}
|
279 |
+
|
280 |
+
(document.activeElement as HTMLElement).blur();
|
281 |
+
session.generating = true;
|
282 |
+
|
283 |
+
try {
|
284 |
+
const promises = conversations.map(c => this.#runInference(c));
|
285 |
+
await Promise.all(promises);
|
286 |
+
} catch (error) {
|
287 |
+
for (const conversation of conversations) {
|
288 |
+
if (conversation.messages.at(-1)?.role === "assistant" && !conversation.messages.at(-1)?.content?.trim()) {
|
289 |
+
conversation.messages.pop();
|
290 |
+
conversation.messages = [...conversation.messages];
|
291 |
+
}
|
292 |
+
// eslint-disable-next-line no-self-assign
|
293 |
+
session.$ = session.$;
|
294 |
+
}
|
295 |
+
|
296 |
+
if (error instanceof Error) {
|
297 |
+
const msg = error.message;
|
298 |
+
if (msg.toLowerCase().includes("montly") || msg.toLowerCase().includes("pro")) {
|
299 |
+
showQuotaModal();
|
300 |
+
}
|
301 |
+
|
302 |
+
if (error.message.includes("token seems invalid")) {
|
303 |
+
token.reset();
|
304 |
+
}
|
305 |
+
|
306 |
+
if (error.name !== "AbortError") {
|
307 |
+
addToast({ title: "Error", description: error.message, variant: "error" });
|
308 |
+
}
|
309 |
+
} else {
|
310 |
+
addToast({ title: "Error", description: "An unknown error occurred", variant: "error" });
|
311 |
+
}
|
312 |
+
} finally {
|
313 |
+
session.generating = false;
|
314 |
+
this.#abortManager.clear();
|
315 |
+
}
|
316 |
+
}
|
317 |
+
|
318 |
+
stopGenerating = () => {
|
319 |
+
this.#abortManager.abortAll();
|
320 |
+
session.generating = false;
|
321 |
+
};
|
322 |
+
|
323 |
+
runOrStop = (c?: Parameters<typeof this.run>[0]) => {
|
324 |
+
if (session.generating) {
|
325 |
+
this.stopGenerating();
|
326 |
+
} else {
|
327 |
+
this.run(c);
|
328 |
+
}
|
329 |
+
};
|
330 |
}
|
331 |
|
332 |
export const session = new SessionState();
|
src/lib/types.ts
CHANGED
@@ -71,6 +71,8 @@ export type CustomModel = {
|
|
71 |
_id: string;
|
72 |
endpointUrl: string;
|
73 |
accessToken?: string;
|
|
|
|
|
74 |
};
|
75 |
|
76 |
export type Config = {
|
@@ -196,6 +198,11 @@ export enum PipelineTag {
|
|
196 |
ImageTextToText = "image-text-to-text",
|
197 |
}
|
198 |
|
|
|
|
|
|
|
|
|
|
|
199 |
export type MaybeGetter<T> = T | (() => T);
|
200 |
|
201 |
export type ValueOf<T> = T[keyof T];
|
|
|
71 |
_id: string;
|
72 |
endpointUrl: string;
|
73 |
accessToken?: string;
|
74 |
+
/** @default "text-generation" */
|
75 |
+
pipeline_tag?: PipelineTag;
|
76 |
};
|
77 |
|
78 |
export type Config = {
|
|
|
198 |
ImageTextToText = "image-text-to-text",
|
199 |
}
|
200 |
|
201 |
+
export const pipelineTagLabel: Record<PipelineTag, string> = {
|
202 |
+
[PipelineTag.TextGeneration]: "Text→Text",
|
203 |
+
[PipelineTag.ImageTextToText]: "Image+Text→Text",
|
204 |
+
};
|
205 |
+
|
206 |
export type MaybeGetter<T> = T | (() => T);
|
207 |
|
208 |
export type ValueOf<T> = T[keyof T];
|
src/lib/utils/array.ts
CHANGED
@@ -10,3 +10,37 @@ export function randomPick<T>(arr: T[]): T | undefined {
|
|
10 |
export function edit<T>(arr: T[], index: number, newValue: T): T[] {
|
11 |
return arr.map((value, i) => (i === index ? newValue : value));
|
12 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
10 |
export function edit<T>(arr: T[], index: number, newValue: T): T[] {
|
11 |
return arr.map((value, i) => (i === index ? newValue : value));
|
12 |
}
|
13 |
+
|
14 |
+
type IterateReturn<T> = [
|
15 |
+
T,
|
16 |
+
{
|
17 |
+
isFirst: boolean;
|
18 |
+
isLast: boolean;
|
19 |
+
array: T[];
|
20 |
+
index: number;
|
21 |
+
length: number;
|
22 |
+
},
|
23 |
+
];
|
24 |
+
|
25 |
+
/**
|
26 |
+
* Returns an an iterator that iterates over the given array.
|
27 |
+
* Each returned item contains helpful properties, such as
|
28 |
+
* `isFirst`, `isLast`, `array`, `index`, and `length`
|
29 |
+
*
|
30 |
+
* @param array The array to iterate over.
|
31 |
+
* @returns An iterator that iterates over the given array.
|
32 |
+
*/
|
33 |
+
export function* iterate<T>(array: T[]): Generator<IterateReturn<T>> {
|
34 |
+
for (let i = 0; i < array.length; i++) {
|
35 |
+
yield [
|
36 |
+
array[i]!,
|
37 |
+
{
|
38 |
+
isFirst: i === 0,
|
39 |
+
isLast: i === array.length - 1,
|
40 |
+
array,
|
41 |
+
index: i,
|
42 |
+
length: array.length,
|
43 |
+
},
|
44 |
+
];
|
45 |
+
}
|
46 |
+
}
|
src/lib/utils/platform.ts
CHANGED
@@ -1,3 +1,6 @@
|
|
1 |
export function isMac() {
|
2 |
return navigator.platform.toUpperCase().indexOf("MAC") >= 0;
|
3 |
}
|
|
|
|
|
|
|
|
1 |
export function isMac() {
|
2 |
return navigator.platform.toUpperCase().indexOf("MAC") >= 0;
|
3 |
}
|
4 |
+
|
5 |
+
export const cmdOrCtrl = isMac() ? "⌘" : "Ctrl";
|
6 |
+
export const optOrAlt = isMac() ? "⌥" : "Alt";
|
src/routes/+layout.svelte
CHANGED
@@ -12,7 +12,7 @@
|
|
12 |
}
|
13 |
|
14 |
let { children }: Props = $props();
|
15 |
-
session.init
|
16 |
</script>
|
17 |
|
18 |
{@render children?.()}
|
|
|
12 |
}
|
13 |
|
14 |
let { children }: Props = $props();
|
15 |
+
session.init();
|
16 |
</script>
|
17 |
|
18 |
{@render children?.()}
|