From 17a842766836e7983c608f299084fa5f5f444d41 Mon Sep 17 00:00:00 2001 From: Altay Date: Sun, 17 Dec 2023 13:03:27 +0300 Subject: [PATCH] feat: increment/decrement `ai_credits` on `ai` services --- .../ai/create-episode-summary-stream.ts | 46 +++++---- lib/services/ai/transcribe-episode.ts | 97 +++++++++++-------- 2 files changed, 80 insertions(+), 63 deletions(-) diff --git a/lib/services/ai/create-episode-summary-stream.ts b/lib/services/ai/create-episode-summary-stream.ts index 161658d..5263601 100644 --- a/lib/services/ai/create-episode-summary-stream.ts +++ b/lib/services/ai/create-episode-summary-stream.ts @@ -4,6 +4,7 @@ import type { OpenAI } from 'openai'; import { OpenAIStream } from 'ai'; import { promptTokensEstimate } from 'openai-chat-tokens'; +import { incrementAccountAICredits } from '../account'; import { saveEpisodeContentSummary } from '../episode-content'; import { openai } from './openai'; @@ -26,32 +27,37 @@ export async function createEpisodeSummaryStream({ episode: Pick, 'id' | 'title'>; transcription: string; }) { - const messages: OpenAI.Chat.Completions.ChatCompletionMessageParam[] = [ - { - content: ` + try { + const messages: OpenAI.Chat.Completions.ChatCompletionMessageParam[] = [ + { + content: ` Summarize the following transcription of the podcast episode "${episode.title}" by following these guidelines: - Skip the ads and intro. - Use paragraphs or bullet point lists. - Prefer short, clean sentences. - Write the summary in the same language as the transcription. `, - role: 'system', - }, - { - content: transcription, - role: 'system', - }, - ]; + role: 'system', + }, + { + content: transcription, + role: 'system', + }, + ]; - const response = await openai.chat.completions.create({ - messages, - model: getModel(promptTokensEstimate({ messages })), - stream: true, - }); + const response = await openai.chat.completions.create({ + messages, + model: getModel(promptTokensEstimate({ messages })), + stream: true, + }); - return OpenAIStream(response, { - onCompletion: async (completion) => { - await saveEpisodeContentSummary(episode.id, completion); - }, - }); + return OpenAIStream(response, { + onCompletion: async (completion) => { + await saveEpisodeContentSummary(episode.id, completion); + }, + }); + } catch (error) { + await incrementAccountAICredits(); + throw error; + } } diff --git a/lib/services/ai/transcribe-episode.ts b/lib/services/ai/transcribe-episode.ts index acae1f7..0c5cc2b 100644 --- a/lib/services/ai/transcribe-episode.ts +++ b/lib/services/ai/transcribe-episode.ts @@ -6,60 +6,71 @@ import { DatabaseError } from '@/lib/errors'; import { getFinalRedirectURL } from '@/lib/utils/get-final-redirect-url'; import { cookies } from 'next/headers'; -import { getAccountId } from '../account'; +import { + decrementAccountAICredits, + getAccountId, + incrementAccountAICredits, +} from '../account'; import { createSupabaseServerClient } from '../supabase/server'; import { transcribeAudio } from './deepgram'; export const transcribeEpisode = async (id: Tables<'episode'>['id']) => { - const supabase = createSupabaseServerClient(cookies()); + try { + await decrementAccountAICredits(); - const existingEpisodeContentQuery = await supabase - .from('episode_content') - .select('transcription') - .eq('episode', id); + const supabase = createSupabaseServerClient(cookies()); - if (existingEpisodeContentQuery.error) { - throw new DatabaseError(existingEpisodeContentQuery.error); - } + const existingEpisodeContentQuery = await supabase + .from('episode_content') + .select('transcription') + .eq('episode', id); - if (existingEpisodeContentQuery.data[0]?.transcription) { - return existingEpisodeContentQuery.data[0].transcription; - } + if (existingEpisodeContentQuery.error) { + throw new DatabaseError(existingEpisodeContentQuery.error); + } - const episodeQuery = await supabase - .from('episode') - .select('audio_url, show(language)') - .eq('id', id) - .single(); + if (existingEpisodeContentQuery.data[0]?.transcription) { + return existingEpisodeContentQuery.data[0].transcription; + } - if (episodeQuery.error) { - throw new DatabaseError(episodeQuery.error); - } + const episodeQuery = await supabase + .from('episode') + .select('audio_url, show(language)') + .eq('id', id) + .single(); - const url = await getFinalRedirectURL(episodeQuery.data.audio_url); - const transcription = await transcribeAudio({ - language: episodeQuery.data.show?.language ?? undefined, - url, - }); + if (episodeQuery.error) { + throw new DatabaseError(episodeQuery.error); + } - const updateEpisodeContentQuery = await supabase - .from('episode_content') - .upsert( - { - account: await getAccountId(), - episode: id, - transcription, - }, - { - onConflict: 'episode', - }, - ) - .select('transcription') - .single(); + const url = await getFinalRedirectURL(episodeQuery.data.audio_url); + const transcription = await transcribeAudio({ + language: episodeQuery.data.show?.language ?? undefined, + url, + }); - if (updateEpisodeContentQuery.error) { - throw new DatabaseError(updateEpisodeContentQuery.error); - } + const updateEpisodeContentQuery = await supabase + .from('episode_content') + .upsert( + { + account: await getAccountId(), + episode: id, + transcription, + }, + { + onConflict: 'episode', + }, + ) + .select('transcription') + .single(); + + if (updateEpisodeContentQuery.error) { + throw new DatabaseError(updateEpisodeContentQuery.error); + } - return transcription; + return transcription; + } catch (error) { + await incrementAccountAICredits(); + throw error; + } };