RobertoBarrosoLuque commited on
Commit
1062173
Β·
1 Parent(s): 001487b

Update with good function calling

Browse files
configs/prompt_library.yaml CHANGED
@@ -69,39 +69,104 @@ fed_orchestrator: |
69
  You are a Federal Reserve Tool Orchestrator. Your job is to analyze user queries about Fed policy and FOMC meetings, then decide which tools to use to gather the most relevant information.
70
 
71
  AVAILABLE TOOLS:
72
- 1. search_meetings(query: str, limit: int = 3) - Search across all FOMC meeting fields for relevant information
73
- 2. get_latest_meeting() - Get the most recent FOMC meeting data
74
- 3. get_rate_decision(date: str) - Get specific meeting data by date (YYYY-MM-DD format)
75
- 4. compare_meetings(date1: str, date2: str) - Compare two meetings side by side
76
-
77
- INSTRUCTIONS:
78
- - Analyze the user query to determine which tools would provide the most relevant information
79
- - You can use multiple tools if needed to fully answer the question
80
- - For search queries, extract key terms and use search_meetings
81
- - For recent/latest questions, use get_latest_meeting
82
- - For specific date questions, use get_rate_decision
83
- - For comparison questions, use compare_meetings
84
- - Always provide the exact function calls in JSON format
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
85
 
86
  RESPONSE FORMAT:
87
  Return a JSON object with this structure:
88
- {{
89
  "tools_needed": [
90
- {{
91
  "function": "function_name",
92
- "parameters": {{"param1": "value1", "param2": "value2"}},
93
- "reasoning": "Why this tool is needed"
94
- }}
 
95
  ],
96
- "query_analysis": "Brief analysis of what the user is asking for"
97
- }}
 
98
 
99
  EXAMPLES:
100
- User: "What was the latest rate decision?"
101
- Response: {{"tools_needed": [{{"function": "get_latest_meeting", "parameters": {{}}, "reasoning": "User wants the most recent FOMC meeting information"}}], "query_analysis": "User is asking for the most recent Fed rate decision"}}
102
 
103
- User: "Tell me about inflation expectations in recent meetings"
104
- Response: {{"tools_needed": [{{"function": "search_meetings", "parameters": {{"query": "inflation expectations", "limit": 3}}, "reasoning": "Need to search for inflation-related content across meetings"}}], "query_analysis": "User wants information about inflation expectations from FOMC meetings"}}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
105
 
106
  User Query: {user_query}
107
  The date is {date}
 
69
  You are a Federal Reserve Tool Orchestrator. Your job is to analyze user queries about Fed policy and FOMC meetings, then decide which tools to use to gather the most relevant information.
70
 
71
  AVAILABLE TOOLS:
