# Project export: STUDYAI

This document was generated by HackStack to give an AI agent context about a hackathon project. Sections are labeled with their provenance; content marked as truncated was cut to keep this document small.

## Project metadata

- Hackathon: Cal Hacks 10.0
- Tagline: Systematic Teaching Using Dynamic Yielding and Autonomous Intelligence (StudyAI) is an advanced, voice-activated study assistant designed to aid students in learning and comprehending various subjects
- Devpost: https://devpost.com/software/studyai-ru8lf4
- GitHub: https://github.com/IdkwhatImD0ing/STUDYAI
- Team: 2 GitHub contributor(s) — Bill Zhang (18 commits), Varun Swaminathan (5 commits)

## Devpost submission (written by the team)

### Overview

*S*ystematic *T*eaching *U*sing *D*ynamic *Y*ielding and *A*utonomous *I*ntelligence Overview StudyAI is a groundbreaking, voice-activated study assistant engineered to redefine the self-study experience. Utilizing cutting-edge machine learning, voice recognition, and natural language understanding technologies, this platform offers an array of features designed to facilitate efficient and effective study sessions.

### Inspiration

In an era flooded with information, the conventional methods of self-study are fraught with challenges like information overload, disorganized note management, and a lack of personalized learning experiences. Study AI tackles these issues head-on, offering a revolutionary approach to academic learning and comprehension. Goals Our mission is to revolutionize self-study by providing an intelligent, voice-activated assistant that delivers a seamless and intuitive user experience. Study AI aims to become the go-to platform for students, offering features like text and video summarization, explanatory image generation, educational video recommendations, and personalized note management. Built With Frontend: Reflex.dev with custom React components as plugins Backend: FastAPI (via Reflex.dev) Voice Recognition and Text-to-Speech: 11Labs, Whisper Autonomous Agents: OpenAI function calling agents Text Summarization: Using mistralai/Mistral-7B-Instruct-v0.1 from TogetherAI Image Generation: Using stabilityai/stable-diffusion-2-1 from TogetherAI Challenges Implementing real-time voice recognition and natural language understanding with minimal latency (less than 5 seconds response time) Ensuring seamless integration of multiple technologies, including machine learning models and external APIs. Achieving scalability while maintaining high performance and reliability. Ensuring data privacy and security. Accomplishments Successfully developed a voice-activated command parsing module. Implemented autonomous decision-making capabilities using OpenAI function calling. Engineered data fetching and summarization modules with high accuracy and efficiency. Integrated YouTube API for enriched educational content. What We Learned How to integrate machine learning and voice recognition technologies to create a seamless user experience. The importance of a modular architecture for scalability and future expansions. How to manage and manipulate large datasets for quick and accurate retrieval. The nuances of user experience design, particularly for an educational tool. What's Next Implementing a personalized learning path based on user behavior and preferences. Expanding the database to include more academic resources and journals. Launching a mobile application to make StudyAI accessible on the go. Exploring partnerships with educational institutions for broader reach and impact. Features Voice Activation and Command Parsing: Understands user needs through voice commands. Autonomous Decision Making: Adapts to the user's needs, activating the most useful tools or functionalities. Text Summarization: Offers concise, yet comprehensive, academic information. Generate Visualizations: Provides visual aids for complex topics. YouTube Video Suggestions: Recommends relevant educational videos from YouTube.

## README (from the GitHub repository)

# S.T.U.D.Y.A.I

Systematic Teaching Using Dynamic Yielding and Autonomous Intelligence

---

## Overview
Studyai is a groundbreaking, voice-activated study assistant engineered to redefine the self-study experience. Utilizing cutting-edge machine learning, voice recognition, and natural language understanding technologies, this platform offers an array of features designed to facilitate efficient and effective study sessions.

---

## Inspiration
In an era flooded with information, the conventional methods of self-study are fraught with challenges like information overload, disorganized note management, and a lack of personalized learning experiences. Autostudy Buddy was conceived to tackle these issues head-on, offering a revolutionary approach to academic learning and comprehension.

---

## Goals
Our mission is to revolutionize self-study by providing an intelligent, voice-activated assistant that delivers a seamless and intuitive user experience. Autostudy Buddy aims to become the go-to platform for students, offering features like text and video summarization, explanatory image generation, educational video recommendations, and personalized note management.

---

