Final commit - working fine
This commit is contained in:
parent
6f0dc1fb35
commit
0bec4eef97
1 changed files with 102 additions and 178 deletions
|
|
@ -1,5 +1,4 @@
|
||||||
from typing import List, Dict, Optional
|
from typing import List, Dict, Optional
|
||||||
from dataclasses import dataclass
|
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
import os
|
import os
|
||||||
from firecrawl import FirecrawlApp
|
from firecrawl import FirecrawlApp
|
||||||
|
|
@ -7,26 +6,19 @@ from qdrant_client import QdrantClient
|
||||||
from qdrant_client.http import models
|
from qdrant_client.http import models
|
||||||
from qdrant_client.http.models import Distance, VectorParams
|
from qdrant_client.http.models import Distance, VectorParams
|
||||||
from fastembed import TextEmbedding
|
from fastembed import TextEmbedding
|
||||||
from agents import Agent, ModelSettings, function_tool, Runner
|
from agents import Agent, Runner
|
||||||
from openai import OpenAI, AsyncOpenAI
|
from openai import AsyncOpenAI
|
||||||
from openai.helpers import LocalAudioPlayer
|
|
||||||
import textwrap
|
|
||||||
import tempfile
|
import tempfile
|
||||||
import uuid
|
import uuid
|
||||||
import numpy as np
|
|
||||||
from typing import Callable
|
|
||||||
from urllib.parse import urlparse
|
|
||||||
from dotenv import load_dotenv
|
|
||||||
import asyncio
|
|
||||||
import json
|
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
import time
|
import time
|
||||||
import streamlit as st
|
import streamlit as st
|
||||||
|
from dotenv import load_dotenv
|
||||||
|
import asyncio
|
||||||
|
|
||||||
load_dotenv()
|
load_dotenv()
|
||||||
|
|
||||||
def init_session_state():
|
def init_session_state():
|
||||||
"""Initialize session state variables for storing API keys and configurations."""
|
|
||||||
defaults = {
|
defaults = {
|
||||||
"initialized": False,
|
"initialized": False,
|
||||||
"qdrant_url": "",
|
"qdrant_url": "",
|
||||||
|
|
@ -39,7 +31,7 @@ def init_session_state():
|
||||||
"embedding_model": None,
|
"embedding_model": None,
|
||||||
"processor_agent": None,
|
"processor_agent": None,
|
||||||
"tts_agent": None,
|
"tts_agent": None,
|
||||||
"selected_voice": "coral" # Default voice
|
"selected_voice": "coral"
|
||||||
}
|
}
|
||||||
|
|
||||||
for key, value in defaults.items():
|
for key, value in defaults.items():
|
||||||
|
|
@ -47,12 +39,10 @@ def init_session_state():
|
||||||
st.session_state[key] = value
|
st.session_state[key] = value
|
||||||
|
|
||||||
def sidebar_config():
|
def sidebar_config():
|
||||||
"""Render and handle the configuration sidebar."""
|
|
||||||
with st.sidebar:
|
with st.sidebar:
|
||||||
st.title("🔑 Configuration")
|
st.title("🔑 Configuration")
|
||||||
st.markdown("---")
|
st.markdown("---")
|
||||||
|
|
||||||
# API Keys and URLs
|
|
||||||
st.session_state.qdrant_url = st.text_input(
|
st.session_state.qdrant_url = st.text_input(
|
||||||
"Qdrant URL",
|
"Qdrant URL",
|
||||||
value=st.session_state.qdrant_url,
|
value=st.session_state.qdrant_url,
|
||||||
|
|
@ -81,7 +71,6 @@ def sidebar_config():
|
||||||
placeholder="https://docs.example.com"
|
placeholder="https://docs.example.com"
|
||||||
)
|
)
|
||||||
|
|
||||||
# Voice selection
|
|
||||||
st.markdown("---")
|
st.markdown("---")
|
||||||
st.markdown("### 🎤 Voice Settings")
|
st.markdown("### 🎤 Voice Settings")
|
||||||
voices = ["alloy", "ash", "ballad", "coral", "echo", "fable", "onyx", "nova", "sage", "shimmer", "verse"]
|
voices = ["alloy", "ash", "ballad", "coral", "echo", "fable", "onyx", "nova", "sage", "shimmer", "verse"]
|
||||||
|
|
@ -92,7 +81,6 @@ def sidebar_config():
|
||||||
help="Choose the voice for the audio response"
|
help="Choose the voice for the audio response"
|
||||||
)
|
)
|
||||||
|
|
||||||
# Setup button
|
|
||||||
if st.button("Initialize System", type="primary"):
|
if st.button("Initialize System", type="primary"):
|
||||||
if all([
|
if all([
|
||||||
st.session_state.qdrant_url,
|
st.session_state.qdrant_url,
|
||||||
|
|
@ -104,7 +92,6 @@ def sidebar_config():
|
||||||
progress_placeholder = st.empty()
|
progress_placeholder = st.empty()
|
||||||
with progress_placeholder.container():
|
with progress_placeholder.container():
|
||||||
try:
|
try:
|
||||||
# Setup Qdrant
|
|
||||||
st.markdown("🔄 Setting up Qdrant connection...")
|
st.markdown("🔄 Setting up Qdrant connection...")
|
||||||
client, embedding_model = setup_qdrant_collection(
|
client, embedding_model = setup_qdrant_collection(
|
||||||
st.session_state.qdrant_url,
|
st.session_state.qdrant_url,
|
||||||
|
|
@ -114,7 +101,6 @@ def sidebar_config():
|
||||||
st.session_state.embedding_model = embedding_model
|
st.session_state.embedding_model = embedding_model
|
||||||
st.markdown("✅ Qdrant setup complete!")
|
st.markdown("✅ Qdrant setup complete!")
|
||||||
|
|
||||||
# Crawl documentation
|
|
||||||
st.markdown("🔄 Crawling documentation pages...")
|
st.markdown("🔄 Crawling documentation pages...")
|
||||||
pages = crawl_documentation(
|
pages = crawl_documentation(
|
||||||
st.session_state.firecrawl_api_key,
|
st.session_state.firecrawl_api_key,
|
||||||
|
|
@ -122,7 +108,6 @@ def sidebar_config():
|
||||||
)
|
)
|
||||||
st.markdown(f"✅ Crawled {len(pages)} documentation pages!")
|
st.markdown(f"✅ Crawled {len(pages)} documentation pages!")
|
||||||
|
|
||||||
# Store embeddings
|
|
||||||
store_embeddings(
|
store_embeddings(
|
||||||
client,
|
client,
|
||||||
embedding_model,
|
embedding_model,
|
||||||
|
|
@ -130,7 +115,6 @@ def sidebar_config():
|
||||||
"docs_embeddings"
|
"docs_embeddings"
|
||||||
)
|
)
|
||||||
|
|
||||||
# Setup agents
|
|
||||||
processor_agent, tts_agent = setup_agents(
|
processor_agent, tts_agent = setup_agents(
|
||||||
st.session_state.openai_api_key
|
st.session_state.openai_api_key
|
||||||
)
|
)
|
||||||
|
|
@ -146,163 +130,117 @@ def sidebar_config():
|
||||||
st.error("Please fill in all the required fields!")
|
st.error("Please fill in all the required fields!")
|
||||||
|
|
||||||
def setup_qdrant_collection(qdrant_url: str, qdrant_api_key: str, collection_name: str = "docs_embeddings"):
|
def setup_qdrant_collection(qdrant_url: str, qdrant_api_key: str, collection_name: str = "docs_embeddings"):
|
||||||
print("\n--- Step 1: Setting up Qdrant Collection ---")
|
client = QdrantClient(url=qdrant_url, api_key=qdrant_api_key)
|
||||||
|
embedding_model = TextEmbedding()
|
||||||
|
test_embedding = list(embedding_model.embed(["test"]))[0]
|
||||||
|
embedding_dim = len(test_embedding)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
client = QdrantClient(url=qdrant_url, api_key=qdrant_api_key)
|
|
||||||
print("✓ Connected to Qdrant")
|
|
||||||
|
|
||||||
embedding_model = TextEmbedding()
|
|
||||||
test_embedding = list(embedding_model.embed(["test"]))[0]
|
|
||||||
embedding_dim = len(test_embedding)
|
|
||||||
print(f"✓ Embedding model ready (dimension: {embedding_dim})")
|
|
||||||
|
|
||||||
client.create_collection(
|
client.create_collection(
|
||||||
collection_name=collection_name,
|
collection_name=collection_name,
|
||||||
vectors_config=VectorParams(size=embedding_dim, distance=Distance.COSINE)
|
vectors_config=VectorParams(size=embedding_dim, distance=Distance.COSINE)
|
||||||
)
|
)
|
||||||
print(f"✓ Created collection: {collection_name}")
|
|
||||||
|
|
||||||
return client, embedding_model
|
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
if "already exists" in str(e):
|
if "already exists" not in str(e):
|
||||||
print(f"✓ Collection {collection_name} already exists")
|
raise e
|
||||||
return client, embedding_model
|
|
||||||
raise e
|
return client, embedding_model
|
||||||
|
|
||||||
def crawl_documentation(firecrawl_api_key: str, url: str, output_dir: Optional[str] = None):
|
def crawl_documentation(firecrawl_api_key: str, url: str, output_dir: Optional[str] = None):
|
||||||
print("\n--- Step 2: Crawling Documentation ---")
|
firecrawl = FirecrawlApp(api_key=firecrawl_api_key)
|
||||||
try:
|
pages = []
|
||||||
firecrawl = FirecrawlApp(api_key=firecrawl_api_key)
|
|
||||||
print(f"✓ Initialized Firecrawl")
|
|
||||||
|
|
||||||
if output_dir:
|
|
||||||
os.makedirs(output_dir, exist_ok=True)
|
|
||||||
print(f"✓ Created output directory: {output_dir}")
|
|
||||||
|
|
||||||
print(f"Starting crawl of {url}...")
|
|
||||||
|
|
||||||
pages = []
|
|
||||||
|
|
||||||
response = firecrawl.crawl_url(
|
|
||||||
url,
|
|
||||||
params={
|
|
||||||
'limit': 5,
|
|
||||||
'scrapeOptions': {
|
|
||||||
'formats': ['markdown', 'html']
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
while True:
|
|
||||||
if response.get('status') == 'scraping':
|
|
||||||
print(f"Progress: {response.get('completed', 0)}/{response.get('total', 0)} pages")
|
|
||||||
print(f"Credits used: {response.get('creditsUsed', 0)}")
|
|
||||||
|
|
||||||
for page in response.get('data', []):
|
|
||||||
content = page.get('markdown') or page.get('html', '')
|
|
||||||
metadata = page.get('metadata', {})
|
|
||||||
source_url = metadata.get('sourceURL', '')
|
|
||||||
|
|
||||||
if output_dir and content:
|
|
||||||
filename = f"{uuid.uuid4()}.md"
|
|
||||||
filepath = os.path.join(output_dir, filename)
|
|
||||||
with open(filepath, 'w', encoding='utf-8') as f:
|
|
||||||
f.write(content)
|
|
||||||
|
|
||||||
pages.append({
|
|
||||||
"content": content,
|
|
||||||
"url": source_url,
|
|
||||||
"metadata": {
|
|
||||||
"title": metadata.get('title', ''),
|
|
||||||
"description": metadata.get('description', ''),
|
|
||||||
"language": metadata.get('language', 'en'),
|
|
||||||
"crawl_date": datetime.now().isoformat()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
print(f"✓ Processed page: {metadata.get('title', 'Untitled')}")
|
|
||||||
|
|
||||||
next_url = response.get('next')
|
|
||||||
if not next_url:
|
|
||||||
break
|
|
||||||
|
|
||||||
response = firecrawl.get(next_url)
|
|
||||||
time.sleep(1)
|
|
||||||
|
|
||||||
print(f"✓ Crawled {len(pages)} pages")
|
|
||||||
return pages
|
|
||||||
|
|
||||||
except Exception as e:
|
if output_dir:
|
||||||
print(f"Error crawling documentation: {str(e)}")
|
os.makedirs(output_dir, exist_ok=True)
|
||||||
raise e
|
|
||||||
|
response = firecrawl.crawl_url(
|
||||||
|
url,
|
||||||
|
params={
|
||||||
|
'limit': 5,
|
||||||
|
'scrapeOptions': {
|
||||||
|
'formats': ['markdown', 'html']
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
while True:
|
||||||
|
for page in response.get('data', []):
|
||||||
|
content = page.get('markdown') or page.get('html', '')
|
||||||
|
metadata = page.get('metadata', {})
|
||||||
|
source_url = metadata.get('sourceURL', '')
|
||||||
|
|
||||||
|
if output_dir and content:
|
||||||
|
filename = f"{uuid.uuid4()}.md"
|
||||||
|
filepath = os.path.join(output_dir, filename)
|
||||||
|
with open(filepath, 'w', encoding='utf-8') as f:
|
||||||
|
f.write(content)
|
||||||
|
|
||||||
|
pages.append({
|
||||||
|
"content": content,
|
||||||
|
"url": source_url,
|
||||||
|
"metadata": {
|
||||||
|
"title": metadata.get('title', ''),
|
||||||
|
"description": metadata.get('description', ''),
|
||||||
|
"language": metadata.get('language', 'en'),
|
||||||
|
"crawl_date": datetime.now().isoformat()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
next_url = response.get('next')
|
||||||
|
if not next_url:
|
||||||
|
break
|
||||||
|
|
||||||
|
response = firecrawl.get(next_url)
|
||||||
|
time.sleep(1)
|
||||||
|
|
||||||
|
return pages
|
||||||
|
|
||||||
def store_embeddings(client: QdrantClient, embedding_model: TextEmbedding, pages: List[Dict], collection_name: str):
|
def store_embeddings(client: QdrantClient, embedding_model: TextEmbedding, pages: List[Dict], collection_name: str):
|
||||||
print("\n--- Step 3: Generating and Storing Embeddings ---")
|
for page in pages:
|
||||||
try:
|
embedding = list(embedding_model.embed([page["content"]]))[0]
|
||||||
for page in pages:
|
client.upsert(
|
||||||
embedding = list(embedding_model.embed([page["content"]]))[0]
|
collection_name=collection_name,
|
||||||
|
points=[
|
||||||
client.upsert(
|
models.PointStruct(
|
||||||
collection_name=collection_name,
|
id=str(uuid.uuid4()),
|
||||||
points=[
|
vector=embedding.tolist(),
|
||||||
models.PointStruct(
|
payload={
|
||||||
id=str(uuid.uuid4()),
|
"content": page["content"],
|
||||||
vector=embedding.tolist(),
|
"url": page["url"],
|
||||||
payload={
|
**page["metadata"]
|
||||||
"content": page["content"],
|
}
|
||||||
"url": page["url"],
|
)
|
||||||
**page["metadata"]
|
]
|
||||||
}
|
)
|
||||||
)
|
|
||||||
]
|
|
||||||
)
|
|
||||||
print(f"✓ Stored embedding for: {page['metadata']['title'] or page['url']}")
|
|
||||||
|
|
||||||
print(f"✓ Stored {len(pages)} embeddings in Qdrant")
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
print(f"Error storing embeddings: {str(e)}")
|
|
||||||
raise e
|
|
||||||
|
|
||||||
def setup_agents(openai_api_key: str):
|
def setup_agents(openai_api_key: str):
|
||||||
print("\n--- Step 4: Setting up OpenAI Agents ---")
|
os.environ["OPENAI_API_KEY"] = openai_api_key
|
||||||
try:
|
|
||||||
# Set OpenAI API key in environment
|
|
||||||
os.environ["OPENAI_API_KEY"] = openai_api_key
|
|
||||||
print("✓ Set OpenAI API key in environment")
|
|
||||||
|
|
||||||
processor_agent = Agent(
|
|
||||||
name="Documentation Processor",
|
|
||||||
instructions="""You are a helpful documentation assistant. Your task is to:
|
|
||||||
1. Analyze the provided documentation content
|
|
||||||
2. Answer the user's question clearly and concisely
|
|
||||||
3. Include relevant examples when available
|
|
||||||
4. Cite the source URLs when referencing specific content
|
|
||||||
5. Keep responses natural and conversational
|
|
||||||
6. Format your response in a way that's easy to speak out loud""",
|
|
||||||
model="gpt-4o"
|
|
||||||
)
|
|
||||||
print("✓ Set up Documentation Processor Agent")
|
|
||||||
|
|
||||||
tts_agent = Agent(
|
|
||||||
name="Text-to-Speech Agent",
|
|
||||||
instructions="""You are a text-to-speech agent. Your task is to:
|
|
||||||
1. Convert the processed documentation response into natural speech
|
|
||||||
2. Maintain proper pacing and emphasis
|
|
||||||
3. Handle technical terms clearly
|
|
||||||
4. Keep the tone professional but friendly
|
|
||||||
5. Use appropriate pauses for better comprehension
|
|
||||||
6. Ensure the speech is clear and well-articulated""",
|
|
||||||
model="gpt-4o-mini-tts"
|
|
||||||
)
|
|
||||||
print("✓ Set up TTS Agent")
|
|
||||||
|
|
||||||
return processor_agent, tts_agent
|
|
||||||
|
|
||||||
except Exception as e:
|
processor_agent = Agent(
|
||||||
print(f"Error setting up agents: {str(e)}")
|
name="Documentation Processor",
|
||||||
raise e
|
instructions="""You are a helpful documentation assistant. Your task is to:
|
||||||
|
1. Analyze the provided documentation content
|
||||||
|
2. Answer the user's question clearly and concisely
|
||||||
|
3. Include relevant examples when available
|
||||||
|
4. Cite the source URLs when referencing specific content
|
||||||
|
5. Keep responses natural and conversational
|
||||||
|
6. Format your response in a way that's easy to speak out loud""",
|
||||||
|
model="gpt-4o"
|
||||||
|
)
|
||||||
|
|
||||||
|
tts_agent = Agent(
|
||||||
|
name="Text-to-Speech Agent",
|
||||||
|
instructions="""You are a text-to-speech agent. Your task is to:
|
||||||
|
1. Convert the processed documentation response into natural speech
|
||||||
|
2. Maintain proper pacing and emphasis
|
||||||
|
3. Handle technical terms clearly
|
||||||
|
4. Keep the tone professional but friendly
|
||||||
|
5. Use appropriate pauses for better comprehension
|
||||||
|
6. Ensure the speech is clear and well-articulated""",
|
||||||
|
model="gpt-4o-mini-tts"
|
||||||
|
)
|
||||||
|
|
||||||
|
return processor_agent, tts_agent
|
||||||
|
|
||||||
async def process_query(
|
async def process_query(
|
||||||
query: str,
|
query: str,
|
||||||
|
|
@ -314,10 +252,7 @@ async def process_query(
|
||||||
openai_api_key: str
|
openai_api_key: str
|
||||||
):
|
):
|
||||||
try:
|
try:
|
||||||
# Generate query embedding
|
|
||||||
query_embedding = list(embedding_model.embed([query]))[0]
|
query_embedding = list(embedding_model.embed([query]))[0]
|
||||||
|
|
||||||
# Search in Qdrant
|
|
||||||
search_response = client.query_points(
|
search_response = client.query_points(
|
||||||
collection_name=collection_name,
|
collection_name=collection_name,
|
||||||
query=query_embedding.tolist(),
|
query=query_embedding.tolist(),
|
||||||
|
|
@ -330,7 +265,6 @@ async def process_query(
|
||||||
if not search_results:
|
if not search_results:
|
||||||
raise Exception("No relevant documents found in the vector database")
|
raise Exception("No relevant documents found in the vector database")
|
||||||
|
|
||||||
# Build context from search results
|
|
||||||
context = "Based on the following documentation:\n\n"
|
context = "Based on the following documentation:\n\n"
|
||||||
for result in search_results:
|
for result in search_results:
|
||||||
payload = result.payload
|
payload = result.payload
|
||||||
|
|
@ -343,14 +277,12 @@ async def process_query(
|
||||||
context += f"\nUser Question: {query}\n\n"
|
context += f"\nUser Question: {query}\n\n"
|
||||||
context += "Please provide a clear, concise answer that can be easily spoken out loud."
|
context += "Please provide a clear, concise answer that can be easily spoken out loud."
|
||||||
|
|
||||||
# Process response with agents
|
|
||||||
processor_result = await Runner.run(processor_agent, context)
|
processor_result = await Runner.run(processor_agent, context)
|
||||||
processor_response = processor_result.final_output
|
processor_response = processor_result.final_output
|
||||||
|
|
||||||
tts_result = await Runner.run(tts_agent, processor_response)
|
tts_result = await Runner.run(tts_agent, processor_response)
|
||||||
tts_response = tts_result.final_output
|
tts_response = tts_result.final_output
|
||||||
|
|
||||||
# Generate audio
|
|
||||||
async_openai = AsyncOpenAI(api_key=openai_api_key)
|
async_openai = AsyncOpenAI(api_key=openai_api_key)
|
||||||
audio_response = await async_openai.audio.speech.create(
|
audio_response = await async_openai.audio.speech.create(
|
||||||
model="gpt-4o-mini-tts",
|
model="gpt-4o-mini-tts",
|
||||||
|
|
@ -360,11 +292,9 @@ async def process_query(
|
||||||
response_format="mp3"
|
response_format="mp3"
|
||||||
)
|
)
|
||||||
|
|
||||||
# Save audio to a temporary file
|
|
||||||
temp_dir = tempfile.gettempdir()
|
temp_dir = tempfile.gettempdir()
|
||||||
audio_path = os.path.join(temp_dir, f"response_{uuid.uuid4()}.mp3")
|
audio_path = os.path.join(temp_dir, f"response_{uuid.uuid4()}.mp3")
|
||||||
|
|
||||||
# Write the audio content to the file
|
|
||||||
with open(audio_path, "wb") as f:
|
with open(audio_path, "wb") as f:
|
||||||
f.write(audio_response.content)
|
f.write(audio_response.content)
|
||||||
|
|
||||||
|
|
@ -382,7 +312,6 @@ async def process_query(
|
||||||
}
|
}
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"\nError processing query: {str(e)}")
|
|
||||||
return {
|
return {
|
||||||
"status": "error",
|
"status": "error",
|
||||||
"error": str(e),
|
"error": str(e),
|
||||||
|
|
@ -390,7 +319,6 @@ async def process_query(
|
||||||
}
|
}
|
||||||
|
|
||||||
def run_streamlit():
|
def run_streamlit():
|
||||||
"""Main Streamlit application."""
|
|
||||||
st.set_page_config(
|
st.set_page_config(
|
||||||
page_title="AI Voice Documentation Agent Team",
|
page_title="AI Voice Documentation Agent Team",
|
||||||
page_icon="🎙️",
|
page_icon="🎙️",
|
||||||
|
|
@ -400,7 +328,6 @@ def run_streamlit():
|
||||||
init_session_state()
|
init_session_state()
|
||||||
sidebar_config()
|
sidebar_config()
|
||||||
|
|
||||||
# Main content area
|
|
||||||
st.title("🎙️ AI Voice Documentation Agent Team")
|
st.title("🎙️ AI Voice Documentation Agent Team")
|
||||||
st.markdown("""
|
st.markdown("""
|
||||||
Get OpenAI SDK voice-powered answers to your documentation questions! Simply:
|
Get OpenAI SDK voice-powered answers to your documentation questions! Simply:
|
||||||
|
|
@ -409,7 +336,6 @@ def run_streamlit():
|
||||||
3. Ask your question below and get both text and voice responses
|
3. Ask your question below and get both text and voice responses
|
||||||
""")
|
""")
|
||||||
|
|
||||||
# Query input and processing
|
|
||||||
query = st.text_input(
|
query = st.text_input(
|
||||||
"What would you like to know about the documentation?",
|
"What would you like to know about the documentation?",
|
||||||
placeholder="e.g., How do I authenticate API requests?",
|
placeholder="e.g., How do I authenticate API requests?",
|
||||||
|
|
@ -438,10 +364,8 @@ def run_streamlit():
|
||||||
|
|
||||||
if "audio_path" in result:
|
if "audio_path" in result:
|
||||||
st.markdown(f"### 🔊 Audio Response (Voice: {st.session_state.selected_voice})")
|
st.markdown(f"### 🔊 Audio Response (Voice: {st.session_state.selected_voice})")
|
||||||
# Pass the file path directly to st.audio
|
|
||||||
st.audio(result["audio_path"], format="audio/mp3", start_time=0)
|
st.audio(result["audio_path"], format="audio/mp3", start_time=0)
|
||||||
|
|
||||||
# For download button, we still need to read the bytes
|
|
||||||
with open(result["audio_path"], "rb") as audio_file:
|
with open(result["audio_path"], "rb") as audio_file:
|
||||||
audio_bytes = audio_file.read()
|
audio_bytes = audio_file.read()
|
||||||
st.download_button(
|
st.download_button(
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue