Spaces:
Running
on
CPU Upgrade
Running
on
CPU Upgrade
| 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> | |
| """ | |