Skip to content

Commit f8f1efd

Browse files
committed
update default models, add grok icon, add default models reset
1 parent 85ae3a7 commit f8f1efd

10 files changed

Lines changed: 134 additions & 69 deletions

File tree

.dockerignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ image.png
6060
*.jpeg
6161
*.gif
6262
*.svg
63+
!icons/grok.png
6364

6465
# Certificates and keys (these should be mounted as secrets if needed)
6566
*.crt

Dockerfile

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,10 @@ COPY package.json bun.lock ./
88
# Install all dependencies (including dev) with frozen lockfile
99
RUN --mount=type=cache,target=/root/.bun/install/cache \
1010
bun install
11+
12+
# Copy local build-time assets that are imported by the app
13+
COPY icons ./icons
14+
1115
# Copy source code
1216
COPY . .
1317

api-docs.md

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2539,7 +2539,7 @@ Enable/disable models or toggle pinned status.
25392539

25402540
```json
25412541
{
2542-
"action": "set" | "togglePinned" | "enableInitial" | "setAll",
2542+
"action": "set" | "togglePinned" | "enableInitial" | "setAll" | "resetDefaults",
25432543
"provider": "string",
25442544
"modelId": "string",
25452545
"enabled": "boolean"
@@ -2550,6 +2550,7 @@ Enable/disable models or toggle pinned status.
25502550
`provider` must be `"nanogpt"` and `modelId` is optional for this action.
25512551
When `SUBSCRIPTION_MODELS_ONLY=true` and the user uses the server NanoGPT key, only models with
25522552
`subscription.included === true` are enabled.
2553+
`resetDefaults` disables all NanoGPT models and re-enables only the default NanoGPT seed list as pinned models.
25532554

25542555
**CURL Example**:
25552556

@@ -2567,6 +2568,13 @@ curl -X POST "http://localhost:3432/api/db/user-models" \
25672568
-d '{"action": "setAll", "provider": "nanogpt", "enabled": false}'
25682569
```
25692570

2571+
```bash
2572+
curl -X POST "http://localhost:3432/api/db/user-models" \
2573+
-H "Content-Type: application/json" \
2574+
-b "session_cookie=your_session" \
2575+
-d '{"action": "resetDefaults"}'
2576+
```
2577+
25702578
#### GET `/api/models`
25712579

25722580
Get all available models with capabilities and user's enabled/pinned status.

bun.lock

Lines changed: 29 additions & 29 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

icons/grok.png

13.9 KB
Loading