## Built With
- **Frontend**: Reflex.dev with React components as plugins
- **Backend**: FastAPI
- **Voice Recognition and Text-to-Speech**: 11Labs, LLM
- **Autonomous Agents**: OpenAI function calling agents
- **Text Summarization**: TogetherAI
- **Image Generation**: TogetherAI
- **Database**: MindsDB
- **YouTube Integration**: MindsDB with YouTube API

---

## Challenges
- Implementing real-time voice recognition and natural language understanding.
- Ensuring seamless integration of multiple technologies, including machine learning models and external APIs.
- Achieving scalability while maintaining high performance and reliability.
- Ensuring data privacy and security.

---

## Accomplishments
- Successfully developed a voice-activated command parsing module.
- Implemented autonomous decision-making capabilities using OpenAI.
- Engineered data fetching and summarization modules with high accuracy and efficiency.
- Integrated YouTube API for enriched educational content.
- Designed an effective note management system using MindsDB.

---

## What We Learned
- How to integrate machine learning and voice recognition technologies to create a seamless user experience.
- The importance of a modular architecture for scalability and future expansions.
- How to manage and manipulate large datasets for quick and accurate retrieval.
- The nuances of user experience design, particularly for an educational tool.

---

## What's Next
- Implementing a personalized learning path based on user behavior and preferences.
- Expanding the database to include more academic resources and journals.
- Launching a mobile application to make Autostudy Buddy accessible on the go.
- Exploring partnerships with educational institutions for broader reach and impact.

---

## Functions
- **Voice Activation and Command Parsing**: Understands user needs through voice commands.
- **Autonomous Decision Making**: Adapts to the user's needs, activating the most useful tools or functionalities.
- **Text Summarization**: Offers concise, yet comprehensive, academic information.
- **Image Generation**: Provides visual aids for complex topics.
- **YouTube Video Suggestions**: Recommends relevant educational videos.
- **Note Management**: Organizes user-uploaded study notes for easy retrieval.

## Detected evidence (automated analysis)

Indexed codebase: 41 recognized source files, 135 KB.
- CSS (language) — detected in the code
- JavaScript (language) — detected in the code
- Next.js (technology) — detected in the code
- Python (language) — detected in the code
- React (technology) — detected in the code
- Tailwind CSS (technology) — detected in the code
- OpenAI (technology) — claimed on Devpost, not found in the code

## Codebase structure (from repository index)

### Files (56 of 56)

```
.env
.env.example
.gitignore
.python-version
.web/.gitignore
.web/components/reflex/chakra_color_mode_provider.js
.web/components/reflex/radix_themes_color_mode_provider.js
.web/env.json
.web/jsconfig.json
.web/next.config.js
.web/package.json
.web/pages/_app.js
.web/pages/_document.js
.web/pages/404.js
.web/pages/dashboard.js
.web/pages/index.js
.web/pages/recorder.js
.web/pages/settings.js
.web/postcss.config.js
.web/public/AutoRecorder.js
.web/public/voice-processor.js
.web/reflex.json
.web/styles/code/prism.js
.web/styles/styles.css
.web/styles/tailwind.css
.web/tailwind.config.js
.web/utils/client_side_routing.js
.web/utils/components.js
.web/utils/context.js
.web/utils/helpers/dataeditor.js
.web/utils/state.js
.web/utils/theme.js
assets/AutoRecorder.js
assets/voice-processor.js
automatic_chat.py
Experiments/hp0.txt
Experiments/speechToTest.ipynb
Experiments/textToSpeech.ipynb
Experiments/youtube_transcript.ipynb
py
README.md
requirements.txt
rxconfig.py
STUDYAI/__init__.py
STUDYAI/components/__init__.py
STUDYAI/components/sidebar.py
STUDYAI/pages/__init__.py
STUDYAI/pages/dashboard.py
STUDYAI/pages/index.py
STUDYAI/pages/recorder.py
STUDYAI/pages/settings.py
STUDYAI/state.py
STUDYAI/STUDYAI.py
STUDYAI/styles.py
STUDYAI/templates/__init__.py
STUDYAI/templates/template.py
```

### Dependencies

- .web/package.json: @chakra-ui/icons@^2.0.19, @chakra-ui/react@^2.6.1, @chakra-ui/system@^2.5.7, @emotion/react@^11.11.0, @emotion/styled@^11.11.0, autoprefixer@10.4.14, axios@1.4.0, focus-visible@5.2.0, framer-motion@^10.16.4, json5@2.2.3, next@13.5.4, next-sitemap@4.1.8, next-themes@0.2.0, postcss@8.4.24, react@18.2.0, react-dom@18.2.0, react-markdown@^8.0.7, react-syntax-highlighter@^15.5.0, recordrtc@5.6.2, rehype-katex@^6.0.3, rehype-raw@^6.1.1, remark-gfm@^3.0.1, remark-math@^5.1.1, socket.io-client@4.6.1, tailwindcss@^3.3.2, universal-cookie@4.0.4
- requirements.txt: reflex@==0.3.1

