All checks were successful
Deploy to Production / Build and Deploy (push) Successful in 12s
- Sidebar: dynamic brand-accent colors, brainstorm section restyled - AI chat general: popup panel with expand/collapse, hides when contextual AI open - AI chat contextual: tabs reordered (Actions first), X close button, height fix - Settings: all tabs restyled, 6 new color presets (sage, terracotta, iron, etc.) - Global color cleanup: emerald/orange hardcoded → brand-accent dynamic - Brainstorm page: orange → brand-accent throughout - PageEntry animation component added to key pages - Floating AI button: bg-brand-accent instead of hardcoded black - i18n: all 15 locales updated with new AI/billing keys - Billing: freemium quota tracking, BYOK, stripe subscription scaffolding - Admin: integrated into new design - AGENTS.md + CLAUDE.md project rules added
109 lines
3.1 KiB
TypeScript
109 lines
3.1 KiB
TypeScript
import {
|
|
getProviderInstance,
|
|
type ProviderType,
|
|
} from '@/lib/ai/factory';
|
|
import { applyByokToConfig } from '@/lib/byok';
|
|
import {
|
|
resolveAiRoute,
|
|
type AiFeatureLane,
|
|
type ResolvedAiRoute,
|
|
} from '@/lib/ai/router';
|
|
import { withAiProviderFallback } from '@/lib/ai/fallback';
|
|
import type { AIProvider } from '@/lib/ai/types';
|
|
|
|
export interface ProviderForUserResult {
|
|
provider: AIProvider;
|
|
usedByok: boolean;
|
|
route: ResolvedAiRoute;
|
|
}
|
|
|
|
async function resolveProviderForLane(
|
|
lane: AiFeatureLane,
|
|
config: Record<string, string>,
|
|
billingUserId?: string,
|
|
): Promise<ProviderForUserResult> {
|
|
const cfg = { ...config };
|
|
const route = resolveAiRoute(lane, cfg);
|
|
let usedByok = false;
|
|
|
|
if (billingUserId) {
|
|
const overlay = await applyByokToConfig(
|
|
billingUserId,
|
|
route.providerType,
|
|
cfg,
|
|
);
|
|
Object.assign(cfg, overlay.config);
|
|
usedByok = overlay.usedByok;
|
|
}
|
|
|
|
const provider = getProviderInstance(
|
|
route.providerType as ProviderType,
|
|
cfg,
|
|
route.modelName,
|
|
route.embeddingModelName,
|
|
route.ollamaBaseUrl,
|
|
);
|
|
|
|
return { provider, usedByok, route };
|
|
}
|
|
|
|
export async function getChatProviderForBillingUser(
|
|
config: Record<string, string>,
|
|
billingUserId?: string,
|
|
): Promise<ProviderForUserResult> {
|
|
return resolveProviderForLane('chat', config, billingUserId);
|
|
}
|
|
|
|
export async function getTagsProviderForBillingUser(
|
|
config: Record<string, string>,
|
|
billingUserId?: string,
|
|
): Promise<ProviderForUserResult> {
|
|
return resolveProviderForLane('tags', config, billingUserId);
|
|
}
|
|
|
|
export async function getEmbeddingsProviderForBillingUser(
|
|
config: Record<string, string>,
|
|
billingUserId?: string,
|
|
): Promise<ProviderForUserResult> {
|
|
return resolveProviderForLane('embedding', config, billingUserId);
|
|
}
|
|
|
|
/** Run a lane with BYOK overlay; skips system fallback when user key is active. */
|
|
export async function willUseByokForLane(
|
|
lane: AiFeatureLane,
|
|
config: Record<string, string>,
|
|
billingUserId?: string,
|
|
): Promise<{ providerType: string; usedByok: boolean }> {
|
|
if (!billingUserId) {
|
|
const route = resolveAiRoute(lane, config)
|
|
return { providerType: route.providerType, usedByok: false }
|
|
}
|
|
const route = resolveAiRoute(lane, config)
|
|
const overlay = await applyByokToConfig(billingUserId, route.providerType, config)
|
|
return { providerType: route.providerType, usedByok: overlay.usedByok }
|
|
}
|
|
|
|
export async function runLaneWithBillingUser<T>(
|
|
lane: AiFeatureLane,
|
|
config: Record<string, string>,
|
|
billingUserId: string | undefined,
|
|
run: (provider: AIProvider) => Promise<T>,
|
|
): Promise<{ result: T; usedByok: boolean }> {
|
|
if (billingUserId) {
|
|
const resolved =
|
|
lane === 'chat'
|
|
? await getChatProviderForBillingUser(config, billingUserId)
|
|
: lane === 'tags'
|
|
? await getTagsProviderForBillingUser(config, billingUserId)
|
|
: await getEmbeddingsProviderForBillingUser(config, billingUserId);
|
|
|
|
if (resolved.usedByok) {
|
|
const result = await run(resolved.provider);
|
|
return { result, usedByok: true };
|
|
}
|
|
}
|
|
|
|
const result = await withAiProviderFallback(lane, config, run);
|
|
return { result, usedByok: false };
|
|
}
|