Files
spoon/packages/backend/convex/aiProviderModels.ts
T
Gabriel Brown d207b8b0b8
Build and Push Spoon Images / quality (push) Successful in 1m41s
Build and Push Spoon Images / build-images (push) Successful in 7m4s
Add features & update project
2026-06-23 02:06:58 -04:00

100 lines
2.8 KiB
TypeScript

import type { Doc } from './_generated/dataModel';
import { query } from './_generated/server';
import { getRequiredUserId } from './model';
type AiProviderProfileWithDefault = Doc<'aiProviderProfiles'> & {
isDefault?: boolean;
};
const labelForModel = (model: string): string => {
const parts = model.split('/');
const raw = parts[parts.length - 1] ?? model;
return raw
.replaceAll('-', ' ')
.replace(/\b\w/g, (letter: string) => letter.toUpperCase());
};
const recommendedFor = (model: string) => {
const lower = model.toLowerCase();
const tags: ('coding' | 'review' | 'fast' | 'large_context')[] = [];
if (
lower.includes('codex') ||
lower.includes('claude') ||
lower.includes('sonnet')
) {
tags.push('coding');
}
if (
lower.includes('mini') ||
lower.includes('haiku') ||
lower.includes('flash')
) {
tags.push('fast');
}
if (
lower.includes('200k') ||
lower.includes('1m') ||
lower.includes('large')
) {
tags.push('large_context');
}
if (!tags.length) tags.push('review');
return tags;
};
export const listAvailableForUser = query({
args: {},
handler: async (ctx) => {
const ownerId = await getRequiredUserId(ctx);
const profiles = await ctx.db
.query('aiProviderProfiles')
.withIndex('by_owner', (q) => q.eq('ownerId', ownerId))
.order('desc')
.collect();
const configuredProfiles = profiles.filter(
(profile) =>
profile.enabled &&
(profile.authType === 'none' || Boolean(profile.encryptedSecret)),
);
const explicitDefault = configuredProfiles.find(
(profile) => (profile as AiProviderProfileWithDefault).isDefault,
);
const defaultProfileId =
explicitDefault?._id ??
(configuredProfiles.length === 1
? configuredProfiles[0]?._id
: undefined);
return {
profiles: profiles
.filter((profile) => profile.enabled)
.map((profile) => {
const configured =
profile.authType === 'none' || Boolean(profile.encryptedSecret);
const modelIds = [
profile.defaultModel,
...(profile.modelOptions ?? []),
]
.map((model) => model.trim())
.filter(Boolean)
.filter((model, index, all) => all.indexOf(model) === index);
return {
profileId: profile._id,
profileName: profile.name,
provider: profile.provider,
configured,
enabled: profile.enabled,
isDefault: profile._id === defaultProfileId,
defaultModel: profile.defaultModel,
reasoningEffort: profile.reasoningEffort,
models: modelIds.map((id) => ({
id,
label: labelForModel(id),
recommendedFor: recommendedFor(id),
})),
};
}),
};
},
});