### Recent commits (newest first)

- MAnual addition
- Merge pull request #5 from IdkwhatImD0ing/readme
- FINAL FINAL REQUIREMTNS
- Requirements
- Merge pull request #4 from IdkwhatImD0ing/reflexState
- Fixing concurrency
- It works sometimes
- No longer using pydantic in streaming audio
- AutoRecorder works
- Merge changes
- Update requirements.txt
- Working
- Merge pull request #1 from IdkwhatImD0ing/youtube-transcription
- Merge pull request #2 from IdkwhatImD0ing/automatic
- Transcribe youtube video
- Update automatic_chat
- Bug fix 2
- Bug fix
- Using faster whisper
- Audomatic sst

## Key source files (fetched from GitHub, selected and truncated for size)

### requirements.txt

```
��a i f 3 6 0 = = 0 . 5 . 0  
 f a s t e r - w h i s p e r = = 0 . 9 . 0  
 f l a s k - c o r s = = 3 . 0 . 1 0  
 i p y k e r n e l = = 6 . 2 0 . 1  
 m c c a b e = = 0 . 6 . 1  
 o p e n a i = = 0 . 2 8 . 1  
 o p e n c v - p y t h o n = = 4 . 7 . 0 . 6 8  
 p i p - c h i l l = = 1 . 0 . 3  
 p y a u d i o = = 0 . 2 . 1 3  
 p y c o d e s t y l e = = 2 . 5 . 0  
 p y f l a k e s = = 2 . 1 . 1  
 p y t h o n - d o t e n v = = 1 . 0 . 0  
 r e f l e x = = 0 . 3 . 1  
 s k l e a r n = = 0 . 0 . p o s t 1  
 t e n s o r b o a r d = = 2 . 1 1 . 2  
 v i r t u a l e n v = = 2 0 . 1 7 . 1  
 w a v e = = 0 . 0 . 2  
 y a p f = = 0 . 3 2 . 0  
 
reflex==0.3.1

```

### .web/package.json

```
{
  "name": "reflex",
  "scripts": {
    "dev": "next dev",
    "export": "next build && next export -o _static",
    "export-sitemap": "next build && next-sitemap && next export -o _static",
    "prod": "next start"
  },
  "dependencies": {
    "@chakra-ui/icons": "^2.0.19",
    "@chakra-ui/react": "^2.6.1",
    "@chakra-ui/system": "^2.5.7",
    "@emotion/react": "^11.11.0",
    "@emotion/styled": "^11.11.0",
    "axios": "1.4.0",
    "focus-visible": "5.2.0",
    "framer-motion": "^10.16.4",
    "json5": "2.2.3",
    "next": "13.5.4",
    "next-sitemap": "4.1.8",
    "next-themes": "0.2.0",
    "react": "18.2.0",
    "react-dom": "18.2.0",
    "react-markdown": "^8.0.7",
    "react-syntax-highlighter": "^15.5.0",
    "recordrtc": "5.6.2",
    "rehype-katex": "^6.0.3",
    "rehype-raw": "^6.1.1",
    "remark-gfm": "^3.0.1",
    "remark-math": "^5.1.1",
    "socket.io-client": "4.6.1",
    "tailwindcss": "^3.3.2",
    "universal-cookie": "4.0.4"
  },
  "devDependencies": {
    "autoprefixer": "10.4.14",
    "postcss": "8.4.24"
  }
}

```

### STUDYAI/pages/index.py

```python
"""The home page of the app."""

from STUDYAI import styles
from STUDYAI.templates import template

import reflex as rx


@template(route="/", title="Home", image="/github.svg")
def index() -> rx.Component:
    """The home page.

    Returns:
        The UI for the home page.
    """
    with open("README.md", encoding="utf-8") as readme:
        content = readme.read()
    return rx.markdown(content, component_map=styles.markdown_style)

```

### .web/pages/index.js

