File size: 9,548 Bytes
900d7e8
f4ca844
 
 
d094ea0
22e33ed
4669a77
cf961a5
900d7e8
f4ca844
d094ea0
f4ca844
d094ea0
 
0d006a9
900d7e8
4618b90
 
 
 
 
 
 
0d006a9
3a65210
f6d1cff
d094ea0
1b4db50
d094ea0
 
252c5e7
f6d1cff
4618b90
 
252c5e7
4618b90
 
 
 
22e33ed
4618b90
 
252c5e7
1b4db50
f6d1cff
 
4618b90
f6d1cff
4618b90
 
900d7e8
f4ca844
 
 
 
3a65210
f4ca844
 
4669a77
 
 
 
 
 
 
 
f4ca844
 
 
 
6e9984b
cf961a5
6e9984b
cf961a5
6e9984b
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
cf961a5
6e9984b
cf961a5
6e9984b
 
 
cf961a5
 
 
6e9984b
cf961a5
 
 
 
 
6e9984b
cf961a5
 
 
 
 
6e9984b
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
cf961a5
d094ea0
6e9984b
cf961a5
 
6e9984b
d094ea0
f4ca844
6e9984b
cf961a5
 
6e9984b
f4ca844
 
6e9984b
cf961a5
6e9984b
cf961a5
 
f4ca844
3a65210
6e9984b
 
 
 
cf961a5
 
 
 
6e9984b
 
cf961a5
 
6e9984b
 
 
 
 
 
cf961a5
 
 
 
 
 
3a65210
cf961a5
3a65210
 
001487b
cf961a5
d094ea0
 
 
252c5e7
cf961a5
f4ca844
6e9984b
f4ca844
4618b90
f4ca844
 
 
 
d094ea0
 
 
 
cf961a5
d094ea0
 
 
cf961a5
d094ea0
 
 
cf961a5
d094ea0
 
 
cf961a5
d094ea0
252c5e7
900d7e8
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
import gradio as gr
import os
from dotenv import load_dotenv
from pathlib import Path
from modules.utils import load_processed_meetings, load_prompt_library
from src.modules.fed_tools import search_meetings_by_date, FED_TOOLS
from src.modules.llm_completions import stream_fed_agent_response
from src.config.theme_config import CUSTOM_CSS, GRADIO_THEME, EXAMPLE_QUESTIONS

load_dotenv()

_FILE_PATH = Path(__file__).parents[1]
FOMC_MEETINGS = load_processed_meetings()
PROMPT_LIBRARY = load_prompt_library()
API_KEY = os.getenv("FIREWORKS_API_KEY")

def convert_history_to_string(history: list) -> str:
    previous_messages = ""
    for msg in history:
        previous_messages += f"{msg['role'].capitalize()}: {msg['content']}\n\n"
    return previous_messages


def respond_for_chat_interface(message: str, history: list, api_key_input: str = None):
    """Enhanced response function for gr.ChatInterface with Fed AI Savant capabilities"""

    api_key = api_key_input.strip() if api_key_input else os.getenv("FIREWORKS_API_KEY", "")

    if not api_key:
        yield "❌ Please enter your Fireworks AI API key in the configuration section above."
        return

    message_with_history = convert_history_to_string(history)

    try:
        for messages in stream_fed_agent_response(
                message=message,
                api_key=api_key,
                prompt_library=PROMPT_LIBRARY,
                fed_tools=FED_TOOLS,
                history=message_with_history
        ):
            if isinstance(messages, list) and len(messages) > 0:
                yield messages
            else:
                yield str(messages)

    except Exception as e:
        error_msg = f"❌ Error: {str(e)}"
        yield error_msg

# Function to create searchable FOMC meetings accordion
def create_fomc_meetings_accordion():
    """Create searchable accordion for FOMC meetings"""
    accordions = []
    for meeting in FOMC_MEETINGS:
        title = f"{meeting['date']} - Rate: {meeting['rate_decision']}"
        content = f"""
            **Meeting Title:** {meeting['title']}
            
            **Rate Decision:** {meeting['rate_decision']}
            
            **Summary:** {meeting['summary']}
            
            ---
            *Click to expand for full meeting details*
        """
        accordions.append((title, content))
    return accordions

def generate_meetings_html(meetings_list):
    """Generate HTML for meetings list with Fireworks AI purple palette"""
    if not meetings_list:
        return '<p style="color: #64748B; text-align: center; padding: 20px;">No meetings available</p>'

    html_content = '<div style="max-height: 600px; overflow-y: auto;">'
    for meeting in meetings_list:
        date = meeting.get('date', 'Unknown Date')
        rate_decision = meeting.get('rate', 'N/A')
        title = meeting.get('title', 'FOMC Meeting')
        action = meeting.get('action', 'N/A')
        magnitude = meeting.get('magnitude', 'N/A')
        forward_guidance = meeting.get('forward_guidance')
        economic_outlook = meeting.get('economic_outlook')
        market_impact = meeting.get('market_impact', 'N/A')
        url = meeting.get('url', '')

        factors_html = ""
        key_factors = meeting.get('key_economic_factors', [])
        if key_factors and len(key_factors) > 0:
            factors_html = "<p style='margin: 10px 0 6px 0; font-size: 0.9em; color: #0F172A; font-weight: 600;'>Key Factors:</p><ul style='margin: 4px 0; padding-left: 20px; font-size: 0.88em; color: #64748B;'>"
            for factor in key_factors:
                factors_html += f"<li style='margin: 4px 0;'>{factor}</li>"
            factors_html += "</ul>"

        html_content += f"""
        <details class="meeting-details" style="border: 1px solid #E6EAF4; border-radius: 10px; margin: 10px 0; background: white; overflow: hidden; transition: all 0.25s ease;">
            <summary class="meeting-summary" style="font-weight: 600; cursor: pointer; color: #0F172A; padding: 14px 18px; font-size: 0.95em; background: linear-gradient(90deg, transparent 0%, #F3F0FF 100%);">
                {date} - {rate_decision}
            </summary>
            <div class="meeting-content" style="padding: 16px 18px; border-top: 1px solid #E6EAF4; background: #FAFBFC; font-size: 0.88em;">
                <p style="margin: 6px 0; color: #0F172A;"><strong style="color: #6720FF;">Action:</strong> {action}</p>
                <p style="margin: 6px 0; color: #0F172A;"><strong style="color: #6720FF;">Magnitude:</strong> {magnitude}</p>
                <p style="margin: 10px 0 6px 0; color: #0F172A;"><strong style="color: #6720FF;">Forward Guidance:</strong></p>
                <p style="margin: 0 0 10px 0; color: #64748B; line-height: 1.5;">{forward_guidance}</p>
                {factors_html}
                <p style="margin: 10px 0 6px 0; color: #0F172A;"><strong style="color: #6720FF;">Economic Outlook:</strong></p>
                <p style="margin: 0 0 10px 0; color: #64748B; line-height: 1.5;">{economic_outlook}</p>
                <p style="margin: 10px 0 6px 0; color: #0F172A;"><strong style="color: #6720FF;">Market Impact:</strong></p>
                <p style="margin: 0 0 10px 0; color: #64748B; line-height: 1.5;">{market_impact}</p>
                {f'<p style="margin: 10px 0 0 0;"><strong style="color: #6720FF;">Source:</strong> <a href="{url}" target="_blank" style="color: #6720FF; text-decoration: none; transition: color 0.2s ease;">Fed Minutes PDF</a></p>' if url else ''}
            </div>
        </details>
        """
    html_content += '</div>'
    return html_content

def search_and_format_meetings(query: str):
    """Search meetings and format them for HTML display"""
    if not query.strip():
        return generate_meetings_html(FOMC_MEETINGS)

    search_result = search_meetings_by_date(query)

    if search_result.get("success") and search_result.get("results"):
        return generate_meetings_html(search_result["results"])
    else:
        return '<p style="color: #ef4444; text-align: center; padding: 20px;">No meetings found matching your search.</p>'

with gr.Blocks(css=CUSTOM_CSS, title="Fed AI Savant", theme=GRADIO_THEME) as demo:

    gr.Markdown("""
    <h1 class="header-title" style="font-size: 2.5em; margin-bottom: 0.5em;">Fed AI Savant</h1>
    <p style="color: #64748B; font-size: 1.1em; margin-top: 0;">Intelligent Analysis of Federal Reserve Policy and FOMC Meetings with <a href="https://huggingface.co/openai/gpt-oss-120b" target="_blank" style="color: #6720FF;">gpt-oss 120B</a></p>
    """)

    with gr.Row():
        # Sidebar for FOMC Meetings
        with gr.Column(scale=1, min_width=300, elem_classes="meetings-container"):
            gr.Markdown("### FOMC Meetings")

            # Date search
            date_search = gr.Textbox(
                placeholder="Search by date...",
                label="Search",
                lines=1,
                container=False,
                elem_classes="search-box"
            )

            meetings_accordion = gr.HTML(generate_meetings_html(FOMC_MEETINGS))

        # Main content area
        with gr.Column(scale=2):
            # Compact header with Fireworks AI logo and API key
            with gr.Row(elem_classes="compact-header"):
                with gr.Column(scale=1, min_width=150):
                    gr.Markdown("<p style='margin: 0; padding: 0; font-size: 0.85em; color: #64748B;'>Powered by</p>")
                    gr.Image(
                        value=str(_FILE_PATH / "assets" / "fireworks_logo.png"),
                        height=35,
                        width=140,
                        show_label=False,
                        show_download_button=False,
                        container=False,
                        show_fullscreen_button=False,
                        show_share_button=False,
                    )
            # Example Questions - compact version
            with gr.Row(elem_classes="compact-examples"):
                example_1 = gr.Button(EXAMPLE_QUESTIONS[0], size="sm", scale=1)
                example_2 = gr.Button(EXAMPLE_QUESTIONS[1], size="sm", scale=1)
                example_3 = gr.Button(EXAMPLE_QUESTIONS[2], size="sm", scale=1)
                example_4 = gr.Button(EXAMPLE_QUESTIONS[3], size="sm", scale=1)

            # Chat interface with more space
            chat_interface = gr.ChatInterface(
                fn=respond_for_chat_interface,
                type="messages",
                chatbot=gr.Chatbot(height=450, show_label=False, type="messages", elem_classes="chat-window"),
                textbox=gr.Textbox(
                    placeholder="Ask about Fed policy, rate decisions, or FOMC meetings...", scale=10
                ),
                cache_examples=False,
                submit_btn="Send"
            )

    date_search.change(
        search_and_format_meetings,
        inputs=date_search,
        outputs=meetings_accordion
    )
    
    def set_example_text(text):
        return text
    
    example_1.click(
        lambda: EXAMPLE_QUESTIONS[0],
        outputs=chat_interface.textbox
    )
    example_2.click(
        lambda: EXAMPLE_QUESTIONS[1],
        outputs=chat_interface.textbox
    )
    example_3.click(
        lambda: EXAMPLE_QUESTIONS[2],
        outputs=chat_interface.textbox
    )
    example_4.click(
        lambda: EXAMPLE_QUESTIONS[3],
        outputs=chat_interface.textbox
    )

if __name__ == "__main__":
    demo.launch()