Skip to content
Open
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions apps/web/__tests__/unit/generate-ai-title.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,9 @@ vi.mock("@cap/web-backend", () => ({
Storage: {},
}));

vi.mock("@/lib/groq-client", () => ({
GROQ_MODEL: "test-model",
getGroqClient: vi.fn(() => null),
vi.mock("@/lib/ai-provider", () => ({
getAiClient: vi.fn(() => null),
getAiModel: vi.fn(() => "test-model"),
}));

vi.mock("@/lib/server", () => ({
Expand Down
9 changes: 7 additions & 2 deletions apps/web/actions/videos/get-status.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,10 @@ export async function getVideoStatus(

const metadata: VideoMetadata = (video.metadata as VideoMetadata) || {};

if (!video.transcriptionStatus && serverEnv().DEEPGRAM_API_KEY) {
if (
!video.transcriptionStatus &&
(serverEnv().DEEPGRAM_API_KEY || serverEnv().STT_BASE_URL)
) {
const activeUpload = await db()
.select({
videoId: videoUploads.videoId,
Expand Down Expand Up @@ -151,7 +154,9 @@ export async function getVideoStatus(
video.transcriptionStatus === "COMPLETE" &&
!metadata.aiGenerationStatus &&
!metadata.summary &&
(serverEnv().GROQ_API_KEY || serverEnv().OPENAI_API_KEY);
(serverEnv().GROQ_API_KEY ||
serverEnv().OPENAI_API_KEY ||
serverEnv().AI_BASE_URL);

if (shouldTriggerAiGeneration) {
try {
Expand Down
14 changes: 7 additions & 7 deletions apps/web/actions/videos/translate-transcript.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { Storage } from "@cap/web-backend";
import type { Video } from "@cap/web-domain";
import { eq } from "drizzle-orm";
import { Effect, Option } from "effect";
import { GROQ_MODEL, getGroqClient } from "@/lib/groq-client";
import { getAiClient, getAiModel } from "@/lib/ai-provider";
import { runPromise } from "@/lib/server";
import { decodeStorageVideo } from "@/lib/video-storage";
import {
Expand Down Expand Up @@ -38,8 +38,8 @@ export async function translateTranscript(
};
}

const groq = getGroqClient();
if (!groq) {
const ai = getAiClient();
if (!ai) {
return {
success: false,
message: "Translation service not configured",
Expand Down Expand Up @@ -94,7 +94,7 @@ export async function translateTranscript(
const translatedVtt = await translateVttContent(
originalVtt.value,
targetLanguage,
groq,
ai,
);

if (!translatedVtt) {
Expand Down Expand Up @@ -124,7 +124,7 @@ export async function translateTranscript(
async function translateVttContent(
vttContent: string,
targetLanguage: LanguageCode,
groq: NonNullable<ReturnType<typeof getGroqClient>>,
ai: NonNullable<ReturnType<typeof getAiClient>>,
): Promise<string | null> {
const targetLanguageName = SUPPORTED_LANGUAGES[targetLanguage];

Expand All @@ -144,8 +144,8 @@ VTT content to translate:
${vttContent}`;

try {
const response = await groq.chat.completions.create({
model: GROQ_MODEL,
const response = await ai.chat.completions.create({
model: getAiModel(),
messages: [{ role: "user", content: prompt }],
temperature: 0.3,
max_tokens: 8000,
Expand Down
59 changes: 59 additions & 0 deletions apps/web/lib/ai-provider.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import "server-only";

import { serverEnv } from "@cap/env";
import OpenAI from "openai";

const GROQ_DEFAULT_BASE_URL = "https://api.groq.com/openai/v1";
const GROQ_DEFAULT_MODEL = "openai/gpt-oss-120b";
const OPENAI_DEFAULT_MODEL = "gpt-4o-mini";
const STT_DEFAULT_MODEL = "whisper-1";

let aiClient: OpenAI | null = null;
let sttClient: OpenAI | null = null;

export function getAiClient(): OpenAI | null {
if (aiClient) return aiClient;
Comment thread
kovashikawa marked this conversation as resolved.
Outdated

const env = serverEnv();
if (env.AI_BASE_URL) {
aiClient = new OpenAI({
baseURL: env.AI_BASE_URL,
Comment thread
kovashikawa marked this conversation as resolved.
apiKey:
env.AI_API_KEY ?? env.GROQ_API_KEY ?? env.OPENAI_API_KEY ?? "none",
});
} else if (env.GROQ_API_KEY) {
aiClient = new OpenAI({
baseURL: GROQ_DEFAULT_BASE_URL,
apiKey: env.GROQ_API_KEY,
});
} else if (env.OPENAI_API_KEY) {
aiClient = new OpenAI({ apiKey: env.OPENAI_API_KEY });
}

return aiClient;
}

export function getAiModel(): string {
const env = serverEnv();
if (env.AI_MODEL) return env.AI_MODEL;
if (!env.AI_BASE_URL && env.GROQ_API_KEY) return GROQ_DEFAULT_MODEL;
return OPENAI_DEFAULT_MODEL;
}

export function getSttClient(): OpenAI | null {
if (sttClient) return sttClient;

const env = serverEnv();
if (!env.STT_BASE_URL) return null;

sttClient = new OpenAI({
baseURL: env.STT_BASE_URL,
apiKey: env.STT_API_KEY ?? "none",
});

return sttClient;
}

export function getSttModel(): string {
return serverEnv().STT_MODEL ?? STT_DEFAULT_MODEL;
}
9 changes: 7 additions & 2 deletions apps/web/lib/generate-ai.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,15 @@ export async function startAiGeneration(
videoId: Video.VideoId,
userId: string,
): Promise<GenerateAiResult> {
if (!serverEnv().GROQ_API_KEY && !serverEnv().OPENAI_API_KEY) {
if (
!serverEnv().GROQ_API_KEY &&
!serverEnv().OPENAI_API_KEY &&
!serverEnv().AI_BASE_URL
) {
return {
success: false,
message: "Missing AI API keys (Groq or OpenAI)",
message:
"Missing AI API keys (set GROQ_API_KEY, OPENAI_API_KEY, or AI_BASE_URL)",
};
}

Expand Down
20 changes: 0 additions & 20 deletions apps/web/lib/groq-client.ts

This file was deleted.

14 changes: 8 additions & 6 deletions apps/web/lib/messenger/agent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import "server-only";

import type { MessengerMessageRole } from "@cap/database/schema";
import { serverEnv } from "@cap/env";
import { GROQ_MODEL, getGroqClient } from "@/lib/groq-client";
import { getAiClient, getAiModel } from "@/lib/ai-provider";
import { CAP_REFERENCE_GUIDE, MESSENGER_AGENT_PROMPT } from "./constants";
import { getKnowledgeTag, searchSupermemory } from "./supermemory";

Expand Down Expand Up @@ -165,18 +165,18 @@ const callOpenAi = async ({
return parseOpenAiContent(payload);
};

const callGroq = async ({
const callAiProvider = async ({
systemPrompt,
history,
}: {
systemPrompt: string;
history: ConversationMessage[];
}) => {
const client = getGroqClient();
const client = getAiClient();
if (!client) return null;

const completion = await client.chat.completions.create({
model: GROQ_MODEL,
model: getAiModel(),
temperature: 0.65,
max_tokens: 500,
messages: [
Expand Down Expand Up @@ -227,8 +227,10 @@ export const generateMessengerAgentReply = async ({
);
if (fromOpenAi) return fromOpenAi;

const fromGroq = await callGroq({ systemPrompt, history }).catch(() => null);
if (fromGroq) return fromGroq;
const fromAi = await callAiProvider({ systemPrompt, history }).catch(
() => null,
);
if (fromAi) return fromAi;

return "Oh no, I'm so sorry about this! I'm having a little technical hiccup on my end. Someone from the team will jump in here shortly to help you out though!";
};
2 changes: 1 addition & 1 deletion apps/web/lib/transcribe.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ export async function transcribeVideo(
aiGenerationEnabled = false,
_isRetry = false,
): Promise<TranscribeResult> {
if (!serverEnv().DEEPGRAM_API_KEY) {
if (!serverEnv().DEEPGRAM_API_KEY && !serverEnv().STT_BASE_URL) {
return {
success: false,
message: "Missing necessary environment variables",
Expand Down
2 changes: 1 addition & 1 deletion apps/web/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,6 @@
"framer-motion": "^11.13.1",
"geist": "^1.3.1",
"gif.js": "0.2.0",
"groq-sdk": "^0.29.0",
"hls.js": "^1.5.3",
"hono": "^4.7.1",
"js-cookie": "^3.0.5",
Expand All @@ -114,6 +113,7 @@
"next": "16.2.1",
"next-auth": "^4.24.5",
"next-mdx-remote": "^6.0.0",
"openai": "^4.86.0",
"posthog-js": "^1.215.3",
"posthog-node": "^4.11.2",
"prettier": "^3.7.4",
Expand Down
Loading