-
Notifications
You must be signed in to change notification settings - Fork 1
fix: add GEMINI_API_KEY support and pass through to services #122
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -68,7 +68,8 @@ export class ConversationService { | |
| constructor( | ||
| private readonly conversationRepository: IConversationRepository, | ||
| private readonly llmGateway: IConversationLLMGateway, | ||
| private readonly walletRepository: IWalletRepository | ||
| private readonly walletRepository: IWalletRepository, | ||
| private readonly geminiApiKey?: string | ||
| ) {} | ||
|
|
||
| private async ensureSubmissionInProgress(assignment: Assignment, userId: string): Promise<void> { | ||
|
|
@@ -279,44 +280,6 @@ export class ConversationService { | |
| ); | ||
| } | ||
|
|
||
| const now = Date.now(); | ||
| const userTurnId = randomUUID(); | ||
| let asrUsageReport = createTokenUsageReport([]); | ||
| let llmUsageReport = createTokenUsageReport([]); | ||
| let ttsUsageReport = createTokenUsageReport([]); | ||
| let userInputText: string; | ||
|
|
||
| if ('audioBase64' in input) { | ||
| try { | ||
| const asrExecutor = getASRExecutor(); | ||
| asrExecutor.resetTokenUsage(); | ||
| userInputText = await asrExecutor.transcribe(input.audioBase64, input.audioMimeType); | ||
| userInputText = userInputText.trim(); | ||
| if (!userInputText) { | ||
| throw errorResponse( | ||
| 'No speech detected in the audio. Please try again or use text input.', | ||
| HttpStatus.BAD_REQUEST, | ||
| ServerErrorCode.INVALID_INPUT | ||
| ); | ||
| } | ||
| asrUsageReport = createTokenUsageReport([ | ||
| { | ||
| feature: TOKEN_USAGE_FEATURES.CONVERSATION_ASR, | ||
| usage: asrExecutor.getTokenUsage() | ||
| } | ||
| ]); | ||
| } catch (error) { | ||
| if (error instanceof Response) throw error; | ||
| throw errorResponse( | ||
| 'Failed to transcribe audio. Please try again or use text input.', | ||
| HttpStatus.INTERNAL_SERVER_ERROR, | ||
| ServerErrorCode.INTERNAL_ERROR | ||
| ); | ||
| } | ||
| } else { | ||
| userInputText = input.text; | ||
| } | ||
|
|
||
| const assignment = await this.conversationRepository.getAssignment(conversation.assignmentId); | ||
| if (!assignment) { | ||
| throw errorResponse('Assignment not found', HttpStatus.NOT_FOUND, ServerErrorCode.NOT_FOUND); | ||
|
|
@@ -369,6 +332,46 @@ export class ConversationService { | |
| } | ||
| } | ||
|
|
||
| const requestApiKey = apiKey ?? this.geminiApiKey; | ||
|
|
||
| const now = Date.now(); | ||
| const userTurnId = randomUUID(); | ||
| let asrUsageReport = createTokenUsageReport([]); | ||
| let llmUsageReport = createTokenUsageReport([]); | ||
| let ttsUsageReport = createTokenUsageReport([]); | ||
| let userInputText: string; | ||
|
|
||
| if ('audioBase64' in input) { | ||
| try { | ||
| const asrExecutor = getASRExecutor(requestApiKey); | ||
| asrExecutor.resetTokenUsage(); | ||
|
Comment on lines
+335
to
+347
|
||
| userInputText = await asrExecutor.transcribe(input.audioBase64, input.audioMimeType); | ||
| userInputText = userInputText.trim(); | ||
| if (!userInputText) { | ||
| throw errorResponse( | ||
| 'No speech detected in the audio. Please try again or use text input.', | ||
| HttpStatus.BAD_REQUEST, | ||
| ServerErrorCode.INVALID_INPUT | ||
| ); | ||
| } | ||
| asrUsageReport = createTokenUsageReport([ | ||
| { | ||
| feature: TOKEN_USAGE_FEATURES.CONVERSATION_ASR, | ||
| usage: asrExecutor.getTokenUsage() | ||
| } | ||
| ]); | ||
| } catch (error) { | ||
| if (error instanceof Response) throw error; | ||
| throw errorResponse( | ||
| 'Failed to transcribe audio. Please try again or use text input.', | ||
| HttpStatus.INTERNAL_SERVER_ERROR, | ||
| ServerErrorCode.INTERNAL_ERROR | ||
| ); | ||
|
Comment on lines
+344
to
+369
|
||
| } | ||
| } else { | ||
| userInputText = input.text; | ||
| } | ||
|
|
||
| let llmResult: Awaited<ReturnType<IConversationLLMGateway['process']>>; | ||
| try { | ||
| llmResult = await this.llmGateway.process({ | ||
|
|
@@ -377,12 +380,13 @@ export class ConversationService { | |
| userInputText, | ||
| question: assignment.question || '', | ||
| prompt: assignment.prompt || '', | ||
| apiKey | ||
| apiKey: requestApiKey | ||
| }); | ||
| } catch (error) { | ||
| if ( | ||
| error instanceof Error && | ||
| assignment.courseId && | ||
| apiKey && | ||
| (error.message.includes('401') || | ||
| error.message.includes('403') || | ||
| error.message.includes('API_KEY_INVALID')) | ||
|
|
@@ -406,7 +410,7 @@ export class ConversationService { | |
| let aiAudioBase64: string; | ||
| const aiAudioMimeType = 'audio/mp3'; | ||
| try { | ||
| const ttsExecutor = getTTSExecutor(); | ||
| const ttsExecutor = getTTSExecutor(requestApiKey); | ||
| ttsExecutor.resetTokenUsage(); | ||
| aiAudioBase64 = await ttsExecutor.synthesize(llmResult.aiMessage); | ||
| ttsUsageReport = createTokenUsageReport([ | ||
|
|
@@ -419,6 +423,7 @@ export class ConversationService { | |
| if ( | ||
| error instanceof Error && | ||
| assignment.courseId && | ||
| apiKey && | ||
| (error.message.includes('401') || | ||
| error.message.includes('403') || | ||
| error.message.includes('API_KEY_INVALID')) | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
.env.exampledocumentsGEMINI_API_KEY, but the runtime/test code also supports falling back toGOOGLE_API_KEY(and several tests skip based on that). Consider documenting theGOOGLE_API_KEYfallback here (or adding a short comment) so local setup matches the supported configuration.