package.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
"devDependencies": {
2424
"@eslint/compat": "^2.0.5",
2525
"@eslint/js": "^10.0.1",
26-
"@iconify/json": "^2.2.461",
26+
"@iconify/json": "^2.2.462",
2727
"@playwright/test": "^1.59.1",
2828
"@shikijs/langs": "^4.0.2",
2929
"@shikijs/markdown-it": "^4.0.2",
@@ -60,7 +60,7 @@
6060
"tailwindcss": "^4.2.2",
6161
"tw-animate-css": "^1.4.0",
6262
"typescript": "^6.0.2",
63-
"typescript-eslint": "^8.58.1",
63+
"typescript-eslint": "^8.58.2",
6464
"unplugin-icons": "^23.0.1",
6565
"vite": "^8.0.8",
6666
"vitest": "^4.1.4"
@@ -89,7 +89,7 @@
8989
"@libsql/client": "^0.17.2",
9090
"@nanogpt/mcp": "^1.1.6",
9191
"@types/node": "^25.6.0",
92-
"@typescript/native-preview": "^7.0.0-dev.20260412.1",
92+
"@typescript/native-preview": "^7.0.0-dev.20260413.1",
9393
"better-auth": "1.6.2",
9494
"cron-parser": "^5.5.0",
9595
"drizzle-kit": "^0.31.10",

src/lib/components/model-picker/model-picker.svelte

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -35,20 +35,25 @@
3535
import ModelInfoPanel from './model-info-panel.svelte';
3636
import type { NanoGPTModel } from '$lib/backend/models/nano-gpt';
3737
import { fade, fly } from 'svelte/transition';
38+
import grokIcon from '../../../../icons/grok.png';
3839
3940
// Helper to check if model is pinned
4041
function isPinned(model: { pinned?: boolean }): boolean {
4142
return model.pinned === true;
4243
}
4344
44-
function getProviderIconKey(iconUrl: string | undefined, modelId?: string): string {
45-
if (iconUrl) return iconUrl;
46-
45+
function isGrokModel(modelId?: string): boolean {
4746
const lowerModelId = modelId?.toLowerCase();
48-
if (lowerModelId && (lowerModelId.includes('grok') || lowerModelId.includes('x-ai'))) {
47+
return !!(lowerModelId && (lowerModelId.includes('grok') || lowerModelId.includes('x-ai')));
48+
}
49+
50+
function getProviderIconKey(iconUrl: string | undefined, modelId?: string): string {
51+
if (isGrokModel(modelId)) {
4952
return 'fallback:grok';
5053
}
5154
55+
if (iconUrl) return iconUrl;
56+
5257
return '';
5358
}
5459
@@ -300,12 +305,15 @@
300305
301306
// Fallback icons for providers that don't have icon_url in the API
302307
const FALLBACK_ICONS: Record<string, string> = {
303-
grok: 'https://upload.wikimedia.org/wikipedia/commons/thumb/f/f9/Grok-icon.svg/640px-Grok-icon.svg.png',
304-
'x-ai':
305-
'https://upload.wikimedia.org/wikipedia/commons/thumb/f/f9/Grok-icon.svg/640px-Grok-icon.svg.png',
308+
grok: grokIcon,
309+
'x-ai': grokIcon,
306310
};
307311
308312
function getIconUrl(iconPath: string | undefined, modelId?: string): string {
313+
if (isGrokModel(modelId)) {
314+
return grokIcon;
315+
}
316+
309317
if (iconPath) {
310318
// Handle fallback: protocol for sidebar icons
311319
if (iconPath.startsWith('fallback:')) {

src/lib/db/queries/user-enabled-models.ts

Lines changed: 33 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,28 @@
11
import { db, generateId } from '../index';
22
import { userEnabledModels, type UserEnabledModel } from '../schema';
3-
import { eq, and, inArray, not } from 'drizzle-orm';
3+
import { eq, and, inArray, not, or } from 'drizzle-orm';
44
import { Provider } from '$lib/types';
55
import { getNanoGPTModels } from '$lib/backend/models/nano-gpt';
66

77
const defaultNanoGptModels = [
8-
'zai-org/glm-4.7',
9-
'zai-org/glm-4.6v',
10-
'minimax/minimax-m2.1',
11-
'moonshotai/kimi-k2-thinking',
8+
'zai-org/glm-5',
9+
'zai-org/glm-5:thinking',
10+
'minimax/minimax-m2.7',
11+
'moonshotai/kimi-k2.5',
12+
'moonshotai/kimi-k2.5:thinking',
13+
'deepseek/deepseek-v3.2-speciale',
14+
'deepseek/deepseek-v3.2:thinking',
1215
'deepseek/deepseek-v3.2',
16+
'qwen/qwen3.5-397b-a17b',
17+
'qwen/qwen3.5-397b-a17b-thinking',
18+
'google/gemma-4-31b-it',
19+
'google/gemma-4-31b-it:thinking',
20+
'google/gemma-4-26b-a4b-it',
21+
'google/gemma-4-26b-a4b-it:thinking',
22+
'nvidia/nemotron-3-super-120b-a12b',
23+
'nvidia/nemotron-3-super-120b-a12b:thinking',
24+
'openai/gpt-oss-120b',
25+
'openai/gpt-oss-20b',
1326
];
1427

1528
const nanoGptAllDisabledSentinel = {
@@ -210,44 +223,36 @@ export async function enableInitialModels(userId: string): Promise<void> {
210223

211224
if (existingModels.length > 0) return;
212225

213-
const now = new Date();
214-
await db.insert(userEnabledModels).values(
215-
defaultNanoGptModels.map((modelId) => ({
216-
id: generateId(),
217-
userId,
218-
provider: 'nanogpt',
219-
modelId,
220-
pinned: true,
221-
createdAt: now,
222-
updatedAt: now,
223-
}))
224-
);
226+
await resetDefaultNanoGptModels(userId);
225227
}
226228

227-
export async function enableDefaultModelsOnKeyAdd(userId: string): Promise<void> {
229+
export async function resetDefaultNanoGptModels(userId: string): Promise<void> {
228230
const now = new Date();
229-
// Clean up anything else when adding a key
230231
await db
231232
.delete(userEnabledModels)
232233
.where(
233234
and(
234235
eq(userEnabledModels.userId, userId),
235-
not(inArray(userEnabledModels.modelId, defaultNanoGptModels))
236+
or(
237+
eq(userEnabledModels.provider, Provider.NanoGPT),
238+
eq(userEnabledModels.provider, nanoGptAllDisabledSentinel.provider)
239+
)
236240
)
237241
);
238242

239-
for (const modelId of defaultNanoGptModels) {
240-
const existing = await getEnabledModel(userId, 'nanogpt', modelId);
241-
if (existing) continue;
242-
243-
await db.insert(userEnabledModels).values({
243+
await db.insert(userEnabledModels).values(
244+
defaultNanoGptModels.map((modelId) => ({
244245
id: generateId(),
245246
userId,
246-
provider: 'nanogpt',
247+
provider: Provider.NanoGPT,
247248
modelId,
248249
pinned: true,
249250
createdAt: now,
250251
updatedAt: now,
251-
});
252-
}
252+
}))
253+
);
254+
}
255+
256+
export async function enableDefaultModelsOnKeyAdd(userId: string): Promise<void> {
257+
await resetDefaultNanoGptModels(userId);
253258
}

src/routes/account/models/+page.svelte

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@
4646
});
4747
4848
const nanoGPTModelCollection = $derived(models.from(Provider.NanoGPT));
49-
let bulkToggleState = $state<'enable' | 'disable' | null>(null);
49+
let bulkToggleState = $state<'enable' | 'disable' | 'reset' | null>(null);
5050
5151
const allNanoGPTModelsEnabled = $derived(nanoGPTModelCollection.every((model) => model.enabled));
5252
const someNanoGPTModelsEnabled = $derived(
@@ -80,6 +80,30 @@
8080
bulkToggleState = null;
8181
}
8282
83+
async function resetNanoGPTModels() {
84+
if (!session.current?.user.id) return;
85+
86+
bulkToggleState = 'reset';
87+
88+
await ResultAsync.fromPromise(
89+
mutate(
90+
api.user_enabled_models.set.url,
91+
{
92+
action: 'resetDefaults',
93+
},
94+
{
95+
invalidatePatterns: [api.user_enabled_models.get_enabled.url],
96+
}
97+
),
98+
(error) => {
99+
console.error('Failed to reset NanoGPT models to defaults', error);
100+
return error;
101+
}
102+
);
103+
104+
bulkToggleState = null;
105+
}
106+
83107
const nanoGPTModels = $derived(
84108
fuzzysearch({
85109
haystack: nanoGPTModelCollection.filter((m) => {
@@ -180,6 +204,15 @@
180204
>
181205
Disable all
182206
</Button>
207+
<Button
208+
variant="outline"
209+
size="sm"
210+
onclick={resetNanoGPTModels}
211+
loading={bulkToggleState === 'reset'}
212+
disabled={bulkToggleState !== null || nanoGPTModelCollection.length === 0}
213+
>
214+
Reset defaults
215+
</Button>
183216
</div>
184217
</div>
185218

src/routes/api/db/user-models/+server.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import {
88
setAllProviderModelsEnabled,
99
toggleModelPinned,
1010
enableInitialModels,
11+
resetDefaultNanoGptModels,
1112
} from '$lib/db/queries';
1213
import { isSubscriptionOnlyMode } from '$lib/backend/message-limits';
1314
import { getAuthenticatedUserId } from '$lib/backend/auth-utils';
@@ -49,6 +50,11 @@ export const POST: RequestHandler = async ({ request }) => {
4950
return json({ ok: true });
5051
}
5152

53+
case 'resetDefaults': {
54+
await resetDefaultNanoGptModels(userId);
55+
return json({ ok: true });
56+
}
57+
5258
case 'setAll': {
5359
const { provider, enabled } = body;
5460
if (typeof provider !== 'string' || typeof enabled !== 'boolean') {

0 commit comments

Comments
 (0)