enzostvs HF Staff commited on
Commit
bd63612
·
1 Parent(s): 7f41bd4

wip new modal model settings

Browse files
src/lib/components/chat/User.svelte CHANGED
@@ -198,7 +198,7 @@
198
  <img
199
  src={`https://huggingface.co/api/avatars/${model.owned_by}`}
200
  alt={model.id}
201
- class="size-3.5 rounded-full"
202
  />
203
  {model.id.split('/').pop() ?? model.id}
204
  {#if !lastMessage || model.isError || selectedModels.length > MAX_MODELS_PER_NODE}
 
198
  <img
199
  src={`https://huggingface.co/api/avatars/${model.owned_by}`}
200
  alt={model.id}
201
+ class="size-3.5 rounded"
202
  />
203
  {model.id.split('/').pop() ?? model.id}
204
  {#if !lastMessage || model.isError || selectedModels.length > MAX_MODELS_PER_NODE}
src/lib/components/model/SettingsModel.svelte CHANGED
@@ -1,14 +1,18 @@
1
  <script lang="ts">
2
  import type { Snippet } from 'svelte';
3
- import { DollarSign, Star, X, Zap } from '@lucide/svelte';
4
 
5
- import * as Popover from '$lib/components/ui/popover/index.js';
6
  import type { ChatModel } from '$lib/helpers/types';
7
  import { formatPricingPerToken } from '$lib/index.js';
8
  import { Button } from '$lib/components/ui/button';
9
  import { Slider } from '$lib/components/ui/slider';
10
  import Input from '$lib/components/ui/input/input.svelte';
11
  import * as Select from '$lib/components/ui/select/index.js';
 
 
 
 
 
12
 
13
  let {
14
  model,
@@ -37,159 +41,241 @@
37
  );
38
  </script>
39
 
40
- <Popover.Root
41
  bind:open
42
  onOpenChange={(value) => !value && onSave({ ...model, temperature, max_tokens, top_p, provider })}
43
  >
44
- <Popover.Trigger>{@render children?.()}</Popover.Trigger>
45
- <Popover.Content class="w-80 rounded-2xl p-0">
46
- <div class="grid gap-4">
47
- <header class="px-5 pt-4">
48
- <h4 class="text-sm leading-none font-medium text-muted-foreground">Generation Settings</h4>
49
- </header>
50
- <main class="grid gap-5 px-5 py-0">
51
- <div class="space-y-1">
52
- <div class="flex items-center justify-between">
53
- <h4 class="text-sm leading-none font-medium">Temperature</h4>
54
- <div class="flex items-center gap-1">
55
- {#if temperature === undefined}
56
- <Button variant="outline-blue" size="2xs" onclick={() => (temperature = 0.5)}
57
- >Set</Button
58
- >
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
59
  {:else}
60
- <Input
61
- type="number"
62
- min={0}
63
- max={2}
64
- step={0.1}
65
- bind:value={temperature}
66
- class="h-7! w-24!"
67
  />
68
- <Button variant="outline" size="icon-xs" onclick={() => (temperature = undefined)}>
69
- <X class="size-3.5" />
70
- </Button>
71
  {/if}
72
  </div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
73
  </div>
74
  {#if temperature !== undefined}
75
- <Slider
76
- type="single"
77
- bind:value={temperature}
78
- min={0}
79
- max={2}
80
- step={0.01}
81
- class="mt-2"
82
- />
 
 
 
 
 
 
 
 
 
 
83
  {/if}
84
  </div>
85
-
86
- <div class="space-y-1">
87
- <div class="flex items-center justify-between">
88
- <h4 class="text-sm leading-none font-medium">Max Tokens</h4>
89
- <div class="flex items-center gap-1">
90
- {#if max_tokens === undefined}
91
- <Button
92
- variant="outline-blue"
93
- size="2xs"
94
- onclick={() => (max_tokens = (maxContentLength ?? 32_000) / 2)}>Set</Button
95
- >
96
- {:else}
97
- <Input
98
- type="number"
99
- min={0}
100
- max={maxContentLength}
101
- step={256}
102
- placeholder="—"
103
- class="h-7! w-24!"
104
- bind:value={max_tokens}
105
- />
106
- <Button variant="outline" size="icon-xs" onclick={() => (max_tokens = undefined)}>
107
- <X class="size-3.5" />
108
- </Button>
109
- {/if}
110
  </div>
 
 
 
 
 
 
 
 
 
 
111
  </div>
112
  {#if max_tokens !== undefined}
113
- <Slider
114
- type="single"
115
- bind:value={max_tokens}
116
- min={0}
117
- max={maxContentLength}
118
- step={256}
119
- class="mt-2"
120
- />
 
 
 
 
 
 
 
 
 
 
121
  {/if}
122
  </div>
123
-
124
- <div class="space-y-1">
125
- <div class="flex items-center justify-between">
126
- <h4 class="text-sm leading-none font-medium">Top-P</h4>
127
- <div class="flex items-center gap-1">
128
- {#if top_p === undefined}
129
- <Button variant="outline-blue" size="2xs" onclick={() => (top_p = 0.7)}>Set</Button>
130
- {:else}
131
- <Input
132
- type="number"
133
- min={0}
134
- max={1}
135
- step={0.01}
136
- bind:value={top_p}
137
- class="h-7! w-24!"
138
- />
139
- <Button variant="outline" size="icon-xs" onclick={() => (top_p = undefined)}>
140
- <X class="size-3.5" />
141
- </Button>
142
- {/if}
143
  </div>
 
 
 
 
 
 
 
 
 
 
144
  </div>
145
  {#if top_p !== undefined}
146
- <Slider type="single" bind:value={top_p} min={0} max={1} step={0.01} class="mt-2" />
 
 
 
 
 
 
 
 
 
 
147
  {/if}
148
  </div>
149
- </main>
150
- <header class="border-t border-border px-5 pt-4">
151
- <h4 class="text-sm leading-none font-medium text-muted-foreground">Provider</h4>
152
- </header>
153
- <main class="w-full px-5 pb-5">
154
- <Select.Root type="single" bind:value={provider}>
155
- <Select.Trigger class="flex w-full items-center justify-between gap-2 capitalize">
156
- <div class="flex items-center gap-2">
157
- {#if provider === 'auto'}
158
- <Star class="size-4 fill-yellow-500 text-yellow-500" />
159
- {:else}
160
- <img
161
- src={`https://huggingface.co/api/avatars/${provider}`}
162
- alt={provider}
163
- class="size-4"
164
- />
165
- {/if}
166
- {provider}
167
- </div>
168
- </Select.Trigger>
169
- <Select.Content>
170
- <Select.Item value="auto"
171
- ><Star class="size-4 fill-yellow-500 text-yellow-500" /> Auto</Select.Item
172
- >
173
- {#each model.providers as provider}
174
- <Select.Item value={provider.provider}>
175
- <div class="flex items-center gap-2">
176
- <img
177
- src={`https://huggingface.co/api/avatars/${provider.provider}`}
178
- alt={provider.provider}
179
- class="size-4"
180
- />
181
- {provider.provider}
182
- </div>
183
- {#if formatPricingPerToken(provider.pricing)}
184
- <span class="text-xs text-muted-foreground"
185
- >{formatPricingPerToken(provider.pricing)}</span
186
- >
187
- {/if}
188
- </Select.Item>
189
- {/each}
190
- </Select.Content>
191
- </Select.Root>
192
- </main>
193
- </div>
194
- </Popover.Content>
195
- </Popover.Root>
 
1
  <script lang="ts">
2
  import type { Snippet } from 'svelte';
3
+ import { ExternalLink, Plus, Save, X } from '@lucide/svelte';
4
 
 
5
  import type { ChatModel } from '$lib/helpers/types';
6
  import { formatPricingPerToken } from '$lib/index.js';
7
  import { Button } from '$lib/components/ui/button';
8
  import { Slider } from '$lib/components/ui/slider';
9
  import Input from '$lib/components/ui/input/input.svelte';
10
  import * as Select from '$lib/components/ui/select/index.js';
11
+ import * as Dialog from '$lib/components/ui/dialog/index.js';
12
+ import { PROVIDER_SELECTION_MODES } from '$lib/consts';
13
+ import Separator from '$lib/components/ui/separator/separator.svelte';
14
+ import Switch from '$lib/components/ui/switch/switch.svelte';
15
+ import { fade } from 'svelte/transition';
16
 
17
  let {
18
  model,
 
41
  );
42
  </script>
43
 
44
+ <Dialog.Root
45
  bind:open
46
  onOpenChange={(value) => !value && onSave({ ...model, temperature, max_tokens, top_p, provider })}
47
  >
48
+ <Dialog.Trigger>{@render children?.()}</Dialog.Trigger>
49
+ <Dialog.Content class="max-w-md! gap-0! p-0!">
50
+ <Dialog.Header class="mb-0 gap-1! rounded-none border-b p-5">
51
+ <Dialog.Title>Model Settings</Dialog.Title>
52
+ <Dialog.Description>Manage your model settings.</Dialog.Description>
53
+ </Dialog.Header>
54
+ {#if model}
55
+ <div class="mb-4 flex items-center justify-between gap-3 bg-accent p-3">
56
+ <div class="flex items-center justify-start gap-3">
57
+ <img
58
+ src={`https://huggingface.co/api/avatars/${model.owned_by}`}
59
+ alt={model.owned_by}
60
+ class="size-8 rounded-md"
61
+ />
62
+ <div>
63
+ <p class="text-sm font-medium">{model.id.split('/').pop() ?? model.id}</p>
64
+ <p class="text-xs text-muted-foreground">by {model.owned_by}</p>
65
+ </div>
66
+ </div>
67
+ <a href={`https://huggingface.co/${model.id}`} target="_blank">
68
+ <Button variant="outline" size="icon-xs">
69
+ <ExternalLink class="size-4" />
70
+ </Button>
71
+ </a>
72
+ </div>
73
+ {/if}
74
+ <main class="mt-0 space-y-5 px-3 pb-5">
75
+ <div class="rounded-lg border border-border p-3.5">
76
+ <h4 class="text-sm leading-none font-medium">Inference provider</h4>
77
+ <p class="mt-0.5 text-xs text-muted-foreground">
78
+ Choose which Inference Provider to use with this model
79
+ </p>
80
+ <Select.Root type="single" bind:value={provider}>
81
+ <Select.Trigger class="mt-3 flex w-full items-center justify-between gap-2 capitalize">
82
+ <div class="flex items-center gap-2">
83
+ {#if PROVIDER_SELECTION_MODES.find((m) => m.value === provider)}
84
+ {@const mode = PROVIDER_SELECTION_MODES.find((m) => m.value === provider)!}
85
+ <div class="flex size-5 items-center justify-center rounded {mode.class}">
86
+ <mode.icon class="size-3 {mode.iconClass}" />
87
+ </div>
88
+ <p>
89
+ {mode.label}
90
+ {#if mode.description}
91
+ <span class="text-xs text-muted-foreground lowercase italic"
92
+ >({mode.description})</span
93
+ >
94
+ {/if}
95
+ </p>
96
  {:else}
97
+ <img
98
+ src={`https://huggingface.co/api/avatars/${provider}`}
99
+ alt={provider}
100
+ class="size-4 rounded"
 
 
 
101
  />
102
+ {provider}
 
 
103
  {/if}
104
  </div>
105
+ </Select.Trigger>
106
+ <Select.Content>
107
+ <Select.Group>
108
+ <Select.GroupHeading>Selection mode</Select.GroupHeading>
109
+ {#each PROVIDER_SELECTION_MODES as mode}
110
+ <Select.Item value={mode.value}>
111
+ <div class="flex size-5 items-center justify-center rounded {mode.class}">
112
+ <mode.icon class="size-3 {mode.iconClass}" />
113
+ </div>
114
+ <p>
115
+ {mode.label}
116
+ {#if mode.description}
117
+ <span class="text-xs text-muted-foreground lowercase italic"
118
+ >({mode.description})</span
119
+ >
120
+ {/if}
121
+ </p>
122
+ </Select.Item>
123
+ {/each}
124
+ </Select.Group>
125
+ <Select.Separator />
126
+ <Select.Group>
127
+ <Select.GroupHeading>Specific provider</Select.GroupHeading>
128
+ {#each model.providers as provider}
129
+ <Select.Item value={provider.provider}>
130
+ <div class="flex items-center gap-2 capitalize">
131
+ <img
132
+ src={`https://huggingface.co/api/avatars/${provider.provider}`}
133
+ alt={provider.provider}
134
+ class="size-4 rounded"
135
+ />
136
+ {provider.provider}
137
+ </div>
138
+ {#if formatPricingPerToken(provider.pricing)}
139
+ <span class="text-xs text-muted-foreground">
140
+ {formatPricingPerToken(provider.pricing)}
141
+ </span>
142
+ {/if}
143
+ </Select.Item>
144
+ {/each}
145
+ </Select.Group>
146
+ </Select.Content>
147
+ </Select.Root>
148
+ </div>
149
+ <div class="grid gap-3 rounded-lg border border-border p-3.5">
150
+ <div class="space-y-2">
151
+ <div class="flex items-center justify-between gap-2">
152
+ <div>
153
+ <h4 class="text-sm leading-none font-medium">Temperature</h4>
154
+ <p class="mt-0.5 text-xs text-muted-foreground">
155
+ Tunes the creativity vs. predictability trade-off.
156
+ </p>
157
+ </div>
158
+ <Switch
159
+ checked={temperature !== undefined}
160
+ onCheckedChange={(value) => {
161
+ if (value) {
162
+ temperature = 0.5;
163
+ } else {
164
+ temperature = undefined;
165
+ }
166
+ }}
167
+ />
168
  </div>
169
  {#if temperature !== undefined}
170
+ <div class="flex items-center gap-2" transition:fade={{ duration: 100 }}>
171
+ <Slider
172
+ type="single"
173
+ bind:value={temperature}
174
+ min={0}
175
+ max={2}
176
+ step={0.01}
177
+ class="mt-2"
178
+ />
179
+ <Input
180
+ type="number"
181
+ min={0}
182
+ max={2}
183
+ step={0.1}
184
+ bind:value={temperature}
185
+ class="h-7! w-24!"
186
+ />
187
+ </div>
188
  {/if}
189
  </div>
190
+ <Separator />
191
+ <div class="space-y-2">
192
+ <div class="flex items-center justify-between gap-2">
193
+ <div>
194
+ <h4 class="text-sm leading-none font-medium">Max Tokens</h4>
195
+ <p class="mt-0.5 text-xs text-muted-foreground">
196
+ Sets the absolute limit for generated content length.
197
+ </p>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
198
  </div>
199
+ <Switch
200
+ checked={max_tokens !== undefined}
201
+ onCheckedChange={(value) => {
202
+ if (value) {
203
+ max_tokens = (maxContentLength ?? 32_000) / 2;
204
+ } else {
205
+ max_tokens = undefined;
206
+ }
207
+ }}
208
+ />
209
  </div>
210
  {#if max_tokens !== undefined}
211
+ <div class="flex items-center gap-2" transition:fade={{ duration: 100 }}>
212
+ <Slider
213
+ type="single"
214
+ bind:value={max_tokens}
215
+ min={0}
216
+ max={maxContentLength ?? 32_000}
217
+ step={256}
218
+ class="mt-2"
219
+ />
220
+ <Input
221
+ type="number"
222
+ min={0}
223
+ max={maxContentLength ?? 32_000}
224
+ step={256}
225
+ bind:value={max_tokens}
226
+ class="h-7! w-24!"
227
+ />
228
+ </div>
229
  {/if}
230
  </div>
231
+ <Separator />
232
+ <div class="space-y-2">
233
+ <div class="flex items-center justify-between gap-2">
234
+ <div>
235
+ <h4 class="text-sm leading-none font-medium">Top-P</h4>
236
+ <p class="mt-0.5 text-xs text-muted-foreground">
237
+ Dynamically filters token selection by probability mass.
238
+ </p>
 
 
 
 
 
 
 
 
 
 
 
 
239
  </div>
240
+ <Switch
241
+ checked={top_p !== undefined}
242
+ onCheckedChange={(value) => {
243
+ if (value) {
244
+ top_p = 0.5;
245
+ } else {
246
+ top_p = undefined;
247
+ }
248
+ }}
249
+ />
250
  </div>
251
  {#if top_p !== undefined}
252
+ <div class="flex items-center gap-2" transition:fade={{ duration: 100 }}>
253
+ <Slider type="single" bind:value={top_p} min={0} max={2} step={0.01} class="mt-2" />
254
+ <Input
255
+ type="number"
256
+ min={0}
257
+ max={2}
258
+ step={0.1}
259
+ bind:value={top_p}
260
+ class="h-7! w-24!"
261
+ />
262
+ </div>
263
  {/if}
264
  </div>
265
+ </div>
266
+ <!-- <header class="border-t border-border">
267
+ <h4 class="text-sm leading-none font-medium text-muted-foreground">Provider</h4>
268
+ </header> -->
269
+ <!-- <main class="w-full">
270
+
271
+ </main> -->
272
+ <div class="flex items-center justify-end gap-3 px-3">
273
+ <Button variant="outline">Reset</Button>
274
+ <Button class="flex-1">
275
+ <Save />
276
+ Save settings
277
+ </Button>
278
+ </div>
279
+ </main>
280
+ </Dialog.Content>
281
+ </Dialog.Root>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/lib/components/ui/button/button.svelte CHANGED
@@ -28,8 +28,8 @@
28
  default: 'h-9 px-4 py-2 has-[>svg]:px-3',
29
  sm: 'h-8 gap-1.5 rounded-md px-3 has-[>svg]:px-2.5',
30
  xs: 'h-7 gap-1 rounded-md px-2.5 has-[>svg]:px-2 text-xs!',
31
- '2xs': 'h-6 gap-1 rounded-md px-2.5 has-[>svg]:px-2 text-xs!',
32
- '3xs': 'h-5 gap-1 rounded-md px-2.5 has-[>svg]:px-2 text-[10px]!',
33
  lg: 'h-10 rounded-md px-6 has-[>svg]:px-4',
34
  icon: 'size-9',
35
  'icon-sm': 'size-8',
 
28
  default: 'h-9 px-4 py-2 has-[>svg]:px-3',
29
  sm: 'h-8 gap-1.5 rounded-md px-3 has-[>svg]:px-2.5',
30
  xs: 'h-7 gap-1 rounded-md px-2.5 has-[>svg]:px-2 text-xs!',
31
+ '2xs': 'h-6 gap-1 rounded-md px-2 has-[>svg]:px-2 text-xs!',
32
+ '3xs': 'h-5 gap-1 rounded-md px-2 has-[>svg]:px-2 text-[10px]!',
33
  lg: 'h-10 rounded-md px-6 has-[>svg]:px-4',
34
  icon: 'size-9',
35
  'icon-sm': 'size-8',
src/lib/components/ui/dialog/dialog-content.svelte CHANGED
@@ -35,7 +35,7 @@
35
  {@render children?.()}
36
  {#if showCloseButton}
37
  <DialogPrimitive.Close
38
- class="absolute end-5 top-5 rounded-xs opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:ring-2 focus:ring-ring focus:ring-offset-2 focus:outline-hidden disabled:pointer-events-none [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-5"
39
  >
40
  <XIcon />
41
  <span class="sr-only">Close</span>
 
35
  {@render children?.()}
36
  {#if showCloseButton}
37
  <DialogPrimitive.Close
38
+ class="absolute end-5 top-5 cursor-pointer rounded-xs opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:ring-2 focus:ring-ring focus:ring-offset-2 focus:outline-hidden disabled:pointer-events-none [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-5"
39
  >
40
  <XIcon />
41
  <span class="sr-only">Close</span>
src/lib/components/ui/select/select-trigger.svelte CHANGED
@@ -19,7 +19,7 @@
19
  data-slot="select-trigger"
20
  data-size={size}
21
  class={cn(
22
- "flex w-fit items-center justify-between gap-2 rounded-md border border-input bg-transparent px-3 py-2 text-sm whitespace-nowrap shadow-xs transition-[color,box-shadow] outline-none select-none focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:cursor-not-allowed disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-destructive/20 data-[placeholder]:text-muted-foreground data-[size=default]:h-9 data-[size=sm]:h-8 *:data-[slot=select-value]:line-clamp-1 *:data-[slot=select-value]:flex *:data-[slot=select-value]:items-center *:data-[slot=select-value]:gap-2 dark:bg-input/30 dark:hover:bg-input/50 dark:aria-invalid:ring-destructive/40 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 [&_svg:not([class*='text-'])]:text-muted-foreground",
23
  className
24
  )}
25
  {...restProps}
 
19
  data-slot="select-trigger"
20
  data-size={size}
21
  class={cn(
22
+ "flex w-fit cursor-pointer items-center justify-between gap-2 rounded-md border border-input bg-transparent px-3 py-2 text-sm whitespace-nowrap shadow-xs transition-[color,box-shadow] outline-none select-none focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:cursor-not-allowed disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-destructive/20 data-[placeholder]:text-muted-foreground data-[size=default]:h-9 data-[size=sm]:h-8 *:data-[slot=select-value]:line-clamp-1 *:data-[slot=select-value]:flex *:data-[slot=select-value]:items-center *:data-[slot=select-value]:gap-2 dark:bg-input/30 dark:hover:bg-input/50 dark:aria-invalid:ring-destructive/40 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 [&_svg:not([class*='text-'])]:text-muted-foreground",
23
  className
24
  )}
25
  {...restProps}
src/lib/components/ui/switch/index.ts ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
 
1
+ import Root from "./switch.svelte";
2
+
3
+ export {
4
+ Root,
5
+ //
6
+ Root as Switch,
7
+ };
src/lib/components/ui/switch/switch.svelte ADDED
@@ -0,0 +1,29 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <script lang="ts">
2
+ import { Switch as SwitchPrimitive } from 'bits-ui';
3
+ import { cn, type WithoutChildrenOrChild } from '$lib/utils.js';
4
+
5
+ let {
6
+ ref = $bindable(null),
7
+ class: className,
8
+ checked = $bindable(false),
9
+ ...restProps
10
+ }: WithoutChildrenOrChild<SwitchPrimitive.RootProps> = $props();
11
+ </script>
12
+
13
+ <SwitchPrimitive.Root
14
+ bind:ref
15
+ bind:checked
16
+ data-slot="switch"
17
+ class={cn(
18
+ 'peer inline-flex h-[1.15rem] w-8 shrink-0 cursor-pointer items-center rounded-full border border-transparent shadow-xs transition-all outline-none focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-blue-500 data-[state=unchecked]:bg-input dark:data-[state=unchecked]:bg-input/80',
19
+ className
20
+ )}
21
+ {...restProps}
22
+ >
23
+ <SwitchPrimitive.Thumb
24
+ data-slot="switch-thumb"
25
+ class={cn(
26
+ 'pointer-events-none block size-4 rounded-full bg-background ring-0 transition-transform data-[state=checked]:translate-x-[calc(100%-2px)] data-[state=unchecked]:translate-x-0 dark:data-[state=checked]:bg-white dark:data-[state=unchecked]:bg-foreground'
27
+ )}
28
+ />
29
+ </SwitchPrimitive.Root>
src/lib/consts.ts CHANGED
@@ -1,3 +1,5 @@
 
 
1
  export const SUGGESTIONS_PROMPT = [
2
  'Roast my code skills',
3
  'Why do LLMs hallucinate?',
@@ -30,3 +32,30 @@ export const SUGGESTIONS_PROMPT = [
30
  'Teach me a fun fact',
31
  'What came first: egg or chicken?'
32
  ];
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { CircleDollarSign, CircleGauge, WandSparkles } from '@lucide/svelte';
2
+
3
  export const SUGGESTIONS_PROMPT = [
4
  'Roast my code skills',
5
  'Why do LLMs hallucinate?',
 
32
  'Teach me a fun fact',
33
  'What came first: egg or chicken?'
34
  ];
35
+
36
+ export const PROVIDER_SELECTION_MODES = [
37
+ {
38
+ value: 'auto',
39
+ label: 'Auto',
40
+ description: 'your HF preference order',
41
+ class: 'bg-yellow-500/10',
42
+ iconClass: 'text-yellow-500',
43
+ icon: WandSparkles
44
+ },
45
+ {
46
+ value: 'fastest',
47
+ label: 'Fastest',
48
+ description: 'highest throughput',
49
+ class: 'bg-emerald-500/10',
50
+ iconClass: 'text-emerald-500',
51
+ icon: CircleGauge
52
+ },
53
+ {
54
+ value: 'cheapest',
55
+ label: 'Cheapest',
56
+ description: 'lowest cost',
57
+ class: 'bg-blue-500/10',
58
+ iconClass: 'text-blue-500',
59
+ icon: CircleDollarSign
60
+ }
61
+ ];
src/lib/state/models.svelte.ts CHANGED
@@ -33,6 +33,7 @@ export async function fetchModels() {
33
  if (!response.ok) throw new Error(response.statusText);
34
 
35
  const { data: models } = (await response.json()) as { data: ChatModel[] };
 
36
  modelsState.models = models;
37
  } catch (e) {
38
  modelsState.error = e instanceof Error ? e.message : 'Failed to fetch models';
 
33
  if (!response.ok) throw new Error(response.statusText);
34
 
35
  const { data: models } = (await response.json()) as { data: ChatModel[] };
36
+ console.log('models', models);
37
  modelsState.models = models;
38
  } catch (e) {
39
  modelsState.error = e instanceof Error ? e.message : 'Failed to fetch models';