pratikbhavsar's picture
added sonnet and improved data explorer
cfec1f3
raw
history blame
43.1 kB
import json
def format_user_message(msg):
"""Format a user message for display."""
# Extract the content based on role
content = msg.get("content", "")
# Handle None content
if content is None:
content = ""
elif isinstance(content, (int, float)):
content = str(content)
elif isinstance(content, list):
# Handle list-type content (may contain multiple parts)
content_text = ""
for item in content:
if item is None:
continue
if isinstance(item, dict) and "text" in item:
text_value = item.get("text", "")
if text_value is not None:
content_text += str(text_value) + "\n"
elif isinstance(item, str):
content_text += item + "\n"
elif item is not None:
content_text += str(item) + "\n"
content = content_text.strip()
# User message - align right using text-align instead of flex
return f"""
<div style="
text-align: right;
margin-bottom: 1.25rem;
padding: 0 0.5rem;">
<div style="
display: inline-block;
max-width: 85%;
background-color: var(--message-bg-user);
padding: 1rem;
border-radius: 1rem 0 1rem 1rem;
color: var(--text-color);
text-align: left;
box-shadow: 0 1px 2px var(--shadow-color);">
<div style="
font-weight: 500;
margin-bottom: 0.5rem;
color: var(--primary-text);
display: flex;
align-items: center;">
<span style="margin-right: 0.5rem;">👤</span>User
</div>
<div style="white-space: pre-wrap; line-height: 1.5;">
{content}
</div>
</div>
</div>
"""
def format_tool_call(tool_name, tool_input):
"""Format a tool call for display."""
# Ensure tool_name is a string
if tool_name is None:
tool_name = "Unknown Tool"
elif not isinstance(tool_name, str):
tool_name = str(tool_name)
# Ensure tool_input is serializable
if tool_input is None:
tool_input = {}
try:
# Try to serialize the tool input as JSON
tool_input_json = json.dumps(tool_input, indent=2)
except TypeError:
# If serialization fails, create a simplified representation
if isinstance(tool_input, dict):
simplified_input = {}
for k, v in tool_input.items():
if v is None or isinstance(v, (str, int, float, bool, list, dict)):
simplified_input[k] = v
else:
simplified_input[k] = str(v)
tool_input_json = json.dumps(simplified_input, indent=2)
else:
tool_input_json = str(tool_input)
return f"""
<div style="
background-color: var(--surface-color-alt);
padding: 0.75rem;
border-radius: 0.5rem;
margin-top: 0.75rem;
border-left: 3px solid var(--primary-text-light);">
<div style="
font-weight: 500;
margin-bottom: 0.5rem;
font-size: 0.9rem;
color: var(--primary-text);">
<span style="margin-right: 0.5rem;">🔧</span>{tool_name}
</div>
<div style="
font-family: monospace;
font-size: 0.85rem;
white-space: pre-wrap;">
{tool_input_json}
</div>
</div>
"""
def extract_assistant_content(msg):
"""Extract text content and tool calls from an assistant message."""
assistant_text = ""
tool_calls_html = ""
if "content" in msg:
content = msg["content"]
# Handle string content
if content is None:
assistant_text = ""
elif isinstance(content, str):
assistant_text = content
elif isinstance(content, (int, float)):
assistant_text = str(content)
# Handle list content with text and tool calls
elif isinstance(content, list):
for item in content:
if item is None:
continue
if isinstance(item, dict):
if "text" in item:
text_value = item.get("text", "")
if text_value is not None:
assistant_text += str(text_value) + "\n"
elif "type" in item and item["type"] == "tool_use":
# Format tool call in a nicer way
tool_name = item.get("name", "Unknown Tool")
tool_input = item.get("input", {})
if tool_input is None:
tool_input = {}
tool_calls_html += format_tool_call(tool_name, tool_input)
elif isinstance(item, str):
assistant_text += item + "\n"
elif item is not None:
assistant_text += str(item) + "\n"
# Extract tool calls if present
elif "tool_calls" in msg:
assistant_text = "The assistant used the following tools:"
tool_calls = msg.get("tool_calls", [])
if tool_calls is None:
tool_calls = []
for tool_call in tool_calls:
if tool_call is None:
continue
tool_name = tool_call.get("name", "Unknown Tool")
tool_args = tool_call.get("args", {})
if tool_args is None:
tool_args = {}
tool_calls_html += format_tool_call(tool_name, tool_args)
return assistant_text.strip(), tool_calls_html
def format_assistant_message(msg):
"""Format an assistant message for display."""
assistant_text, tool_calls_html = extract_assistant_content(msg)
return f"""
<div style="
text-align: left;
margin-bottom: 1.25rem;
padding: 0 0.5rem;">
<div style="
display: inline-block;
max-width: 85%;
background-color: var(--message-bg-assistant);
padding: 1rem;
border-radius: 0 1rem 1rem 1rem;
color: var(--text-color);
text-align: left;
box-shadow: 0 1px 2px var(--shadow-color);">
<div style="
font-weight: 500;
margin-bottom: 0.5rem;
color: var(--primary-text);
display: flex;
align-items: center;">
<span style="margin-right: 0.5rem;">🤖</span>Assistant
</div>
<div style="white-space: pre-wrap; line-height: 1.5;">
{assistant_text}
</div>
{tool_calls_html}
</div>
</div>
"""
def format_system_message(msg):
"""Format a system or other message for display."""
content = msg.get("content", "")
# Handle None content
if content is None:
content = ""
elif isinstance(content, (int, float)):
content = str(content)
elif isinstance(content, list):
content_text = ""
for item in content:
if item is None:
continue
if isinstance(item, dict) and "text" in item:
text_value = item.get("text", "")
if text_value is not None:
content_text += str(text_value) + "\n"
elif isinstance(item, str):
content_text += item + "\n"
elif item is not None:
content_text += str(item) + "\n"
content = content_text.strip()
return f"""
<div style="
text-align: center;
margin-bottom: 1rem;
padding: 0 0.5rem;">
<div style="
display: inline-block;
max-width: 85%;
background-color: var(--message-bg-system);
padding: 0.75rem;
border-radius: 0.5rem;
color: var(--text-color);
text-align: left;
font-style: italic;
font-size: 0.9rem;">
{content}
</div>
</div>
"""
def parse_complex_response(response):
"""Parse complex JSON response and extract text and tool calls."""
try:
# Ensure response is a string
if response is None:
return "", ""
if isinstance(response, (int, float)):
return str(response), ""
# Convert to string if it's not already
if not isinstance(response, str):
response = str(response)
# Try to parse as JSON
if not response.strip().startswith("[") and not response.strip().startswith(
"{"
):
return response, ""
response_obj = json.loads(response)
# Handle array format like in the example
if isinstance(response_obj, list) and len(response_obj) > 0:
response_obj = response_obj[0] # Take first item in array
# Extract text content and tool calls
text_content = ""
tool_calls_html = ""
# Handle content field which can be string or list
if "content" in response_obj:
content = response_obj["content"]
if content is None:
text_content = ""
elif isinstance(content, str):
text_content = content
elif isinstance(content, (int, float)):
text_content = str(content)
elif isinstance(content, list):
# Extract only text content from items with type="text"
for item in content:
if item is None:
continue
if isinstance(item, dict):
if "type" in item and item["type"] == "text" and "text" in item:
text_value = item.get("text", "")
if text_value is not None:
text_content += str(text_value) + "\n"
# Get formatted tool calls if they exist
if "tool_calls" in response_obj:
tool_calls = response_obj.get("tool_calls", [])
if tool_calls is None:
tool_calls = []
if tool_calls:
try:
tool_calls_html = f"""
<div style="
background-color: var(--surface-color-alt);
padding: 0.75rem;
border-radius: 0.5rem;
margin-top: 0.75rem;
border-left: 3px solid var(--primary-text-light);">
<div style="
font-weight: 500;
margin-bottom: 0.5rem;
font-size: 0.9rem;
color: var(--primary-text);">
<span style="margin-right: 0.5rem;">🔧</span>Tool Calls
</div>
<div style="
font-family: monospace;
font-size: 0.85rem;
white-space: pre-wrap;">
{json.dumps(tool_calls, indent=2)}
</div>
</div>
"""
except:
# Fallback if JSON serialization fails
tool_calls_html = (
"<div>Tool calls present but could not be formatted.</div>"
)
return text_content.strip(), tool_calls_html
except Exception as e:
# If parsing fails, return the original response with error info
return f"{response}\n\nError parsing response: {str(e)}", ""
def format_final_response(response):
"""Format the final response for display."""
# First try to process as complex JSON with tool calls
text_content, tool_calls_html = parse_complex_response(response)
# If that didn't work, try basic JSON parsing
if text_content == response:
# Clean up JSON response if it looks like JSON
if response.strip().startswith("{") and "content" in response:
try:
response_obj = json.loads(response)
if isinstance(response_obj, dict) and "content" in response_obj:
if isinstance(response_obj["content"], str):
text_content = response_obj["content"]
else:
text_content = json.dumps(response_obj["content"], indent=2)
else:
text_content = response
except:
text_content = response
else:
text_content = response
return f"""
<div style="
text-align: left;
margin-bottom: 1.25rem;
margin-top: 1.5rem;
padding: 0 0.5rem;">
<div style="
display: inline-block;
max-width: 85%;
background-color: var(--response-bg);
padding: 1rem;
border-radius: 0 1rem 1rem 1rem;
color: var(--text-color);
text-align: left;
box-shadow: 0 1px 2px var(--shadow-color);
border-left: 4px solid var(--primary-text);">
<div style="
font-weight: 500;
margin-bottom: 0.5rem;
color: var(--primary-text);
display: flex;
align-items: center;">
<span style="margin-right: 0.5rem;">🤖</span>Final Response
</div>
<div style="
white-space: pre-wrap;
line-height: 1.5;
font-family: var(--font-sans);">
{text_content}
</div>
{tool_calls_html}
</div>
</div>
"""
def update_chat_display(existing_display, new_message):
"""Update an existing chat display with a new message."""
try:
# Parse the new message
role = new_message.get("role", "unknown").lower()
# Format the new message based on its role
if role == "user":
message_html = format_user_message(new_message)
elif role == "assistant" or role == "ai":
message_html = format_assistant_message(new_message)
else:
message_html = format_system_message(new_message)
# Find the position to insert the new message (before the Final Response section)
insert_marker = '<div style="padding-top: 0.5rem;margin-top: 1rem;margin-bottom: 1rem;border-top: 1px solid var(--border-color-light);'
parts = existing_display.split(insert_marker)
if len(parts) == 2:
# Insert the new message before the Final Response section
updated_display = parts[0] + message_html + insert_marker + parts[1]
return updated_display
else:
# If we can't find the insertion point, append to the end
return existing_display + message_html
except Exception as e:
return (
existing_display
+ f"""
<div style="
padding: 1rem;
color: var(--score-low);
background-color: var(--surface-color);
border: 1px solid var(--score-low);
border-radius: 10px;
margin-top: 1rem;">
<div style="font-weight: 600; margin-bottom: 0.5rem;">Error Updating Chat</div>
<div style="font-family: monospace; white-space: pre-wrap;">{str(e)}</div>
</div>
"""
)
def format_chat_display(row):
"""Format the chat display with better styling for user and assistant messages."""
try:
# Parse the conversation JSON
messages = json.loads(row["conversation"])
# Create HTML for all messages
messages_html = ""
for msg in messages:
role = msg.get("role", "unknown").lower()
if role == "user":
messages_html += format_user_message(msg)
elif role == "assistant" or role == "ai":
messages_html += format_assistant_message(msg)
else:
# System or other message types
messages_html += format_system_message(msg)
# Format the final response from the assistant
response_html = format_final_response(row["response"])
# Combine all HTML
full_chat_html = f"""
<div style="
padding: 1.5rem;
background-color: var(--surface-color);
border-radius: 10px;
border: 1px solid var(--border-color);
box-shadow: 0 2px 6px var(--shadow-color);
height: 100%;
overflow-y: auto;
max-height: 600px;
font-family: var(--font-sans);">
<div style="
padding-bottom: 1rem;
margin-bottom: 1.5rem;
border-bottom: 1px solid var(--border-color-light);
display: flex;
align-items: center;">
<div style="
font-weight: 600;
font-size: 1.1rem;
color: var(--primary-text);">
<span style="margin-right: 0.5rem;">💬</span>Conversation
</div>
</div>
{messages_html}
{response_html}
</div>
"""
return full_chat_html
except Exception as e:
return f"""
<div style="
padding: 1.5rem;
color: var(--score-low);
background-color: var(--surface-color);
border: 1px solid var(--score-low);
border-radius: 10px;">
<div style="font-weight: 600; margin-bottom: 0.5rem;">Error Formatting Chat</div>
<div style="font-family: monospace; white-space: pre-wrap;">{str(e)}</div>
<div style="margin-top: 1rem; font-family: monospace; font-size: 0.8rem;">
Original conversation: {str(row["conversation"])}
</div>
</div>
"""
def parse_tool_schema(tool):
"""Parse tool schema to extract name, description, and parameters properly."""
# Handle schema wrapped in a list
if isinstance(tool, list) and len(tool) > 0:
tool = tool[0]
# Extract function information from the new schema structure with "function" key
if "function" in tool:
function_data = tool["function"]
name = function_data.get("name", "Unnamed Tool")
description = function_data.get("description", "No description available")
parameters = {}
if (
"parameters" in function_data
and "properties" in function_data["parameters"]
):
properties = function_data["parameters"]["properties"]
for param_name, param_data in properties.items():
param_desc = param_data.get("description", "No description")
param_type = param_data.get("type", "unknown")
param_default = param_data.get("default", "None")
# Include default value in parameter description
parameters[param_name] = (
f"{param_desc} (Type: {param_type}, Default: {param_default})"
)
# Check for required parameters
required_params = function_data.get("parameters", {}).get("required", [])
if required_params:
for param_name in required_params:
if param_name in parameters:
parameters[param_name] = f"[REQUIRED] {parameters[param_name]}"
else:
# Original schema parsing
name = tool.get("title", "Unnamed Tool")
description = tool.get("description", "No description available")
parameters = {}
if "properties" in tool:
for param_name, param_data in tool["properties"].items():
param_desc = param_data.get("description", "No description")
param_type = param_data.get("type", "unknown")
param_title = param_data.get("title", param_name)
parameters[param_name] = (
f"{param_desc} (Type: {param_type}, Title: {param_title})"
)
# Check for required parameters in the original schema
required_params = tool.get("required", [])
if required_params:
for param_name in required_params:
if param_name in parameters:
parameters[param_name] = f"[REQUIRED] {parameters[param_name]}"
return name, description, parameters
def format_parameters(parameters):
if not parameters:
return '<div style="color: var(--text-muted); font-style: italic;">No parameters</div>'
params_html = ""
for name, desc in parameters.items():
is_required = "[REQUIRED]" in desc
param_style = "required" if is_required else "optional"
# Clean up the description to remove the REQUIRED marker but keep the info
cleaned_desc = desc.replace("[REQUIRED] ", "") if is_required else desc
params_html += f"""
<div style="
margin-bottom: 1.2rem;
padding-bottom: 1.2rem;
border-bottom: 1px solid var(--border-color);
last-child: border-bottom: none;">
<div style="
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 0.5rem;">
<div style="
font-weight: 600;
color: var(--primary-text);
font-size: 1.05rem;
display: flex;
align-items: center;">
{name}
</div>
<div style="
font-size: 0.8rem;
padding: 0.2rem 0.6rem;
border-radius: 12px;
background-color: {f"rgba(234, 67, 53, 0.1)" if is_required else "rgba(160, 160, 160, 0.1)"};
color: var(--{param_style}-color);
font-weight: 500;">
{f"Required" if is_required else "Optional"}
</div>
</div>
<div style="
color: var(--text-color);
line-height: 1.5;
font-size: 0.95rem;
opacity: 0.9;">
{cleaned_desc}
</div>
</div>
"""
# Remove the border-bottom from the last parameter
params_html = params_html.replace("last-child: border-bottom: none;", "")
return (
params_html
+ """
<style>
div:last-child {
border-bottom: none !important;
margin-bottom: 0 !important;
padding-bottom: 0 !important;
}
</style>
"""
)
def format_metrics(score, rationale, explanation):
"""Format metrics display with improved visual hierarchy and dark theme support."""
# Determine score color and add emoji indicator
if score >= 0.7:
score_color = "var(--score-high)"
score_emoji = "🟢"
score_text = "High"
elif score >= 0.4:
score_color = "var(--score-med)"
score_emoji = "🟠"
score_text = "Medium"
else:
score_color = "var(--score-low)"
score_emoji = "🔴"
score_text = "Low"
return f"""
<div style="
padding: 1.75rem;
background-color: var(--surface-color);
border-radius: 10px;
border: 1px solid var(--border-color);
box-shadow: 0 3px 8px var(--shadow-color);">
<div style="
display: flex;
align-items: center;
margin-bottom: 1.75rem;
padding-bottom: 1.5rem;
border-bottom: 1px solid var(--border-color-light);">
<div style="flex: 1;">
<h3 style="
color: var(--text-color);
font-size: 1.2rem;
margin-bottom: 0.25rem;
font-weight: 600;">TSQ Score</h3>
<div style="
display: flex;
align-items: baseline;">
<div style="
font-size: 2.5rem;
font-weight: 700;
color: {score_color};">
{score:.2f}
</div>
<div style="
margin-left: 0.75rem;
font-size: 1rem;
color: {score_color};
font-weight: 500;
display: flex;
align-items: center;">
<span style="margin-right: 0.5rem;">{score_emoji}</span>{score_text}
</div>
</div>
</div>
</div>
<div style="margin-bottom: 1.75rem;">
<h3 style="
color: var(--text-color);
font-size: 1.1rem;
margin-bottom: 0.75rem;
font-weight: 600;
display: flex;
align-items: center;">
<span style="
display: inline-block;
width: 18px;
height: 18px;
background-color: var(--primary-text-light);
border-radius: 4px;
margin-right: 0.5rem;"></span>
Rationale
</h3>
<div style="
color: var(--text-color);
line-height: 1.6;
padding-left: 1.5rem;
border-left: 3px solid var(--primary-text-light);
font-size: 0.95rem;">
{rationale}
</div>
</div>
<div>
<h3 style="
color: var(--text-color);
font-size: 1.1rem;
margin-bottom: 0.75rem;
font-weight: 600;
display: flex;
align-items: center;">
<span style="
display: inline-block;
width: 18px;
height: 18px;
background-color: var(--primary-text-light);
border-radius: 4px;
margin-right: 0.5rem;"></span>
Explanation
</h3>
<div style="
color: var(--text-color);
line-height: 1.6;
padding-left: 1.5rem;
border-left: 3px solid var(--primary-text-light);
font-size: 0.95rem;">
{explanation}
</div>
</div>
</div>
"""
def format_metrics_display(row):
"""Format the metrics display with score, rationale and explanation."""
try:
score = row["score"]
rationale = row["rationale"]
explanation = row["explanation"]
# Determine score color and add emoji indicator
if score >= 0.7:
score_color = "var(--score-high)"
score_emoji = "🟢"
score_text = "High"
elif score >= 0.4:
score_color = "var(--score-med)"
score_emoji = "🟠"
score_text = "Medium"
else:
score_color = "var(--score-low)"
score_emoji = "🔴"
score_text = "Low"
metrics_html = f"""
<div style="
padding: 1.5rem;
background-color: var(--surface-color);
border-radius: 10px;
border: 1px solid var(--border-color);
box-shadow: 0 2px 6px var(--shadow-color);
height: 100%;
overflow-y: auto;
max-height: 600px;">
<div style="
padding-bottom: 1rem;
margin-bottom: 1.5rem;
border-bottom: 1px solid var(--border-color-light);
display: flex;
align-items: center;">
<div style="
font-weight: 600;
font-size: 1.1rem;
color: var(--primary-text);">
<span style="margin-right: 0.5rem;">📊</span>Evaluation Metrics
</div>
</div>
<div style="
margin-bottom: 1.5rem;
padding-bottom: 1.5rem;
border-bottom: 1px solid var(--border-color-light);">
<div style="
display: flex;
align-items: center;
justify-content: space-between;">
<div>
<div style="
font-weight: 600;
margin-bottom: 0.25rem;
color: var(--text-color);">
TSQ Score
</div>
<div style="
font-size: 2.5rem;
font-weight: 700;
color: {score_color};
display: flex;
align-items: center;">
{score:.2f}
<div style="
margin-left: 0.75rem;
font-size: 1rem;
display: flex;
align-items: center;">
{score_emoji} <span style="margin-left: 0.25rem;">{score_text}</span>
</div>
</div>
</div>
</div>
</div>
<div style="margin-bottom: 1.5rem;">
<div style="
font-weight: 600;
margin-bottom: 0.75rem;
color: var(--text-color);
display: flex;
align-items: center;">
<span style="
display: inline-block;
width: 12px;
height: 12px;
background-color: var(--primary-text-light);
border-radius: 2px;
margin-right: 0.5rem;"></span>
Rationale
</div>
<div style="
background-color: var(--surface-color-alt);
padding: 1rem;
border-radius: 8px;
border-left: 3px solid var(--primary-text-light);
line-height: 1.5;
color: var(--text-color);
font-size: 0.95rem;">
{rationale}
</div>
</div>
<div>
<div style="
font-weight: 600;
margin-bottom: 0.75rem;
color: var(--text-color);
display: flex;
align-items: center;">
<span style="
display: inline-block;
width: 12px;
height: 12px;
background-color: var(--primary-text-light);
border-radius: 2px;
margin-right: 0.5rem;"></span>
Explanation
</div>
<div style="
background-color: var(--surface-color-alt);
padding: 1rem;
border-radius: 8px;
border-left: 3px solid var(--primary-text-light);
line-height: 1.5;
color: var(--text-color);
font-size: 0.95rem;">
{explanation}
</div>
</div>
</div>
"""
return metrics_html
except Exception as e:
return f"""
<div style="
padding: 1.5rem;
color: var(--score-low);
background-color: var(--surface-color);
border: 1px solid var(--score-low);
border-radius: 10px;">
<div style="font-weight: 600; margin-bottom: 0.5rem;">Error Formatting Metrics</div>
<div style="font-family: monospace; white-space: pre-wrap;">{str(e)}</div>
</div>
"""
def format_tool_info(tools_data):
"""Format the tool information with improved styling."""
try:
if not tools_data or tools_data == "[]":
return """
<div style="
padding: 1.5rem;
text-align: center;
color: var(--text-muted);
background-color: var(--surface-color);
border-radius: 10px;
border: 1px solid var(--border-color);
box-shadow: 0 2px 6px var(--shadow-color);">
<div style="font-size: 1.5rem; margin-bottom: 0.75rem;">🔍</div>
<div style="font-weight: 500; margin-bottom: 0.5rem;">No Tool Information</div>
<div style="font-size: 0.9rem; font-style: italic;">This conversation doesn't use any tools</div>
</div>
"""
if isinstance(tools_data, str):
try:
tools = json.loads(tools_data)
except:
tools = []
else:
tools = tools_data
if not tools:
return """
<div style="
padding: 1.5rem;
text-align: center;
color: var(--text-muted);
background-color: var(--surface-color);
border-radius: 10px;
border: 1px solid var(--border-color);
box-shadow: 0 2px 6px var(--shadow-color);">
<div style="font-size: 1.5rem; margin-bottom: 0.75rem;">🔍</div>
<div style="font-weight: 500; margin-bottom: 0.5rem;">No Tool Information</div>
<div style="font-size: 0.9rem; font-style: italic;">This conversation doesn't use any tools</div>
</div>
"""
# Format each tool
tool_items = ""
for tool in tools:
name = tool.get("title", tool.get("name", "Unnamed Tool"))
description = tool.get("description", "No description available")
# Get parameters
parameters = {}
required_params = []
# Handle different schema formats
if "function" in tool:
# Function schema format
function_data = tool["function"]
name = function_data.get("name", name)
description = function_data.get("description", description)
if (
"parameters" in function_data
and "properties" in function_data["parameters"]
):
properties = function_data["parameters"]["properties"]
for param_name, param_data in properties.items():
param_desc = param_data.get("description", "No description")
param_type = param_data.get("type", "unknown")
param_default = param_data.get("default", "None")
parameters[param_name] = {
"description": param_desc,
"type": param_type,
"default": param_default,
}
required_params = function_data.get("parameters", {}).get(
"required", []
)
elif "properties" in tool:
# Original schema format
if "properties" in tool:
for param_name, param_data in tool["properties"].items():
param_desc = param_data.get("description", "No description")
param_type = param_data.get("type", "unknown")
param_title = param_data.get("title", param_name)
parameters[param_name] = {
"description": param_desc,
"type": param_type,
"title": param_title,
}
required_params = tool.get("required", [])
# Format parameters
params_html = ""
if parameters:
for param_name, param_data in parameters.items():
is_required = param_name in required_params
param_style = "required" if is_required else "optional"
params_html += f"""
<div style="
margin-bottom: 1rem;
padding-bottom: 1rem;
border-bottom: 1px solid var(--border-color-light);">
<div style="
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 0.5rem;">
<div style="
font-weight: 600;
color: var(--primary-text);
font-size: 0.95rem;">
{param_name}
</div>
<div style="
font-size: 0.75rem;
padding: 0.15rem 0.5rem;
border-radius: 12px;
background-color: {f"rgba(234, 67, 53, 0.1)" if is_required else "rgba(160, 160, 160, 0.1)"};
color: {f"var(--score-low)" if is_required else "var(--text-muted)"};
font-weight: 500;">
{f"Required" if is_required else "Optional"}
</div>
</div>
<div style="
color: var(--text-muted);
line-height: 1.5;
font-size: 0.85rem;
margin-bottom: 0.25rem;">
{param_data.get("description", "No description")}
</div>
<div style="
display: flex;
font-size: 0.8rem;
color: var(--text-muted);">
<div style="margin-right: 1rem;">
<span style="font-weight: 500;">Type:</span> {param_data.get("type", "unknown")}
</div>
{f'<div><span style="font-weight: 500;">Default:</span> {param_data.get("default", "None")}</div>' if "default" in param_data else ''}
</div>
</div>
"""
else:
params_html = """
<div style="
color: var(--text-muted);
font-style: italic;
padding: 0.75rem;
text-align: center;
font-size: 0.9rem;">
No parameters
</div>
"""
# Remove border from last parameter
params_html += """
<style>
.tool-params > div:last-child {
border-bottom: none !important;
margin-bottom: 0 !important;
padding-bottom: 0 !important;
}
</style>
"""
tool_items += f"""
<div style="
margin-bottom: 1.5rem;
padding: 1.5rem;
border-radius: 8px;
background-color: var(--surface-color-alt);
border: 1px solid var(--border-color);
box-shadow: 0 1px 3px var(--shadow-color);">
<div style="
font-weight: 600;
color: var(--primary-text);
margin-bottom: 0.75rem;
font-size: 1.05rem;
display: flex;
align-items: center;">
<span style="margin-right: 8px;">⚙️</span> {name}
</div>
<div style="
color: var(--text-color);
margin-bottom: 1.25rem;
line-height: 1.5;
font-size: 0.95rem;
padding-left: 0.5rem;
border-left: 3px solid var(--primary-text-light);">
{description}
</div>
<div style="
font-weight: 600;
color: var(--text-color);
margin-bottom: 0.75rem;
font-size: 0.9rem;">
Parameters:
</div>
<div class="tool-params">
{params_html}
</div>
</div>
"""
full_tools_html = f"""
<div style="
padding: 1.5rem;
background-color: var(--surface-color);
border-radius: 10px;
border: 1px solid var(--border-color);
box-shadow: 0 2px 6px var(--shadow-color);
height: 100%;
overflow-y: auto;
max-height: 600px;">
<div style="
padding-bottom: 1rem;
margin-bottom: 1.5rem;
border-bottom: 1px solid var(--border-color-light);
display: flex;
align-items: center;">
<div style="
font-weight: 600;
font-size: 1.1rem;
color: var(--primary-text);">
<span style="margin-right: 0.5rem;">🛠️</span>Available Tools
</div>
</div>
{tool_items}
</div>
"""
return full_tools_html
except Exception as e:
return f"""
<div style="
padding: 1.5rem;
color: var(--score-low);
background-color: var(--surface-color);
border: 1px solid var(--score-low);
border-radius: 10px;">
<div style="font-weight: 600; margin-bottom: 0.5rem;">Error Formatting Tool Info</div>
<div style="font-family: monospace; white-space: pre-wrap;">{str(e)}</div>
</div>
"""