Riley Claude commited on
Commit
ff0b7db
·
1 Parent(s): 9e7adc5

fix: Improve UX - text comparisons, remove preset bloat, fix tool message errors

Browse files

- Convert grant comparison from markdown table to cleaner text-based format
- Remove past winners preset questions (not needed)
- Fix tool message error by implementing safe message trimming that preserves tool_call/tool pairs
- Add system prompt instruction to eliminate verbose "I'm going to..." announcements
- Assistant now just calls tools and presents results directly

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>

src/analyzer/chat/chat_tools.py CHANGED
@@ -550,9 +550,14 @@ class ChatTools:
550
  ),
551
  ]
552
 
553
- table = ["### Side-by-side", "| Field | A | B |", "|---|---|---|"]
 
554
  for name, va, vb in fields:
555
- table.append(f"| {name} | {va} | {vb} |")
 
 
 
 
556
 
557
  # ---------------- optional insight ----------------
558
  context_text = ""
@@ -565,10 +570,10 @@ class ChatTools:
565
  if self.client and self.client.is_ready() and context_text.strip():
566
  try:
567
  prompt = (
568
- "Given the factual table and context below, write 3-5 bullet points "
569
  "highlighting *meaningful differences* that matter to SMEs (funding size, "
570
  "duration, eligibility, etc.). Do not restate identical facts.\n\n"
571
- + "\n".join(table)
572
  + "\n\n"
573
  + context_text
574
  )
@@ -578,8 +583,7 @@ class ChatTools:
578
 
579
  md = [
580
  "### Comparison",
581
- f"**{getf(A, 'title')}** _(A)_ vs **{getf(B, 'title')}** _(B)_",
582
- "\n".join(table),
583
  ]
584
  if insight:
585
  md += ["\n### Key differences", insight]
 
550
  ),
551
  ]
552
 
553
+ # Build text-based comparison instead of table
554
+ comparison_lines = ["### Grant A: " + getf(A, "title")]
555
  for name, va, vb in fields:
556
+ comparison_lines.append(f"**{name}:** {va}")
557
+
558
+ comparison_lines.append("\n### Grant B: " + getf(B, "title"))
559
+ for name, va, vb in fields:
560
+ comparison_lines.append(f"**{name}:** {vb}")
561
 
562
  # ---------------- optional insight ----------------
563
  context_text = ""
 
570
  if self.client and self.client.is_ready() and context_text.strip():
571
  try:
572
  prompt = (
573
+ "Given the factual comparison and context below, write 3-5 bullet points "
574
  "highlighting *meaningful differences* that matter to SMEs (funding size, "
575
  "duration, eligibility, etc.). Do not restate identical facts.\n\n"
576
+ + "\n".join(comparison_lines)
577
  + "\n\n"
578
  + context_text
579
  )
 
583
 
584
  md = [
585
  "### Comparison",
586
+ "\n".join(comparison_lines),
 
587
  ]
588
  if insight:
589
  md += ["\n### Key differences", insight]
src/analyzer/chat/demo_app.py CHANGED
@@ -42,8 +42,6 @@ PRESET_QUESTIONS = {
42
  "Compare two grants": "Compare competition-2313 and competition-2314",
43
  "Grant details": "Tell me about competition-2317 in detail",
44
  "SME funding options": "What grants are available for SMEs with funding over £100k?",
45
- "Past winners of open grants": "Of the grants that are currently open, are there any past winners listed as reference?",
46
- "Past AI winners": "Show me past winners related to AI projects",
47
  }
48
 
49
 
@@ -169,7 +167,9 @@ class GrantAnalystDemo:
169
  "- Avoid fluff, filler, or over-formality\n"
170
  "- Skip headings, bullet points, or 'as an AI' disclaimers\n"
171
  "- Prefer short, active sentences\n"
172
- "- Be bold, human, and efficient\n\n"
 
 
173
  ),
174
  }
175
  ]
@@ -394,6 +394,53 @@ class GrantAnalystDemo:
394
  logging.error(f"Tool {tool_name} failed: {e}", exc_info=True)
395
  return {"error": str(e)}
396
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
397
  def chat(self, user_message: str, history: List) -> Tuple[str, List]:
398
  """
399
  Process a chat message.
@@ -422,7 +469,8 @@ class GrantAnalystDemo:
422
  llm_start = time.time()
423
 
424
  # Limit conversation history (keep system + last 10 messages)
425
- messages_to_send = self.messages[:1] + self.messages[-10:]
 
426
  if len(self.messages) > len(messages_to_send):
427
  logger.debug(
428
  f"Trimmed conversation from {len(self.messages)} to {len(messages_to_send)} messages"
@@ -491,7 +539,8 @@ class GrantAnalystDemo:
491
  final_start = time.time()
492
 
493
  # Limit conversation history to prevent bloat (keep system + last 10 messages)
494
- messages_to_send = self.messages[:1] + self.messages[-10:]
 
495
  if len(self.messages) > len(messages_to_send):
496
  logger.debug(
497
  f"Trimmed conversation from {len(self.messages)} to {len(messages_to_send)} messages"
 
42
  "Compare two grants": "Compare competition-2313 and competition-2314",
43
  "Grant details": "Tell me about competition-2317 in detail",
44
  "SME funding options": "What grants are available for SMEs with funding over £100k?",
 
 
45
  }
46
 
47
 
 
167
  "- Avoid fluff, filler, or over-formality\n"
168
  "- Skip headings, bullet points, or 'as an AI' disclaimers\n"
169
  "- Prefer short, active sentences\n"
170
+ "- Be bold, human, and efficient\n"
171
+ "- NEVER announce what you're going to do - just do it and present the results\n"
172
+ "- NO phrases like 'I'll search for', 'Let me find', 'Calling the tool' - just call it\n\n"
173
  ),
174
  }
175
  ]
 
394
  logging.error(f"Tool {tool_name} failed: {e}", exc_info=True)
395
  return {"error": str(e)}
396
 
397
+ def _trim_messages_safely(self, messages: List, max_recent: int = 10) -> List:
398
+ """
399
+ Trim messages to reduce context size while preserving tool_call/tool pairs.
400
+
401
+ Args:
402
+ messages: Full message history
403
+ max_recent: Max number of recent messages to keep (excluding system)
404
+
405
+ Returns:
406
+ Trimmed messages list with system message + recent messages
407
+ """
408
+ if len(messages) <= max_recent + 1: # +1 for system message
409
+ return messages
410
+
411
+ # Always keep system message (index 0)
412
+ system_msg = messages[0] if messages and messages[0].get("role") == "system" else None
413
+
414
+ # Get recent messages
415
+ recent = messages[-(max_recent):]
416
+
417
+ # Check if the first recent message is a tool response without its tool_call
418
+ # If so, find the corresponding assistant message with tool_calls and include it
419
+ if recent and recent[0].get("role") == "tool":
420
+ # Find the preceding assistant message with tool_calls
421
+ tool_call_id = recent[0].get("tool_call_id")
422
+ if tool_call_id:
423
+ # Search backwards from the cutoff point
424
+ cutoff_idx = len(messages) - max_recent
425
+ for i in range(cutoff_idx - 1, 0, -1):
426
+ msg = messages[i]
427
+ if msg.get("role") == "assistant" and hasattr(msg, "tool_calls"):
428
+ # Found the assistant message with tool_calls, include it
429
+ recent = [msg] + recent
430
+ break
431
+ # Check if it's a dict with tool_calls key
432
+ elif msg.get("role") == "assistant" and "tool_calls" in str(msg):
433
+ recent = [msg] + recent
434
+ break
435
+
436
+ # Build final message list
437
+ result = []
438
+ if system_msg:
439
+ result.append(system_msg)
440
+ result.extend(recent)
441
+
442
+ return result
443
+
444
  def chat(self, user_message: str, history: List) -> Tuple[str, List]:
445
  """
446
  Process a chat message.
 
469
  llm_start = time.time()
470
 
471
  # Limit conversation history (keep system + last 10 messages)
472
+ # BUT preserve tool_call/tool pairs to avoid OpenAI API errors
473
+ messages_to_send = self._trim_messages_safely(self.messages, max_recent=10)
474
  if len(self.messages) > len(messages_to_send):
475
  logger.debug(
476
  f"Trimmed conversation from {len(self.messages)} to {len(messages_to_send)} messages"
 
539
  final_start = time.time()
540
 
541
  # Limit conversation history to prevent bloat (keep system + last 10 messages)
542
+ # BUT preserve tool_call/tool pairs to avoid OpenAI API errors
543
+ messages_to_send = self._trim_messages_safely(self.messages, max_recent=10)
544
  if len(self.messages) > len(messages_to_send):
545
  logger.debug(
546
  f"Trimmed conversation from {len(self.messages)} to {len(messages_to_send)} messages"