broadfield-dev commited on
Commit
5638299
·
verified ·
1 Parent(s): eacf435

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +36 -27
app.py CHANGED
@@ -123,60 +123,42 @@ def process_user_interaction_gradio(user_input: str, provider_name: str, model_d
123
  user_input_lower = user_input.lower()
124
  time_before_tool_decision = time.time()
125
 
126
- # --- REFACTORED TOOL-DECISION LOGIC ---
127
-
128
- # Heuristic for simple interactions that don't need a tool-decision LLM call
129
  is_simple_interaction = len(user_input.split()) <= 3 and any(kw in user_input_lower for kw in ["hello", "hi", "thanks", "ok", "bye"]) and not "?" in user_input
130
 
131
  if is_simple_interaction:
132
  action_type = "quick_respond"
133
  else:
134
- # For any non-trivial interaction, use an LLM to decide the best tool.
135
  yield "status", "<i>[LLM choosing best approach...]</i>"
136
-
137
- # 1. Define all possible tools and their descriptions
138
  tool_definitions = {
139
  "answer_using_conversation_memory": "Use if the user's query refers to a previous conversation, asks you to 'remember' or 'recall' something specific, or seems like it could be answered by a past interaction you've had. This tool searches a database of your past conversations.",
140
  "search_duckduckgo_and_report": "Use for general knowledge questions, questions about current events, or when the user explicitly asks you to search the web for information.",
141
  "scrape_url_and_report": "Use ONLY when the user provides a specific URL to read from.",
142
  "quick_respond": "Use as a fallback for simple greetings, acknowledgements, or if the answer is obvious from the immediate context and requires no special tools."
143
  }
144
-
145
- # 2. Build the list of available tools for this specific run
146
  available_tool_names = ["quick_respond", "answer_using_conversation_memory"]
147
  if WEB_SEARCH_ENABLED:
148
- available_tool_names.insert(1, "search_duckduckgo_and_report") # Give search higher priority
149
  available_tool_names.insert(2, "scrape_url_and_report")
150
-
151
- # 3. Create the prompt with the dynamic list of tools and their descriptions
152
  tool_descriptions_for_prompt = "\n".join(f'- "{name}": {tool_definitions[name]}' for name in available_tool_names)
153
-
154
  tool_sys_prompt = "You are a precise routing agent. Your job is to analyze the user's query and the conversation context, then select the single best action to provide an answer. Output ONLY a single, valid JSON object with 'action' and 'action_input' keys. Do not add any other text or explanations."
155
  history_snippet = "\n".join([f"{msg['role']}: {msg['content'][:100]}" for msg in chat_history_for_prompt[-2:]])
156
  guideline_snippet = initial_insights_ctx_str[:200].replace('\n', ' ')
157
-
158
  tool_user_prompt = f"""User Query: "{user_input}"
159
 
160
  Recent History:
161
  {history_snippet}
162
 
163
- Guidelines Snippet (for context):
164
- {guideline_snippet}
165
-
166
  Available Actions and their descriptions:
167
  {tool_descriptions_for_prompt}
168
 
169
- Based on the query and the action descriptions, select the single best action to take. Output the corresponding JSON object.
170
  Example for web search: {{"action": "search_duckduckgo_and_report", "action_input": {{"search_engine_query": "latest AI research"}}}}
171
  Example for memory recall: {{"action": "answer_using_conversation_memory", "action_input": {{}}}}
172
  """
173
-
174
  tool_decision_messages = [{"role":"system", "content": tool_sys_prompt}, {"role":"user", "content": tool_user_prompt}]
175
-
176
  tool_provider, tool_model_id = TOOL_DECISION_PROVIDER_ENV, TOOL_DECISION_MODEL_ID_ENV
177
  tool_model_display = next((dn for dn, mid in MODELS_BY_PROVIDER.get(tool_provider.lower(), {}).get("models", {}).items() if mid == tool_model_id), None)
178
  if not tool_model_display: tool_model_display = get_default_model_display_name_for_provider(tool_provider)
179
-
180
  if tool_model_display:
181
  try:
182
  logger.info(f"PUI_GRADIO [{request_id}]: Tool decision LLM: {tool_provider}/{tool_model_display}")
@@ -191,15 +173,13 @@ Example for memory recall: {{"action": "answer_using_conversation_memory", "acti
191
  logger.info(f"PUI_GRADIO [{request_id}]: LLM Tool Decision: Action='{action_type}', Input='{action_input_dict}'")
192
  else:
193
  logger.warning(f"PUI_GRADIO [{request_id}]: Tool decision LLM non-JSON. Defaulting to quick_respond. Raw: {tool_resp_raw}")
194
- action_type = "quick_respond" # Fallback
195
  except Exception as e:
196
  logger.error(f"PUI_GRADIO [{request_id}]: Tool decision LLM error. Defaulting to quick_respond: {e}", exc_info=False)
197
- action_type = "quick_respond" # Fallback
198
  else:
199
  logger.error(f"No model for tool decision provider {tool_provider}. Defaulting to quick_respond.")
200
- action_type = "quick_respond" # Fallback
201
-
202
- # --- END OF REFACTORED LOGIC ---
203
 
204
  logger.info(f"PUI_GRADIO [{request_id}]: Tool decision logic took {time.time() - time_before_tool_decision:.3f}s. Action: {action_type}, Input: {action_input_dict}")
205
  yield "status", f"<i>[Path: {action_type}. Preparing response...]</i>"
@@ -208,17 +188,46 @@ Example for memory recall: {{"action": "answer_using_conversation_memory", "acti
208
  if action_type == "quick_respond":
209
  final_system_prompt_str += " Respond directly using guidelines & history."
210
  final_user_prompt_content_str = f"History:\n{history_str_for_prompt}\nGuidelines:\n{initial_insights_ctx_str}\nQuery: \"{user_input}\"\nResponse:"
 
211
  elif action_type == "answer_using_conversation_memory":
212
- yield "status", "<i>[Searching conversation memory (semantic)...]</i>"
213
- retrieved_mems = retrieve_memories_semantic(f"User query: {user_input}\nContext:\n{history_str_for_prompt[-1000:]}", k=3)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
214
  if retrieved_mems:
215
  logger.info(f"PUI_GRADIO [{request_id}]: Found {len(retrieved_mems)} relevant memories.")
216
  memory_context = "Relevant Past Interactions (for your context only, do not repeat verbatim):\n" + "\n".join([f"- User asked: '{m.get('user_input','')}'. You responded: '{m.get('bot_response','')}'. (Key takeaway: {m.get('metrics',{}).get('takeaway','N/A')})" for m in retrieved_mems])
217
  else:
218
  logger.info(f"PUI_GRADIO [{request_id}]: No relevant memories found for the query.")
219
  memory_context = "No relevant past interactions were found in the memory database."
 
220
  final_system_prompt_str += " You MUST use the provided 'Memory Context' to inform your answer. Synthesize the information from the memory with the current conversation history to respond to the user's query."
221
  final_user_prompt_content_str = f"History:\n{history_str_for_prompt}\n\nGuidelines:\n{initial_insights_ctx_str}\n\nMemory Context:\n{memory_context}\n\nUser's Query: \"{user_input}\"\n\nResponse (use the Memory Context to answer the query):"
 
 
222
  elif action_type in ["search_duckduckgo_and_report", "scrape_url_and_report"]:
223
  query_or_url = action_input_dict.get("search_engine_query") if "search" in action_type else action_input_dict.get("url")
224
  if not query_or_url:
 
123
  user_input_lower = user_input.lower()
124
  time_before_tool_decision = time.time()
125
 
 
 
 
126
  is_simple_interaction = len(user_input.split()) <= 3 and any(kw in user_input_lower for kw in ["hello", "hi", "thanks", "ok", "bye"]) and not "?" in user_input
127
 
128
  if is_simple_interaction:
129
  action_type = "quick_respond"
130
  else:
 
131
  yield "status", "<i>[LLM choosing best approach...]</i>"
 
 
132
  tool_definitions = {
133
  "answer_using_conversation_memory": "Use if the user's query refers to a previous conversation, asks you to 'remember' or 'recall' something specific, or seems like it could be answered by a past interaction you've had. This tool searches a database of your past conversations.",
134
  "search_duckduckgo_and_report": "Use for general knowledge questions, questions about current events, or when the user explicitly asks you to search the web for information.",
135
  "scrape_url_and_report": "Use ONLY when the user provides a specific URL to read from.",
136
  "quick_respond": "Use as a fallback for simple greetings, acknowledgements, or if the answer is obvious from the immediate context and requires no special tools."
137
  }
 
 
138
  available_tool_names = ["quick_respond", "answer_using_conversation_memory"]
139
  if WEB_SEARCH_ENABLED:
140
+ available_tool_names.insert(1, "search_duckduckgo_and_report")
141
  available_tool_names.insert(2, "scrape_url_and_report")
 
 
142
  tool_descriptions_for_prompt = "\n".join(f'- "{name}": {tool_definitions[name]}' for name in available_tool_names)
 
143
  tool_sys_prompt = "You are a precise routing agent. Your job is to analyze the user's query and the conversation context, then select the single best action to provide an answer. Output ONLY a single, valid JSON object with 'action' and 'action_input' keys. Do not add any other text or explanations."
144
  history_snippet = "\n".join([f"{msg['role']}: {msg['content'][:100]}" for msg in chat_history_for_prompt[-2:]])
145
  guideline_snippet = initial_insights_ctx_str[:200].replace('\n', ' ')
 
146
  tool_user_prompt = f"""User Query: "{user_input}"
147
 
148
  Recent History:
149
  {history_snippet}
150
 
 
 
 
151
  Available Actions and their descriptions:
152
  {tool_descriptions_for_prompt}
153
 
154
+ Based on the query and the action descriptions, select the single best action. Output the corresponding JSON.
155
  Example for web search: {{"action": "search_duckduckgo_and_report", "action_input": {{"search_engine_query": "latest AI research"}}}}
156
  Example for memory recall: {{"action": "answer_using_conversation_memory", "action_input": {{}}}}
157
  """
 
158
  tool_decision_messages = [{"role":"system", "content": tool_sys_prompt}, {"role":"user", "content": tool_user_prompt}]
 
159
  tool_provider, tool_model_id = TOOL_DECISION_PROVIDER_ENV, TOOL_DECISION_MODEL_ID_ENV
160
  tool_model_display = next((dn for dn, mid in MODELS_BY_PROVIDER.get(tool_provider.lower(), {}).get("models", {}).items() if mid == tool_model_id), None)
161
  if not tool_model_display: tool_model_display = get_default_model_display_name_for_provider(tool_provider)
 
162
  if tool_model_display:
163
  try:
164
  logger.info(f"PUI_GRADIO [{request_id}]: Tool decision LLM: {tool_provider}/{tool_model_display}")
 
173
  logger.info(f"PUI_GRADIO [{request_id}]: LLM Tool Decision: Action='{action_type}', Input='{action_input_dict}'")
174
  else:
175
  logger.warning(f"PUI_GRADIO [{request_id}]: Tool decision LLM non-JSON. Defaulting to quick_respond. Raw: {tool_resp_raw}")
176
+ action_type = "quick_respond"
177
  except Exception as e:
178
  logger.error(f"PUI_GRADIO [{request_id}]: Tool decision LLM error. Defaulting to quick_respond: {e}", exc_info=False)
179
+ action_type = "quick_respond"
180
  else:
181
  logger.error(f"No model for tool decision provider {tool_provider}. Defaulting to quick_respond.")
182
+ action_type = "quick_respond"
 
 
183
 
184
  logger.info(f"PUI_GRADIO [{request_id}]: Tool decision logic took {time.time() - time_before_tool_decision:.3f}s. Action: {action_type}, Input: {action_input_dict}")
185
  yield "status", f"<i>[Path: {action_type}. Preparing response...]</i>"
 
188
  if action_type == "quick_respond":
189
  final_system_prompt_str += " Respond directly using guidelines & history."
190
  final_user_prompt_content_str = f"History:\n{history_str_for_prompt}\nGuidelines:\n{initial_insights_ctx_str}\nQuery: \"{user_input}\"\nResponse:"
191
+
192
  elif action_type == "answer_using_conversation_memory":
193
+ # --- MODIFIED: Query Transformation Step ---
194
+ yield "status", "<i>[Optimizing query for long-term memory search...]</i>"
195
+
196
+ # 1. Ask an LLM to generate a better search query
197
+ optimized_query = user_input # Fallback
198
+ try:
199
+ query_gen_system_prompt = "You are an expert at reformulating a user's question into a concise, effective search query for a vector database. Given the conversation history and the user's latest query, extract the key topics and entities to create a self-contained query. The query should be a short phrase or question. Output ONLY the query text, nothing else."
200
+ query_gen_user_prompt = f"Conversation History:\n{history_str_for_prompt}\n\nUser's Latest Query: \"{user_input}\"\n\nOptimized Search Query:"
201
+ query_gen_messages = [{"role":"system", "content":query_gen_system_prompt}, {"role":"user", "content":query_gen_user_prompt}]
202
+
203
+ # Use the same fast model as the tool decider
204
+ query_gen_chunks = list(call_model_stream(provider=tool_provider, model_display_name=tool_model_display, messages=query_gen_messages, temperature=0.0, max_tokens=50))
205
+ generated_query = "".join(query_gen_chunks).strip()
206
+
207
+ if generated_query:
208
+ optimized_query = generated_query.replace('"', '') # Clean up quotes
209
+ logger.info(f"PUI_GRADIO [{request_id}]: Original query: '{user_input}'. Optimized memory search query: '{optimized_query}'")
210
+ else:
211
+ logger.warning(f"PUI_GRADIO [{request_id}]: Query generation returned empty. Using original input.")
212
+ except Exception as e:
213
+ logger.error(f"PUI_GRADIO [{request_id}]: Error during query generation: {e}. Using original input.")
214
+
215
+ # 2. Use the optimized query to search the unified memory
216
+ yield "status", f"<i>[Searching all memories with query: '{optimized_query[:40]}...']</i>"
217
+ retrieved_mems = retrieve_memories_semantic(optimized_query, k=3)
218
+
219
+ # 3. Build the context for the final response
220
  if retrieved_mems:
221
  logger.info(f"PUI_GRADIO [{request_id}]: Found {len(retrieved_mems)} relevant memories.")
222
  memory_context = "Relevant Past Interactions (for your context only, do not repeat verbatim):\n" + "\n".join([f"- User asked: '{m.get('user_input','')}'. You responded: '{m.get('bot_response','')}'. (Key takeaway: {m.get('metrics',{}).get('takeaway','N/A')})" for m in retrieved_mems])
223
  else:
224
  logger.info(f"PUI_GRADIO [{request_id}]: No relevant memories found for the query.")
225
  memory_context = "No relevant past interactions were found in the memory database."
226
+
227
  final_system_prompt_str += " You MUST use the provided 'Memory Context' to inform your answer. Synthesize the information from the memory with the current conversation history to respond to the user's query."
228
  final_user_prompt_content_str = f"History:\n{history_str_for_prompt}\n\nGuidelines:\n{initial_insights_ctx_str}\n\nMemory Context:\n{memory_context}\n\nUser's Query: \"{user_input}\"\n\nResponse (use the Memory Context to answer the query):"
229
+ # --- END OF MODIFICATION ---
230
+
231
  elif action_type in ["search_duckduckgo_and_report", "scrape_url_and_report"]:
232
  query_or_url = action_input_dict.get("search_engine_query") if "search" in action_type else action_input_dict.get("url")
233
  if not query_or_url: