Skip to content
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

add dockerfile and deploy.yml #1

Merged
merged 12 commits into from
Oct 30, 2024
141 changes: 141 additions & 0 deletions .github/workflows/deploy.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
name: Deploy app to Google Cloudrun
on:
pull_request:
paths:
- "src/**"
- ".github/workflows/deploy.yml"
push:
branches:
- main
paths:
- "src/**"
- ".github/workflows/deploy.yml"
tags:
- "release-*"

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

env:
CI: true
PROJECT_ID: ${{ secrets.PROJECT_ID }}
MAIN: ${{ github.ref == 'refs/heads/main' }}
SERVICE: audiora
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
GEMINI_API_KEY: ${{ secrets.GEMINI_API_KEY }}
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
ELEVENLABS_API_KEY: ${{ secrets.ELEVENLABS_API_KEY }}

jobs:
prepare:
runs-on: ubuntu-latest
outputs:
VERSION: ${{ github.event_name == 'pull_request' && format('pr-{0}', github.event.number) || format('main-{0}', steps.prepare_env.outputs.SHORT_SHA) }}
MAIN_OR_TAGGED: ${{ fromJSON(env.MAIN) || (fromJSON(steps.prepare_env.outputs.TAGGED) && steps.prepare_env.outputs.TAG_BRANCH_NAME == 'main') }}
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- id: prepare_env
run: |
echo "TAGGED=${{ startsWith(github.ref, 'refs/tags/api') }}" >> $GITHUB_OUTPUT

SHORT_SHA=$(git rev-parse --short HEAD)
echo "SHORT_SHA=$SHORT_SHA" >> $GITHUB_OUTPUT

RAW=$(git branch -r --contains $SHORT_SHA)
TAG_BRANCH_NAME="${RAW##*/}"
echo "TAG_BRANCH_NAME=$TAG_BRANCH_NAME" >> $GITHUB_OUTPUT

lint:
runs-on: ubuntu-latest
needs: prepare
timeout-minutes: 5
steps:
- uses: actions/checkout@v4
- id: setup-python
uses: actions/setup-python@v5
with:
python-version: "3.12"
cache: "pip" # caching pip dependencies
check-latest: true

- run: pip install --force-reinstall -r requirements.txt
if: ${{ steps.setup-python.outputs.cache-hit != 'true' }}

- run: pip install -r requirements.txt
if: ${{ steps.setup-python.outputs.cache-hit == 'true' }}

- uses: chartboost/ruff-action@v1

deploy:
runs-on: ubuntu-latest
needs: [prepare, lint]
timeout-minutes: 10
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: "3.12"
cache: "pip" # caching pip dependencies
check-latest: true

- uses: google-github-actions/auth@v2
with:
credentials_json: "${{ secrets.GCP_SA_KEY }}"

- uses: google-github-actions/setup-gcloud@v2
- run: gcloud config set app/cloud_build_timeout 300

- id: deploy
uses: google-github-actions/deploy-cloudrun@v2
with:
service: ${{ env.SERVICE }}
source: ./
tag: ${{ needs.prepare.outputs.VERSION }}
no_traffic: true
timeout: "5m"
gcloud_version: "482.0.0"
env_vars: |
ENV=prod
OPENAI_API_KEY=${{ secrets.OPENAI_API_KEY }}
GEMINI_API_KEY=${{ secrets.GEMINI_API_KEY }}
ANTHROPIC_API_KEY=${{ secrets.ANTHROPIC_API_KEY }}
ELEVENLABS_API_KEY=${{ secrets.ELEVENLABS_API_KEY }}

flags: "--allow-unauthenticated --memory=32Gi --cpu=8 --execution-environment=gen2 --concurrency=80 --max-instances=10"

- name: health-check
run: curl -f "${{ steps.deploy.outputs.url }}"
- uses: marocchino/sticky-pull-request-comment@v2
with:
header: api
message: |
app: ${{ steps.deploy.outputs.url }} (${{ github.event.pull_request.head.sha }})

