Spaces:
Sleeping
Sleeping
| 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) |