Merge branch 'model-list' into dialog

This commit is contained in:
Thomas G. Lopes 2025-06-16 20:43:48 +01:00
commit 9bc1b3766c
10 changed files with 69 additions and 35 deletions

View file

@ -72,7 +72,7 @@
--muted-foreground: oklch(0.794 0.0372 307.1032);
--accent: oklch(0.3649 0.0508 308.4911);
--accent-foreground: oklch(0.9647 0.0091 341.8035);
--destructive: oklch(0.2258 0.0524 12.6119);
--destructive: oklch(0.5248 0.1368 20.8317);
--destructive-foreground: oklch(1 0 0);
--border: oklch(0.3286 0.0154 343.4461);
--input: oklch(0.3387 0.0195 332.8347);

View file

@ -5,6 +5,7 @@ import * as array from '../../utils/array';
import * as object from '../../utils/object';
import { internal } from './_generated/api';
import { Provider } from '../../types';
import type { Doc } from './_generated/dataModel';
export const getModelKey = (args: { provider: Provider; model_id: string }) => {
return `${args.provider}:${args.model_id}`;
@ -12,12 +13,18 @@ export const getModelKey = (args: { provider: Provider; model_id: string }) => {
export const get_enabled = query({
args: {
user_id: v.string(),
session_token: v.string(),
},
handler: async (ctx, args) => {
handler: async (ctx, args): Promise<Record<string, Doc<'user_enabled_models'>>> => {
const session = await ctx.runQuery(internal.betterAuth.getSession, {
sessionToken: args.session_token,
});
if (!session) throw new Error('Invalid session token');
const models = await ctx.db
.query('user_enabled_models')
.withIndex('by_user', (q) => q.eq('user_id', args.user_id))
.withIndex('by_user', (q) => q.eq('user_id', session.userId))
.collect();
return array.toMap(models, (m) => [getModelKey(m), m]);
@ -26,15 +33,21 @@ export const get_enabled = query({
export const is_enabled = query({
args: {
user_id: v.string(),
sessionToken: v.string(),
provider: providerValidator,
model_id: v.string(),
},
handler: async (ctx, args) => {
handler: async (ctx, args): Promise<boolean> => {
const session = await ctx.runQuery(internal.betterAuth.getSession, {
sessionToken: args.sessionToken,
});
if (!session) throw new Error('Invalid session token');
const model = await ctx.db
.query('user_enabled_models')
.withIndex('by_model_provider_user', (q) =>
q.eq('model_id', args.model_id).eq('provider', args.provider).eq('user_id', args.user_id)
q.eq('model_id', args.model_id).eq('provider', args.provider).eq('user_id', session.userId)
)
.first();
@ -46,13 +59,19 @@ export const get = query({
args: {
provider: providerValidator,
model_id: v.string(),
user_id: v.string(),
session_token: v.string(),
},
handler: async (ctx, args) => {
handler: async (ctx, args): Promise<Doc<'user_enabled_models'> | null> => {
const session = await ctx.runQuery(internal.betterAuth.getSession, {
sessionToken: args.session_token,
});
if (!session) throw new Error('Invalid session token');
const model = await ctx.db
.query('user_enabled_models')
.withIndex('by_model_provider_user', (q) =>
q.eq('model_id', args.model_id).eq('provider', args.provider).eq('user_id', args.user_id)
q.eq('model_id', args.model_id).eq('provider', args.provider).eq('user_id', session.userId)
)
.first();
@ -64,7 +83,6 @@ export const set = mutation({
args: {
provider: providerValidator,
model_id: v.string(),
user_id: v.string(),
enabled: v.boolean(),
session_token: v.string(),
},
@ -73,9 +91,7 @@ export const set = mutation({
sessionToken: args.session_token,
});
if (!session) {
throw new Error('Unauthorized');
}
if (!session) throw new Error('Invalid session token');
const existing = await ctx.db
.query('user_enabled_models')
@ -90,7 +106,8 @@ export const set = mutation({
await ctx.db.delete(existing._id);
} else {
await ctx.db.insert('user_enabled_models', {
...object.pick(args, ['provider', 'model_id', 'user_id']),
...object.pick(args, ['provider', 'model_id']),
user_id: session.userId,
pinned: null,
});
}

View file

@ -9,11 +9,11 @@ export const create = mutation({
name: v.string(),
attach: ruleAttachValidator,
rule: v.string(),
sessionToken: v.string(),
session_token: v.string(),
},
handler: async (ctx, args) => {
const session = await ctx.runQuery(internal.betterAuth.getSession, {
sessionToken: args.sessionToken,
sessionToken: args.session_token,
});
if (!session) throw new Error('Invalid session token');
@ -39,11 +39,11 @@ export const update = mutation({
ruleId: v.id('user_rules'),
attach: ruleAttachValidator,
rule: v.string(),
sessionToken: v.string(),
session_token: v.string(),
},
handler: async (ctx, args) => {
const session = await ctx.runQuery(internal.betterAuth.getSession, {
sessionToken: args.sessionToken,
sessionToken: args.session_token,
});
if (!session) throw new Error('Invalid session token');
@ -63,11 +63,11 @@ export const update = mutation({
export const remove = mutation({
args: {
ruleId: v.id('user_rules'),
sessionToken: v.string(),
session_token: v.string(),
},
handler: async (ctx, args) => {
const session = await ctx.runQuery(internal.betterAuth.getSession, {
sessionToken: args.sessionToken,
sessionToken: args.session_token,
});
if (!session) throw new Error('Invalid session token');
@ -83,11 +83,11 @@ export const remove = mutation({
export const all = query({
args: {
sessionToken: v.string(),
session_token: v.string(),
},
handler: async (ctx, args): Promise<Doc<'user_rules'>[]> => {
const session = await ctx.runQuery(internal.betterAuth.getSession, {
sessionToken: args.sessionToken,
sessionToken: args.session_token,
});
if (!session) throw new Error('Invalid session token');

View file

@ -13,7 +13,7 @@ export class Models {
init = createInit(() => {
const query = useCachedQuery(api.user_enabled_models.get_enabled, {
user_id: session.current?.user.id ?? '',
session_token: session.current?.session.token ?? '',
});
watch(
() => $state.snapshot(query.data),

View file

@ -23,7 +23,7 @@
let creatingRule = $state(false);
const userRulesQuery: QueryResult<Doc<'user_rules'>[]> = useCachedQuery(api.user_rules.all, {
sessionToken: session.current?.session.token ?? '',
session_token: session.current?.session.token ?? '',
});
async function submitNewRule(e: SubmitEvent) {
@ -44,7 +44,7 @@
name,
attach,
rule,
sessionToken: session.current?.session.token ?? '',
session_token: session.current?.session.token ?? '',
});
creatingRule = false;
@ -92,7 +92,7 @@
<Input id="name" name="name" placeholder="My Rule" required />
</div>
<div class="flex flex-col gap-2">
<Label for="attach">Attach</Label>
<Label for="attach">Rule Type</Label>
<select
id="attach"
name="attach"

View file

@ -9,6 +9,7 @@
import { session } from '$lib/state/session.svelte';
import { LocalToasts } from '$lib/builders/local-toasts.svelte';
import { ResultAsync } from 'neverthrow';
import TrashIcon from '~icons/lucide/trash';
type Props = {
rule: Doc<'user_rules'>;
@ -21,6 +22,7 @@
const client = useConvexClient();
let updating = $state(false);
let deleting = $state(false);
const toasts = new LocalToasts({ id });
@ -39,7 +41,7 @@
ruleId: rule._id,
attach,
rule: ruleText,
sessionToken: session.current?.session.token ?? '',
session_token: session.current?.session.token ?? '',
}),
(e) => e
);
@ -53,15 +55,31 @@
updating = false;
}
async function deleteRule() {
deleting = true;
await client.mutation(api.user_rules.remove, {
ruleId: rule._id,
session_token: session.current?.session.token ?? '',
});
deleting = false;
}
</script>
<Card.Root>
<Card.Header>
<Card.Title>{rule.name}</Card.Title>
<div class="flex items-center justify-between">
<Card.Title>{rule.name}</Card.Title>
<Button variant="destructive" size="icon" onclick={deleteRule} disabled={deleting}>
<TrashIcon class="size-4" />
</Button>
</div>
</Card.Header>
<Card.Content tag="form" onsubmit={updateRule}>
<div class="flex flex-col gap-2">
<Label for="attach">Attach</Label>
<Label for="attach">Rule Type</Label>
<select
id="attach"
name="attach"

View file

@ -45,7 +45,6 @@
const res = await ResultAsync.fromPromise(
client.mutation(api.user_enabled_models.set, {
provider,
user_id: session.current.user.id,
model_id: model.id,
enabled: v,
session_token: session.current?.session.token,

View file

@ -52,7 +52,7 @@ async function generateAIResponse(
client.query(api.user_enabled_models.get, {
provider: Provider.OpenRouter,
model_id: modelId,
user_id: session.userId,
session_token: session.token,
}),
(e) => `Failed to get model: ${e}`
);

View file

@ -96,7 +96,7 @@
<Sidebar.Trigger class="fixed top-3 left-2">
<PanelLeftIcon />
</Sidebar.Trigger>
<div class="mx-auto flex size-full max-w-3xl flex-col">
<div class="mx-auto flex size-full min-h-svh max-w-3xl flex-col">
{@render children()}
<div class="mt-auto flex w-full flex-col gap-1">
<ModelPicker class=" w-min " />
@ -129,7 +129,7 @@
<SendIcon />
</Button>
</form>
<div class="flex w-full place-items-center justify-between gap-2">
<div class="flex w-full place-items-center justify-between gap-2 pb-1">
<span class="text-muted-foreground text-xs">
Crafted by <Icons.Svelte class="inline size-3" /> wizards.
</span>

View file

@ -11,7 +11,7 @@
let { class: className }: Props = $props();
const enabledModelsQuery = useCachedQuery(api.user_enabled_models.get_enabled, {
user_id: session.current?.user.id ?? '',
session_token: session.current?.session.token ?? '',
});
const enabledArr = $derived(Object.values(enabledModelsQuery.data ?? {}));