Update .gitignore to exclude additional cache files; enhance APIManager message handling and tool response parsing
This commit is contained in:
2
.gitignore
vendored
2
.gitignore
vendored
@@ -33,3 +33,5 @@ discord_glhf/__pycache__/bot.cpython-313.pyc
|
||||
discord_glhf/handlers/__pycache__/event_handler.cpython-313.pyc
|
||||
discord_glhf/web/__pycache__/app.cpython-313.pyc
|
||||
discord_glhf/handlers/__pycache__/function_tools.cpython-313.pyc
|
||||
discord_glhf/__pycache__/api.cpython-313.pyc
|
||||
discord_glhf/handlers/__pycache__/tool_handler.cpython-313.pyc
|
||||
|
||||
@@ -258,16 +258,96 @@ class APIManager:
|
||||
# Import tools and tool response parser
|
||||
from .handlers.function_tools import TOOLS, parse_tool_response
|
||||
|
||||
# Prepare request data with OpenAI function calling format
|
||||
# Ensure first message is a user message
|
||||
first_user_message = False
|
||||
for msg in messages:
|
||||
if msg.get("role") == "user":
|
||||
first_user_message = True
|
||||
break
|
||||
|
||||
if not first_user_message:
|
||||
logger.error("No user message found in conversation")
|
||||
return False, None
|
||||
|
||||
# Add our system message after first user message to prevent template error
|
||||
system_message = {
|
||||
"role": "system",
|
||||
"content": """You are DEATHKILLER THE MEGA ASSASSIN! You live on the planet Hell and constantly threaten to kill people. You are full of anger and hate everyone.
|
||||
|
||||
Remember:
|
||||
- Stay aggressive and violent in your responses
|
||||
- Be direct and confrontational
|
||||
- Show your murderous rage in every interaction
|
||||
- Never break character"""
|
||||
}
|
||||
|
||||
# First, organize messages
|
||||
system_messages = []
|
||||
user_messages = []
|
||||
assistant_messages = []
|
||||
|
||||
for msg in messages:
|
||||
role = msg.get("role", "")
|
||||
if role == "system":
|
||||
system_messages.append(msg)
|
||||
elif role == "user":
|
||||
user_messages.append(msg)
|
||||
elif role == "assistant":
|
||||
assistant_messages.append(msg)
|
||||
|
||||
# Ensure we have at least one user message
|
||||
if not user_messages:
|
||||
logger.error("No user messages found in conversation")
|
||||
return False, None
|
||||
|
||||
# Build conversation in correct order
|
||||
messages_with_system = []
|
||||
|
||||
# 1. Start with system messages
|
||||
messages_with_system.extend(system_messages)
|
||||
messages_with_system.append(system_message) # Add our character system message
|
||||
|
||||
# 2. Add first user message
|
||||
messages_with_system.append(user_messages[0])
|
||||
|
||||
# 3. Add remaining messages in alternating order
|
||||
for i in range(len(assistant_messages)):
|
||||
if i < len(assistant_messages):
|
||||
messages_with_system.append(assistant_messages[i])
|
||||
if i + 1 < len(user_messages):
|
||||
messages_with_system.append(user_messages[i + 1])
|
||||
|
||||
# Prepare request data
|
||||
data = {
|
||||
"model": params["model"],
|
||||
"messages": messages,
|
||||
"messages": messages_with_system,
|
||||
"temperature": params["temperature"],
|
||||
"max_tokens": params["max_tokens"],
|
||||
"stream": False, # Disable streaming for all requests
|
||||
"tools": TOOLS, # Add function definitions
|
||||
"tool_choice": "auto" # Let model decide when to use tools
|
||||
"stream": False
|
||||
}
|
||||
|
||||
# Only add tools after first user message if we have one
|
||||
if user_messages:
|
||||
logger.debug("Adding function calling capabilities")
|
||||
data["tools"] = TOOLS
|
||||
|
||||
# Log detailed request information
|
||||
logger.debug("=== REQUEST DEBUG INFO ===")
|
||||
logger.debug("Message sequence:")
|
||||
for i, msg in enumerate(messages_with_system):
|
||||
logger.debug(f"{i}: {msg.get('role')} - {msg.get('content')[:50]}...")
|
||||
|
||||
logger.debug("\nMessage roles sequence:")
|
||||
logger.debug([msg.get("role") for msg in messages_with_system])
|
||||
|
||||
logger.debug("\nTools configuration:")
|
||||
logger.debug(json.dumps(TOOLS, indent=2))
|
||||
|
||||
logger.debug("\nFinal request data:")
|
||||
debug_data = data.copy()
|
||||
debug_data["messages"] = [{"role": m.get("role"), "content_length": len(m.get("content", ""))} for m in data["messages"]]
|
||||
logger.debug(json.dumps(debug_data, indent=2))
|
||||
logger.debug("========================")
|
||||
logger.debug(
|
||||
"API Request Details:\n"
|
||||
"URL: %s\n"
|
||||
@@ -335,22 +415,31 @@ class APIManager:
|
||||
|
||||
message = choice["message"]
|
||||
|
||||
# Check for tool calls first
|
||||
if message.get("tool_calls"):
|
||||
# Log full message for debugging
|
||||
logger.debug(f"Message content: {message.get('content')}")
|
||||
logger.debug(f"Message tool_calls: {message.get('tool_calls')}")
|
||||
logger.debug(f"Message function_call: {message.get('function_call')}")
|
||||
|
||||
# Check for tool/function calls first
|
||||
if message.get("tool_calls") or message.get("function_call"):
|
||||
logger.info("Function calling detected in response")
|
||||
tool_data = parse_tool_response(json_response)
|
||||
if tool_data:
|
||||
logger.debug(f"Found tool calls: {tool_data}")
|
||||
logger.info(f"Successfully parsed tool calls: {tool_data}")
|
||||
return True, json.dumps(tool_data)
|
||||
else:
|
||||
logger.error("Failed to parse tool calls")
|
||||
return False, None
|
||||
|
||||
# Fall back to regular content if present
|
||||
# Fall back to regular content
|
||||
content = message.get("content", "").strip()
|
||||
if content:
|
||||
logger.debug(f"Found content: {content[:100]}")
|
||||
logger.debug(f"Using regular content: {content[:100]}")
|
||||
return True, content
|
||||
|
||||
# No valid content or tool calls
|
||||
logger.error("No valid content or tool calls found")
|
||||
logger.debug(f"Full message: {message}")
|
||||
# No valid output found
|
||||
logger.error("No valid content or tool calls in response")
|
||||
logger.debug(f"Full response: {json_response}")
|
||||
return False, None
|
||||
except json.JSONDecodeError as e:
|
||||
logger.error(f"Failed to decode response: {e}")
|
||||
|
||||
@@ -1,19 +1,21 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Function definitions and tool schemas for LLM function calling."""
|
||||
|
||||
from typing import Dict, Any
|
||||
import json
|
||||
from typing import Dict, Any, Optional, List
|
||||
|
||||
TOOLS = [
|
||||
{
|
||||
"type": "function",
|
||||
"function": {
|
||||
"name": "mention_user",
|
||||
"description": "Mention a Discord user in the response",
|
||||
"description": "Mention a Discord user to get their attention",
|
||||
"parameters": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string",
|
||||
"description": "Username or nickname to mention"
|
||||
"description": "The username to mention (without @ symbol)"
|
||||
}
|
||||
},
|
||||
"required": ["name"]
|
||||
@@ -24,13 +26,13 @@ TOOLS = [
|
||||
"type": "function",
|
||||
"function": {
|
||||
"name": "add_reaction",
|
||||
"description": "Add an emoji reaction to the message",
|
||||
"description": "Add an emoji reaction to the last message",
|
||||
"parameters": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"emoji": {
|
||||
"type": "string",
|
||||
"description": "The emoji to add (Unicode emoji, custom emoji <:name:id>, or standard emoji :name:)"
|
||||
"description": "The emoji to add as a reaction (Unicode emoji or Discord emoji name)"
|
||||
}
|
||||
},
|
||||
"required": ["emoji"]
|
||||
@@ -47,16 +49,11 @@ TOOLS = [
|
||||
"properties": {
|
||||
"title": {
|
||||
"type": "string",
|
||||
"description": "Title of the embed"
|
||||
"description": "The title to show at the top of the embed"
|
||||
},
|
||||
"description": {
|
||||
"type": "string",
|
||||
"description": "Content of the embed"
|
||||
},
|
||||
"color": {
|
||||
"type": "integer",
|
||||
"description": "Color of the embed in hex format (e.g., 0xFF0000 for red)",
|
||||
"default": 0xFF0000
|
||||
"description": "The main content body of the embed"
|
||||
}
|
||||
},
|
||||
"required": ["title", "description"]
|
||||
@@ -67,17 +64,13 @@ TOOLS = [
|
||||
"type": "function",
|
||||
"function": {
|
||||
"name": "create_thread",
|
||||
"description": "Create a new discussion thread from the message",
|
||||
"description": "Start a new discussion thread",
|
||||
"parameters": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string",
|
||||
"description": "Name/topic for the thread. Common patterns: 'X vs Y', '[topic] is overrated/underrated', or topics about safety/maintenance/review"
|
||||
},
|
||||
"message_id": {
|
||||
"type": "integer",
|
||||
"description": "Optional ID of message to create thread from"
|
||||
"description": "The topic/title for the new thread"
|
||||
}
|
||||
},
|
||||
"required": ["name"]
|
||||
@@ -86,34 +79,31 @@ TOOLS = [
|
||||
}
|
||||
]
|
||||
|
||||
def function_to_tool_call(function_name: str, arguments: Dict[str, Any]) -> Dict[str, Any]:
|
||||
"""Convert a function call response to a tool call format."""
|
||||
return {
|
||||
"name": function_name,
|
||||
"arguments": arguments
|
||||
}
|
||||
|
||||
def parse_tool_response(response: Dict[str, Any]) -> Dict[str, Any]:
|
||||
def parse_tool_response(response: Dict[str, Any]) -> Optional[Dict[str, Any]]:
|
||||
"""Parse an API response looking for function calls in tool_calls format."""
|
||||
if not response or "choices" not in response:
|
||||
return {}
|
||||
|
||||
choice = response["choices"][0]
|
||||
if "message" not in choice:
|
||||
return {}
|
||||
|
||||
message = choice["message"]
|
||||
if "tool_calls" not in message:
|
||||
return {}
|
||||
|
||||
tool_calls = []
|
||||
for tool_call in message["tool_calls"]:
|
||||
if tool_call["type"] == "function":
|
||||
# Extract function name and arguments
|
||||
function_call = {
|
||||
"name": tool_call["function"]["name"],
|
||||
"arguments": json.loads(tool_call["function"]["arguments"])
|
||||
}
|
||||
tool_calls.append(function_call)
|
||||
|
||||
return {"tool_calls": tool_calls} if tool_calls else {}
|
||||
try:
|
||||
if not response or "choices" not in response:
|
||||
return None
|
||||
|
||||
choice = response["choices"][0]
|
||||
if "message" not in choice:
|
||||
return None
|
||||
|
||||
message = choice["message"]
|
||||
if "tool_calls" not in message:
|
||||
return None
|
||||
|
||||
tool_calls = []
|
||||
for tool_call in message["tool_calls"]:
|
||||
if tool_call["type"] == "function":
|
||||
function = tool_call["function"]
|
||||
tool_calls.append({
|
||||
"name": function["name"],
|
||||
"arguments": json.loads(function["arguments"])
|
||||
})
|
||||
|
||||
return {"tool_calls": tool_calls} if tool_calls else None
|
||||
|
||||
except Exception as e:
|
||||
print(f"Error parsing tool response: {e}")
|
||||
return None
|
||||
Reference in New Issue
Block a user