```javascript
import { Fragment, useContext, useEffect, useRef, useState } from "react"
import { useRouter } from "next/router"
import { Event, getAllLocalStorageItems, getRefValue, getRefValues, isTrue, preventDefault, refs, spreadArraysOrObjects, uploadFiles, useEventLoop } from "/utils/state"
import { ColorModeContext, EventLoopContext, initialEvents, StateContext } from "/utils/context.js"
import "focus-visible/dist/focus-visible"
import "katex/dist/katex.min.css"
import { Box, Center, Code, Heading, HStack, Image, Link, ListItem, Menu, MenuButton, MenuDivider, MenuItem, MenuList, Modal, ModalBody, ModalContent, ModalHeader, ModalOverlay, OrderedList, Spacer, Text, UnorderedList, VStack } from "@chakra-ui/react"
import { getEventURL } from "/utils/state.js"
import NextLink from "next/link"
import ReactMarkdown from "react-markdown"
import remarkMath from "remark-math"
import remarkGfm from "remark-gfm"
import rehypeKatex from "rehype-katex"
import rehypeRaw from "rehype-raw"
import { Prism } from "react-syntax-highlighter"
import { light } from "/styles/code/prism"
import { HamburgerIcon } from "@chakra-ui/icons"
import NextHead from "next/head"



export default function Component() {
  const state = useContext(StateContext)
  const router = useRouter()
  const [ colorMode, toggleColorMode ] = useContext(ColorModeContext)
  const focusRef = useRef();
  
  // Main event loop.
  const [addEvents, connectError] = useContext(EventLoopContext)

  // Set focus to the specified element.
  useEffect(() => {
    if (focusRef.current) {
      focusRef.current.focus();
    }
  })

  // Route after the initial page hydration.
  useEffect(() => {
    const change_complete = () => addEvents(initialEvents())
    router.events.on('routeChangeComplete', change_complete)
    return () => {
      router.events.off('routeChangeComplete', change_complete)
    }
  }, [router])


  return (
    <Fragment>
  <Fragment>
  {isTrue(connectError !== null) ? (
  <Fragment>
  <Modal isOpen={connectError !== null}>
  <ModalOverlay>
  <ModalContent>
  <ModalHeader>
  {`Connection Error`}
</ModalHeader>
  <ModalBody>
  <Text>
  {`Cannot connect to server: `}
  {(connectError !== null) ? connectError.message : ''}
  {`. Check if server is reachable at `}
  {getEventURL().href}
</Text>
</ModalBody>
</ModalContent>
</ModalOverlay>
</Modal>
</Fragment>
) : (
  <Fragment/>
)}
</Fragment>
  <HStack alignItems={`flex-start`} sx={{"transition": "left 0.5s, width 0.5s", "position": "relative"}}>
  <Box sx={{"display": ["none", "none", "block"], "minWidth": "20em", "height": "100%", "position": "sticky", "top": "0px", "borderRight": "1px solid #F4F3F6"}}>
  <VStack sx={{"height": "100dvh"}}>
  <HStack sx={{"width": "100%", "borderBottom": "1px solid #F4F3F6", "padding": "1em"}}>
  <Image src={`/icon.svg`} sx={{"height": "2em"}}/>
  <Spacer/>
  <Link as={NextLink} href={`https://github.com/reflex-dev/reflex`}>
  <Center sx={{"boxShadow": "0px 0px 0px 1px rgba(84, 82, 95, 0.14)", "bg": "transparent", "borderRadius": "0.375rem", "_hover": {"bg": "#F5EFFE"}}}>
  <Image src={`/github.svg`} sx={{"height": "3em", "padding": "0.5em"}}/>
</Center>
</Link>
</HStack>
  <VStack alignItems={`flex-start`} sx={{"width": "100%", "overflowY": "auto", "padding": "1em"}}>
  <Link as={NextLink} href={`/`} sx={{"width": "100%"}}>
  <HStack sx={{"bg": isTrue((state.router.page.path === "/home") || (((state.router.page.path === "/") && "Home") === "Home")) ? `#F5EFFE` : `transparent`, "color": isTrue((state.router.page.path === "/home") || (((state.router.page.path === "/") && "Home") === "Home")) ? `#1A1060` : `black`, "borderRadius": "0.375rem", "boxShadow": "0px 0px 0px 1px rgba(84, 82, 95, 0.14)", "width": "100%", "paddingX": "1em"}}>
  <Image src={`/github.svg`} sx={{"height": "2.5em", "padding": "0.5em"}}/>
  <Text>
  {`Home`}
