Conversation
the second page seems to still be completely empty
Merge frontend into the full-stack dev branch
There was a problem hiding this comment.
Summary of Changes
Hello @ThePrunerator, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!
This pull request introduces a complete full-stack application for audio transcription and summarization. It includes a React frontend for user interaction and a FastAPI backend for processing audio files and managing transcriptions. The application allows users to upload audio, view transcriptions, and manage historical data, providing a comprehensive solution for meeting memo creation.
Highlights
- Full-Stack Implementation: This PR merges the full-stack application, including a React frontend and a FastAPI backend, into the main branch.
- Audio Transcription: The application now supports audio uploads to the backend, which are then transcribed and output to the user.
- Historical Transcriptions: Users can now view all stored transcriptions from the past and expand them to display the transcribed text.
- Backend API: The FastAPI backend includes endpoints for uploading, transcribing, deleting, and summarizing audio files.
- Frontend UI: The React frontend provides a user interface for uploading audio files, viewing transcriptions, and managing historical data.
- Docker Configuration: The Dockerfile for the backend has been updated to include necessary dependencies and use uv for package management.
Using Gemini Code Assist
The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.
Invoking Gemini
You can request assistance from Gemini at any point in your pull request via creating an issue comment (i.e. comment on the pull request page) using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands.
| Feature | Command | Description |
|---|---|---|
| Code Review | /gemini review |
Performs a code review for the current pull request in its current state. |
| Pull Request Summary | /gemini summary |
Provides a summary of the current pull request in its current state. |
| Comment | @gemini-code-assist | Responds in comments when explicitly tagged, both in issue comments and review comments. |
| Help | /gemini help |
Displays a list of available commands. |
Customization
To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.
Limitations & Feedback
Gemini Code Assist is currently in preview and may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments to provide feedback.
You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.
Footnotes
-
Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution. ↩
There was a problem hiding this comment.
Code Review
The pull request introduces a full-stack structure with a React frontend and FastAPI backend, enabling audio transcription and summarization. Key changes include setting up routing in the frontend, implementing file upload, recording, listing, viewing details, and deletion functionalities, and updating the backend Dockerfile and main application file. Several areas for improvement were identified, particularly regarding error handling, state management in the frontend, and consistency in the backend API.
|
|
||
|
|
||
| @app.delete("/jobs/{uuid}") | ||
| async def delete_item(uuid: int) -> dict[str, str]: |
There was a problem hiding this comment.
The UUID parameter here is typed as int, while in other routes (/jobs/{uuid}/filename, /jobs/{uuid}/transcript, /jobs/{uuid}/summarise) it is typed as str. This inconsistency can lead to unexpected type errors or incorrect routing. It should be consistent across all routes that use a UUID.
| async def delete_item(uuid: int) -> dict[str, str]: | |
| async def delete_item(uuid: str) -> dict[str, str]: |
| .catch((err) => { | ||
| setSummary("Error fetching summary."); | ||
| setIsSummarizing(false); | ||
| }); |
There was a problem hiding this comment.
The isSummarizing state is not set back to false in the .catch block of the fetchSummary function. If the summarization request fails, the loading spinner will remain visible indefinitely. Ensure setIsSummarizing(false) is called in the catch block.
.catch((err) => {
console.error("Error fetching summary:", err);
setSummary("Error fetching summary.");
setIsSummarizing(false);
});| from fastapi import FastAPI, UploadFile, File | ||
| import time | ||
|
|
||
| app = FastAPI() | ||
|
|
||
| @app.get("/jobs/{uuid}/filename") | ||
| async def get_file_name(uuid: str) -> dict[str, str]: | ||
| """ | ||
| Returns the filename associated with a given UUID. | ||
| """ | ||
| csv_list = { | ||
| "1": "file_1", | ||
| "2": "file_2", | ||
| "3": "file_3", | ||
| "4": "file_4", | ||
| "5": "file_5" | ||
| } | ||
|
|
||
| if uuid in csv_list: | ||
| return {"name": csv_list[uuid]} | ||
| return {"error": "UUID not found"} | ||
|
|
||
|
|
||
| @app.get("/jobs") | ||
| async def check_files() -> dict[str, dict[str, str]]: | ||
| """ | ||
| Dummy route to return a JSON response including a list of strings. | ||
| """ | ||
| return { | ||
| "csv_list": | ||
| { | ||
| "1": "file_1", | ||
| "2": "file_2", | ||
| "3": "file_3", | ||
| "4": "file_4", | ||
| "5": "file_5" | ||
| } | ||
| } | ||
|
|
||
|
|
||
| @app.post("/jobs") | ||
| async def transcribe(file: UploadFile = File(...)) -> dict[str, str | list[dict[str, str]]]: | ||
| """ | ||
| Dummy function to transcribe input audio file. | ||
| """ | ||
| time.sleep(3) | ||
| dummy_transcription = [ | ||
| {"Customer": "Hi, I'm having trouble connecting to the internet."}, | ||
| {"Support Agent": "I'm sorry to hear that. Can you tell me if any of the router lights are blinking red?"}, | ||
| {"Customer": "Yes, the one labeled 'WAN' is flashing red."}, | ||
| {"Support Agent": "Alright. That usually means the router isn't detecting a signal. Let's try restarting it first."}, | ||
| {"Customer": "Okay, I've unplugged it and plugged it back in."}, | ||
| {"Support Agent": "Great. Let's give it a minute... Are the lights back to normal now?"}, | ||
| {"Customer": "Yes, they're all green now!"}, | ||
| {"Support Agent": "Perfect. You should be back online. Is everything working on your end?"}, | ||
| {"Customer": "Yes, thank you so much for your help!"}, | ||
| {"Support Agent": "You're very welcome. Have a great day!"} | ||
| ] | ||
|
|
||
| return { | ||
| "uuid": "1", | ||
| "file_name": "new_file", | ||
| "transcript": dummy_transcription | ||
| } | ||
|
|
||
|
|
||
| @app.delete("/jobs/{uuid}") | ||
| async def delete_item(uuid: int) -> dict[str, str]: | ||
| uuid_files = { | ||
| 1: "file_1", | ||
| 2: "file_2", | ||
| 3: "file_3", | ||
| 4: "file_4", | ||
| 5: "file_5" | ||
| } | ||
| return {"status": "success", "message": f"Job with UUID {uuid} and file {uuid_files[uuid]} deleted successfully."} | ||
|
|
||
|
|
||
| @app.get("/jobs/{uuid}/transcript") | ||
| async def get_file_transcript(uuid: str) -> dict[str, list[dict[str, str]]]: | ||
| """ | ||
| Gets the transcription contents from the file. | ||
| """ | ||
| dummy_transcripts = { | ||
| "1": [ | ||
| {"Speaker 1": "Good morning, everyone. Let's begin with the project updates."}, | ||
| {"Speaker 2": "Sure. We've completed the initial design phase and are moving into development."}, | ||
| {"Speaker 1": "Excellent. Any blockers or concerns we should address now?"}, | ||
| {"Speaker 3": "Not at the moment, but we'll need additional resources by next sprint."} | ||
| ], | ||
| "2": [ | ||
| {"Interviewer": "Can you tell me about a time you handled a challenging situation at work?"}, | ||
| {"Candidate": "Yes, there was a time we had to deliver a project in half the usual time. I reorganized our workflow and prioritized key features."}, | ||
| {"Interviewer": "Impressive. What was the result?"}, | ||
| {"Candidate": "We launched on time and received positive feedback from the client."} | ||
| ], | ||
| "3": [ | ||
| {"Professor": "Today, we're going to explore the principles of quantum mechanics."}, | ||
| {"Student": "Is this related to what we covered in thermodynamics?"}, | ||
| {"Professor": "That's a great question. There are connections, but the foundational principles are quite different."} | ||
| ], | ||
| "4": [ | ||
| {"Host": "Welcome back to TechTalk Weekly. I'm your host, Jamie."}, | ||
| {"Guest": "Thanks for having me, Jamie. Excited to dive into today's topic."}, | ||
| {"Host": "Let's start with AI and privacy concerns. What's your take?"}, | ||
| {"Guest": "It's a big issue. Transparency and data control need to be built into systems by default."} | ||
| ], | ||
| "5": [ | ||
| {"Alex": "Hey, did you watch the match last night?"}, | ||
| {"Jordan": "Yeah! It was intense. That last-minute goal was wild."}, | ||
| {"Alex": "I know, right? Totally unexpected turn."} | ||
| ] | ||
| } | ||
|
|
||
| return {"result": dummy_transcripts[uuid]} | ||
|
|
||
| @app.post("/jobs/{uuid}/summarise") | ||
| def summarise_job(uuid: str) -> dict: | ||
| """ | ||
| Dummy function to summarise transcription result | ||
| with the help of an LLM. | ||
| """ | ||
| time.sleep(3) | ||
|
|
||
| return { | ||
| "status": "success", | ||
| "summary": "The quick brown fox jumps over the lazy dog." | ||
| } No newline at end of file |
There was a problem hiding this comment.
This file appears to contain dummy API routes that duplicate functionality present in backend/main.py. Having two separate FastAPI applications or sets of routes serving similar purposes is confusing and likely unintended. It seems this file might have been a placeholder during development and should not be merged into the main branch alongside the actual backend implementation.
| setLoading(false); | ||
| if (data.uuid) summarizeTranscript(data.uuid); |
There was a problem hiding this comment.
Similar to the recording logic, the loading state is not reset to false in the .then block after a successful upload and transcription. It is only reset in the .catch block. This means the loading indicator will not disappear if the upload/transcription succeeds but summarization fails.
setTranscription(Array.isArray(data.transcript) ? data.transcript : []);
if (data.uuid) {
await summarizeTranscript(data.uuid);
}
setLoading(false);
})| return updated; | ||
| }); | ||
| }) | ||
| .catch(err => console.error("Failed to delete file:", err)); |
| .catch(err => { | ||
| console.error("Failed to fetch transcription:", err); | ||
| setLoading(false); | ||
| }); |
|
|
||
| # Install system packages, Rust + uv, and audio tools | ||
| RUN apt-get update && \ | ||
| apt-get install -y curl build-essential gcc ffmpeg libsndfile1 bash && \ |
There was a problem hiding this comment.
Installing build-essential and gcc adds significant size to the Docker image. While necessary for compiling some dependencies, consider if these are strictly required at runtime or if dependencies can be installed from pre-built wheels. If they are only needed for the build stage, consider a multi-stage build to keep the final image smaller.
| setFileName(data.name); | ||
| } | ||
| }) | ||
| .catch(err => console.error("Failed to fetch file name:", err)); |
| )} | ||
| </div> | ||
|
|
||
| <a href="/" className="button-link">Back to home</a> |
There was a problem hiding this comment.
No description provided.