promote:
runs-on: ubuntu-latest
if: (needs.prepare.outputs.MAIN_OR_TAGGED == 'true')
needs: [prepare, deploy, lint]
timeout-minutes: 3
steps:
- uses: google-github-actions/auth@v2
with:
credentials_json: "${{ secrets.GCP_SA_KEY }}"

- uses: google-github-actions/setup-gcloud@v2
- run: gcloud run services update-traffic ${{ env.SERVICE }} --to-tags=${{ needs.prepare.outputs.VERSION }}=100 --project=${{ env.PROJECT_ID }} --region=us-central1

cleanup:
runs-on: ubuntu-latest
needs: promote
timeout-minutes: 3
steps:
- uses: google-github-actions/auth@v2
with:
credentials_json: "${{ secrets.GCP_SA_KEY }}"
- uses: google-github-actions/setup-gcloud@v2
- name: cleanup old revisions
run: |
gcloud run revisions list --service=${{ env.SERVICE }} --project=${{ env.PROJECT_ID }} --region=us-central1 --sort-by=CREATE_TIME --format="value(REVISION)" | tail -n +4 | xargs -I {} gcloud run revisions delete {} --project=${{ env.PROJECT_ID }} --region=us-central1 --quiet
24 changes: 24 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# Use the official lightweight Python image.
# https://hub.docker.com/_/python
FROM python:3.12-slim

# Allow statements and log messages to immediately appear in the Knative logs
ENV PYTHONUNBUFFERED True
ENV PYTHONDONTWRITEBYTECODE 1

WORKDIR /app

# Install FFmpeg and any other required dependencies
RUN apt-get -yqq update && apt-get -yqq install build-essential ffmpeg && \
rm -rf /var/lib/apt/lists/*

COPY . ./

# Install production dependencies.
RUN pip install --no-cache-dir -r requirements.txt

ENV HOST '0.0.0.0'
EXPOSE $PORT
HEALTHCHECK CMD curl --fail http://$HOST:$PORT/_stcore/health

CMD exec streamlit run app.py --server.port=$PORT --server.address=$HOST
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Audiora

> Learn or listen to anything, anytime, through the power of AI-generated audio.
> Listen to anything, anytime, leveraging AI-generated audio.

Audiora is an AI-enhanced audio platform that transforms user preferences into personalized engaging audio experiences.

Expand Down
25 changes: 18 additions & 7 deletions app.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,22 +8,33 @@


async def main():
init_session_state()

# Configure page
st.set_page_config(page_title="Audiora", page_icon="🎧", layout="wide")

st.title("🎧 Audiora")
st.subheader("Listen to anything, anytime, leveraging AI")

st.sidebar.markdown(
"""
<p>
A <a href="https://veedo.ai">VeedoAI</a> project. (c) 2024
</p>
""",
unsafe_allow_html=True,
)

# Sidebar for content type selection
st.sidebar.title("Audiocast Info")

init_session_state()

if st.session_state.content_category:
st.sidebar.subheader(
f"Content Category: {st.session_state.content_category.capitalize()}"
)

# Main chat interface
st.title("🎧 Audiora")
st.subheader("Learn anything, anytime, through the power of AI-generated audio.")
else:
st.sidebar.markdown(
"> Your preferences and audiocast metadata will appear here"
)

# Declare chat interface container
uichat = st.empty()
Expand Down
38 changes: 38 additions & 0 deletions setup.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
from setuptools import find_packages, setup

setup(
name="audiora",
version="1.0.0",
description="Learn or listen to anything, anytime, through the power of AI-generated audio",
author="Chukwuma Nwaugha",
author_email="chuks@veedo.ai",
url="https://github.com/nwaughachukwuma/audiora",
packages=find_packages(),
install_requires=[
"streamlit",
"httpx",
"asyncio",
"openai",
"anthropic",
"pyperclip",
"python-multipart",
"python-slugify",
"python-dotenv",
"pydub",
"firebase-admin",
"google-auth",
"google-cloud-storage",
"google-api-python-client",
"google-generativeai",
"ruff",
],
classifiers=[
"Development Status :: 3 - Alpha",
"Intended Audience :: Developers",
"License :: OSI Approved :: MIT License",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.6",
"Programming Language :: Python :: 3.7",
"Programming Language :: Python :: 3.8",
],
)
7 changes: 2 additions & 5 deletions src/env_var.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,9 @@
else:
load_dotenv()

BACKEND_URL = environ.get("BACKEND_URL", "http://localhost:8000")
APP_URL = environ.get("APP_URL", "http://localhost:8501")

OPENAI_API_KEY = environ["OPENAI_API_KEY"]
ANTHROPIC_API_KEY = environ["ANTHROPIC_API_KEY"]
GEMINI_API_KEY = environ["GEMINI_API_KEY"]
GROQ_API_KEY = environ["GROQ_API_KEY"]

ELEVENLABS_API_KEY = environ["ELEVENLABS_API_KEY"]

APP_URL = environ.get("APP_URL", "http://localhost:8501")
5 changes: 3 additions & 2 deletions src/uis/audioui.py
Original file line number Diff line number Diff line change
@@ -1,18 +1,19 @@
import streamlit as st
from streamlit.delta_generator import DeltaGenerator

from src.utils.chat_thread import use_audiocast_request
from src.utils.render_audiocast import render_audiocast


async def audioui(uichat=st.empty()):
async def audioui(uichat: DeltaGenerator):
"""
Audiocast interface
"""
uichat.chat_input("What would you like to listen to?", disabled=True)
uichat.empty()

if not st.session_state.current_audiocast:
st.info("Using your specifications")
st.info("Using your preferences")

summary = st.session_state.user_specification
content_category = st.session_state.content_category
Expand Down
3 changes: 2 additions & 1 deletion src/uis/chatui.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import streamlit as st
from streamlit.delta_generator import DeltaGenerator

from src.utils.chat_thread import (
evaluate_final_response,
Expand All @@ -9,7 +10,7 @@
from src.utils.render_chat import render_chat_history


async def chatui(uichat=st.empty()):
async def chatui(uichat: DeltaGenerator):
"""
Chat interface
"""
Expand Down
2 changes: 1 addition & 1 deletion src/utils/chat_thread.py
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ async def use_audiocast_request(summary: str, content_category: ContentCategory)
Call audiocast creating workflow

Args:
summary (str): user request summary or user specification
summary (str): user request summary or preferences
content_category (ContentCategory): content category
"""
try:
Expand Down
2 changes: 1 addition & 1 deletion src/utils/chat_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ class SessionChatRequest(BaseModel):

def display_example_cards():
"""Display example content cards if there are no messages"""
st.markdown("#### You can start with one of the following")
st.markdown("##### You can start with one of the following")

# CSS for fixed-height buttons and responsive columns
st.markdown(
Expand Down
5 changes: 3 additions & 2 deletions src/utils/render_audiocast.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,10 @@ class GenerateAudiocastDict(TypedDict):

def render_audiocast():
"""
Render the audiocast based on the user's specifications
Render the audiocast based on the user's preferences
- Display current audiocast if available
"""
st.header("Your Audiocast")
st.markdown("#### Your Audiocast")
current_audiocast: GenerateAudiocastDict = st.session_state.current_audiocast

# Audio player
Expand Down Expand Up @@ -53,4 +53,5 @@ def render_audiocast():
st.rerun()

if st.session_state.get("show_copy_success", False):
st.session_state.show_copy_succes = False
st.success("Share link copied successfully!", icon="✅")
4 changes: 1 addition & 3 deletions src/utils/render_chat.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,11 @@ def on_value_change():
st.session_state.messages.append({"role": "human", "content": content})
st.session_state.content_category = content_category

st.rerun()

with st.container():
col1, _ = st.columns(2)
with col1:
st.selectbox(
"Select Content Type",
"Select Content Category",
content_categories,
format_func=lambda x: x.title(),
key="selected_content_category",
Expand Down
Loading