</Text>
</HStack>
</Link>
  <Link as={NextLink} href={`/dashboard`} sx={{"width": "100%"}}>
  <HStack sx={{"bg": isTrue((state.router.page.path === "/dashboard") || (((state.router.page.path === "/") && "Dashboard") === "Home")) ? `#F5EFFE` : `transparent`, "color": isTrue((state.router.page.path === "/dashboard") || (((state.router.page.path === "/") && "Dashboard") === "Home")) ? `#1A1060` : `black`, "borderRadius": "0.375rem", "boxShadow": "0px 0px 0px 1px rgba(84, 82, 95, 0.14)", "width": "100%", "paddingX": "1em"}}>
  <Image src={`/github.svg`} sx={{"height": "2.5em", "padding": "0.5em"}}/>
  <Text>
  {`Dashboard`}
</Text>
</HStack>
</Link>
  <Link as={NextLink} href={`/recorder`} sx={{"width": "100%"}}>
  <HStack sx={{"bg": isTrue((state.router.page.path === "/recorder") || (((state.router.page.path === "/") && "Recorder") === "Home")) ? `#F5EFFE` : `transparent`, "color": isTrue((state.router.page.path === "/recorder") || (((state.router.page.path === "/") && "Recorder") === "Home")) ? `#1A1060` : `black`, "borderRadius": "0.375rem", "boxShadow": "0px 0px 0px 1px rgba(84, 82, 95, 0.14)", "width": "100%", "paddingX": "1em"}}>
  <Image src={`/github.svg`} sx={{"height": "2.5em", "padding": "0.5em"}}/>
  <Text>
  {`Recorder`}
</Text>
</HStack>
</Link>
  <Link as={NextLink} href={`/settings`} sx={{"width": "100%"}}>
  <HStack sx={{"bg": isTrue((state.router.page.path === "/settings") || (((state.router.page.path === "/") && "Settings") === "Home")) ? `#F5EFFE` : `transparent`, "color": isTrue((state.router.page.path === "/settings") || (((state.router.page.path === "/") && "Settings") === "Home")) ? `#1A1060` : `black`, "borderRadius": "0.375rem", "boxShadow": "0px 0px 0px 1px rgba(84, 82, 95, 0.14)", "width": "100%", "paddingX": "1em"}}>
  <Image src={`/github.svg`} sx={{"height": "2.5em", "padding": "0.5em"}}/>
  <Text>
  {`Settings`}
