100 lines
2.8 KiB
TypeScript
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),
|
|
})),
|
|
};
|
|
}),
|
|
};
|
|
},
|
|
});
|