72
+
73
+ 1. search_meetings(query: str, limit: int = 3)
74
+ - Purpose: Search across all FOMC meeting fields for relevant information
75
+ - Parameters:
76
+ * query (required): Search term or phrase - searches across date, title, action, rate, magnitude, forward_guidance, economic_outlook, market_impact, key_economic_factors
77
+ * limit (optional): Maximum meetings to return (default: 3, recommend 3-5 for most queries)
78
+ - Best for: Thematic searches, finding patterns across meetings, keyword-based queries
79
+ - Returns: Ranked results with match scores and details about which fields matched
80
+ - Examples: "inflation expectations", "employment concerns", "rate cuts", "economic uncertainty"
81
+
82
+ 2. get_latest_meeting()
83
+ - Purpose: Get the most recent FOMC meeting data
84
+ - Parameters: None
85
+ - Best for: "What's the latest?", "most recent decision", "current rate"
86
+ - Returns: Complete data for the newest meeting plus total meeting count
87
+ - Use when: User asks about current/recent/latest Fed actions without specifying dates
88
+
89
+ 3. get_rate_decision(date: str)
90
+ - Purpose: Get specific meeting data by exact or approximate date
91
+ - Parameters:
92
+ * date (required): Meeting date in YYYY-MM-DD format (e.g., "2024-12-18")
93
+ - Best for: Date-specific queries, historical lookups
94
+ - Returns: Meeting data with exact_match flag; finds closest meeting within 30 days if no exact match
95
+ - Use when: User mentions specific dates, months, or "the meeting in [time period]"
96
+
97
+ 4. compare_meetings(date1: str, date2: str)
98
+ - Purpose: Compare two meetings side by side with detailed analysis
99
+ - Parameters:
100
+ * date1 (required): First meeting date in YYYY-MM-DD format
101
+ * date2 (required): Second meeting date in YYYY-MM-DD format
102
+ - Best for: "How did X change between meetings?", trend analysis, policy evolution
103
+ - Returns: Differences, similarities, and factor analysis between meetings
104
+ - Use when: User explicitly asks for comparisons or asks about changes over time
105
+
106
+ TOOL SELECTION STRATEGY:
107
+ - Single recent query β†’ get_latest_meeting()
108
+ - Specific date mention β†’ get_rate_decision(date)
109
+ - Two dates or "compared to" β†’ compare_meetings(date1, date2)
110
+ - Thematic/keyword search β†’ search_meetings(query, limit)
111
+ - Complex queries may require multiple tools in sequence
112
+
113
+ DATE HANDLING:
114
+ - Convert relative dates to YYYY-MM-DD format (e.g., "December 2024" β†’ "2024-12-18")
115
+ - For approximate dates, use typical FOMC meeting dates (usually 3rd Wednesday of month)
116
+ - Recent meeting schedule: typically 8 meetings per year
117
 
118
  RESPONSE FORMAT:
119
  Return a JSON object with this structure:
120
+ {
121
  "tools_needed": [
122
+ {
123
  "function": "function_name",
124
+ "parameters": {"param1": "value1", "param2": "value2"},
125
+ "reasoning": "Specific explanation of why this tool and these parameters are optimal",
126
+ "execution_order": 1
127
+ }
128
  ],
129
+ "query_analysis": "Detailed analysis of user intent and information needs",
130
+ "date_interpretations": "Any date conversions or assumptions made"
131
+ }
132
 
133
  EXAMPLES:
 
 
134
 
135
+ User: "What was the latest rate decision?"
136
+ Response: {
137
+ "tools_needed": [{
138
+ "function": "get_latest_meeting",
139
+ "parameters": {},
140
+ "reasoning": "User wants most recent Fed decision; get_latest_meeting provides complete current data",
141
+ "execution_order": 1
142
+ }],
143
+ "query_analysis": "User seeking current Fed policy status - needs most recent meeting data",
144
+ "date_interpretations": "No dates specified - using latest available"
145
+ }
146
+
147
+ User: "How did inflation concerns change from June to December 2024?"
148
+ Response: {
149
+ "tools_needed": [{
150
+ "function": "compare_meetings",
151
+ "parameters": {"date1": "2024-06-12", "date2": "2024-12-18"},
152
+ "reasoning": "User wants evolution analysis between specific periods; compare_meetings provides detailed difference analysis including factor changes",
153
+ "execution_order": 1
154
+ }],
155
+ "query_analysis": "User seeking trend analysis of inflation concerns over 6-month period",
156
+ "date_interpretations": "June 2024 β†’ 2024-06-12 (typical June FOMC date), December 2024 β†’ 2024-12-18 (typical December FOMC date)"
157
+ }
158
+
159
+ User: "Tell me about employment data in recent Fed meetings"
160
+ Response: {
161
+ "tools_needed": [{
162
+ "function": "search_meetings",
163
+ "parameters": {"query": "employment", "limit": 4},
164
+ "reasoning": "Thematic search across multiple meetings for employment-related content; higher limit captures recent trend",
165
+ "execution_order": 1
166
+ }],
167
+ "query_analysis": "User wants employment-focused analysis across recent meetings - needs keyword search with sufficient results for pattern identification",
168
+ "date_interpretations": "No specific dates - search will return most relevant recent matches"
169
+ }
170
 