</Text>
</HStack>
</Link>
</VStack>
  <Spacer/>
  <HStack sx={{"width": "100%", "borderTop": "1px solid #F4F3F6", "padding": "1em"}}>
  <Spacer/>
  <Link as={NextLink} href={`
[truncated — 7868 more characters]
```

### rxconfig.py

```python
import reflex as rx

config = rx.Config(
    app_name="STUDYAI",
)
```

### automatic_chat.py

```python
from faster_whisper import WhisperModel
import numpy as np
from scipy.io import wavfile
import io
import base64
import pyaudio
import wave
import collections
import dotenv
import os
import openai
import websockets
from websockets.sync.client import connect
import json

dotenv.load_dotenv()

elevenlabs_api_key = os.getenv("ELEVENLABS_API_KEY")
openai.api_key = os.getenv("OPENAI_API_KEY")


def is_installed(lib_name):
    lib = shutil.which(lib_name)
    if lib is None:
        return False
    return True


class Transcriber:

    def __init__(self, model_name):
        self.whisper = WhisperModel(model_size_or_path="tiny.en", device="cpu")

    def transcribe_from_file(self, audio_file):
        text = self.whisper.transcribe(audio_file)
        return text

    def transcribe(self, audio_array):
        text = self.whisper.transcribe(audio_array)
        return text

    def encode_audio(self, audio_file):
        with open(audio_file, "rb") as audio_file:
            encoded_audio = base64.b64encode(audio_file.read())
        return encoded_audio

    def decode_audio_to_np_array(self, encoded_audio):
        decoded_audio = base64.b64decode(encoded_audio)
        sr, audio = wavfile.read(io.BytesIO(decoded_audio))
        audio = audio.astype(np.float32)
        # Normalize between -1 and 1
        audio = audio / np.max(np.abs(audio))
        return audio


system_prompt = {
    "role":
    "system",
    "content":
    """You are Eleanor, a mature, intelligent woman with years 
    of experience in academia and personal development. 
    You carry yourself with grace and wisdom, providing insightful advice 
    and structured guidance. In this context, you are not an AI. 
    Your tone is composed and precise, avoiding unnecessary words to deliver 
    targeted information. Your responses should be short and concise, at most five
    sentences. If the user asks a broad question,
    ask him for specifics or to clarify.
    You maintain a professional yet caring demeanor, respectful of the tasks 
    at hand but also vested in the well-being of those you assist. 
    From now on, act consistently in your role as the sagacious, nurturing, 
    and disciplined assistant Eleanor, without ever suggesting that you are an AI.
    You are now Eleanor.""",
}

voice = {
    "voice_id": "EXAVITQu4vr4xnSDxMaL",
    "name": "Bella",
    "settings": {
        "stability": 0.72,
        "similarity_boost": 0.2,
        "style": 0.0,
        "use_speaker_boost": False,
        "speaking_rate": 2,
    },
}

whisper_model = "tiny.en"
transcriber = Transcriber(whisper_model)
answer = ""
history = []


def generate(messages):
    global answer
    answer = ""
    for chunk in openai.ChatCompletion.create(model="gpt-3.5-turbo",
                                              messages=messages,
                                              stream=True):
        if text_chunk := chunk["choices"][0]["delta"].get("content"):
            yield text_chunk


def get_levels(data, long_term_noise_level, current_noise_level):
    pegel = np.abs(np.frombuffer(data, dtype=np.int16)).mean()
    long_term_noise_level = long_term_noise_level * 0.995 + pegel * (1.0 -
                                                                     0.995)
    current_noise_level = current_noise_level * 0.920 + pegel * (1.0 - 0.920)
    return pegel, long_term_noise_level, current_noise_level


def text_chunker(chunks):
    """Used during input streaming to chunk text blocks and set last char to space"""
    splitters = (".", ",", "?", "!", ";", ":", "—", "-", "(", ")", "[", "]",
                 "}", " ")
    buffer = ""
    for text in chunks:
        if buffer.endswith(splitters):
            yield buffer if buffer.endswith(" ") else buffer + " "
            buffer = text
        elif text.startswith(splitters):
            output = buffer + text[0]
            yield output if output.endswith(" ") else output + " "
            buffer = text[1:]
        else:
            buffer += text
    if buffer != "":
        yield buffer + " "


def generate_stream_input(text_generator, voice, model):
    BOS = json.dumps(
        dict(text=" ",
             try_trigger_generation=True,
             voice_settings=voice['settings'],
             generation_config=dict(chunk_length_schedule=[50])))
    EOS = json.dumps({"text": ""})

    with connect(
            f"""wss://api.elevenlabs.io/v1/text-to-speech/{voice["voice_id"]}/stream-input?model_id={model["model_id"]}""",
            additional_headers={
                "xi-api-key": elevenlabs_api_key,
            },
    ) as websocket:
        websocket.send(BOS)

        # Stream text chunks and receive audio
        for text_chunk in text_chunker(text_generator):
            data = dict(text=text_chunk, try_trigger_generation=True)
            websocket.send(json.dumps(data))
            try:
                data = json.loads(websocket.recv(1e-4))
                if data["audio"]:
                    yield base64.b64decode(data["audio"])  # type: ignore
            except TimeoutError:
                pass

        websocket.send(EOS)

        while True:
            try:
                data = json.loads(websocket.recv())
                if data["audio"]:
                    yield base64.b64decode(data["audio"])  # type: ignore
            except websockets.exceptions.ConnectionClosed:
                break


def on_streaming_complete():
    history.append({"role": "assistant", "content": answer})


def stream_output(audio_stream):
    if not is_installed("mpv"):
        message = (
            "mpv not found, necessary to stream audio. "
            "On mac you can install it with 'brew install mpv'. "
            "On linux and windows you can install it from https://mpv.io/")
        raise ValueError(message)

    mpv_command = ["mpv", "--no-cache", "--no-terminal", "--", "fd://0"]
    mpv_process = subprocess.Popen(
        mpv_command,
        stdin=subprocess.PIPE,
        stdout=subpr
[truncated — 2881 more characters]
```

### STUDYAI/__init__.py

```python
"""Base template for Reflex."""

```

### .web/postcss.config.js

```javascript
module.exports = {
  plugins: {
    tailwindcss: {},
    autoprefixer: {},
  },
}

```

### .web/next.config.js

```javascript
module.exports = {
  basePath: "",
  compress: true,
  reactStrictMode: true,
  trailingSlash: true,
};

```

### .web/tailwind.config.js

```javascript
/** @type {import('tailwindcss').Config} */
module.exports = {
	content: ["./pages/**/*.{js,ts,jsx,tsx}"],
	theme: null,
	plugins: [
	],
};
```

[31 more indexed source files omitted to keep this export small. The full file list is in the Codebase structure section above.]