VaibT's picture
Update app.py
5f8d363 verified
Raw
History Blame Contribute Delete
9.19 kB
import os
import urllib.request
import nest_asyncio
import gradio as gr
import openai
from llama_parse import LlamaParse
from llama_index.core import VectorStoreIndex, SimpleDirectoryReader, Settings
from llama_index.llms.openai import OpenAI
from llama_index.embeddings.openai import OpenAIEmbedding
# Apply nest_asyncio to allow nested event loops in a synchronous environment
nest_asyncio.apply()
# --- 1. ENVIRONMENT & API INITIALIZATION ---
OPENAI_API_KEY = os.environ.get("OPENAI_API_KEY")
LLAMA_CLOUD_API_KEY = os.environ.get("LLAMA_CLOUD_API_KEY")
if not OPENAI_API_KEY or not LLAMA_CLOUD_API_KEY:
raise ValueError("Missing essential API credentials. Ensure OPENAI_API_KEY and LLAMA_CLOUD_API_KEY are configured in Secrets.")
openai.api_key = OPENAI_API_KEY
os.environ["LLAMA_CLOUD_API_KEY"] = LLAMA_CLOUD_API_KEY
# --- 2. DATA ACQUISITION & PARSING ---
pdf_path = "apple_10k.pdf"
url = "https://s2.q4cdn.com/470004039/files/doc_financials/2021/q4/_10-K-2021-(As-Filed).pdf"
if not os.path.exists(pdf_path):
print("Downloading Apple 10-K report...")
urllib.request.urlretrieve(url, pdf_path)
print("Parsing document via LlamaParse...")
parser = LlamaParse(result_type="markdown")
document = parser.load_data(pdf_path)
with open("apple_10k.md", "w", encoding="utf-8") as f:
f.write(document[0].text)
# FIXED: Changed deprecated 'parsing_instruction' to 'system_prompt'
documents_with_instruction = LlamaParse(
result_type="markdown",
system_prompt="This is the Apple annual report. Make sure the language is English, if not translate it to English."
).load_data(pdf_path)
with open("apple_10k_instructions.md", "w", encoding="utf-8") as f:
f.write(documents_with_instruction[0].text)
# --- 3. LLAMAINDEX RAG CONFIGURATION ---
Settings.llm = OpenAI(
model="gpt-5-nano",
temperature=0.1,
system_prompt=(
"You are an expert financial analyst. Your task is to answer questions strictly "
"and accurately based on the provided Apple 10-K report. Provide concise answers, "
"directly referencing sections or figures from the report where possible. "
"If the information is not explicitly present in the document, "
"clearly state that the answer cannot be found in the provided text."
)
)
Settings.embed_model = OpenAIEmbedding(model="text-embedding-3-small")
Settings.chunk_size = 512
reader = SimpleDirectoryReader(input_files=["apple_10k.md", "apple_10k_instructions.md"])
docs = reader.load_data()
index = VectorStoreIndex.from_documents(docs)
query_engine = index.as_query_engine(similarity_top_k=5)
print("LlamaIndex Knowledge Base initialized.")
# --- 4. GRADIO INTERFACE CONFIGURATION ---
custom_css = """
.gradio-container {
font-family: 'Inter', 'Helvetica Neue', sans-serif !important;
background: linear-gradient(135deg, #f3f4f6 0%, #e5e7eb 100%);
}
.sidebar-panel {
background: white;
border-radius: 16px;
padding: 20px;
box-shadow: 0 10px 25px rgba(0,0,0,0.05);
border: 1px solid #e5e7eb;
}
.gradient-text {
background: linear-gradient(90deg, #1d4ed8, #9333ea);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
font-weight: 800;
}
.stat-card {
background: linear-gradient(135deg, #1e293b 0%, #0f172a 100%);
color: white;
padding: 20px;
border-radius: 12px;
margin-bottom: 15px;
text-align: center;
box-shadow: 0 4px 6px rgba(0,0,0,0.1);
}
.action-button {
background: linear-gradient(90deg, #3b82f6, #2563eb);
border: none;
color: white;
font-weight: bold;
}
"""
def chat_with_financial_analyst(user_message, history):
if not user_message.strip():
return "", history
history.append({"role": "user", "content": user_message})
history.append({"role": "assistant", "content": "Calculating financials..."})
yield "", history
try:
response = query_engine.query(user_message)
final_answer = str(response)
except Exception as e:
final_answer = f"⚠️ **Error querying the document:** {str(e)}"
history[-1]["content"] = final_answer
yield "", history
def load_quick_prompt(prompt, history):
for text, hist in chat_with_financial_analyst(prompt, history):
pass
return "", hist
# FIXED: Removed 'css=custom_css' from gr.Blocks() (Moved to app.launch())
with gr.Blocks(fill_width=True) as app:
with gr.Row():
gr.HTML("""
<div style='text-align: center; padding: 20px 0;'>
<img src='https://upload.wikimedia.org/wikipedia/commons/f/fa/Apple_logo_black.svg' width='50' style='margin: 0 auto 10px auto;'/>
<h1 class='gradient-text' style='margin: 0; font-size: 2.5em;'>Apple Intelligence: 10-K Financial Oracle</h1>
<p style='color: #64748b; font-size: 1.1em;'>Powered by LlamaIndex, OpenAI, and LlamaParse</p>
</div>
""")
with gr.Row():
with gr.Column(scale=1, elem_classes="sidebar-panel"):
gr.HTML("<h3 style='color: #0f172a; margin-top: 0;'>πŸ“Š Document Context</h3>")
gr.Image(
value="https://images.unsplash.com/photo-1611974789855-9c2a0a7236a3?q=80&w=1000&auto=format&fit=crop",
show_label=False,
container=False,
height=180
)
gr.HTML("""
<div class='stat-card' style='margin-top: 15px;'>
<h4 style='margin: 0; color: #94a3b8;'>Active Document</h4>
<h2 style='margin: 5px 0; color: white;'>AAPL 2021 10-K</h2>
<p style='margin: 0; font-size: 0.8em; color: #38bdf8;'>Vector Indexed & Parsed</p>
</div>
""")
# FIX: Explicit inline styling with !important flags prevents system dark-mode overrides
gr.HTML(
"""
<div style='color: #1e293b; font-size: 0.95em; line-height: 1.6;'>
<strong style='color: #0f172a; font-size: 1.1em; display: block; margin-bottom: 8px;'>Capabilities:</strong>
<ul style='padding-left: 20px; margin-top: 5px; list-style-type: none;'>
<li style='color: #1e293b !important; margin-bottom: 6px;'>πŸ“ˆ Extracts exact revenue and profit figures.</li>
<li style='color: #1e293b !important; margin-bottom: 6px;'>⚠️ Analyzes corporate risk factors.</li>
<li style='color: #1e293b !important; margin-bottom: 6px;'>πŸ“Š Interprets 5-year cumulative return charts.</li>
<li style='color: #1e293b !important; margin-bottom: 6px;'>🌐 Translates complex financial jargon.</li>
</ul>
</div>
"""
)
with gr.Column(scale=3):
# FIXED: Removed type="messages" (This is what caused your fatal error)
chatbot = gr.Chatbot(
label="Financial Expert AI",
height=550,
avatar_images=(
"https://cdn-icons-png.flaticon.com/512/3135/3135715.png",
"https://cdn-icons-png.flaticon.com/512/12108/12108151.png"
)
)
with gr.Row():
msg_input = gr.Textbox(
show_label=False,
placeholder="Ask about Apple's net sales, iPhone revenue, or operational risks...",
container=False,
scale=4
)
submit_btn = gr.Button("Analyze ⚑", variant="primary", scale=1, elem_classes="action-button")
gr.Markdown("### ⚑ Quick Actions")
with gr.Row():
btn_revenue = gr.Button("πŸ’° Summarize Stock Classes", size="sm")
btn_risk = gr.Button("⚠️ Filing Regulations", size="sm")
btn_chart = gr.Button("πŸ“ˆ Explain Context", size="sm")
btn_clear = gr.Button("πŸ—‘οΈ Clear Chat", size="sm", variant="secondary")
# Event binding workflows
msg_input.submit(fn=chat_with_financial_analyst, inputs=[msg_input, chatbot], outputs=[msg_input, chatbot])
submit_btn.click(fn=chat_with_financial_analyst, inputs=[msg_input, chatbot], outputs=[msg_input, chatbot])
btn_revenue.click(
fn=lambda history: load_quick_prompt("Summarize the common stocks and notes listed in the report.", history),
inputs=[chatbot],
outputs=[msg_input, chatbot]
)
btn_risk.click(
fn=lambda history: load_quick_prompt("What does 'Annual Report pursuant to Section 13 or 15(d) of the Securities Exchange Act of 1934' signify?", history),
inputs=[chatbot],
outputs=[msg_input, chatbot]
)
btn_chart.click(
fn=lambda history: load_quick_prompt("Does the document contain numbers validating the 5-Year Cumulative Total Return chart?", history),
inputs=[chatbot],
outputs=[msg_input, chatbot]
)
btn_clear.click(lambda: [], None, chatbot, queue=False)
# FIXED: Passed BOTH theme and css inside the app.launch() method
app.launch(theme=gr.themes.Ocean(primary_hue="blue", neutral_hue="slate"), css=custom_css)