diff --git a/PAGINATION_IMPLEMENTATION.md b/PAGINATION_IMPLEMENTATION.md deleted file mode 100644 index 9b9bb432..00000000 --- a/PAGINATION_IMPLEMENTATION.md +++ /dev/null @@ -1,130 +0,0 @@ -# Pagination Implementation for Agents and Marketplace - -This document outlines the implementation of server-side pagination, searching, sorting, and filtering for both the agents page and marketplace page. - -## Backend Changes - -### 1. Updated API Endpoints - -#### Agents Endpoint (`/agents`) -- **New Parameters:** - - `page`: Page number (1-based, default: 1) - - `limit`: Items per page (1-100, default: 20) - - `search`: Search in name and description - - `sort_by`: Sort field (name, created_at, updated_at, tools_count) - - `sort_order`: Sort order (asc, desc) - - `has_default`: Filter by default agents - - `has_mcp_tools`: Filter by agents with MCP tools - - `has_agentpress_tools`: Filter by agents with AgentPress tools - - `tools`: Comma-separated list of tools to filter by - -- **Response Format:** -```json -{ - "agents": [...], - "pagination": { - "page": 1, - "limit": 20, - "total": 150, - "pages": 8 - } -} -``` - -#### Marketplace Endpoint (`/marketplace/agents`) -- **New Parameters:** - - `page`: Page number (1-based, default: 1) - - `limit`: Items per page (1-100, default: 20) - - `search`: Search in name and description - - `tags`: Comma-separated string of tags - - `sort_by`: Sort by (newest, popular, most_downloaded, name) - - `creator`: Filter by creator name - -- **Response Format:** -```json -{ - "agents": [...], - "pagination": { - "page": 1, - "limit": 20, - "total": 75, - "pages": 4 - } -} -``` - -### 2. Database Functions - -#### Updated `get_marketplace_agents` -- Added `p_creator` parameter for filtering by creator name -- Enhanced search functionality - -#### New `get_marketplace_agents_count` -- Returns total count of marketplace agents matching filters -- Used for pagination calculation - -## Frontend Changes - -### 1. New Components - -#### Pagination Component -- Located at: `frontend/src/app/(dashboard)/agents/_components/pagination.tsx` -- Features: - - Smart page number display with ellipsis - - Previous/Next navigation - - Disabled state during loading - - Responsive design - -### 2. Updated Hooks - -#### useAgents Hook -- Now accepts `AgentsParams` for server-side filtering -- Returns `AgentsResponse` with pagination info - -#### useMarketplaceAgents Hook -- Updated to support new pagination parameters -- Returns `MarketplaceAgentsResponse` with pagination info - -### 3. Updated Pages - -#### Agents Page -- Replaced client-side filtering with server-side parameters -- Added pagination component -- Automatic page reset when filters change -- Enhanced results display with pagination info - -#### Marketplace Page -- Added pagination support -- Enhanced sorting options (added "Name A-Z") -- Improved results display -- Added pagination component - -## Benefits - -1. **Performance**: Only loads necessary data, reducing bandwidth and improving load times -2. **Scalability**: Can handle large datasets efficiently -3. **User Experience**: Faster page loads and responsive filtering -4. **Server Resources**: Reduced memory usage and database load -5. **Search**: Real-time search with backend optimization - -## Usage - -### Agents Page -- Search agents by name or description -- Filter by default status, tool types, or specific tools -- Sort by name, creation date, update date, or tool count -- Navigate through pages with pagination controls - -### Marketplace Page -- Search marketplace agents by name or description -- Filter by tags or creator name -- Sort by newest, popularity, downloads, or name -- Browse through paginated results - -## Technical Notes - -- Page size is limited to 100 items maximum for performance -- Search is case-insensitive and matches partial strings -- Tool filtering supports both MCP and AgentPress tools -- Sorting is handled both in database (for simple fields) and post-processing (for computed fields like tools_count) -- Pagination automatically resets to page 1 when filters change \ No newline at end of file diff --git a/backend/test_image_compression.py b/backend/test_image_compression.py deleted file mode 100644 index 32263a5b..00000000 --- a/backend/test_image_compression.py +++ /dev/null @@ -1,92 +0,0 @@ -#!/usr/bin/env python3 -"""Test script for image compression functionality in SeeImage tool.""" - -import os -import sys -from io import BytesIO -from PIL import Image -import base64 - -# Add the backend directory to the Python path -sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) - -from agent.tools.sb_vision_tool import SandboxVisionTool - -def create_test_image(width=3000, height=2000, format='PNG'): - """Create a test image with specified dimensions.""" - # Create a colorful test pattern - img = Image.new('RGB', (width, height)) - pixels = img.load() - - for x in range(width): - for y in range(height): - # Create a gradient pattern - r = int((x / width) * 255) - g = int((y / height) * 255) - b = int(((x + y) / (width + height)) * 255) - pixels[x, y] = (r, g, b) - - # Save to bytes - output = BytesIO() - img.save(output, format=format) - return output.getvalue() - -def test_compression(): - """Test the compression functionality.""" - # Create a mock SandboxVisionTool instance - # We'll just test the compress_image method directly - tool = SandboxVisionTool(project_id="test", thread_id="test", thread_manager=None) - - print("Testing image compression functionality...\n") - - # Test 1: Large PNG image - print("Test 1: Large PNG image") - png_bytes = create_test_image(3000, 2000, 'PNG') - original_size = len(png_bytes) - print(f"Original PNG size: {original_size / 1024:.1f}KB") - - compressed_bytes, mime_type = tool.compress_image(png_bytes, 'image/png', 'test.png') - compressed_size = len(compressed_bytes) - print(f"Compressed size: {compressed_size / 1024:.1f}KB") - print(f"Compression ratio: {(1 - compressed_size / original_size) * 100:.1f}%") - print(f"Output MIME type: {mime_type}\n") - - # Test 2: JPEG image - print("Test 2: JPEG image") - jpeg_bytes = create_test_image(2000, 1500, 'JPEG') - original_size = len(jpeg_bytes) - print(f"Original JPEG size: {original_size / 1024:.1f}KB") - - compressed_bytes, mime_type = tool.compress_image(jpeg_bytes, 'image/jpeg', 'test.jpg') - compressed_size = len(compressed_bytes) - print(f"Compressed size: {compressed_size / 1024:.1f}KB") - print(f"Compression ratio: {(1 - compressed_size / original_size) * 100:.1f}%") - print(f"Output MIME type: {mime_type}\n") - - # Test 3: Small image (should not be resized) - print("Test 3: Small image (800x600)") - small_bytes = create_test_image(800, 600, 'PNG') - original_size = len(small_bytes) - print(f"Original size: {original_size / 1024:.1f}KB") - - compressed_bytes, mime_type = tool.compress_image(small_bytes, 'image/png', 'small.png') - compressed_size = len(compressed_bytes) - print(f"Compressed size: {compressed_size / 1024:.1f}KB") - print(f"Compression ratio: {(1 - compressed_size / original_size) * 100:.1f}%") - print(f"Output MIME type: {mime_type}\n") - - # Test 4: Verify image quality after compression - print("Test 4: Verifying image quality") - # Open the compressed image to check it's valid - try: - compressed_img = Image.open(BytesIO(compressed_bytes)) - print(f"Compressed image dimensions: {compressed_img.size}") - print(f"Compressed image mode: {compressed_img.mode}") - print("✓ Compressed image is valid and can be opened") - except Exception as e: - print(f"✗ Error opening compressed image: {e}") - - print("\nAll tests completed!") - -if __name__ == "__main__": - test_compression() \ No newline at end of file diff --git a/backend/test_mcp_tools.py b/backend/test_mcp_tools.py deleted file mode 100644 index e6fd5ab3..00000000 --- a/backend/test_mcp_tools.py +++ /dev/null @@ -1,108 +0,0 @@ -""" -Test script to list ONLY MCP tool OpenAI schema method names -""" - -import asyncio -import os -from dotenv import load_dotenv -from agentpress.thread_manager import ThreadManager -from agent.tools.mcp_tool_wrapper import MCPToolWrapper -from agentpress.tool import SchemaType -from utils.logger import logger - -load_dotenv() - -async def test_mcp_tools_only(): - """Test listing only MCP tools and their OpenAI schema method names""" - - # Create thread manager - thread_manager = ThreadManager() - - print("\n=== MCP Tools Test ===") - - # MCP configuration with ALL tools enabled (empty enabledTools) - mcp_configs = [ - { - "name": "Exa Search", - "qualifiedName": "exa", - "config": {"exaApiKey": os.getenv("EXA_API_KEY", "test-key")}, - "enabledTools": [] # Empty to get ALL tools - } - ] - - # Register MCP tool wrapper - logger.info("Registering MCP tool wrapper...") - thread_manager.add_tool(MCPToolWrapper, mcp_configs=mcp_configs) - - # Get the tool instance - mcp_wrapper_instance = None - for tool_name, tool_info in thread_manager.tool_registry.tools.items(): - if isinstance(tool_info['instance'], MCPToolWrapper): - mcp_wrapper_instance = tool_info['instance'] - break - - if not mcp_wrapper_instance: - logger.error("Failed to find MCP wrapper instance") - return - - try: - # Initialize MCP tools - logger.info("Initializing MCP tools...") - await mcp_wrapper_instance.initialize_and_register_tools() - - # Get all available MCP tools from the server - available_mcp_tools = await mcp_wrapper_instance.get_available_tools() - print(f"\nTotal MCP tools available from server: {len(available_mcp_tools)}") - - # Get the dynamically created schemas - updated_schemas = mcp_wrapper_instance.get_schemas() - mcp_method_schemas = {k: v for k, v in updated_schemas.items() if k != 'call_mcp_tool'} - - print(f"\nDynamically created MCP methods: {len(mcp_method_schemas)}") - - # List all MCP tool method names with descriptions - print("\n=== MCP Tool Method Names (Clean Names) ===") - for method_name, schema_list in sorted(mcp_method_schemas.items()): - for schema in schema_list: - if schema.schema_type == SchemaType.OPENAPI: - func_info = schema.schema.get('function', {}) - func_desc = func_info.get('description', 'No description') - # Extract just the description part before "(MCP Server:" - desc_parts = func_desc.split(' (MCP Server:') - clean_desc = desc_parts[0] if desc_parts else func_desc - print(f"\n{method_name}") - print(f" Description: {clean_desc}") - - # Show parameters - params = func_info.get('parameters', {}) - props = params.get('properties', {}) - required = params.get('required', []) - if props: - print(f" Parameters:") - for param_name, param_info in props.items(): - param_type = param_info.get('type', 'any') - param_desc = param_info.get('description', 'No description') - is_required = param_name in required - req_marker = " (required)" if is_required else " (optional)" - print(f" - {param_name}: {param_type}{req_marker} - {param_desc}") - - # Show the name mapping - print("\n\n=== MCP Tool Name Mapping (Original -> Clean) ===") - for original_name, tool_data in sorted(mcp_wrapper_instance._dynamic_tools.items()): - print(f"{original_name} -> {tool_data['method_name']}") - - # Summary of callable method names - print("\n\n=== Summary: Callable MCP Method Names ===") - method_names = sorted(mcp_method_schemas.keys()) - for name in method_names: - print(f"- {name}") - - print(f"\nTotal callable MCP methods: {len(method_names)}") - - except Exception as e: - logger.error(f"Error during MCP initialization: {e}") - import traceback - traceback.print_exc() - -if __name__ == "__main__": - asyncio.run(test_mcp_tools_only()) \ No newline at end of file diff --git a/frontend/src/app/opengraph-image.tsx b/frontend/src/app/opengraph-image.tsx deleted file mode 100644 index 47d761c0..00000000 --- a/frontend/src/app/opengraph-image.tsx +++ /dev/null @@ -1,50 +0,0 @@ -import { headers } from 'next/headers'; -import { ImageResponse } from 'next/og'; - -// Configuration exports -export const runtime = 'edge'; -export const alt = 'Kortix Suna'; -export const size = { - width: 1200, - height: 630, -}; -export const contentType = 'image/png'; - -export default async function Image() { - try { - // Get the host from headers - const headersList = await headers(); - const host = headersList.get('host') || ''; - const protocol = process.env.NODE_ENV === 'development' ? 'http' : 'https'; - const baseUrl = `${protocol}://${host}`; - - return new ImageResponse( - ( -
- {alt} -
- ), - { ...size }, - ); - } catch (error) { - console.error('Error generating OpenGraph image:', error); - return new Response(`Failed to generate image`, { status: 500 }); - } -}