Move to threads based system.
This commit is contained in:
@@ -0,0 +1,90 @@
|
||||
'use node';
|
||||
|
||||
import { getAuthUserId } from '@convex-dev/auth/server';
|
||||
import { ConvexError, v } from 'convex/values';
|
||||
|
||||
import type { Id } from './_generated/dataModel';
|
||||
import type { ActionCtx } from './_generated/server';
|
||||
import { internal } from './_generated/api';
|
||||
import { action } from './_generated/server';
|
||||
import { encryptSecret } from './secretCrypto';
|
||||
|
||||
const provider = v.union(
|
||||
v.literal('openai'),
|
||||
v.literal('anthropic'),
|
||||
v.literal('google'),
|
||||
v.literal('openrouter'),
|
||||
v.literal('requesty'),
|
||||
v.literal('litellm'),
|
||||
v.literal('cloudflare_ai_gateway'),
|
||||
v.literal('custom_openai_compatible'),
|
||||
v.literal('opencode_openai_login'),
|
||||
);
|
||||
|
||||
const authType = v.union(
|
||||
v.literal('api_key'),
|
||||
v.literal('opencode_auth_json'),
|
||||
v.literal('none'),
|
||||
);
|
||||
|
||||
const reasoningEffort = v.union(
|
||||
v.literal('none'),
|
||||
v.literal('minimal'),
|
||||
v.literal('low'),
|
||||
v.literal('medium'),
|
||||
v.literal('high'),
|
||||
v.literal('xhigh'),
|
||||
);
|
||||
|
||||
const getRequiredUserId = async (ctx: ActionCtx): Promise<Id<'users'>> => {
|
||||
const userId = await getAuthUserId(ctx);
|
||||
if (!userId) throw new ConvexError('Not authenticated.');
|
||||
return userId;
|
||||
};
|
||||
|
||||
const previewSecret = (secret: string) => {
|
||||
const trimmed = secret.trim();
|
||||
if (!trimmed) return undefined;
|
||||
if (trimmed.startsWith('{')) return 'auth json configured';
|
||||
if (trimmed.length <= 10) return 'configured';
|
||||
return `${trimmed.slice(0, 7)}...${trimmed.slice(-4)}`;
|
||||
};
|
||||
|
||||
export const save = action({
|
||||
args: {
|
||||
profileId: v.optional(v.id('aiProviderProfiles')),
|
||||
name: v.string(),
|
||||
provider,
|
||||
authType,
|
||||
secret: v.optional(v.string()),
|
||||
baseUrl: v.optional(v.string()),
|
||||
defaultModel: v.string(),
|
||||
modelOptions: v.optional(v.array(v.string())),
|
||||
reasoningEffort,
|
||||
enabled: v.boolean(),
|
||||
},
|
||||
handler: async (ctx, args): Promise<Id<'aiProviderProfiles'>> => {
|
||||
const ownerId = await getRequiredUserId(ctx);
|
||||
const secret = args.secret?.trim();
|
||||
if (!args.profileId && args.authType !== 'none' && !secret) {
|
||||
throw new ConvexError('A credential is required for this provider.');
|
||||
}
|
||||
return await ctx.runMutation(
|
||||
internal.aiProviderProfiles.upsertEncryptedInternal,
|
||||
{
|
||||
ownerId,
|
||||
profileId: args.profileId,
|
||||
name: args.name,
|
||||
provider: args.provider,
|
||||
authType: args.authType,
|
||||
encryptedSecret: secret ? encryptSecret(secret) : undefined,
|
||||
secretPreview: secret ? previewSecret(secret) : undefined,
|
||||
baseUrl: args.baseUrl,
|
||||
defaultModel: args.defaultModel,
|
||||
modelOptions: args.modelOptions,
|
||||
reasoningEffort: args.reasoningEffort,
|
||||
enabled: args.enabled,
|
||||
},
|
||||
);
|
||||
},
|
||||
});
|
||||
Reference in New Issue
Block a user