Add tool markers configuration and update usage guidelines for explicit markers
This commit is contained in:
@@ -223,6 +223,45 @@ class ToolHandler:
|
||||
except Exception as e:
|
||||
logger.error(f"Tool Use - Create Thread: Failed to create thread: {e}")
|
||||
|
||||
def validate_tool_response(self, response: str) -> bool:
|
||||
"""Validate proper tool marker closure.
|
||||
|
||||
Args:
|
||||
response: The response string to validate
|
||||
|
||||
Returns:
|
||||
bool: True if all tool markers are properly closed, False otherwise
|
||||
"""
|
||||
from .tools import TOOL_MARKERS
|
||||
try:
|
||||
stack = []
|
||||
|
||||
# Find all tool markers
|
||||
pattern = f"{re.escape(TOOL_MARKERS['start'])}\\w+\\]|{re.escape(TOOL_MARKERS['end'])}"
|
||||
markers = re.finditer(pattern, response)
|
||||
|
||||
for match in markers:
|
||||
marker = match.group(0)
|
||||
if TOOL_MARKERS['end'] in marker:
|
||||
if not stack:
|
||||
logger.warning(f"Tool Use - Validate: Found end marker without matching start: {marker}")
|
||||
return False
|
||||
start_marker = stack.pop()
|
||||
logger.debug(f"Tool Use - Validate: Matched {start_marker} with {marker}")
|
||||
else:
|
||||
stack.append(marker)
|
||||
logger.debug(f"Tool Use - Validate: Found start marker: {marker}")
|
||||
|
||||
if stack:
|
||||
logger.warning(f"Tool Use - Validate: Unclosed tool markers found: {stack}")
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Tool Use - Validate: Error validating tool markers: {e}")
|
||||
return False
|
||||
|
||||
def parse_tool_calls(
|
||||
self,
|
||||
response: str,
|
||||
@@ -236,101 +275,87 @@ class ToolHandler:
|
||||
message_id: Optional ID of the message being responded to
|
||||
channel_id: Optional ID of the channel the message is in
|
||||
"""
|
||||
from .tools import TOOL_MARKERS
|
||||
|
||||
try:
|
||||
# Validate tool markers first
|
||||
if not self.validate_tool_response(response):
|
||||
logger.warning("Tool Use - Parse: Invalid tool marker structure, skipping tool parsing")
|
||||
return [], response, {}
|
||||
|
||||
tool_calls = []
|
||||
lines = response.split("\n")
|
||||
response_lines = []
|
||||
i = 0
|
||||
final_response = response
|
||||
|
||||
while i < len(lines):
|
||||
line = lines[i].strip()
|
||||
if not line:
|
||||
i += 1
|
||||
continue
|
||||
|
||||
# Convert line to lowercase for command checking
|
||||
line_lower = line.lower()
|
||||
command_found = False
|
||||
|
||||
# Process tools based on natural language patterns
|
||||
# Process tool markers in order of appearance
|
||||
for tool_type, markers in TOOL_MARKERS["patterns"].items():
|
||||
pattern = f"{re.escape(markers['start'])}(.*?){re.escape(markers['end'])}"
|
||||
matches = list(re.finditer(pattern, response, re.DOTALL))
|
||||
|
||||
# Simple pattern matching for basic tool usage
|
||||
# Let the LLM decide most tool usage through natural language
|
||||
if "@" in line: # Basic mention support
|
||||
logger.info(f"Tool Use - Parse: Detected mention pattern in line: '{line}'")
|
||||
for match in re.finditer(r"@(\w+(?:\s+\w+)*)", line):
|
||||
name = match.group(1).strip()
|
||||
logger.info(f"Tool Use - Parse: Adding find_user tool call for '{name}'")
|
||||
tool_calls.append(("find_user", {"name": name}))
|
||||
command_found = True
|
||||
for match in matches:
|
||||
content = match.group(1).strip()
|
||||
if not content:
|
||||
continue
|
||||
|
||||
# Thread creation patterns
|
||||
thread_patterns = [
|
||||
(r'(?i)(.*?\bvs\.?\b.*?(?:debate|discussion)?)', r'\1 debate'), # X vs Y
|
||||
(r'(?i)(.*?\b(?:over|under)rated\b.*?(?:discussion)?)', r'\1 discussion'), # overrated/underrated
|
||||
(r'(?i)(.*?\b(?:safety|maintenance|review)\b.*?(?:discussion|thread)?)', r'\1 discussion') # Specific topics
|
||||
]
|
||||
|
||||
for pattern, name_format in thread_patterns:
|
||||
if re.search(pattern, line_lower):
|
||||
# Extract the topic from the line
|
||||
match = re.search(pattern, line, re.IGNORECASE)
|
||||
if match:
|
||||
thread_name = re.sub(pattern, name_format, match.group(1))
|
||||
logger.info(f"Tool Use - Parse: Adding create_thread tool call for '{thread_name}'")
|
||||
logger.info(f"Tool Use - Parse: Found {tool_type} tool usage: {content[:50]}...")
|
||||
|
||||
# Process based on tool type
|
||||
if tool_type == "mention":
|
||||
# Extract username from @mention
|
||||
if "@" in content:
|
||||
name = re.search(r"@(\w+(?:\s+\w+)*)", content)
|
||||
if name:
|
||||
name = name.group(1).strip()
|
||||
tool_calls.append(("find_user", {"name": name}))
|
||||
logger.info(f"Tool Use - Parse: Adding find_user tool call for '{name}'")
|
||||
|
||||
elif tool_type == "reaction":
|
||||
# Process emoji reactions
|
||||
emoji_pattern = r'([😀-🙏🌀-🗿]|<a?:[a-zA-Z0-9_]+:\d+>|:[a-zA-Z0-9_]+:)'
|
||||
emoji_matches = re.finditer(emoji_pattern, content)
|
||||
for emoji_match in emoji_matches:
|
||||
emoji = emoji_match.group(1)
|
||||
if emoji.strip():
|
||||
tool_calls.append(("add_reaction", {
|
||||
"emoji": emoji,
|
||||
"message_id": message_id,
|
||||
"channel_id": channel_id
|
||||
}))
|
||||
logger.info(f"Tool Use - Parse: Adding add_reaction tool call for emoji '{emoji}'")
|
||||
|
||||
elif tool_type == "embed":
|
||||
# Process embed content
|
||||
lines = content.strip().split("\n")
|
||||
if lines:
|
||||
title = lines[0]
|
||||
description = "\n".join(lines[1:]) if len(lines) > 1 else ""
|
||||
tool_calls.append(("create_embed", {
|
||||
"title": title,
|
||||
"description": description,
|
||||
"color": 0xFF0000 # Default red color
|
||||
}))
|
||||
logger.info(f"Tool Use - Parse: Adding create_embed tool call with title '{title}'")
|
||||
|
||||
elif tool_type == "thread":
|
||||
# Process thread creation
|
||||
thread_name = content.strip()
|
||||
if thread_name:
|
||||
tool_calls.append(("create_thread", {
|
||||
"channel_id": channel_id,
|
||||
"name": thread_name,
|
||||
"message_id": message_id
|
||||
}))
|
||||
command_found = True
|
||||
break
|
||||
logger.info(f"Tool Use - Parse: Adding create_thread tool call for '{thread_name}'")
|
||||
|
||||
# Support emoji reactions (both explicit and from text)
|
||||
# 1. Match Unicode emojis, custom emojis, and standard Discord emojis
|
||||
emoji_pattern = r'([😀-🙏🌀-🗿]|<a?:[a-zA-Z0-9_]+:\d+>|:[a-zA-Z0-9_]+:)'
|
||||
emoji_matches = re.finditer(emoji_pattern, line)
|
||||
for match in emoji_matches:
|
||||
emoji = match.group(1)
|
||||
if emoji.strip():
|
||||
logger.info(f"Tool Use - Parse: Adding add_reaction tool call for emoji '{emoji}'")
|
||||
tool_calls.append(("add_reaction", {
|
||||
"emoji": emoji,
|
||||
"message_id": message_id,
|
||||
"channel_id": channel_id
|
||||
}))
|
||||
command_found = True
|
||||
# Remove the tool marker and its content from the final response
|
||||
final_response = final_response.replace(match.group(0), "")
|
||||
|
||||
# 2. Also detect emoticons and convert them to emojis
|
||||
emoticon_map = {
|
||||
r'(?:^|\s)[:;]-?[)D](?:\s|$)': '😊', # :) ;) :-) :D
|
||||
r'(?:^|\s)[:;]-?[(\[](?:\s|$)': '😢', # :( ;( :-( :[
|
||||
r'(?:^|\s)[:;]-?[pP](?:\s|$)': '😛', # :p ;p :-p
|
||||
r'(?:^|\s)[:;]-?[oO](?:\s|$)': '😮', # :o ;o :-o
|
||||
r'(?:^|\s)[xX][dD](?:\s|$)': '😂', # xD XD
|
||||
}
|
||||
for pattern, emoji in emoticon_map.items():
|
||||
if re.search(pattern, line):
|
||||
logger.info(f"Tool Use - Parse: Converting emoticon to emoji reaction '{emoji}'")
|
||||
tool_calls.append(("add_reaction", {
|
||||
"emoji": emoji,
|
||||
"message_id": message_id,
|
||||
"channel_id": channel_id
|
||||
}))
|
||||
command_found = True
|
||||
# Clean up the final response
|
||||
final_response = "\n".join(line for line in final_response.split("\n") if line.strip())
|
||||
|
||||
# Always include the line in the response, regardless of commands
|
||||
response_lines.append(line)
|
||||
|
||||
i += 1
|
||||
|
||||
# Join response lines, removing empty lines at start/end
|
||||
final_response = "\n".join(response_lines).strip()
|
||||
|
||||
# Log summary of detected tools
|
||||
if tool_calls:
|
||||
logger.info(f"Tool Use - Parse: Detected {len(tool_calls)} tool calls: {[call[0] for call in tool_calls]}")
|
||||
|
||||
|
||||
return tool_calls, final_response, self.mentioned_users
|
||||
|
||||
except Exception as e:
|
||||
|
||||
@@ -1,5 +1,29 @@
|
||||
"""Tools available for LLM use and their documentation."""
|
||||
|
||||
# Tool Markers Configuration
|
||||
TOOL_MARKERS = {
|
||||
"start": "[TOOL:",
|
||||
"end": "[/TOOL]",
|
||||
"patterns": {
|
||||
"mention": {
|
||||
"start": "[TOOL:mention]",
|
||||
"end": "[/TOOL]"
|
||||
},
|
||||
"reaction": {
|
||||
"start": "[TOOL:reaction]",
|
||||
"end": "[/TOOL]"
|
||||
},
|
||||
"embed": {
|
||||
"start": "[TOOL:embed]",
|
||||
"end": "[/TOOL]"
|
||||
},
|
||||
"thread": {
|
||||
"start": "[TOOL:thread]",
|
||||
"end": "[/TOOL]"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# Tool Definitions
|
||||
TOOLS = {
|
||||
"mention_user": {
|
||||
@@ -7,8 +31,8 @@ TOOLS = {
|
||||
"parameters": {
|
||||
"name": "The username or nickname to mention",
|
||||
},
|
||||
"example": "@username",
|
||||
"usage": "Simply include @ before the username in your response: @john"
|
||||
"example": "[TOOL:mention]@username[/TOOL]",
|
||||
"usage": "Wrap mentions with [TOOL:mention] markers: [TOOL:mention]@john[/TOOL]"
|
||||
},
|
||||
|
||||
"add_reaction": {
|
||||
@@ -16,8 +40,8 @@ TOOLS = {
|
||||
"parameters": {
|
||||
"emoji": "The emoji to add as a reaction. Can be Unicode emoji, custom emoji (<:name:id>), or standard emoji (:name:)"
|
||||
},
|
||||
"example": "😊 or :smile: or <:custom:123456789>",
|
||||
"usage": "Include the emoji naturally in your response text"
|
||||
"example": "[TOOL:reaction]😊[/TOOL] or [TOOL:reaction]:smile:[/TOOL]",
|
||||
"usage": "Wrap emojis with [TOOL:reaction] markers"
|
||||
},
|
||||
|
||||
"create_embed": {
|
||||
@@ -28,13 +52,13 @@ TOOLS = {
|
||||
"color": "Optional hex color code (defaults to red)"
|
||||
},
|
||||
"example": """
|
||||
[Embed]
|
||||
[TOOL:embed]
|
||||
Title Goes Here
|
||||
This is the description content
|
||||
It can span multiple lines
|
||||
[/Embed]
|
||||
[/TOOL]
|
||||
""",
|
||||
"usage": "Wrap embed content in [Embed] tags"
|
||||
"usage": "Wrap embed content with [TOOL:embed] markers"
|
||||
},
|
||||
|
||||
"create_thread": {
|
||||
@@ -43,34 +67,36 @@ TOOLS = {
|
||||
"name": "The name/topic for the thread",
|
||||
"message_id": "Optional ID of message to create thread from"
|
||||
},
|
||||
"example": "Let's discuss X vs Y or This coaster is overrated",
|
||||
"usage": "Use natural language patterns like 'X vs Y' or include keywords like 'overrated', 'safety', 'maintenance', 'review'"
|
||||
"example": "[TOOL:thread]Let's discuss X vs Y[/TOOL] or [TOOL:thread]This coaster is overrated[/TOOL]",
|
||||
"usage": "Wrap thread topics with [TOOL:thread] markers"
|
||||
}
|
||||
}
|
||||
|
||||
# Tool Usage Guidelines
|
||||
USAGE_GUIDELINES = """
|
||||
Tools can be used naturally in conversation:
|
||||
Tools must be used with explicit markers in conversation:
|
||||
|
||||
1. To mention a user: Just include @ before their name
|
||||
Example: "Hey @john, what do you think?"
|
||||
1. To mention a user:
|
||||
Example: "Hey [TOOL:mention]@john[/TOOL], what do you think?"
|
||||
|
||||
2. To add reactions: Simply include emojis in your response
|
||||
Example: "That's awesome! 👍"
|
||||
2. To add reactions:
|
||||
Example: "That's a great point! [TOOL:reaction]👍[/TOOL]"
|
||||
|
||||
3. To create embeds: Use [Embed] tags
|
||||
Example:
|
||||
[Embed]
|
||||
Poll Results
|
||||
First place: X
|
||||
Second place: Y
|
||||
[/Embed]
|
||||
3. To create embeds:
|
||||
Example:
|
||||
[TOOL:embed]
|
||||
Poll Results
|
||||
First place: X
|
||||
Second place: Y
|
||||
[/TOOL]
|
||||
|
||||
4. To create threads: Use natural discussion patterns
|
||||
Examples:
|
||||
- "Let's compare X vs Y"
|
||||
- "Is this coaster overrated?"
|
||||
- "Time for a safety discussion"
|
||||
4. To create threads:
|
||||
Examples:
|
||||
- [TOOL:thread]Let's compare X vs Y[/TOOL]
|
||||
- [TOOL:thread]Is this coaster overrated?[/TOOL]
|
||||
- [TOOL:thread]Time for a safety discussion[/TOOL]
|
||||
|
||||
Note: Always ensure tool markers are properly closed with their corresponding end tags.
|
||||
"""
|
||||
|
||||
# Error Messages
|
||||
|
||||
Reference in New Issue
Block a user