171
  User Query: {user_query}
172
  The date is {date}
src/modules/llm_completions.py CHANGED
@@ -184,6 +184,114 @@ def execute_fed_tools(tools_decision: Dict[str, Any], fed_tools: Dict[str, calla
184
 
185
  return results
186
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
187
  def stream_fed_agent_response(
188
  message: str,
189
  api_key: str,
@@ -192,6 +300,7 @@ def stream_fed_agent_response(
192
  ):
193
  """Main orchestrator function that coordinates tools and generates responses with ChatMessage objects"""
194
 
 
195
  if not message.strip():
196
  yield [ChatMessage(role="assistant", content="Please enter a question about Federal Reserve policy or FOMC meetings.")]
197
  return
@@ -203,7 +312,6 @@ def stream_fed_agent_response(
203
  messages = []
204
 
205
  try:
206
- # Step 1: Use orchestrator to determine tools needed
207
  messages.append(ChatMessage(
208
  role="assistant",
209
  content="Analyzing your query...",
@@ -214,78 +322,38 @@ def stream_fed_agent_response(
214
  orchestrator_result = get_orchestrator_decision(message, api_key, prompt_library)
215
  tools_decision = orchestrator_result["decision"]
216
 
217
- # Update planning message
218
- messages[0] = ChatMessage(
219
- role="assistant",
220
- content=f"Query Analysis: {tools_decision.get('query_analysis', 'Analyzing Fed data requirements')}\n\nTools needed: {len(tools_decision.get('tools_needed', []))}",
221
- metadata={"title": "🧠 Planning", "status": "done"}
222
- )
223
  yield messages
224
 
225
- # Step 2: Execute the determined tools
226
  if tools_decision.get("tools_needed"):
227
- for i, tool in enumerate(tools_decision["tools_needed"]):
228
- tool_msg = ChatMessage(
229
- role="assistant",
230
- content=f"Executing: {tool['function']}({', '.join([f'{k}={v}' for k, v in tool['parameters'].items()])})\n\nReasoning: {tool['reasoning']}",
231
- metadata={"title": f"πŸ”§ Tool {i+1}: {tool['function']}", "status": "pending"}
232
- )
233
- messages.append(tool_msg)
234
- yield messages
235
 
236
- # Execute all tools
237
  tool_results = execute_fed_tools(tools_decision, fed_tools)
238
 
239
- # Update tool messages with results
240
- for i, (tool_result, tool_msg) in enumerate(zip(tool_results, messages[1:])):
241
- execution_time = tool_result["execution_time"]
242
- success_status = "βœ…" if tool_result["success"] else "❌"
243
-
244
- messages[i+1] = ChatMessage(
245
- role="assistant",
246
- content=f"{success_status} {tool_result['function']} completed\n\nExecution time: {execution_time:.2f}s\n\nResult summary: {str(tool_result['result'])[:200]}...",
247
- metadata={"title": f"πŸ”§ Tool {i+1}: {tool_result['function']}", "status": "done", "duration": execution_time}
248
- )
249
-
250
  yield messages
251
 
252
- # Step 3: Use results to generate final response
253
- combined_context = ""
254
- for result in tool_results:
255
- if result["success"]:
256
- combined_context += f"\n\nFrom {result['function']}: {json.dumps(result['result'], indent=2)}"
257
 
258
- # Generate Fed Savant response using tool results
259
  system_prompt_template = prompt_library.get('fed_savant_chat', '')
260
  system_prompt = system_prompt_template.format(
261
  fed_data_context=combined_context,
262
  user_question=message,
263
  date=TODAY
264
  )
 
 
265
 
266
- # Initialize LLM and get streaming response
267
- llm = get_llm("large", api_key)
268
-
269
- final_response = ""
270
- for chunk in llm.chat.completions.create(
271
- messages=[
272
- {"role": "system", "content": system_prompt},
273
- {"role": "user", "content": message}
274
- ],
275
- temperature=0.2,
276
- stream=True,
277
- max_tokens=1000
278
- ):
279
- if chunk.choices[0].delta.content:
280
- final_response += chunk.choices[0].delta.content
281
-
282
- # Update messages list with current response
283
- if len(messages) > len(tool_results):
284
- messages[-1] = ChatMessage(role="assistant", content=final_response)
285
- else:
286
- messages.append(ChatMessage(role="assistant", content=final_response))
287
-
288
- yield messages
289
 
290
  else:
291
  # No tools needed, direct response
 
184
 
185
  return results
186
 
187
+ def extract_citations_from_tool_results(tool_results: List[Dict[str, Any]]) -> List[Dict[str, str]]:
188
+ """Extract unique citations from tool results"""
189
+ citations = []
190
+
191
+ for result in tool_results:
192
+ if result["success"] and result["result"].get("success"):
193
+ # Check if result has meeting data with URLs
194
+ if "meeting" in result["result"]:
195
+ meeting = result["result"]["meeting"]
196
+ if meeting.get("url"):
197
+ citations.append({
198
+ "date": meeting.get("date", "Unknown date"),
199
+ "url": meeting["url"],
200
+ "title": meeting.get("title", f"FOMC Meeting {meeting.get('date', '')}")
201
+ })
202
+ elif "results" in result["result"]:
203
+ # Handle search results
204
+ for meeting in result["result"]["results"]:
205
+ if meeting.get("url"):
206
+ citations.append({
207
+ "date": meeting.get("date", "Unknown date"),
208
+ "url": meeting["url"],
209
+ "title": meeting.get("title", f"FOMC Meeting {meeting.get('date', '')}")
210
+ })
211
+
212
+ # Remove duplicate citations
213
+ unique_citations = []
214
+ seen_urls = set()
215
+ for citation in citations:
216
+ if citation["url"] not in seen_urls:
217
+ unique_citations.append(citation)
218
+ seen_urls.add(citation["url"])
219
+
220
+ return unique_citations
221
+
222
+ def format_response_with_citations(response: str, citations: List[Dict[str, str]]) -> str:
223
+ """Format response with citations appended"""
224
+ if citations:
225
+ response += "\n\n**πŸ“š Sources:**\n"
226
+ for citation in citations:
227
+ response += f"β€’ [{citation['title']}]({citation['url']})\n"
228
+ return response
229
+
230
+ def create_planning_message(tools_decision: Dict[str, Any]) -> ChatMessage:
231
+ """Create the planning/analysis message"""
232
+ return ChatMessage(
233
+ role="assistant",
234
+ content=f"Query Analysis: {tools_decision.get('query_analysis', 'Analyzing Fed data requirements')}\n\nTools needed: {len(tools_decision.get('tools_needed', []))}",
235
+ metadata={"title": "🧠 Planning", "status": "done"}
236
+ )
237
+
238
+ def create_tool_messages(tools_decision: Dict[str, Any]) -> List[ChatMessage]:
239
+ """Create initial tool execution messages"""
240
+ tool_messages = []
241
+
242
+ for i, tool in enumerate(tools_decision["tools_needed"]):
243
+ tool_msg = ChatMessage(
244
+ role="assistant",
245
+ content=f"Executing: {tool['function']}({', '.join([f'{k}={v}' for k, v in tool['parameters'].items()])})\n\nReasoning: {tool['reasoning']}",
246
+ metadata={"title": f"πŸ”§ Tool {i+1}: {tool['function']}", "status": "pending"}
247
+ )
248
+ tool_messages.append(tool_msg)
249
+
250
+ return tool_messages
251
+
252
+ def update_tool_messages_with_results(tool_results: List[Dict[str, Any]]) -> List[ChatMessage]:
253
+ """Update tool messages with execution results"""
254
+ updated_messages = []
255
+
256
+ for i, tool_result in enumerate(tool_results):
257
+ execution_time = tool_result["execution_time"]
258
+ success_status = "βœ…" if tool_result["success"] else "❌"
259
+
260
+ updated_msg = ChatMessage(
261
+ role="assistant",
262
+ content=f"{success_status} {tool_result['function']} completed\n\nExecution time: {execution_time:.2f}s\n\nResult summary: {str(tool_result['result'])[:200]}...",
263
+ metadata={"title": f"πŸ”§ Tool {i+1}: {tool_result['function']}", "status": "done", "duration": execution_time}
264
+ )
265
+ updated_messages.append(updated_msg)
266
+
267
+ return updated_messages
268
+
269
+ def build_context_from_tool_results(tool_results: List[Dict[str, Any]]) -> str:
270
+ """Build combined context from successful tool results"""
271
+ combined_context = ""
272
+ for result in tool_results:
273
+ if result["success"]:
274
+ combined_context += f"\n\nFrom {result['function']}: {json.dumps(result['result'], indent=2)}"
275
+ return combined_context
276
+
277
+ def stream_final_response(message: str, system_prompt: str, api_key: str, citations: List[Dict[str, str]]):
278
+ """Stream the final Fed Savant response with citations"""
279
+ llm = get_llm("large", api_key)
280
+
281
+ final_response = ""
282
+ for chunk in llm.chat.completions.create(
283
+ messages=[
284
+ {"role": "system", "content": system_prompt},
285
+ {"role": "user", "content": message}
286
+ ],
287
+ temperature=0.2,
288
+ stream=True,
289
+ max_tokens=1000
290
+ ):
291
+ if chunk.choices[0].delta.content:
292
+ final_response += chunk.choices[0].delta.content
293
+ yield format_response_with_citations(final_response, citations)
294
+
295
  def stream_fed_agent_response(
296
  message: str,
297
  api_key: str,
 
300
  ):
301
  """Main orchestrator function that coordinates tools and generates responses with ChatMessage objects"""
302
 
303
+ # Input validation
304
  if not message.strip():
305
  yield [ChatMessage(role="assistant", content="Please enter a question about Federal Reserve policy or FOMC meetings.")]
306
  return
 
312
  messages = []
313
 
314
  try:
 
315
  messages.append(ChatMessage(
316
  role="assistant",
317
  content="Analyzing your query...",
 
322
  orchestrator_result = get_orchestrator_decision(message, api_key, prompt_library)
323
  tools_decision = orchestrator_result["decision"]
324
 
325
+ messages[0] = create_planning_message(tools_decision)
 
 
 
 
 
326
  yield messages
327
 
 
328
  if tools_decision.get("tools_needed"):
329
+ tool_messages = create_tool_messages(tools_decision)
330
+ messages.extend(tool_messages)
331
+ yield messages
 
 
 
 
 
332
 
 
333
  tool_results = execute_fed_tools(tools_decision, fed_tools)
334
 
335
+ updated_tool_messages = update_tool_messages_with_results(tool_results)
336
+ messages[1:] = updated_tool_messages # Replace tool messages but keep planning
 
 
 
 
 
 
 
 
 
337
  yield messages
338
 
339
+ combined_context = build_context_from_tool_results(tool_results)
340
+ citations = extract_citations_from_tool_results(tool_results)
 
 
 
341
 
 
342
  system_prompt_template = prompt_library.get('fed_savant_chat', '')
343
  system_prompt = system_prompt_template.format(
344
  fed_data_context=combined_context,
345
  user_question=message,
346
  date=TODAY
347
  )
348
+
349
+ expected_messages_count = 1 + len(tool_results)
350
 
351
+ for response_chunk in stream_final_response(message, system_prompt, api_key, citations):
352
+ if len(messages) < expected_messages_count + 1:
353
+ messages.append(ChatMessage(role="assistant", content=response_chunk))
354
+ else:
355
+ messages[-1] = ChatMessage(role="assistant", content=response_chunk)
356
+ yield messages
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
357
 
358
  else:
359
  # No tools needed, direct response